render-zig

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

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 }