reze-engine 0.15.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/camera.d.ts +2 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +8 -3
- package/dist/engine.d.ts +19 -4
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +294 -27
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +13 -7
- package/dist/math.d.ts +2 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +65 -46
- package/dist/model.d.ts +28 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +259 -75
- package/dist/physics/physics.d.ts +3 -0
- package/dist/physics/physics.d.ts.map +1 -1
- package/dist/physics/physics.js +55 -4
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +2 -30
- package/dist/shaders/materials/body.d.ts +1 -1
- package/dist/shaders/materials/body.d.ts.map +1 -1
- package/dist/shaders/materials/body.js +3 -1
- package/dist/shaders/materials/cloth_rough.d.ts +1 -1
- package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
- package/dist/shaders/materials/cloth_rough.js +3 -1
- package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
- package/dist/shaders/materials/common.d.ts +2 -2
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +8 -0
- package/dist/shaders/materials/default.d.ts +1 -1
- package/dist/shaders/materials/default.d.ts.map +1 -1
- package/dist/shaders/materials/eye.d.ts +1 -1
- package/dist/shaders/materials/eye.d.ts.map +1 -1
- package/dist/shaders/materials/face.d.ts +1 -1
- package/dist/shaders/materials/face.d.ts.map +1 -1
- package/dist/shaders/materials/face.js +4 -1
- package/dist/shaders/materials/hair.d.ts +1 -1
- package/dist/shaders/materials/hair.d.ts.map +1 -1
- package/dist/shaders/materials/metal.d.ts +1 -1
- package/dist/shaders/materials/metal.d.ts.map +1 -1
- package/dist/shaders/materials/stockings.d.ts +1 -1
- package/dist/shaders/materials/stockings.d.ts.map +1 -1
- package/dist/shaders/materials/stockings.js +13 -9
- package/dist/shaders/passes/composite.d.ts +1 -1
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +18 -12
- package/dist/shaders/passes/morph.d.ts +2 -0
- package/dist/shaders/passes/morph.d.ts.map +1 -0
- package/dist/shaders/passes/morph.js +49 -0
- package/package.json +1 -1
- package/src/camera.ts +9 -3
- package/src/engine.ts +336 -29
- package/src/ik-solver.ts +14 -7
- package/src/math.ts +41 -51
- package/src/model.ts +288 -91
- package/src/physics/physics.ts +56 -13
- package/src/pmx-loader.ts +2 -35
- package/src/shaders/materials/body.ts +3 -1
- package/src/shaders/materials/cloth_rough.ts +3 -1
- package/src/shaders/materials/common.ts +8 -0
- package/src/shaders/materials/face.ts +4 -1
- package/src/shaders/materials/stockings.ts +13 -9
- package/src/shaders/passes/composite.ts +18 -12
- package/src/shaders/passes/morph.ts +50 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const COMPOSITE_SHADER_WGSL = "\n// Pipeline-override constant: the engine creates two composite pipelines, one\n// with APPLY_GAMMA=false (gamma=1 fast path) and one with APPLY_GAMMA=true.\n// The 'if (APPLY_GAMMA)' below is resolved at pipeline-compile time \u2014 the\n// dead branch is dropped by the shader compiler (no runtime branch, no pow\n// invocation on Safari's Metal backend in the common case).\noverride APPLY_GAMMA: bool = true;\n\n@group(0) @binding(0) var hdrTex: texture_2d<f32>;\n@group(0) @binding(1) var bloomTex: texture_2d<f32>; // bloomUpTexture mip 0 (full pyramid top)\n@group(0) @binding(2) var bloomSamp: sampler;\n@group(0) @binding(3) var<uniform> viewU: array<vec4<f32>, 2>;\n// Aux mask/alpha texture. .r = bloom mask (unused here; bloom blit uses it).\n// .g = accumulated canvas alpha (what hdr.a carried before the HDR format\n// became rg11b10ufloat). We unpremultiply HDR by this alpha for tonemap, then\n// re-premultiply the tonemapped color for output so the premultiplied canvas\n// alphaMode composites the WebGPU surface over the page background correctly.\n@group(0) @binding(4) var maskTex: texture_2d<f32>;\n// viewU[0] = (exposure, invGamma, _, _); viewU[1] = (tint.rgb, intensity)\n// invGamma = 1/gamma precomputed on CPU \u2014 avoids a per-pixel divide.\n\nfn filmic(x: f32) -> f32 {\n //
|
|
1
|
+
export declare const COMPOSITE_SHADER_WGSL = "\n// Pipeline-override constant: the engine creates two composite pipelines, one\n// with APPLY_GAMMA=false (gamma=1 fast path) and one with APPLY_GAMMA=true.\n// The 'if (APPLY_GAMMA)' below is resolved at pipeline-compile time \u2014 the\n// dead branch is dropped by the shader compiler (no runtime branch, no pow\n// invocation on Safari's Metal backend in the common case).\noverride APPLY_GAMMA: bool = true;\n\n@group(0) @binding(0) var hdrTex: texture_2d<f32>;\n@group(0) @binding(1) var bloomTex: texture_2d<f32>; // bloomUpTexture mip 0 (full pyramid top)\n@group(0) @binding(2) var bloomSamp: sampler;\n@group(0) @binding(3) var<uniform> viewU: array<vec4<f32>, 2>;\n// Aux mask/alpha texture. .r = bloom mask (unused here; bloom blit uses it).\n// .g = accumulated canvas alpha (what hdr.a carried before the HDR format\n// became rg11b10ufloat). We unpremultiply HDR by this alpha for tonemap, then\n// re-premultiply the tonemapped color for output so the premultiplied canvas\n// alphaMode composites the WebGPU surface over the page background correctly.\n@group(0) @binding(4) var maskTex: texture_2d<f32>;\n// Filmic tone curve baked to a WIDTH\u00D71 r16float LUT (bakeFilmicLut on the CPU side).\n// Domain: log2(linear) mapped to [0,13]. Replaces the old array<f32,14> that was indexed\n// by a runtime u32 (a Metal-backend smell \u2014 dynamic local-array indexing lowers to a\n// per-invocation copy/switch) and interpolated piecewise-linearly (C0, so segment slope\n// discontinuities showed as Mach bands in smooth skin/shadow gradients). The LUT is a\n// monotone-cubic (Fritsch\u2013Carlson) fit through the same 14 anchors \u2014 same values, C1\n// continuity kills the banding \u2014 sampled with hardware linear filtering.\n@group(0) @binding(5) var filmicLut: texture_2d<f32>;\n// viewU[0] = (exposure, invGamma, _, _); viewU[1] = (tint.rgb, intensity)\n// invGamma = 1/gamma precomputed on CPU \u2014 avoids a per-pixel divide.\n\n// Must match FILMIC_LUT_WIDTH in engine.ts (bakeFilmicLut).\nconst FILMIC_LUT_W: f32 = 256.0;\n\nfn filmic(x: f32) -> f32 {\n // Reference checkpoints (Blender 3.6 Filmic MHC, sobotka/filmic-blender\n // look_medium-high-contrast.spi1d): linear 0.18 \u2192 ~0.395, linear 1.0 \u2192 ~0.83.\n // NOTE: version-pinned to Blender 3.6 \u2014 4.x defaults to AgX, not Filmic.\n let t = clamp(log2(max(x, 1e-10)) + 10.0, 0.0, 13.0);\n // Map t\u2208[0,13] to the texel-center of baked sample j = t\u00B7(W-1)/13.\n let u = (t * (FILMIC_LUT_W - 1.0) / 13.0 + 0.5) / FILMIC_LUT_W;\n // textureSampleLevel (explicit LOD, no derivatives) is legal in non-uniform flow.\n return textureSampleLevel(filmicLut, bloomSamp, vec2f(u, 0.5), 0.0).r;\n}\n\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {\n let x = f32((vi & 1u) << 2u) - 1.0;\n let y = f32((vi & 2u) << 1u) - 1.0;\n return vec4f(x, y, 0.0, 1.0);\n}\n\n@fragment fn fs(@builtin(position) fragCoord: vec4f) -> @location(0) vec4f {\n let coord = vec2<i32>(fragCoord.xy);\n let hdr = textureLoad(hdrTex, coord, 0);\n let alpha = textureLoad(maskTex, coord, 0).g;\n let a = max(alpha, 1e-6);\n let straight = hdr.rgb / a;\n let fullSz = vec2f(textureDimensions(hdrTex));\n // Bloom is at half-res (pyramid mip 0). Sampler interpolates back to full-res UVs.\n // fragCoord.xy is already at pixel center (e.g. 0.5, 0.5 for first pixel).\n let bloomUv = fragCoord.xy / max(fullSz, vec2f(1.0));\n let tint = viewU[1].xyz;\n let intensity = viewU[1].w;\n let bloom = textureSampleLevel(bloomTex, bloomSamp, bloomUv, 0.0).rgb * tint * intensity;\n let combined = straight + bloom;\n let exposed = combined * exp2(viewU[0].x);\n let tm = vec3f(filmic(exposed.r), filmic(exposed.g), filmic(exposed.b));\n var disp = max(tm, vec3f(0.0));\n if (APPLY_GAMMA) {\n disp = pow(disp, vec3f(viewU[0].y));\n }\n return vec4f(disp * alpha, alpha);\n}\n";
|
|
2
2
|
//# sourceMappingURL=composite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composite.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/composite.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"composite.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/composite.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,s0HAuEjC,CAAA"}
|
|
@@ -18,23 +18,29 @@ override APPLY_GAMMA: bool = true;
|
|
|
18
18
|
// re-premultiply the tonemapped color for output so the premultiplied canvas
|
|
19
19
|
// alphaMode composites the WebGPU surface over the page background correctly.
|
|
20
20
|
@group(0) @binding(4) var maskTex: texture_2d<f32>;
|
|
21
|
+
// Filmic tone curve baked to a WIDTH×1 r16float LUT (bakeFilmicLut on the CPU side).
|
|
22
|
+
// Domain: log2(linear) mapped to [0,13]. Replaces the old array<f32,14> that was indexed
|
|
23
|
+
// by a runtime u32 (a Metal-backend smell — dynamic local-array indexing lowers to a
|
|
24
|
+
// per-invocation copy/switch) and interpolated piecewise-linearly (C0, so segment slope
|
|
25
|
+
// discontinuities showed as Mach bands in smooth skin/shadow gradients). The LUT is a
|
|
26
|
+
// monotone-cubic (Fritsch–Carlson) fit through the same 14 anchors — same values, C1
|
|
27
|
+
// continuity kills the banding — sampled with hardware linear filtering.
|
|
28
|
+
@group(0) @binding(5) var filmicLut: texture_2d<f32>;
|
|
21
29
|
// viewU[0] = (exposure, invGamma, _, _); viewU[1] = (tint.rgb, intensity)
|
|
22
30
|
// invGamma = 1/gamma precomputed on CPU — avoids a per-pixel divide.
|
|
23
31
|
|
|
32
|
+
// Must match FILMIC_LUT_WIDTH in engine.ts (bakeFilmicLut).
|
|
33
|
+
const FILMIC_LUT_W: f32 = 256.0;
|
|
34
|
+
|
|
24
35
|
fn filmic(x: f32) -> f32 {
|
|
25
|
-
//
|
|
26
|
-
// look_medium-high-contrast.spi1d).
|
|
27
|
-
//
|
|
28
|
-
// as "washed-out" on saturated surfaces (hair especially).
|
|
29
|
-
// Reference checkpoints: linear 0.18 → ~0.395, linear 1.0 → ~0.83.
|
|
30
|
-
var lut = array<f32, 14>(
|
|
31
|
-
0.0028, 0.0068, 0.0151, 0.0313, 0.0610, 0.1120, 0.1920,
|
|
32
|
-
0.3060, 0.4590, 0.6310, 0.8200, 0.9070, 0.9620, 0.9890
|
|
33
|
-
);
|
|
36
|
+
// Reference checkpoints (Blender 3.6 Filmic MHC, sobotka/filmic-blender
|
|
37
|
+
// look_medium-high-contrast.spi1d): linear 0.18 → ~0.395, linear 1.0 → ~0.83.
|
|
38
|
+
// NOTE: version-pinned to Blender 3.6 — 4.x defaults to AgX, not Filmic.
|
|
34
39
|
let t = clamp(log2(max(x, 1e-10)) + 10.0, 0.0, 13.0);
|
|
35
|
-
|
|
36
|
-
let
|
|
37
|
-
|
|
40
|
+
// Map t∈[0,13] to the texel-center of baked sample j = t·(W-1)/13.
|
|
41
|
+
let u = (t * (FILMIC_LUT_W - 1.0) / 13.0 + 0.5) / FILMIC_LUT_W;
|
|
42
|
+
// textureSampleLevel (explicit LOD, no derivatives) is legal in non-uniform flow.
|
|
43
|
+
return textureSampleLevel(filmicLut, bloomSamp, vec2f(u, 0.5), 0.0).r;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const MORPH_COMPUTE_WGSL = "\nstruct Params { vertexCount: u32 };\n\n@group(0) @binding(0) var<storage, read> basePos: array<f32>; // vertexCount * 3\n@group(0) @binding(1) var<storage, read> rowStart: array<u32>; // vertexCount + 1\n@group(0) @binding(2) var<storage, read> colMorph: array<u32>; // entryCount\n@group(0) @binding(3) var<storage, read> colOffset: array<f32>; // entryCount * 3\n@group(0) @binding(4) var<storage, read> weights: array<f32>; // morphCount (effective)\n@group(0) @binding(5) var<storage, read_write> vtx: array<f32>; // vertexCount * 8\n@group(0) @binding(6) var<uniform> params: Params;\n\n@compute @workgroup_size(64)\nfn cs(@builtin(global_invocation_id) gid: vec3<u32>) {\n let v = gid.x;\n if (v >= params.vertexCount) { return; }\n\n let b3 = v * 3u;\n var px = basePos[b3];\n var py = basePos[b3 + 1u];\n var pz = basePos[b3 + 2u];\n\n let s = rowStart[v];\n let e = rowStart[v + 1u];\n for (var i = s; i < e; i = i + 1u) {\n let w = weights[colMorph[i]];\n if (w != 0.0) {\n let o3 = i * 3u;\n px = px + w * colOffset[o3];\n py = py + w * colOffset[o3 + 1u];\n pz = pz + w * colOffset[o3 + 2u];\n }\n }\n\n let vo = v * 8u; // VERTEX_STRIDE \u2014 write only the position slots\n vtx[vo] = px;\n vtx[vo + 1u] = py;\n vtx[vo + 2u] = pz;\n}\n";
|
|
2
|
+
//# sourceMappingURL=morph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"morph.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/morph.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,kBAAkB,2yCAsC9B,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// GPU vertex-morph compute pass. Produces per-vertex morphed positions and writes them
|
|
2
|
+
// straight into the interleaved vertex buffer (stride 8: pos3 + normal3 + uv2), so every
|
|
3
|
+
// downstream pass (main / shadow / outline / pick) sees the morphed geometry with no VS
|
|
4
|
+
// change. Runs once per frame only when a model's morph weights changed.
|
|
5
|
+
//
|
|
6
|
+
// Morph data is a CSR (compressed sparse row) inversion of the per-morph vertex-offset
|
|
7
|
+
// lists: for vertex v, its entries live in [rowStart[v], rowStart[v+1]); each entry is a
|
|
8
|
+
// (morphIndex, xyz-offset) pair, ordered by ascending morph index — the same accumulation
|
|
9
|
+
// order the CPU path used, so results match. Group morphs are resolved to effective
|
|
10
|
+
// weights on the CPU and uploaded in `weights`.
|
|
11
|
+
export const MORPH_COMPUTE_WGSL = /* wgsl */ `
|
|
12
|
+
struct Params { vertexCount: u32 };
|
|
13
|
+
|
|
14
|
+
@group(0) @binding(0) var<storage, read> basePos: array<f32>; // vertexCount * 3
|
|
15
|
+
@group(0) @binding(1) var<storage, read> rowStart: array<u32>; // vertexCount + 1
|
|
16
|
+
@group(0) @binding(2) var<storage, read> colMorph: array<u32>; // entryCount
|
|
17
|
+
@group(0) @binding(3) var<storage, read> colOffset: array<f32>; // entryCount * 3
|
|
18
|
+
@group(0) @binding(4) var<storage, read> weights: array<f32>; // morphCount (effective)
|
|
19
|
+
@group(0) @binding(5) var<storage, read_write> vtx: array<f32>; // vertexCount * 8
|
|
20
|
+
@group(0) @binding(6) var<uniform> params: Params;
|
|
21
|
+
|
|
22
|
+
@compute @workgroup_size(64)
|
|
23
|
+
fn cs(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
24
|
+
let v = gid.x;
|
|
25
|
+
if (v >= params.vertexCount) { return; }
|
|
26
|
+
|
|
27
|
+
let b3 = v * 3u;
|
|
28
|
+
var px = basePos[b3];
|
|
29
|
+
var py = basePos[b3 + 1u];
|
|
30
|
+
var pz = basePos[b3 + 2u];
|
|
31
|
+
|
|
32
|
+
let s = rowStart[v];
|
|
33
|
+
let e = rowStart[v + 1u];
|
|
34
|
+
for (var i = s; i < e; i = i + 1u) {
|
|
35
|
+
let w = weights[colMorph[i]];
|
|
36
|
+
if (w != 0.0) {
|
|
37
|
+
let o3 = i * 3u;
|
|
38
|
+
px = px + w * colOffset[o3];
|
|
39
|
+
py = py + w * colOffset[o3 + 1u];
|
|
40
|
+
pz = pz + w * colOffset[o3 + 2u];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let vo = v * 8u; // VERTEX_STRIDE — write only the position slots
|
|
45
|
+
vtx[vo] = px;
|
|
46
|
+
vtx[vo + 1u] = py;
|
|
47
|
+
vtx[vo + 2u] = pz;
|
|
48
|
+
}
|
|
49
|
+
`;
|
package/package.json
CHANGED
package/src/camera.ts
CHANGED
|
@@ -37,6 +37,10 @@ export class Camera {
|
|
|
37
37
|
lowerBetaLimit: number = 0.001
|
|
38
38
|
upperBetaLimit: number = Math.PI - 0.001
|
|
39
39
|
|
|
40
|
+
// Reused each frame so getViewMatrix/getProjectionMatrix don't allocate a Mat4 per call.
|
|
41
|
+
private _viewMat = new Mat4(new Float32Array(16))
|
|
42
|
+
private _projMat = new Mat4(new Float32Array(16))
|
|
43
|
+
|
|
40
44
|
constructor(alpha: number, beta: number, radius: number, target: Vec3, fov: number = Math.PI / 4) {
|
|
41
45
|
this.alpha = alpha
|
|
42
46
|
this.beta = beta
|
|
@@ -66,8 +70,9 @@ export class Camera {
|
|
|
66
70
|
|
|
67
71
|
getViewMatrix(): Mat4 {
|
|
68
72
|
const eye = this.getPosition()
|
|
69
|
-
const
|
|
70
|
-
|
|
73
|
+
const t = this.target
|
|
74
|
+
Mat4.lookAtInto(this._viewMat.values, eye.x, eye.y, eye.z, t.x, t.y, t.z, 0, 1, 0)
|
|
75
|
+
return this._viewMat
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
// Get camera's right and up vectors for panning
|
|
@@ -139,7 +144,8 @@ export class Camera {
|
|
|
139
144
|
|
|
140
145
|
getProjectionMatrix(): Mat4 {
|
|
141
146
|
this.updateFarFromRadius()
|
|
142
|
-
|
|
147
|
+
Mat4.perspectiveInto(this._projMat.values, this.fov, this.aspect, this.near, this.far)
|
|
148
|
+
return this._projMat
|
|
143
149
|
}
|
|
144
150
|
|
|
145
151
|
attachControl(canvas: HTMLCanvasElement) {
|