s3daw.c (13582B)
1 /* Copyright (C) 2015, 2016, 2020, 2021, 2023 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* dirname support */ 17 18 #include "s3daw.h" 19 20 #include <rsys/dynamic_array_uint.h> 21 #include <rsys/dynamic_array_float.h> 22 #include <rsys/float3.h> 23 #include <rsys/float4.h> 24 #include <rsys/hash_table.h> 25 #include <rsys/logger.h> 26 #include <rsys/mem_allocator.h> 27 #include <rsys/ref_count.h> 28 29 #include <star/s3d.h> 30 31 #include <aw.h> 32 #include <polygon.h> 33 #include <string.h> 34 35 #define DARRAY_NAME shape 36 #define DARRAY_DATA struct s3d_shape* 37 #include <rsys/dynamic_array.h> 38 39 #define HTABLE_NAME vertex_id 40 #define HTABLE_DATA unsigned 41 #define HTABLE_KEY size_t 42 #include <rsys/hash_table.h> 43 44 #define HTABLE_NAME material 45 #define HTABLE_DATA struct aw_material 46 #define HTABLE_KEY struct str 47 #define HTABLE_KEY_FUNCTOR_EQ str_eq 48 #define HTABLE_KEY_FUNCTOR_HASH str_hash 49 #define HTABLE_KEY_FUNCTOR_INIT str_init 50 #define HTABLE_KEY_FUNCTOR_COPY str_copy 51 #define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release 52 #define HTABLE_KEY_FUNCTOR_RELEASE str_release 53 #include <rsys/hash_table.h> 54 55 struct s3daw { 56 struct aw_obj* loader_obj; 57 struct aw_mtl* loader_mtl; 58 struct polygon* polygon; 59 struct s3d_device* s3d; 60 61 struct darray_shape shapes; 62 63 int verbose; 64 65 struct mem_allocator* allocator; 66 struct logger* logger; 67 ref_T ref; 68 }; 69 70 struct context { 71 const struct darray_float* positions; 72 const struct darray_uint* indices; 73 }; 74 75 /******************************************************************************* 76 * Helper functions 77 ******************************************************************************/ 78 static void 79 get_indices(const unsigned itri, unsigned ids[3], void* context) 80 { 81 struct context* ctx = context; 82 const unsigned* indices; 83 ASSERT(ids && ctx && itri < darray_uint_size_get(ctx->indices)/3); 84 indices = darray_uint_cdata_get(ctx->indices) + itri*3; 85 ids[0] = indices[0]; 86 ids[1] = indices[1]; 87 ids[2] = indices[2]; 88 } 89 90 static void 91 get_position(const unsigned ivert, float pos[3], void* context) 92 { 93 struct context* ctx = context; 94 const float* position; 95 ASSERT(ctx && pos && ivert < darray_float_size_get(ctx->positions)/3); 96 position = darray_float_cdata_get(ctx->positions) + ivert*3; 97 pos[0] = position[0]; 98 pos[1] = position[1]; 99 pos[2] = position[2]; 100 } 101 102 static INLINE void 103 shapes_clear(struct darray_shape* shapes) 104 { 105 size_t i; 106 ASSERT(shapes); 107 FOR_EACH(i, 0, darray_shape_size_get(shapes)) { 108 S3D(shape_ref_put(darray_shape_data_get(shapes)[i])); 109 } 110 darray_shape_clear(shapes); 111 } 112 113 static res_T 114 shape_register 115 (struct s3daw* s3daw, 116 const struct aw_obj_named_group* obj_mtl, 117 struct darray_float* vertices, 118 struct darray_uint* indices, 119 struct htable_vertex_id* vertices_ids) 120 { 121 struct context ctx; 122 struct s3d_vertex_data vertex_data; 123 struct s3d_shape* shape = NULL; 124 size_t iface; 125 size_t ntris, nverts; 126 res_T res = RES_OK; 127 ASSERT(s3daw && obj_mtl && vertices && indices && vertices_ids); 128 129 /* Reset the scrach shape data */ 130 darray_float_clear(vertices); 131 darray_uint_clear(indices); 132 htable_vertex_id_clear(vertices_ids); 133 134 FOR_EACH(iface, obj_mtl->face_id, obj_mtl->face_id + obj_mtl->faces_count) { 135 struct aw_obj_face face; 136 struct aw_obj_vertex vertex; 137 struct aw_obj_vertex_data vdata; 138 const uint32_t* tri_ids; 139 uint32_t ntri_ids; 140 size_t ivertex; 141 size_t i; 142 143 AW(obj_get_face(s3daw->loader_obj, iface, &face)); 144 145 /* Triangulate the face */ 146 POLYGON(clear(s3daw->polygon)); 147 FOR_EACH(ivertex, face.vertex_id, face.vertex_id + face.vertices_count) { 148 float position[3]; 149 AW(obj_get_vertex(s3daw->loader_obj, ivertex, &vertex)); 150 AW(obj_get_vertex_data(s3daw->loader_obj, &vertex, &vdata)); 151 position[0] = (float)vdata.position[0]; 152 position[1] = (float)vdata.position[1]; 153 position[2] = (float)vdata.position[2]; 154 res = polygon_vertex_add(s3daw->polygon, position); 155 if(res != RES_OK) goto error; 156 } 157 res = polygon_triangulate(s3daw->polygon, &tri_ids, &ntri_ids); 158 if(res != RES_OK) goto error; 159 160 FOR_EACH(i, 0, ntri_ids) { 161 unsigned* ivertex_registered; 162 163 /* Define if the obj_face vertex is already registered */ 164 ivertex = tri_ids[i] + face.vertex_id; 165 AW(obj_get_vertex(s3daw->loader_obj, ivertex, &vertex)); 166 ivertex_registered = htable_vertex_id_find(vertices_ids, &vertex.position_id); 167 168 if(ivertex_registered) { 169 /* Vertex is registered. Simply add its id to the indices */ 170 res = darray_uint_push_back(indices, ivertex_registered); 171 if(res != RES_OK) goto error; 172 } else { 173 /* Vertex is not registered. Register it and add its id to the indices */ 174 float position[3]; 175 const unsigned ivertex_new = (unsigned)darray_float_size_get(vertices)/3; 176 177 AW(obj_get_vertex_data(s3daw->loader_obj, &vertex, &vdata)); 178 position[0] = (float)vdata.position[0]; 179 position[1] = (float)vdata.position[1]; 180 position[2] = (float)vdata.position[2]; 181 182 #define CALL(Func) if((res = Func) != RES_OK) goto error; 183 CALL(darray_float_push_back(vertices, position+0)); 184 CALL(darray_float_push_back(vertices, position+1)); 185 CALL(darray_float_push_back(vertices, position+2)); 186 CALL(darray_uint_push_back(indices, &ivertex_new)); 187 CALL(htable_vertex_id_set(vertices_ids, &vertex.position_id, &ivertex_new)); 188 #undef CALL 189 } 190 } 191 } 192 193 nverts = darray_float_size_get(vertices) / 3; 194 ntris = darray_uint_size_get(indices) / 3; 195 if(!ntris) goto exit; /* Nothing to do */ 196 197 res = s3d_shape_create_mesh(s3daw->s3d, &shape); 198 if(res != RES_OK) goto error; 199 200 vertex_data.usage = S3D_POSITION; 201 vertex_data.type = S3D_FLOAT3; 202 vertex_data.get = get_position; 203 ctx.indices = indices; 204 ctx.positions = vertices; 205 206 /* Setup the S3D mesh data */ 207 res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)ntris, get_indices, 208 (unsigned)nverts, &vertex_data, 1, &ctx); 209 if(res != RES_OK) goto error; 210 211 /* Register the shape */ 212 res = darray_shape_push_back(&s3daw->shapes, &shape); 213 if(res != RES_OK) goto error; 214 215 exit: 216 return res; 217 error: 218 if(shape) S3D(shape_ref_put(shape)); 219 goto exit; 220 } 221 222 static res_T 223 shapes_create(struct s3daw* s3daw, const char* filename) 224 { 225 struct darray_uint indices; 226 struct darray_float vertices; 227 struct htable_vertex_id vertices_ids; 228 struct aw_obj_desc obj_desc; 229 struct aw_obj_named_group grp; 230 res_T res = RES_OK; 231 ASSERT(s3daw && filename); 232 233 /* Clean up the previously created shapes */ 234 shapes_clear(&s3daw->shapes); 235 236 /* Init scratch data structures */ 237 darray_uint_init(s3daw->allocator, &indices); 238 darray_float_init(s3daw->allocator, &vertices); 239 htable_vertex_id_init(s3daw->allocator, &vertices_ids); 240 241 AW(obj_get_desc(s3daw->loader_obj, &obj_desc)); 242 243 if(obj_desc.faces_count == 0) { 244 if(s3daw->verbose) { 245 logger_print(s3daw->logger, LOG_WARNING, 246 "Empty file content `%s'. It is discarded.\n", filename); 247 } 248 goto exit; 249 } 250 251 if(!obj_desc.usemtls_count) { /* No material grouping => triangle soup */ 252 grp.name = NULL; 253 grp.face_id = 0; 254 grp.faces_count = obj_desc.faces_count; 255 res = shape_register(s3daw, &grp, &vertices, &indices, &vertices_ids); 256 if(res != RES_OK) goto error; 257 } else { /* Create a S3D shape per material */ 258 size_t imtl; 259 FOR_EACH(imtl, 0, obj_desc.usemtls_count) { 260 AW(obj_get_mtl(s3daw->loader_obj, imtl, &grp)); 261 res = shape_register(s3daw, &grp, &vertices, &indices, &vertices_ids); 262 if(res != RES_OK) goto error; 263 } 264 } 265 266 exit: 267 darray_uint_release(&indices); 268 darray_float_release(&vertices); 269 htable_vertex_id_release(&vertices_ids); 270 return res; 271 error: 272 shapes_clear(&s3daw->shapes); 273 goto exit; 274 } 275 276 static void 277 release_s3daw(ref_T* ref) 278 { 279 struct s3daw* s3daw = CONTAINER_OF(ref, struct s3daw, ref); 280 ASSERT(ref); 281 if(s3daw->loader_obj) AW(obj_ref_put(s3daw->loader_obj)); 282 if(s3daw->loader_mtl) AW(mtl_ref_put(s3daw->loader_mtl)); 283 if(s3daw->polygon) POLYGON(ref_put(s3daw->polygon)); 284 if(s3daw->s3d) S3D(device_ref_put(s3daw->s3d)); 285 shapes_clear(&s3daw->shapes); 286 darray_shape_release(&s3daw->shapes); 287 MEM_RM(s3daw->allocator, s3daw); 288 } 289 290 /******************************************************************************* 291 * Exported functions 292 ******************************************************************************/ 293 res_T 294 s3daw_create 295 (struct logger* logger, 296 struct mem_allocator* allocator, 297 struct aw_obj* obj, 298 struct aw_mtl* mtl, 299 struct s3d_device* s3d, 300 const int verbose, 301 struct s3daw** out_s3daw) 302 { 303 struct s3daw* s3daw = NULL; 304 struct mem_allocator* mem_allocator; 305 res_T res = RES_OK; 306 307 if(!s3d || !out_s3daw) { 308 res = RES_BAD_ARG; 309 goto error; 310 } 311 mem_allocator = allocator ? allocator : &mem_default_allocator; 312 s3daw = MEM_CALLOC(mem_allocator, 1, sizeof(struct s3daw)); 313 if(!s3daw) { 314 res = RES_MEM_ERR; 315 goto error; 316 } 317 318 ref_init(&s3daw->ref); 319 s3daw->allocator = mem_allocator; 320 s3daw->logger = logger ? logger : LOGGER_DEFAULT; 321 s3daw->verbose = verbose; 322 S3D(device_ref_get(s3d)); 323 s3daw->s3d = s3d; 324 darray_shape_init(s3daw->allocator, &s3daw->shapes); 325 326 res = polygon_create(s3daw->allocator, &s3daw->polygon); 327 if(res != RES_OK) goto error; 328 329 if(obj) { 330 AW(obj_ref_get(obj)); 331 s3daw->loader_obj = obj; 332 } else { 333 res = aw_obj_create(logger, allocator, verbose, &s3daw->loader_obj); 334 if(res != RES_OK) goto error; 335 } 336 337 if(mtl) { 338 AW(mtl_ref_get(mtl)); 339 s3daw->loader_mtl = mtl; 340 } else { 341 res = aw_mtl_create(logger, allocator, verbose, &s3daw->loader_mtl); 342 if(res != RES_OK) goto error; 343 } 344 345 exit: 346 if(out_s3daw) *out_s3daw = s3daw; 347 return res; 348 error: 349 if(s3daw) { 350 S3DAW(ref_put(s3daw)); 351 s3daw = NULL; 352 } 353 goto exit; 354 } 355 356 res_T 357 s3daw_ref_get(struct s3daw* s3daw) 358 { 359 if(!s3daw) return RES_BAD_ARG; 360 ref_get(&s3daw->ref); 361 return RES_OK; 362 } 363 364 res_T 365 s3daw_ref_put(struct s3daw* s3daw) 366 { 367 if(!s3daw) return RES_BAD_ARG; 368 ref_put(&s3daw->ref, release_s3daw); 369 return RES_OK; 370 } 371 372 res_T 373 s3daw_get_loaders(struct s3daw* s3daw, struct aw_obj** obj, struct aw_mtl** mtl) 374 { 375 if(!s3daw) return RES_BAD_ARG; 376 if(obj) *obj = s3daw->loader_obj; 377 if(mtl) *mtl = s3daw->loader_mtl; 378 return RES_OK; 379 } 380 381 res_T 382 s3daw_get_s3d_device(struct s3daw* s3daw, struct s3d_device** s3d) 383 { 384 if(!s3daw || !s3d) return RES_BAD_ARG; 385 *s3d = s3daw->s3d; 386 return RES_OK; 387 } 388 389 res_T 390 s3daw_load(struct s3daw* s3daw, const char* filename) 391 { 392 res_T res; 393 394 if(!s3daw || !filename) return RES_BAD_ARG; 395 396 res = aw_obj_load(s3daw->loader_obj, filename); 397 if(res != RES_OK) return res; 398 return shapes_create(s3daw, filename); 399 } 400 401 res_T 402 s3daw_load_stream(struct s3daw* s3daw, FILE* stream) 403 { 404 res_T res = RES_OK; 405 406 if(!s3daw || !stream) return RES_BAD_ARG; 407 408 res = aw_obj_load_stream(s3daw->loader_obj, stream, "stream"); 409 if(res != RES_OK) return res; 410 return shapes_create(s3daw, "./"); 411 } 412 413 res_T 414 s3daw_clear(struct s3daw* s3daw) 415 { 416 if(!s3daw) return RES_BAD_ARG; 417 418 /* Clear intermediary data structures */ 419 POLYGON(clear(s3daw->polygon)); 420 421 /* Clear shape */ 422 shapes_clear(&s3daw->shapes); 423 424 AW(obj_clear(s3daw->loader_obj)); 425 AW(mtl_clear(s3daw->loader_mtl)); 426 427 return RES_OK; 428 } 429 430 res_T 431 s3daw_get_shapes_count(const struct s3daw* s3daw, size_t* nshapes) 432 { 433 if(!s3daw || !nshapes) return RES_BAD_ARG; 434 *nshapes = darray_shape_size_get(&s3daw->shapes); 435 return RES_OK; 436 } 437 438 res_T 439 s3daw_get_shape 440 (struct s3daw* s3daw, 441 const size_t ishape, 442 struct s3d_shape** shape) 443 { 444 if(!s3daw || !shape || ishape >= darray_shape_size_get(&s3daw->shapes)) 445 return RES_BAD_ARG; 446 *shape = darray_shape_data_get(&s3daw->shapes)[ishape]; 447 return RES_OK; 448 } 449 450 res_T 451 s3daw_attach_to_scene(struct s3daw* s3daw, struct s3d_scene* scene) 452 { 453 struct s3d_shape* shape = NULL; 454 size_t i=0, n=0; 455 res_T res = RES_OK; 456 457 if(!s3daw) { 458 res = RES_BAD_ARG; 459 goto error; 460 } 461 462 S3DAW(get_shapes_count(s3daw, &n)); 463 FOR_EACH(i, 0, n) { 464 S3DAW(get_shape(s3daw, i, &shape)); 465 res = s3d_scene_attach_shape(scene, shape); 466 if(res != RES_OK) goto error; 467 } 468 469 exit: 470 return res; 471 error: 472 /* Rollback the attachments */ 473 n = i; 474 FOR_EACH(i, 0, n) { 475 S3DAW(get_shape(s3daw, i, &shape)); 476 S3D(scene_detach_shape(scene, shape)); 477 } 478 goto exit; 479 } 480 481 res_T 482 s3daw_detach_from_scene(struct s3daw* s3daw, struct s3d_scene* scene) 483 { 484 struct s3d_shape* shape = NULL; 485 size_t i=0, n=0; 486 res_T res = RES_OK; 487 488 if(!s3daw) { 489 res = RES_BAD_ARG; 490 goto error; 491 } 492 493 S3DAW(get_shapes_count(s3daw, &n)); 494 FOR_EACH(i, 0, n) { 495 S3DAW(get_shape(s3daw, i, &shape)); 496 res = s3d_scene_detach_shape(scene, shape); 497 if(res != RES_OK) goto error; 498 } 499 500 exit: 501 return res; 502 error: 503 /* Rollback the detachments */ 504 n = i; 505 FOR_EACH(i, 0, n) { 506 S3DAW(get_shape(s3daw, i, &shape)); 507 S3D(scene_attach_shape(scene, shape)); 508 } 509 goto exit; 510 } 511