added model loading

This commit is contained in:
matthew 2025-07-17 08:10:05 +10:00
parent ad08d31f6a
commit d737e1feb1
5 changed files with 325 additions and 28 deletions

View File

@ -1,3 +1,4 @@
.git
build
external
assets

View File

@ -7,7 +7,7 @@
"targetType": "executable",
"targetName": "Gears",
"targetPath": "build",
"sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a"],
"sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a", "build/libm3d.a"],
"sourceFiles-windows": [],
"importPaths": ["src/gears", "src/shared", "src/generated", "external/xxhash"],
"sourcePaths": ["src/gears", "src/shared", "src/generated", "external/xxhash"],

View File

@ -12,7 +12,6 @@ import p = platform;
alias Shader = VkShaderModule;
alias Pipeline = PipelineHandle;
alias Attribute = VkVertexInputAttributeDescription;
alias Buffer = VkBuffer;
enum InputRate : int
{
@ -78,6 +77,8 @@ struct Renderer
Pipeline triangle_pipeline;
Pipeline compute_pipeline;
Pipeline ui_pipeline;
Model yoder;
}
struct GlobalUniforms
@ -90,6 +91,20 @@ struct ShaderUniforms
f32 placeholder;
}
struct Material
{
u32 albedo_texture;
u32 ambient_texture;
u32 specular_texture;
b32 albedo_has_texture;
b32 ambient_has_texture;
b32 specular_has_texture;
Vec4 ambient;
Vec4 diffuse;
Vec4 specular;
f32 shininess = 0.0;
}
struct UIVertex
{
Vec2 p0;
@ -100,8 +115,8 @@ struct UIVertex
struct Vertex
{
Vec3 pos;
Vec3 n;
Vec4 pos;
Vec4 n;
Vec2 uv;
u32 col;
}
@ -122,10 +137,7 @@ struct Model
string name;
Buffer[] materials;
u32[] m_indices;
ImageView[] textures;
u32[] t_indices;
}
Renderer
@ -165,6 +177,8 @@ Init(p.Window* window)
rd.ui_pipeline = BuildGfxPipeline(&rd, &ui_info);
rd.compute_pipeline = BuildCompPipeline(&rd, "shaders/gradient.comp");
rd.yoder = LoadModel(&rd, "models/yoda");
return rd;
}
@ -215,64 +229,216 @@ LoadModel(Renderer* rd, string name)
u8[] data = LoadAssetData(&rd.temp_arena, name);
m3d_t* m3d = m3d_load(data.ptr, null, null, null);
u32[] tex_lookup = AllocArray!(u32)(&rd.temp_arena, m3d.numtexture);
model.textures = AllocArray!(ImageView)(&rd.arena, m3d.numtexture);
foreach(i; 0 .. m3d.numtexture)
{
u32 w = m3d.texture[i].w; u32 h = m3d.texture[i].h; u32 ch = m3d.texture[i].f;
model.textures[i] = CreateImageView(&rd.vk, w, h, ch);
u8[] texture_data = m3d.texture[i].d[0 .. w * h * ch];
assert(Transfer(&rd.vk, &model.textures[i], texture_data, w, h), "LoadModel failure: Texture transfer error");
tex_lookup[i] = Pop(&rd.vk, DT.SampledImage);
}
u32[] mat_lookup = AllocArray!(u32)(&rd.temp_arena, m3d.nummaterial);
model.materials = AllocArray!(Buffer)(&rd.arena, m3d.nummaterial);
foreach(i; 0 .. m3d.nummaterial)
{
Material mat;
foreach(j; 0 .. m3d.material[i].numprop)
{
switch (m3d.material[i].prop[j].type)
{
case m3dp_Ka: ConvertColor(&mat.ambient, m3d.material[i].prop[j].value.color); break;
case m3dp_Ks: ConvertColor(&mat.specular, m3d.material[i].prop[j].value.color); break;
case m3dp_Ns: mat.shininess = m3d.material[i].prop[j].value.fnum; break;
case m3dp_map_Kd:
{
mat.albedo_texture = tex_lookup[m3d.material[i].prop[j].value.textureid];
mat.albedo_has_texture = true;
} break;
case m3dp_map_Ka:
{
mat.ambient_texture = tex_lookup[m3d.material[i].prop[j].value.textureid];
mat.ambient_has_texture = true;
} break;
case m3dp_map_Ks:
{
mat.specular_texture = tex_lookup[m3d.material[i].prop[j].value.textureid];
mat.specular_has_texture = true;
} break;
default: break;
}
}
model.materials[i] = CreateBuffer(&rd.vk, BT.Uniform, Material.sizeof, false);
assert(Transfer(&rd.vk, &model.materials[i], &mat), "LoadModel failure: Transfer error when transferring material");
mat_lookup[i] = Pop(&rd.vk, DT.Material);
}
u32 mesh_count = 0;
u64 last = u64.max;
foreach(i; 0 .. m3d.numface)
{
if (m3d.face[i].materialid != last)
{
last = m3d.face[i].materialid;
mesh_count += 1;
}
}
model.parts = AllocArray!(MeshPart)(&rd.arena, mesh_count);
last = u64.max;
u32 index = 0;
foreach(i; 0 .. m3d.numface)
{
if (last == u64.max)
{
model.parts[index].mat = mat_lookup[m3d.face[i].materialid];
model.parts[index].offset = 0;
last = m3d.face[i].materialid;
}
else if (m3d.face[i].materialid != last)
{
u32 vertex_index = i * 3;
model.parts[index].length = vertex_index - model.parts[index].offset;
index += 1;
model.parts[index].mat = mat_lookup[m3d.face[i].materialid];
model.parts[index].offset = vertex_index;
last = m3d.face[i].materialid;
}
else if (i == m3d.numface-1)
{
u32 vertex_index = i * 3;
model.parts[index].length = vertex_index+3 - model.parts[index].offset;
}
}
m3dv_t* vertex;
u32[] indices = AllocArray!(u32)(&rd.temp_arena, m3d.numface * 3);
Vertex[] vertices = AllocArray!(Vertex)(&rd.temp_arena, m3d.numface * 3);
foreach(i; 0 .. m3d.numface)
{
u32 vi = (i * 3);
CopyVertex(&vertices[vi+0].pos, &m3d.vertex[m3d.face[i].vertex[0]]);
CopyVertex(&vertices[vi+1].pos, &m3d.vertex[m3d.face[i].vertex[1]]);
CopyVertex(&vertices[vi+2].pos, &m3d.vertex[m3d.face[i].vertex[2]]);
CopyVertex(&vertices[vi+0].n, &m3d.vertex[m3d.face[i].vertex[0]]);
CopyVertex(&vertices[vi+1].n, &m3d.vertex[m3d.face[i].vertex[1]]);
CopyVertex(&vertices[vi+2].n, &m3d.vertex[m3d.face[i].vertex[2]]);
vertices[vi+0].col = m3d.vertex[m3d.face[i].vertex[0]].color;
vertices[vi+1].col = m3d.vertex[m3d.face[i].vertex[1]].color;
vertices[vi+2].col = m3d.vertex[m3d.face[i].vertex[2]].color;
if (m3d.numtmap)
{
vertices[vi+0].uv.x = m3d.tmap[m3d.face[i].texcoord[0]].u;
vertices[vi+0].uv.y = m3d.tmap[m3d.face[i].texcoord[0]].v;
vertices[vi+1].uv.x = m3d.tmap[m3d.face[i].texcoord[1]].u;
vertices[vi+1].uv.y = m3d.tmap[m3d.face[i].texcoord[1]].v;
vertices[vi+2].uv.x = m3d.tmap[m3d.face[i].texcoord[2]].u;
vertices[vi+2].uv.y = m3d.tmap[m3d.face[i].texcoord[2]].v;
}
indices[vi+0] = vi+0;
indices[vi+1] = vi+1;
indices[vi+2] = vi+2;
}
model.vertex_buffer = CreateBuffer(&rd.vk, BT.Vertex, vertices.length * Vertex.sizeof, false);
model.index_buffer = CreateBuffer(&rd.vk, BT.Index, indices.length * u32.sizeof, false);
assert(Transfer(&rd.vk, &model.vertex_buffer, vertices), "LoadModel failure: Unable to transfer vertex buffer");
assert(Transfer(&rd.vk, &model.index_buffer, indices), "LoadModel failure: Unable to transfer index buffer");
Reset(&rd.temp_arena);
return model;
}
bool
pragma(inline): void
CopyVertex(Vec4* dst, m3dv_t* src)
{
asm
{
mov R8, src;
mov R9, dst;
movups XMM0, src.x.offsetof[R8];
movups dst.x.offsetof[R9], XMM0;
}
assert(dst.x == src.x && dst.y == src.y && dst.z == src.z && dst.w == src.w, "CopyVertex failure");
}
pragma(inline): bool
BeginFrame(Renderer* rd)
{
return BeginFrame(&rd.vk);
}
bool
pragma(inline): bool
FinishFrame(Renderer* rd)
{
return FinishFrame(&rd.vk);
}
void
pragma(inline): void
Dispatch(Renderer* rd)
{
Dispatch(&rd.vk);
}
void
pragma(inline): void
PrepCompute(Renderer* rd)
{
PrepCompute(&rd.vk);
}
void
pragma(inline): void
SetUniform(Renderer* rd, GlobalUniforms* uniforms)
{
SetUniform(&rd.vk, uniforms);
}
void
pragma(inline): void
BeginRender(Renderer* rd)
{
BeginRender(&rd.vk);
}
void
pragma(inline): void
BindUIBuffers(Renderer* rd)
{
BindUIBuffers(&rd.vk);
}
void
pragma(inline): void
DrawIndexed(Renderer* rd, u32 index_count, u32 instance_count)
{
DrawIndexed(&rd.vk, index_count, instance_count);
}
void
pragma(inline): void
Draw(Renderer* rd, u32 index_count, u32 instance_count)
{
Draw(&rd.vk, index_count, instance_count);
}
void Bind(Renderer* rd, Pipeline* pipeline)
pragma(inline): void
Bind(Renderer* rd, Pipeline* pipeline)
{
Bind(&rd.vk, pipeline);
}
@ -298,13 +464,13 @@ DrawRect(Renderer* rd, f32 p0_x, f32 p0_y, f32 p1_x, f32 p1_y, Vec4 col)
rd.ui_count += 1;
}
Pipeline
pragma(inline): Pipeline
BuildGfxPipeline(Renderer* rd, GfxPipelineInfo* info)
{
return CreateGraphicsPipeline(&rd.vk, info);
}
Pipeline
pragma(inline): Pipeline
BuildCompPipeline(Renderer* rd, string compute)
{
return CreateComputePipeline(&rd.vk, compute);

View File

@ -3,9 +3,9 @@ import vulkan_logging;
import aliases;
import std.stdio;
import std.algorithm.comparison;
import core.stdc.string : strcmp;
import core.stdc.string : strcmp, memcpy;
import std.format : sformat;
import u = util : HashTable, Result, Logf, Log, MB;
import u = util : HashTable, Result, Logf, Log, MB, Delete;
import alloc;
import p = platform;
import ap = assets;
@ -643,10 +643,12 @@ CreateImageView(Vulkan* vk, u32 w, u32 h, u32 ch)
}
ImageView view = {
format: VK_IMAGE_LAYOUT_UNDEFINED,
base: {
layout: VK_IMAGE_LAYOUT_UNDEFINED,
},
};
VkResult result = vmaCreateImage(vk.vma, &image_info, &alloc_info, &view.image, *view.alloc, null);
VkResult result = vmaCreateImage(vk.vma, &image_info, &alloc_info, &view.image, &view.alloc, null);
// TODO: handle errors and realloc
assert(VkCheck("CreateImageView failure: vmaCreateImage error", result), "CreateImageView failure");
@ -670,9 +672,66 @@ CreateImageView(Vulkan* vk, u32 w, u32 h, u32 ch)
}
u32
Push(Vulkan* vk, ImageView* view)
Pop(Vulkan* vk, DescType type)
{
DescBindings* bindings = vk.desc_bindings.ptr + type;
assert(bindings.count > 0, "Pop failure: no free bindings remaining");
bindings.count -= 1;
return bindings.free[bindings.count];
}
void
Push(Vulkan* vk, u32 index, DescType type)
{
DescBindings* bindings = vk.desc_bindings.ptr + type;
bindings.free[bindings.count] = index;
bindings.count += 1;
}
u32
Pop(Vulkan* vk, string name, DescType type)
{
DescBindings* bindings = vk.desc_bindings.ptr + type;
u32 index;
auto result = bindings.lookup_table[name];
if (!result.ok)
{
// TODO: handle unbinding assets (maybe)
assert(bindings.count > 0, "Pop failure: no free bindings remaining");
bindings.count -= 1;
index = bindings.free[bindings.count];
}
else
{
index = result.value;
}
return index;
}
void
Push(Vulkan* vk, string name, DescType type)
{
DescBindings* bindings = vk.desc_bindings.ptr + type;
auto result = Delete(&bindings.lookup_table, name);
if (result.ok)
{
assert(bindings.count < bindings.free.length, "Push failure: attempt to push a binding into a full stack");
bindings.free[bindings.count] = result.value;
bindings.count += 1;
}
}
bool
Transfer(T)(Vulkan* vk, Buffer* buf, T[] data)
{
u8[] u8_data = (cast(u8*)(data.ptr))[0 .. T.sizeof * data.length];
return Transfer(vk, buf, u8_data);
}
bool
@ -708,6 +767,33 @@ Transfer(Vulkan* vk, Buffer* buf, u8[] data)
return success;
}
bool
Transfer(T)(Vulkan* vk, Buffer* buf, T* ptr)
{
assert(T.sizeof < vk.transfer_buf.data.length, "Transfer failure: structure size is too large");
memcpy(vk.transfer_buf.data.ptr, ptr, T.sizeof);
auto fn = delegate()
{
VkBufferCopy copy = {
srcOffset: 0,
dstOffset: 0,
size: T.sizeof,
};
vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, &copy);
};
return ImmSubmit(vk, fn);
}
pragma(inline): bool
Transfer(Vulkan* vk, ImageView* view, u8[] data, u32 w, u32 h)
{
return Transfer(vk, &view.base, data, w, h);
}
bool
Transfer(Vulkan* vk, Image* image, u8[] data, u32 w, u32 h)
{

View File

@ -42,6 +42,29 @@ GB(u64 v)
return MB(v) * 1024;
};
pragma(inline): void
ConvertColor(Vec4 *dst, u32 src)
{
if (src == 0)
{
dst.v[] = 0.0;
dst.a = 1.0;
}
else
{
Convert(dst, src);
}
}
pragma(inline): void
Convert(Vec4* dst, u32 src)
{
dst.r = cast(f32)((src >> 0) & 0xFF) / 255.0;
dst.g = cast(f32)((src >> 8) & 0xFF) / 255.0;
dst.b = cast(f32)((src >> 16) & 0xFF) / 255.0;
dst.a = cast(f32)((src >> 24) & 0xFF) / 255.0;
}
bool
BitEq(u64 l, u64 r)
{
@ -103,6 +126,30 @@ Pop(T)(SLList!(T)*list, Node!(T)* nil)
return node;
}
pragma(inline): void
Remove(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* prev, Node!(T)* nil)
{
node.next = nil;
if (list.first == list.last)
{
list.first = list.last = nil;
}
else if (list.first == node)
{
list.first = node.next;
}
else if (list.last == node)
{
list.last = prev;
prev.next = nil;
}
else
{
prev.next = node.next;
}
}
pragma(inline): void
PushFront(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* nil)
{
@ -282,10 +329,7 @@ Delete(K, V)(HashTable!(K, V)* ht, K key)
{
if (node.value.key == key)
{
if (prev != ht.nil)
{
prev.next = node.next;
}
Remove(list, node, prev, ht.nil);
result.ok = true;
result.value = node.value.value;