diff options
| -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 | ||
