First scene loaded, discovered an issue with ubo's, weird stuff in fullscreen

This commit is contained in:
sergeypdev 2024-02-24 23:57:31 +04:00
parent 96449b65d7
commit 5f426c61de
11 changed files with 803 additions and 130 deletions

2
assets/test_scene.mtl Normal file
View File

@ -0,0 +1,2 @@
# Blender 4.0.2 MTL File: 'None'
# www.blender.org

303
assets/test_scene.obj Normal file
View File

@ -0,0 +1,303 @@
# Blender 4.0.2
# www.blender.org
mtllib test_scene.mtl
o Cube
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
f 1/1/1 2/2/1 4/3/1 3/4/1
f 3/4/2 4/3/2 8/5/2 7/6/2
f 7/6/3 8/5/3 6/7/3 5/8/3
f 5/8/4 6/7/4 2/9/4 1/10/4
f 3/11/5 7/6/5 5/8/5 1/12/5
f 8/5/6 4/13/6 2/14/6 6/7/6
o Cylinder
v -4.365820 -1.000000 -1.000000
v -4.365820 1.000000 -1.000000
v -4.170730 -1.000000 -0.980785
v -4.170730 1.000000 -0.980785
v -3.983137 -1.000000 -0.923880
v -3.983137 1.000000 -0.923880
v -3.810250 -1.000000 -0.831470
v -3.810250 1.000000 -0.831470
v -3.658714 -1.000000 -0.707107
v -3.658714 1.000000 -0.707107
v -3.534351 -1.000000 -0.555570
v -3.534351 1.000000 -0.555570
v -3.441941 -1.000000 -0.382683
v -3.441941 1.000000 -0.382683
v -3.385035 -1.000000 -0.195090
v -3.385035 1.000000 -0.195090
v -3.365820 -1.000000 0.000000
v -3.365820 1.000000 0.000000
v -3.385035 -1.000000 0.195090
v -3.385035 1.000000 0.195090
v -3.441941 -1.000000 0.382683
v -3.441941 1.000000 0.382683
v -3.534351 -1.000000 0.555570
v -3.534351 1.000000 0.555570
v -3.658714 -1.000000 0.707107
v -3.658714 1.000000 0.707107
v -3.810250 -1.000000 0.831470
v -3.810250 1.000000 0.831470
v -3.983137 -1.000000 0.923880
v -3.983137 1.000000 0.923880
v -4.170730 -1.000000 0.980785
v -4.170730 1.000000 0.980785
v -4.365820 -1.000000 1.000000
v -4.365820 1.000000 1.000000
v -4.560911 -1.000000 0.980785
v -4.560911 1.000000 0.980785
v -4.748504 -1.000000 0.923880
v -4.748504 1.000000 0.923880
v -4.921391 -1.000000 0.831470
v -4.921391 1.000000 0.831470
v -5.072927 -1.000000 0.707107
v -5.072927 1.000000 0.707107
v -5.197290 -1.000000 0.555570
v -5.197290 1.000000 0.555570
v -5.289700 -1.000000 0.382683
v -5.289700 1.000000 0.382683
v -5.346606 -1.000000 0.195090
v -5.346606 1.000000 0.195090
v -5.365820 -1.000000 0.000000
v -5.365820 1.000000 0.000000
v -5.346606 -1.000000 -0.195090
v -5.346606 1.000000 -0.195090
v -5.289700 -1.000000 -0.382683
v -5.289700 1.000000 -0.382683
v -5.197290 -1.000000 -0.555570
v -5.197290 1.000000 -0.555570
v -5.072927 -1.000000 -0.707107
v -5.072927 1.000000 -0.707107
v -4.921391 -1.000000 -0.831470
v -4.921391 1.000000 -0.831470
v -4.748504 -1.000000 -0.923880
v -4.748504 1.000000 -0.923880
v -4.560911 -1.000000 -0.980785
v -4.560911 1.000000 -0.980785
vn 0.0980 -0.0000 -0.9952
vn 0.2903 -0.0000 -0.9569
vn 0.4714 -0.0000 -0.8819
vn 0.6344 -0.0000 -0.7730
vn 0.7730 -0.0000 -0.6344
vn 0.8819 -0.0000 -0.4714
vn 0.9569 -0.0000 -0.2903
vn 0.9952 -0.0000 -0.0980
vn 0.9952 -0.0000 0.0980
vn 0.9569 -0.0000 0.2903
vn 0.8819 -0.0000 0.4714
vn 0.7730 -0.0000 0.6344
vn 0.6344 -0.0000 0.7730
vn 0.4714 -0.0000 0.8819
vn 0.2903 -0.0000 0.9569
vn 0.0980 -0.0000 0.9952
vn -0.0980 -0.0000 0.9952
vn -0.2903 -0.0000 0.9569
vn -0.4714 -0.0000 0.8819
vn -0.6344 -0.0000 0.7730
vn -0.7730 -0.0000 0.6344
vn -0.8819 -0.0000 0.4714
vn -0.9569 -0.0000 0.2903
vn -0.9952 -0.0000 0.0980
vn -0.9952 -0.0000 -0.0980
vn -0.9569 -0.0000 -0.2903
vn -0.8819 -0.0000 -0.4714
vn -0.7730 -0.0000 -0.6344
vn -0.6344 -0.0000 -0.7730
vn -0.4714 -0.0000 -0.8819
vn -0.0000 1.0000 -0.0000
vn -0.2903 -0.0000 -0.9569
vn -0.0980 -0.0000 -0.9952
vn -0.0000 -1.0000 -0.0000
vt 1.000000 0.500000
vt 1.000000 1.000000
vt 0.968750 1.000000
vt 0.968750 0.500000
vt 0.937500 1.000000
vt 0.937500 0.500000
vt 0.906250 1.000000
vt 0.906250 0.500000
vt 0.875000 1.000000
vt 0.875000 0.500000
vt 0.843750 1.000000
vt 0.843750 0.500000
vt 0.812500 1.000000
vt 0.812500 0.500000
vt 0.781250 1.000000
vt 0.781250 0.500000
vt 0.750000 1.000000
vt 0.750000 0.500000
vt 0.718750 1.000000
vt 0.718750 0.500000
vt 0.687500 1.000000
vt 0.687500 0.500000
vt 0.656250 1.000000
vt 0.656250 0.500000
vt 0.625000 1.000000
vt 0.625000 0.500000
vt 0.593750 1.000000
vt 0.593750 0.500000
vt 0.562500 1.000000
vt 0.562500 0.500000
vt 0.531250 1.000000
vt 0.531250 0.500000
vt 0.500000 1.000000
vt 0.500000 0.500000
vt 0.468750 1.000000
vt 0.468750 0.500000
vt 0.437500 1.000000
vt 0.437500 0.500000
vt 0.406250 1.000000
vt 0.406250 0.500000
vt 0.375000 1.000000
vt 0.375000 0.500000
vt 0.343750 1.000000
vt 0.343750 0.500000
vt 0.312500 1.000000
vt 0.312500 0.500000
vt 0.281250 1.000000
vt 0.281250 0.500000
vt 0.250000 1.000000
vt 0.250000 0.500000
vt 0.218750 1.000000
vt 0.218750 0.500000
vt 0.187500 1.000000
vt 0.187500 0.500000
vt 0.156250 1.000000
vt 0.156250 0.500000
vt 0.125000 1.000000
vt 0.125000 0.500000
vt 0.093750 1.000000
vt 0.093750 0.500000
vt 0.062500 1.000000
vt 0.062500 0.500000
vt 0.296822 0.485388
vt 0.250000 0.490000
vt 0.203178 0.485388
vt 0.158156 0.471731
vt 0.116663 0.449553
vt 0.080294 0.419706
vt 0.050447 0.383337
vt 0.028269 0.341844
vt 0.014612 0.296822
vt 0.010000 0.250000
vt 0.014612 0.203178
vt 0.028269 0.158156
vt 0.050447 0.116663
vt 0.080294 0.080294
vt 0.116663 0.050447
vt 0.158156 0.028269
vt 0.203178 0.014612
vt 0.250000 0.010000
vt 0.296822 0.014612
vt 0.341844 0.028269
vt 0.383337 0.050447
vt 0.419706 0.080294
vt 0.449553 0.116663
vt 0.471731 0.158156
vt 0.485388 0.203178
vt 0.490000 0.250000
vt 0.485388 0.296822
vt 0.471731 0.341844
vt 0.449553 0.383337
vt 0.419706 0.419706
vt 0.383337 0.449553
vt 0.341844 0.471731
vt 0.031250 1.000000
vt 0.031250 0.500000
vt 0.000000 1.000000
vt 0.000000 0.500000
vt 0.750000 0.490000
vt 0.796822 0.485388
vt 0.841844 0.471731
vt 0.883337 0.449553
vt 0.919706 0.419706
vt 0.949553 0.383337
vt 0.971731 0.341844
vt 0.985388 0.296822
vt 0.990000 0.250000
vt 0.985388 0.203178
vt 0.971731 0.158156
vt 0.949553 0.116663
vt 0.919706 0.080294
vt 0.883337 0.050447
vt 0.841844 0.028269
vt 0.796822 0.014612
vt 0.750000 0.010000
vt 0.703178 0.014612
vt 0.658156 0.028269
vt 0.616663 0.050447
vt 0.580294 0.080294
vt 0.550447 0.116663
vt 0.528269 0.158156
vt 0.514612 0.203178
vt 0.510000 0.250000
vt 0.514612 0.296822
vt 0.528269 0.341844
vt 0.550447 0.383337
vt 0.580294 0.419706
vt 0.616663 0.449553
vt 0.658156 0.471731
vt 0.703178 0.485388
s 0
f 9/15/7 10/16/7 12/17/7 11/18/7
f 11/18/8 12/17/8 14/19/8 13/20/8
f 13/20/9 14/19/9 16/21/9 15/22/9
f 15/22/10 16/21/10 18/23/10 17/24/10
f 17/24/11 18/23/11 20/25/11 19/26/11
f 19/26/12 20/25/12 22/27/12 21/28/12
f 21/28/13 22/27/13 24/29/13 23/30/13
f 23/30/14 24/29/14 26/31/14 25/32/14
f 25/32/15 26/31/15 28/33/15 27/34/15
f 27/34/16 28/33/16 30/35/16 29/36/16
f 29/36/17 30/35/17 32/37/17 31/38/17
f 31/38/18 32/37/18 34/39/18 33/40/18
f 33/40/19 34/39/19 36/41/19 35/42/19
f 35/42/20 36/41/20 38/43/20 37/44/20
f 37/44/21 38/43/21 40/45/21 39/46/21
f 39/46/22 40/45/22 42/47/22 41/48/22
f 41/48/23 42/47/23 44/49/23 43/50/23
f 43/50/24 44/49/24 46/51/24 45/52/24
f 45/52/25 46/51/25 48/53/25 47/54/25
f 47/54/26 48/53/26 50/55/26 49/56/26
f 49/56/27 50/55/27 52/57/27 51/58/27
f 51/58/28 52/57/28 54/59/28 53/60/28
f 53/60/29 54/59/29 56/61/29 55/62/29
f 55/62/30 56/61/30 58/63/30 57/64/30
f 57/64/31 58/63/31 60/65/31 59/66/31
f 59/66/32 60/65/32 62/67/32 61/68/32
f 61/68/33 62/67/33 64/69/33 63/70/33
f 63/70/34 64/69/34 66/71/34 65/72/34
f 65/72/35 66/71/35 68/73/35 67/74/35
f 67/74/36 68/73/36 70/75/36 69/76/36
f 12/77/37 10/78/37 72/79/37 70/80/37 68/81/37 66/82/37 64/83/37 62/84/37 60/85/37 58/86/37 56/87/37 54/88/37 52/89/37 50/90/37 48/91/37 46/92/37 44/93/37 42/94/37 40/95/37 38/96/37 36/97/37 34/98/37 32/99/37 30/100/37 28/101/37 26/102/37 24/103/37 22/104/37 20/105/37 18/106/37 16/107/37 14/108/37
f 69/76/38 70/75/38 72/109/38 71/110/38
f 71/110/39 72/109/39 10/111/39 9/112/39
f 9/113/40 11/114/40 13/115/40 15/116/40 17/117/40 19/118/40 21/119/40 23/120/40 25/121/40 27/122/40 29/123/40 31/124/40 33/125/40 35/126/40 37/127/40 39/128/40 41/129/40 43/130/40 45/131/40 47/132/40 49/133/40 51/134/40 53/135/40 55/136/40 57/137/40 59/138/40 61/139/40 63/140/40 65/141/40 67/142/40 69/143/40 71/144/40

