render_data.zig (8305B)
1 const std = @import("std"); 2 const gpu = @import("mach_gpu"); 3 4 const Buffer = @import("main.zig").Buffer; 5 const Material = @import("material.zig"); 6 const Mesh = @import("mesh.zig"); 7 const Transform = @import("transform.zig"); 8 9 const RenderData = @This(); 10 11 pub const f32x3 = @Vector(3, f32); 12 pub const f32x4 = @Vector(4, f32); 13 pub const mat4 = [4]@Vector(4, f32); 14 15 pub const InstanceData = struct { 16 object_id: u32, 17 }; 18 19 pub const ObjectData = struct { 20 model_matrix: mat4, 21 material: Material, 22 }; 23 24 pub const RenderObject = struct { 25 num_vertices: usize, 26 num_indices: usize, 27 transform: *Transform, 28 material: Material = undefined, 29 dirty: bool = false, 30 31 pub fn instanceData(self: *const RenderObject) ObjectData { 32 return .{ 33 .model_matrix = self.transform.matrix(), 34 .material = self.material, 35 }; 36 } 37 38 pub fn setScale(self: *RenderObject, scale: f32) void { 39 self.transform.scale = .{ scale, scale, scale }; 40 self.dirty = true; 41 } 42 43 pub fn setOffset(self: *RenderObject, offset: f32x3) void { 44 self.transform.offset = offset; 45 self.dirty = true; 46 } 47 48 pub fn setMaterial(self: *RenderObject, material: *const Material) void { 49 self.material = material.*; 50 self.dirty = true; 51 } 52 }; 53 54 device: *gpu.Device, 55 queue: *gpu.Queue, 56 57 positions: Buffer(f32x4), 58 normals: Buffer(f32x4), 59 tex_coords: Buffer(f32x4), 60 tangents: Buffer(f32x4), 61 bitangents: Buffer(f32x4), 62 indices: Buffer(u32), 63 64 // TODO: indirect buffers should be generated by pipelines 65 indirect_mesh: Buffer([5]u32), 66 indirect_line: Buffer([5]u32), 67 68 instance_data: Buffer(InstanceData), 69 object_data: Buffer(ObjectData), 70 71 objects: std.ArrayList(RenderObject), 72 73 pub const Options = struct { 74 device: *gpu.Device, 75 queue: *gpu.Queue, 76 n_instances: u32, 77 n_objects: u32, 78 n_vertices: u32, 79 n_indices: u32, 80 }; 81 82 pub fn init(options: Options, allocator: std.mem.Allocator) RenderData { 83 return .{ 84 .device = options.device, 85 .queue = options.queue, 86 .positions = Buffer(f32x4).init( 87 options.n_vertices, 88 options.device, 89 .{ .vertex = true, .copy_dst = true }, 90 false, 91 ), 92 .normals = Buffer(f32x4).init( 93 options.n_vertices, 94 options.device, 95 .{ .vertex = true, .copy_dst = true }, 96 false, 97 ), 98 .tex_coords = Buffer(f32x4).init( 99 options.n_vertices, 100 options.device, 101 .{ .vertex = true, .copy_dst = true }, 102 false, 103 ), 104 .tangents = Buffer(f32x4).init( 105 options.n_vertices, 106 options.device, 107 .{ .vertex = true, .copy_dst = true }, 108 false, 109 ), 110 .bitangents = Buffer(f32x4).init( 111 options.n_vertices, 112 options.device, 113 .{ .vertex = true, .copy_dst = true }, 114 false, 115 ), 116 .indices = Buffer(u32).init( 117 options.n_indices, 118 options.device, 119 .{ .index = true, .copy_dst = true }, 120 false, 121 ), 122 .instance_data = Buffer(InstanceData).init( 123 options.n_instances, 124 options.device, 125 .{ .storage = true, .copy_dst = true }, 126 false, 127 ), 128 .object_data = Buffer(ObjectData).init( 129 options.n_objects, 130 options.device, 131 .{ .storage = true, .copy_dst = true }, 132 false, 133 ), 134 .indirect_mesh = Buffer([5]u32).init( 135 options.n_instances, 136 options.device, 137 .{ .indirect = true, .copy_dst = true }, 138 false, 139 ), 140 .indirect_line = Buffer([5]u32).init( 141 options.n_instances, 142 options.device, 143 .{ .indirect = true, .copy_dst = true }, 144 false, 145 ), 146 .objects = std.ArrayList(RenderObject).init(allocator), 147 }; 148 } 149 150 pub fn addLines( 151 self: *RenderData, 152 positions: []const f32x4, 153 indices: []const u32, 154 transform: *Transform, 155 ) !*RenderObject { 156 const offset_p = try self.positions.allocWrite(positions, self.queue); 157 const n: u32 = @intCast(positions.len); 158 _ = try self.normals.alloc(n); 159 _ = try self.tex_coords.alloc(n); 160 _ = try self.tangents.alloc(n); 161 _ = try self.bitangents.alloc(n); 162 const offset_i = try self.indices.allocWrite(indices, self.queue); 163 164 const object = RenderObject{ 165 .num_vertices = positions.len, 166 .num_indices = indices.len, 167 .transform = transform, 168 }; 169 const object_data = [_]ObjectData{object.instanceData()}; 170 _ = try self.object_data.allocWrite(&object_data, self.queue); 171 172 const instance_data = [_]InstanceData{ 173 .{ .object_id = @intCast(self.objects.items.len) }, 174 }; 175 const indirect_args = [_][5]u32{.{ 176 @intCast(indices.len), 177 1, 178 offset_i, 179 offset_p, 180 // lines are always one "piece", so we don't need to map from an 181 // instance id to an object 182 @intCast(self.objects.items.len), 183 }}; 184 _ = try self.instance_data.allocWrite(&instance_data, self.queue); 185 _ = try self.indirect_line.allocWrite(&indirect_args, self.queue); 186 187 try self.objects.append(object); 188 return &self.objects.items[self.objects.items.len - 1]; 189 } 190 191 pub fn addMesh( 192 self: *RenderData, 193 transform: *Transform, 194 mesh: *const Mesh, 195 allocator: std.mem.Allocator, 196 ) !*RenderObject { 197 const offset_p = try self.positions.allocWrite(mesh.positions, self.queue); 198 const n: u32 = @intCast(mesh.positions.len); 199 if (mesh.normals.len > 0) { 200 _ = try self.normals.allocWrite(mesh.normals, self.queue); 201 } else { 202 _ = try self.normals.alloc(n); 203 } 204 if (mesh.tex_coords.len > 0) { 205 _ = try self.tex_coords.allocWrite(mesh.tex_coords, self.queue); 206 } else { 207 _ = try self.tex_coords.alloc(n); 208 } 209 _ = try self.tangents.allocWrite(mesh.tangents, self.queue); 210 _ = try self.bitangents.allocWrite(mesh.bitangents, self.queue); 211 const offset_i = try self.indices.allocWrite(mesh.indices, self.queue); 212 213 const object = RenderObject{ 214 .num_vertices = mesh.positions.len, 215 .num_indices = mesh.indices.len, 216 .transform = transform, 217 }; 218 const object_data = [_]ObjectData{object.instanceData()}; 219 _ = try self.object_data.allocWrite(&object_data, self.queue); 220 221 if (mesh.descriptors != null) { 222 const descriptors = mesh.descriptors.?; 223 const instance_data = try allocator.alloc(InstanceData, descriptors.len); 224 defer allocator.free(instance_data); 225 const indirect_args = try allocator.alloc([5]u32, descriptors.len); 226 defer allocator.free(indirect_args); 227 228 var instance_id = self.indirect_mesh.count(); 229 for (descriptors, 0..) |descriptor, i| { 230 instance_data[i] = .{ .object_id = @intCast(self.objects.items.len) }; 231 indirect_args[i] = .{ 232 @intCast(descriptor.num_triangles * 3), 233 1, 234 offset_i + descriptor.offset, 235 offset_p, 236 instance_id, 237 }; 238 instance_id += 1; 239 } 240 _ = try self.instance_data.allocWrite(instance_data, self.queue); 241 _ = try self.indirect_mesh.allocWrite(indirect_args, self.queue); 242 } else { 243 const instance_data = [_]InstanceData{ 244 .{ .object_id = @intCast(self.objects.items.len) }, 245 }; 246 const indirect_args = [_][5]u32{.{ 247 @intCast(mesh.indices.len), 248 1, 249 offset_i, 250 offset_p, 251 self.indirect_mesh.count(), 252 }}; 253 _ = try self.instance_data.allocWrite(&instance_data, self.queue); 254 _ = try self.indirect_mesh.allocWrite(&indirect_args, self.queue); 255 } 256 257 try self.objects.append(object); 258 return &self.objects.items[self.objects.items.len - 1]; 259 } 260 261 pub fn updateObjects(self: *RenderData) void { 262 for (self.objects.items, 0..) |*object, i| { 263 if (!object.dirty) { 264 continue; 265 } 266 const object_data = [_]ObjectData{object.instanceData()}; 267 self.object_data.write(&object_data, self.queue, @intCast(i)); 268 object.dirty = false; 269 } 270 }