pub const papyrus = @import("papyrus"); const core = @import("core"); const rend = @import("rend"); const std = @import("std"); const sdl = @import("sdl3"); const gpu = sdl.gpu; allocator: std.mem.Allocator, runtime: *papyrus.PapyrusRuntime, screenContext: *papyrus.Context, quadMesh: ?rend.IndexedMesh = null, quadMeshName: core.Name, stringArena: std.heap.ArenaAllocator, drawList: papyrus.DrawList, first: bool = true, rectPipeline: *gpu.GPUGraphicsPipeline = undefined, // textPipeline: *gpu.GPUGraphicsPipeline = undefined, pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "ui.runtime"); pub fn create(allocator: std.mem.Allocator) !*@This() { const self: *@This() = try allocator.create(@This()); core.engine_logs("UI module started"); self.* = .{ .allocator = allocator, .screenContext = undefined, .runtime = try papyrus.PapyrusRuntime.create(allocator), .stringArena = std.heap.ArenaAllocator.init(allocator), .quadMeshName = core.MakeName("m_screenPlane"), .drawList = papyrus.DrawList.init(allocator), }; self.screenContext = try self.runtime.addContext(); return self; } pub fn setup(self: *@This()) !void { core.graphics_log("imgui startup", .{}); try rend.registerRendererObject(@This(), self); } pub fn destroy(self: *@This()) void { self.screenContext.destroy(); self.runtime.destroy(); self.allocator.destroy(self); } pub fn createTextPipeline() !void {} pub fn createRectPipeline(self: *@This()) !void { const ctx = rend.context(); const vertex = try ctx.loadShader("rect.vert", rect_vert.LoadArgs); const fragment = try ctx.loadShader("rect.frag", rect_frag.LoadArgs); var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo); pci.vertex_shader = vertex; pci.fragment_shader = fragment; var attributes = try ctx.generateVertexAttributeList(); defer attributes.deinit(); 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.target_info.num_color_targets = 1; pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{ .{ .format = ctx.swapchainTargetFormat, .blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState), }, }; pci.rasterizer_state.fill_mode = .fillmodeFill; self.rectPipeline = ctx.device.createGPUGraphicsPipeline(&pci); } pub fn postRender(p: *anyopaque, cmd: *gpu.GPUCommandBuffer) void { const self: *@This() = @ptrCast(@alignCast(p)); if (self.quadMesh == null) { self.quadMesh = rend.getMeshByName(&self.quadMeshName); } self.drawToTarget(self.screenContext, rend.context().state.swapchainTargetTexture.?); _ = cmd; } pub fn drawToTarget(self: *@This(), ctx: *papyrus.Context, targetTexture: *gpu.GPUTexture) void { ctx.makeDrawList(&self.drawList, &self.stringArena) catch return; if (self.first) { self.first = false; for (self.drawList.items) |item| { core.engine_log("{any}", .{item}); } } const cmd = rend.context().state.cmd.?; const targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroInit(gpu.GPUColorTargetInfo, .{ .texture = targetTexture, .load_op = .loadopLoad, .store_op = .storeopStore, }); const pass = cmd.beginGPURenderPass(&targetInfo, 1, null); pass.bindGPUVertexBuffers(1, &.{ .buffer = rend.context().meshPool.vertexBuffer, .offset = 0 }, 1); pass.bindGPUIndexBuffer(&.{ .buffer = rend.context().meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit); for (self.drawList.items) |item| { switch (item.primitive) { .Rect => {}, .Text => {}, } } pass.endGPURenderPass(); } pub const rect_frag = @import("rect.frag"); pub const rect_vert = @import("rect.vert");