View File

@ -32,13 +32,11 @@ pub fn build(b: *Build) void {
const assets_step = b.step("assets", "Build and install assets");
b.getInstallStep().dependOn(assets_step);
const assetc = buildAssetCompiler(b, buildOptimize);
const assetc = buildAssetCompiler(b, buildOptimize, assets_mod);
const install_assetc_step = b.addInstallArtifact(assetc, .{ .dest_dir = .{ .override = .prefix } });
assets_step.dependOn(&install_assetc_step.step);
assetc.root_module.addImport("assets", assets_mod);
const gen_asset_manifest = buildAssets(b, &install_assetc_step.step, assets_step, assetc, "assets") catch |err| {
std.log.err("Failed to build assets {}\n", .{err});
@panic("buildAssets");
@ -205,15 +203,18 @@ fn buildAssets(b: *std.Build, install_assetc_step: *Step, step: *Step, assetc: *
return asset_manifest_file;
}
fn buildAssetCompiler(b: *Build, optimize: std.builtin.OptimizeMode) *Step.Compile {
fn buildAssetCompiler(b: *Build, optimize: std.builtin.OptimizeMode, assets_mod: *Build.Module) *Step.Compile {
const assimp_dep = b.dependency("zig-assimp", .{
.target = b.host,
.optimize = optimize,
//.formats = @as([]const u8, "3DS,3MF,AC,AMF,ASE,Assbin,Assjson,Assxml,B3D,Blender,BVH,C4D,COB,Collada,CSM,DXF,FBX,glTF,glTF2,HMP,IFC,Irr,LWO,LWS,M3D,MD2,MD3,MD5,MDC,MDL,MMD,MS3D,NDO,NFF,Obj,OFF,Ogre,OpenGEX,Ply,Q3BSP,Q3D,Raw,SIB,SMD,Step,STEPParser,STL,Terragen,Unreal,X,X3D,XGL"),
.formats = @as([]const u8, "Obj"),
});
const zalgebra_dep = b.dependency("zalgebra", .{});
const assimp_lib = assimp_dep.artifact("assimp");
// HACK: fix in assimp
assimp_lib.defineCMacro("AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER", "\"AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER\"");
const assetc = b.addExecutable(.{
.name = "assetc",
@ -223,13 +224,21 @@ fn buildAssetCompiler(b: *Build, optimize: std.builtin.OptimizeMode) *Step.Compi
});
assetc.linkLibC();
b.installFile("libs/ispc_texcomp/lib/ispc_texcomp.dll", "ispc_texcomp.dll");
b.installFile("libs/ispc_texcomp/lib/ispc_texcomp.pdb", "ispc_texcomp.pdb");
if (b.host.result.os.tag == .windows) {
b.installFile("libs/ispc_texcomp/lib/ispc_texcomp.dll", "ispc_texcomp.dll");
b.installFile("libs/ispc_texcomp/lib/ispc_texcomp.pdb", "ispc_texcomp.pdb");
}
assetc.addLibraryPath(.{ .path = "libs/ispc_texcomp/lib" });
assetc.addIncludePath(.{ .path = "libs/ispc_texcomp/include" });
assetc.linkSystemLibrary("ispc_texcomp");
assetc.root_module.addAnonymousImport("formats", .{ .root_source_file = .{ .path = "src/formats.zig" } });
const zalgebra_mod = zalgebra_dep.module("zalgebra");
const formats_mod = b.addModule("formats", .{ .root_source_file = .{ .path = "src/formats.zig" } });
formats_mod.addImport("zalgebra", zalgebra_mod);
formats_mod.addImport("assets", assets_mod);
assetc.root_module.addImport("formats", formats_mod);
assetc.root_module.addImport("zalgebra", zalgebra_mod);
assetc.root_module.addImport("assets", assets_mod);
assetc.linkLibrary(assimp_lib);
assetc.linkLibC();

View File

@ -122,6 +122,21 @@ pub fn resolveTexture(self: *AssetManager, handle: Handle.Texture) *const Loaded
return self.loadTexture(handle.id);
}
pub fn resolveScene(self: *AssetManager, handle: Handle.Scene) *const formats.Scene {
if (handle.id == 0) return &NullScene.scene;
if (self.loaded_assets.getPtr(handle.id)) |asset| {
switch (asset.*) {
.scene => |*scene| {
return &scene.scene;
},
else => unreachable,
}
}
return &self.loadScene(handle.id).scene;
}
// TODO: proper watching
pub fn watchChanges(self: *AssetManager) void {
var iter = self.loaded_assets.iterator();
@ -258,6 +273,11 @@ const NullTexture = LoadedTexture{
.handle = 0,
};
const NullScene = LoadedScene{
.buf = "",
.scene = .{},
};
pub fn loadMesh(self: *AssetManager, id: AssetId) *const LoadedMesh {
return self.loadMeshErr(id) catch |err| {
std.log.err("Error: {} loading mesh at path: {s}", .{ err, asset_manifest.getPath(id) });
@ -425,11 +445,41 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !*const LoadedTexture {
return &self.loaded_assets.getPtr(id).?.texture;
}
fn loadScene(self: *AssetManager, id: AssetId) *const LoadedScene {
return self.loadSceneErr(id) catch |err| {
std.log.err("Error: {} loading scene at path {s}\n", .{ err, asset_manifest.getPath(id) });
return &NullScene;
};
}
fn loadSceneErr(self: *AssetManager, id: AssetId) !*const LoadedScene {
const path = asset_manifest.getPath(id);
const data = try self.loadFile(self.allocator, path, TEXTURE_MAX_BYTES);
const scene = try formats.Scene.fromBuffer(data.bytes);
try self.loaded_assets.put(
self.allocator,
id,
.{
.scene = LoadedScene{
.buf = data.bytes,
.scene = scene,
},
},
);
try self.modified_times.put(self.allocator, id, data.modified);
return &self.loaded_assets.getPtr(id).?.scene;
}
const LoadedAsset = union(enum) {
shader: LoadedShader,
shaderProgram: LoadedShaderProgram,
mesh: LoadedMesh,
texture: LoadedTexture,
scene: LoadedScene,
};
const LoadedShader = struct {
@ -454,6 +504,12 @@ const LoadedTexture = struct {
handle: gl.GLuint64,
};
const LoadedScene = struct {
// Buffer that holds scene data
buf: []const u8,
scene: formats.Scene,
};
pub const AABB = struct {
min: Vec3 = Vec3.zero(),
max: Vec3 = Vec3.zero(),
@ -607,6 +663,9 @@ fn unloadAssetWithDependees(self: *AssetManager, id: AssetId) void {
gl.GL_ARB_bindless_texture.makeTextureHandleNonResidentARB(texture.handle);
gl.deleteTextures(1, &texture.name);
},
.scene => |*scene| {
self.allocator.free(scene.buf);
},
}
}
_ = self.loaded_assets.remove(id);

View File

@ -4,6 +4,7 @@ const c = @import("sdl.zig");
const AssetManager = @import("AssetManager.zig");
const a = @import("asset_manifest");
const globals = @import("globals.zig");
pub const Material = @import("formats.zig").Material;
const za = @import("zalgebra");
const Vec2 = za.Vec2;
@ -27,9 +28,10 @@ mesh_vao: gl.GLuint = 0,
tripple_buffer_index: usize = MAX_FRAMES_QUEUED - 1,
gl_fences: [MAX_FRAMES_QUEUED]?gl.GLsync = [_]?gl.GLsync{null} ** MAX_FRAMES_QUEUED,
camera_ubo: gl.GLuint = 0,
camera_matrices: []CameraMatrices = &.{},
camera_matrices: []u8 = &.{},
point_lights_ubo: gl.GLuint = 0,
point_lights: []PointLightArray = &.{},
point_lights: []u8 = &.{},
ubo_align: usize = 0,
pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetman: *AssetManager) Render {
var render = Render{
@ -38,6 +40,13 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
.assetman = assetman,
};
var buffer_align_int: gl.GLint = 0;
gl.getIntegerv(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT, &buffer_align_int);
if (buffer_align_int == 0) @panic("Failed to query GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT");
render.ubo_align = @intCast(buffer_align_int);
// MESH VAO
var vao: gl.GLuint = 0;
gl.createVertexArrays(1, &vao);
@ -72,17 +81,23 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
gl.createBuffers(1, &render.camera_ubo);
std.debug.assert(render.camera_ubo != 0);
const buf_size = render.uboAlignedSizeOf(CameraMatrices) * MAX_FRAMES_QUEUED;
gl.namedBufferStorage(
render.camera_ubo,
@sizeOf(CameraMatrices) * MAX_FRAMES_QUEUED,
@intCast(buf_size),
null,
PERSISTENT_BUFFER_FLAGS,
);
const camera_matrices_c: [*c]CameraMatrices = @alignCast(@ptrCast(gl.mapNamedBufferRange(render.camera_ubo, 0, @sizeOf(CameraMatrices) * MAX_FRAMES_QUEUED, PERSISTENT_BUFFER_FLAGS) orelse {
const camera_matrices_c: [*]u8 = @ptrCast(gl.mapNamedBufferRange(
render.camera_ubo,
0,
@intCast(buf_size),
PERSISTENT_BUFFER_FLAGS,
) orelse {
checkGLError();
@panic("bind camera_ubo");
}));
render.camera_matrices = camera_matrices_c[0..MAX_FRAMES_QUEUED];
});
render.camera_matrices = camera_matrices_c[0..buf_size];
}
// Point lights ubo
@ -90,22 +105,23 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
gl.createBuffers(1, &render.point_lights_ubo);
std.debug.assert(render.camera_ubo != 0);
const buf_size = render.uboAlignedSizeOf(PointLightArray) * MAX_FRAMES_QUEUED;
gl.namedBufferStorage(
render.point_lights_ubo,
@sizeOf(PointLightArray) * MAX_FRAMES_QUEUED,
@intCast(buf_size),
null,
PERSISTENT_BUFFER_FLAGS,
);
const point_lights_c: [*c]PointLightArray = @alignCast(@ptrCast(gl.mapNamedBufferRange(
const point_lights_c: [*]u8 = @ptrCast(gl.mapNamedBufferRange(
render.point_lights_ubo,
0,
@sizeOf(PointLightArray) * MAX_FRAMES_QUEUED,
@intCast(buf_size),
PERSISTENT_BUFFER_FLAGS,
) orelse {
checkGLError();
@panic("bind point_lights_ubo");
}));
render.point_lights = point_lights_c[0..MAX_FRAMES_QUEUED];
});
render.point_lights = point_lights_c[0..buf_size];
}
return render;
@ -123,7 +139,7 @@ pub fn begin(self: *Render) void {
gl.bindVertexArray(self.mesh_vao);
if (self.gl_fences[self.tripple_buffer_index]) |fence| {
const syncResult = gl.clientWaitSync(fence, gl.SYNC_FLUSH_COMMANDS_BIT, 9999999);
const syncResult = gl.clientWaitSync(fence, gl.SYNC_FLUSH_COMMANDS_BIT, 9999999999);
switch (syncResult) {
gl.ALREADY_SIGNALED => {
@ -132,6 +148,7 @@ pub fn begin(self: *Render) void {
gl.TIMEOUT_EXPIRED => {
// oh no, driver will crash soon :(
std.log.err("OpenGL clientWaitSync timeout expired D:\n", .{});
checkGLError();
},
gl.CONDITION_SATISFIED => {
// awesome
@ -146,35 +163,43 @@ pub fn begin(self: *Render) void {
}
self.gl_fences[self.tripple_buffer_index] = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
}
pub fn getPointLights(self: *Render) *PointLightArray {
return @alignCast(@ptrCast(self.point_lights[self.tripple_buffer_index * self.uboAlignedSizeOf(PointLightArray) ..].ptr));
}
pub fn flushUBOs(self: *Render) void {
const idx = self.tripple_buffer_index;
{
const camera_matrix = &self.camera_matrices[self.tripple_buffer_index];
const camera_matrix: *CameraMatrices = @alignCast(@ptrCast(self.camera_matrices[idx * self.uboAlignedSizeOf(CameraMatrices) ..].ptr));
camera_matrix.* = .{
.projection = self.camera.projection(),
.view = self.camera.view_mat,
};
//gl.flushMappedNamedBufferRange(self.camera_ubo, idx * @sizeOf(CameraMatrices), @sizeOf(CameraMatrices));
gl.bindBufferRange(
gl.UNIFORM_BUFFER,
UBO.CameraMatrices.value(),
self.camera_ubo,
self.tripple_buffer_index * @sizeOf(CameraMatrices),
@sizeOf(CameraMatrices),
idx * self.uboAlignedSizeOf(CameraMatrices),
@intCast(self.uboAlignedSizeOf(CameraMatrices)),
);
checkGLError();
}
// gl.flushMappedNamedBufferRange(self.point_lights_ubo, idx * @sizeOf(PointLightArray), @sizeOf(PointLightArray));
gl.bindBufferRange(
gl.UNIFORM_BUFFER,
UBO.PointLights.value(),
self.point_lights_ubo,
self.tripple_buffer_index * @sizeOf(PointLightArray),
@sizeOf(PointLightArray),
idx * self.uboAlignedSizeOf(PointLightArray),
@intCast(self.uboAlignedSizeOf(PointLightArray)),
);
}
pub fn getPointLights(self: *Render) *PointLightArray {
return &self.point_lights[self.tripple_buffer_index];
checkGLError();
}
pub fn draw(self: *Render, cmd: DrawCommand) void {
@ -321,14 +346,6 @@ pub const PointLightArray = extern struct {
count: c_uint,
};
pub const Material = struct {
albedo: Vec3 = Vec3.one(),
albedo_map: AssetManager.Handle.Texture = .{},
normal_map: AssetManager.Handle.Texture = .{},
metallic: f32 = 0,
metallic_map: AssetManager.Handle.Texture = .{},
roughness: f32 = 1,
roughness_map: AssetManager.Handle.Texture = .{},
emission: f32 = 0,
emission_map: AssetManager.Handle.Texture = .{},
};
fn uboAlignedSizeOf(self: *const Render, comptime T: type) usize {
return std.mem.alignForward(usize, @sizeOf(T), self.ubo_align);
}

View File

@ -1,8 +1,9 @@
pub const AssetId = u64;
pub const Handle = struct {
pub const Scene = extern struct { id: AssetId = 0 };
pub const Shader = extern struct { id: AssetId = 0 };
pub const ShaderProgram = extern struct { id: AssetId = 0 };
pub const Mesh = struct { id: AssetId = 0 };
pub const Texture = struct { id: AssetId = 0 };
pub const Mesh = extern struct { id: AssetId = 0 };
pub const Texture = extern struct { id: AssetId = 0 };
};

View File

@ -1,6 +1,9 @@
const std = @import("std");
const builtin = @import("builtin");
const Handle = @import("assets").Handle;
const za = @import("zalgebra");
const Vec3 = za.Vec3;
pub const Entity = @import("globals.zig").Entity;
pub const native_endian = builtin.cpu.arch.endian();
@ -229,6 +232,10 @@ pub const Texture = struct {
.data = data,
};
}
pub fn free(self: *Texture, allocator: std.mem.Allocator) void {
allocator.free(self.data);
}
};
// TODO: this doesn't respect endiannes at all
@ -245,19 +252,111 @@ test "texture write/parse" {
var data = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
const source = Texture{
.header = .{
.format = .bc7_srgb,
.format = .bc7,
.width = 123,
.height = 234,
.mip_levels = 1,
.size = data.len,
.mip_count = 1,
},
.data = &data,
.data = &.{&data},
};
var buf: [@sizeOf(Texture.Header) + data.len]u8 = undefined;
var buf: [@sizeOf(Texture.Header) + data.len + 4]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf);
try writeTexture(stream.writer(), source);
try writeTexture(stream.writer(), source, native_endian);
const decoded = try Texture.fromBuffer(&buf);
var decoded = try Texture.fromBuffer(std.testing.allocator, &buf);
defer decoded.free(std.testing.allocator);
try std.testing.expectEqualDeep(source, decoded);
}
pub const Scene = struct {
pub const Header = extern struct {
magic: [4]u8 = [_]u8{ 'S', 'C', 'N', 'F' }, // Scene Format
entity_count: u32 = 0,
};
header: Header = .{},
entities: []align(1) const Entity.Data = &.{},
/// Store parenting info for each entity
/// NOTE: Parent index should never be larger than entity index
/// because entities will be created in order
/// -1 means no parent
parents: []align(1) const i64 = &.{},
pub fn fromBuffer(buf: []u8) !Scene {
const header_ptr: *align(1) Header = @ptrCast(buf.ptr);
const header = header_ptr.*;
if (!std.mem.eql(u8, &header.magic, "SCNF")) {
return error.MagicMatch;
}
var offset: usize = @sizeOf(Header);
var size: usize = @sizeOf(Entity.Data) * header.entity_count;
const entities: []align(1) Entity.Data = std.mem.bytesAsSlice(Entity.Data, buf[offset .. offset + size]);
offset += size;
size = @sizeOf(i64) * header.entity_count;
const parents: []align(1) i64 = std.mem.bytesAsSlice(i64, buf[offset .. offset + size]);
offset += size;
return Scene{
.header = header,
.entities = entities,
.parents = parents,
};
}
};
// TODO: this doesn't respect endiannes at all
pub fn writeScene(writer: anytype, value: Scene, endian: std.builtin.Endian) !void {
try writer.writeStruct(value.header);
// TODO: make writeSlice?
for (value.entities) |ent| {
try writer.writeStruct(ent);
}
for (value.parents) |parentIdx| {
try writer.writeInt(i64, parentIdx, endian);
}
}
test "write and read scene" {
var entities = [_]Entity.Data{
.{
.flags = .{ .point_light = true },
.transform = .{},
},
.{
.flags = .{ .point_light = true },
.transform = .{ .pos = Vec3.new(1, 2, 3) },
},
};
var parents = [_]i64{-1} ** entities.len;
const source = Scene{
.header = .{
.entity_count = entities.len,
},
.entities = &entities,
.parents = &parents,
};
var buf: [@sizeOf(Scene.Header) + entities.len * @sizeOf(Entity.Data) + entities.len * @sizeOf(i64)]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf);
try writeScene(stream.writer(), source, native_endian);
const decoded = try Scene.fromBuffer(&buf);
try std.testing.expectEqualDeep(source, decoded);
}
pub const Material = extern struct {
albedo: Vec3 = Vec3.one(),
albedo_map: Handle.Texture = .{},
normal_map: Handle.Texture = .{},
metallic: f32 = 0,
metallic_map: Handle.Texture = .{},
roughness: f32 = 1,
roughness_map: Handle.Texture = .{},
emission: f32 = 0,
emission_map: Handle.Texture = .{},
};

View File

@ -127,6 +127,7 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
.frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer),
.assetman = AssetManager.init(global_allocator.*, globals.g_mem.frame_fba.allocator()),
.render = Render.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_mem.assetman),
.world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() },
};
globals.g_mem.render.camera = &globals.g_mem.free_cam.camera;
std.log.debug("actual ptr: {}, correct ptr {}", .{ globals.g_mem.assetman.frame_arena.ptr, globals.g_mem.frame_fba.allocator().ptr });
@ -142,23 +143,29 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
gl.viewport(0, 0, globals.g_init.width, globals.g_init.height);
const light_root = globals.g_mem.world.addEntity(.{
.flags = .{ .rotate = true },
.transform = .{ .pos = Vec3.new(0, 0.1, 0) },
.rotate = .{ .axis = Vec3.up(), .rate = 60 },
});
const light1 = globals.g_mem.world.addEntity(.{
.transform = .{ .pos = Vec3.new(1.8, 1, 0) },
.flags = .{ .point_light = true, .rotate = true },
.point_light = .{ .color_intensity = Vec4.new(1.0, 0.3, 0.1, 100.0), .radius = 0.1 },
.rotate = .{ .axis = Vec3.up(), .rate = 60 },
.rotate = .{ .axis = Vec3.up(), .rate = -40 },
});
light1.ptr.setParent(light_root.handle);
_ = globals.g_mem.world.addEntity(.{
const light2 = globals.g_mem.world.addEntity(.{
.transform = .{ .pos = Vec3.new(-2, 0, 0) },
.parent = light1,
.flags = .{ .point_light = true, .rotate = true },
.point_light = .{
.color_intensity = Vec4.new(0.2, 0.5, 1.0, 100.0),
.radius = 0.1,
},
.rotate = .{ .axis = Vec3.up(), .rate = -20 },
});
light2.ptr.setParent(light1.handle);
_ = globals.g_mem.world.addEntity(.{
.transform = .{ .pos = Vec3.new(1, 0.5, 4) },
@ -219,6 +226,10 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
});
}
}
const test_scene_root = globals.g_mem.world.createScene(globals.g_assetman.resolveScene(a.Scenes.test_scene.scene));
std.log.debug("test scene root idx {}\n", .{test_scene_root.idx});
}
export fn game_update() bool {
@ -257,10 +268,11 @@ export fn game_update() bool {
},
c.SDL_KEYUP, c.SDL_KEYDOWN => {
const pressed = event.key.state == c.SDL_PRESSED;
switch (event.key.keysym.scancode) {
// Toggle fullscreen
c.SDL_SCANCODE_F11 => {
if (event.type == c.SDL_KEYDOWN) {
c.SDL_SCANCODE_RETURN => {
if (event.type == c.SDL_KEYDOWN and event.key.keysym.mod & c.KMOD_ALT > 0) {
toggleFullScreen() catch continue;
}
},
@ -344,15 +356,27 @@ export fn game_update() bool {
move.zMut().* -= 1;
}
// TODO: make this an entity
gmem.free_cam.update(gmem.delta_time, move, look.scale(0.008));
const f_width: f32 = @floatFromInt(ginit.width);
const f_height: f32 = @floatFromInt(ginit.height);
gmem.free_cam.camera.aspect = f_width / f_height;
gmem.rotation += 60 * gmem.delta_time;
// TODO: make this an entity
gmem.free_cam.update(gmem.delta_time, move, look.scale(0.008));
// Update
{
for (gmem.world.entities[0..gmem.world.entity_count]) |*ent| {
if (!ent.data.flags.active) continue;
if (ent.data.flags.rotate) {
ent.data.transform.rotate(ent.data.rotate.axis, ent.data.rotate.rate * gmem.delta_time);
}
}
}
// Render
{
gmem.render.begin();
defer gmem.render.finish();
@ -364,25 +388,16 @@ export fn game_update() bool {
for (0..gmem.world.entity_count) |i| {
const ent = &gmem.world.entities[i];
if (!ent.flags.active) continue;
if (!ent.data.flags.active) continue;
if (ent.flags.rotate) {
const old_pos = ent.transform.pos;
const new_pos = Mat4.fromRotation(
ent.rotate.rate * gmem.delta_time,
ent.rotate.axis,
).mulByVec4(Vec4.new(old_pos.x(), old_pos.y(), old_pos.z(), 1));
ent.transform.setPos(Vec3.new(new_pos.x(), new_pos.y(), new_pos.z()));
}
if (ent.flags.point_light) {
if (ent.data.flags.point_light) {
const pos = ent.globalMatrix(&gmem.world).extractTranslation();
var pos4 = Vec4.new(pos.x(), pos.y(), pos.z(), 1.0);
pos4 = gmem.render.camera.view_mat.mulByVec4(pos4);
point_lights.lights[point_lights.count] = .{
.pos_radius = Vec4.new(pos4.x(), pos4.y(), pos4.z(), ent.point_light.radius),
.color_intensity = ent.point_light.color_intensity,
.pos_radius = Vec4.new(pos4.x(), pos4.y(), pos4.z(), ent.data.point_light.radius),
.color_intensity = ent.data.point_light.color_intensity,
};
point_lights.count += 1;
if (point_lights.count == Render.MAX_POINT_LIGHTS) {
@ -390,25 +405,27 @@ export fn game_update() bool {
}
}
}
}
// Render meshes and lights
for (0..gmem.world.entity_count) |i| {
const ent = &gmem.world.entities[i];
if (!ent.flags.active) continue;
gmem.render.flushUBOs();
if (ent.flags.mesh) {
gmem.render.draw(.{
.mesh = ent.mesh.handle,
.material = ent.mesh.material,
.transform = ent.globalMatrix(&gmem.world).*,
});
} else if (ent.flags.point_light) {
gmem.render.draw(.{
.mesh = a.Meshes.sphere,
.material = .{ .albedo = ent.point_light.color() },
.transform = ent.globalMatrix(&gmem.world).*,
});
// Render meshes and lights
for (0..gmem.world.entity_count) |i| {
const ent = &gmem.world.entities[i];
if (!ent.data.flags.active) continue;
if (ent.data.flags.mesh) {
gmem.render.draw(.{
.mesh = ent.data.mesh.handle,
.material = ent.data.mesh.material,
.transform = ent.globalMatrix(&gmem.world).*,
});
} else if (ent.data.flags.point_light) {
gmem.render.draw(.{
.mesh = a.Meshes.sphere,
.material = .{ .albedo = ent.data.point_light.color() },
.transform = ent.globalMatrix(&gmem.world).*,
});
}
}
}
}

View File

@ -1,5 +1,6 @@
pub const manifest = @import("asset_manifest_gen");
pub const Scenes = manifest.Scenes;
pub const Meshes = manifest.Meshes;
pub const Shaders = manifest.Shaders;
pub const ShaderPrograms = manifest.ShaderPrograms;

View File

@ -2,6 +2,9 @@ const std = @import("std");
const c = @import("sdl.zig");
const AssetManager = @import("AssetManager.zig");
const Render = @import("Render.zig");
const formats = @import("formats.zig");
const Material = formats.Material;
const Scene = formats.Scene;
const za = @import("zalgebra");
const Vec2 = za.Vec2;
@ -37,9 +40,10 @@ pub const Entity = struct {
mesh: bool = false,
point_light: bool = false,
rotate: bool = false,
_pad: u4 = 0, // make it abi sized
};
pub const Transform = struct {
pub const Transform = extern struct {
pos: Vec3 = Vec3.zero(),
rot: Quat = Quat.identity(),
scale: Vec3 = Vec3.one(),
@ -64,13 +68,18 @@ pub const Entity = struct {
self.pos = self.pos.add(by);
self.dirty();
}
pub fn rotate(self: *Transform, axis: Vec3, angle: f32) void {
self.rot = self.rot.mul(Quat.fromAxis(angle, axis));
self.dirty();
}
};
pub const Mesh = struct {
pub const Mesh = extern struct {
handle: AssetManager.Handle.Mesh = .{},
material: Render.Material = .{},
material: Material = .{},
};
pub const PointLight = struct {
pub const PointLight = extern struct {
radius: f32 = std.math.floatEps(f32), // should never be 0 or bad things happen
color_intensity: Vec4 = Vec4.one(), // x, y, z - color, w - intensity
@ -79,42 +88,53 @@ pub const Entity = struct {
return Vec3.new(col.x(), col.y(), col.z());
}
};
pub const Rotate = struct {
pub const Rotate = extern struct {
axis: Vec3 = Vec3.up(),
rate: f32 = 0, // deg/s
};
/// Serializable entity data
pub const Data = extern struct {
flags: Flags = .{},
transform: Transform = .{},
mesh: Mesh = .{},
point_light: PointLight = .{},
rotate: Rotate = .{},
};
// Entity list and handle management
idx: u32 = 0,
gen: u32 = 0,
// Free list
next: ?*Entity = null,
flags: Flags = .{},
parent: ?EntityHandle = null,
transform: Transform = .{},
mesh: Mesh = .{},
point_light: PointLight = .{},
rotate: Rotate = .{},
data: Data = .{},
pub fn setParent(self: *Entity, parent: ?EntityHandle) void {
self.parent = parent;
self.data.transform.dirty();
}
pub fn localMatrix(self: *Entity) *const Mat4 {
if (self.transform._local_dirty) {
self.transform._local = Mat4.recompose(self.transform.pos, self.transform.rot, self.transform.scale);
self.transform._local_dirty = false;
if (self.data.transform._local_dirty) {
self.data.transform._local = Mat4.recompose(self.data.transform.pos, self.data.transform.rot, self.data.transform.scale);
self.data.transform._local_dirty = false;
}
return &self.transform._local;
return &self.data.transform._local;
}
pub fn globalMatrix(self: *Entity, world: *World) *const Mat4 {
// TODO: think how to reduce pointer chasing
if (self.parent) |parent_ent| {
if (world.getEntity(parent_ent)) |parent| {
if (parent.transform._global_dirty or self.transform._global_dirty) {
self.transform._global = parent.globalMatrix(world).mul(self.localMatrix().*);
self.transform._global_dirty = false;
if (parent.data.transform._global_dirty or self.data.transform._global_dirty) {
self.data.transform._global = parent.globalMatrix(world).mul(self.localMatrix().*);
self.data.transform._global_dirty = false;
}
return &self.transform._global;
return &self.data.transform._global;
}
}
@ -123,16 +143,28 @@ pub const Entity = struct {
};
pub const EntityHandle = packed struct {
idx: u32,
gen: u32,
idx: u32 = 0,
gen: u32 = 0,
/// Returns handle with 0 idx and gen.
/// 0 gen is invalid, valid generations start from 0
pub fn invalid() EntityHandle {
return EntityHandle{};
}
};
pub const EntityCreateResult = struct {
handle: EntityHandle,
ptr: *Entity,
};
pub const World = struct {
frame_arena: std.mem.Allocator,
entities: [MAX_ENTITIES]Entity = [_]Entity{.{}} ** MAX_ENTITIES,
entity_count: usize = 0,
free_entity: ?*Entity = null,
pub fn addEntity(self: *World, entity: Entity) EntityHandle {
pub fn addEntity(self: *World, data: Entity.Data) EntityCreateResult {
const ent = result: {
if (self.free_entity) |ent| {
break :result ent;
@ -146,16 +178,44 @@ pub const World = struct {
const next = ent.next;
const gen = ent.gen;
const idx = ent.idx;
ent.* = entity;
ent.data = data;
// TODO: handle wrapping
ent.gen = gen + 1;
ent.idx = idx;
ent.flags.active = true;
ent.data.flags.active = true;
self.free_entity = next;
return EntityHandle{ .idx = idx, .gen = ent.gen };
return EntityCreateResult{
.handle = EntityHandle{ .idx = idx, .gen = ent.gen },
.ptr = ent,
};
}
/// Spawns a scene and returns a hand to the root entity
pub fn createScene(self: *World, scene: *const Scene) EntityHandle {
if (scene.entities.len == 0) {
return EntityHandle.invalid();
}
const handles = self.frame_arena.alloc(EntityHandle, scene.entities.len) catch @panic("OOM"); // not handling error, this is unrecoverable
for (0.., handles, scene.entities, scene.parents) |i, *out_handle, *ent, parent| {
const res = self.addEntity(ent.*);
out_handle.* = res.handle;
if (parent >= 0) {
std.debug.assert(parent < i);
res.ptr.parent = handles[@intCast(parent)];
}
}
return handles[0];
}
pub fn getEntity(self: *World, handle: EntityHandle) ?*Entity {
// Gen 0 is always invalid
if (handle.gen == 0) return null;
const ent = &self.entities[handle.idx];
if (ent.gen != handle.gen) {
return null;
@ -183,7 +243,7 @@ pub const GameMemory = struct {
input_state: InputState = .{},
free_cam: FreeLookCamera = .{},
mouse_focus: bool = false,
world: World = .{},
world: World,
};
pub const InputState = packed struct {

View File

@ -7,6 +7,8 @@ const asset_list = @import("asset_list.zig");
const AssetListEntry = asset_list.AssetListEntry;
const Vector2 = formats.Vector2;
const Vector3 = formats.Vector3;
const za = @import("zalgebra");
const Vec3 = za.Vec3;
const c = @cImport({
@cInclude("assimp/cimport.h");
@cInclude("assimp/scene.h");
@ -22,8 +24,8 @@ const c = @cImport({
const ASSET_MAX_BYTES = 1024 * 1024 * 1024;
pub fn resolveAssetTypeByExtension(path: []const u8) ?AssetType {
if (std.mem.endsWith(u8, path, ".obj")) {
return .Mesh;
if (std.mem.endsWith(u8, path, ".obj") or std.mem.endsWith(u8, path, ".fbx")) {
return .Scene;
}
if (std.mem.endsWith(u8, path, ".prog")) {
return .ShaderProgram;
@ -69,11 +71,11 @@ pub fn main() !void {
std.log.debug("rel_input: {s}", .{rel_input});
switch (asset_type) {
.Mesh => try processMesh(allocator, rel_input, output_dir, asset_list_writer),
.Scene => try processScene(allocator, rel_input, output_dir, asset_list_writer),
.Shader => try copyFile(asset_type, rel_input, output_dir, asset_list_writer),
.ShaderProgram => try processShaderProgram(allocator, rel_input, output_dir, asset_list_writer),
.Texture => try processTexture(allocator, rel_input, output_dir, asset_list_writer),
.Scene => return error.NotImplemented,
else => unreachable,
}
try buf_asset_list_writer.flush();
}
@ -95,7 +97,12 @@ fn copyFile(_type: AssetType, input: []const u8, output_dir: std.fs.Dir, asset_l
try asset_list.writeAssetListEntryText(asset_list_writer, asset_list_entry);
}
fn createOutput(_type: AssetType, asset_path: AssetPath, output_dir: std.fs.Dir, writer: anytype) !std.fs.File {
const AssetOutput = struct {
file: std.fs.File,
list_entry: AssetListEntry,
};
fn createOutput(_type: AssetType, asset_path: AssetPath, output_dir: std.fs.Dir, writer: anytype) !AssetOutput {
const asset_list_entry = AssetListEntry{
.type = _type,
.src_path = asset_path,
@ -109,10 +116,18 @@ fn createOutput(_type: AssetType, asset_path: AssetPath, output_dir: std.fs.Dir,
try asset_list.writeAssetListEntryText(writer, asset_list_entry);
return try output_subdir.createFile(std.fs.path.basename(out_path), .{});
const file = try output_subdir.createFile(std.fs.path.basename(out_path), .{});
return AssetOutput{
.file = file,
.list_entry = asset_list_entry,
};
}
fn processMesh(allocator: std.mem.Allocator, input: []const u8, output_dir: std.fs.Dir, asset_list_writer: anytype) !void {
/// This can output either a single mesh (for simple formats like obj)
/// or a scene + a bunch of sub assets (meshes, materials, textures, animations, etc.)
/// It all depends on the source asset.
fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std.fs.Dir, asset_list_writer: anytype) !void {
const input_z = try std.mem.concatWithSentinel(allocator, u8, &.{input}, 0);
const maybe_scene: ?*const c.aiScene = @ptrCast(c.aiImportFile(
input_z.ptr,
@ -126,10 +141,103 @@ fn processMesh(allocator: std.mem.Allocator, input: []const u8, output_dir: std.
defer c.aiReleaseImport(scene);
if (scene.mNumMeshes == 0) return error.NoMeshes;
if (scene.mNumMeshes > 1) return error.TooManyMeshes;
const mesh: *c.aiMesh = @ptrCast(scene.mMeshes[0]);
if (scene.mNumMeshes == 1) {
const mesh: *c.aiMesh = @ptrCast(scene.mMeshes[0]);
var output = try createOutput(.Mesh, AssetPath{ .simple = input }, output_dir, asset_list_writer);
defer output.file.close();
return try processMesh(allocator, scene, mesh, output.file);
} else {
const base_asset_path = AssetPath{ .simple = input };
const meshes: []*c.aiMesh = @ptrCast(scene.mMeshes[0..@intCast(scene.mNumMeshes)]);
var mesh_outputs = std.ArrayList(AssetListEntry).init(allocator);
for (meshes) |mesh| {
const name = mesh.mName.data[0..mesh.mName.length];
var output = try createOutput(.Mesh, base_asset_path.subPath(name), output_dir, asset_list_writer);
defer output.file.close();
try mesh_outputs.append(output.list_entry);
try processMesh(allocator, scene, mesh, output.file);
}
if (scene.mRootNode == null) return;
var node_to_entity_idx = std.AutoHashMap(*c.aiNode, usize).init(allocator);
var entities = std.ArrayList(formats.Entity.Data).init(allocator);
var parents = std.ArrayList(i64).init(allocator);
// Breadth first traversal
var nodeq = std.ArrayList(*c.aiNode).init(allocator);
try nodeq.append(@ptrCast(scene.mRootNode));
while (nodeq.popOrNull()) |node| {
if (node.mChildren != null) {
const children: []*c.aiNode = @ptrCast(node.mChildren[0..@intCast(node.mNumChildren)]);
for (0..children.len) |i| {
// Reverse order, because pop taks from end of the list
const child = children[children.len - i - 1];
try nodeq.append(child);
}
}
try entities.append(.{});
const idx = entities.items.len - 1;
try node_to_entity_idx.put(node, idx);
const maybe_parent: ?*c.aiNode = @ptrCast(node.mParent);
if (maybe_parent) |parent| {
const parent_idx = node_to_entity_idx.get(parent) orelse return error.MissingParentIdx; // this is a bug in our code
try parents.append(@intCast(parent_idx));
} else {
try parents.append(-1);
}
const ent = &entities.items[idx];
// TODO: extract transform
if (node.mMeshes != null) {
const mesh_indices = node.mMeshes[0..node.mNumMeshes];
for (mesh_indices) |mesh_idx| {
const mesh_entry = mesh_outputs.items[@intCast(mesh_idx)];
ent.flags.mesh = true;
ent.flags.rotate = true;
// TODO: turn multiple meshes into sub-entities
ent.mesh = .{
.handle = .{ .id = mesh_entry.src_path.hash() },
// TODO: extract material
};
ent.rotate = .{ .axis = Vec3.up(), .rate = 80 };
}
}
}
const out_scene = formats.Scene{
.header = .{
.entity_count = @intCast(entities.items.len),
},
.entities = entities.items,
.parents = parents.items,
};
const output = try createOutput(.Scene, base_asset_path.subPath("scene"), output_dir, asset_list_writer);
defer output.file.close();
var buf_writer = std.io.bufferedWriter(output.file.writer());
try formats.writeScene(buf_writer.writer(), out_scene, formats.native_endian);
try buf_writer.flush();
}
}
fn processMesh(allocator: std.mem.Allocator, scene: *const c.aiScene, mesh: *const c.aiMesh, out_file: std.fs.File) !void {
_ = scene; // autofix
if (mesh.mNormals == null) return error.MissingNormals;
if (mesh.mTangents == null) return error.MissingTangents;
if (mesh.mTextureCoords[0] == null) return error.MissingUVs;
@ -196,10 +304,7 @@ fn processMesh(allocator: std.mem.Allocator, input: []const u8, output_dir: std.
.indices = indices,
};
var out_file = try createOutput(.Mesh, AssetPath{ .simple = input }, output_dir, asset_list_writer);
defer out_file.close();
var buf_writer = std.io.bufferedWriter(out_file.writer());
try formats.writeMesh(
buf_writer.writer(),
out_mesh,
@ -236,9 +341,9 @@ fn processShaderProgram(allocator: std.mem.Allocator, input: []const u8, output_
return error.InvalidShaderPath;
}
var out_file = try createOutput(.ShaderProgram, AssetPath{ .simple = input }, output_dir, asset_list_writer);
defer out_file.close();
var buf_writer = std.io.bufferedWriter(out_file.writer());
const output = try createOutput(.ShaderProgram, AssetPath{ .simple = input }, output_dir, asset_list_writer);
defer output.file.close();
var buf_writer = std.io.bufferedWriter(output.file.writer());
try formats.writeShaderProgram(buf_writer.writer(), shader_asset_id, program.value.vertex, program.value.fragment, formats.native_endian);
try buf_writer.flush();
@ -347,9 +452,9 @@ fn processTexture(allocator: std.mem.Allocator, input: []const u8, output_dir: s
.data = out_data,
};
const out_file = try createOutput(.Texture, AssetPath{ .simple = input }, output_dir, asset_list_writer);
defer out_file.close();
var buf_writer = std.io.bufferedWriter(out_file.writer());
const output = try createOutput(.Texture, AssetPath{ .simple = input }, output_dir, asset_list_writer);
defer output.file.close();
var buf_writer = std.io.bufferedWriter(output.file.writer());
try formats.writeTexture(buf_writer.writer(), texture, formats.native_endian);
try buf_writer.flush();