From f836ab8b20496ce22394e367bea622edde82f703 Mon Sep 17 00:00:00 2001 From: duck Date: Fri, 19 Sep 2025 11:09:45 +0500 Subject: [PATCH] Gltf parsing and rendering --- .ignore | 5 + data/cubemap.bin | Bin 0 -> 840 bytes data/cubemap.gltf | 142 ++++++++++ data/hand.bin | Bin 0 -> 140 bytes data/hand.gltf | 144 ++++++++++ data/yakuza/card.bin | Bin 0 -> 4296 bytes data/yakuza/card.gltf | 144 ++++++++++ data/{yakuza.png => yakuza/game.png} | Bin data/yakuza/pad.bin | Bin 0 -> 45768 bytes data/yakuza/pad.gltf | 144 ++++++++++ data/yakuza/table.bin | Bin 0 -> 840 bytes data/yakuza/table.gltf | 144 ++++++++++ src/assets.zig | 18 +- src/assets/gltf.zig | 389 +++++++++++++++++++++++++++ src/graphics.zig | 148 ++-------- src/world.zig | 142 ++-------- 16 files changed, 1167 insertions(+), 253 deletions(-) create mode 100644 .ignore create mode 100644 data/cubemap.bin create mode 100644 data/cubemap.gltf create mode 100644 data/hand.bin create mode 100644 data/hand.gltf create mode 100644 data/yakuza/card.bin create mode 100644 data/yakuza/card.gltf rename data/{yakuza.png => yakuza/game.png} (100%) create mode 100644 data/yakuza/pad.bin create mode 100644 data/yakuza/pad.gltf create mode 100644 data/yakuza/table.bin create mode 100644 data/yakuza/table.gltf create mode 100644 src/assets/gltf.zig diff --git a/.ignore b/.ignore new file mode 100644 index 0000000..f6ebcfc --- /dev/null +++ b/.ignore @@ -0,0 +1,5 @@ +/data/**.png +/data/**.bin +/data/**.blend +/data/**.blend1 +/.git diff --git a/data/cubemap.bin b/data/cubemap.bin new file mode 100644 index 0000000000000000000000000000000000000000..b44dd6f8a705c4ed71623586c46360860b992912 GIT binary patch literal 840 zcmezW|NlM)28R708iaANp>p;h8iW}b7_hPJko03^L-pAqnSsv6s_*~*|0v>k^n?5Z zavR88m>!&LB=>;SBI^UQ8Q`G79>PSYA)*knVLy~cQwtG=kZ}EIY9X=^5^fGYHE4Fg z^gv92kT5ZHJ0Y?V5?v0a2OA`_+k?ym>4Mk`R>J@h1F=EoL);5e1B!bl z1`Y;p1||k>237`k24My<26hH91}+8>20jKs1`!5925|;n21W)J23`gh1_1^(1_lNm X1~vvB1|bGf27U%+22loP22KV5T)_6h literal 0 HcmV?d00001 diff --git a/data/cubemap.gltf b/data/cubemap.gltf new file mode 100644 index 0000000..99b448a --- /dev/null +++ b/data/cubemap.gltf @@ -0,0 +1,142 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"cubemap" + } + ], + "materials":[ + { + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material_0", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Cube.001", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"cubemap.png", + "uri":"cubemap.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 0.4999999701976776, + 0.5, + 0.5 + ], + "min":[ + -0.4999999701976776, + -0.5, + -0.5 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":576, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"cubemap.bin" + } + ] +} diff --git a/data/hand.bin b/data/hand.bin new file mode 100644 index 0000000000000000000000000000000000000000..230f2b923011f86b942c6dd58d4e88e94b33749d GIT binary patch literal 140 zcmZQzVAv1E3=9km_DF1yIEW7uhp|C?1_p)(Bt9WFNFIiv+Tr>c8JHOu7?>HD7y$Xi B3Hks4 literal 0 HcmV?d00001 diff --git a/data/hand.gltf b/data/hand.gltf new file mode 100644 index 0000000..e09a730 --- /dev/null +++ b/data/hand.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"hand" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane.001", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"hand.png", + "uri":"hand.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":4, + "max":[ + 0.5, + 0.5, + 0 + ], + "min":[ + -0.5, + -0.5, + 0 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":4, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":4, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":6, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":48, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":48, + "byteOffset":48, + "target":34962 + }, + { + "buffer":0, + "byteLength":32, + "byteOffset":96, + "target":34962 + }, + { + "buffer":0, + "byteLength":12, + "byteOffset":128, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":140, + "uri":"hand.bin" + } + ] +} diff --git a/data/yakuza/card.bin b/data/yakuza/card.bin new file mode 100644 index 0000000000000000000000000000000000000000..2967a5f156cddc69054f87e3b8cf46a64f5b55ec GIT binary patch literal 4296 zcmZQzXxPWVkYUHb&|pn~y$?wZ7Ip?m41}#27;v&d_Q2HOU^m!7^@H4lj}6ifQ-g&K z_7hYbCmZAzm>L}HihT?WAPfp05DmiE*kC_fL)7AAL+t{Y0d^BiJ(73@0XE2NkbaOG z@Uo%ifYo4Q+dAm5j)e_QFHmuuY>-=EYH+YY zaRtH+yCLz9iw*WOR1Hox)Gm-2U^l_kBZ-6K2r7<^4Kf>~ALJgqY^a%FHQ3mo^a=9w zZb*3sHxpePrWYiRg$;5eOfNn*$Q>XzgVf+;L(K=P!NCT}!OX$NW`KeQdk6zg?}zY_ zNp!geYN>(Rjn^)GYM^p3_hB`M6gAZH7d~@f?x9o-R6pLhAjU43IZ(A^haoZM5Tgd_ z4zlAE%?_;L15<-Wqxc<7jwo}WZpUj5J~dD|tbT{NhZHqXJ$T(isT!Dj@R>u58mNA} z;Y^A-P_53-MtepFd9WLq81C6II5615=>Pjc_A)U1`+pED4xlQ2o(8KwAonyd*u!WB2YGt|h8kP2{~Ms;(7+G%SA!MUKMhcSfz1c`7o`5a z9RpZBls*6wFR^7{fQB2428)B-4-yBtA4-G8LGB0X2e}_YGcg>n`@bJ#A1eLt|9(40 z2Y!2WnxO#{E&}$L^gTOpT4_L|86Ma%IDq_#g+2gs#{)YCB$}z=o*e@ND4n6w|LQ^Z zGT5Wj3=IeD7(nR>8x67-B#usl>;s9T(+&*wVDnJugHZEP=|3R%I560w(+nX0gWUs7 z=TJV_U!e2~@*ffnPTyel_o4P6(O`GqhlC#}f57-)cZ1Y{+>1nm-3?L?6?b5;*JTi5 z5M@wjP-Ku}&;_&17%Uld8F(1@8O#{87z`MU8H^ct7|a;V7<3ub8RQrk7?>H%z$`5W zV+Jz@HU>@xEe2@@2?iAg6$Um2Ee0(HGX^;ZIR<71X$ENq6$UK^IRPuwjT|h+;?vt4Ux8X9#DoXNY1*V+d!6Vh~{PVJK!0U?^rVXJBD) zV2EH~VTfRmVNhm>XNX}?W{6=>V322sX0TCVsK$lV{l>MU|?dfV{l|(VsK<&Wzb+qVTfbUV2EQ-V$f&EW(Z)=X9!>r zVPI!)W^iL*XK-WSV$fztXUJvHX2@kQWZ-6SXUJgSX2@XBVc=u%V(?|)WAJ4VWUyi= zW5{B#V#s39V=!XKV<==WVkl%VWe{fYX9!{tW(Z;sV=!STVDMxxVen+&Ww2lW{_owWC&xBWe8)CVgLZx*AET= literal 0 HcmV?d00001 diff --git a/data/yakuza/card.gltf b/data/yakuza/card.gltf new file mode 100644 index 0000000..c83e0b1 --- /dev/null +++ b/data/yakuza/card.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"card" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane.002", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"game.png", + "uri":"game.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":120, + "max":[ + 0.25, + 0.25, + 0.00390625 + ], + "min":[ + -0.25, + -0.25, + -0.00390625 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":120, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":120, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":228, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":1440, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":1440, + "byteOffset":1440, + "target":34962 + }, + { + "buffer":0, + "byteLength":960, + "byteOffset":2880, + "target":34962 + }, + { + "buffer":0, + "byteLength":456, + "byteOffset":3840, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":4296, + "uri":"card.bin" + } + ] +} diff --git a/data/yakuza.png b/data/yakuza/game.png similarity index 100% rename from data/yakuza.png rename to data/yakuza/game.png diff --git a/data/yakuza/pad.bin b/data/yakuza/pad.bin new file mode 100644 index 0000000000000000000000000000000000000000..fab5724f5d1ee8991b24377bf530cc97fea5f606 GIT binary patch literal 45768 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 literal 0 HcmV?d00001 diff --git a/data/yakuza/pad.gltf b/data/yakuza/pad.gltf new file mode 100644 index 0000000..ee42156 --- /dev/null +++ b/data/yakuza/pad.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"pad" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane.003", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"game.png", + "uri":"game.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":1272, + "max":[ + 0.4140625, + 0.4140625, + 0.00390625 + ], + "min":[ + -0.4140625, + -0.4140625, + -0.00390625 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":1272, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":1272, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":2532, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":15264, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":15264, + "byteOffset":15264, + "target":34962 + }, + { + "buffer":0, + "byteLength":10176, + "byteOffset":30528, + "target":34962 + }, + { + "buffer":0, + "byteLength":5064, + "byteOffset":40704, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":45768, + "uri":"pad.bin" + } + ] +} diff --git a/data/yakuza/table.bin b/data/yakuza/table.bin new file mode 100644 index 0000000000000000000000000000000000000000..e5600254bda1d25d43d205b586d60d54d30550ed GIT binary patch literal 840 zcmZQzXgC1D3=9w&#K&MeK;*#UxY!_b96%}{Y8$YzL3&~0xY!_b4lpn0|$c$10Mqi10Mqu0~doBgD?XZgD?X-10w@3 Ng8%~~g8%~y0|3-fT@wHR literal 0 HcmV?d00001 diff --git a/data/yakuza/table.gltf b/data/yakuza/table.gltf new file mode 100644 index 0000000..b9a135a --- /dev/null +++ b/data/yakuza/table.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"table" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"game.png", + "uri":"game.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 4, + 4, + 0 + ], + "min":[ + -4, + -4, + -0.5 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":576, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"table.bin" + } + ] +} diff --git a/src/assets.zig b/src/assets.zig index ba8e505..99c4471 100644 --- a/src/assets.zig +++ b/src/assets.zig @@ -1,11 +1,17 @@ const std = @import("std"); +const builtin = @import("builtin"); const err = @import("error.zig"); const Game = @import("game.zig"); const FileLoader = @import("assets/file.zig"); const TextureLoader = @import("assets/texture.zig"); +const GltfLoader = @import("assets/gltf.zig"); const Assets = @This(); +// TODO: Unload assets in correct order to account for asset dependencies + +pub const File = AssetContainer(FileLoader); pub const Texture = AssetContainer(TextureLoader); +pub const Object = AssetContainer(GltfLoader); const WORKERS_MAX = 4; var next_worker_update: usize = 0; @@ -50,16 +56,18 @@ pub const LoadError = error{ ParsingError, SdlError, FileTooBig, -} || std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError; +} || std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.json.ParseError(std.json.Scanner); pub const AssetType = enum { file, texture, + gltf, pub fn getType(comptime self: @This()) type { return switch (self) { .file => FileLoader, .texture => TextureLoader, + .gltf => GltfLoader, }; } }; @@ -82,6 +90,8 @@ pub const AssetCell = struct { fn load(self: *AssetCell, alloc: std.mem.Allocator) void { self.loader(self, alloc) catch |e| { + if (builtin.mode == .Debug) + std.debug.panic("Asset loading error: {s} - {}!\n", .{ self.path, e }); self.state = .{ .fail = e }; return; }; @@ -119,6 +129,8 @@ pub fn AssetContainer(comptime T: type) type { }, } } + // TODO: Add smth like `Assets.immediateLoad` + /// To be used by worker threads to request other assets pub fn getSync(self: *@This()) !*T { sw: switch (self.last_state) { @@ -168,6 +180,7 @@ pub fn deinit() void { std.debug.assert(asset.*.counter == 0); if (asset.*.state == .loaded) asset.*.unload(Game.alloc); + Game.alloc.free(asset.*.path); Game.alloc.destroy(asset.*); } Assets.asset_map.clearAndFree(Game.alloc); @@ -202,6 +215,7 @@ pub fn update() void { if (!Assets.asset_map.remove(.{ .type = request.type, .path = request.path })) continue; if (request.state == .loaded) request.unload(Game.alloc); + Game.alloc.free(request.path); Game.alloc.destroy(request); } } @@ -259,7 +273,7 @@ fn mapAsset(comptime asset_type: AssetType, path: []const u8) *AssetCell { .mutex = .{}, .type = asset_type, .data = undefined, - .path = path, + .path = Game.alloc.dupe(u8, path) catch err.oom(), .loader = Assets.makeLoader(asset_type.getType(), asset_type.getType().load), .unloader = Assets.makeUnloader(asset_type.getType(), asset_type.getType().unload), .state = .not_loaded, diff --git a/src/assets/gltf.zig b/src/assets/gltf.zig new file mode 100644 index 0000000..0a5e96d --- /dev/null +++ b/src/assets/gltf.zig @@ -0,0 +1,389 @@ +const std = @import("std"); +const sdl = @import("sdl"); +const err = @import("../error.zig"); +const Assets = @import("../assets.zig"); +const Graphics = @import("../graphics.zig"); + +nodes: []Node, +meshes: []Mesh, + +const Node = struct { + mesh: u32, +}; + +const Mesh = struct { + primitives: []Primitive, +}; + +const Primitive = struct { + vertex_buffer: *sdl.GPUBuffer, + index_buffer: ?*sdl.GPUBuffer = null, + + vertices: u32, + indices: u32 = 0, + texture: Assets.Texture, +}; + +const GltfJson = struct { + scene: u32, + scenes: []GltfSceneJson, + nodes: []GltfNodeJson, + materials: []GltfMaterialJson, + meshes: []GltfMeshJson, + textures: []GltfTextureJson, + images: []GltfImageJson, + accessors: []GltfAccessorJson, + bufferViews: []GltfBufferViewJson, + samplers: []GltfSamplerJson, + buffers: []GltfBufferJson, + + const GltfSceneJson = struct { + nodes: []u32, + }; + const GltfNodeJson = struct { + mesh: u32, + }; + const GltfMaterialJson = struct { + pbrMetallicRoughness: GltfPbrMRJson, + + const GltfPbrMRJson = struct { + baseColorTexture: GltfPbrMRBaseColorTexture, + + const GltfPbrMRBaseColorTexture = struct { + index: u32, + }; + }; + }; + const GltfMeshJson = struct { + primitives: []GltfPrimitiveJson, + + const GltfPrimitiveJson = struct { + attributes: GltfAttributesJson, + indices: u32, + material: u32, + + const GltfAttributesJson = struct { + POSITION: u32, + TEXCOORD_0: u32, + }; + }; + }; + const GltfTextureJson = struct { + sampler: ?u32, + source: u32, + }; + const GltfImageJson = struct { + uri: []u8, + }; + const GltfAccessorJson = struct { + bufferView: u32, + componentType: GltfComponentTypeJson, + count: u32, + type: GltfAccessorTypeJson, + + const GltfComponentTypeJson = enum(u32) { + i8 = 5120, + u8 = 5121, + i16 = 5122, + u16 = 5123, + i32 = 5125, + f32 = 5126, + }; + const GltfAccessorTypeJson = enum { + SCALAR, + VEC2, + VEC3, + VEC4, + MAT2, + MAT3, + MAT4, + }; + + pub fn slice(accessor: @This(), comptime T: type, views: []const GltfBufferViewJson, buffers: []Assets.File) !?[]align(1) T { + if (accessor.bufferView >= views.len) return null; + const view = &views[accessor.bufferView]; + + if (view.buffer >= buffers.len) return null; + const buffer = try buffers[view.buffer].getSync(); + + const component_length = switch (T) { + [3]f32 => 12, + [2]f32 => 8, + u16 => 2, + else => @compileError("Accessor of " ++ @tagName(accessor.type) ++ " of " ++ @tagName(accessor.componentType) ++ " is not supported."), + }; + + const start = view.byteOffset; + const length = component_length * accessor.count; + const end = start + length; + + if (length != view.byteLength) return error.ParsingError; + + return @as([]align(1) T, @ptrCast(buffer.bytes[start..end])); + } + }; + const GltfBufferViewJson = struct { + buffer: u32, + byteLength: u32, + byteOffset: u32 = 0, + }; + const GltfSamplerJson = struct { + magFilter: GltfFilterJson = .NEAREST, + minFilter: GltfFilterJson = .LINEAR, + wrapS: GltfWrapJson = .CLAMP_TO_EDGE, + wrapT: GltfWrapJson = .CLAMP_TO_EDGE, + + const GltfFilterJson = enum(u32) { + NEAREST = 9728, + LINEAR = 9729, + NEAREST_MIPMAP_NEAREST = 9984, + LINEAR_MIPMAP_NEAREST = 9985, + NEAREST_MIPMAP_LINEAR = 9986, + LINEAR_MIPMAP_LINEAR = 9987, + }; + const GltfWrapJson = enum(u32) { + CLAMP_TO_EDGE = 33071, + MIRRORED_REPEAT = 33648, + REPEAT = 10497, + }; + }; + const GltfBufferJson = struct { + // byteLength: u32, + uri: []u8, + }; +}; + +pub fn load(path: []const u8, alloc: std.mem.Allocator) Assets.LoadError!@This() { + var json = Assets.load(.file, path); + defer Assets.free(json); + + const parsed_gltf = try std.json.parseFromSlice( + GltfJson, + alloc, + (try json.getSync()).bytes, + .{ .ignore_unknown_fields = true }, + ); + defer parsed_gltf.deinit(); + const gltf = &parsed_gltf.value; + + if (gltf.scene >= gltf.scenes.len) return error.ParsingError; + const scene = &gltf.scenes[gltf.scene]; + + var buffers_init: u32 = 0; + const buffers = try alloc.alloc(Assets.File, gltf.buffers.len); + defer alloc.free(buffers); + + defer for (buffers[0..buffers_init]) |buffer| { + Assets.free(buffer); + }; + + for (0.., buffers) |i, *buffer| { + const buffer_path = try std.fs.path.join(alloc, &.{ std.fs.path.dirname(path) orelse return error.ParsingError, gltf.buffers[i].uri }); + defer alloc.free(buffer_path); + buffer.* = Assets.load(.file, buffer_path); + buffers_init += 1; + } + + const nodes = try alloc.alloc(Node, scene.nodes.len); + errdefer alloc.free(nodes); + + var meshes_init: u32 = 0; + const meshes = try alloc.alloc(Mesh, gltf.meshes.len); + errdefer alloc.free(nodes); + + errdefer for (meshes[0..meshes_init]) |*mesh| { + for (mesh.primitives) |*primitive| { + sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + if (primitive.index_buffer) |buf| sdl.ReleaseGPUBuffer(Graphics.device, buf); + Assets.free(primitive.texture); + } + }; + + for (0.., nodes) |i, *node| { + const node_index = scene.nodes[i]; + if (node_index >= gltf.nodes.len) return error.ParsingError; + const gltf_node = &gltf.nodes[node_index]; + if (gltf_node.mesh >= gltf.meshes.len) return error.ParsingError; + node.mesh = gltf_node.mesh; + } + for (0.., meshes) |i, *mesh| { + const gltf_mesh = &gltf.meshes[i]; + + var primitivs_init: u32 = 0; + const primitives = try alloc.alloc(Primitive, gltf_mesh.primitives.len); + errdefer alloc.free(primitives); + + errdefer for (primitives[0..primitivs_init]) |*primitive| { + sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + if (primitive.index_buffer) |buf| sdl.ReleaseGPUBuffer(Graphics.device, buf); + Assets.free(primitive.texture); + }; + + for (0.., primitives) |j, *primitive| { + const gltf_primitive = gltf_mesh.primitives[j]; + + if (gltf_primitive.attributes.POSITION >= gltf.accessors.len) return error.ParsingError; + if (gltf_primitive.attributes.TEXCOORD_0 >= gltf.accessors.len) return error.ParsingError; + if (gltf_primitive.material >= gltf.materials.len) return error.ParsingError; + const material = &gltf.materials[gltf_primitive.material]; + const texture_index = material.pbrMetallicRoughness.baseColorTexture.index; + if (texture_index >= gltf.textures.len) return error.ParsingError; + const texture = &gltf.textures[texture_index]; + if (texture.source >= gltf.images.len) return error.ParsingError; + const image = &gltf.images[texture.source]; + + const position = try gltf.accessors[gltf_primitive.attributes.POSITION].slice([3]f32, gltf.bufferViews, buffers) orelse return error.ParsingError; + const uv = try gltf.accessors[gltf_primitive.attributes.TEXCOORD_0].slice([2]f32, gltf.bufferViews, buffers) orelse return error.ParsingError; + const index = try gltf.accessors[gltf_primitive.indices].slice(u16, gltf.bufferViews, buffers) orelse return error.ParsingError; + + primitive.vertex_buffer, primitive.index_buffer = try loadMesh(position, uv, index, alloc); + errdefer sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + errdefer sdl.ReleaseGPUBuffer(Graphics.device, primitive.index_buffer); + + primitive.vertices = @intCast(position.len); + primitive.indices = @intCast(index.len); + + const texture_path = try std.fs.path.join(alloc, &.{ std.fs.path.dirname(path) orelse return error.ParsingError, image.uri }); + defer alloc.free(texture_path); + primitive.texture = Assets.load(.texture, texture_path); + + primitivs_init += 1; + } + + mesh.primitives = primitives; + meshes_init += 1; + } + + return .{ + .nodes = nodes, + .meshes = meshes, + }; +} + +pub fn unload(self: @This(), alloc: std.mem.Allocator) void { + for (self.meshes) |mesh| { + for (mesh.primitives) |*primitive| { + sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + sdl.ReleaseGPUBuffer(Graphics.device, primitive.index_buffer); + Assets.free(primitive.texture); + } + alloc.free(mesh.primitives); + } + alloc.free(self.meshes); + alloc.free(self.nodes); +} + +pub fn loadMesh(position: []align(1) const [3]f32, uv: []align(1) const [2]f32, index: []align(1) const u16, alloc: std.mem.Allocator) !struct { *sdl.GPUBuffer, *sdl.GPUBuffer } { + if (position.len != uv.len) return error.ParsingError; + const vertices: u32 = @intCast(position.len); + const indices: u32 = @intCast(index.len); + + const BYTES_PER_VERTEX = 20; + const BYTES_PER_INDEX = 2; + + const vertex_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ + .size = vertices * BYTES_PER_VERTEX, + .usage = sdl.GPU_BUFFERUSAGE_VERTEX, + }) orelse return error.SdlError; + errdefer sdl.ReleaseGPUBuffer(Graphics.device, vertex_buffer); + + const index_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ + .size = indices * 2, + .usage = sdl.GPU_BUFFERUSAGE_INDEX, + }) orelse return error.SdlError; + errdefer sdl.ReleaseGPUBuffer(Graphics.device, index_buffer); + + const TRANSFER_CAPACITY = Graphics.TRANSFER_BUFFER_DEFAULT_CAPACITY; + + const transfer_buffer = sdl.CreateGPUTransferBuffer(Graphics.device, &.{ + .size = TRANSFER_CAPACITY, + .usage = sdl.GPU_TRANSFERBUFFERUSAGE_UPLOAD, + }) orelse return error.SdlError; + defer sdl.ReleaseGPUTransferBuffer(Graphics.device, transfer_buffer); + + const buffer = try alloc.alloc(u8, TRANSFER_CAPACITY); + defer alloc.free(buffer); + + var vertices_uploaded: u32 = 0; + while (vertices_uploaded < vertices) { + const vertices_to_upload = @min(vertices - vertices_uploaded, TRANSFER_CAPACITY / BYTES_PER_VERTEX); + if (vertices_to_upload == 0) return error.FileTooBig; + + for (0..vertices_to_upload) |i| { + const V = packed struct { x: f32, y: f32, z: f32, u: f32, v: f32 }; + std.mem.copyForwards( + u8, + buffer[BYTES_PER_VERTEX * i ..], + &@as([BYTES_PER_VERTEX]u8, @bitCast(V{ + .x = position[vertices_uploaded + i][0], + .y = position[vertices_uploaded + i][1], + .z = position[vertices_uploaded + i][2], + .u = uv[vertices_uploaded + i][0], + .v = uv[vertices_uploaded + i][1], + })), + ); + } + + const command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse return error.SdlError; + { + errdefer _ = sdl.CancelGPUCommandBuffer(command_buffer); + const copy_pass = sdl.BeginGPUCopyPass(command_buffer) orelse return error.SdlError; + defer sdl.EndGPUCopyPass(copy_pass); + + const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, transfer_buffer, false) orelse err.sdl()); + @memcpy(map, buffer[0 .. vertices_to_upload * BYTES_PER_VERTEX]); + sdl.UnmapGPUTransferBuffer(Graphics.device, transfer_buffer); + + sdl.UploadToGPUBuffer(copy_pass, &.{ + .transfer_buffer = transfer_buffer, + }, &.{ + .buffer = vertex_buffer, + .offset = vertices_uploaded * BYTES_PER_VERTEX, + .size = vertices_to_upload * BYTES_PER_VERTEX, + }, false); + } + vertices_uploaded += vertices_to_upload; + const fence = sdl.SubmitGPUCommandBufferAndAcquireFence(command_buffer) orelse return error.SdlError; + defer sdl.ReleaseGPUFence(Graphics.device, fence); + if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) return error.SdlError; + } + + var indices_uploaded: u32 = 0; + while (indices_uploaded < indices) { + const indices_to_upload = @min(indices - indices_uploaded, TRANSFER_CAPACITY / BYTES_PER_INDEX); + if (indices_to_upload == 0) return error.FileTooBig; + + for (0..indices_to_upload) |i| { + std.mem.copyForwards( + u8, + buffer[BYTES_PER_INDEX * i ..], + &@as([BYTES_PER_INDEX]u8, @bitCast(index[i])), + ); + } + + const command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse return error.SdlError; + { + errdefer _ = sdl.CancelGPUCommandBuffer(command_buffer); + const copy_pass = sdl.BeginGPUCopyPass(command_buffer) orelse return error.SdlError; + defer sdl.EndGPUCopyPass(copy_pass); + + const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, transfer_buffer, false) orelse err.sdl()); + @memcpy(map, buffer[0 .. indices_to_upload * BYTES_PER_INDEX]); + sdl.UnmapGPUTransferBuffer(Graphics.device, transfer_buffer); + + sdl.UploadToGPUBuffer(copy_pass, &.{ + .transfer_buffer = transfer_buffer, + }, &.{ + .buffer = index_buffer, + .offset = indices_uploaded * BYTES_PER_INDEX, + .size = indices_to_upload * BYTES_PER_INDEX, + }, false); + } + indices_uploaded += indices_to_upload; + const fence = sdl.SubmitGPUCommandBufferAndAcquireFence(command_buffer) orelse return error.SdlError; + defer sdl.ReleaseGPUFence(Graphics.device, fence); + if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) return error.SdlError; + } + + return .{ vertex_buffer, index_buffer }; +} diff --git a/src/graphics.zig b/src/graphics.zig index b377556..32966de 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -7,11 +7,6 @@ const Assets = @import("assets.zig"); pub const Transform = @import("graphics/transform.zig"); pub const Camera = @import("graphics/camera.zig"); -pub const Mesh = struct { - vertex_start: usize, - vertex_count: usize, -}; - pub var window: *sdl.Window = undefined; pub var device: *sdl.GPUDevice = undefined; /// Only available while drawing @@ -22,13 +17,6 @@ var render_target: ?*sdl.GPUTexture = null; var shader_vert: *sdl.GPUShader = undefined; var shader_frag: *sdl.GPUShader = undefined; -var vertex_buffer: *sdl.GPUBuffer = undefined; -var vertex_buffer_capacity: usize = undefined; -var vertex_buffer_used: usize = undefined; - -var transfer_buffer: *sdl.GPUTransferBuffer = undefined; -var transfer_buffer_capacity: usize = undefined; - var depth_texture: *sdl.GPUTexture = undefined; var fsaa_target: *sdl.GPUTexture = undefined; var pipeline: *sdl.GPUGraphicsPipeline = undefined; @@ -40,11 +28,9 @@ var fsaa_level: u32 = 3; pub var camera: Camera = undefined; -const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024; -const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2; const BYTES_PER_VERTEX = 5 * 4; const DEPTH_FORMAT = sdl.GPU_TEXTUREFORMAT_D32_FLOAT; -pub const TRANSFER_BUFFER_DEFAULT_CAPACITY = 256 * 1024; +pub const TRANSFER_BUFFER_DEFAULT_CAPACITY = 512 * 1024; pub const MIP_LEVEL = 4; const Graphics = @This(); @@ -92,19 +78,6 @@ pub fn create() void { }, ); - Graphics.vertex_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ - .usage = sdl.GPU_BUFFERUSAGE_VERTEX, - .size = VERTEX_BUFFER_DEFAULT_CAPACITY, - }) orelse err.sdl(); - Graphics.vertex_buffer_capacity = VERTEX_BUFFER_DEFAULT_CAPACITY; - Graphics.vertex_buffer_used = 0; - - Graphics.transfer_buffer = sdl.CreateGPUTransferBuffer(Graphics.device, &.{ - .size = TRANSFER_BUFFER_DEFAULT_CAPACITY, - .usage = sdl.GPU_TRANSFERBUFFERUSAGE_UPLOAD | sdl.GPU_TRANSFERBUFFERUSAGE_DOWNLOAD, - }) orelse err.sdl(); - Graphics.transfer_buffer_capacity = TRANSFER_BUFFER_DEFAULT_CAPACITY; - const target_format = sdl.GetGPUSwapchainTextureFormat(Graphics.device, Graphics.window); if (target_format == sdl.GPU_TEXTUREFORMAT_INVALID) err.sdl(); @@ -183,8 +156,6 @@ pub fn destroy() void { sdl.ReleaseGPUGraphicsPipeline(Graphics.device, Graphics.pipeline); sdl.ReleaseGPUTexture(Graphics.device, Graphics.fsaa_target); sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture); - sdl.ReleaseGPUBuffer(Graphics.device, Graphics.vertex_buffer); - sdl.ReleaseGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer); sdl.ReleaseGPUShader(Graphics.device, Graphics.shader_vert); sdl.ReleaseGPUShader(Graphics.device, Graphics.shader_frag); @@ -196,98 +167,6 @@ pub fn destroy() void { sdl.DestroyGPUDevice(Graphics.device); } -pub fn loadMesh(mesh_bytes: []const u8) Mesh { - std.debug.assert(mesh_bytes.len < Graphics.transfer_buffer_capacity); - - var size_mult: usize = 1; - while (Graphics.vertex_buffer_used + mesh_bytes.len > Graphics.vertex_buffer_capacity * size_mult) { - size_mult *= VERTEX_BUFFER_GROWTH_MULTIPLIER; - } - if (size_mult > 1) { - Graphics.growVertexBuffer(Graphics.vertex_buffer_capacity * size_mult); - } - - const map = sdl.MapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer, true) orelse err.sdl(); - @memcpy(@as([*]u8, @ptrCast(map)), mesh_bytes); - sdl.UnmapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer); - - const temp_command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse err.sdl(); - const fence = blk: { - const copy_pass = sdl.BeginGPUCopyPass(temp_command_buffer) orelse err.sdl(); - sdl.UploadToGPUBuffer(copy_pass, &.{ - .transfer_buffer = Graphics.transfer_buffer, - .offset = 0, - }, &.{ - .buffer = Graphics.vertex_buffer, - .offset = @intCast(Graphics.vertex_buffer_used), - .size = @intCast(mesh_bytes.len), - }, false); - sdl.EndGPUCopyPass(copy_pass); - - break :blk sdl.SubmitGPUCommandBufferAndAcquireFence(temp_command_buffer) orelse err.sdl(); - }; - defer sdl.ReleaseGPUFence(Graphics.device, fence); - - if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) err.sdl(); - - const vertex_start = Graphics.vertex_buffer_used; - Graphics.vertex_buffer_used += mesh_bytes.len; - - return Mesh{ - .vertex_start = vertex_start / BYTES_PER_VERTEX, - .vertex_count = mesh_bytes.len / BYTES_PER_VERTEX, - }; -} - -pub fn unloadMesh(mesh: Mesh) void { - // TODO: free some memory - _ = &mesh; -} - -fn growVertexBuffer(new_size: usize) void { - const new_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ - .size = @intCast(new_size), - .usage = sdl.GPU_BUFFERUSAGE_VERTEX, - }) orelse err.sdl(); - - const temp_command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse err.sdl(); - - const fence = blk: { - const copy_pass = sdl.BeginGPUCopyPass(temp_command_buffer); - var copied: usize = 0; - while (copied < Graphics.vertex_buffer_used) { - const to_transer = @min(Graphics.vertex_buffer_used - copied, Graphics.transfer_buffer_capacity); - sdl.DownloadFromGPUBuffer(copy_pass, &.{ - .buffer = Graphics.vertex_buffer, - .offset = @intCast(copied), - .size = @intCast(to_transer), - }, &.{ - .transfer_buffer = Graphics.transfer_buffer, - .offset = 0, - }); - sdl.UploadToGPUBuffer(copy_pass, &.{ - .transfer_buffer = Graphics.transfer_buffer, - .offset = 0, - }, &.{ - .buffer = new_buffer, - .offset = @intCast(copied), - .size = @intCast(to_transer), - }, false); - copied += to_transer; - } - sdl.EndGPUCopyPass(copy_pass); - - break :blk sdl.SubmitGPUCommandBufferAndAcquireFence(temp_command_buffer) orelse err.sdl(); - }; - defer sdl.ReleaseGPUFence(Graphics.device, fence); - - if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) err.sdl(); - - sdl.ReleaseGPUBuffer(Graphics.device, Graphics.vertex_buffer); - Graphics.vertex_buffer = new_buffer; - Graphics.vertex_buffer_capacity = new_size; -} - /// If window is minimized returns `false`, `render_pass` remains null /// Otherwise `command_buffer` and `render_pass` are both set pub fn beginDraw() bool { @@ -323,7 +202,6 @@ pub fn beginDraw() bool { }) orelse err.sdl(); sdl.BindGPUGraphicsPipeline(Graphics.render_pass, Graphics.pipeline); - sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = Graphics.vertex_buffer }, 1); Graphics.camera.computeMatrix(); sdl.PushGPUVertexUniformData(Graphics.command_buffer, 0, &Graphics.camera.matrix, 16 * 4); @@ -350,20 +228,28 @@ pub fn clearDepth() void { }) orelse err.sdl(); sdl.BindGPUGraphicsPipeline(Graphics.render_pass, Graphics.pipeline); - sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = Graphics.vertex_buffer }, 1); sdl.PushGPUVertexUniformData(Graphics.command_buffer, 0, &Graphics.camera.matrix, 16 * 4); } -pub fn drawMesh(mesh: Mesh, texture: *Assets.Texture, transform: Transform) void { +pub fn drawObject(object: *Assets.Object, transform: Transform) void { if (Graphics.render_pass == null) return; - const asset_texture = texture.get() orelse return; + const asset_object = object.get() orelse return; sdl.PushGPUVertexUniformData(Graphics.command_buffer, 1, &transform.matrix(), 16 * 4); - sdl.BindGPUFragmentSamplers(Graphics.render_pass, 0, &sdl.GPUTextureSamplerBinding{ - .texture = asset_texture.texture, - .sampler = asset_texture.sampler, - }, 1); - sdl.DrawGPUPrimitives(Graphics.render_pass, @intCast(mesh.vertex_count), 1, @intCast(mesh.vertex_start), 0); + for (asset_object.nodes) |node| { + const mesh = &asset_object.meshes[node.mesh]; + + for (mesh.primitives) |*primitive| { + const asset_texture = primitive.texture.get() orelse continue; + sdl.BindGPUFragmentSamplers(Graphics.render_pass, 0, &sdl.GPUTextureSamplerBinding{ + .texture = asset_texture.texture, + .sampler = asset_texture.sampler, + }, 1); + sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = primitive.vertex_buffer }, 1); + sdl.BindGPUIndexBuffer(Graphics.render_pass, &.{ .buffer = primitive.index_buffer }, sdl.GPU_INDEXELEMENTSIZE_16BIT); + sdl.DrawGPUIndexedPrimitives(Graphics.render_pass, primitive.indices, 1, 0, 0, 0); + } + } } pub fn endDraw() void { diff --git a/src/world.zig b/src/world.zig index fc564c3..950de95 100644 --- a/src/world.zig +++ b/src/world.zig @@ -12,13 +12,9 @@ const Order = i32; pub var object_map: std.AutoHashMapUnmanaged(Id, usize) = .{}; pub var objects: std.ArrayListUnmanaged(Object) = .{}; -pub var hand_mesh: Graphics.Mesh = undefined; -pub var cube_mesh: Graphics.Mesh = undefined; -pub var table_mesh: Graphics.Mesh = undefined; -pub var cubemap_mesh: Graphics.Mesh = undefined; -pub var table_texture: Assets.Texture = undefined; -pub var hand_texture: Assets.Texture = undefined; -pub var cubemap_texture: Assets.Texture = undefined; +pub var hand: Assets.Object = undefined; +pub var table: Assets.Object = undefined; +pub var cubemap: Assets.Object = undefined; pub var camera_position: @Vector(2, f32) = @splat(0); pub var hand_transform: Graphics.Transform = .{}; @@ -46,8 +42,7 @@ const Object = struct { target_transform: Graphics.Transform = .{}, width: f32, height: f32, - mesh: Graphics.Mesh, - texture: Assets.Texture, + object: Assets.Object, order: Order, id: Id, index: u32, @@ -99,15 +94,7 @@ pub fn initDebug() void { .type = .card, .width = 0.5, .height = 0.5, - .mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane( - @as(f32, @floatFromInt(9 + i / 10)) / 16.0, - @as(f32, @floatFromInt(0 + i % 10)) / 16.0, - @as(f32, @floatFromInt(10 + i / 10)) / 16.0, - @as(f32, @floatFromInt(1 + i % 10)) / 16.0, - 0.5, - 0.5, - ))), - .texture = Assets.load(.texture, "data/yakuza.png"), + .object = Assets.load(.gltf, "data/yakuza/card.gltf"), .order = @intCast(i), .id = @intCast(i), .index = @intCast(i), @@ -120,15 +107,7 @@ pub fn initDebug() void { .type = .deck, .width = 1, .height = 1, - .mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane( - 0.0 / 8.0, - 4.0 / 8.0, - 1.0 / 8.0, - 5.0 / 8.0, - 1, - 1, - ))), - .texture = Assets.load(.texture, "data/yakuza.png"), + .object = Assets.load(.gltf, "data/yakuza/pad.gltf"), .order = 70, .id = 70, .index = 70, @@ -139,27 +118,16 @@ pub fn initDebug() void { .type = .deck, .width = 1, .height = 1, - .mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane( - 0.0 / 8.0, - 4.0 / 8.0, - 1.0 / 8.0, - 5.0 / 8.0, - 1, - 1, - ))), - .texture = Assets.load(.texture, "data/yakuza.png"), + .object = Assets.load(.gltf, "data/yakuza/pad.gltf"), .order = 71, .id = 71, .index = 71, }) catch err.oom(); World.object_map.put(Game.alloc, 71, 71) catch err.oom(); - World.table_mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane(0, 0, 0.5, 0.5, 8, 8))); - World.hand_mesh = Graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA)); - World.cubemap_mesh = Graphics.loadMesh(@ptrCast(&CUBEMAP_MESH_DATA)); - World.table_texture = Assets.load(.texture, "data/yakuza.png"); - World.hand_texture = Assets.load(.texture, "data/hand.png"); - World.cubemap_texture = Assets.load(.texture, "data/cubemap.png"); + World.hand = Assets.load(.gltf, "data/hand.gltf"); + World.table = Assets.load(.gltf, "data/yakuza/table.gltf"); + World.cubemap = Assets.load(.gltf, "data/cubemap.gltf"); World.camera_position = @splat(0); World.hand_transform = .{}; @@ -177,15 +145,11 @@ pub fn initDebug() void { } pub fn deinit() void { - Graphics.unloadMesh(World.table_mesh); - Graphics.unloadMesh(World.hand_mesh); - Graphics.unloadMesh(World.cubemap_mesh); - Assets.free(World.table_texture); - Assets.free(World.hand_texture); - Assets.free(World.cubemap_texture); + Assets.free(World.hand); + Assets.free(World.table); + Assets.free(World.cubemap); for (World.objects.items) |*object| { - Assets.free(object.texture); - Graphics.unloadMesh(object.mesh); + Assets.free(object.object); } World.objects.clearAndFree(Game.alloc); World.object_map.clearAndFree(Game.alloc); @@ -424,8 +388,9 @@ pub fn updateObject(object: *Object, delta: f32) void { .dock => { var topleft_x = -World.dock_last_width * 0.5 * DOCK_TILT_COS + World.dock_spacing * (@as(f32, @floatFromInt(object.parent_index)) - @as(f32, @floatFromInt(World.dock_objects - 1)) * 0.5); const total_w = @as(f32, @floatFromInt(World.dock_objects - 1)) * World.dock_spacing + World.dock_last_width * DOCK_TILT_COS; + const mouse_x = if (World.dock_focused) Game.mouse.x_norm else 0.5; if (total_w > Graphics.camera.aspect * 2) { - topleft_x += math.lerp(0, Graphics.camera.aspect - total_w * 0.5, Game.mouse.x_norm); + topleft_x += math.lerp(0, Graphics.camera.aspect - total_w * 0.5, mouse_x); } const hit = World.hover == object.id; const topleft_y = if (World.dock_focused) if (hit) @as(f32, 0.5) else @as(f32, 0.3) else @as(f32, 0.2); @@ -465,12 +430,12 @@ pub fn updateObject(object: *Object, delta: f32) void { } pub fn draw() void { - Graphics.drawMesh(World.table_mesh, &World.table_texture, .{}); + Graphics.drawObject(&World.table, .{}); for (World.objects.items) |*object| { sw: switch (object.parent) { .none, .hand => { - Graphics.drawMesh(object.mesh, &object.texture, object.drawingTransform()); + Graphics.drawObject(&object.object, object.drawingTransform()); }, .dock => {}, .deck => |id| { @@ -482,9 +447,8 @@ pub fn draw() void { } } - Graphics.drawMesh( - World.hand_mesh, - &World.hand_texture, + Graphics.drawObject( + &World.hand, Graphics.Transform.combineTransforms( .{ .position = .{ World.hand_scale * 0.5, -World.hand_scale * 0.5, 0 }, @@ -494,14 +458,14 @@ pub fn draw() void { ), ); - Graphics.drawMesh(World.cubemap_mesh, &World.cubemap_texture, .{ + Graphics.drawObject(&World.cubemap, .{ .scale = Graphics.camera.far, .position = Graphics.camera.transform.position, }); Graphics.clearDepth(); for (World.objects.items) |*object| { if (object.parent == .dock) - Graphics.drawMesh(object.mesh, &object.texture, object.drawingTransform()); + Graphics.drawObject(&object.object, object.drawingTransform()); } } @@ -580,65 +544,3 @@ fn objectOrderLessThan(ctx: void, lhs: Object, rhs: Object) bool { _ = ctx; return lhs.order < rhs.order; } - -const T1 = 1.0 / 3.0; -const T2 = 2.0 / 3.0; -const CUBEMAP_MESH_DATA = [_]f32{ - -0.5, 0.5, -0.5, T2, 0, - -0.5, -0.5, -0.5, T2, 0.5, - 0.5, 0.5, -0.5, 1, 0, - 0.5, -0.5, -0.5, 1, 0.5, - 0.5, 0.5, -0.5, 1, 0, - -0.5, -0.5, -0.5, T2, 0.5, - - 0.5, 0.5, -0.5, 0, 1, - 0.5, -0.5, -0.5, T1, 1, - 0.5, 0.5, 0.5, 0, 0.5, - 0.5, -0.5, 0.5, T1, 0.5, - 0.5, 0.5, 0.5, 0, 0.5, - 0.5, -0.5, -0.5, T1, 1, - - 0.5, 0.5, 0.5, 1.0, 0.0, - 0.5, -0.5, 0.5, 1.0, 1.0, - -0.5, 0.5, 0.5, 0.0, 0.0, - -0.5, -0.5, 0.5, 0.0, 1.0, - -0.5, 0.5, 0.5, 0.0, 0.0, - 0.5, -0.5, 0.5, 1.0, 1.0, - - -0.5, 0.5, 0.5, T1, 0, - -0.5, -0.5, 0.5, 0, 0, - -0.5, 0.5, -0.5, T1, 0.5, - -0.5, -0.5, -0.5, 0, 0.5, - -0.5, 0.5, -0.5, T1, 0.5, - -0.5, -0.5, 0.5, 0, 0, - - -0.5, 0.5, 0.5, T1, 0.5, - -0.5, 0.5, -0.5, T1, 1, - 0.5, 0.5, 0.5, T2, 0.5, - 0.5, 0.5, -0.5, T2, 1, - 0.5, 0.5, 0.5, T2, 0.5, - -0.5, 0.5, -0.5, T1, 1, - - -0.5, -0.5, -0.5, T2, 0.5, - -0.5, -0.5, 0.5, T2, 0, - 0.5, -0.5, -0.5, T1, 0.5, - 0.5, -0.5, 0.5, T1, 0, - 0.5, -0.5, -0.5, T1, 0.5, - -0.5, -0.5, 0.5, T2, 0, -}; -const PLANE_MESH_DATA = [_]f32{ - -0.5, -0.5, 0, 0.0, 1.0, - 0.5, 0.5, 0, 1.0, 0.0, - -0.5, 0.5, 0, 0.0, 0.0, - 0.5, 0.5, 0, 1.0, 0.0, - -0.5, -0.5, 0, 0.0, 1.0, - 0.5, -0.5, 0, 1.0, 1.0, -}; -const PLANE_MESH_DATA_HALF = [_]f32{ - -0.25, -0.25, 0, 0.0, 1.0, - 0.25, 0.25, 0, 1.0, 0.0, - -0.25, 0.25, 0, 0.0, 0.0, - 0.25, 0.25, 0, 1.0, 0.0, - -0.25, -0.25, 0, 0.0, 1.0, - 0.25, -0.25, 0, 1.0, 1.0, -};