gltf mesh/material loading, adjust build script

This commit is contained in:
Matthew 2025-11-16 15:31:42 +11:00
parent 10b5c0a127
commit cce005d410
5 changed files with 362 additions and 22 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

313
assets.d
View File

@ -4,6 +4,7 @@ import includes;
import dlib.aliases; import dlib.aliases;
import dlib.util; import dlib.util;
import dlib.alloc; import dlib.alloc;
import dlib.math;
import std.file; import std.file;
import std.path; import std.path;
@ -90,14 +91,13 @@ struct Vertex
Vec4 tangent; Vec4 tangent;
Vec3 pos; Vec3 pos;
Vec3 normal; Vec3 normal;
Vec2 uv; Vec2[2] uv;
Vec2 uv2;
} }
struct Mesh struct Mesh
{ {
Vertex[] vtx; Vertex[] vtx;
u32 idx; u32[] idx;
u32 mat_id; u32 mat_id;
} }
@ -105,6 +105,9 @@ struct Model
{ {
Mat4 transform; Mat4 transform;
Vertex[] vtx_buf;
u32[] idx_buf;
Mesh[] meshes; Mesh[] meshes;
Material[] mats; Material[] mats;
@ -345,6 +348,8 @@ LoadGLTF(Arena* arena, string file_name)
} }
u64 primitive_count; u64 primitive_count;
u64 vtx_count;
u64 idx_count;
for(u64 i = 0; i < data.nodes_count; i += 1) for(u64 i = 0; i < data.nodes_count; i += 1)
{ {
@ -357,10 +362,27 @@ LoadGLTF(Arena* arena, string file_name)
if(node.mesh.primitives[j].type == cgltf_primitive_type_triangles) if(node.mesh.primitives[j].type == cgltf_primitive_type_triangles)
{ {
primitive_count += 1; primitive_count += 1;
if(node.mesh.primitives[j].indices != null && node.mesh.primitives[j].indices.buffer_view != null)
{
idx_count += node.mesh.primitives[j].indices.count;
}
for(u64 k = 0; k < node.mesh.primitives[j].attributes_count; k += 1)
{
if(node.mesh.primitives[j].attributes[k].type == cgltf_attribute_type_position)
{
vtx_count += node.mesh.primitives[j].attributes[k].data.count;
}
}
} }
} }
} }
model.idx_buf = Alloc!(u32)(idx_count);
model.vtx_buf = Alloc!(Vertex)(vtx_count);
model.meshes = Alloc!(Mesh)(primitive_count); model.meshes = Alloc!(Mesh)(primitive_count);
model.mats = Alloc!(Material)(data.materials_count+1); model.mats = Alloc!(Material)(data.materials_count+1);
m_data.tex = Alloc!(ImageData)(data.textures_count+1); m_data.tex = Alloc!(ImageData)(data.textures_count+1);
@ -420,12 +442,297 @@ LoadGLTF(Arena* arena, string file_name)
} }
} }
u64 mesh_index, idx_index;
vtx_count = 0;
for(u64 i = 0; i < data.nodes_count; i += 1)
{
cgltf_node* node = &data.nodes[i];
cgltf_mesh* mesh = node.mesh;
if(mesh == null) continue;
cgltf_float[16] world_transform;
cgltf_node_transform_world(node, world_transform.ptr);
Mat4 world_matrix = Mat4(world_transform);
for(u64 p = 0; p < mesh.primitives_count; p += 1)
{
if(mesh.primitives[p].type != cgltf_primitive_type_triangles)
{
Logf("Unable to process primitive type [%s]", mesh.primitives[p].type);
continue;
}
auto prim = mesh.primitives + p;
for(u64 j = 0; j < prim.attributes_count; j += 1)
{
switch(prim.attributes[j].type)
{
case cgltf_attribute_type_position:
{
cgltf_accessor* attr = prim.attributes[j].data;
if(attr.type == cgltf_type_vec3 && attr.component_type == cgltf_component_type_r_32f)
{
AddMeshVertices(attr, model, mesh_index, &vtx_count);
f32* buffer = GetGLTFBuffer!(f32)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].pos = Vec3(buffer[3*k+0], buffer[3*k+1], buffer[3*k+2]);
Transform(&model.meshes[mesh_index].vtx[k].pos, &world_matrix, 1.0);
}
}
else Logf("Format needs to be Vec3 f32, skipping.");
} break;
case cgltf_attribute_type_normal:
{
cgltf_accessor* attr = prim.attributes[j].data;
if(attr.type == cgltf_type_vec3 && attr.component_type == cgltf_component_type_r_32f)
{
AddMeshVertices(attr, model, mesh_index, &vtx_count);
f32* buffer = GetGLTFBuffer!(f32)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].normal = Vec3(buffer[3*k+0], buffer[3*k+1], buffer[3*k+2]);
Transform(&model.meshes[mesh_index].vtx[k].normal, &world_matrix, 1.0);
}
}
else Logf("Format needs to be Vec3 f32, skipping.");
} break;
case cgltf_attribute_type_tangent:
{
cgltf_accessor* attr = prim.attributes[j].data;
if(attr.type == cgltf_type_vec4 && attr.component_type == cgltf_component_type_r_32f)
{
AddMeshVertices(attr, model, mesh_index, &vtx_count);
f32* buffer = GetGLTFBuffer!(f32)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].tangent = world_matrix * Vec4(buffer[4*k+0], buffer[4*k+1], buffer[4*k+2], buffer[4*k+3]);
}
}
else Logf("Format needs to be Vec4 f32, skipping.");
} break;
case cgltf_attribute_type_texcoord:
{
cgltf_accessor* attr = prim.attributes[j].data;
if(attr.type == cgltf_type_vec2)
{
AddMeshVertices(attr, model, mesh_index, &vtx_count);
u32 uv_idx = prim.attributes[j].index;
if(uv_idx >= 2)
{
Logf("Unable to use more than two uvs, skipping");
continue;
}
if(attr.component_type == cgltf_component_type_r_32f)
{
f32* buffer = GetGLTFBuffer!(f32)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].uv[uv_idx] = Vec2(buffer[2*k+0], buffer[2*k+1]);
}
}
else if(attr.component_type == cgltf_component_type_r_8u)
{
u8* buffer = GetGLTFBuffer!(u8)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].uv[uv_idx] = Vec2(cast(f32)(buffer[2*k+0])/255.0, cast(f32)(buffer[2*k+1])/255.0);
}
}
else if(attr.component_type == cgltf_component_type_r_16u)
{
u16* buffer = GetGLTFBuffer!(u16)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].uv[uv_idx] = Vec2(cast(f32)(buffer[2*k+0])/65535.0, cast(f32)(buffer[2*k+1])/65535.0);
}
}
else
{
Logf("Unsupported format [%s] for uv, skipping", attr.component_type);
}
}
} break;
case cgltf_attribute_type_color:
{
cgltf_accessor* attr = prim.attributes[j].data;
if(attr.type == cgltf_type_vec3 || attr.type == cgltf_type_vec4)
{
AddMeshVertices(attr, model, mesh_index, &vtx_count);
if(attr.type == cgltf_type_vec3)
{
if(attr.component_type == cgltf_component_type_r_8u)
{
u8* buffer = GetGLTFBuffer!(u8)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
cast(f32)(buffer[k*3+0])/255.0,
cast(f32)(buffer[k*3+1])/255.0,
cast(f32)(buffer[k*3+2])/255.0,
1.0
);
}
}
else if(attr.component_type == cgltf_component_type_r_16u)
{
u16* buffer = GetGLTFBuffer!(u16)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
cast(f32)(buffer[k*3+0])/65535.0,
cast(f32)(buffer[k*3+1])/65535.0,
cast(f32)(buffer[k*3+2])/65535.0,
1.0
);
}
}
else if(attr.component_type == cgltf_component_type_r_32f)
{
f32* buffer = GetGLTFBuffer!(f32)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(buffer[k*3+0], buffer[k*3+1], buffer[k*3+2], 1.0);
}
}
else
{
Logf("Color component type [%s] not supported", attr.component_type);
}
}
else if(attr.type == cgltf_type_vec4)
{
if(attr.component_type == cgltf_component_type_r_8u)
{
u8* buffer = GetGLTFBuffer!(u8)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
cast(f32)(buffer[k*4+0])/255.0,
cast(f32)(buffer[k*4+1])/255.0,
cast(f32)(buffer[k*4+2])/255.0,
cast(f32)(buffer[k*4+3])/255.0
);
}
}
else if(attr.component_type == cgltf_component_type_r_16u)
{
u16* buffer = GetGLTFBuffer!(u16)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
cast(f32)(buffer[k*4+0])/65535.0,
cast(f32)(buffer[k*4+1])/65535.0,
cast(f32)(buffer[k*4+2])/65535.0,
cast(f32)(buffer[k*4+3])/65535.0
);
}
}
else if(attr.component_type == cgltf_component_type_r_32f)
{
f32* buffer = GetGLTFBuffer!(f32)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(buffer[k*4+0], buffer[k*4+1], buffer[k*4+2], buffer[k*4+3]);
}
}
else
{
Logf("Color component type [%s] not supported", attr.component_type);
}
}
}
} break;
default: break;
}
}
if(prim.indices != null && prim.indices.buffer_view != null)
{
cgltf_accessor* attr = prim.indices;
model.meshes[mesh_index].idx = model.idx_buf[idx_index .. idx_index+attr.count];
idx_index += attr.count;
if(attr.component_type == cgltf_component_type_r_16u)
{
u16* buffer = GetGLTFBuffer!(u16)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].idx.length; k += 1)
{
model.meshes[mesh_index].idx[k] = cast(u32)buffer[k];
}
}
else if(attr.component_type == cgltf_component_type_r_8u)
{
u8* buffer = GetGLTFBuffer!(u8)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].idx.length; k += 1)
{
model.meshes[mesh_index].idx[k] = cast(u32)buffer[k];
}
}
else if(attr.component_type == cgltf_component_type_r_32u)
{
u32* buffer = GetGLTFBuffer!(u32)(attr);
for(u64 k = 0; k < model.meshes[mesh_index].idx.length; k += 1)
{
model.meshes[mesh_index].idx[k] = buffer[k];
}
}
else Logf("Unsupported index type [%s]", attr.component_type);
}
else // unindexed mesh (may implement later)
{
Logf("Unindexed mesh encountered");
}
for(u32 k = 0; k < data.materials_count; k += 1)
{
if(data.materials + k == prim.material)
{
model.meshes[mesh_index].mat_id = k+1;
break;
}
}
mesh_index += 1;
}
}
} }
return m_data; return m_data;
} }
void
AddMeshVertices(cgltf_accessor* accessor, Model* model, u64 mesh_index, u64* vtx_count)
{
if(model.meshes[mesh_index].vtx == null)
{
model.meshes[mesh_index].vtx = model.vtx_buf[*vtx_count .. *vtx_count+accessor.count];
*vtx_count += accessor.count;
}
}
T*
GetGLTFBuffer(T)(cgltf_accessor* accessor)
{
return cast(T*)(accessor.buffer_view.buffer.data + accessor.buffer_view.offset/T.sizeof + accessor.offset/T.sizeof);
}
static ImageData static ImageData
GenerateDefaultTexture(u32 x, u32 y) GenerateDefaultTexture(u32 x, u32 y)

View File

@ -36,16 +36,16 @@ if ! [ -f "${build}/libstb.a" ]; then
fi fi
# M3D # M3D
src="${script_dir}/external/m3d/m3d.c" #src="${script_dir}/external/m3d/m3d.c"
flags="-std=c99 -Wno-everything -Iexternal/m3d -c -static" #flags="-std=c99 -Wno-everything -Iexternal/m3d -c -static"
obj="${build}/m3d.o" #obj="${build}/m3d.o"
lib="${build}/libm3d.a" #lib="${build}/libm3d.a"
#
if ! [ -f "${build}/libm3d.a" ]; then #if ! [ -f "${build}/libm3d.a" ]; then
$c_compiler $flags $src $out $obj # $c_compiler $flags $src $out $obj
ar rcs $lib $obj # ar rcs $lib $obj
rm $obj # rm $obj
fi #fi
# CGLM # CGLM
src="${script_dir}/external/cglm/cglm.c" src="${script_dir}/external/cglm/cglm.c"

32
math.d
View File

@ -435,6 +435,11 @@ align(16) struct Matrix(T, int D)
} }
} }
this(U)(U x) if(is(U: typeof(this.v)))
{
this.v = x;
}
@property inout(T)* ptr() inout @property inout(T)* ptr() inout
{ {
return v.ptr; return v.ptr;
@ -924,6 +929,14 @@ Inverse(Mat4 mat)
return res; return res;
} }
void
Transform(Vec3* vec, Mat4* mat, f32 last = 1.0)
{
Vec4 tvec = Vec4(*vec, last);
tvec = *mat * tvec;
*vec = tvec.xyz;
}
pragma(inline) f32 pragma(inline) f32
Lerp(f32 x, f32 y, f32 a) Lerp(f32 x, f32 y, f32 a)
{ {
@ -1129,10 +1142,27 @@ unittest
assert(vec == Vec4(1.0, 2.0, 3.0, 4.0)); assert(vec == Vec4(1.0, 2.0, 3.0, 4.0));
Mat2 mat = Mat2(1.0, 0.0, 0.0, 1.0); Mat2 mat = Mat2(1.0, 0.0, 0.0, 1.0);
Mat4 mat4;
f32[16] floats = 22.0;
Mat4 mat4 = Mat4(floats);
assert(mat4.v == floats);
Quat quat = Quat(1.0, 1.0, 1.0, 1.0); Quat quat = Quat(1.0, 1.0, 1.0, 1.0);
Quat quat2; Quat quat2;
} }
{
Vec3 vec = Vec3(1.0, 2.0, 3.0);
Mat4 mat = Mat4(
1.0, 1.0, 1.0, 1.0,
2.0, 2.0, 2.0, 2.0,
3.0, 3.0, 3.0, 3.0,
4.0, 4.0, 4.0, 4.0
);
Transform(&vec, &mat, 1.0);
assert(vec == Vec3(18.0));
}
} }

View File

@ -2,7 +2,9 @@
name="Test_Runner" name="Test_Runner"
ldc2 platform.d aliases.d math.d util.d alloc.d assets.d external/xxhash/xxhash.d -P-I/usr/include/freetype2 -L-lfreetype --main --unittest -g --of=$name /bin/bash ./build.sh build
ldc2 platform.d aliases.d math.d util.d alloc.d assets.d external/xxhash/xxhash.d build/libcglm.a -Xcc=-mno-sse -P-I/usr/include/freetype2 -L-lfreetype --main --unittest -g --of=$name
rm $name.o rm $name.o
./$name ./$name