From 7469ef307235ea36c0caa76b95ba2fd809ca3058 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Mon, 30 Sep 2024 18:47:40 +0900 Subject: [PATCH] sparse --- deps/sokol_samples/build.zig | 7 +- deps/sokol_samples/glb/main.zig | 1 - deps/sokol_samples/sample_framework/Scene.zig | 1 + .../sample_framework/gltf_fetcher.zig | 1 - .../{ => tutorials}/minimal/main.zig | 5 +- deps/sokol_samples/tutorials/sparse/main.zig | 221 ++++++++++++++++++ deps/zigltf/src/GltfBuffer.zig | 52 ++++- deps/zigltf/src/types/Accessor.zig | 24 +- deps/zigltf/src/types/AccessorSparse.zig | 7 + .../src/types/AccessorSparseIndices.zig | 7 + .../zigltf/src/types/AccessorSparseValues.zig | 4 + src/data.ts | 10 + 12 files changed, 328 insertions(+), 12 deletions(-) rename deps/sokol_samples/{ => tutorials}/minimal/main.zig (98%) create mode 100644 deps/sokol_samples/tutorials/sparse/main.zig create mode 100644 deps/zigltf/src/types/AccessorSparse.zig create mode 100644 deps/zigltf/src/types/AccessorSparseIndices.zig create mode 100644 deps/zigltf/src/types/AccessorSparseValues.zig diff --git a/deps/sokol_samples/build.zig b/deps/sokol_samples/build.zig index ee1c31c..3aa69ae 100644 --- a/deps/sokol_samples/build.zig +++ b/deps/sokol_samples/build.zig @@ -164,8 +164,13 @@ const Sample = struct { pub const samples = [_]Sample{ .{ .name = "minimal", - .root_source_file = "minimal/main.zig", + .root_source_file = "tutorials/minimal/main.zig", }, + .{ + .name = "sparse", + .root_source_file = "tutorials/sparse/main.zig", + }, + // .{ .name = "glb", .root_source_file = "glb/main.zig", diff --git a/deps/sokol_samples/glb/main.zig b/deps/sokol_samples/glb/main.zig index c6e4328..be20d04 100644 --- a/deps/sokol_samples/glb/main.zig +++ b/deps/sokol_samples/glb/main.zig @@ -46,7 +46,6 @@ export fn init() void { fn on_gltf(gltf: std.json.Parsed(zigltf.Gltf), bin:?[]const u8) void { state.gltf = gltf; - std.debug.print("{s}\n", .{gltf.value}); state.scene.load(gltf, bin) catch |e| { std.debug.print("{s}\n", .{@errorName(e)}); @panic("Scene.load"); diff --git a/deps/sokol_samples/sample_framework/Scene.zig b/deps/sokol_samples/sample_framework/Scene.zig index 612cbc3..c5f6046 100644 --- a/deps/sokol_samples/sample_framework/Scene.zig +++ b/deps/sokol_samples/sample_framework/Scene.zig @@ -57,6 +57,7 @@ pub fn load( json: std.json.Parsed(zigltf.Gltf), bin: ?[]const u8, ) !void { + std.debug.print("{s}\n", .{json.value}); self.gltf = json; const gltf = json.value; diff --git a/deps/sokol_samples/sample_framework/gltf_fetcher.zig b/deps/sokol_samples/sample_framework/gltf_fetcher.zig index 9ce3582..1362baa 100644 --- a/deps/sokol_samples/sample_framework/gltf_fetcher.zig +++ b/deps/sokol_samples/sample_framework/gltf_fetcher.zig @@ -99,7 +99,6 @@ export fn fetch_callback(response: [*c]const sokol.fetch.Response) void { state.status = "parsed"; gltf_task.callback(parsed, glb.bin); } else |e| { - // std.debug.print("{s}\n", .{glb.json_bytes}); state.status = std.fmt.bufPrintZ( &state.status_buffer, "fail to parse: {s}", diff --git a/deps/sokol_samples/minimal/main.zig b/deps/sokol_samples/tutorials/minimal/main.zig similarity index 98% rename from deps/sokol_samples/minimal/main.zig rename to deps/sokol_samples/tutorials/minimal/main.zig index 6e883b2..9a99113 100644 --- a/deps/sokol_samples/minimal/main.zig +++ b/deps/sokol_samples/tutorials/minimal/main.zig @@ -2,6 +2,7 @@ const std = @import("std"); const sokol = @import("sokol"); const sg = sokol.gfx; +const title = "minimal"; const zigltf = @import("zigltf"); const rowmath = @import("rowmath"); const framework = @import("framework"); @@ -142,7 +143,7 @@ export fn frame() void { sokol.debugtext.canvas(sokol.app.widthf() * 0.5, sokol.app.heightf() * 0.5); sokol.debugtext.pos(0.5, 0.5); - sokol.debugtext.puts("minimal_gltf"); + sokol.debugtext.puts(title); sg.beginPass(.{ .action = state.pass_action, @@ -207,7 +208,7 @@ pub fn main() void { .event_cb = event, .width = 800, .height = 600, - .window_title = "rowmath: examples/sokol/camera_simple", + .window_title = title, .icon = .{ .sokol_default = true }, .logger = .{ .func = sokol.log.func }, }); diff --git a/deps/sokol_samples/tutorials/sparse/main.zig b/deps/sokol_samples/tutorials/sparse/main.zig new file mode 100644 index 0000000..4a0a5fb --- /dev/null +++ b/deps/sokol_samples/tutorials/sparse/main.zig @@ -0,0 +1,221 @@ +const std = @import("std"); +const sokol = @import("sokol"); +const sg = sokol.gfx; + +const title = "sparse"; +const zigltf = @import("zigltf"); +const rowmath = @import("rowmath"); +const framework = @import("framework"); +// const utils = @import("utils"); +const Scene = framework.Scene; +// const gltf_fetcher = @import("gltf_fetcher.zig"); + +// https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_003_MinimalGltfFile.html +const minimal_gltf = + \\{ + \\ "scenes" : [ { + \\ "nodes" : [ 0 ] + \\ } ], + \\ + \\ "nodes" : [ { + \\ "mesh" : 0 + \\ } ], + \\ + \\ "meshes" : [ { + \\ "primitives" : [ { + \\ "attributes" : { + \\ "POSITION" : 1 + \\ }, + \\ "indices" : 0 + \\ } ] + \\ } ], + \\ + \\ "buffers" : [ { + \\ "uri" : "data:application/gltf-buffer;base64,AAAIAAcAAAABAAgAAQAJAAgAAQACAAkAAgAKAAkAAgADAAoAAwALAAoAAwAEAAsABAAMAAsABAAFAAwABQANAAwABQAGAA0AAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAQAAAAAAAAAAAAABAQAAAAAAAAAAAAACAQAAAAAAAAAAAAACgQAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAQAAAgD8AAAAAAABAQAAAgD8AAAAAAACAQAAAgD8AAAAAAACgQAAAgD8AAAAAAADAQAAAgD8AAAAACAAKAAwAAAAAAIA/AAAAQAAAAAAAAEBAAABAQAAAAAAAAKBAAACAQAAAAAA=", + \\ "byteLength" : 284 + \\ } ], + \\ + \\ "bufferViews" : [ { + \\ "buffer" : 0, + \\ "byteOffset" : 0, + \\ "byteLength" : 72, + \\ "target" : 34963 + \\ }, { + \\ "buffer" : 0, + \\ "byteOffset" : 72, + \\ "byteLength" : 168 + \\ }, { + \\ "buffer" : 0, + \\ "byteOffset" : 240, + \\ "byteLength" : 6 + \\ }, { + \\ "buffer" : 0, + \\ "byteOffset" : 248, + \\ "byteLength" : 36 + \\ } ], + \\ + \\ "accessors" : [ { + \\ "bufferView" : 0, + \\ "byteOffset" : 0, + \\ "componentType" : 5123, + \\ "count" : 36, + \\ "type" : "SCALAR", + \\ "max" : [ 13 ], + \\ "min" : [ 0 ] + \\ }, { + \\ "bufferView" : 1, + \\ "byteOffset" : 0, + \\ "componentType" : 5126, + \\ "count" : 14, + \\ "type" : "VEC3", + \\ "max" : [ 6.0, 4.0, 0.0 ], + \\ "min" : [ 0.0, 0.0, 0.0 ], + \\ "sparse" : { + \\ "count" : 3, + \\ "indices" : { + \\ "bufferView" : 2, + \\ "byteOffset" : 0, + \\ "componentType" : 5123 + \\ }, + \\ "values" : { + \\ "bufferView" : 3, + \\ "byteOffset" : 0 + \\ } + \\ } + \\ } ], + \\ + \\ "asset" : { + \\ "version" : "2.0" + \\ } + \\} +; + +const state = struct { + var pass_action = sg.PassAction{}; + var input = rowmath.InputState{}; + var orbit = rowmath.OrbitCamera{}; + var gltf: ?std.json.Parsed(zigltf.Gltf) = null; + var scene = Scene{}; +}; + +export fn init() void { + sg.setup(.{ + .environment = sokol.glue.environment(), + .logger = .{ .func = sokol.log.func }, + }); + sokol.gl.setup(.{ + .logger = .{ .func = sokol.log.func }, + }); + + var debugtext_desc = sokol.debugtext.Desc{ + .logger = .{ .func = sokol.log.func }, + }; + debugtext_desc.fonts[0] = sokol.debugtext.fontOric(); + sokol.debugtext.setup(debugtext_desc); + + state.pass_action.colors[0] = .{ + .load_action = .CLEAR, + .clear_value = .{ .r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0 }, + }; + + state.scene.init(std.heap.c_allocator); + + // parse gltf + const allocator = std.heap.c_allocator; + const parsed = std.json.parseFromSlice( + zigltf.Gltf, + allocator, + minimal_gltf, + .{ + .ignore_unknown_fields = true, + }, + ) catch |e| { + std.debug.print("{s}\n", .{@errorName(e)}); + @panic("parseFromSlice"); + }; + + // build + state.scene.load(parsed, &.{}) catch |e| { + std.debug.print("{s}\n", .{@errorName(e)}); + @panic("Scene.load"); + }; +} + +export fn frame() void { + state.input.screen_width = sokol.app.widthf(); + state.input.screen_height = sokol.app.heightf(); + state.orbit.frame(state.input); + state.input.mouse_wheel = 0; + + sokol.debugtext.canvas(sokol.app.widthf() * 0.5, sokol.app.heightf() * 0.5); + sokol.debugtext.pos(0.5, 0.5); + sokol.debugtext.puts(title); + + sg.beginPass(.{ + .action = state.pass_action, + .swapchain = sokol.glue.swapchain(), + }); + state.scene.draw(state.orbit.camera); + sokol.debugtext.draw(); + sg.endPass(); + sg.commit(); +} + +export fn event(e: [*c]const sokol.app.Event) void { + switch (e.*.type) { + .MOUSE_DOWN => { + switch (e.*.mouse_button) { + .LEFT => { + state.input.mouse_left = true; + }, + .RIGHT => { + state.input.mouse_right = true; + }, + .MIDDLE => { + state.input.mouse_middle = true; + }, + .INVALID => {}, + } + }, + .MOUSE_UP => { + switch (e.*.mouse_button) { + .LEFT => { + state.input.mouse_left = false; + }, + .RIGHT => { + state.input.mouse_right = false; + }, + .MIDDLE => { + state.input.mouse_middle = false; + }, + .INVALID => {}, + } + }, + .MOUSE_MOVE => { + state.input.mouse_x = e.*.mouse_x; + state.input.mouse_y = e.*.mouse_y; + }, + .MOUSE_SCROLL => { + state.input.mouse_wheel = e.*.scroll_y; + }, + else => {}, + } +} + +export fn cleanup() void { + sg.shutdown(); +} + +pub fn main() void { + sokol.app.run(.{ + .init_cb = init, + .frame_cb = frame, + .cleanup_cb = cleanup, + .event_cb = event, + .width = 800, + .height = 600, + .window_title = title, + .icon = .{ .sokol_default = true }, + .logger = .{ .func = sokol.log.func }, + }); +} diff --git a/deps/zigltf/src/GltfBuffer.zig b/deps/zigltf/src/GltfBuffer.zig index c829940..1e45146 100644 --- a/deps/zigltf/src/GltfBuffer.zig +++ b/deps/zigltf/src/GltfBuffer.zig @@ -41,7 +41,55 @@ pub fn getImageBytes(self: *@This(), image_index: u32) ![]const u8 { pub fn getAccessorBytes(self: *@This(), T: type, accessor_index: u32) ![]const T { const accessor = self.gltf.accessors[accessor_index]; - if (@sizeOf(T) == accessor.stride()) { + if (@sizeOf(T) != accessor.stride()) { + return error.AccessorStrideNotEquals; + } + + if (accessor.sparse) |sparse| { + if (accessor.bufferView) |bufferView_index| { + const bufferViewBytes = try self.getBufferViewBytes(bufferView_index); + const bytes = bufferViewBytes[accessor.byteOffset .. accessor.byteOffset + accessor.count * accessor.stride()]; + const slice: []const T = @alignCast(std.mem.bytesAsSlice(T, bytes)); + var buffer = try self.allocator.alloc(T, accessor.count); + std.mem.copyForwards(T, buffer, slice[0..accessor.count]); + + switch (sparse.indices.componentType) { + 5121 => { + // u8 + std.debug.assert(sparse.indices.byteOffset == 0); + const indices = try self.getBufferViewBytes(sparse.indices.bufferView); + std.debug.assert(sparse.values.byteOffset == 0); + const values = try self.getBufferViewBytes(sparse.values.bufferView); + const values_t: []const T = @alignCast(std.mem.bytesAsSlice(T, values)); + for (indices, 0..) |index, i| { + buffer[index] = values_t[i]; + } + }, + 5123 => { + // u16 + std.debug.assert(sparse.indices.byteOffset == 0); + const indices = try self.getBufferViewBytes(sparse.indices.bufferView); + const indices_t: []const u16 = @alignCast(std.mem.bytesAsSlice(u16, indices)); + std.debug.assert(sparse.values.byteOffset == 0); + const values = try self.getBufferViewBytes(sparse.values.bufferView); + const values_t: []const T = @alignCast(std.mem.bytesAsSlice(T, values)); + for (indices_t, 0..) |index, i| { + buffer[index] = values_t[i]; + } + }, + 5125 => { + // u32 + @panic("u32 sparse indices"); + }, + else => { + unreachable; + }, + } + return buffer; + } else { + @panic("zero sparse not impl"); + } + } else { if (accessor.bufferView) |bufferView_index| { const bufferViewBytes = try self.getBufferViewBytes(bufferView_index); const bytes = bufferViewBytes[accessor.byteOffset .. accessor.byteOffset + accessor.count * accessor.stride()]; @@ -49,8 +97,6 @@ pub fn getAccessorBytes(self: *@This(), T: type, accessor_index: u32) ![]const T } else { unreachable; } - } else { - return error.AccessorStrideNotEquals; } } diff --git a/deps/zigltf/src/types/Accessor.zig b/deps/zigltf/src/types/Accessor.zig index 207d61b..99705f8 100644 --- a/deps/zigltf/src/types/Accessor.zig +++ b/deps/zigltf/src/types/Accessor.zig @@ -1,5 +1,6 @@ const std = @import("std"); const format_helper = @import("format_helper.zig"); +pub const AccessorSparse = @import("AccessorSparse.zig"); pub const Accessor = @This(); name: ?[]const u8 = null, @@ -8,6 +9,7 @@ type: []const u8, count: u32, bufferView: ?u32 = null, byteOffset: u32 = 0, +sparse: ?AccessorSparse = null, fn componentTypeToStr(componentType: u32) []const u8 { return switch (componentType) { @@ -57,10 +59,24 @@ pub fn format( typeToSuffix(self.type), self.count, }); - if (self.bufferView) |bufferView| { - try writer.print(" => bufferView#{}", .{bufferView}); - if (self.byteOffset > 0) { - try writer.print("+{}", .{self.byteOffset}); + if (self.sparse) |sparse| { + if (self.bufferView) |bufferView| { + try writer.print(" => bufferView#{} + sparse[{}]", .{ + bufferView, + sparse.count, + }); + } else { + // the sparse accessor is initialized as an array of zeros of size (size of the accessor element) * (accessor.count) bytes. + try writer.print(" => + sparse[{}]", .{ + sparse.count, + }); + } + } else { + if (self.bufferView) |bufferView| { + try writer.print(" => bufferView#{}", .{bufferView}); + if (self.byteOffset > 0) { + try writer.print("+{}", .{self.byteOffset}); + } } } } diff --git a/deps/zigltf/src/types/AccessorSparse.zig b/deps/zigltf/src/types/AccessorSparse.zig new file mode 100644 index 0000000..1f99e1b --- /dev/null +++ b/deps/zigltf/src/types/AccessorSparse.zig @@ -0,0 +1,7 @@ +const AccessorSparseIndices = @import("AccessorSparseIndices.zig"); +const AccessorSparseValues = @import("AccessorSparseValues.zig"); +pub const AccessorSpars = @This(); + +count: u32, +indices: AccessorSparseIndices, +values: AccessorSparseValues, diff --git a/deps/zigltf/src/types/AccessorSparseIndices.zig b/deps/zigltf/src/types/AccessorSparseIndices.zig new file mode 100644 index 0000000..e0689cf --- /dev/null +++ b/deps/zigltf/src/types/AccessorSparseIndices.zig @@ -0,0 +1,7 @@ +pub const AccessorSparseIndices = @This(); + +bufferView: u32, +byteOffset: u32 = 0, + +// 5121(u8) / 5123(u16) / 5125(u32) +componentType: u32, diff --git a/deps/zigltf/src/types/AccessorSparseValues.zig b/deps/zigltf/src/types/AccessorSparseValues.zig new file mode 100644 index 0000000..a225169 --- /dev/null +++ b/deps/zigltf/src/types/AccessorSparseValues.zig @@ -0,0 +1,4 @@ +pub const AccessorSparseValues = @This(); + +bufferView: u32, +byteOffset: u32 = 0, diff --git a/src/data.ts b/src/data.ts index 40e9cc4..43c01bc 100644 --- a/src/data.ts +++ b/src/data.ts @@ -17,6 +17,16 @@ export const SAMPLES: SampleType[] = [ }, ], }, + { + name: "sparse", + links: [ + { + name: 'glTF-Tutorials', + url: "https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.html", + }, + ], + }, + // { name: "glb", links: [