basic model rendering completed

This commit is contained in:
matthew 2025-07-18 07:37:52 +10:00
parent 2dd274742d
commit b4ba1f491b
19 changed files with 201 additions and 119 deletions

BIN
assets/models/cube.m3d Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/shaders/pbr.frag.spv Normal file

Binary file not shown.

BIN
assets/shaders/pbr.vert.spv Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,11 +7,6 @@ import r = renderer;
import util;
import core.simd;
struct Floats
{
f32 r = 0.0, g = 0.0, b = 0.0, a = 0.0;
}
void main()
{
p.Window window = p.CreateWindow("Video Game", 1920, 1080);

View File

@ -6,6 +6,7 @@ import alloc;
import vulkan;
import vulkan : Destroy, Init, Draw, DrawIndexed, Bind, BindUIBuffers, BeginRender, SetUniform, PrepCompute, Dispatch, FinishFrame, BeginFrame;
import assets;
import std.math.traits : isNaN;
import u = util;
import p = platform;
@ -28,6 +29,8 @@ enum Format : VkFormat
RG_F32 = VK_FORMAT_R32G32_SFLOAT,
RGB_F32 = VK_FORMAT_R32G32B32_SFLOAT,
RGBA_F32 = VK_FORMAT_R32G32B32A32_SFLOAT,
RGBA_UINT = VK_FORMAT_B8G8R8A8_UINT,
RGBA_UNORM = VK_FORMAT_R8G8B8A8_UNORM,
}
alias FMT = Format;
@ -74,6 +77,7 @@ struct Renderer
u32[] ui_index_buf;
u32 ui_count;
Pipeline pbr_pipeline;
Pipeline triangle_pipeline;
Pipeline compute_pipeline;
Pipeline ui_pipeline;
@ -173,6 +177,19 @@ Init(p.Window* window)
],
};
GfxPipelineInfo pbr_info = {
vertex_shader: "shaders/pbr.vert",
frag_shader: "shaders/pbr.frag",
input_rate_stride: Vertex.sizeof,
vertex_attributes: [
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: 0 },
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.n.offsetof },
{ binding: 0, location: 2, format: FMT.RG_F32, offset: Vertex.uv.offsetof },
{ binding: 0, location: 3, format: FMT.RGBA_UNORM, offset: Vertex.col.offsetof },
],
};
rd.pbr_pipeline = BuildGfxPipeline(&rd, &pbr_info);
rd.triangle_pipeline = BuildGfxPipeline(&rd, &triangle_info);
rd.ui_pipeline = BuildGfxPipeline(&rd, &ui_info);
rd.compute_pipeline = BuildCompPipeline(&rd, "shaders/gradient.comp");
@ -207,18 +224,144 @@ Cycle(Renderer* rd)
BindUIBuffers(rd);
DrawIndexed(rd, 6, rd.ui_count);
DrawIndexed(rd, 6, rd.ui_count, 0);
Bind(rd, &rd.triangle_pipeline);
Draw(rd, 3, 1);
Bind(rd, &rd.pbr_pipeline);
DrawModel(rd, &rd.yoder);
success = FinishFrame(rd);
}
return success;
}
pragma(inline): void
DrawModel(Renderer* rd, Model* model)
{
BindBuffers(&rd.vk, &model.index_buffer, &model.vertex_buffer);
foreach(i, part; model.parts)
{
DrawIndexed(rd, part.length, 1, part.offset);
}
}
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;
}
debug
{
assert(dst.x == src.x && dst.y == src.y && dst.z == src.z && dst.w == src.w, "Vertex copy failed");
assert(!isNaN(dst.x) && !isNaN(dst.y) && !isNaN(dst.z) && !isNaN(dst.w), "Vertex contains NaN");
}
}
pragma(inline): bool
BeginFrame(Renderer* rd)
{
return BeginFrame(&rd.vk);
}
pragma(inline): bool
FinishFrame(Renderer* rd)
{
return FinishFrame(&rd.vk);
}
pragma(inline): void
Dispatch(Renderer* rd)
{
Dispatch(&rd.vk);
}
pragma(inline): void
PrepCompute(Renderer* rd)
{
PrepCompute(&rd.vk);
}
pragma(inline): void
SetUniform(Renderer* rd, GlobalUniforms* uniforms)
{
SetUniform(&rd.vk, uniforms);
}
pragma(inline): void
BeginRender(Renderer* rd)
{
BeginRender(&rd.vk);
}
pragma(inline): void
BindUIBuffers(Renderer* rd)
{
BindUIBuffers(&rd.vk);
}
pragma(inline): void
DrawIndexed(Renderer* rd, u32 index_count, u32 instance_count, u32 index_offset)
{
DrawIndexed(&rd.vk, index_count, instance_count, index_offset);
}
pragma(inline): void
Draw(Renderer* rd, u32 index_count, u32 instance_count)
{
Draw(&rd.vk, index_count, instance_count);
}
pragma(inline): void
Bind(Renderer* rd, Pipeline* pipeline)
{
Bind(&rd.vk, pipeline);
}
void
DrawRect(Renderer* rd, f32 p0_x, f32 p0_y, f32 p1_x, f32 p1_y, Vec4 col)
{
rd.ui_vertex_buf[rd.ui_count].p0.x = p0_x;
rd.ui_vertex_buf[rd.ui_count].p0.y = p0_y;
rd.ui_vertex_buf[rd.ui_count].p1.x = p1_x;
rd.ui_vertex_buf[rd.ui_count].p1.y = p1_y;
rd.ui_vertex_buf[rd.ui_count].col = col;
u32 index_count = rd.ui_count * 6;
rd.ui_index_buf[index_count+0] = index_count+0;
rd.ui_index_buf[index_count+1] = index_count+1;
rd.ui_index_buf[index_count+2] = index_count+2;
rd.ui_index_buf[index_count+3] = index_count+1;
rd.ui_index_buf[index_count+4] = index_count+2;
rd.ui_index_buf[index_count+5] = index_count+3;
rd.ui_count += 1;
}
pragma(inline): Pipeline
BuildGfxPipeline(Renderer* rd, GfxPipelineInfo* info)
{
return CreateGraphicsPipeline(&rd.vk, info);
}
pragma(inline): Pipeline
BuildCompPipeline(Renderer* rd, string compute)
{
return CreateComputePipeline(&rd.vk, compute);
}
Model
LoadModel(Renderer* rd, string name)
{
@ -298,7 +441,7 @@ LoadModel(Renderer* rd, string name)
{
if (last == u64.max)
{
model.parts[index].mat = mat_lookup[m3d.face[i].materialid];
model.parts[index].mat = m3d.face[i].materialid != u32.max ? mat_lookup[m3d.face[i].materialid] : 0;
model.parts[index].offset = 0;
last = m3d.face[i].materialid;
}
@ -369,111 +512,6 @@ LoadModel(Renderer* rd, string name)
return model;
}
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;
}
}
pragma(inline): bool
BeginFrame(Renderer* rd)
{
return BeginFrame(&rd.vk);
}
pragma(inline): bool
FinishFrame(Renderer* rd)
{
return FinishFrame(&rd.vk);
}
pragma(inline): void
Dispatch(Renderer* rd)
{
Dispatch(&rd.vk);
}
pragma(inline): void
PrepCompute(Renderer* rd)
{
PrepCompute(&rd.vk);
}
pragma(inline): void
SetUniform(Renderer* rd, GlobalUniforms* uniforms)
{
SetUniform(&rd.vk, uniforms);
}
pragma(inline): void
BeginRender(Renderer* rd)
{
BeginRender(&rd.vk);
}
pragma(inline): void
BindUIBuffers(Renderer* rd)
{
BindUIBuffers(&rd.vk);
}
pragma(inline): void
DrawIndexed(Renderer* rd, u32 index_count, u32 instance_count)
{
DrawIndexed(&rd.vk, index_count, instance_count);
}
pragma(inline): void
Draw(Renderer* rd, u32 index_count, u32 instance_count)
{
Draw(&rd.vk, index_count, instance_count);
}
pragma(inline): void
Bind(Renderer* rd, Pipeline* pipeline)
{
Bind(&rd.vk, pipeline);
}
void
DrawRect(Renderer* rd, f32 p0_x, f32 p0_y, f32 p1_x, f32 p1_y, Vec4 col)
{
rd.ui_vertex_buf[rd.ui_count].p0.x = p0_x;
rd.ui_vertex_buf[rd.ui_count].p0.y = p0_y;
rd.ui_vertex_buf[rd.ui_count].p1.x = p1_x;
rd.ui_vertex_buf[rd.ui_count].p1.y = p1_y;
rd.ui_vertex_buf[rd.ui_count].col = col;
u32 index_count = rd.ui_count * 6;
rd.ui_index_buf[index_count+0] = index_count+0;
rd.ui_index_buf[index_count+1] = index_count+1;
rd.ui_index_buf[index_count+2] = index_count+2;
rd.ui_index_buf[index_count+3] = index_count+1;
rd.ui_index_buf[index_count+4] = index_count+2;
rd.ui_index_buf[index_count+5] = index_count+3;
rd.ui_count += 1;
}
pragma(inline): Pipeline
BuildGfxPipeline(Renderer* rd, GfxPipelineInfo* info)
{
return CreateGraphicsPipeline(&rd.vk, info);
}
pragma(inline): Pipeline
BuildCompPipeline(Renderer* rd, string compute)
{
return CreateComputePipeline(&rd.vk, compute);
}
void
Destroy(Renderer* rd)
{

View File

@ -316,6 +316,15 @@ BindUIBuffers(Vulkan* vk)
vkCmdBindVertexBuffers(vk.cmds[vk.frame_index], 0, 1, &vk.ui_vert_buf.buffer, &offset);
}
void
BindBuffers(Vulkan* vk, Buffer* index_buffer, Buffer* vertex_buffer)
{
VkDeviceSize offset = 0;
vkCmdBindIndexBuffer(vk.cmds[vk.frame_index], index_buffer.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindVertexBuffers(vk.cmds[vk.frame_index], 0, 1, &vertex_buffer.buffer, &offset);
}
MappedBuffer!(T)
CreateMappedBuffer(T)(Vulkan* vk, BufferType type, u64 count)
{
@ -541,9 +550,9 @@ Draw(Vulkan* vk, u32 index_count, u32 instance_count)
}
void
DrawIndexed(Vulkan* vk, u32 index_count, u32 instance_count)
DrawIndexed(Vulkan* vk, u32 index_count, u32 instance_count, u32 index_offset)
{
vkCmdDrawIndexed(vk.cmds[vk.frame_index], index_count, instance_count, 0, 0, 0);
vkCmdDrawIndexed(vk.cmds[vk.frame_index], index_count, instance_count, index_offset, 0, 0);
}
bool

View File

@ -11,16 +11,19 @@ enum AssetType : u32
alias AT = AssetType;
static immutable string[] MODEL_FILES = [
"models/cube.m3d",
"models/test_char.m3d",
"models/yoda.m3d",
];
static immutable string[] MODEL_NAMES = [
"models/cube",
"models/test_char",
"models/yoda",
];
static immutable u64[] MODEL_HASHES = [
16487010975852376380,
13826959199295087925,
4559395153940738542,
];
@ -28,25 +31,31 @@ static immutable u64[] MODEL_HASHES = [
static immutable string[] SHADER_FILES = [
"shaders/gui.frag.spv",
"shaders/triangle.vert.spv",
"shaders/pbr.frag.spv",
"shaders/gui.vert.spv",
"shaders/gradient.comp.spv",
"shaders/triangle.frag.spv",
"shaders/pbr.vert.spv",
];
static immutable string[] SHADER_NAMES = [
"shaders/gui.frag",
"shaders/triangle.vert",
"shaders/pbr.frag",
"shaders/gui.vert",
"shaders/gradient.comp",
"shaders/triangle.frag",
"shaders/pbr.vert",
];
static immutable u64[] SHADER_HASHES = [
15780387719315455808,
8769314645675479020,
2230071466542309169,
14797956403837654625,
16130483095684998267,
6282520872716708711,
8518761701216801634,
];
static immutable string[] TEXTURE_FILES = [

View File

@ -1,6 +1,5 @@
#version 460
#extension GL_EXT_buffer_reference : require
#extension GL_GOOGLE_include_directive : require
#include "structures.layout"

View File

@ -1,6 +1,5 @@
#version 460
#extension GL_EXT_buffer_reference : require
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_nonuniform_qualifier : require

View File

@ -1,6 +1,5 @@
#version 460
#extension GL_EXT_buffer_reference : require
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_nonuniform_qualifier : require

16
src/shaders/pbr.frag.glsl Normal file
View File

@ -0,0 +1,16 @@
#version 460
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_nonuniform_qualifier : require
#include "structures.layout"
layout (location = 0) in vec4 in_col;
layout (location = 1) in vec2 in_uv;
layout (location = 0) out vec4 FragColor;
void main()
{
FragColor = in_col;
}

20
src/shaders/pbr.vert.glsl Normal file
View File

@ -0,0 +1,20 @@
#version 460
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_nonuniform_qualifier : require
#include "structures.layout"
layout (location = 0) in vec4 in_pos;
layout (location = 1) in vec4 in_normal;
layout (location = 2) in vec2 in_uv;
layout (location = 3) in vec4 in_col;
layout (location = 0) out vec4 out_col;
layout (location = 1) out vec2 out_uv;
void main()
{
gl_Position = in_pos;
out_col = in_col;
}

View File

@ -1,6 +1,5 @@
#version 460
#extension GL_EXT_buffer_reference : require
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_nonuniform_qualifier : require

View File

@ -1,6 +1,5 @@
#version 460
#extension GL_EXT_buffer_reference : require
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_nonuniform_qualifier : require