diff options
author | 3gg <3gg@shellblade.net> | 2023-01-04 16:53:28 -0800 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2023-01-04 16:53:28 -0800 |
commit | 134282937e724f33f9cc18731024346373e6c38c (patch) | |
tree | f65e379d081ce022b6ba4008b5abfd72debcc5d7 | |
parent | 27ff505b6daaf5b0ec5f6af422f727a032f83c6b (diff) |
Simplify cleanup in the scene loader.
-rw-r--r-- | gfx/src/util/scene.c | 471 |
1 files changed, 156 insertions, 315 deletions
diff --git a/gfx/src/util/scene.c b/gfx/src/util/scene.c index dc97259..706e518 100644 --- a/gfx/src/util/scene.c +++ b/gfx/src/util/scene.c | |||
@@ -207,80 +207,44 @@ static size_t get_total_primitives(const cgltf_data* data) { | |||
207 | /// | 207 | /// |
208 | /// Return an array of Buffers such that the index of each glTF buffer in | 208 | /// Return an array of Buffers such that the index of each glTF buffer in |
209 | /// the original array matches the same Buffer in the resulting array. | 209 | /// the original array matches the same Buffer in the resulting array. |
210 | static Buffer** load_buffers( | 210 | static bool load_buffers( |
211 | const cgltf_data* data, RenderBackend* render_backend, | 211 | const cgltf_data* data, RenderBackend* render_backend, Buffer** buffers) { |
212 | cgltf_size* num_buffers) { | ||
213 | assert(data); | 212 | assert(data); |
214 | assert(render_backend); | 213 | assert(render_backend); |
215 | assert(num_buffers); | 214 | assert(buffers); |
216 | |||
217 | Buffer** buffers = 0; | ||
218 | |||
219 | buffers = calloc(data->buffers_count, sizeof(Buffer*)); | ||
220 | if (!buffers) { | ||
221 | goto cleanup; | ||
222 | } | ||
223 | 215 | ||
224 | for (cgltf_size i = 0; i < data->buffers_count; ++i) { | 216 | for (cgltf_size i = 0; i < data->buffers_count; ++i) { |
225 | const cgltf_buffer* buffer = &data->buffers[i]; | 217 | const cgltf_buffer* buffer = &data->buffers[i]; |
226 | assert(buffer->data); | 218 | assert(buffer->data); |
227 | buffers[i] = gfx_make_buffer(render_backend, buffer->data, buffer->size); | 219 | buffers[i] = gfx_make_buffer(render_backend, buffer->data, buffer->size); |
228 | if (!buffers[i]) { | 220 | if (!buffers[i]) { |
229 | goto cleanup; | 221 | return false; |
230 | } | 222 | } |
231 | } | 223 | } |
232 | *num_buffers = data->buffers_count; | ||
233 | return buffers; | ||
234 | 224 | ||
235 | cleanup: | 225 | return true; |
236 | if (buffers) { | ||
237 | for (cgltf_size i = 0; i < data->buffers_count; ++i) { | ||
238 | if (buffers[i]) { | ||
239 | gfx_destroy_buffer(render_backend, &buffers[i]); | ||
240 | } | ||
241 | } | ||
242 | free(buffers); | ||
243 | } | ||
244 | *num_buffers = 0; | ||
245 | return 0; | ||
246 | } | 226 | } |
247 | 227 | ||
248 | /// Load tangent buffers. | 228 | /// Load tangent buffers. |
249 | static Buffer** load_tangent_buffers( | 229 | static bool load_tangent_buffers( |
250 | const cgltfTangentBuffer* tangent_buffers, cgltf_size num_tangent_buffers, | 230 | const cgltfTangentBuffer* cgltf_tangent_buffers, |
251 | RenderBackend* render_backend) { | 231 | cgltf_size num_tangent_buffers, RenderBackend* render_backend, |
252 | assert(tangent_buffers); | 232 | Buffer** tangent_buffers) { |
233 | assert(cgltf_tangent_buffers); | ||
253 | assert(render_backend); | 234 | assert(render_backend); |
254 | 235 | assert(tangent_buffers); | |
255 | Buffer** buffers = 0; | ||
256 | |||
257 | buffers = calloc(num_tangent_buffers, sizeof(Buffer*)); | ||
258 | if (!buffers) { | ||
259 | goto cleanup; | ||
260 | } | ||
261 | 236 | ||
262 | for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { | 237 | for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { |
263 | const cgltfTangentBuffer* buffer = &tangent_buffers[i]; | 238 | const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; |
264 | assert(buffer->data); | 239 | assert(buffer->data); |
265 | buffers[i] = | 240 | tangent_buffers[i] = |
266 | gfx_make_buffer(render_backend, buffer->data, buffer->size_bytes); | 241 | gfx_make_buffer(render_backend, buffer->data, buffer->size_bytes); |
267 | if (!buffers[i]) { | 242 | if (!tangent_buffers[i]) { |
268 | goto cleanup; | 243 | return false; |
269 | } | 244 | } |
270 | } | 245 | } |
271 | 246 | ||
272 | return buffers; | 247 | return true; |
273 | |||
274 | cleanup: | ||
275 | if (buffers) { | ||
276 | for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { | ||
277 | if (buffers[i]) { | ||
278 | gfx_destroy_buffer(render_backend, &buffers[i]); | ||
279 | } | ||
280 | } | ||
281 | free(buffers); | ||
282 | } | ||
283 | return 0; | ||
284 | } | 248 | } |
285 | 249 | ||
286 | /// Lazily load all textures from the glTF scene. | 250 | /// Lazily load all textures from the glTF scene. |
@@ -294,24 +258,13 @@ cleanup: | |||
294 | /// we know their colour space when loading glTF materials. | 258 | /// we know their colour space when loading glTF materials. |
295 | /// | 259 | /// |
296 | /// Return an array of LoadTextureCmds such that the index of each cmd matches | 260 | /// Return an array of LoadTextureCmds such that the index of each cmd matches |
297 | /// the index of each glTF texture in the scene. Also return the number of | 261 | /// the index of each glTF texture in the scene. |
298 | /// textures. | 262 | static void load_textures_lazy( |
299 | /// | ||
300 | /// Return true on success (all textures processed or no textures in the | ||
301 | /// scene), false otherwise. | ||
302 | static bool load_textures_lazy( | ||
303 | const cgltf_data* data, RenderBackend* render_backend, | 263 | const cgltf_data* data, RenderBackend* render_backend, |
304 | const char* directory, LoadTextureCmd** load_texture_cmds, | 264 | const char* directory, LoadTextureCmd* load_texture_cmds) { |
305 | cgltf_size* num_textures) { | ||
306 | assert(data); | 265 | assert(data); |
307 | assert(render_backend); | 266 | assert(render_backend); |
308 | assert(load_texture_cmds); | 267 | assert(load_texture_cmds); |
309 | assert(num_textures); | ||
310 | |||
311 | *load_texture_cmds = calloc(data->textures_count, sizeof(LoadTextureCmd)); | ||
312 | if (!*load_texture_cmds) { | ||
313 | goto cleanup; | ||
314 | } | ||
315 | 268 | ||
316 | for (cgltf_size i = 0; i < data->textures_count; ++i) { | 269 | for (cgltf_size i = 0; i < data->textures_count; ++i) { |
317 | const cgltf_texture* texture = &data->textures[i]; | 270 | const cgltf_texture* texture = &data->textures[i]; |
@@ -359,7 +312,7 @@ static bool load_textures_lazy( | |||
359 | mstring fullpath = | 312 | mstring fullpath = |
360 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); | 313 | mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); |
361 | 314 | ||
362 | (*load_texture_cmds)[i] = (LoadTextureCmd){ | 315 | load_texture_cmds[i] = (LoadTextureCmd){ |
363 | .origin = TextureFromFile, | 316 | .origin = TextureFromFile, |
364 | .type = LoadTexture, | 317 | .type = LoadTexture, |
365 | .colour_space = sRGB, | 318 | .colour_space = sRGB, |
@@ -368,16 +321,6 @@ static bool load_textures_lazy( | |||
368 | .mipmaps = mipmaps, | 321 | .mipmaps = mipmaps, |
369 | .data.texture.filepath = fullpath}; | 322 | .data.texture.filepath = fullpath}; |
370 | } | 323 | } |
371 | |||
372 | *num_textures = data->textures_count; | ||
373 | return true; | ||
374 | |||
375 | cleanup: | ||
376 | if (*load_texture_cmds) { | ||
377 | free(*load_texture_cmds); | ||
378 | } | ||
379 | *num_textures = 0; | ||
380 | return false; | ||
381 | } | 324 | } |
382 | 325 | ||
383 | /// Load a texture uniform. | 326 | /// Load a texture uniform. |
@@ -438,25 +381,15 @@ static bool load_texture_and_uniform( | |||
438 | /// Return an array of Materials such that the index of each descriptor matches | 381 | /// Return an array of Materials such that the index of each descriptor matches |
439 | /// the index of each glTF material in the scene. Also return the number of | 382 | /// the index of each glTF material in the scene. Also return the number of |
440 | /// materials and the textures used by them. | 383 | /// materials and the textures used by them. |
441 | static Material** load_materials( | 384 | static bool load_materials( |
442 | const cgltf_data* data, RenderBackend* render_backend, | 385 | const cgltf_data* data, RenderBackend* render_backend, |
443 | LoadTextureCmd* load_texture_cmds, Texture*** textures, | 386 | LoadTextureCmd* load_texture_cmds, Texture** textures, |
444 | cgltf_size* num_materials) { | 387 | Material** materials) { |
445 | assert(data); | 388 | assert(data); |
446 | assert(render_backend); | 389 | assert(render_backend); |
447 | assert(load_texture_cmds); | 390 | assert(load_texture_cmds); |
448 | assert(textures); | 391 | assert(textures); |
449 | assert(num_materials); | 392 | assert(materials); |
450 | |||
451 | Material** materials = calloc(data->materials_count, sizeof(Material*)); | ||
452 | if (!materials) { | ||
453 | goto cleanup; | ||
454 | } | ||
455 | |||
456 | *textures = calloc(data->textures_count, sizeof(Texture*)); | ||
457 | if (!*textures) { | ||
458 | goto cleanup; | ||
459 | } | ||
460 | 393 | ||
461 | for (cgltf_size i = 0; i < data->materials_count; ++i) { | 394 | for (cgltf_size i = 0; i < data->materials_count; ++i) { |
462 | const cgltf_material* mat = &data->materials[i]; | 395 | const cgltf_material* mat = &data->materials[i]; |
@@ -495,18 +428,18 @@ static Material** load_materials( | |||
495 | if (pbr->base_color_texture.texture) { | 428 | if (pbr->base_color_texture.texture) { |
496 | if (!load_texture_and_uniform( | 429 | if (!load_texture_and_uniform( |
497 | data, render_backend, &pbr->base_color_texture, | 430 | data, render_backend, &pbr->base_color_texture, |
498 | BaseColorTexture, *textures, load_texture_cmds, &next_uniform, | 431 | BaseColorTexture, textures, load_texture_cmds, &next_uniform, |
499 | &desc)) { | 432 | &desc)) { |
500 | goto cleanup; | 433 | return false; |
501 | } | 434 | } |
502 | } | 435 | } |
503 | 436 | ||
504 | if (pbr->metallic_roughness_texture.texture) { | 437 | if (pbr->metallic_roughness_texture.texture) { |
505 | if (!load_texture_and_uniform( | 438 | if (!load_texture_and_uniform( |
506 | data, render_backend, &pbr->metallic_roughness_texture, | 439 | data, render_backend, &pbr->metallic_roughness_texture, |
507 | MetallicRoughnessTexture, *textures, load_texture_cmds, | 440 | MetallicRoughnessTexture, textures, load_texture_cmds, |
508 | &next_uniform, &desc)) { | 441 | &next_uniform, &desc)) { |
509 | goto cleanup; | 442 | return false; |
510 | } | 443 | } |
511 | } | 444 | } |
512 | } | 445 | } |
@@ -514,25 +447,25 @@ static Material** load_materials( | |||
514 | if (mat->emissive_texture.texture) { | 447 | if (mat->emissive_texture.texture) { |
515 | if (!load_texture_and_uniform( | 448 | if (!load_texture_and_uniform( |
516 | data, render_backend, &mat->emissive_texture, EmissiveTexture, | 449 | data, render_backend, &mat->emissive_texture, EmissiveTexture, |
517 | *textures, load_texture_cmds, &next_uniform, &desc)) { | 450 | textures, load_texture_cmds, &next_uniform, &desc)) { |
518 | goto cleanup; | 451 | return false; |
519 | } | 452 | } |
520 | } | 453 | } |
521 | 454 | ||
522 | if (mat->occlusion_texture.texture) { | 455 | if (mat->occlusion_texture.texture) { |
523 | if (!load_texture_and_uniform( | 456 | if (!load_texture_and_uniform( |
524 | data, render_backend, &mat->occlusion_texture, | 457 | data, render_backend, &mat->occlusion_texture, |
525 | AmbientOcclusionTexture, *textures, load_texture_cmds, | 458 | AmbientOcclusionTexture, textures, load_texture_cmds, |
526 | &next_uniform, &desc)) { | 459 | &next_uniform, &desc)) { |
527 | goto cleanup; | 460 | return false; |
528 | } | 461 | } |
529 | } | 462 | } |
530 | 463 | ||
531 | if (mat->normal_texture.texture) { | 464 | if (mat->normal_texture.texture) { |
532 | if (!load_texture_and_uniform( | 465 | if (!load_texture_and_uniform( |
533 | data, render_backend, &mat->normal_texture, NormalMap, *textures, | 466 | data, render_backend, &mat->normal_texture, NormalMap, textures, |
534 | load_texture_cmds, &next_uniform, &desc)) { | 467 | load_texture_cmds, &next_uniform, &desc)) { |
535 | goto cleanup; | 468 | return false; |
536 | } | 469 | } |
537 | } | 470 | } |
538 | 471 | ||
@@ -541,42 +474,20 @@ static Material** load_materials( | |||
541 | 474 | ||
542 | materials[i] = gfx_make_material(&desc); | 475 | materials[i] = gfx_make_material(&desc); |
543 | if (!materials[i]) { | 476 | if (!materials[i]) { |
544 | goto cleanup; | 477 | return false; |
545 | } | 478 | } |
546 | } | 479 | } |
547 | 480 | ||
548 | *num_materials = data->materials_count; | 481 | return true; |
549 | return materials; | ||
550 | |||
551 | cleanup: | ||
552 | if (materials) { | ||
553 | for (cgltf_size i = 0; i < data->materials_count; ++i) { | ||
554 | if (materials[i]) { | ||
555 | gfx_destroy_material(&materials[i]); | ||
556 | } | ||
557 | } | ||
558 | free(materials); | ||
559 | } | ||
560 | if (*textures) { | ||
561 | for (cgltf_size i = 0; i < data->textures_count; ++i) { | ||
562 | if ((*textures)[i]) { | ||
563 | gfx_destroy_texture(render_backend, &(*textures)[i]); | ||
564 | } | ||
565 | } | ||
566 | free(*textures); | ||
567 | *textures = 0; | ||
568 | } | ||
569 | *num_materials = 0; | ||
570 | return 0; | ||
571 | } | 482 | } |
572 | 483 | ||
573 | /// Load all meshes from the glTF scene. | 484 | /// Load all meshes from the glTF scene. |
574 | static SceneObject** load_meshes( | 485 | static bool load_meshes( |
575 | const cgltf_data* data, Gfx* gfx, Buffer** buffers, | 486 | const cgltf_data* data, Gfx* gfx, Buffer** buffers, |
576 | Buffer** tangent_buffers, const cgltfTangentBuffer* cgltf_tangent_buffers, | 487 | Buffer** tangent_buffers, const cgltfTangentBuffer* cgltf_tangent_buffers, |
577 | cgltf_size num_tangent_buffers, Material** materials, ShaderProgram* shader, | 488 | cgltf_size num_tangent_buffers, Material** materials, ShaderProgram* shader, |
578 | Geometry*** geometries, Mesh*** meshes, cgltf_size* num_geometries, | 489 | size_t primitive_count, Geometry** geometries, Mesh** meshes, |
579 | cgltf_size* num_meshes, cgltf_size* num_scene_objects) { | 490 | SceneObject** scene_objects) { |
580 | // Walk through the mesh primitives to create Meshes. A GLTF mesh primitive | 491 | // Walk through the mesh primitives to create Meshes. A GLTF mesh primitive |
581 | // has a material (Mesh) and vertex data (Geometry). A GLTF mesh maps to | 492 | // has a material (Mesh) and vertex data (Geometry). A GLTF mesh maps to |
582 | // a SceneObject. | 493 | // a SceneObject. |
@@ -594,9 +505,7 @@ static SceneObject** load_meshes( | |||
594 | assert(shader); | 505 | assert(shader); |
595 | assert(geometries); | 506 | assert(geometries); |
596 | assert(meshes); | 507 | assert(meshes); |
597 | assert(num_geometries); | 508 | assert(scene_objects); |
598 | assert(num_meshes); | ||
599 | assert(num_scene_objects); | ||
600 | if (num_tangent_buffers > 0) { | 509 | if (num_tangent_buffers > 0) { |
601 | assert(tangent_buffers); | 510 | assert(tangent_buffers); |
602 | assert(cgltf_tangent_buffers); | 511 | assert(cgltf_tangent_buffers); |
@@ -605,21 +514,6 @@ static SceneObject** load_meshes( | |||
605 | // TODO: pass render_backend as argument instead of gfx. | 514 | // TODO: pass render_backend as argument instead of gfx. |
606 | RenderBackend* render_backend = gfx_get_render_backend(gfx); | 515 | RenderBackend* render_backend = gfx_get_render_backend(gfx); |
607 | 516 | ||
608 | const size_t primitive_count = get_total_primitives(data); | ||
609 | |||
610 | *geometries = calloc(primitive_count, sizeof(Geometry*)); | ||
611 | if (!*geometries) { | ||
612 | goto cleanup; | ||
613 | } | ||
614 | *meshes = calloc(primitive_count, sizeof(Mesh*)); | ||
615 | if (!*meshes) { | ||
616 | goto cleanup; | ||
617 | } | ||
618 | SceneObject** objects = calloc(data->meshes_count, sizeof(SceneObject*)); | ||
619 | if (!objects) { | ||
620 | goto cleanup; | ||
621 | } | ||
622 | |||
623 | // Points to the next available Mesh and also the next available Geometry. | 517 | // Points to the next available Mesh and also the next available Geometry. |
624 | // There is one (Mesh, Geometry) pair per glTF mesh primitive. | 518 | // There is one (Mesh, Geometry) pair per glTF mesh primitive. |
625 | size_t next_mesh = 0; | 519 | size_t next_mesh = 0; |
@@ -627,9 +521,9 @@ static SceneObject** load_meshes( | |||
627 | for (cgltf_size m = 0; m < data->meshes_count; ++m) { | 521 | for (cgltf_size m = 0; m < data->meshes_count; ++m) { |
628 | const cgltf_mesh* mesh = &data->meshes[m]; | 522 | const cgltf_mesh* mesh = &data->meshes[m]; |
629 | 523 | ||
630 | objects[m] = gfx_make_object(); | 524 | scene_objects[m] = gfx_make_object(); |
631 | if (!objects[m]) { | 525 | if (!scene_objects[m]) { |
632 | goto cleanup; | 526 | return false; |
633 | } | 527 | } |
634 | 528 | ||
635 | for (cgltf_size p = 0; p < mesh->primitives_count; ++p) { | 529 | for (cgltf_size p = 0; p < mesh->primitives_count; ++p) { |
@@ -689,7 +583,7 @@ static SceneObject** load_meshes( | |||
689 | "Unhandled accessor type %d in vertex positions", | 583 | "Unhandled accessor type %d in vertex positions", |
690 | accessor->type); | 584 | accessor->type); |
691 | assert(false); | 585 | assert(false); |
692 | break; | 586 | return false; |
693 | } | 587 | } |
694 | // It is assumed that meshes have positions, so there is nothing to | 588 | // It is assumed that meshes have positions, so there is nothing to |
695 | // do for the mesh permutation in this case. | 589 | // do for the mesh permutation in this case. |
@@ -776,16 +670,9 @@ static SceneObject** load_meshes( | |||
776 | assert(material_index < data->materials_count); | 670 | assert(material_index < data->materials_count); |
777 | Material* material = materials[material_index]; | 671 | Material* material = materials[material_index]; |
778 | 672 | ||
779 | // TODO: We need a better way to handle clean-up, specifically of | 673 | geometries[next_mesh] = gfx_make_geometry(render_backend, &geometry_desc); |
780 | // materials. One is to add materials to a dynamically-allocated list or | 674 | if (!geometries[next_mesh]) { |
781 | // vector. Another is to expose some kind of scene purge that deletes the | 675 | return false; |
782 | // resources of a given scene. The latter would make clean-up much simpler | ||
783 | // in general, not just for materials. | ||
784 | |||
785 | (*geometries)[next_mesh] = | ||
786 | gfx_make_geometry(render_backend, &geometry_desc); | ||
787 | if (!(*geometries)[next_mesh]) { | ||
788 | goto cleanup; | ||
789 | } | 676 | } |
790 | 677 | ||
791 | // If the user specifies a custom shader, use that instead. | 678 | // If the user specifies a custom shader, use that instead. |
@@ -802,65 +689,31 @@ static SceneObject** load_meshes( | |||
802 | // swapping shaders, so shader caching is the most important thing here. | 689 | // swapping shaders, so shader caching is the most important thing here. |
803 | assert(shader); | 690 | assert(shader); |
804 | 691 | ||
805 | (*meshes)[next_mesh] = gfx_make_mesh(&(MeshDesc){ | 692 | meshes[next_mesh] = gfx_make_mesh(&(MeshDesc){ |
806 | .geometry = (*geometries)[next_mesh], | 693 | .geometry = geometries[next_mesh], |
807 | .material = material, | 694 | .material = material, |
808 | .shader = shader}); | 695 | .shader = shader}); |
809 | 696 | ||
810 | gfx_add_object_mesh(objects[m], (*meshes)[next_mesh]); | 697 | if (!meshes[next_mesh]) { |
698 | return false; | ||
699 | } | ||
700 | |||
701 | gfx_add_object_mesh(scene_objects[m], meshes[next_mesh]); | ||
811 | 702 | ||
812 | ++next_mesh; | 703 | ++next_mesh; |
813 | } // glTF mesh primitive / gfx Mesh. | 704 | } // glTF mesh primitive / gfx Mesh. |
814 | } // glTF mesh / gfx SceneObject. | 705 | } // glTF mesh / gfx SceneObject. |
815 | 706 | ||
816 | *num_geometries = primitive_count; | 707 | return true; |
817 | *num_meshes = primitive_count; | ||
818 | *num_scene_objects = data->meshes_count; | ||
819 | return objects; | ||
820 | |||
821 | cleanup: | ||
822 | // Destroy resources, free pointers arrays. | ||
823 | if (*geometries) { | ||
824 | for (size_t i = 0; i < primitive_count; ++i) { | ||
825 | if ((*geometries)[i]) { | ||
826 | gfx_destroy_geometry(render_backend, &(*geometries[i])); | ||
827 | } | ||
828 | } | ||
829 | free(*geometries); | ||
830 | *geometries = 0; | ||
831 | } | ||
832 | if (*meshes) { | ||
833 | for (size_t i = 0; i < primitive_count; ++i) { | ||
834 | if ((*meshes[i])) { | ||
835 | gfx_destroy_mesh(&(*meshes)[i]); | ||
836 | } | ||
837 | } | ||
838 | free(*meshes); | ||
839 | *meshes = 0; | ||
840 | } | ||
841 | if (objects) { | ||
842 | for (cgltf_size i = 0; i < data->meshes_count; ++i) { | ||
843 | if (objects[i]) { | ||
844 | gfx_destroy_object(&objects[i]); | ||
845 | } | ||
846 | } | ||
847 | free(objects); | ||
848 | objects = 0; | ||
849 | } | ||
850 | *num_geometries = 0; | ||
851 | *num_meshes = 0; | ||
852 | *num_scene_objects = 0; | ||
853 | return 0; | ||
854 | } | 708 | } |
855 | 709 | ||
856 | /// Load all nodes from the glTF scene. | 710 | /// Load all nodes from the glTF scene. |
857 | /// | 711 | /// |
858 | /// This function ignores the many scenes and default scene of the glTF spec | 712 | /// This function ignores the many scenes and default scene of the glTF spec |
859 | /// and instead just loads all nodes into a single gfx Scene. | 713 | /// and instead just loads all nodes into a single gfx Scene. |
860 | static SceneNode** load_nodes( | 714 | static void load_nodes( |
861 | const cgltf_data* data, Gfx* gfx, SceneNode* root_node, | 715 | const cgltf_data* data, Gfx* gfx, SceneNode* root_node, |
862 | SceneObject** scene_objects, SceneCamera*** cameras, | 716 | SceneObject** objects, SceneCamera** cameras, SceneNode** nodes) { |
863 | cgltf_size* num_cameras, cgltf_size* num_nodes) { | ||
864 | // Note that with glTF 2.0, nodes do not form a DAG / scene graph but a | 717 | // Note that with glTF 2.0, nodes do not form a DAG / scene graph but a |
865 | // disjount union of strict trees: | 718 | // disjount union of strict trees: |
866 | // | 719 | // |
@@ -876,19 +729,9 @@ static SceneNode** load_nodes( | |||
876 | // TODO: gfx not needed? | 729 | // TODO: gfx not needed? |
877 | assert(gfx); | 730 | assert(gfx); |
878 | assert(root_node); | 731 | assert(root_node); |
879 | assert(scene_objects); | 732 | assert(objects); |
880 | assert(cameras); | 733 | assert(cameras); |
881 | assert(num_cameras); | 734 | assert(nodes); |
882 | assert(num_nodes); | ||
883 | |||
884 | SceneNode** nodes = calloc(data->nodes_count, sizeof(SceneNode**)); | ||
885 | if (!nodes) { | ||
886 | goto cleanup; | ||
887 | } | ||
888 | *cameras = calloc(data->cameras_count, sizeof(SceneCamera**)); | ||
889 | if (!*cameras) { | ||
890 | goto cleanup; | ||
891 | } | ||
892 | 735 | ||
893 | cgltf_size next_camera = 0; | 736 | cgltf_size next_camera = 0; |
894 | 737 | ||
@@ -902,7 +745,7 @@ static SceneNode** load_nodes( | |||
902 | if (node->mesh) { | 745 | if (node->mesh) { |
903 | const cgltf_size mesh_index = node->mesh - data->meshes; | 746 | const cgltf_size mesh_index = node->mesh - data->meshes; |
904 | assert(mesh_index < data->meshes_count); | 747 | assert(mesh_index < data->meshes_count); |
905 | nodes[n] = gfx_make_object_node(scene_objects[mesh_index]); | 748 | nodes[n] = gfx_make_object_node(objects[mesh_index]); |
906 | } else if (node->camera) { | 749 | } else if (node->camera) { |
907 | assert(next_camera < data->cameras_count); | 750 | assert(next_camera < data->cameras_count); |
908 | 751 | ||
@@ -927,8 +770,8 @@ static SceneNode** load_nodes( | |||
927 | break; | 770 | break; |
928 | } | 771 | } |
929 | 772 | ||
930 | gfx_set_camera_camera((*cameras)[next_camera], &camera); | 773 | gfx_set_camera_camera(cameras[next_camera], &camera); |
931 | nodes[n] = gfx_make_camera_node((*cameras)[next_camera]); | 774 | nodes[n] = gfx_make_camera_node(cameras[next_camera]); |
932 | ++next_camera; | 775 | ++next_camera; |
933 | } else { | 776 | } else { |
934 | continue; // TODO: implementation for missing node types. | 777 | continue; // TODO: implementation for missing node types. |
@@ -977,32 +820,6 @@ static SceneNode** load_nodes( | |||
977 | gfx_set_node_parent(child_scene_node, parent_scene_node); | 820 | gfx_set_node_parent(child_scene_node, parent_scene_node); |
978 | } | 821 | } |
979 | } | 822 | } |
980 | |||
981 | *num_cameras = data->cameras_count; | ||
982 | *num_nodes = data->nodes_count; | ||
983 | return nodes; | ||
984 | |||
985 | cleanup: | ||
986 | // Destroy resources, free pointers arrays. | ||
987 | if (nodes) { | ||
988 | for (cgltf_size i = 0; i < data->nodes_count; ++i) { | ||
989 | if (nodes[i]) { | ||
990 | gfx_destroy_node(&nodes[i]); | ||
991 | } | ||
992 | } | ||
993 | free(nodes); | ||
994 | } | ||
995 | if (cameras) { | ||
996 | for (cgltf_size i = 0; i < data->cameras_count; ++i) { | ||
997 | if (cameras[i]) { | ||
998 | gfx_destroy_camera(&(*cameras)[i]); | ||
999 | } | ||
1000 | } | ||
1001 | free(cameras); | ||
1002 | } | ||
1003 | *num_cameras = 0; | ||
1004 | *num_nodes = 0; | ||
1005 | return 0; | ||
1006 | } | 823 | } |
1007 | 824 | ||
1008 | /// Load all scenes from the glTF file into the given gfx Scene. | 825 | /// Load all scenes from the glTF file into the given gfx Scene. |
@@ -1015,143 +832,167 @@ static bool load_scene( | |||
1015 | cgltf_data* data, Gfx* gfx, SceneNode* root_node, const char* filepath, | 832 | cgltf_data* data, Gfx* gfx, SceneNode* root_node, const char* filepath, |
1016 | ShaderProgram* shader, const cgltfTangentBuffer* cgltf_tangent_buffers, | 833 | ShaderProgram* shader, const cgltfTangentBuffer* cgltf_tangent_buffers, |
1017 | cgltf_size num_tangent_buffers) { | 834 | cgltf_size num_tangent_buffers) { |
1018 | /// In a GLTF scene, buffers can be shared among meshes, meshes among nodes, | 835 | // In a GLTF scene, buffers can be shared among meshes, meshes among nodes, |
1019 | /// etc. Each object is referenced by its index in the relevant array. Here we | 836 | // etc. Each object is referenced by its index in the relevant array. Here we |
1020 | /// do a button-up construction, first allocating our own graphics objects in | 837 | // do a button-up construction, first allocating our own graphics objects in |
1021 | /// the same quantities and then re-using the GLTF indices to index these | 838 | // the same quantities and then re-using the GLTF indices to index these |
1022 | /// arrays. | 839 | // arrays. |
840 | // | ||
841 | // For simplicity, this function also handles all of the cleanup. Arrays are | ||
842 | // allocated up front, and the helper functions construct their elements. If | ||
843 | // an error is encountered, the helper functions can simply return and this | ||
844 | // function cleans up any intermediate objects that had been created up until | ||
845 | // the point of failure. | ||
1023 | assert(data); | 846 | assert(data); |
1024 | assert(gfx); | 847 | assert(gfx); |
1025 | assert(root_node); | 848 | assert(root_node); |
1026 | 849 | ||
1027 | RenderBackend* render_backend = gfx_get_render_backend(gfx); | 850 | RenderBackend* render_backend = gfx_get_render_backend(gfx); |
851 | const size_t primitive_count = get_total_primitives(data); | ||
1028 | 852 | ||
1029 | const mstring directory = mstring_dirname(mstring_make(filepath)); | 853 | const mstring directory = mstring_dirname(mstring_make(filepath)); |
1030 | LOGD("Filepath: %s", filepath); | 854 | LOGD("Filepath: %s", filepath); |
1031 | LOGD("Directory: %s", mstring_cstring(&directory)); | 855 | LOGD("Directory: %s", mstring_cstring(&directory)); |
1032 | 856 | ||
1033 | Buffer** buffers = 0; | ||
1034 | Buffer** tangent_buffers = 0; | 857 | Buffer** tangent_buffers = 0; |
1035 | Geometry** geometries = 0; | 858 | Buffer** buffers = 0; |
859 | LoadTextureCmd* load_texture_cmds = 0; | ||
860 | Texture** textures = 0; | ||
1036 | Material** materials = 0; | 861 | Material** materials = 0; |
862 | Geometry** geometries = 0; | ||
1037 | Mesh** meshes = 0; | 863 | Mesh** meshes = 0; |
1038 | SceneObject** scene_objects = 0; | 864 | SceneObject** scene_objects = 0; |
1039 | SceneCamera** scene_cameras = 0; | 865 | SceneCamera** scene_cameras = 0; |
1040 | SceneNode** scene_nodes = 0; | 866 | SceneNode** scene_nodes = 0; |
1041 | Texture** textures = 0; | 867 | |
1042 | LoadTextureCmd* load_texture_cmds = 0; | 868 | tangent_buffers = calloc(num_tangent_buffers, sizeof(Buffer*)); |
1043 | cgltf_size num_buffers = 0; | 869 | buffers = calloc(data->buffers_count, sizeof(Buffer*)); |
1044 | cgltf_size num_geometries = 0; | 870 | textures = calloc(data->textures_count, sizeof(Texture*)); |
1045 | cgltf_size num_materials = 0; | 871 | materials = calloc(data->materials_count, sizeof(Material*)); |
1046 | cgltf_size num_meshes = 0; | 872 | geometries = calloc(primitive_count, sizeof(Geometry*)); |
1047 | cgltf_size num_scene_objects = 0; | 873 | meshes = calloc(primitive_count, sizeof(Mesh*)); |
1048 | cgltf_size num_scene_cameras = 0; | 874 | scene_objects = calloc(data->meshes_count, sizeof(SceneObject*)); |
1049 | cgltf_size num_scene_nodes = 0; | 875 | scene_cameras = calloc(data->cameras_count, sizeof(SceneCamera**)); |
1050 | cgltf_size num_textures = 0; | 876 | scene_nodes = calloc(data->nodes_count, sizeof(SceneNode**)); |
1051 | 877 | // A glTF scene does not necessarily have textures. Materials can be given | |
1052 | // TODO: Let this function handle all the cleanup. Let the other functions | 878 | // as constants, for example. |
1053 | // return pass/failure booleans and the arrays as in/out parameters. This way | 879 | if (data->textures_count > 0) { |
1054 | // we do not need the individual functions to duplicate cleanup code. | 880 | load_texture_cmds = calloc(data->textures_count, sizeof(LoadTextureCmd)); |
1055 | buffers = load_buffers(data, render_backend, &num_buffers); | ||
1056 | if (!buffers) { | ||
1057 | goto cleanup; | ||
1058 | } | 881 | } |
1059 | 882 | ||
1060 | if (num_tangent_buffers > 0) { | 883 | if (!buffers || !tangent_buffers || |
1061 | tangent_buffers = load_tangent_buffers( | 884 | ((data->textures_count > 0) && !load_texture_cmds) || !textures || |
1062 | cgltf_tangent_buffers, num_tangent_buffers, render_backend); | 885 | !materials || !geometries || !meshes || !scene_objects || |
1063 | if (!tangent_buffers) { | 886 | !scene_cameras || !scene_nodes) { |
1064 | goto cleanup; | 887 | goto cleanup; |
1065 | } | ||
1066 | } | 888 | } |
1067 | 889 | ||
1068 | if (!load_textures_lazy( | 890 | if ((num_tangent_buffers > 0) && |
1069 | data, render_backend, mstring_cstring(&directory), &load_texture_cmds, | 891 | !load_tangent_buffers( |
1070 | &num_textures)) { | 892 | cgltf_tangent_buffers, num_tangent_buffers, render_backend, |
893 | tangent_buffers)) { | ||
1071 | goto cleanup; | 894 | goto cleanup; |
1072 | } | 895 | } |
1073 | 896 | ||
1074 | materials = load_materials( | 897 | if (!load_buffers(data, render_backend, buffers)) { |
1075 | data, render_backend, load_texture_cmds, &textures, &num_materials); | ||
1076 | if (!materials) { | ||
1077 | goto cleanup; | 898 | goto cleanup; |
1078 | } | 899 | } |
1079 | 900 | ||
1080 | scene_objects = load_meshes( | 901 | load_textures_lazy( |
1081 | data, gfx, buffers, tangent_buffers, cgltf_tangent_buffers, | 902 | data, render_backend, mstring_cstring(&directory), load_texture_cmds); |
1082 | num_tangent_buffers, materials, shader, &geometries, &meshes, | 903 | |
1083 | &num_geometries, &num_meshes, &num_scene_objects); | 904 | if (!load_materials( |
1084 | if (!scene_objects) { | 905 | data, render_backend, load_texture_cmds, textures, materials)) { |
1085 | goto cleanup; | 906 | goto cleanup; |
1086 | } | 907 | } |
1087 | 908 | ||
1088 | scene_nodes = load_nodes( | 909 | if (!load_meshes( |
1089 | data, gfx, root_node, scene_objects, &scene_cameras, &num_scene_cameras, | 910 | data, gfx, buffers, tangent_buffers, cgltf_tangent_buffers, |
1090 | &num_scene_nodes); | 911 | num_tangent_buffers, materials, shader, primitive_count, geometries, |
1091 | if (!scene_nodes) { | 912 | meshes, scene_objects)) { |
1092 | goto cleanup; | 913 | goto cleanup; |
1093 | } | 914 | } |
1094 | 915 | ||
916 | load_nodes(data, gfx, root_node, scene_objects, scene_cameras, scene_nodes); | ||
917 | |||
1095 | return true; | 918 | return true; |
1096 | 919 | ||
1097 | cleanup: | 920 | cleanup: |
1098 | if (buffers) { | ||
1099 | for (cgltf_size i = 0; i < num_buffers; ++i) { | ||
1100 | gfx_destroy_buffer(render_backend, &buffers[i]); | ||
1101 | } | ||
1102 | free(buffers); | ||
1103 | } | ||
1104 | if (tangent_buffers) { | 921 | if (tangent_buffers) { |
1105 | for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { | 922 | for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { |
1106 | gfx_destroy_buffer(render_backend, &tangent_buffers[i]); | 923 | if (tangent_buffers[i]) { |
924 | gfx_destroy_buffer(render_backend, &tangent_buffers[i]); | ||
925 | } | ||
1107 | } | 926 | } |
1108 | free(tangent_buffers); | 927 | free(tangent_buffers); |
1109 | } | 928 | } |
929 | if (buffers) { | ||
930 | for (cgltf_size i = 0; i < data->buffers_count; ++i) { | ||
931 | if (buffers[i]) { | ||
932 | gfx_destroy_buffer(render_backend, &buffers[i]); | ||
933 | } | ||
934 | } | ||
935 | free(buffers); | ||
936 | } | ||
937 | if (load_texture_cmds) { | ||
938 | free(load_texture_cmds); | ||
939 | } | ||
1110 | if (textures) { | 940 | if (textures) { |
1111 | for (cgltf_size i = 0; i < num_textures; ++i) { | 941 | for (cgltf_size i = 0; i < data->textures_count; ++i) { |
1112 | gfx_destroy_texture(render_backend, &textures[i]); | 942 | if (textures[i]) { |
943 | gfx_destroy_texture(render_backend, &textures[i]); | ||
944 | } | ||
1113 | } | 945 | } |
1114 | free(textures); | 946 | free(textures); |
1115 | } | 947 | } |
1116 | if (materials) { | 948 | if (materials) { |
1117 | for (cgltf_size i = 0; i < num_materials; ++i) { | 949 | for (cgltf_size i = 0; i < data->materials_count; ++i) { |
1118 | gfx_destroy_material(&materials[i]); | 950 | if (materials[i]) { |
951 | gfx_destroy_material(&materials[i]); | ||
952 | } | ||
1119 | } | 953 | } |
1120 | free(materials); | 954 | free(materials); |
1121 | } | 955 | } |
1122 | if (geometries) { | 956 | if (geometries) { |
1123 | for (cgltf_size i = 0; i < num_geometries; ++i) { | 957 | for (size_t i = 0; i < primitive_count; ++i) { |
1124 | gfx_destroy_geometry(render_backend, &geometries[i]); | 958 | if (geometries[i]) { |
959 | gfx_destroy_geometry(render_backend, &geometries[i]); | ||
960 | } | ||
1125 | } | 961 | } |
1126 | free(geometries); | 962 | free(geometries); |
1127 | } | 963 | } |
1128 | if (meshes) { | 964 | if (meshes) { |
1129 | for (cgltf_size i = 0; i < num_meshes; ++i) { | 965 | for (size_t i = 0; i < primitive_count; ++i) { |
1130 | gfx_destroy_mesh(&meshes[i]); | 966 | if (meshes[i]) { |
967 | gfx_destroy_mesh(&meshes[i]); | ||
968 | } | ||
1131 | } | 969 | } |
1132 | free(meshes); | 970 | free(meshes); |
1133 | } | 971 | } |
1134 | if (scene_objects) { | 972 | if (scene_objects) { |
1135 | for (cgltf_size i = 0; i < num_scene_objects; ++i) { | 973 | for (cgltf_size i = 0; i < data->meshes_count; ++i) { |
1136 | gfx_destroy_object(&scene_objects[i]); | 974 | if (scene_objects[i]) { |
975 | gfx_destroy_object(&scene_objects[i]); | ||
976 | } | ||
1137 | } | 977 | } |
1138 | free(scene_objects); | 978 | free(scene_objects); |
1139 | } | 979 | } |
1140 | if (scene_cameras) { | 980 | if (scene_cameras) { |
1141 | for (cgltf_size i = 0; i < num_scene_cameras; ++i) { | 981 | for (cgltf_size i = 0; i < data->cameras_count; ++i) { |
1142 | gfx_destroy_camera(&scene_cameras[i]); | 982 | if (scene_cameras[i]) { |
983 | gfx_destroy_camera(&scene_cameras[i]); | ||
984 | } | ||
1143 | } | 985 | } |
1144 | free(scene_cameras); | 986 | free(scene_cameras); |
1145 | } | 987 | } |
1146 | if (scene_nodes) { | 988 | if (scene_nodes) { |
1147 | for (cgltf_size i = 0; i < num_scene_nodes; ++i) { | 989 | for (cgltf_size i = 0; i < data->nodes_count; ++i) { |
1148 | gfx_destroy_node(&scene_nodes[i]); | 990 | if (scene_nodes[i]) { |
991 | gfx_destroy_node(&scene_nodes[i]); | ||
992 | } | ||
1149 | } | 993 | } |
1150 | free(scene_nodes); | 994 | free(scene_nodes); |
1151 | } | 995 | } |
1152 | if (load_texture_cmds) { | ||
1153 | free(load_texture_cmds); | ||
1154 | } | ||
1155 | return false; | 996 | return false; |
1156 | } | 997 | } |
1157 | 998 | ||