From dbbdcf4be06c05f60798b322890c88b79d802bd8 Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Sat, 11 Feb 2023 09:13:18 -0800
Subject: Create default material for meshes that do not specify one.

---
 gfx/src/util/scene.c | 101 +++++++++++++++++++++++++++++++++++----------------
 1 file changed, 70 insertions(+), 31 deletions(-)

diff --git a/gfx/src/util/scene.c b/gfx/src/util/scene.c
index 49e661d..6f34922 100644
--- a/gfx/src/util/scene.c
+++ b/gfx/src/util/scene.c
@@ -803,13 +803,44 @@ static bool load_materials(
   return true;
 }
 
+/// Create a default material for meshes that do not have a material.
+static Material* make_default_material() {
+  MaterialDesc desc = (MaterialDesc){0};
+
+  assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL);
+  desc.uniforms[desc.num_uniforms++] = (ShaderUniform){
+      .name       = sstring_make(UNIFORM_BASE_COLOR_FACTOR),
+      .type       = UniformVec4,
+      .value.vec4 = vec4_make(1, 1, 1, 1)};
+
+  assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL);
+  desc.uniforms[desc.num_uniforms++] = (ShaderUniform){
+      .name         = sstring_make(UNIFORM_METALLIC_FACTOR),
+      .type         = UniformFloat,
+      .value.scalar = 0};
+
+  assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL);
+  desc.uniforms[desc.num_uniforms++] = (ShaderUniform){
+      .name         = sstring_make(UNIFORM_ROUGHNESS_FACTOR),
+      .type         = UniformFloat,
+      .value.scalar = 1};
+
+  assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL);
+  desc.uniforms[desc.num_uniforms++] = (ShaderUniform){
+      .name       = sstring_make(UNIFORM_EMISSIVE_FACTOR),
+      .type       = UniformVec3,
+      .value.vec3 = vec3_make(0, 0, 0)};
+
+  return gfx_make_material(&desc);
+}
+
 /// Load all meshes from the glTF scene.
 static bool load_meshes(
     const cgltf_data* data, Gfx* gfx, Buffer** buffers,
     Buffer** tangent_buffers, const cgltfTangentBuffer* cgltf_tangent_buffers,
-    cgltf_size num_tangent_buffers, Material** materials, ShaderProgram* shader,
-    size_t primitive_count, Geometry** geometries, Mesh** meshes,
-    SceneObject** scene_objects) {
+    cgltf_size num_tangent_buffers, Material** materials,
+    ShaderProgram* const shader, size_t primitive_count, Geometry** geometries,
+    Mesh** meshes, SceneObject** scene_objects) {
   // Walk through the mesh primitives to create Meshes. A GLTF mesh primitive
   // has a material (Mesh) and vertex data (Geometry). A GLTF mesh maps to
   // a SceneObject.
@@ -852,16 +883,21 @@ static bool load_meshes(
       const cgltf_primitive* prim = &mesh->primitives[p];
       const cgltf_material*  mat  = prim->material;
 
-      MeshPermutation perm   = {0};
-      perm.has_normal_map    = mat->normal_texture.texture != 0;
-      perm.has_occlusion_map = mat->occlusion_texture.texture != 0;
-      perm.has_emissive_map  = mat->emissive_texture.texture != 0;
-      // TODO: specular/glossiness and other material parameters.
-      if (mat->has_pbr_metallic_roughness) {
-        const cgltf_pbr_metallic_roughness* pbr = &mat->pbr_metallic_roughness;
-        perm.has_albedo_map = pbr->base_color_texture.texture != 0;
-        perm.has_metallic_roughness_map =
-            pbr->metallic_roughness_texture.texture != 0;
+      MeshPermutation perm = {0};
+      if (mat) {
+        perm.has_normal_map    = mat->normal_texture.texture != 0;
+        perm.has_occlusion_map = mat->occlusion_texture.texture != 0;
+        perm.has_emissive_map  = mat->emissive_texture.texture != 0;
+
+        if (mat->has_pbr_metallic_roughness) {
+          const cgltf_pbr_metallic_roughness* pbr =
+              &mat->pbr_metallic_roughness;
+          perm.has_albedo_map = pbr->base_color_texture.texture != 0;
+          perm.has_metallic_roughness_map =
+              pbr->metallic_roughness_texture.texture != 0;
+        } else {
+          // TODO: specular/glossiness and other material parameters.
+        }
       }
 
       GeometryDesc geometry_desc = {
@@ -1034,36 +1070,39 @@ static bool load_meshes(
       CHECK_COUNT(weights.u16, uint16_t, 4);
       CHECK_COUNT(weights.floats, float, 4);
 
-      const cgltf_size material_index = prim->material - data->materials;
-      assert(material_index < data->materials_count);
-      Material* material = materials[material_index];
+      Material* material = 0;
+      if (mat) {
+        const cgltf_size material_index = mat - data->materials;
+        assert(material_index < data->materials_count);
+        material = materials[material_index];
+      } else {
+        // Create a default  material for meshes that do not specify one.
+        material = make_default_material();
+      }
+      assert(material);
 
       geometries[next_mesh] = gfx_make_geometry(render_backend, &geometry_desc);
       if (!geometries[next_mesh]) {
         return false;
       }
 
-      // If the user specifies a custom shader, use that instead.
-      // else TODO: Build a shader based on permutation.
+      // If the user specifies a custom shader, use that instead. Otherwise
+      // compile a shader based on the mesh's permutation.
       //
-      // TODO: We should cache shader permutations to re-use shader programs.
-      // Caching should not be done locally here because a caller may call
-      // gfx_load_scene() multiple times to load multiple scenes, and we want
-      // shader re-use across scenes too.
+      // Note that Gfx takes care of caching shaders and shader programs.
       //
-      // On the other hand, caching materials is not necessary since, provided
-      // they can share shaders, the renderer can check later whether uniforms
-      // have the same values. Also, changing uniforms is much faster than
-      // swapping shaders, so shader caching is the most important thing here.
-      if (!shader) {
-        shader = make_shader_permutation(render_backend, perm);
-      }
-      assert(shader);
+      // Caching materials could be useful, but, provided they can share
+      // shaders, the renderer can check later whether uniforms have the same
+      // values. Also, changing uniforms is much faster than swapping shaders,
+      // so shader caching is the most important thing here.
+      ShaderProgram* mesh_shader =
+          shader ? shader : make_shader_permutation(render_backend, perm);
+      assert(mesh_shader);
 
       meshes[next_mesh] = gfx_make_mesh(&(MeshDesc){
           .geometry = geometries[next_mesh],
           .material = material,
-          .shader   = shader});
+          .shader   = mesh_shader});
 
       if (!meshes[next_mesh]) {
         return false;
-- 
cgit v1.2.3