diff --git a/deps/sokol_samples/build.zig b/deps/sokol_samples/build.zig index defa60a..7c19368 100644 --- a/deps/sokol_samples/build.zig +++ b/deps/sokol_samples/build.zig @@ -155,8 +155,16 @@ pub const samples = [_]Sample{ .name = "glb", .root_source_file = "glb/main.zig", }, + .{ + .name = "gltf", + .root_source_file = "gltf/main.zig", + }, .{ .name = "draco", .root_source_file = "draco/main.zig", }, + .{ + .name = "vrm0", + .root_source_file = "vrm0/main.zig", + }, }; diff --git a/deps/sokol_samples/build.zig.zon b/deps/sokol_samples/build.zig.zon index 956d57f..3750df5 100644 --- a/deps/sokol_samples/build.zig.zon +++ b/deps/sokol_samples/build.zig.zon @@ -41,6 +41,10 @@ .url = "git+https://github.com/google/draco.git#f4e08970937303d3016b8d841fe9e6141a9b66a0", .hash = "12203bbaae27dde1346550405b678c29bbc6bae0db6e59fb5be1a20d5d51f454dc04", }, + .univrm = .{ + .url = "git+https://github.com/vrm-c/UniVRM.git#9e5d11be621c6349ad53b4f7fdc8cf9e52e7bbcd", + .hash = "122098e4ef97163fc7ba0ec63b6bec618a5fffead8e0e19e17a517cd05741f92a098", + }, }, .paths = .{ "", diff --git a/deps/sokol_samples/vrm0/main.zig b/deps/sokol_samples/vrm0/main.zig new file mode 100644 index 0000000..cd702f3 --- /dev/null +++ b/deps/sokol_samples/vrm0/main.zig @@ -0,0 +1,136 @@ +const std = @import("std"); +const sokol = @import("sokol"); +const sg = sokol.gfx; + +const zigltf = @import("zigltf"); +const rowmath = @import("rowmath"); +const framework = @import("framework"); +const Scene = framework.Scene; +const gltf_fetcher = framework.gltf_fetcher; + +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{}; +}; + +const load_file = "UniVRM/Alicia_vrm-0.51/Alicia_vrm-0.51.vrm"; + +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); + + gltf_fetcher.init(std.heap.c_allocator); + gltf_fetcher.fetch_gltf(load_file, &on_gltf) catch @panic("fetch_gltf"); +} + +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"); + }; + gltf_fetcher.state.status = "loaded"; +} + +export fn frame() void { + sokol.fetch.dowork(); + + 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(gltf_fetcher.state.status); + + 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 = "rowmath: examples/sokol/camera_simple", + .icon = .{ .sokol_default = true }, + .logger = .{ .func = sokol.log.func }, + }); +} diff --git a/package-lock.json b/package-lock.json index 33eb648..616325d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-router-dom": "^6.26.2" }, "devDependencies": { "@eslint/js": "^9.9.0", @@ -940,6 +941,14 @@ "node": ">=18" } }, + "node_modules/@remix-run/router": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", + "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", @@ -2725,6 +2734,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", + "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", + "dependencies": { + "@remix-run/router": "1.19.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", + "dependencies": { + "@remix-run/router": "1.19.2", + "react-router": "6.26.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", diff --git a/package.json b/package.json index a1068a4..5a45a41 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,13 @@ }, "dependencies": { "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-router-dom": "^6.26.2" }, "devDependencies": { "@eslint/js": "^9.9.0", + "@playwright/test": "^1.46.1", + "@types/node": "^22.5.5", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", @@ -24,12 +27,10 @@ "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.9", "globals": "^15.9.0", + "playwright": "^1.46.0", "typescript": "^5.5.3", "typescript-eslint": "^8.0.1", "vite": "^5.4.1", - "@playwright/test": "^1.46.1", - "@types/node": "^22.5.5", - "playwright": "^1.46.0", "vite-node": "^2.0.5" } } diff --git a/src/App.css b/src/App.css index eae956b..9c18f1d 100644 --- a/src/App.css +++ b/src/App.css @@ -1,5 +1,6 @@ html, -body { +body, +#root { width: 100%; height: 100%; margin: 0; @@ -14,9 +15,29 @@ body { border: solid 4px; width: 150px; height: 150px; + overflow-wrap: anywhere; figure { margin: 0; padding: 0; } } + +ul { + padding: 0; + margin: 0; +} + +li { + margin-left: 0 0 1em 0; + list-style: none; +} + +.not_found { + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} diff --git a/src/App.tsx b/src/App.tsx index acad055..e661bab 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,4 @@ +import { BrowserRouter, Routes, Route } from 'react-router-dom' import { SAMPLES, type SampleType } from './data'; import "./App.css"; const BASE_URL = import.meta.env.BASE_URL; @@ -22,12 +23,31 @@ function Sample(props: SampleType) { ); } +function Home() { + return (<> +