Mesh pool storage + mesh asset loader interfaces
This commit is contained in:
parent
67d07367da
commit
b9f3f75972
|
|
@ -1,4 +1,3 @@
|
|||
//!
|
||||
// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
|
||||
b: *std.Build,
|
||||
nw_builder: *std.Build,
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ pub const AssetLoaderInterface = struct {
|
|||
|
||||
assetType: []const u8,
|
||||
loadAsset: *const fn (*anyopaque, AssetRef, ?AssetPropertiesBag) AssetLoaderError!void,
|
||||
destroy: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||
// destroy: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||
discardAll: *const fn (*anyopaque) void,
|
||||
|
||||
pub fn from(comptime assetType: []const u8, comptime TargetType: type) @This() {
|
||||
|
|
@ -131,7 +131,6 @@ pub const AssetLoaderInterface = struct {
|
|||
.typeAlign = @alignOf(TargetType),
|
||||
.loadAsset = wrappedFuncs.loadAsset,
|
||||
.discardAll = wrappedFuncs.discardAll,
|
||||
.destroy = wrappedFuncs.destroy,
|
||||
.assetType = assetType,
|
||||
};
|
||||
|
||||
|
|
@ -151,10 +150,6 @@ pub const AssetLoaderRef = struct {
|
|||
pub fn discardAll(self: @This()) void {
|
||||
self.vtable.discardAll(self.target);
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
|
||||
self.vtable.destroy(self.target, allocator);
|
||||
}
|
||||
};
|
||||
|
||||
pub const AssetReferenceSys = struct {
|
||||
|
|
@ -178,6 +173,7 @@ pub const AssetReferenceSys = struct {
|
|||
.target = loader,
|
||||
.size = @sizeOf(@TypeOf(loader)),
|
||||
});
|
||||
core.engine_log("asset loader for asset type ({s}) registered", .{assetType.utf8()});
|
||||
}
|
||||
|
||||
pub fn loadRef(self: *@This(), asset: AssetRef, propertiesBag: ?AssetPropertiesBag) !void {
|
||||
|
|
@ -201,10 +197,13 @@ pub const AssetReferenceSys = struct {
|
|||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
var iter = self.loaders.valueIterator();
|
||||
while (iter.next()) |i| {
|
||||
i.destroy(self.allocator);
|
||||
}
|
||||
// the loader system shall no longer
|
||||
// control lifetimes for assset loaders
|
||||
// each asset loader will be responsible for its own lifetime
|
||||
// var iter = self.loaders.valueIterator();
|
||||
// while (iter.next()) |i| {
|
||||
// i.destroy(self.allocator);
|
||||
// }
|
||||
self.loaders.deinit(self.allocator);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub fn start_module(comptime programSpec: anytype, args: anytype, allocator: std
|
|||
}
|
||||
|
||||
gSoundEngine = core.gEngine.createObject(NeonSoundEngine, .{ .can_tick = true }) catch unreachable;
|
||||
|
||||
gSoundLoader = allocator.create(soundEngine.SoundLoader) catch unreachable;
|
||||
gSoundLoader.* = soundEngine.SoundLoader.init(gSoundEngine);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,10 +37,16 @@ var shutdownModuleNames: std.ArrayListUnmanaged([]const u8) = .{};
|
|||
pub fn start_modules(comptime programSpec: anytype, maybeArgs: ?NwArgs, allocator: std.mem.Allocator) !void {
|
||||
const Backlog = @This();
|
||||
|
||||
var z = core.tracy.ZoneN(@src(), "Starting all Modules");
|
||||
defer z.End();
|
||||
|
||||
inline for (modulelist) |feature| {
|
||||
if (@hasDecl(Backlog, feature)) {
|
||||
const Struct = @field(Backlog, feature);
|
||||
if (comptime core.isModuleEnabled(Struct.Module, programSpec)) {
|
||||
var z1 = core.tracy.ZoneN(@src(), @ptrCast("Initializing Module"));
|
||||
defer z1.End();
|
||||
core.tracy.Message(Struct.Module.name);
|
||||
if (maybeArgs) |args| {
|
||||
try Struct.start_module(programSpec, args, allocator);
|
||||
} else {
|
||||
|
|
@ -84,13 +90,7 @@ pub fn run_everything(comptime GameContext: type) !void {
|
|||
canTick = true;
|
||||
}
|
||||
|
||||
var gameContext = try core.createObject(GameContext, .{ .can_tick = canTick });
|
||||
|
||||
if (@hasDecl(GameContext, "prepare_game")) {
|
||||
gameContext.prepare_game() catch @panic("Unable to run base level prepare script");
|
||||
} else if (@hasDecl(GameContext, "prepare")) {
|
||||
gameContext.prepare() catch @panic("Unable to run base level prepare script");
|
||||
}
|
||||
_ = try core.createObject(GameContext, .{ .can_tick = canTick });
|
||||
|
||||
try core.gEngine.run();
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ pub const Engine = struct {
|
|||
destroyListSimple: ArrayListUnmanaged(EngineObjectRef) = .{},
|
||||
destroyListCore: ArrayListUnmanaged(EngineObjectRef) = .{},
|
||||
|
||||
prepares: ArrayListUnmanaged(EngineObjectRef) = .{},
|
||||
|
||||
lastEngineTime: f64,
|
||||
deltaTime: f64, // delta time for this frame from the previous frame
|
||||
frameNumber: u64,
|
||||
|
|
@ -127,6 +129,7 @@ pub const Engine = struct {
|
|||
self.eventors.deinit(self.allocator);
|
||||
|
||||
core.engine_logs("destroying tickables");
|
||||
self.prepares.deinit(self.allocator);
|
||||
self.tickables.deinit(self.allocator);
|
||||
self.preTickables.deinit(self.allocator);
|
||||
self.nfdRuntime.destroy();
|
||||
|
|
@ -169,6 +172,10 @@ pub const Engine = struct {
|
|||
try self.renderers.append(self.allocator, newObjectRef);
|
||||
}
|
||||
|
||||
if (@hasDecl(T, "prepare")) {
|
||||
try self.prepares.append(self.allocator, newObjectRef);
|
||||
}
|
||||
|
||||
if (@hasDecl(T, "preTick")) {
|
||||
try self.preTickables.append(self.allocator, newObjectRef);
|
||||
}
|
||||
|
|
@ -308,8 +315,15 @@ pub const Engine = struct {
|
|||
setupFunc(self.platformCtx) catch unreachable;
|
||||
}
|
||||
|
||||
if (self.rendererSetupFunc) |setupFunc| {
|
||||
setupFunc(self.rendererCtx) catch unreachable;
|
||||
{
|
||||
var z = core.tracy.Zone(@src());
|
||||
for (self.prepares.items) |prep| {
|
||||
var z1 = core.tracy.ZoneN(@src(), "Preparing game");
|
||||
core.tracy.Message(prep.vtable.typeName);
|
||||
prep.vtable.prepare_func.?(prep.ptr) catch unreachable;
|
||||
z1.End();
|
||||
}
|
||||
z.End();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
|
|
|||
|
|
@ -51,23 +51,59 @@ pub const EngineObjectVTable = struct {
|
|||
exitSignal_func: ?*const fn (*anyopaque) EngineDataEventError!void = null,
|
||||
readyToExit_func: ?*const fn (*anyopaque) bool = null,
|
||||
|
||||
prepare_func: ?*const fn (*anyopaque) EngineDataEventError!void = null,
|
||||
|
||||
pub fn from(comptime TargetType: type) EngineObjectVTable {
|
||||
const wrappedInit = struct {
|
||||
const funcFind: @TypeOf(@field(TargetType, "init")) = @field(TargetType, "init");
|
||||
|
||||
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
|
||||
const newObject = funcFind(allocator) catch return error.BadInit;
|
||||
return @as(*anyopaque, @ptrCast(newObject));
|
||||
}
|
||||
};
|
||||
|
||||
var self = EngineObjectVTable{
|
||||
.typeName = @typeName(TargetType),
|
||||
.typeSize = @sizeOf(TargetType),
|
||||
.typeAlign = @alignOf(TargetType),
|
||||
.init_func = wrappedInit.func,
|
||||
.init_func = undefined,
|
||||
};
|
||||
|
||||
if (@hasDecl(TargetType, "init")) {
|
||||
const wrappedInit = struct {
|
||||
const funcFind: @TypeOf(@field(TargetType, "init")) = @field(TargetType, "init");
|
||||
|
||||
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
|
||||
const newObject = funcFind(allocator) catch return error.BadInit;
|
||||
return @as(*anyopaque, @ptrCast(newObject));
|
||||
}
|
||||
};
|
||||
|
||||
self.init_func = wrappedInit.func;
|
||||
}
|
||||
|
||||
if (@hasDecl(TargetType, "create")) {
|
||||
const wrappedInit = struct {
|
||||
const funcFind: @TypeOf(@field(TargetType, "create")) = @field(TargetType, "create");
|
||||
|
||||
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
|
||||
const newObject = funcFind(allocator) catch return error.BadInit;
|
||||
return @as(*anyopaque, @ptrCast(newObject));
|
||||
}
|
||||
};
|
||||
|
||||
self.init_func = wrappedInit.func;
|
||||
}
|
||||
|
||||
// function used for objects created before engine loop starts;
|
||||
// this is a defferred startup
|
||||
//
|
||||
// will run normally during create if in systems thread
|
||||
//
|
||||
// or else will run
|
||||
if (@hasDecl(TargetType, "prepare")) {
|
||||
const wrap = struct {
|
||||
pub fn func(pointer: *anyopaque) EngineDataEventError!void {
|
||||
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
||||
ptr.prepare() catch return error.BadInit;
|
||||
}
|
||||
};
|
||||
|
||||
self.prepare_func = wrap.func;
|
||||
}
|
||||
|
||||
if (@hasDecl(TargetType, "postInit")) {
|
||||
const wrappedPostInit = struct {
|
||||
pub fn func(pointer: *anyopaque) EngineDataEventError!void {
|
||||
|
|
@ -155,6 +191,17 @@ pub const EngineObjectVTable = struct {
|
|||
self.deinit_func = wrappedDeinit.func;
|
||||
}
|
||||
|
||||
if (@hasDecl(TargetType, "destroy")) {
|
||||
const wrappedDeinit = struct {
|
||||
pub fn func(pointer: *anyopaque) void {
|
||||
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
||||
ptr.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
self.deinit_func = wrappedDeinit.func;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -216,7 +216,9 @@ pub const LoggerSys = struct {
|
|||
if (builtin.is_test) {
|
||||
std.debug.print("{s}", .{self.flushBuffer.items});
|
||||
} else {
|
||||
try self.consoleFile.writer().writeAll(self.flushBuffer.items);
|
||||
const writer = self.consoleFile.writer();
|
||||
try writer.writeAll(self.flushBuffer.items);
|
||||
try self.consoleFile.writeAll("");
|
||||
}
|
||||
self.flushBuffer.clearRetainingCapacity();
|
||||
self.lock.unlock();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const sdl3 = @import("sdl3");
|
|||
|
||||
const dependencyList = [_][]const u8{
|
||||
"core",
|
||||
"assets",
|
||||
"sdl3",
|
||||
"platform",
|
||||
"shaderTypes",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
mesh: ?meshes.IndexedMesh = null,
|
||||
textureId: ?u32 = null,
|
||||
visibility: bool = true,
|
||||
|
||||
textureName: core.Name = core.NameInvalid,
|
||||
meshName: core.Name = core.NameInvalid,
|
||||
|
||||
pub var BaseContainer: *MeshSet = undefined;
|
||||
pub const ComponentName = "Mesh";
|
||||
|
||||
pub const ScriptExports: []const []const u8 = &.{
|
||||
"setMesh",
|
||||
"setTextureByName",
|
||||
};
|
||||
|
||||
pub fn setMeshByName(self: *@This(), meshName: core.Name) void {
|
||||
self.meshName = meshName;
|
||||
}
|
||||
|
||||
// script function
|
||||
pub fn setMesh(self: *@This(), meshName: []const u8) void {
|
||||
const name = core.MakeName(meshName);
|
||||
self.mesh = rend.getMeshByName(core.MakeName(meshName));
|
||||
self.meshName = name;
|
||||
}
|
||||
|
||||
// animator: ?*Animator = null, todo.. implement animator
|
||||
pub const MeshSet = core.SparseSet(@This());
|
||||
|
||||
const meshes = @import("meshes.zig");
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
const rend = @import("../rend.zig");
|
||||
|
|
@ -42,7 +42,6 @@ pub const JointNameEntry = struct {
|
|||
index: u32 = 0,
|
||||
|
||||
pub fn deinit(self: @This(), allocator: std.mem.Allocator) void {
|
||||
// const allocator = graphics.getContext().allocator;
|
||||
allocator.free(self.name);
|
||||
}
|
||||
};
|
||||
|
|
@ -54,6 +53,45 @@ pub const IndexedMesh = struct {
|
|||
jointRemap: ?[]u8, // this is NOT a string, they're joint indices.. which happen to be u8s
|
||||
};
|
||||
|
||||
pub const MeshSourceType = enum { obj, gltf };
|
||||
|
||||
pub const LoadMeshSettings = struct {
|
||||
path: []const u8,
|
||||
sourceType: ?MeshSourceType = null,
|
||||
skeletonName: ?core.Name,
|
||||
};
|
||||
|
||||
pub fn loadIndexedMeshForPooling(meshName: core.Name, opt: LoadMeshSettings) !void {
|
||||
var sourceType = MeshSourceType.gltf;
|
||||
if (opt.sourceType) |st| {
|
||||
sourceType = st;
|
||||
}
|
||||
|
||||
// todo.. check if we have a cooked version of that file, if so just load that instead.
|
||||
|
||||
// otherwise, load the file
|
||||
var update: MeshUpdate = undefined;
|
||||
|
||||
switch (sourceType) {
|
||||
.obj => {
|
||||
update = try loadIndexedMeshForPoolingObj(rend.getAllocator(), meshName, opt.path);
|
||||
},
|
||||
.gltf => {
|
||||
update = try loadIndexedMeshForPoolingGltf(rend.getAllocator(), meshName, opt.skeletonName, opt.path);
|
||||
},
|
||||
}
|
||||
|
||||
try renderer.pushMeshUpdate(update);
|
||||
}
|
||||
|
||||
const loadIndexedMeshForPoolingGltf = gltfLoader.loadIndexedMeshForPoolingGltf;
|
||||
const loadIndexedMeshForPoolingObj = objLoader.loadIndexedMeshForPoolingObj;
|
||||
|
||||
const gltfLoader = @import("gltfLoader.zig");
|
||||
const objLoader = @import("objLoader.zig");
|
||||
|
||||
const core = @import("core");
|
||||
const rend = @import("../rend.zig");
|
||||
const renderer = rend.renderer;
|
||||
const std = @import("std");
|
||||
const zgltf = @import("zgltf");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
const MeshVertexTransmute = extern struct { data: [@sizeOf(MeshVertex)]u8 };
|
||||
|
||||
pub fn loadIndexedMeshForPoolingObj(allocator: std.mem.Allocator, meshName: core.Name, path: []const u8) !MeshUpdate {
|
||||
const file = try core.fs().loadFile(path);
|
||||
defer core.fs().unmap(file);
|
||||
|
||||
var Objs = try objLoader.loadObjBytes(file.bytes, allocator);
|
||||
defer Objs.deinit();
|
||||
|
||||
var vertexMap = std.AutoHashMap(MeshVertexTransmute, u32).init(allocator);
|
||||
defer vertexMap.deinit();
|
||||
var vertexList = std.ArrayList(MeshVertex).init(allocator);
|
||||
var indexList = std.ArrayList(u32).init(allocator);
|
||||
|
||||
const m: *objLoader.ObjMesh = &Objs.meshes.items[0];
|
||||
|
||||
// only thing i care about right now is normal and position
|
||||
for (m.v_faces.items) |f| {
|
||||
const face: objLoader.ObjFace = f;
|
||||
|
||||
if (face.count == 3) {
|
||||
for (0..face.count) |i| {
|
||||
const p = m.v_positions.items[face.vertex[i] - 1];
|
||||
const n = m.v_normals.items[face.normal[i] - 1];
|
||||
const u = m.v_uvs.items[face.texture[i] - 1];
|
||||
const meshVertex: MeshVertex = .{
|
||||
.position = .{ .x = p.x, .y = p.y, .z = p.z },
|
||||
.normal = .{ .x = n.x, .y = n.y, .z = n.z },
|
||||
.color = .{ .r = n.x, .g = n.y, .b = n.z, .a = 1.0 },
|
||||
.uv = .{ .x = u.x, .y = 1 - u.y },
|
||||
};
|
||||
|
||||
var index: u32 = @intCast(vertexList.items.len);
|
||||
|
||||
const transmute: MeshVertexTransmute = @bitCast(meshVertex);
|
||||
if (vertexMap.get(transmute)) |cachedIndex| {
|
||||
index = cachedIndex;
|
||||
} else {
|
||||
try vertexMap.put(transmute, index);
|
||||
try vertexList.append(meshVertex);
|
||||
}
|
||||
try indexList.append(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (face.count == 4) {
|
||||
const il: []const usize = &.{ 0, 1, 2, 2, 3, 0 };
|
||||
for (il) |i| {
|
||||
const p = m.v_positions.items[face.vertex[i] - 1];
|
||||
const n = m.v_normals.items[face.normal[i] - 1];
|
||||
const u = m.v_uvs.items[face.texture[i] - 1];
|
||||
const meshVertex: MeshVertex = .{
|
||||
.position = .{ .x = p.x, .y = p.y, .z = p.z },
|
||||
.normal = .{ .x = n.x, .y = n.y, .z = n.z },
|
||||
.color = .{ .r = n.x, .g = n.y, .b = n.z, .a = 1.0 },
|
||||
.uv = .{ .x = u.x, .y = 1 - u.y },
|
||||
};
|
||||
|
||||
var index: u32 = @intCast(vertexList.items.len);
|
||||
|
||||
const transmute: MeshVertexTransmute = @bitCast(meshVertex);
|
||||
if (vertexMap.get(transmute)) |cachedIndex| {
|
||||
index = cachedIndex;
|
||||
} else {
|
||||
try vertexMap.put(transmute, index);
|
||||
try vertexList.append(meshVertex);
|
||||
}
|
||||
try indexList.append(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var jointNames = std.ArrayList(JointNameEntry).init(allocator);
|
||||
|
||||
const rv: MeshUpdate = .{
|
||||
.new = .{
|
||||
.vertices = try vertexList.toOwnedSlice(),
|
||||
.indices = try indexList.toOwnedSlice(),
|
||||
.jointNames = try jointNames.toOwnedSlice(),
|
||||
.skeletonName = null,
|
||||
.name = meshName,
|
||||
},
|
||||
};
|
||||
|
||||
core.graphics_log("[{s}] vertex count vertices={d} indices={d}", .{ path, rv.new.vertices.len, rv.new.indices.len });
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
const meshes = @import("meshes.zig");
|
||||
const MeshUpdate = meshes.MeshUpdate;
|
||||
const JointNameEntry = meshes.JointNameEntry;
|
||||
const MeshVertex = meshes.MeshVertex;
|
||||
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
const objLoader = @import("objLoader");
|
||||
|
|
@ -17,6 +17,14 @@ pub const context = renderer.context; // context getter func
|
|||
|
||||
pub const MeshPool = renderer.MeshPool;
|
||||
|
||||
pub const getMesh = renderer.getMesh;
|
||||
pub const getMeshByName = renderer.getMeshByName;
|
||||
|
||||
var rendAllocator: std.mem.Allocator = undefined;
|
||||
pub fn getAllocator() std.mem.Allocator {
|
||||
return rendAllocator;
|
||||
}
|
||||
|
||||
// controls glfw and general windowing
|
||||
// graphics depends on this one
|
||||
pub const Module: core.ModuleDescription = .{
|
||||
|
|
@ -27,7 +35,8 @@ pub const Module: core.ModuleDescription = .{
|
|||
pub fn start_module(comptime programSpec: anytype, args: anytype, allocator: std.mem.Allocator) !void {
|
||||
_ = args;
|
||||
_ = programSpec;
|
||||
_ = allocator;
|
||||
core.engine_log("starting up renderer interface", .{});
|
||||
rendAllocator = allocator;
|
||||
try renderer.start();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// @peterino2
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
pub var LoaderInterfaceVTable = assets.AssetLoaderInterface.from("Mesh", @This());
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn discardAll(self: *@This()) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
// unfortunately this one is blocking
|
||||
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
|
||||
_ = self;
|
||||
|
||||
const sourceType = getSourceType(propertiesBag);
|
||||
|
||||
core.engine_log("loading mesh asset {s} [{s}]", .{ propertiesBag.?.path, if (sourceType) |s| @tagName(s) else "default" });
|
||||
|
||||
meshes.loadIndexedMeshForPooling(assetRef.name, .{
|
||||
.path = propertiesBag.?.path,
|
||||
.sourceType = getSourceType(propertiesBag),
|
||||
.skeletonName = if (propertiesBag.?.skeletonName) |skName| core.MakeName(skName) else null,
|
||||
}) catch return error.UnableToLoad;
|
||||
}
|
||||
|
||||
fn getSourceType(propertiesBag: ?assets.AssetPropertiesBag) ?meshes.MeshSourceType {
|
||||
if (propertiesBag) |bag| {
|
||||
if (bag.meshType) |meshType| {
|
||||
if (std.mem.eql(u8, meshType, "obj")) {
|
||||
return meshes.MeshSourceType.obj;
|
||||
}
|
||||
if (std.mem.eql(u8, meshType, "gltf")) {
|
||||
return meshes.MeshSourceType.gltf;
|
||||
}
|
||||
}
|
||||
|
||||
// try to deduce it by file name, if nothing is set.
|
||||
const ext = core.getFileExtension(bag.path);
|
||||
if (std.mem.eql(u8, ext, ".obj")) {
|
||||
return meshes.MeshSourceType.obj;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, ext, ".gltf")) {
|
||||
return meshes.MeshSourceType.gltf;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, ext, ".glb")) {
|
||||
return meshes.MeshSourceType.gltf;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const assets = @import("assets");
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
|
||||
const rend = @import("../rend.zig");
|
||||
const renderer = rend.renderer;
|
||||
const meshes = rend.meshes;
|
||||
|
|
@ -12,14 +12,21 @@ pub const MeshPool = struct {
|
|||
|
||||
device: *gpu.GPUDevice,
|
||||
|
||||
destroyList: std.ArrayList(*gpu.GPUTransferBuffer),
|
||||
|
||||
installedMeshes: std.AutoHashMapUnmanaged(u32, meshes.IndexedMesh) = .{},
|
||||
|
||||
pub fn create(device: *gpu.GPUDevice, allocator: std.mem.Allocator, settings: MeshPoolCreationSettings) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
gMeshPool = self;
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.indexSpans = try core.MergedSpans.init(allocator, settings.indexCount),
|
||||
.vertexSpans = try core.MergedSpans.init(allocator, settings.vertexCount),
|
||||
.meshUpdates = try core.RingQueue(rend.MeshUpdate).init(allocator, 128),
|
||||
.destroyList = std.ArrayList(*gpu.GPUTransferBuffer).init(allocator),
|
||||
.device = device,
|
||||
};
|
||||
|
||||
|
|
@ -35,24 +42,106 @@ pub const MeshPool = struct {
|
|||
.props = 0,
|
||||
});
|
||||
|
||||
core.graphics_log("mesh pool created {d}k vertices, {d}k indices", .{ settings.vertexCount / 1000, settings.indexCount / 1000 });
|
||||
|
||||
try assets.gAssetSys.registerLoader(try core.createObject(MeshAssetLoader, .{}));
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn onUploadCleanup(p: *anyopaque) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(p));
|
||||
|
||||
for (self.destroyList.items) |transferBuffer| {
|
||||
self.device.releaseGPUTransferBuffer(transferBuffer);
|
||||
}
|
||||
|
||||
self.destroyList.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn onUploadInner(self: *@This(), copyPass: *gpu.GPUCopyPass) !void {
|
||||
self.meshUpdates.lock();
|
||||
defer self.meshUpdates.unlock();
|
||||
|
||||
self.destroyList = std.ArrayList(*gpu.GPUTransferBuffer).init(self.allocator);
|
||||
|
||||
while (self.meshUpdates.popFromUnlocked()) |u| {
|
||||
switch (u) {
|
||||
.new => |new| {
|
||||
const indexSpan = try self.addTransfer(copyPass, self.indexBuffer, u32, &self.indexSpans, new.indices);
|
||||
const vertexSpan = try self.addTransfer(copyPass, self.vertexBuffer, meshes.MeshVertex, &self.vertexSpans, new.vertices);
|
||||
|
||||
var name = new.name;
|
||||
try self.installedMeshes.put(self.allocator, name.handle(), .{
|
||||
.vertex = vertexSpan,
|
||||
.index = indexSpan,
|
||||
.name = new.name,
|
||||
.jointRemap = null, // joint remaps... parse the installed skeletal meshes to generate this remap
|
||||
});
|
||||
},
|
||||
.free => |free| {
|
||||
var name = free.name;
|
||||
if (!self.installedMeshes.remove(name.handle())) {
|
||||
core.engine_log("unable to find handle from mesh pool.. {s}", .{name.utf8()});
|
||||
}
|
||||
self.vertexSpans.removeSpan(free.vertices);
|
||||
self.indexSpans.removeSpan(free.indices);
|
||||
},
|
||||
}
|
||||
|
||||
u.deinit(self.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onUpload(p: *anyopaque, copyPass: *gpu.GPUCopyPass) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(p));
|
||||
if (self.meshUpdates.count() <= 0)
|
||||
return;
|
||||
_ = copyPass;
|
||||
|
||||
self.onUploadInner(copyPass) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn addTransfer(
|
||||
self: *@This(),
|
||||
copyPass: *gpu.GPUCopyPass,
|
||||
destBuffer: *gpu.GPUBuffer,
|
||||
comptime T: type,
|
||||
mergedSpans: *core.MergedSpans,
|
||||
uploadSlice: []const T,
|
||||
) !core.Span {
|
||||
const newSpan = try mergedSpans.allocate(@intCast(uploadSlice.len));
|
||||
const upload = self.device.createGPUTransferBuffer(&.{ .usage = .transferbufferusageUpload, .size = @sizeOf(T) * newSpan.size, .props = 0 });
|
||||
try self.destroyList.append(upload);
|
||||
|
||||
var mappedSlice: []T = undefined;
|
||||
mappedSlice.ptr = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(upload, false)));
|
||||
defer self.device.unmapGPUTransferBuffer(upload);
|
||||
mappedSlice.len = uploadSlice.len;
|
||||
std.mem.copyForwards(T, mappedSlice, uploadSlice);
|
||||
|
||||
//copyPass.uploadToGPUBuffer(source: [*c]const GPUTransferBufferLocation, destination: [*c]const GPUBufferRegion, cycle: bool)
|
||||
copyPass.uploadToGPUBuffer(
|
||||
&.{ .transfer_buffer = upload, .offset = 0 },
|
||||
&.{ .buffer = destBuffer, .offset = newSpan.start, .size = newSpan.size },
|
||||
false,
|
||||
);
|
||||
|
||||
return newSpan;
|
||||
}
|
||||
|
||||
// pushes the meshUpdate into the pool, either an insert or a free.
|
||||
pub fn pushMeshUpdate(self: *@This(), meshUpdate: rend.MeshUpdate) !void {
|
||||
self.meshUpdates.pushLocked(meshUpdate);
|
||||
try self.meshUpdates.pushLocked(meshUpdate);
|
||||
}
|
||||
|
||||
pub fn destroy(p: *anyopaque) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(p));
|
||||
|
||||
self.device.releaseGPUBuffer(self.indexBuffer);
|
||||
self.device.releaseGPUBuffer(self.vertexBuffer);
|
||||
|
||||
self.installedMeshes.deinit(self.allocator);
|
||||
self.destroyList.deinit();
|
||||
self.indexSpans.deinit();
|
||||
self.meshUpdates.deinit();
|
||||
self.vertexSpans.deinit();
|
||||
|
|
@ -65,25 +154,27 @@ pub const MeshPool = struct {
|
|||
};
|
||||
};
|
||||
|
||||
/// === renderer interface implementation ===
|
||||
var gMeshPool: *MeshPool = undefined;
|
||||
|
||||
pub fn getIndexedMesh(name: []const u8) ?rend.IndexedMesh {
|
||||
pub fn getMesh(name: []const u8) ?rend.IndexedMesh {
|
||||
var n = core.MakeName(name);
|
||||
return getIndexedMeshByName(&n);
|
||||
return getMeshByName(&n);
|
||||
}
|
||||
|
||||
pub fn getIndexedMeshByName(name: *core.Name) ?rend.IndexedMesh {
|
||||
// var name = _name;
|
||||
// gMeshPoolBuffer.vertexMapLock.lock();
|
||||
// defer gMeshPoolBuffer.vertexMapLock.unlock();
|
||||
// return gMeshPoolBuffer.vertexMap.get(name.handle());
|
||||
_ = name;
|
||||
return null;
|
||||
pub fn getMeshByName(name: *core.Name) ?rend.IndexedMesh {
|
||||
return gMeshPool.installedMeshes.get(name.handle());
|
||||
}
|
||||
|
||||
pub fn pushMeshUpdate(meshUpdate: rend.MeshUpdate) !void {
|
||||
try gMeshPool.pushMeshUpdate(meshUpdate);
|
||||
}
|
||||
|
||||
const MeshAssetLoader = @import("MeshAssetLoader.zig");
|
||||
const sdl3 = @import("sdl3");
|
||||
const gpu = sdl3.gpu;
|
||||
const rend = @import("../rend.zig");
|
||||
const std = @import("std");
|
||||
const core = @import("core");
|
||||
const assets = @import("assets");
|
||||
const meshes = rend.meshes;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ pub const Renderer = struct {
|
|||
meshPool: *MeshPool = undefined,
|
||||
|
||||
uploads: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCopyPass) void }) = .{},
|
||||
uploadCleanup: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
|
||||
destroys: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
|
||||
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
|
||||
|
|
@ -35,18 +36,19 @@ pub const Renderer = struct {
|
|||
.allocator = allocator,
|
||||
};
|
||||
|
||||
core.registerRendererSetup(self, engineSetup);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn engineSetup(p: *anyopaque) core.EngineDataEventError!void {
|
||||
@as(*@This(), @ptrCast(@alignCast(p))).startRenderer() catch return error.BadInit;
|
||||
pub fn prepare(self: *@This()) core.EngineDataEventError!void {
|
||||
core.engine_log("starting up renderer interface", .{});
|
||||
self.startRenderer() catch return error.BadInit;
|
||||
}
|
||||
|
||||
pub fn registerRendererObject(self: *@This(), T: type) !void {
|
||||
const object = try T.create(self.device, self.allocator, .{});
|
||||
|
||||
try self.uploads.append(self.allocator, .{ .ptr = object, .func = T.onUpload });
|
||||
try self.uploadCleanup.append(self.allocator, .{ .ptr = object, .func = T.onUploadCleanup });
|
||||
try self.destroys.append(self.allocator, .{ .ptr = object, .func = T.destroy });
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +167,8 @@ pub const Renderer = struct {
|
|||
return rv;
|
||||
}
|
||||
|
||||
pub fn frameUploads(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
|
||||
pub fn frameUploads(self: *@This()) void {
|
||||
const cmd = self.device.acquireGPUCommandBuffer();
|
||||
const buffer = self.device.mapGPUTransferBuffer(self.colorBufferTransfer, true);
|
||||
|
||||
var b: [*][4]f32 = @ptrCast(@alignCast(buffer));
|
||||
|
|
@ -187,18 +190,25 @@ pub const Renderer = struct {
|
|||
for (self.uploads.items) |upload| {
|
||||
upload.func(upload.ptr, copyPass);
|
||||
}
|
||||
if (!cmd.submitGPUCommandBuffer()) {
|
||||
core.graphics_log("submit gpu command buffer SDL ERROR: {s}", .{sdl3.getError()});
|
||||
}
|
||||
|
||||
for (self.uploadCleanup.items) |uploadCleanup| {
|
||||
uploadCleanup.func(uploadCleanup.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
self.totalTime += dt;
|
||||
|
||||
const cmd = self.device.acquireGPUCommandBuffer();
|
||||
self.frameUploads(cmd);
|
||||
self.frameUploads();
|
||||
|
||||
var swapchain_texture: *gpu.GPUTexture = undefined;
|
||||
if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, &swapchain_texture, null, null)) {
|
||||
var swapchainTexture: *gpu.GPUTexture = undefined;
|
||||
if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, &swapchainTexture, null, null)) {
|
||||
var targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroes(gpu.GPUColorTargetInfo);
|
||||
targetInfo.texture = swapchain_texture;
|
||||
targetInfo.texture = swapchainTexture;
|
||||
targetInfo.clear_color = .{ .r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0 };
|
||||
targetInfo.load_op = .loadopClear;
|
||||
targetInfo.store_op = .storeopStore;
|
||||
|
|
@ -218,6 +228,7 @@ pub const Renderer = struct {
|
|||
for (self.destroys.items) |d| {
|
||||
d.func(d.ptr);
|
||||
}
|
||||
self.uploadCleanup.deinit(self.allocator);
|
||||
self.uploads.deinit(self.allocator);
|
||||
self.destroys.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
|
|
@ -227,6 +238,22 @@ pub const Renderer = struct {
|
|||
pub var gRenderer: *Renderer = undefined;
|
||||
pub var gAllocator: std.mem.Allocator = undefined;
|
||||
|
||||
const rend = @import("../rend.zig");
|
||||
|
||||
const std = @import("std");
|
||||
const core = @import("core");
|
||||
const platform = @import("platform");
|
||||
const sdl3 = @import("sdl3");
|
||||
const gpu = sdl3.gpu;
|
||||
const SSBO_Scene = @import("sample.vert").Scene;
|
||||
const meshes_vert = @import("meshes.vert");
|
||||
const MeshVertices = meshes_vert.Scene;
|
||||
const MeshUniforms = meshes_vert.Uniforms;
|
||||
|
||||
pub const mesh_pool = @import("mesh-pool.zig");
|
||||
pub const MeshPool = mesh_pool.MeshPool;
|
||||
|
||||
// ====== renderer API =======
|
||||
pub fn start() !void {
|
||||
gRenderer = try core.createObject(Renderer, .{ .can_tick = true, .isCore = true });
|
||||
gAllocator = gRenderer.allocator;
|
||||
|
|
@ -237,15 +264,7 @@ pub fn shutdown() void {}
|
|||
pub fn context() *Renderer {
|
||||
return gRenderer;
|
||||
}
|
||||
pub const getMesh = mesh_pool.getMesh;
|
||||
pub const getMeshByName = mesh_pool.getMeshByName;
|
||||
|
||||
const rend = @import("../rend.zig");
|
||||
|
||||
pub const mesh_pool = @import("mesh-pool.zig");
|
||||
pub const MeshPool = mesh_pool.MeshPool;
|
||||
|
||||
const std = @import("std");
|
||||
const core = @import("core");
|
||||
const platform = @import("platform");
|
||||
const sdl3 = @import("sdl3");
|
||||
const gpu = sdl3.gpu;
|
||||
const SSBO_Scene = @import("sample.vert").Scene;
|
||||
pub const pushMeshUpdate = mesh_pool.pushMeshUpdate;
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ pub fn toksIntoFace(toks: anytype) !ObjFace {
|
|||
while (toks.next()) |tok| {
|
||||
if (tok.len == 0)
|
||||
continue;
|
||||
var face_desc = std.mem.tokenize(u8, tok, "/");
|
||||
var face_desc = std.mem.tokenizeAny(u8, tok, "/");
|
||||
var ic: u32 = 0; // ic= inner_count
|
||||
if (count >= 4) {
|
||||
continue;
|
||||
|
|
@ -232,7 +232,7 @@ fn parse_line(lineIn: []const u8, allocator: std.mem.Allocator) !LineParseResult
|
|||
line = line[1..line.len];
|
||||
}
|
||||
|
||||
var tokens = std.mem.tokenize(u8, line, " ");
|
||||
var tokens = std.mem.tokenizeAny(u8, line, " ");
|
||||
const first = tokens.next().?;
|
||||
|
||||
if (line[0] == '#') {
|
||||
|
|
@ -354,16 +354,16 @@ pub fn fileIntoLines(file_contents: []const u8) std.mem.SplitIterator(u8, .seque
|
|||
if (file_contents[index] == '\n') {
|
||||
if (index > 0) {
|
||||
if (file_contents[index - 1] == '\r') {
|
||||
return std.mem.split(u8, file_contents, "\r\n");
|
||||
return std.mem.splitSequence(u8, file_contents, "\r\n");
|
||||
} else {
|
||||
return std.mem.split(u8, file_contents, "\n");
|
||||
return std.mem.splitSequence(u8, file_contents, "\n");
|
||||
}
|
||||
} else {
|
||||
return std.mem.split(u8, file_contents, "\n");
|
||||
return std.mem.splitSequence(u8, file_contents, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return std.mem.split(u8, file_contents, "\n");
|
||||
return std.mem.splitSequence(u8, file_contents, "\n");
|
||||
}
|
||||
|
||||
const ObjContents = struct {
|
||||
|
|
|
|||
|
|
@ -44,5 +44,9 @@ pub fn pollEvent() ?*Event {
|
|||
return null;
|
||||
}
|
||||
|
||||
pub fn getError() [*c]const u8 {
|
||||
return c.SDL_GetError();
|
||||
}
|
||||
|
||||
pub const gpu = @import("gpu.zig");
|
||||
pub const Scancode = @import("scancode.zig").Scancode;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void {
|
|||
|
||||
//const tracy_enabled = b.option(bool, "tracy", "Enables tracy integration") orelse false;
|
||||
|
||||
const tracy_enabled: bool = false; //if (b.graph.env_map.hash_map.get("WITH_TRACY") != null) true else false;
|
||||
const tracy_enabled: bool = true; //if (b.graph.env_map.hash_map.get("WITH_TRACY") != null) true else false;
|
||||
// if (b.graph.env_map.hash_map.get("WITH_TRACY")) |with_tracy| {
|
||||
// tracy_enabled = with_tracy;
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -10,21 +10,34 @@ pub fn init(allocator: std.mem.Allocator) !*@This() {
|
|||
return self;
|
||||
}
|
||||
|
||||
pub fn prepare_game(self: *@This()) !void {
|
||||
const assetReferences = [_]assets.AssetImportReference{
|
||||
assets.MakeImportRefOptions(
|
||||
"Mesh",
|
||||
"m_empire",
|
||||
.{ .path = "meshes/lost_empire.obj" },
|
||||
),
|
||||
assets.MakeImportRefOptions(
|
||||
"Mesh",
|
||||
"m_fox",
|
||||
.{ .path = "gltf-samples/Fox/glTF/Fox.gltf" },
|
||||
),
|
||||
};
|
||||
|
||||
pub fn prepare(self: *@This()) !void {
|
||||
_ = self;
|
||||
core.engine_log(">>>>>>> game prepare", .{});
|
||||
var z = core.tracy.ZoneN(@src(), "PREPARING GAME");
|
||||
defer z.End();
|
||||
try core.fs().addContentPath("sampleGame");
|
||||
try script.loadTypes("scripts");
|
||||
try script.runScriptFile("scripts/prepare.lua");
|
||||
|
||||
try assets.loadList(assetReferences);
|
||||
|
||||
const exitInput = try core.ActionBinding.create(core.MakeName("exit"));
|
||||
exitInput.addKey(.escape, .keyDown);
|
||||
_ = exitInput.data.addListener(null, onExit);
|
||||
exitInput.activate();
|
||||
|
||||
const results = try rend.gltfLoader.loadIndexedMeshForPoolingGltf(self.allocator, core.MakeName("test"), null, "gltf-samples/Fox/glTF/Fox.gltf");
|
||||
defer results.deinit(self.allocator);
|
||||
|
||||
core.engine_log("loaded fox, vertices: {d}", .{results.new.vertices.len});
|
||||
core.engine_log("loaded fox, indices: {d}", .{results.new.indices.len});
|
||||
}
|
||||
|
||||
pub fn onExit(ctx: ?*anyopaque, action: core.ActionEvent) void {
|
||||
|
|
@ -54,6 +67,7 @@ pub fn main() anyerror!void {
|
|||
|
||||
const std = @import("std");
|
||||
const backlog = @import("Backlog");
|
||||
const assets = backlog.assets;
|
||||
const core = backlog.core;
|
||||
const rend = backlog.rend;
|
||||
const script = core.script;
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
print("hello world!")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue