403 lines
13 KiB
Zig
403 lines
13 KiB
Zig
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "rend.DebugDrawSystem");
|
|
|
|
allocator: std.mem.Allocator,
|
|
debugDraws: core.RingQueueU(DebugPrimitive),
|
|
|
|
device: *gpu.GPUDevice = undefined,
|
|
renderer: *Renderer = undefined,
|
|
pipeline: *gpu.GPUGraphicsPipeline = undefined,
|
|
|
|
ssbo: *gpu.GPUBuffer = undefined,
|
|
ssobUpload: *gpu.GPUTransferBuffer = undefined,
|
|
|
|
meshes: [@as(usize, @intCast(@intFromEnum(DebugPrimitiveType.box) + 1))]?rend.IndexedMesh = .{ null, null, null },
|
|
deltaTime: f64 = 0,
|
|
|
|
drawsThisFrame: std.ArrayListUnmanaged(DebugPrimitive) = .{},
|
|
|
|
pub const MaxObjectCount = 4096;
|
|
|
|
const assetReferences = [_]assets.AssetImportReference{
|
|
assets.MakeImportRefOptions(
|
|
"Mesh",
|
|
"m_debug_box",
|
|
.{ .path = "embedded:debug_box.obj" },
|
|
),
|
|
assets.MakeImportRefOptions(
|
|
"Mesh",
|
|
"m_debug_line",
|
|
.{ .path = "embedded:debug_line.obj" },
|
|
),
|
|
assets.MakeImportRefOptions(
|
|
"Mesh",
|
|
"m_debug_sphere",
|
|
.{ .path = "embedded:debug_sphere.obj" },
|
|
),
|
|
};
|
|
|
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|
const self = try allocator.create(@This());
|
|
self.* = .{
|
|
.debugDraws = try core.RingQueueU(DebugPrimitive).init(allocator, MaxObjectCount),
|
|
.allocator = allocator,
|
|
};
|
|
core.engine_logs("[DebugDrawSystem] starting up...");
|
|
|
|
_ = try core.fs().installFileBytesMount("embedded:debug_box.obj", @constCast(&primitive_box), true);
|
|
_ = try core.fs().installFileBytesMount("embedded:debug_line.obj", @constCast(&primitive_line), true);
|
|
_ = try core.fs().installFileBytesMount("embedded:debug_sphere.obj", @constCast(&primitive_sphere), true);
|
|
|
|
try assets.loadList(assetReferences);
|
|
|
|
gDebugDrawSys = self;
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn updateMeshes(self: *@This()) void {
|
|
|
|
// line = 0,
|
|
// sphere = 1,
|
|
// box = 2,
|
|
const list: [3][]const u8 = .{
|
|
"m_debug_line", // 0
|
|
"m_debug_sphere", // 1
|
|
"m_debug_box", // 2
|
|
};
|
|
|
|
for (list, 0..) |path, i| {
|
|
self.meshes[i] = rend.getMesh(path);
|
|
}
|
|
}
|
|
|
|
pub fn tick(self: *@This(), dt: f64) void {
|
|
self.deltaTime = dt;
|
|
|
|
if (self.meshes[0] == null) {
|
|
self.updateMeshes();
|
|
}
|
|
}
|
|
|
|
// ========= Rendering section =========
|
|
pub fn onUpload(p: *anyopaque, copyPass: *gpu.GPUCopyPass) void {
|
|
const self: *@This() = @ptrCast(@alignCast(p));
|
|
|
|
const count = self.debugDraws.count();
|
|
{
|
|
const uploadMapped: [*]debug_vert.Scene = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(self.ssobUpload, true)));
|
|
defer self.device.unmapGPUTransferBuffer(self.ssobUpload);
|
|
|
|
var offset: usize = 0;
|
|
|
|
self.drawsThisFrame.clearRetainingCapacity();
|
|
|
|
while (offset < count) {
|
|
var primitive: DebugPrimitive = self.debugDraws.pop().?;
|
|
uploadMapped[offset] = .{
|
|
.Model = primitive.resolve(),
|
|
.Color = @bitCast(primitive.color.toZm()),
|
|
};
|
|
|
|
self.drawsThisFrame.append(self.allocator, primitive) catch unreachable;
|
|
|
|
primitive.duration -= @as(f32, @floatCast(self.deltaTime));
|
|
offset += 1;
|
|
if (primitive.duration >= 0) {
|
|
// push this primitive so that it goes to the next frame
|
|
self.debugDraws.push(primitive) catch continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count > 0) {
|
|
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.ssobUpload, .offset = 0 }, &.{
|
|
.buffer = self.ssbo,
|
|
.offset = 0,
|
|
.size = @intCast(count * @sizeOf(debug_vert.Scene)),
|
|
}, false);
|
|
}
|
|
}
|
|
|
|
pub fn postMesh(p: *anyopaque, cmd: *gpu.GPUCommandBuffer, renderpass: *gpu.GPURenderPass) void {
|
|
const self: *@This() = @ptrCast(@alignCast(p));
|
|
|
|
if (self.meshes[0] == null) {
|
|
return;
|
|
}
|
|
|
|
renderpass.bindGPUGraphicsPipeline(self.pipeline);
|
|
renderpass.bindGPUVertexStorageBuffers(0, &self.ssbo, 1);
|
|
|
|
for (self.drawsThisFrame.items, 0..) |draw, i| {
|
|
var mesh: rend.IndexedMesh = undefined;
|
|
switch (draw.primitive) {
|
|
.box => {
|
|
mesh = self.meshes[2].?;
|
|
},
|
|
.sphere => {
|
|
mesh = self.meshes[1].?;
|
|
},
|
|
.line => {
|
|
mesh = self.meshes[0].?;
|
|
},
|
|
}
|
|
|
|
renderpass.drawGPUIndexedPrimitives(mesh.index.size, 1, mesh.index.start, @intCast(mesh.vertex.start), @intCast(i));
|
|
}
|
|
_ = cmd;
|
|
}
|
|
// ========= End of Rendering section =========
|
|
|
|
pub fn createBuffers(self: *@This()) !void {
|
|
self.ssbo = self.device.createGPUBuffer(&.{
|
|
.usage = .{ .bufferusageGraphicsStorageRead = true, .bufferusageVertex = true },
|
|
.size = MaxObjectCount * @sizeOf(debug_vert.Scene),
|
|
.props = 0,
|
|
});
|
|
|
|
self.ssobUpload = rend.renderer.createGPUTransferBuffer(&.{
|
|
.usage = .transferbufferusageUpload,
|
|
.size = MaxObjectCount * @sizeOf(debug_vert.Scene),
|
|
.props = 0,
|
|
});
|
|
}
|
|
|
|
pub fn createPipeline(self: *@This()) !*gpu.GPUGraphicsPipeline {
|
|
const vertex = try rend.context().loadShader("debug.vert", debug_vert.LoadArgs);
|
|
const fragment = try rend.context().loadShader("debug.frag", debug_frag.LoadArgs);
|
|
|
|
var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo);
|
|
pci.vertex_shader = vertex;
|
|
pci.fragment_shader = fragment;
|
|
|
|
var attributes = std.ArrayList(gpu.GPUVertexAttribute).init(self.allocator);
|
|
defer attributes.deinit();
|
|
|
|
var offset: u32 = 0;
|
|
{
|
|
try addAttribute(&attributes, &offset, @sizeOf(f32) * 3, .vertexelementformatFloat3);
|
|
}
|
|
|
|
pci.vertex_input_state = .{
|
|
.num_vertex_buffers = 1,
|
|
.vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{
|
|
.{ .slot = 0, .pitch = @sizeOf(rend.MeshVertex), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 },
|
|
},
|
|
.num_vertex_attributes = @intCast(attributes.items.len),
|
|
.vertex_attributes = @ptrCast(attributes.items.ptr),
|
|
};
|
|
|
|
pci.depth_stencil_state.compare_op = .compareopLess;
|
|
pci.depth_stencil_state.enable_depth_test = true;
|
|
pci.depth_stencil_state.enable_depth_write = true;
|
|
pci.depth_stencil_state.write_mask = 0xff;
|
|
|
|
pci.target_info.has_depth_stencil_target = true;
|
|
pci.target_info.depth_stencil_format = rend.context().depthFormat;
|
|
pci.target_info.num_color_targets = 4;
|
|
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
|
|
.{
|
|
.format = rend.context().hdrTextureFormat,
|
|
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
|
|
},
|
|
.{
|
|
.format = rend.context().hdrTextureFormat,
|
|
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
|
|
},
|
|
.{
|
|
.format = rend.context().positionTargetFormat,
|
|
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
|
|
},
|
|
.{
|
|
.format = rend.context().normalTargetFormat,
|
|
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
|
|
},
|
|
};
|
|
|
|
pci.rasterizer_state.fill_mode = .fillmodeLine;
|
|
const pipe = self.device.createGPUGraphicsPipeline(&pci);
|
|
|
|
return pipe;
|
|
}
|
|
|
|
fn addAttribute(list: *std.ArrayList(gpu.GPUVertexAttribute), offset: *u32, size: u32, format: gpu.GPUVertexElementFormat) !void {
|
|
try list.append(.{ .location = @intCast(list.items.len), .offset = offset.*, .format = format, .buffer_slot = 0 });
|
|
offset.* = offset.* + size;
|
|
}
|
|
|
|
pub fn setup(self: *@This(), device: *gpu.GPUDevice) !void {
|
|
core.engine_logs("[DebugDrawSystem] registering to renderer...");
|
|
self.device = device;
|
|
self.renderer = rend.context();
|
|
self.pipeline = try self.createPipeline();
|
|
try self.createBuffers();
|
|
|
|
//core.installDebugDrawInterface(self.allocator, newInterface: DebugDrawInterface);
|
|
try core.installDebugDrawInterface(self.allocator, .{
|
|
.debugSphereFn = debugSphere, // : *const fn (pos: core.Vectorf, radius: f32, DebugDrawParams) void,
|
|
.debugBoxFn = debugBox, // : *const fn (pos: core.Vectorf, extents: core.Vectorf, DebugDrawParams) void,
|
|
.debugLineFn = debugLine, // : *const fn (start: core.Vectorf, end: core.Vectorf, DebugDrawParams) void,
|
|
});
|
|
}
|
|
|
|
pub fn destroy(self: *@This()) void {
|
|
self.debugDraws.deinit(self.allocator);
|
|
self.drawsThisFrame.deinit(self.allocator);
|
|
self.allocator.destroy(self);
|
|
}
|
|
|
|
// ==================== Primitives ===================
|
|
|
|
pub const DebugLine = struct {
|
|
start: core.Vectorf,
|
|
end: core.Vectorf,
|
|
|
|
pub fn resolve(self: @This(), _: anytype) core.Transform {
|
|
var delta = self.start.sub(self.end);
|
|
const d = delta.normalize();
|
|
const axz = std.math.atan2(-d.z, d.x) + core.radians(180.0);
|
|
const ay = -std.math.asin(d.y);
|
|
const mat1 = core.zm.matFromRollPitchYaw(0, 0, ay);
|
|
const mat2 = core.zm.rotationY(axz);
|
|
const len = delta.length();
|
|
return core.zm.mul(core.zm.mul(
|
|
core.zm.mul(mat1, mat2),
|
|
core.zm.scaling(len, len, len),
|
|
), core.zm.translationV(self.start.toZm()));
|
|
}
|
|
};
|
|
|
|
pub const DebugSphere = struct {
|
|
position: core.Vectorf,
|
|
radius: f32,
|
|
rotation: core.Quat,
|
|
|
|
pub fn resolve(self: @This(), _: anytype) core.Transform {
|
|
return core.zm.mul(core.zm.mul(
|
|
core.zm.matFromQuat(self.rotation),
|
|
core.zm.scaling(self.radius, self.radius, self.radius),
|
|
), core.zm.translationV(self.position.toZm()));
|
|
}
|
|
};
|
|
|
|
pub const DebugBox = struct {
|
|
position: core.Vectorf,
|
|
extents: core.Vectorf,
|
|
rotation: core.Quat,
|
|
|
|
pub fn resolve(self: @This(), _: anytype) core.Transform {
|
|
return core.zm.mul(core.zm.mul(
|
|
core.zm.matFromQuat(self.rotation),
|
|
core.zm.scalingV(self.extents.toZm()),
|
|
), core.zm.translationV(self.position.toZm()));
|
|
}
|
|
};
|
|
|
|
const DebugPrimitiveType = enum(u8) {
|
|
line = 0,
|
|
sphere = 1,
|
|
box = 2,
|
|
};
|
|
|
|
pub const DebugPrimitive = struct {
|
|
primitive: union(DebugPrimitiveType) {
|
|
line: DebugLine,
|
|
sphere: DebugSphere,
|
|
box: DebugBox,
|
|
},
|
|
color: core.Vectorf = .{ .x = 0.0, .y = 1.0, .z = 0.0 },
|
|
duration: f32 = 0.0,
|
|
|
|
pub fn resolve(self: @This()) core.Transform {
|
|
// comptime core.asserts(@sizeOf(DebugPrimitiveGpu) == DebugPrimitiveGpu.TargetSize, "");
|
|
|
|
switch (self.primitive) {
|
|
.line => |inner| {
|
|
return inner.resolve(.{});
|
|
},
|
|
.sphere => |inner| {
|
|
return inner.resolve(.{});
|
|
},
|
|
.box => |inner| {
|
|
return inner.resolve(.{});
|
|
},
|
|
}
|
|
unreachable;
|
|
|
|
// return core.implement_func_for_tagged_union_nonull(self.primitive, "resolve", core.Transform, .{});
|
|
}
|
|
};
|
|
|
|
const DebugPrimitiveGpu = struct {
|
|
const UnpaddedSize = @sizeOf(core.Transform) + @sizeOf(core.Vectorf);
|
|
const TargetSize = 80;
|
|
|
|
model: core.Transform,
|
|
color: core.Vectorf,
|
|
|
|
pad: [TargetSize - UnpaddedSize]u8 = std.mem.zeroes([TargetSize - UnpaddedSize]u8),
|
|
};
|
|
|
|
const DebugDrawSharedInstance = struct {};
|
|
|
|
const DebugSharedData = struct {
|
|
drawsThisFrame: std.ArrayListUnmanaged(DebugPrimitive) = .{},
|
|
lock: std.Thread.Mutex = .{},
|
|
|
|
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
|
|
self.drawsThisFrame.deinit(allocator);
|
|
}
|
|
};
|
|
|
|
const objectCount = 2048;
|
|
|
|
// ==================== End of primitives ============
|
|
|
|
var gDebugDrawSys: *@This() = undefined;
|
|
|
|
pub fn debugSphere(position: core.Vectorf, radius: f32, params: DebugDrawParams) void {
|
|
gDebugDrawSys.debugDraws.push(.{
|
|
.primitive = .{ .sphere = .{ .position = position, .radius = radius, .rotation = params.rotation } },
|
|
.color = params.color,
|
|
.duration = params.duration,
|
|
}) catch return;
|
|
}
|
|
|
|
pub fn debugLine(start: core.Vectorf, end: core.Vectorf, params: DebugDrawParams) void {
|
|
gDebugDrawSys.debugDraws.push(.{
|
|
.primitive = .{ .line = .{
|
|
.start = start,
|
|
.end = end,
|
|
} },
|
|
.color = params.color,
|
|
.duration = params.duration,
|
|
}) catch return;
|
|
}
|
|
|
|
pub fn debugBox(position: core.Vectorf, extents: core.Vectorf, params: DebugDrawParams) void {
|
|
gDebugDrawSys.debugDraws.push(.{
|
|
.primitive = .{
|
|
.box = .{ .position = position, .extents = extents, .rotation = params.rotation },
|
|
},
|
|
.color = params.color,
|
|
.duration = params.duration,
|
|
}) catch return;
|
|
}
|
|
|
|
const DebugDrawParams = core.DebugDrawParams;
|
|
|
|
const core = @import("core");
|
|
const rend = @import("../rend.zig");
|
|
const assets = @import("assets");
|
|
const texture = rend.texture;
|
|
const sdl3 = @import("sdl3");
|
|
const gpu = sdl3.gpu;
|
|
const std = @import("std");
|
|
|
|
const debug_frag = @import("debug.frag");
|
|
const debug_vert = @import("debug.vert");
|
|
const Renderer = @import("renderer.zig").Renderer;
|
|
const primitive_box align(8) = @embedFile("embedded/primitive_box.obj").*;
|
|
const primitive_line align(8) = @embedFile("embedded/primitive_line.obj").*;
|
|
const primitive_sphere align(8) = @embedFile("embedded/primitive_sphere.obj").*;
|