got text rendering working again... sort of...
This commit is contained in:
parent
5ee9ff78e0
commit
dabeeb5fc8
|
|
@ -31,7 +31,8 @@ pub const PrimitiveText = struct {
|
||||||
text: papyrus.LocText, // unsafe for multithreading
|
text: papyrus.LocText, // unsafe for multithreading
|
||||||
color: papyrus.Color,
|
color: papyrus.Color,
|
||||||
textSize: f32,
|
textSize: f32,
|
||||||
rendererHash: u32,
|
//rendererHash: u32,
|
||||||
|
fontHandle: u32, // this is just a core.Name handle
|
||||||
flags: packed struct {
|
flags: packed struct {
|
||||||
setSourceGeometry: bool,
|
setSourceGeometry: bool,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@ pub const FontAtlas = struct {
|
||||||
fromArchive: bool = false,
|
fromArchive: bool = false,
|
||||||
filePath: []const u8,
|
filePath: []const u8,
|
||||||
|
|
||||||
rendererHash: u32 = 0, // optional field to associate this atlas with an identifier to the renderer implementation
|
// rendererHash: u32 = 0, // optional field to associate this atlas with an identifier to the renderer implementation
|
||||||
|
fontHandle: u32 = 0,
|
||||||
isEmbedded: bool = false,
|
isEmbedded: bool = false,
|
||||||
|
|
||||||
isSDF: bool = false,
|
isSDF: bool = false,
|
||||||
|
|
@ -102,6 +103,10 @@ pub const FontAtlas = struct {
|
||||||
|
|
||||||
var rv = try initFromArchive(allocator, archiveBytes);
|
var rv = try initFromArchive(allocator, archiveBytes);
|
||||||
rv.fromArchive = true;
|
rv.fromArchive = true;
|
||||||
|
|
||||||
|
var fontAsName = core.MakeName(fontName);
|
||||||
|
rv.fontHandle = fontAsName.handle();
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,8 @@ pub const PapyrusRuntime = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
fontCache: *FontCache,
|
fontCache: *FontCache,
|
||||||
|
|
||||||
|
var gPapyrusRuntime: *PapyrusRuntime = undefined;
|
||||||
|
|
||||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
const self = try allocator.create(@This());
|
const self = try allocator.create(@This());
|
||||||
|
|
||||||
|
|
@ -359,6 +361,8 @@ pub const PapyrusRuntime = struct {
|
||||||
.fontCache = try FontCache.create(allocator),
|
.fontCache = try FontCache.create(allocator),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gPapyrusRuntime = self;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -368,6 +372,17 @@ pub const PapyrusRuntime = struct {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get() *@This() {
|
||||||
|
return gPapyrusRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getFont(self: *@This(), nameHandle: u32) ?*FontAtlas {
|
||||||
|
if (self.fontCache.fonts.get(nameHandle)) |font| {
|
||||||
|
return font.atlas;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *@This()) void {
|
pub fn destroy(self: *@This()) void {
|
||||||
self.fontCache.destroy();
|
self.fontCache.destroy();
|
||||||
self.allocator.destroy(self);
|
self.allocator.destroy(self);
|
||||||
|
|
@ -1082,7 +1097,9 @@ pub const Context = struct {
|
||||||
},
|
},
|
||||||
} });
|
} });
|
||||||
|
|
||||||
try drawList.append(.{ .node = node, .primitive = .{
|
try drawList.append(.{
|
||||||
|
.node = node,
|
||||||
|
.primitive = .{
|
||||||
.Text = .{
|
.Text = .{
|
||||||
.tl = dlb.resolvedPos.add(.{ .x = 3 + 5, .y = 3 }),
|
.tl = dlb.resolvedPos.add(.{ .x = 3 + 5, .y = 3 }),
|
||||||
.size = .{ .x = dlb.resolvedSize.x, .y = panel.titleSize },
|
.size = .{ .x = dlb.resolvedSize.x, .y = panel.titleSize },
|
||||||
|
|
@ -1090,12 +1107,14 @@ pub const Context = struct {
|
||||||
.renderMode = n.textMode,
|
.renderMode = n.textMode,
|
||||||
.color = panel.titleColor,
|
.color = panel.titleColor,
|
||||||
.textSize = panel.titleSize - 4,
|
.textSize = panel.titleSize - 4,
|
||||||
.rendererHash = panel.font.rendererHash,
|
// .rendererHash = panel.font.rendererHash,
|
||||||
|
.fontHandle = panel.font.fontHandle,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.setSourceGeometry = false,
|
.setSourceGeometry = false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} });
|
},
|
||||||
|
});
|
||||||
|
|
||||||
self._displayLayout.items[node.index] = .{
|
self._displayLayout.items[node.index] = .{
|
||||||
.baseSize = n.baseSize,
|
.baseSize = n.baseSize,
|
||||||
|
|
@ -1131,7 +1150,9 @@ pub const Context = struct {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.DisplayText => |txt| {
|
.DisplayText => |txt| {
|
||||||
try drawList.append(.{ .node = node, .primitive = .{
|
try drawList.append(.{
|
||||||
|
.node = node,
|
||||||
|
.primitive = .{
|
||||||
.Text = .{
|
.Text = .{
|
||||||
.tl = dlb.resolvedPos,
|
.tl = dlb.resolvedPos,
|
||||||
.size = n.size,
|
.size = n.size,
|
||||||
|
|
@ -1139,12 +1160,14 @@ pub const Context = struct {
|
||||||
.renderMode = n.textMode,
|
.renderMode = n.textMode,
|
||||||
.color = n.style.foregroundColor,
|
.color = n.style.foregroundColor,
|
||||||
.textSize = txt.textSize,
|
.textSize = txt.textSize,
|
||||||
.rendererHash = txt.font.atlas.rendererHash,
|
.fontHandle = txt.font.atlas.fontHandle,
|
||||||
|
// .rendererHash = txt.font.atlas.rendererHash,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.setSourceGeometry = false,
|
.setSourceGeometry = false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} });
|
},
|
||||||
|
});
|
||||||
|
|
||||||
self._displayLayout.items[node.index] = .{
|
self._displayLayout.items[node.index] = .{
|
||||||
.baseSize = n.baseSize,
|
.baseSize = n.baseSize,
|
||||||
|
|
@ -1184,7 +1207,7 @@ pub const Context = struct {
|
||||||
var yOffset: f32 = sizePerLine;
|
var yOffset: f32 = sizePerLine;
|
||||||
const width = defaultHeight / 2 * 120;
|
const width = defaultHeight / 2 * 120;
|
||||||
|
|
||||||
const fontHash = self.fontCache.defaultMonoFont.atlas.rendererHash;
|
const fontHandle = self.fontCache.defaultMonoFont.atlas.fontHandle;
|
||||||
|
|
||||||
try self.mousePick.addMousePickInfo(&self, drawList);
|
try self.mousePick.addMousePickInfo(&self, drawList);
|
||||||
|
|
||||||
|
|
@ -1235,7 +1258,7 @@ pub const Context = struct {
|
||||||
.renderMode = .NoControl,
|
.renderMode = .NoControl,
|
||||||
.color = Color.Yellow,
|
.color = Color.Yellow,
|
||||||
.textSize = defaultHeight,
|
.textSize = defaultHeight,
|
||||||
.rendererHash = fontHash,
|
.fontHandle = fontHandle,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.setSourceGeometry = false,
|
.setSourceGeometry = false,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,8 @@ pub fn addToDrawList(dlb: DrawListBuilder) !void {
|
||||||
.text = dlb.n.text,
|
.text = dlb.n.text,
|
||||||
.renderMode = dlb.n.textMode,
|
.renderMode = dlb.n.textMode,
|
||||||
.textSize = button.textSize,
|
.textSize = button.textSize,
|
||||||
.rendererHash = button.font.atlas.rendererHash,
|
.fontHandle = button.font.atlas.fontHandle,
|
||||||
|
// .rendererHash = button.font.atlas.rendererHash,
|
||||||
// TODO: figure out centering.
|
// TODO: figure out centering.
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.setSourceGeometry = false,
|
.setSourceGeometry = false,
|
||||||
|
|
|
||||||
|
|
@ -70,18 +70,21 @@ pub fn addToDrawList(dlb: DrawListBuilder) !void {
|
||||||
|
|
||||||
try drawlist.append(.{
|
try drawlist.append(.{
|
||||||
.node = dlb.node,
|
.node = dlb.node,
|
||||||
.primitive = .{ .Text = .{
|
.primitive = .{
|
||||||
|
.Text = .{
|
||||||
.color = foregroundColor,
|
.color = foregroundColor,
|
||||||
.tl = dlb.resolvedPos.add(.{ .x = 5, .y = 2 }),
|
.tl = dlb.resolvedPos.add(.{ .x = 5, .y = 2 }),
|
||||||
.size = dlb.resolvedSize,
|
.size = dlb.resolvedSize,
|
||||||
.renderMode = dlb.n.textMode,
|
.renderMode = dlb.n.textMode,
|
||||||
.textSize = te.textSize,
|
.textSize = te.textSize,
|
||||||
.text = LocText.fromUtf8(te.editText.items),
|
.text = LocText.fromUtf8(te.editText.items),
|
||||||
.rendererHash = te.font.atlas.rendererHash,
|
.fontHandle = te.font.atlas.fontHandle,
|
||||||
|
//.rendererHash = te.font.atlas.rendererHash,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.setSourceGeometry = (te.entryState == .Pressed),
|
.setSourceGeometry = (te.entryState == .Pressed),
|
||||||
},
|
},
|
||||||
} },
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// someday i need to re-implement this layout algorithm
|
// someday i need to re-implement this layout algorithm
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ pub fn resetAllLines(self: *@This()) !void {
|
||||||
// get yourself a new GeometryLineEntry
|
// get yourself a new GeometryLineEntry
|
||||||
pub fn recycleOrNewGeoLine(self: *@This()) GeometryLineEntry {
|
pub fn recycleOrNewGeoLine(self: *@This()) GeometryLineEntry {
|
||||||
if (self.geoPool.items.len > 0) {
|
if (self.geoPool.items.len > 0) {
|
||||||
const newLine = self.geoPool.pop();
|
const newLine = self.geoPool.pop().?;
|
||||||
return .{
|
return .{
|
||||||
.yOffset = 0,
|
.yOffset = 0,
|
||||||
.lineGeo = newLine,
|
.lineGeo = newLine,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ pub const gltfLoader = @import("meshes/gltfLoader.zig");
|
||||||
// switch out based on backend implementation
|
// switch out based on backend implementation
|
||||||
pub const renderer = @import("sgpu/renderer.zig");
|
pub const renderer = @import("sgpu/renderer.zig");
|
||||||
pub const context = renderer.context; // context getter func
|
pub const context = renderer.context; // context getter func
|
||||||
|
pub const Renderer = renderer.Renderer;
|
||||||
|
|
||||||
pub const MeshPool = renderer.MeshPool;
|
pub const MeshPool = renderer.MeshPool;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,88 @@ pub fn getMiplevelFromSize(size: core.Vector2u) u32 {
|
||||||
return std.math.log2(@as(u32, @intCast(@max(size.x, size.y)))) + 1;
|
return std.math.log2(@as(u32, @intCast(@max(size.x, size.y)))) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const UploadTextureBytesOptions = struct {
|
||||||
|
bytes: []const u8,
|
||||||
|
size: core.Vector2u,
|
||||||
|
use_blocky: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn uploadTextureFromBytes(self: *@This(), name: *core.Name, opts: UploadTextureBytesOptions) !*Texture {
|
||||||
|
const cmd = self.device.acquireGPUCommandBuffer();
|
||||||
|
const mipLevelCount = getMiplevelFromSize(opts.size);
|
||||||
|
|
||||||
|
core.engine_log("creating mipmap level {d}", .{mipLevelCount});
|
||||||
|
|
||||||
|
const gpuTexture = self.device.createGPUTexture(&std.mem.zeroInit(gpu.GPUTextureCreateInfo, .{
|
||||||
|
.type = .texturetype2d,
|
||||||
|
.format = .textureformatR8g8b8a8UnormSrgb,
|
||||||
|
.usage = .{ .textureusageSampler = true, .textureusageColorTarget = true },
|
||||||
|
.width = opts.size.x,
|
||||||
|
.height = opts.size.y,
|
||||||
|
.layer_count_or_depth = 1,
|
||||||
|
.num_levels = mipLevelCount,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const transferBuffer = self.device.createGPUTransferBuffer(&.{
|
||||||
|
.usage = .transferbufferusageUpload,
|
||||||
|
.size = opts.size.x * opts.size.y * @sizeOf(u32),
|
||||||
|
.props = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
const data: [*]u8 = @ptrCast(self.device.mapGPUTransferBuffer(transferBuffer, false));
|
||||||
|
for (opts.bytes, 0..) |pixel, i| {
|
||||||
|
data[i] = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.device.unmapGPUTransferBuffer(transferBuffer);
|
||||||
|
|
||||||
|
const copyPass = cmd.beginGPUCopyPass();
|
||||||
|
// copyPass.uploadToGPUTexture(source: [*c]const GPUTextureTransferInfo, destination: [*c]const GPUTextureRegion, cycle: bool)
|
||||||
|
copyPass.uploadToGPUTexture(
|
||||||
|
&.{
|
||||||
|
.transfer_buffer = transferBuffer,
|
||||||
|
.offset = 0,
|
||||||
|
.pixels_per_row = opts.size.x,
|
||||||
|
.rows_per_layer = opts.size.y,
|
||||||
|
},
|
||||||
|
&std.mem.zeroInit(gpu.GPUTextureRegion, .{
|
||||||
|
.texture = gpuTexture,
|
||||||
|
.w = opts.size.x,
|
||||||
|
.h = opts.size.y,
|
||||||
|
.d = 1,
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
copyPass.endGPUCopyPass();
|
||||||
|
|
||||||
|
if (mipLevelCount > 1)
|
||||||
|
cmd.generateMipmapsForGPUTexture(gpuTexture);
|
||||||
|
|
||||||
|
if (!cmd.submitGPUCommandBuffer()) {
|
||||||
|
return error.CopyFailed;
|
||||||
|
}
|
||||||
|
self.device.releaseGPUTransferBuffer(transferBuffer);
|
||||||
|
|
||||||
|
const tex = try self.allocator.create(Texture);
|
||||||
|
tex.* = .{
|
||||||
|
.texture = gpuTexture,
|
||||||
|
.id = 0,
|
||||||
|
.format = .f32_rgba,
|
||||||
|
.usage = .{},
|
||||||
|
.size = opts.size,
|
||||||
|
.name = name.*,
|
||||||
|
};
|
||||||
|
|
||||||
|
tex.samplerMode = if (opts.use_blocky) .blocky else .linear;
|
||||||
|
|
||||||
|
self.map.put(self.allocator, name.handle(), tex) catch return error.UnableToLoad;
|
||||||
|
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uploadTextureFromPath(self: *@This(), name: core.Name, path: []const u8) !*Texture {
|
pub fn uploadTextureFromPath(self: *@This(), name: core.Name, path: []const u8) !*Texture {
|
||||||
var png = try core.png.PngContents.initFromFS(core.fs(), self.allocator, path);
|
var png = try core.png.PngContents.initFromFS(core.fs(), self.allocator, path);
|
||||||
defer png.deinit();
|
defer png.deinit();
|
||||||
|
|
|
||||||
|
|
@ -1057,6 +1057,8 @@ pub fn textureExists(name: []const u8) bool {
|
||||||
return context().textureList.requestMap.contains(n.handle());
|
return context().textureList.requestMap.contains(n.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const addVertexAttributesFromStruct = @import("vertexAttributes.zig").addVertexAttributesFromStruct;
|
||||||
|
|
||||||
pub const CustomMeshRenderFunc = *const fn (?*anyopaque) void;
|
pub const CustomMeshRenderFunc = *const fn (?*anyopaque) void;
|
||||||
pub const GPUTextureType = gpu.GPUTexture;
|
pub const GPUTextureType = gpu.GPUTexture;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
const shaderTypes = sdl3.shaderTypes;
|
||||||
|
|
||||||
|
pub fn getVertexFormatFromType(comptime T: type) gpu.GPUVertexElementFormat {
|
||||||
|
switch (T) {
|
||||||
|
f32 => {
|
||||||
|
return gpu.GPUVertexElementFormat.vertexelementformatFloat;
|
||||||
|
},
|
||||||
|
u32, u8vec4 => {
|
||||||
|
return gpu.GPUVertexElementFormat.vertexelementformatUint;
|
||||||
|
},
|
||||||
|
vec2 => {
|
||||||
|
return gpu.GPUVertexElementFormat.vertexelementformatFloat2;
|
||||||
|
},
|
||||||
|
vec3 => {
|
||||||
|
return gpu.GPUVertexElementFormat.vertexelementformatFloat3;
|
||||||
|
},
|
||||||
|
vec4 => {
|
||||||
|
return gpu.GPUVertexElementFormat.vertexelementformatFloat4;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
@compileError("unable to get vertex format from subtype" ++ @typeName(T));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addVertexAttributesFromStruct(comptime T: type, pci: *gpu.GPUGraphicsPipelineCreateInfo) !std.ArrayList(gpu.GPUVertexAttribute) {
|
||||||
|
const ctx = rend.context();
|
||||||
|
var list = std.ArrayList(gpu.GPUVertexAttribute).init(ctx.allocator);
|
||||||
|
var offset: u32 = 0;
|
||||||
|
|
||||||
|
inline for (@typeInfo(T).@"struct".fields) |field| {
|
||||||
|
try Renderer.addAttribute(&list, &offset, @sizeOf(field.type), getVertexFormatFromType(field.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
pci.vertex_input_state = .{
|
||||||
|
.num_vertex_buffers = 1,
|
||||||
|
.vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{
|
||||||
|
.{ .slot = 0, .pitch = @sizeOf(T), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 },
|
||||||
|
},
|
||||||
|
.num_vertex_attributes = @intCast(list.items.len),
|
||||||
|
.vertex_attributes = @ptrCast(list.items.ptr),
|
||||||
|
};
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const int = shaderTypes.int;
|
||||||
|
pub const uint = shaderTypes.uint;
|
||||||
|
|
||||||
|
pub const vec2 = shaderTypes.vec2;
|
||||||
|
pub const u8vec4 = shaderTypes.u8vec4;
|
||||||
|
pub const vec3 = shaderTypes.vec3;
|
||||||
|
pub const vec4 = shaderTypes.vec4;
|
||||||
|
pub const mat4 = shaderTypes.mat4;
|
||||||
|
pub const float = shaderTypes.float;
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const assets = @import("assets");
|
||||||
|
const core = @import("core");
|
||||||
|
const platform = @import("platform");
|
||||||
|
const rend = @import("../rend.zig");
|
||||||
|
const Renderer = rend.renderer.Renderer;
|
||||||
|
const sdl3 = @import("sdl3");
|
||||||
|
pub const gpu = sdl3.gpu;
|
||||||
|
|
@ -10,9 +10,13 @@ fn noprint(comptime fmt: []const u8, args: anytype) void {
|
||||||
_ = args;
|
_ = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
const debugPrint = noprint;
|
const debugPrint = __debugPrint;
|
||||||
|
|
||||||
pub const SubprocessTask = struct {
|
pub const SubprocessTask = struct {
|
||||||
|
// any and all string values added to this subprocesstask must exist and be usable across threads.
|
||||||
|
// i reeccomend converting strings to a name via core.MakeName before passing them over. if they are freed
|
||||||
|
// by the time the thread uses it, it will crash .
|
||||||
|
|
||||||
mutex: std.Thread.Mutex = .{},
|
mutex: std.Thread.Mutex = .{},
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
args: []const []const u8,
|
args: []const []const u8,
|
||||||
|
|
@ -36,16 +40,21 @@ pub const SubprocessTask = struct {
|
||||||
self.stderr = .{};
|
self.stderr = .{};
|
||||||
self.stdout = .{};
|
self.stdout = .{};
|
||||||
debugPrint("running task", .{});
|
debugPrint("running task", .{});
|
||||||
|
for (self.args) |arg| {
|
||||||
|
debugPrint("{s}", .{arg});
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("cwd = {s}", .{self.workingDir.?});
|
||||||
self.child = std.process.Child.init(self.args, self.allocator);
|
self.child = std.process.Child.init(self.args, self.allocator);
|
||||||
self.child.?.stdout_behavior = .Inherit;
|
self.child.?.stdout_behavior = .Inherit;
|
||||||
self.child.?.stderr_behavior = .Inherit;
|
self.child.?.stderr_behavior = .Inherit;
|
||||||
self.child.?.cwd = self.workingDir;
|
self.child.?.cwd = self.workingDir;
|
||||||
|
|
||||||
_ = self.child.?.spawn() catch {};
|
_ = self.child.?.spawn() catch unreachable;
|
||||||
self.mutex.unlock();
|
self.mutex.unlock();
|
||||||
|
|
||||||
// close and cleanup everything
|
// close and cleanup everything
|
||||||
self.waitInner() catch unreachable;
|
self.waitInner() catch {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ float4 main(
|
||||||
|
|
||||||
if(!scissor(pixelPosition, position, size))
|
if(!scissor(pixelPosition, position, size))
|
||||||
{
|
{
|
||||||
discard;
|
// discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
float4 textColor = float4(
|
float4 textColor = float4(
|
||||||
|
|
@ -34,7 +34,7 @@ float4 main(
|
||||||
getBlueFromUint(Color),
|
getBlueFromUint(Color),
|
||||||
getAlphaFromUint(Color));
|
getAlphaFromUint(Color));
|
||||||
|
|
||||||
if(isSdf == 1)
|
if(isSdf == 0)
|
||||||
{
|
{
|
||||||
float dist = tex.r;
|
float dist = tex.r;
|
||||||
float width = fwidth(dist);
|
float width = fwidth(dist);
|
||||||
|
|
@ -70,12 +70,20 @@ float4 main(
|
||||||
//outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
//outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* debug test.
|
#if 0
|
||||||
if(!rect(pixelPosition, position, size))
|
float4 outFragColor = 0;
|
||||||
{
|
|
||||||
outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
//if(!rect(pixelPosition, position, size))
|
||||||
}
|
//{
|
||||||
*/
|
//}
|
||||||
|
outFragColor = float4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
return outFragColor + o * 0.00001;
|
||||||
|
#else
|
||||||
|
float alpha = 1.0;
|
||||||
|
float gray = dot(textColor.xyz, float3(0.2126, 0.7152, 0.0722));
|
||||||
|
o = float4(textColor.xyz, tex.x);//pow(tex.x / gray, 1/(2.2)) );//textColor.* alpha);
|
||||||
|
return float4(1.0, 1.0, 1.0, tex.x) + o * 0.00001;
|
||||||
|
#endif
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"types" : {
|
"types" : {
|
||||||
"_13" : {
|
"_12" : {
|
||||||
"name" : "FontInfo",
|
"name" : "FontInfo",
|
||||||
"members" : [
|
"members" : [
|
||||||
{
|
{
|
||||||
|
|
@ -16,32 +16,32 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "size",
|
"name" : "size",
|
||||||
"type" : "float",
|
"type" : "vec2",
|
||||||
"offset" : 8
|
"offset" : 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "isSdf",
|
"name" : "isSdf",
|
||||||
"type" : "uint",
|
"type" : "uint",
|
||||||
"offset" : 12
|
"offset" : 16
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "pad0",
|
"name" : "pad0",
|
||||||
"type" : "uint",
|
"type" : "uint",
|
||||||
"offset" : 16
|
"offset" : 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "pad2",
|
"name" : "pad2",
|
||||||
"type" : "vec2",
|
"type" : "vec2",
|
||||||
"offset" : 20
|
"offset" : 24
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_12" : {
|
"_11" : {
|
||||||
"name" : "type.StructuredBuffer.FontInfo",
|
"name" : "type.StructuredBuffer.FontInfo",
|
||||||
"members" : [
|
"members" : [
|
||||||
{
|
{
|
||||||
"name" : "_m0",
|
"name" : "_m0",
|
||||||
"type" : "_13",
|
"type" : "_12",
|
||||||
"array" : [
|
"array" : [
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
|
|
@ -65,11 +65,6 @@
|
||||||
"name" : "in.var.TEXCOORD1",
|
"name" : "in.var.TEXCOORD1",
|
||||||
"location" : 1
|
"location" : 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type" : "vec2",
|
|
||||||
"name" : "in.var.TEXCOORD2",
|
|
||||||
"location" : 2
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type" : "uint",
|
"type" : "uint",
|
||||||
"name" : "in.var.TEXCOORD3",
|
"name" : "in.var.TEXCOORD3",
|
||||||
|
|
@ -101,7 +96,7 @@
|
||||||
],
|
],
|
||||||
"ssbos" : [
|
"ssbos" : [
|
||||||
{
|
{
|
||||||
"type" : "_12",
|
"type" : "_11",
|
||||||
"name" : "fontBuffer",
|
"name" : "fontBuffer",
|
||||||
"readonly" : true,
|
"readonly" : true,
|
||||||
"block_size" : 0,
|
"block_size" : 0,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ VSOut main(Input v)
|
||||||
float2 t = v.Position + s.position;
|
float2 t = v.Position + s.position;
|
||||||
o.pixelPosition = t;
|
o.pixelPosition = t;
|
||||||
|
|
||||||
o.Position = float4((t / Extents) * 2 + float2(-1.0f, -1.0f), 0.0, 1.0);
|
float4 fp = float4((t / Extents) * 2 + float2(-1.0f, -1.0f), 0.0, 1.0);
|
||||||
|
fp.y = -fp.y;
|
||||||
|
o.Position = fp;
|
||||||
|
|
||||||
o.UV = v.UV;
|
o.UV = v.UV;
|
||||||
o.Color = v.Color;
|
o.Color = v.Color;
|
||||||
|
|
|
||||||
|
|
@ -16,23 +16,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "size",
|
"name" : "size",
|
||||||
"type" : "float",
|
"type" : "vec2",
|
||||||
"offset" : 8
|
"offset" : 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "isSdf",
|
"name" : "isSdf",
|
||||||
"type" : "uint",
|
"type" : "uint",
|
||||||
"offset" : 12
|
"offset" : 16
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "pad0",
|
"name" : "pad0",
|
||||||
"type" : "uint",
|
"type" : "uint",
|
||||||
"offset" : 16
|
"offset" : 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name" : "pad2",
|
"name" : "pad2",
|
||||||
"type" : "vec2",
|
"type" : "vec2",
|
||||||
"offset" : 20
|
"offset" : 24
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
struct FontInfo {
|
struct FontInfo {
|
||||||
float2 position; // 8 bytes alignment 0
|
float2 position; // 8 bytes alignment 0
|
||||||
float size; // 8 bytes alignment 8
|
float2 size; // 8 bytes alignment 8
|
||||||
uint isSdf; // 4 bytes 16
|
uint isSdf; // 4 bytes 16
|
||||||
uint pad0; // 4 bytes
|
uint pad0; // 4 bytes
|
||||||
float2 pad2; // 8 bytes
|
float2 pad2; // 8 bytes
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ rectPipeline: *gpu.GPUGraphicsPipeline = undefined,
|
||||||
textPipeline: *gpu.GPUGraphicsPipeline = undefined,
|
textPipeline: *gpu.GPUGraphicsPipeline = undefined,
|
||||||
|
|
||||||
tempDrawCommand: std.ArrayListUnmanaged(DrawCommand) = .{},
|
tempDrawCommand: std.ArrayListUnmanaged(DrawCommand) = .{},
|
||||||
|
fontTextures: std.AutoHashMapUnmanaged(u32, *rend.Texture) = .{},
|
||||||
|
|
||||||
|
textRenderers: std.AutoHashMapUnmanaged(*papyrus.Context, *TextRenderer) = .{},
|
||||||
|
|
||||||
const DrawCommand = union(enum(u8)) {
|
const DrawCommand = union(enum(u8)) {
|
||||||
rect: struct {
|
rect: struct {
|
||||||
|
|
@ -28,6 +31,7 @@ const DrawCommand = union(enum(u8)) {
|
||||||
},
|
},
|
||||||
text: struct {
|
text: struct {
|
||||||
ssboIndex: u32,
|
ssboIndex: u32,
|
||||||
|
textMeshBuffer: *text_renderer.TextMeshBuffer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -95,7 +99,7 @@ const ScreenBuffers = struct {
|
||||||
pub fn init(device: *gpu.GPUDevice) @This() {
|
pub fn init(device: *gpu.GPUDevice) @This() {
|
||||||
return .{
|
return .{
|
||||||
.rect = SsboBuffer.init(device, @sizeOf(rect_frag.Scene), 4096),
|
.rect = SsboBuffer.init(device, @sizeOf(rect_frag.Scene), 4096),
|
||||||
.text = undefined,
|
.text = SsboBuffer.init(device, @sizeOf(text_frag.FontInfo), 4096),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -117,6 +121,7 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.screenContext = try self.runtime.addContext();
|
self.screenContext = try self.runtime.addContext();
|
||||||
|
try self.textRenderers.put(allocator, self.screenContext, try TextRenderer.create(allocator));
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
@ -132,9 +137,39 @@ pub fn setup(self: *@This()) !void {
|
||||||
try self.createTextPipeline();
|
try self.createTextPipeline();
|
||||||
|
|
||||||
self.screenBuffers = ScreenBuffers.init(ctx.device);
|
self.screenBuffers = ScreenBuffers.init(ctx.device);
|
||||||
|
|
||||||
|
const fontCache = papyrus.PapyrusRuntime.get().fontCache;
|
||||||
|
|
||||||
|
// upload and create textures for all the pre-initialized fonts
|
||||||
|
var iter = fontCache.fonts.valueIterator();
|
||||||
|
while (iter.next()) |font| {
|
||||||
|
const bytes = try font.atlas.makeBitmapRGBA(self.allocator);
|
||||||
|
defer self.allocator.free(bytes);
|
||||||
|
|
||||||
|
const formattedName = try std.fmt.allocPrint(self.stringArena.allocator(), "papyrusFont:{s}", .{font.name.utf8()});
|
||||||
|
|
||||||
|
var name = core.MakeName(formattedName);
|
||||||
|
|
||||||
|
const texture = try rend.context().textureList.uploadTextureFromBytes(&name, .{
|
||||||
|
.bytes = bytes,
|
||||||
|
.size = .{
|
||||||
|
.x = @intCast(font.atlas.atlasSize.x),
|
||||||
|
.y = @intCast(font.atlas.atlasSize.y),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
try self.fontTextures.put(self.allocator, font.atlas.fontHandle, texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *@This()) void {
|
pub fn destroy(self: *@This()) void {
|
||||||
|
var iterator = self.textRenderers.valueIterator();
|
||||||
|
|
||||||
|
while (iterator.next()) |next| {
|
||||||
|
next.*.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fontTextures.deinit(self.allocator);
|
||||||
|
self.textRenderers.deinit(self.allocator);
|
||||||
self.screenContext.destroy();
|
self.screenContext.destroy();
|
||||||
self.stringArena.deinit();
|
self.stringArena.deinit();
|
||||||
self.runtime.destroy();
|
self.runtime.destroy();
|
||||||
|
|
@ -154,7 +189,7 @@ pub fn createTextPipeline(self: *@This()) !void {
|
||||||
pci.vertex_shader = vertex;
|
pci.vertex_shader = vertex;
|
||||||
pci.fragment_shader = fragment;
|
pci.fragment_shader = fragment;
|
||||||
|
|
||||||
var attributes = try textRenderer.addVertexAttributes(&pci);
|
var attributes = try text_renderer.addVertexAttributes(&pci);
|
||||||
defer attributes.deinit();
|
defer attributes.deinit();
|
||||||
|
|
||||||
pci.target_info.num_color_targets = 1;
|
pci.target_info.num_color_targets = 1;
|
||||||
|
|
@ -162,11 +197,20 @@ pub fn createTextPipeline(self: *@This()) !void {
|
||||||
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
|
pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{
|
||||||
.{
|
.{
|
||||||
.format = ctx.swapchainTargetFormat,
|
.format = ctx.swapchainTargetFormat,
|
||||||
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
|
.blend_state = std.mem.zeroInit(gpu.GPUColorTargetBlendState, .{
|
||||||
|
.alpha_blend_op = .blendopAdd,
|
||||||
|
.color_blend_op = .blendopAdd,
|
||||||
|
.src_color_blendfactor = .blendfactorSrcAlpha,
|
||||||
|
.dst_color_blendfactor = .blendfactorOneMinusSrcAlpha,
|
||||||
|
.src_alpha_blendfactor = .blendfactorSrcAlpha,
|
||||||
|
.dst_alpha_blendfactor = .blendfactorOneMinusSrcAlpha,
|
||||||
|
.enable_blend = true,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pci.rasterizer_state.fill_mode = .fillmodeFill;
|
pci.rasterizer_state.fill_mode = .fillmodeFill;
|
||||||
|
pci.rasterizer_state.cull_mode = .cullmodeNone;
|
||||||
self.textPipeline = ctx.device.createGPUGraphicsPipeline(&pci);
|
self.textPipeline = ctx.device.createGPUGraphicsPipeline(&pci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,6 +246,7 @@ pub fn createRectPipeline(self: *@This()) !void {
|
||||||
};
|
};
|
||||||
|
|
||||||
pci.rasterizer_state.fill_mode = .fillmodeFill;
|
pci.rasterizer_state.fill_mode = .fillmodeFill;
|
||||||
|
pci.rasterizer_state.cull_mode = .cullmodeNone;
|
||||||
self.rectPipeline = ctx.device.createGPUGraphicsPipeline(&pci);
|
self.rectPipeline = ctx.device.createGPUGraphicsPipeline(&pci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,12 +263,35 @@ pub fn postRender(p: *anyopaque, cmd: *gpu.GPUCommandBuffer) void {
|
||||||
pub fn onShaderReload(p: *anyopaque) void {
|
pub fn onShaderReload(p: *anyopaque) void {
|
||||||
const self: *@This() = @ptrCast(@alignCast(p));
|
const self: *@This() = @ptrCast(@alignCast(p));
|
||||||
self.createRectPipeline() catch return; // you think i give a remote fuck about leaks? this is a debug function son.
|
self.createRectPipeline() catch return; // you think i give a remote fuck about leaks? this is a debug function son.
|
||||||
|
self.createTextPipeline() catch return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getTextRenderer(self: *@This(), ctx: *papyrus.Context) ?*TextRenderer {
|
||||||
|
if (self.textRenderers.get(ctx)) |tr| {
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
core.engine_err("unable to get text renderer for papyrus context", .{});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getFontSampler(self: *@This(), fontHandle: u32) ?gpu.GPUTextureSamplerBinding {
|
||||||
|
if (self.fontTextures.get(fontHandle)) |ft| {
|
||||||
|
const rv = gpu.GPUTextureSamplerBinding{
|
||||||
|
.sampler = if (ft.samplerMode == .blocky) rend.context().blockySampler else rend.context().linearSampler,
|
||||||
|
.texture = ft.texture,
|
||||||
|
};
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Context, targetTexture: *gpu.GPUTexture) void {
|
pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Context, targetTexture: *gpu.GPUTexture) void {
|
||||||
ctx.makeDrawList(&self.drawList, &self.stringArena) catch return;
|
ctx.makeDrawList(&self.drawList, &self.stringArena) catch return;
|
||||||
|
|
||||||
|
const textRenderer = self.getTextRenderer(ctx);
|
||||||
|
|
||||||
var rectIndex: u32 = 0;
|
var rectIndex: u32 = 0;
|
||||||
|
var textIndex: u32 = 0;
|
||||||
// map ssbos
|
// map ssbos
|
||||||
|
|
||||||
const renderer = rend.context();
|
const renderer = rend.context();
|
||||||
|
|
@ -232,6 +300,7 @@ pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Co
|
||||||
// upload rectangle ssbos
|
// upload rectangle ssbos
|
||||||
{
|
{
|
||||||
const rectBuffer = self.screenBuffers.rect.mapSlice(rect_frag.Scene, renderer.device);
|
const rectBuffer = self.screenBuffers.rect.mapSlice(rect_frag.Scene, renderer.device);
|
||||||
|
const textBuffer = self.screenBuffers.text.mapSlice(text_frag.FontInfo, renderer.device);
|
||||||
|
|
||||||
for (self.drawList.items) |item| {
|
for (self.drawList.items) |item| {
|
||||||
switch (item.primitive) {
|
switch (item.primitive) {
|
||||||
|
|
@ -267,11 +336,39 @@ pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Co
|
||||||
|
|
||||||
rectIndex += 1;
|
rectIndex += 1;
|
||||||
},
|
},
|
||||||
.Text => {},
|
.Text => |text| {
|
||||||
|
if (textRenderer) |tr| {
|
||||||
|
const node = item.node;
|
||||||
|
|
||||||
|
const meshBuffer = tr.uploadMesh(copyPass, node, text) catch return;
|
||||||
|
|
||||||
|
textBuffer[textIndex] = .{
|
||||||
|
.isSdf = if (meshBuffer.isSDF) 1 else 0,
|
||||||
|
.position = .{ .x = text.tl.x, .y = text.tl.y },
|
||||||
|
.size = .{ .x = text.size.x, .y = text.size.y },
|
||||||
|
.pad0 = undefined,
|
||||||
|
.pad2 = undefined,
|
||||||
|
};
|
||||||
|
// textBuffer[textIndex] = .{
|
||||||
|
// .. fill this shit out
|
||||||
|
// };
|
||||||
|
|
||||||
|
self.tempDrawCommand.append(self.allocator, .{
|
||||||
|
.text = .{
|
||||||
|
.ssboIndex = textIndex,
|
||||||
|
.textMeshBuffer = meshBuffer,
|
||||||
|
},
|
||||||
|
}) catch unreachable;
|
||||||
|
textIndex += 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.screenBuffers.rect.submit(copyPass);
|
self.screenBuffers.rect.submit(copyPass);
|
||||||
self.screenBuffers.rect.unmap(renderer.device);
|
self.screenBuffers.rect.unmap(renderer.device);
|
||||||
|
|
||||||
|
self.screenBuffers.text.submit(copyPass);
|
||||||
|
self.screenBuffers.text.unmap(renderer.device);
|
||||||
}
|
}
|
||||||
|
|
||||||
copyPass.endGPUCopyPass();
|
copyPass.endGPUCopyPass();
|
||||||
|
|
@ -280,7 +377,15 @@ pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Co
|
||||||
self.first = false;
|
self.first = false;
|
||||||
|
|
||||||
for (self.drawList.items) |item| {
|
for (self.drawList.items) |item| {
|
||||||
core.engine_log("{any}", .{item});
|
//core.engine_log("{any}", .{item});
|
||||||
|
switch (item.primitive) {
|
||||||
|
.Text => |text| {
|
||||||
|
core.engine_log("text position {d}, {d} size {d}x{d}", .{ text.tl.x, text.tl.y, text.size.x, text.size.y });
|
||||||
|
},
|
||||||
|
.Rect => |rect| {
|
||||||
|
core.engine_log("rect position {d}, {d} size {d}x{d}", .{ rect.tl.x, rect.tl.y, rect.size.x, rect.size.y });
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,8 +415,6 @@ pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Co
|
||||||
}
|
}
|
||||||
|
|
||||||
const pass = cmd.beginGPURenderPass(&targetInfo, 1, null);
|
const pass = cmd.beginGPURenderPass(&targetInfo, 1, null);
|
||||||
pass.bindGPUVertexBuffers(0, &.{ .buffer = rend.context().meshPool.vertexBuffer, .offset = 0 }, 1);
|
|
||||||
pass.bindGPUIndexBuffer(&.{ .buffer = rend.context().meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
|
|
||||||
pass.bindGPUFragmentSamplers(0, &.{ .sampler = rend.context().blockySampler, .texture = rend.context().defaultTexture.texture }, 1);
|
pass.bindGPUFragmentSamplers(0, &.{ .sampler = rend.context().blockySampler, .texture = rend.context().defaultTexture.texture }, 1);
|
||||||
|
|
||||||
if (self.quadMesh) |quadMesh| {
|
if (self.quadMesh) |quadMesh| {
|
||||||
|
|
@ -322,11 +425,40 @@ pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Co
|
||||||
pass.bindGPUVertexStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
pass.bindGPUVertexStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
||||||
pass.bindGPUFragmentStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
pass.bindGPUFragmentStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
||||||
|
|
||||||
// bind samplers if needed
|
pass.bindGPUVertexBuffers(0, &.{ .buffer = rend.context().meshPool.vertexBuffer, .offset = 0 }, 1);
|
||||||
|
pass.bindGPUIndexBuffer(&.{ .buffer = rend.context().meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
|
||||||
|
|
||||||
|
// todo: bind samplers for textures.
|
||||||
|
|
||||||
pass.drawGPUIndexedPrimitives(quadMesh.index.size, 1, quadMesh.index.start, @intCast(quadMesh.vertex.start), rect.ssboIndex);
|
pass.drawGPUIndexedPrimitives(quadMesh.index.size, 1, quadMesh.index.start, @intCast(quadMesh.vertex.start), rect.ssboIndex);
|
||||||
},
|
},
|
||||||
.text => |text| {
|
.text => |text| {
|
||||||
_ = text;
|
if (textRenderer) |tr| {
|
||||||
|
const tmb = text.textMeshBuffer;
|
||||||
|
tr.bindFontBuffersAndSamplers(pass, tmb);
|
||||||
|
|
||||||
|
pass.bindGPUVertexStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
||||||
|
pass.bindGPUFragmentStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
||||||
|
pass.bindGPUFragmentSamplers(0, @ptrCast(&self.getFontSampler(text.textMeshBuffer.fontHandle)), 1);
|
||||||
|
pass.bindGPUGraphicsPipeline(self.textPipeline);
|
||||||
|
|
||||||
|
pass.drawGPUIndexedPrimitives(tmb.indexCount, 1, 0, 0, text.ssboIndex);
|
||||||
|
|
||||||
|
// pass.drawGPUIndexedPrimitives(tr., fewafe, first_index: u32, vertex_offset: i32, first_instance: u32)
|
||||||
|
// pass.bindGPUVertexBuffers(0, &.{ .buffer = tr.getVertexBuffer(text.meshIndex), .offset = 0 }, 1);
|
||||||
|
// pass.bindGPUIndexBuffer(&.{ .buffer = tr.getIndexBuffer(text.meshIndex), .offset = 0 }, .indexelementsize32bit);
|
||||||
|
|
||||||
|
// pass.bindGPUFragmentSamplers(0, &tr.getFontSampler(text.meshIndex), 1);
|
||||||
|
|
||||||
|
// // pass.bindGPUFragmentSamplers(0, &.{
|
||||||
|
// // .sampler = rend.context().blockySampler,
|
||||||
|
// // .texture = rend.context().defaultTexture.texture,
|
||||||
|
// // }, 1);
|
||||||
|
|
||||||
|
// pass.bindGPUVertexStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
||||||
|
// pass.bindGPUFragmentStorageBuffers(0, &self.screenBuffers.rect.buffer, 1);
|
||||||
|
//
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +469,8 @@ pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Co
|
||||||
self.tempDrawCommand.clearRetainingCapacity();
|
self.tempDrawCommand.clearRetainingCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const textRenderer = @import("textRenderer.zig");
|
pub const text_renderer = @import("textRenderer.zig");
|
||||||
|
pub const TextRenderer = text_renderer.TextRenderer;
|
||||||
|
|
||||||
pub const rect_frag = @import("rect.frag");
|
pub const rect_frag = @import("rect.frag");
|
||||||
pub const rect_vert = @import("rect.vert");
|
pub const rect_vert = @import("rect.vert");
|
||||||
|
|
|
||||||
|
|
@ -4,44 +4,508 @@
|
||||||
const vec2 = text_frag.vec2;
|
const vec2 = text_frag.vec2;
|
||||||
const u8vec4 = text_frag.u8vec4;
|
const u8vec4 = text_frag.u8vec4;
|
||||||
|
|
||||||
const TextMeshVertex = extern struct {
|
pub const TextMeshVertex = extern struct {
|
||||||
position: vec2,
|
position: vec2,
|
||||||
uv: vec2,
|
uv: vec2,
|
||||||
Color: u8vec4,
|
color: u8vec4,
|
||||||
|
// pad0: u32 = 0, hope it isnt needed..
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn addVertexAttributes(pci: *gpu.GPUGraphicsPipelineCreateInfo) !std.ArrayList(gpu.GPUVertexAttribute) {
|
pub fn addVertexAttributes(pci: *gpu.GPUGraphicsPipelineCreateInfo) !std.ArrayList(gpu.GPUVertexAttribute) {
|
||||||
const ctx = rend.context();
|
return rend.renderer.addVertexAttributesFromStruct(TextMeshVertex, pci);
|
||||||
|
|
||||||
var list = std.ArrayList(gpu.GPUVertexAttribute).init(ctx.allocator);
|
|
||||||
|
|
||||||
var offset: u32 = 0;
|
|
||||||
{
|
|
||||||
const Renderer = rend.renderer.Renderer;
|
|
||||||
try Renderer.addAttribute(&list, &offset, @sizeOf(f32) * 2, .vertexelementformatFloat2);
|
|
||||||
try Renderer.addAttribute(&list, &offset, @sizeOf(f32) * 2, .vertexelementformatFloat2);
|
|
||||||
try Renderer.addAttribute(&list, &offset, @sizeOf(u32), .vertexelementformatUint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pci.vertex_input_state = .{
|
pub fn TextBufferList(comptime T: type) type {
|
||||||
.num_vertex_buffers = 1,
|
return struct {
|
||||||
.vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{
|
slice: []T,
|
||||||
.{ .slot = 0, .pitch = @sizeOf(TextMeshVertex), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 },
|
count: u32 = 0,
|
||||||
},
|
|
||||||
.num_vertex_attributes = @intCast(list.items.len),
|
pub fn init(s: []T) @This() {
|
||||||
.vertex_attributes = @ptrCast(list.items.ptr),
|
return .{
|
||||||
|
.slice = s,
|
||||||
|
.count = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(self: *@This(), v: T) !void {
|
||||||
|
if (self.count < self.slice.len) {
|
||||||
|
self.slice[self.count] = v;
|
||||||
|
self.count += 1;
|
||||||
|
} else {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushQuadToWriters(
|
||||||
|
topLeft: core.Vector2f,
|
||||||
|
size: core.Vector2f,
|
||||||
|
uvTopLeft: core.Vector2f,
|
||||||
|
uvSize: core.Vector2f,
|
||||||
|
color: core.colors.Color,
|
||||||
|
vertex: *TextBufferList(TextMeshVertex),
|
||||||
|
index: *TextBufferList(u32),
|
||||||
|
) !void {
|
||||||
|
const o = vertex.count;
|
||||||
|
|
||||||
|
const quadColor: u8vec4 = .{
|
||||||
|
@intFromFloat(std.math.clamp(color.r, 0.0, 1.0) * 255),
|
||||||
|
@intFromFloat(std.math.clamp(color.g, 0.0, 1.0) * 255),
|
||||||
|
@intFromFloat(std.math.clamp(color.b, 0.0, 1.0) * 255),
|
||||||
|
@intFromFloat(std.math.clamp(color.a, 0.0, 1.0) * 255),
|
||||||
};
|
};
|
||||||
|
|
||||||
return list;
|
try vertex.push(.{
|
||||||
|
.position = @bitCast(topLeft),
|
||||||
|
.uv = @bitCast(uvTopLeft),
|
||||||
|
.color = quadColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
try vertex.push(.{
|
||||||
|
.position = @bitCast(topLeft.add(.{ .x = size.x })),
|
||||||
|
.uv = @bitCast(uvTopLeft.add(.{ .x = uvSize.x })),
|
||||||
|
.color = quadColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
try vertex.push(.{
|
||||||
|
.position = @bitCast(topLeft.add(size)),
|
||||||
|
.uv = @bitCast(uvTopLeft.add(uvSize)),
|
||||||
|
.color = quadColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
try vertex.push(.{
|
||||||
|
.position = @bitCast(topLeft.add(.{ .y = size.y })),
|
||||||
|
.uv = @bitCast(uvTopLeft.add(.{ .y = uvSize.y })),
|
||||||
|
.color = quadColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
try index.push(o + 0);
|
||||||
|
try index.push(o + 1);
|
||||||
|
try index.push(o + 2);
|
||||||
|
|
||||||
|
try index.push(o + 2);
|
||||||
|
try index.push(o + 3);
|
||||||
|
try index.push(o + 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TextBuffer = struct {
|
pub const TextRenderer = struct {
|
||||||
pub fn create() @This() {}
|
// operation of the text renderer.
|
||||||
|
// given
|
||||||
|
|
||||||
|
textInstances: std.ArrayListUnmanaged(*TextMeshBuffer) = .{},
|
||||||
|
deadList: std.ArrayListUnmanaged(*TextMeshBuffer) = .{},
|
||||||
|
assignedBuffers: std.AutoHashMapUnmanaged(papyrus.NodeHandle, *TextMeshBuffer) = .{},
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
lastUpdateTime: f64 = 0,
|
||||||
|
|
||||||
|
geo: *papyrus.TextRenderGeometry,
|
||||||
|
|
||||||
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
|
const self = try allocator.create(@This());
|
||||||
|
|
||||||
|
self.* = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.geo = try papyrus.TextRenderGeometry.create(allocator),
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uploadMesh(self: *@This(), copyPass: *gpu.GPUCopyPass, node: papyrus.NodeHandle, text: papyrus.DrawCommand.PrimitiveText) !*TextMeshBuffer {
|
||||||
|
var checkGC: bool = false;
|
||||||
|
if (core.getEngineTime() - self.lastUpdateTime > 5) {
|
||||||
|
checkGC = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign meshBuffer
|
||||||
|
const meshBuffer = try self.getOrAllocMeshBuffer(node, text);
|
||||||
|
|
||||||
|
// upload mesh data to mesh buffer
|
||||||
|
try self.updateMeshBuffer(meshBuffer, copyPass, text);
|
||||||
|
|
||||||
|
// set the fragment sampler
|
||||||
|
return meshBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateMeshBuffer(self: *@This(), meshBuffer: *TextMeshBuffer, copyPass: *gpu.GPUCopyPass, text: papyrus.DrawCommand.PrimitiveText) !void {
|
||||||
|
var z = tracy.ZoneN(@src(), "meshBuffer updates");
|
||||||
|
defer z.End();
|
||||||
|
const string = text.text.getRead();
|
||||||
|
if (string.len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var indexList = meshBuffer.mapIndexBufferWriter();
|
||||||
|
var vertexList = meshBuffer.mapVertexBufferWriter();
|
||||||
|
|
||||||
|
//const position = core.Vector2f{.x = text.tl.x, .y = text.tl.y};
|
||||||
|
const displaySize = text.textSize;
|
||||||
|
|
||||||
|
// 1. get the font Atlas from the textPrimitive's font file reference
|
||||||
|
const atlas = papyrus.PapyrusRuntime.get().getFont(text.fontHandle) orelse {
|
||||||
|
meshBuffer.unmapAll();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
meshBuffer.isSDF = atlas.isSDF;
|
||||||
|
meshBuffer.fontHandle = text.fontHandle;
|
||||||
|
|
||||||
|
const ratio = displaySize / atlas.fontSize;
|
||||||
|
const stride = @as(f32, @floatFromInt(atlas.glyphMetrics['l'].x)) * ratio;
|
||||||
|
|
||||||
|
var xOffset: f32 = 0;
|
||||||
|
var yOffset: f32 = 0;
|
||||||
|
|
||||||
|
const fontHeight = @as(f32, @floatFromInt(atlas.glyphMetrics['l'].y)) * ratio;
|
||||||
|
|
||||||
|
self.geo.setCharHeight(fontHeight);
|
||||||
|
self.geo.setPosition(text.tl);
|
||||||
|
try self.geo.addGeoLine(yOffset + text.tl.y, 0);
|
||||||
|
|
||||||
|
var largestXOffset: f32 = 0;
|
||||||
|
|
||||||
|
for (string, 0..) |ch, i| {
|
||||||
|
// if (i * 4 > self.mesh.maxVertexCount - 16) {
|
||||||
|
//break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (!atlas.hasGlyph[ch]) {
|
||||||
|
try self.geo.addCharGeo(text.tl.x + xOffset, stride, @intCast(i));
|
||||||
|
xOffset += stride;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 0 or ch == '\r') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == ' ' or (ch == '\n' and text.renderMode == .NoControl)) {
|
||||||
|
try self.geo.addCharGeo(text.tl.x + xOffset, stride, @intCast(i));
|
||||||
|
xOffset += stride;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// newline if we see newline and we're in simple or rich mode.
|
||||||
|
if (ch == '\n' and (text.renderMode == .Simple or text.renderMode == .Rich)) {
|
||||||
|
try self.geo.addCharGeo(text.tl.x + xOffset, stride, @intCast(i));
|
||||||
|
xOffset = 0;
|
||||||
|
yOffset += fontHeight * 1.2;
|
||||||
|
try self.geo.addGeoLine(yOffset + text.tl.y, @intCast(i));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == ' ') {
|
||||||
|
try self.geo.addCharGeo(text.tl.x + xOffset, stride, @intCast(i));
|
||||||
|
xOffset += stride;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const box = core.Vector2f.from(atlas.glyphBox1[ch]).fmul(ratio);
|
||||||
|
const metrics = core.Vector2f.from(atlas.glyphMetrics[ch]).fmul(ratio);
|
||||||
|
const baseMetrics = core.Vector2f.from(atlas.glyphMetrics[ch]);
|
||||||
|
|
||||||
|
const uv_tl = atlas.glyphCoordinates[ch][0];
|
||||||
|
|
||||||
|
xOffset += box.x;
|
||||||
|
|
||||||
|
//if (xOffset + box.x + metrics.x > self.boxSize.x) {
|
||||||
|
if (xOffset + box.x + metrics.x > text.size.x) {
|
||||||
|
xOffset = 0;
|
||||||
|
yOffset += fontHeight * 1.2;
|
||||||
|
try self.geo.addGeoLine(yOffset + text.tl.y, @intCast(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
const color = text.color;
|
||||||
|
|
||||||
|
const topLeft = core.Vector2f{
|
||||||
|
// .x = self.position.x + xOffset + box.x,
|
||||||
|
// .y = yOffset + self.position.y + box.y + fontHeight,
|
||||||
|
.x = xOffset,
|
||||||
|
.y = yOffset + box.y + fontHeight,
|
||||||
|
};
|
||||||
|
|
||||||
|
// core.engine_log("topleft = {d}x{d}", .{ topLeft.x, topLeft.y });
|
||||||
|
|
||||||
|
const metric_size = core.Vector2f{ .x = metrics.x, .y = metrics.y };
|
||||||
|
|
||||||
|
try pushQuadToWriters(
|
||||||
|
topLeft, // topLeft: core.Vector2f,
|
||||||
|
metric_size, // size: core.Vector2f,
|
||||||
|
.{ .x = uv_tl.x, .y = uv_tl.y }, // uvTopLeft: core.Vector2f,
|
||||||
|
.{
|
||||||
|
.x = baseMetrics.x / @as(f32, @floatFromInt(atlas.atlasSize.x)),
|
||||||
|
.y = baseMetrics.y / @as(f32, @floatFromInt(atlas.atlasSize.y)),
|
||||||
|
}, // uv size
|
||||||
|
.{ .r = color.r, .g = color.g, .b = color.b }, // color
|
||||||
|
// uvSize: core.Vector2f,
|
||||||
|
// color: core.colors.Color,
|
||||||
|
// vertex: *TextBufferList(TextMeshVertex),
|
||||||
|
// index: *TextBufferList(u32),
|
||||||
|
&vertexList,
|
||||||
|
&indexList,
|
||||||
|
);
|
||||||
|
|
||||||
|
// self.mesh.addQuad2D(
|
||||||
|
// topLeft,
|
||||||
|
// metric_size,
|
||||||
|
// .{ .x = uv_tl.x, .y = uv_tl.y }, // uv topleft
|
||||||
|
// .{
|
||||||
|
// .x = baseMetrics.x / @as(f32, @floatFromInt(atlas.atlasSize.x)),
|
||||||
|
// .y = baseMetrics.y / @as(f32, @floatFromInt(atlas.atlasSize.y)),
|
||||||
|
// }, // uv size
|
||||||
|
// .{ .r = color.r, .g = color.g, .b = color.b }, // color
|
||||||
|
// );
|
||||||
|
|
||||||
|
// todo insert geo
|
||||||
|
//try self.renderedGeo.addCharGeo(self.position.x + xOffset, box.x + metrics.x, @intCast(i));
|
||||||
|
//xOffset += box.x + metrics.x;
|
||||||
|
|
||||||
|
try self.geo.addCharGeo(text.tl.x + xOffset, metrics.x, @intCast(i));
|
||||||
|
xOffset += metrics.x;
|
||||||
|
|
||||||
|
if (xOffset > largestXOffset) {
|
||||||
|
largestXOffset = xOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.geo.addCharGeo(text.tl.x + xOffset, 200.0, @intCast(string.len));
|
||||||
|
const renderedSize = .{
|
||||||
|
.x = largestXOffset,
|
||||||
|
.y = yOffset + fontHeight * 1.2,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.geo.setBoundsX(text.tl.x, text.tl.x + renderedSize.x);
|
||||||
|
|
||||||
|
// pushQuadToWriters(
|
||||||
|
// // topLeft: core.Vector2f,
|
||||||
|
// // size: core.Vector2f,
|
||||||
|
// // uvTopLeft: core.Vector2f,
|
||||||
|
// // uvSize: core.Vector2f,
|
||||||
|
// // color: core.colors.Color,
|
||||||
|
// vertexList, // vertex: *TextBufferList(TextMeshVertex),
|
||||||
|
// indexList,
|
||||||
|
// ); //index: *TextBufferList(u32),);
|
||||||
|
|
||||||
|
meshBuffer.unmapAll();
|
||||||
|
|
||||||
|
meshBuffer.submitBuffers(copyPass);
|
||||||
|
|
||||||
|
meshBuffer.indexCount = indexList.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOrAllocMeshBuffer(self: *@This(), node: papyrus.NodeHandle, text: papyrus.DrawCommand.PrimitiveText) !*TextMeshBuffer {
|
||||||
|
const textLen = text.text.getRead().len;
|
||||||
|
|
||||||
|
// 1. get the assigned buffer;
|
||||||
|
if (self.assignedBuffers.get(node)) |buffer| {
|
||||||
|
if (textLen <= buffer.capacity) {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.engine_log("recycling text buffer {any}", .{node});
|
||||||
|
// if the buffer is too small for the text, dont use that one and add it to the dead list.
|
||||||
|
try self.deadList.append(self.allocator, buffer);
|
||||||
|
_ = self.assignedBuffers.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. if no text renderer is available, search through the deadList until you find a TextMeshBuffer
|
||||||
|
// which has a capacity large enough for the text we're trying to render.
|
||||||
|
{
|
||||||
|
var foundBuffer: ?*TextMeshBuffer = null;
|
||||||
|
var foundIndex: usize = 0;
|
||||||
|
|
||||||
|
for (self.deadList.items, 0..) |buffer, i| {
|
||||||
|
if (textLen <= buffer.capacity) {
|
||||||
|
try self.assignedBuffers.put(self.allocator, node, buffer);
|
||||||
|
foundIndex = i;
|
||||||
|
foundBuffer = buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundBuffer) |buffer| {
|
||||||
|
_ = self.deadList.swapRemove(foundIndex);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. if we don't have any allocations already available in the deadList create a new one large enough for this text
|
||||||
|
const textLengthBins: []const u32 = &.{ 32, 64, 256, 512, 1024, 4096, 8192, 8192 * 4 };
|
||||||
|
|
||||||
|
var newBufferLength: u32 = @intFromFloat(@as(f32, @floatFromInt(textLen)) * 1.5);
|
||||||
|
|
||||||
|
for (textLengthBins) |bin| {
|
||||||
|
if (textLen <= bin) {
|
||||||
|
newBufferLength = bin;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.engine_log("creating text buffer {any} with length {d} for textLen = {d}", .{ node, newBufferLength, textLen });
|
||||||
|
const newBuffer = try TextMeshBuffer.create(self.allocator, newBufferLength);
|
||||||
|
try self.assignedBuffers.put(self.allocator, node, newBuffer);
|
||||||
|
try self.textInstances.append(self.allocator, newBuffer);
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// once every 5 seconds mark the frame as a GC frame
|
||||||
|
// during a GC frame any TextMeshBuffers which aren't being used get kicked to the deadList
|
||||||
|
// any TextMeshBuffers which stay in the deadList for 10 or more GC frames get released and removed from the deadlist
|
||||||
|
|
||||||
|
pub fn bindFontBuffersAndSamplers(
|
||||||
|
self: *@This(),
|
||||||
|
pass: *gpu.GPURenderPass,
|
||||||
|
meshBuffer: *TextMeshBuffer,
|
||||||
|
) void {
|
||||||
|
pass.bindGPUVertexBuffers(0, &.{ .buffer = meshBuffer.vertexBuffer, .offset = 0 }, 1);
|
||||||
|
pass.bindGPUIndexBuffer(&.{ .buffer = meshBuffer.indexBuffer, .offset = 0 }, .indexelementsize32bit);
|
||||||
|
|
||||||
|
// pass.bindGPUFragmentSamplers(0, meshBuffer.fragmentSampler, 1);
|
||||||
|
|
||||||
|
//pass.bindGPUFragmentSamplers(0, &.{
|
||||||
|
//.sampler = rend.context().blockySampler,
|
||||||
|
//.texture = rend.context().defaultTexture.texture,
|
||||||
|
//}, 1);
|
||||||
|
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *@This()) void {
|
||||||
|
for (self.textInstances.items) |item| {
|
||||||
|
item.destroy(self.allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.geo.destroy();
|
||||||
|
self.assignedBuffers.deinit(self.allocator);
|
||||||
|
self.textInstances.deinit(self.allocator);
|
||||||
|
self.allocator.destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TextMeshBuffer = struct {
|
||||||
|
indexBuffer: *gpu.GPUBuffer = undefined,
|
||||||
|
vertexBuffer: *gpu.GPUBuffer = undefined,
|
||||||
|
indexTransferBuffer: *gpu.GPUTransferBuffer = undefined,
|
||||||
|
vertexTransferBuffer: *gpu.GPUTransferBuffer = undefined,
|
||||||
|
indexCount: u32 = 0,
|
||||||
|
isSDF: bool = true,
|
||||||
|
fontHandle: u32 = 0,
|
||||||
|
capacity: u32 = 0,
|
||||||
|
|
||||||
|
pub fn create(allocator: std.mem.Allocator, maxChars: u32) !*@This() {
|
||||||
|
var self: *@This() = try allocator.create(@This());
|
||||||
|
self.capacity = maxChars;
|
||||||
|
const vertexBufferCount: u32 = maxChars * 4;
|
||||||
|
const indexBufferCount: u32 = maxChars * 6;
|
||||||
|
|
||||||
|
const ctx = rend.context();
|
||||||
|
|
||||||
|
self.vertexTransferBuffer = ctx.device.createGPUTransferBuffer(&.{
|
||||||
|
.usage = .transferbufferusageUpload,
|
||||||
|
.size = vertexBufferCount * @sizeOf(TextMeshVertex),
|
||||||
|
.props = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.indexTransferBuffer = ctx.device.createGPUTransferBuffer(&.{
|
||||||
|
.usage = .transferbufferusageUpload,
|
||||||
|
.size = indexBufferCount * @sizeOf(u32),
|
||||||
|
.props = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.vertexBuffer = ctx.device.createGPUBuffer(&.{
|
||||||
|
.usage = .{ .bufferusageVertex = true },
|
||||||
|
.size = vertexBufferCount * @sizeOf(TextMeshVertex),
|
||||||
|
.props = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.indexBuffer = ctx.device.createGPUBuffer(&.{
|
||||||
|
.usage = .{ .bufferusageIndex = true },
|
||||||
|
.size = indexBufferCount * @sizeOf(u32),
|
||||||
|
.props = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
|
||||||
|
const ctx = rend.context();
|
||||||
|
|
||||||
|
ctx.device.releaseGPUBuffer(self.indexBuffer);
|
||||||
|
ctx.device.releaseGPUBuffer(self.vertexBuffer);
|
||||||
|
ctx.device.releaseGPUTransferBuffer(self.indexTransferBuffer);
|
||||||
|
ctx.device.releaseGPUTransferBuffer(self.vertexTransferBuffer);
|
||||||
|
|
||||||
|
allocator.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer management stuff below
|
||||||
|
pub fn mapIndexBuffer(self: *@This()) []u32 {
|
||||||
|
const ctx = rend.context();
|
||||||
|
|
||||||
|
var slice: []u32 = undefined;
|
||||||
|
|
||||||
|
slice.ptr = @ptrCast(@alignCast(ctx.device.mapGPUTransferBuffer(self.indexTransferBuffer, true)));
|
||||||
|
slice.len = self.capacity * 6;
|
||||||
|
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmapIndexBuffer(self: *@This()) void {
|
||||||
|
const ctx = rend.context();
|
||||||
|
ctx.device.unmapGPUTransferBuffer(self.indexTransferBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mapVertexBuffer(self: *@This()) []TextMeshVertex {
|
||||||
|
const ctx = rend.context();
|
||||||
|
|
||||||
|
var slice: []TextMeshVertex = undefined;
|
||||||
|
|
||||||
|
slice.ptr = @ptrCast(@alignCast(ctx.device.mapGPUTransferBuffer(self.vertexTransferBuffer, true)));
|
||||||
|
slice.len = self.capacity * 4;
|
||||||
|
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmapVertexBuffer(self: *@This()) void {
|
||||||
|
const ctx = rend.context();
|
||||||
|
ctx.device.unmapGPUTransferBuffer(self.vertexTransferBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mapVertexBufferWriter(self: *@This()) TextBufferList(TextMeshVertex) {
|
||||||
|
const slice = self.mapVertexBuffer();
|
||||||
|
return TextBufferList(TextMeshVertex).init(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mapIndexBufferWriter(self: *@This()) TextBufferList(u32) {
|
||||||
|
const slice = self.mapIndexBuffer();
|
||||||
|
return TextBufferList(u32).init(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmapAll(self: *@This()) void {
|
||||||
|
self.unmapIndexBuffer();
|
||||||
|
self.unmapVertexBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn submitBuffers(self: *@This(), copyPass: *gpu.GPUCopyPass) void {
|
||||||
|
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.indexTransferBuffer, .offset = 0 }, &.{
|
||||||
|
.buffer = self.indexBuffer,
|
||||||
|
.offset = 0,
|
||||||
|
.size = self.capacity * 6 * @sizeOf(u32),
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.vertexTransferBuffer, .offset = 0 }, &.{
|
||||||
|
.buffer = self.vertexBuffer,
|
||||||
|
.offset = 0,
|
||||||
|
.size = self.capacity * 4 * @sizeOf(TextMeshVertex),
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const text_frag = @import("text.frag");
|
pub const text_frag = @import("text.frag");
|
||||||
pub const text_vert = @import("text.vert");
|
pub const text_vert = @import("text.vert");
|
||||||
pub const rend = @import("rend");
|
pub const rend = @import("rend");
|
||||||
|
pub const core = @import("core");
|
||||||
|
pub const tracy = core.tracy;
|
||||||
pub const std = @import("std");
|
pub const std = @import("std");
|
||||||
const sdl = @import("sdl3");
|
const sdl = @import("sdl3");
|
||||||
const gpu = sdl.gpu;
|
const gpu = sdl.gpu;
|
||||||
|
const papyrus = @import("papyrus");
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -6,11 +6,10 @@ using namespace metal;
|
||||||
struct FontInfo
|
struct FontInfo
|
||||||
{
|
{
|
||||||
float2 position;
|
float2 position;
|
||||||
float size;
|
float2 size;
|
||||||
uint isSdf;
|
uint isSdf;
|
||||||
uint pad0;
|
uint pad0;
|
||||||
packed_float2 pad2;
|
float2 pad2;
|
||||||
char _m0_final_padding[4];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct type_StructuredBuffer_FontInfo
|
struct type_StructuredBuffer_FontInfo
|
||||||
|
|
@ -18,7 +17,7 @@ struct type_StructuredBuffer_FontInfo
|
||||||
FontInfo _m0[1];
|
FontInfo _m0[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
constant float _60 = {};
|
constant float _43 = {};
|
||||||
|
|
||||||
struct main0_out
|
struct main0_out
|
||||||
{
|
{
|
||||||
|
|
@ -29,77 +28,26 @@ struct main0_in
|
||||||
{
|
{
|
||||||
uint in_var_TEXCOORD0 [[user(locn0)]];
|
uint in_var_TEXCOORD0 [[user(locn0)]];
|
||||||
float2 in_var_TEXCOORD1 [[user(locn1)]];
|
float2 in_var_TEXCOORD1 [[user(locn1)]];
|
||||||
float2 in_var_TEXCOORD2 [[user(locn2)]];
|
|
||||||
uint in_var_TEXCOORD3 [[user(locn3)]];
|
uint in_var_TEXCOORD3 [[user(locn3)]];
|
||||||
};
|
};
|
||||||
|
|
||||||
fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_FontInfo& fontBuffer [[buffer(0)]], texture2d<float> Texture0 [[texture(0)]], sampler Sampler0 [[sampler(0)]])
|
fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_FontInfo& fontBuffer [[buffer(0)]], texture2d<float> Texture0 [[texture(0)]], sampler Sampler0 [[sampler(0)]])
|
||||||
{
|
{
|
||||||
main0_out out = {};
|
main0_out out = {};
|
||||||
float4 _68 = Texture0.sample(Sampler0, in.in_var_TEXCOORD1);
|
float4 _51 = Texture0.sample(Sampler0, in.in_var_TEXCOORD1);
|
||||||
bool _103;
|
float4 _65 = float4(float(in.in_var_TEXCOORD0 & 255u) * 0.0039215688593685626983642578125, float((in.in_var_TEXCOORD0 >> 8u) & 255u) * 0.0039215688593685626983642578125, float((in.in_var_TEXCOORD0 >> 16u) & 255u) * 0.0039215688593685626983642578125, _43);
|
||||||
do
|
float4 _73;
|
||||||
|
if (fontBuffer._m0[in.in_var_TEXCOORD3].isSdf == 0u)
|
||||||
{
|
{
|
||||||
bool _85;
|
float3 _71 = powr(_65.xyz, float3(2.2000000476837158203125));
|
||||||
if (in.in_var_TEXCOORD2.x >= fontBuffer._m0[in.in_var_TEXCOORD3].position.x)
|
_73 = float4(_71.x, _71.y, _71.z, _65.w);
|
||||||
{
|
|
||||||
_85 = in.in_var_TEXCOORD2.x <= (fontBuffer._m0[in.in_var_TEXCOORD3].position.x + fontBuffer._m0[in.in_var_TEXCOORD3].size);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_85 = false;
|
_73 = _65;
|
||||||
}
|
}
|
||||||
bool _92;
|
float _74 = _51.x;
|
||||||
if (_85)
|
out.out_var_SV_Target0 = float4(1.0, 1.0, 1.0, _74) + (float4(_73.xyz, _74) * 9.9999997473787516355514526367188e-06);
|
||||||
{
|
|
||||||
_92 = in.in_var_TEXCOORD2.y >= fontBuffer._m0[in.in_var_TEXCOORD3].position.y;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_92 = false;
|
|
||||||
}
|
|
||||||
bool _100;
|
|
||||||
if (_92)
|
|
||||||
{
|
|
||||||
_100 = in.in_var_TEXCOORD2.y <= (fontBuffer._m0[in.in_var_TEXCOORD3].position.y + fontBuffer._m0[in.in_var_TEXCOORD3].size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_100 = false;
|
|
||||||
}
|
|
||||||
if (_100)
|
|
||||||
{
|
|
||||||
_103 = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_103 = false;
|
|
||||||
break;
|
|
||||||
} while(false);
|
|
||||||
if (!_103)
|
|
||||||
{
|
|
||||||
discard_fragment();
|
|
||||||
}
|
|
||||||
float _109 = float(in.in_var_TEXCOORD0 & 255u) * 0.0039215688593685626983642578125;
|
|
||||||
float _113 = float((in.in_var_TEXCOORD0 >> 8u) & 255u) * 0.0039215688593685626983642578125;
|
|
||||||
float _117 = float((in.in_var_TEXCOORD0 >> 16u) & 255u) * 0.0039215688593685626983642578125;
|
|
||||||
float4 _180;
|
|
||||||
if (fontBuffer._m0[in.in_var_TEXCOORD3].isSdf == 1u)
|
|
||||||
{
|
|
||||||
float _123 = _68.x;
|
|
||||||
float _124 = fwidth(_123);
|
|
||||||
float _125 = 0.529411792755126953125 - _124;
|
|
||||||
float _126 = 0.529411792755126953125 + _124;
|
|
||||||
float2 _132 = (dfdx(in.in_var_TEXCOORD1) + dfdy(in.in_var_TEXCOORD1)) * 0.3540000021457672119140625;
|
|
||||||
float4 _139 = float4(in.in_var_TEXCOORD1 - _132, in.in_var_TEXCOORD1 + _132);
|
|
||||||
float4 _170 = float4(_109, _113, _117, (fast::clamp(smoothstep(_125, _126, _123), 0.0, 1.0) + (0.5 * (((fast::clamp(smoothstep(_125, _126, Texture0.sample(Sampler0, _139.xy).x), 0.0, 1.0) + fast::clamp(smoothstep(_125, _126, Texture0.sample(Sampler0, _139.zw).x), 0.0, 1.0)) + fast::clamp(smoothstep(_125, _126, Texture0.sample(Sampler0, _139.xw).x), 0.0, 1.0)) + fast::clamp(smoothstep(_125, _126, Texture0.sample(Sampler0, _139.zy).x), 0.0, 1.0)))) * 0.3333333432674407958984375);
|
|
||||||
float3 _172 = powr(_170.xyz, float3(2.2000000476837158203125));
|
|
||||||
_180 = float4(_172.x, _172.y, _172.z, _170.w);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_180 = float4(_109, _113, _117, powr(_68.x / dot(float4(_109, _113, _117, _60).xyz, float3(0.2125999927520751953125, 0.715200006961822509765625, 0.072200000286102294921875)), 0.4545454680919647216796875));
|
|
||||||
}
|
|
||||||
out.out_var_SV_Target0 = _180;
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ using namespace metal;
|
||||||
struct FontInfo
|
struct FontInfo
|
||||||
{
|
{
|
||||||
float2 position;
|
float2 position;
|
||||||
float size;
|
float2 size;
|
||||||
uint isSdf;
|
uint isSdf;
|
||||||
uint pad0;
|
uint pad0;
|
||||||
packed_float2 pad2;
|
float2 pad2;
|
||||||
char _m0_final_padding[4];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct type_StructuredBuffer_FontInfo
|
struct type_StructuredBuffer_FontInfo
|
||||||
|
|
@ -43,11 +42,14 @@ vertex main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniform
|
||||||
{
|
{
|
||||||
main0_out out = {};
|
main0_out out = {};
|
||||||
float2 _46 = in.in_var_TEXCOORD0 + fontBuffer._m0[gl_InstanceIndex].position;
|
float2 _46 = in.in_var_TEXCOORD0 + fontBuffer._m0[gl_InstanceIndex].position;
|
||||||
|
float2 _51 = ((_46 / Uniforms.Extents) * 2.0) + float2(-1.0);
|
||||||
|
float4 _54 = float4(_51, 0.0, 1.0);
|
||||||
|
_54.y = -_51.y;
|
||||||
out.out_var_TEXCOORD0 = in.in_var_TEXCOORD2;
|
out.out_var_TEXCOORD0 = in.in_var_TEXCOORD2;
|
||||||
out.out_var_TEXCOORD1 = in.in_var_TEXCOORD1;
|
out.out_var_TEXCOORD1 = in.in_var_TEXCOORD1;
|
||||||
out.out_var_TEXCOORD2 = _46;
|
out.out_var_TEXCOORD2 = _46;
|
||||||
out.out_var_TEXCOORD3 = gl_InstanceIndex;
|
out.out_var_TEXCOORD3 = gl_InstanceIndex;
|
||||||
out.gl_Position = float4(((_46 / Uniforms.Extents) * 2.0) + float2(-1.0), 0.0, 1.0);
|
out.gl_Position = _54;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -97,7 +97,7 @@ pub const ExternGameObject = struct {
|
||||||
ctx.getPanel(panel2).hasTitle = true;
|
ctx.getPanel(panel2).hasTitle = true;
|
||||||
//ctx.get(panel2).anchor = .TopRight;
|
//ctx.get(panel2).anchor = .TopRight;
|
||||||
// ctx.get(panel2).fill = .FillY; todo, this is a bug. why.
|
// ctx.get(panel2).fill = .FillY; todo, this is a bug. why.
|
||||||
ctx.get(panel2).pos = .{ .x = 200, .y = 200 };
|
ctx.get(panel2).pos = .{ .x = 900, .y = 200 };
|
||||||
ctx.get(panel2).size = .{ .x = 100, .y = 100 };
|
ctx.get(panel2).size = .{ .x = 100, .y = 100 };
|
||||||
ctx.get(panel2).style.backgroundColor = ui.ModernStyle.GreyDark;
|
ctx.get(panel2).style.backgroundColor = ui.ModernStyle.GreyDark;
|
||||||
ctx.getPanel(panel2).titleColor = ui.ModernStyle.GreyDark;
|
ctx.getPanel(panel2).titleColor = ui.ModernStyle.GreyDark;
|
||||||
|
|
|
||||||
|
|
@ -36,20 +36,22 @@ pub fn prepare(self: *@This()) !void {
|
||||||
pub fn tick(self: *@This(), dt: f64) void {
|
pub fn tick(self: *@This(), dt: f64) void {
|
||||||
_ = dt;
|
_ = dt;
|
||||||
|
|
||||||
|
std.time.sleep(10 * 1000 * 1000);
|
||||||
|
|
||||||
self.tickTasks() catch {};
|
self.tickTasks() catch {};
|
||||||
|
|
||||||
if (self.activeCommand != null or self.commandQueue.count() > 0) {
|
if (self.activeCommand != null or self.commandQueue.count() > 0) {
|
||||||
ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{});
|
//ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{});
|
||||||
|
|
||||||
if (ig.begin("tasks", null, .{
|
// if (ig.begin("tasks", null, .{
|
||||||
.always_auto_resize = true,
|
// .always_auto_resize = true,
|
||||||
.no_title_bar = true,
|
// .no_title_bar = true,
|
||||||
.no_move = true,
|
// .no_move = true,
|
||||||
.no_resize = true,
|
// .no_resize = true,
|
||||||
.no_collapse = true,
|
// .no_collapse = true,
|
||||||
})) {}
|
// })) {}
|
||||||
|
|
||||||
ig.end();
|
// ig.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize docking layout on first run
|
// Initialize docking layout on first run
|
||||||
|
|
@ -68,8 +70,12 @@ pub fn tick(self: *@This(), dt: f64) void {
|
||||||
if (ig.begin("Toolbox Window", null, .{})) {
|
if (ig.begin("Toolbox Window", null, .{})) {
|
||||||
ig.textf("This is a docked toolbox window", .{});
|
ig.textf("This is a docked toolbox window", .{});
|
||||||
|
|
||||||
if (ig.button("Recompile Shaders", .{})) {
|
if (ig.button("Compile Shaders", .{})) {
|
||||||
start_RecompileShaders() catch {};
|
start_CompileShaders() catch {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ig.button("Launch TrenchBroom", .{})) {
|
||||||
|
start_TrenchBroom() catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ig.separator();
|
ig.separator();
|
||||||
|
|
@ -91,7 +97,118 @@ pub fn tick(self: *@This(), dt: f64) void {
|
||||||
ig.end();
|
ig.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_RecompileShaders() !void {}
|
fn findRootDir(allocator: std.mem.Allocator) ![]u8 {
|
||||||
|
var current_dir = std.fs.cwd();
|
||||||
|
|
||||||
|
// Start from current directory and walk up
|
||||||
|
var path_components = std.ArrayList([]const u8).init(allocator);
|
||||||
|
defer path_components.deinit();
|
||||||
|
|
||||||
|
// Try current directory first
|
||||||
|
current_dir.access("content.txt", .{}) catch {
|
||||||
|
// content.txt not found, need to walk up
|
||||||
|
var temp_dir = current_dir;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Try to go up one directory
|
||||||
|
const parent_dir = temp_dir.openDir("..", .{}) catch return error.RootNotFound;
|
||||||
|
temp_dir = parent_dir;
|
||||||
|
|
||||||
|
// Check if content.txt exists in parent directory
|
||||||
|
temp_dir.access("content.txt", .{}) catch {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Found content.txt, get the absolute path
|
||||||
|
return try temp_dir.realpathAlloc(allocator, ".");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// content.txt found in current directory
|
||||||
|
return try current_dir.realpathAlloc(allocator, ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_CompileShaders() !void {
|
||||||
|
const self = core.getEngineObject(@This()) orelse return;
|
||||||
|
|
||||||
|
const root_dir = findRootDir(self.allocator) catch {
|
||||||
|
core.engine_log("Could not find BacklogEngine root directory (content.txt not found)", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer self.allocator.free(root_dir);
|
||||||
|
core.engine_log("Found root directory: {s}", .{root_dir});
|
||||||
|
|
||||||
|
const compile_shaders_dir = try std.fs.path.join(self.allocator, &.{ root_dir, "tools", "compileShaders" });
|
||||||
|
defer self.allocator.free(compile_shaders_dir);
|
||||||
|
|
||||||
|
var compile_shaders_name = core.MakeName(compile_shaders_dir);
|
||||||
|
|
||||||
|
core.engine_log("CompileShaders directory: {s}", .{compile_shaders_dir});
|
||||||
|
|
||||||
|
const exe_path = try std.fs.path.join(self.allocator, &.{ compile_shaders_dir, "zig-out", "bin", "compileShaders.exe" });
|
||||||
|
defer self.allocator.free(exe_path);
|
||||||
|
core.engine_log("Looking for executable at: {s}", .{exe_path});
|
||||||
|
|
||||||
|
// Check if compileShaders.exe exists
|
||||||
|
if (std.fs.cwd().access(exe_path, .{})) |_| {
|
||||||
|
// File exists, run it directly
|
||||||
|
core.engine_log("CompileShaders executable found, running directly", .{});
|
||||||
|
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
|
||||||
|
self.allocator,
|
||||||
|
&.{"zig-out/bin/compileShaders.exe"},
|
||||||
|
compile_shaders_name.utf8(),
|
||||||
|
) });
|
||||||
|
} else |_| {
|
||||||
|
// File doesn't exist, build it first
|
||||||
|
core.engine_log("CompileShaders executable not found, building first", .{});
|
||||||
|
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
|
||||||
|
self.allocator,
|
||||||
|
&.{ "zig", "build", "install" },
|
||||||
|
compile_shaders_name.utf8(),
|
||||||
|
) });
|
||||||
|
|
||||||
|
// Then run it
|
||||||
|
core.engine_log("Queuing compileShaders execution after build", .{});
|
||||||
|
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
|
||||||
|
self.allocator,
|
||||||
|
&.{"zig-out/bin/compileShaders.exe"},
|
||||||
|
compile_shaders_name.utf8(),
|
||||||
|
) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn launchDetachedProcess(allocator: std.mem.Allocator, argv: []const []const u8, cwd: ?[]const u8) !void {
|
||||||
|
var child = std.process.Child.init(argv, allocator);
|
||||||
|
child.cwd = cwd;
|
||||||
|
child.stdin_behavior = .Ignore;
|
||||||
|
child.stdout_behavior = .Ignore;
|
||||||
|
child.stderr_behavior = .Ignore;
|
||||||
|
|
||||||
|
try child.spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_TrenchBroom() !void {
|
||||||
|
const self = core.getEngineObject(@This()) orelse return;
|
||||||
|
|
||||||
|
const root_dir = findRootDir(self.allocator) catch {
|
||||||
|
core.engine_log("Could not find BacklogEngine root directory (content.txt not found)", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer self.allocator.free(root_dir);
|
||||||
|
|
||||||
|
const trenchbroom_dir = try std.fs.path.join(self.allocator, &.{ root_dir, "tools", "trenchbroom" });
|
||||||
|
defer self.allocator.free(trenchbroom_dir);
|
||||||
|
|
||||||
|
var trenchbroom_name = core.MakeName(trenchbroom_dir);
|
||||||
|
|
||||||
|
core.engine_log("Launching TrenchBroom from: {s}", .{trenchbroom_dir});
|
||||||
|
|
||||||
|
try launchDetachedProcess(
|
||||||
|
self.allocator,
|
||||||
|
&.{"TrenchBroom.exe"},
|
||||||
|
trenchbroom_name.utf8(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn tickTasks(self: *@This()) !void {
|
fn tickTasks(self: *@This()) !void {
|
||||||
if (self.activeCommand == null) {
|
if (self.activeCommand == null) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue