render-zig

A 3D rendering engine written in Zig
git clone git://git.christianermann.dev/render-zig
Log | Files | Refs

commit 1392f56b6a10e64db00d73e6b24a12f76ed8d57d
parent bbf61fdd93fb8e5a352d58fd7577bc86a52caa54
Author: Christian Ermann <christianermann@gmail.com>
Date:   Thu, 11 Jul 2024 17:35:46 -0400

Add ability to update render mesh properties

Diffstat:
Msrc/load_obj.zig | 29++++++++++-------------------
Msrc/main.zig | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Asrc/mesh.zig | 11+++++++++++
3 files changed, 85 insertions(+), 43 deletions(-)

diff --git a/src/load_obj.zig b/src/load_obj.zig @@ -1,9 +1,7 @@ const std = @import("std"); const gpu = @import("mach_gpu"); -const Mesh = @import("main.zig").Mesh; -const MeshBuffer = @import("main.zig").MeshBuffer; -const MeshVertexData = @import("main.zig").MeshVertexData; +const Mesh = @import("mesh.zig"); const f32x2 = @Vector(2, f32); const f32x3 = @Vector(3, f32); @@ -102,10 +100,7 @@ fn parseVertex(line: []const u8) !Vertex { pub const LoadFileOptions = struct { allocator: std.mem.Allocator, - mesh_buffer: *MeshBuffer, path: []const u8, - scale: f32 = 1.0, - offset: f32x3 = .{ 0, 0, 0 }, }; pub fn loadFile(options: LoadFileOptions) !Mesh { @@ -188,7 +183,7 @@ pub fn loadFile(options: LoadFileOptions) !Mesh { const nslice = try normals_indexed.toOwnedSlice(); const tslice = try tex_coords_indexed.toOwnedSlice(); const islice = try indices.toOwnedSlice(); - const mesh_data = MeshVertexData{ + const mesh = Mesh{ .positions = pslice, .normals = nslice, .tex_coords = tslice, @@ -197,24 +192,20 @@ pub fn loadFile(options: LoadFileOptions) !Mesh { .bitangents = try allocator.alloc(f32x3, pslice.len), }; - try tangentsAndBitangents(&mesh_data, allocator); + try tangentsAndBitangents(&mesh, allocator); - return options.mesh_buffer.initMesh(&.{ - .scale = options.scale, - .offset = options.offset, - .data = &mesh_data, - }); + return mesh; } fn tangentsAndBitangents( - mesh_data: *const MeshVertexData, + mesh: *const Mesh, allocator: std.mem.Allocator, ) !void { - const positions = mesh_data.positions; - const tex_coords = mesh_data.tex_coords; - const indices = mesh_data.indices; - const tangents = mesh_data.tangents; - const bitangents = mesh_data.bitangents; + const positions = mesh.positions; + const tex_coords = mesh.tex_coords; + const indices = mesh.indices; + const tangents = mesh.tangents; + const bitangents = mesh.bitangents; @memset(std.mem.asBytes(tangents), 0); @memset(std.mem.asBytes(bitangents), 0); diff --git a/src/main.zig b/src/main.zig @@ -11,6 +11,7 @@ const SkyBoxRenderPipeline = @import("render_pipeline.zig").SkyBoxRenderPipeline const CubeMap = @import("cubemap.zig"); const Textures = @import("textures.zig"); const Material = @import("material.zig"); +const Mesh = @import("mesh.zig"); const Camera = @import("camera.zig"); const input = @import("input.zig"); @@ -390,6 +391,15 @@ pub const Buffer = struct { return offset_start; } + pub fn write( + self: *Buffer, + data_slice: anytype, + queue: *gpu.Queue, + offset: u32, + ) void { + queue.writeBuffer(self.data, offset, data_slice); + } + pub fn allocWrite(self: *Buffer, data_slice: anytype, queue: *gpu.Queue) !u32 { const offset = try self.alloc(@intCast(data_slice.len)); queue.writeBuffer(self.data, offset, data_slice); @@ -406,19 +416,10 @@ pub const MeshBufferOptions = struct { allocator: std.mem.Allocator, }; -pub const MeshVertexData = struct { - positions: []f32x4, - normals: []f32x3, - tex_coords: []f32x3, - tangents: []f32x3, - bitangents: []f32x3, - indices: []u32, -}; - pub const MeshOptions = struct { scale: f32 = 1.0, offset: f32x3 = .{ 0, 0, 0 }, - data: *const MeshVertexData, + data: *const Mesh, }; pub const MeshBuffer = struct { @@ -435,7 +436,7 @@ pub const MeshBuffer = struct { instances: Buffer, indirect: Buffer, - meshes: std.ArrayList(Mesh), + meshes: std.ArrayList(RenderMesh), pub fn init(options: MeshBufferOptions) MeshBuffer { const device = options.device; @@ -453,11 +454,11 @@ pub const MeshBuffer = struct { .indices = Buffer.init_idx(n_indices, device), .instances = Buffer.init_instance(options.n_meshes, device), .indirect = Buffer.init_indirect(options.n_meshes, device), - .meshes = std.ArrayList(Mesh).init(options.allocator), + .meshes = std.ArrayList(RenderMesh).init(options.allocator), }; } - pub fn initMesh(self: *MeshBuffer, options: *const MeshOptions) !Mesh { + pub fn initMesh(self: *MeshBuffer, options: *const MeshOptions) !*RenderMesh { const mesh_data = options.data; const num_vertices = mesh_data.positions.len; const num_indices = mesh_data.indices.len; @@ -473,7 +474,7 @@ pub const MeshBuffer = struct { const offset_i = try self.indices.allocWrite(mesh_data.indices, self.queue); std.log.info("idx offset: {} bytes", .{offset_i}); - const mesh = Mesh{ + const mesh = RenderMesh{ .num_vertices = num_vertices, .num_indices = num_indices, .scale = options.scale, @@ -498,17 +499,36 @@ pub const MeshBuffer = struct { _ = try self.indirect.allocWrite(&indirect_args, self.queue); try self.meshes.append(mesh); - return mesh; + return &self.meshes.items[0]; + } + + pub fn updateMeshes(self: *MeshBuffer) void { + std.log.info("RUNNING!", .{}); + for (self.meshes.items, 0..) |*mesh, i| { + if (!mesh.dirty) { + std.log.info("{}", .{mesh.dirty}); + continue; + } + const offset = @sizeOf(InstanceData) * i; + const instances = [_]InstanceData{ + mesh.instanceData(), + }; + std.log.info("instance: {}", .{mesh.instanceData()}); + self.instances.write(&instances, self.queue, @intCast(offset)); + mesh.dirty = false; + } } }; -pub const Mesh = struct { +pub const RenderMesh = struct { num_vertices: usize, num_indices: usize, scale: f32 = 1.0, offset: f32x3 = .{ 0.0, 0.0, 0.0 }, + material: Material = undefined, + dirty: bool = false, - pub fn instanceData(self: *const Mesh) InstanceData { + pub fn instanceData(self: *const RenderMesh) InstanceData { return InstanceData{ .model_matrix = .{ .{ self.scale, 0, 0, 0 }, @@ -516,9 +536,24 @@ pub const Mesh = struct { .{ 0, 0, self.scale, 0 }, .{ self.offset[0], self.offset[1], self.offset[2], 1 }, }, - .material = .{ .albedo = 0, .normal = 1, .roughness = 2, .metalness = 3 }, + .material = self.material, }; } + + pub fn setScale(self: *RenderMesh, scale: f32) void { + self.scale = scale; + self.dirty = true; + } + + pub fn setOffset(self: *RenderMesh, offset: f32x3) void { + self.offset = offset; + self.dirty = true; + } + + pub fn setMaterial(self: *RenderMesh, material: *const Material) void { + self.material = material.*; + self.dirty = true; + } }; pub fn main() !void { @@ -561,24 +596,26 @@ pub fn main() !void { .allocator = allocator, }); - _ = try load_obj.loadFile(.{ + const mesh = try load_obj.loadFile(.{ .allocator = allocator, - .mesh_buffer = &mesh_buffer, .path = "Cottage_Clean/cottage.obj", + }); + var render_mesh = try mesh_buffer.initMesh(&.{ + .data = &mesh, .scale = 1, .offset = .{ 0, -5, 15 }, }); + _ = try load_obj.loadFile(.{ .allocator = allocator, - .mesh_buffer = &mesh_buffer, .path = "teapot.obj", - .scale = 0.05, - .offset = .{ 0, -0.5, 0 }, + //.scale = 0.05, + //.offset = .{ 0, -0.5, 0 }, }); var rp = try MeshRenderPipeline.init(&app, &mesh_buffer, &textures); - _ = try Material.init( + const material = try Material.init( &textures, "Cottage_Clean/Cottage_Clean_Base_Color_fixed.png", "Cottage_Clean/Cottage_Clean_Normal_fixed.png", @@ -587,6 +624,9 @@ pub fn main() !void { app.queue, ); + render_mesh.setMaterial(&material); + mesh_buffer.updateMeshes(); + var skybox = SkyBoxRenderPipeline.init(&app, cube_map); var fp = ForwardScreenPass.init(.{ diff --git a/src/mesh.zig b/src/mesh.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +const f32x3 = @Vector(3, f32); +const f32x4 = @Vector(4, f32); + +positions: []f32x4, +normals: []f32x3, +tex_coords: []f32x3, +tangents: []f32x3, +bitangents: []f32x3, +indices: []u32,