reze-engine 0.63.0 → 0.64.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.
Files changed (82) hide show
  1. package/dist/engine.d.ts +95 -57
  2. package/dist/engine.d.ts.map +1 -1
  3. package/dist/engine.js +465 -162
  4. package/dist/graph/compile.d.ts.map +1 -1
  5. package/dist/graph/compile.js +4 -1
  6. package/dist/graph/registry.d.ts +3 -0
  7. package/dist/graph/registry.d.ts.map +1 -1
  8. package/dist/graph/registry.js +133 -0
  9. package/dist/graph/schema.d.ts +12 -0
  10. package/dist/graph/schema.d.ts.map +1 -1
  11. package/dist/graph/slots.d.ts.map +1 -1
  12. package/dist/graph/slots.js +11 -20
  13. package/dist/index.d.ts +9 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/shaders/lights.d.ts +3 -2
  17. package/dist/shaders/lights.d.ts.map +1 -1
  18. package/dist/shaders/lights.js +16 -3
  19. package/dist/shaders/materials/common.d.ts.map +1 -1
  20. package/dist/shaders/materials/common.js +63 -56
  21. package/dist/shaders/materials/nodes.d.ts +1 -1
  22. package/dist/shaders/materials/nodes.d.ts.map +1 -1
  23. package/dist/shaders/materials/nodes.js +210 -0
  24. package/dist/shaders/passes/composite.d.ts.map +1 -1
  25. package/dist/shaders/passes/composite.js +13 -1
  26. package/dist/shaders/passes/cull.d.ts +1 -1
  27. package/dist/shaders/passes/cull.d.ts.map +1 -1
  28. package/dist/shaders/passes/cull.js +7 -3
  29. package/dist/shaders/passes/ground.d.ts.map +1 -1
  30. package/dist/shaders/passes/ground.js +32 -55
  31. package/dist/shaders/passes/particles.d.ts +1 -1
  32. package/dist/shaders/passes/particles.js +1 -1
  33. package/dist/shaders/scene-light-api.d.ts +18 -10
  34. package/dist/shaders/scene-light-api.d.ts.map +1 -1
  35. package/dist/shaders/scene-light-api.js +58 -46
  36. package/dist/shadow-cascades.d.ts +47 -20
  37. package/dist/shadow-cascades.d.ts.map +1 -1
  38. package/dist/shadow-cascades.js +36 -13
  39. package/dist/unity/character.d.ts +59 -0
  40. package/dist/unity/character.d.ts.map +1 -0
  41. package/dist/unity/character.js +120 -0
  42. package/dist/unity/globals.d.ts +93 -0
  43. package/dist/unity/globals.d.ts.map +1 -0
  44. package/dist/unity/globals.js +340 -0
  45. package/dist/unity/host.d.ts +137 -0
  46. package/dist/unity/host.d.ts.map +1 -0
  47. package/dist/unity/host.js +489 -0
  48. package/dist/unity/looks.d.ts +88 -0
  49. package/dist/unity/looks.d.ts.map +1 -0
  50. package/dist/unity/looks.js +295 -0
  51. package/dist/unity/skin.d.ts +9 -0
  52. package/dist/unity/skin.d.ts.map +1 -0
  53. package/dist/unity/skin.js +117 -0
  54. package/dist/unity/stage.d.ts +148 -0
  55. package/dist/unity/stage.d.ts.map +1 -0
  56. package/dist/unity/stage.js +375 -0
  57. package/dist/unity/state.d.ts +22 -0
  58. package/dist/unity/state.d.ts.map +1 -0
  59. package/dist/unity/state.js +170 -0
  60. package/package.json +1 -1
  61. package/src/engine.ts +503 -176
  62. package/src/graph/compile.ts +3 -1
  63. package/src/graph/registry.ts +142 -0
  64. package/src/graph/schema.ts +9 -0
  65. package/src/graph/slots.ts +11 -20
  66. package/src/index.ts +11 -0
  67. package/src/shaders/lights.ts +16 -3
  68. package/src/shaders/materials/common.ts +63 -57
  69. package/src/shaders/materials/nodes.ts +210 -0
  70. package/src/shaders/passes/composite.ts +13 -1
  71. package/src/shaders/passes/cull.ts +7 -3
  72. package/src/shaders/passes/ground.ts +32 -55
  73. package/src/shaders/passes/particles.ts +1 -1
  74. package/src/shaders/scene-light-api.ts +59 -46
  75. package/src/shadow-cascades.ts +61 -32
  76. package/src/unity/character.ts +169 -0
  77. package/src/unity/globals.ts +443 -0
  78. package/src/unity/host.ts +587 -0
  79. package/src/unity/looks.ts +372 -0
  80. package/src/unity/skin.ts +128 -0
  81. package/src/unity/stage.ts +568 -0
  82. package/src/unity/state.ts +172 -0
@@ -0,0 +1,59 @@
1
+ import type { NativeValue } from "./host";
2
+ /** A game character's CharacterEffect values, as its prefab serialises them. */
3
+ export type CharacterRig = {
4
+ localLightInclination: number;
5
+ localLightAzimuth: number;
6
+ localLightIntensity: number;
7
+ localLightColor: [number, number, number, number];
8
+ faceReceiveShadow: boolean;
9
+ disableFaceSDFShadow: boolean;
10
+ emissionColor: [number, number, number, number];
11
+ fillOuter: [number, number, number, number];
12
+ fillInner: [number, number, number, number];
13
+ fillColor: [number, number, number, number];
14
+ fillRatio: number;
15
+ rimlightColor: [number, number, number, number];
16
+ rimlightThreshold: number;
17
+ rimlightFade: number;
18
+ rimlightRange: number;
19
+ rimlightInclination: number;
20
+ rimlightAzimuth1: number;
21
+ rimlightAzimuth2: number;
22
+ characterLayer: number;
23
+ /** EnvironmentEffect's trilight, sRGB as serialised. */
24
+ skyColor: [number, number, number];
25
+ equatorColor: [number, number, number];
26
+ groundColor: [number, number, number];
27
+ };
28
+ /**
29
+ * Unity's ambient probe of a trilight (RenderSettings.ambientProbe in Trilight
30
+ * mode), measured in Unity 6 rather than derived: the colours are taken to
31
+ * linear, then
32
+ * c0 = (S + 4E + G) / 6 c1 = (√6 / 9)(S − G)
33
+ * c6 = (5/192)(2E − S − G) c8 = (5/64)(2E − S − G)
34
+ * and every other coefficient is zero — Unity's raw coefficients, which its
35
+ * shader constants pack as SHA = (c3, c1, c2, c0 − c6), SHB = (c4, c5, 3c6, c7),
36
+ * SHC = c8. Checked against the game's recorded block for (0.8, 0.5, 0.3).
37
+ */
38
+ export declare function trilightProbe(sky: [number, number, number], equator: [number, number, number], ground: [number, number, number]): number[][];
39
+ /** Unity's shader constants from raw coefficients (channel, index). */
40
+ export declare function probeConstants(c: number[][]): Record<string, number[]>;
41
+ /**
42
+ * sim_TowardMatrix (LightingFeatures.SetupTowardMatrix): the frame the
43
+ * character light is expressed in — the camera's backward direction flattened
44
+ * to the ground, as columns (up × d, up, d). Taken immediately, as the DLC
45
+ * poster sets CameraExtension.shadowRotationImmediately.
46
+ */
47
+ export declare function towardMatrix(cameraForward: [number, number, number]): Float32Array;
48
+ /** CharacterGlobalParams.Execute: the outline's resolution allowance. */
49
+ export declare function outlineMaxOffsetMultiplier(pixelWidth: number): number;
50
+ /**
51
+ * The character's property block (CharacterEffect.LateUpdate with the default
52
+ * look: no dissolve, dither, interference), by name.
53
+ *
54
+ * `head` is the head bone's skinning matrix (its pose relative to the bind
55
+ * pose, engine units) and `headRest` its rest position; `scale` engine units
56
+ * per game unit.
57
+ */
58
+ export declare function characterBlock(rig: CharacterRig, head: ArrayLike<number> | null, headRest: [number, number, number], scale: number): Record<string, NativeValue>;
59
+ //# sourceMappingURL=character.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"character.d.ts","sourceRoot":"","sources":["../../src/unity/character.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAGzC,gFAAgF;AAChF,MAAM,MAAM,YAAY,GAAG;IACzB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,iBAAiB,EAAE,MAAM,CAAA;IACzB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACjD,iBAAiB,EAAE,OAAO,CAAA;IAC1B,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/C,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3C,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3C,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/C,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,wDAAwD;IACxD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC,CAAA;AAID;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC7B,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACjC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAC/B,MAAM,EAAE,EAAE,CAUZ;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAUtE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAMlF;AAED,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,EAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAClC,KAAK,EAAE,MAAM,GACZ,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CA+C7B"}
@@ -0,0 +1,120 @@
1
+ // The game's per-character shading inputs, for a model wearing its character
2
+ // shaders.
3
+ //
4
+ // In the game a CharacterEffect component (P08.RenderPipeline, ported in
5
+ // ag-rip's charfx/CharacterEffect.cs from the decompiled GameAssembly.dll)
6
+ // writes one property block shared by all of a character's renderers: its own
7
+ // key light, expressed in a camera-facing frame (_LocalLightDir in
8
+ // sim_TowardMatrix, LightingFeatures.SetupTowardMatrix); its face frame for the
9
+ // SDF face shadow (_World2Face); its rim lights; its fills; and an ambient of
10
+ // its own from three colours (EnvironmentEffect). Nothing here is per scene:
11
+ // it is the character's look, carried by the character.
12
+ //
13
+ // An MMD model has no CharacterEffect. It takes a CharacterRig — the values a
14
+ // game character's prefab carries, exported with the look — and the frames
15
+ // come from its own bones: the face is the head bone turned to face the way
16
+ // the game's faces do (the game's face node rests aligned with the world,
17
+ // looking +Z; a PMX model looks −Z, so a half turn about Y).
18
+ import { gameToEngine, invert4, mul4 } from "./globals";
19
+ const srgbToLinear = (c) => (c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
20
+ /**
21
+ * Unity's ambient probe of a trilight (RenderSettings.ambientProbe in Trilight
22
+ * mode), measured in Unity 6 rather than derived: the colours are taken to
23
+ * linear, then
24
+ * c0 = (S + 4E + G) / 6 c1 = (√6 / 9)(S − G)
25
+ * c6 = (5/192)(2E − S − G) c8 = (5/64)(2E − S − G)
26
+ * and every other coefficient is zero — Unity's raw coefficients, which its
27
+ * shader constants pack as SHA = (c3, c1, c2, c0 − c6), SHB = (c4, c5, 3c6, c7),
28
+ * SHC = c8. Checked against the game's recorded block for (0.8, 0.5, 0.3).
29
+ */
30
+ export function trilightProbe(sky, equator, ground) {
31
+ const out = [];
32
+ for (let k = 0; k < 3; k++) {
33
+ const S = srgbToLinear(sky[k]), E = srgbToLinear(equator[k]), G = srgbToLinear(ground[k]);
34
+ const ring = 2 * E - S - G;
35
+ out.push([(S + 4 * E + G) / 6, (Math.sqrt(6) / 9) * (S - G), 0, 0, 0, 0, (5 / 192) * ring, 0, (5 / 64) * ring]);
36
+ }
37
+ return out;
38
+ }
39
+ /** Unity's shader constants from raw coefficients (channel, index). */
40
+ export function probeConstants(c) {
41
+ const ch = ["r", "g", "b"];
42
+ const o = {};
43
+ for (let k = 0; k < 3; k++) {
44
+ const s = c[k];
45
+ o[`SHA${ch[k]}`] = [s[3], s[1], s[2], s[0] - s[6]];
46
+ o[`SHB${ch[k]}`] = [s[4], s[5], s[6] * 3, s[7]];
47
+ }
48
+ o.SHC = [c[0][8], c[1][8], c[2][8], 1];
49
+ return o;
50
+ }
51
+ /**
52
+ * sim_TowardMatrix (LightingFeatures.SetupTowardMatrix): the frame the
53
+ * character light is expressed in — the camera's backward direction flattened
54
+ * to the ground, as columns (up × d, up, d). Taken immediately, as the DLC
55
+ * poster sets CameraExtension.shadowRotationImmediately.
56
+ */
57
+ export function towardMatrix(cameraForward) {
58
+ const x = -cameraForward[0], z = -cameraForward[2];
59
+ const len = Math.hypot(x, z);
60
+ const d = len > 1e-5 ? [x / len, 0, z / len] : [0, 0, 0];
61
+ return Float32Array.from([d[2], 0, -d[0], 0, 0, 1, 0, 0, d[0], d[1], d[2], 0, 0, 0, 0, 1]);
62
+ }
63
+ /** CharacterGlobalParams.Execute: the outline's resolution allowance. */
64
+ export function outlineMaxOffsetMultiplier(pixelWidth) {
65
+ return Math.max(pixelWidth / 768 - 1, 0);
66
+ }
67
+ /**
68
+ * The character's property block (CharacterEffect.LateUpdate with the default
69
+ * look: no dissolve, dither, interference), by name.
70
+ *
71
+ * `head` is the head bone's skinning matrix (its pose relative to the bind
72
+ * pose, engine units) and `headRest` its rest position; `scale` engine units
73
+ * per game unit.
74
+ */
75
+ export function characterBlock(rig, head, headRest, scale) {
76
+ const o = {};
77
+ // _World2Face: the face frame's world-to-local, in game units. The face is
78
+ // the head's pose applied to a frame at the head's rest position looking −Z.
79
+ const faceRest = Float32Array.from([-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, headRest[0], headRest[1], headRest[2], 1]);
80
+ const face = head ? mul4(head, faceRest) : faceRest;
81
+ o._World2Face = mul4(invert4(face), gameToEngine(scale));
82
+ o._ExtralEmissionColor = rig.emissionColor;
83
+ let inner = rig.fillInner, outer = rig.fillOuter;
84
+ inner = [inner[0], inner[1], inner[2], inner[3] * rig.fillRatio];
85
+ outer = [outer[0], outer[1], outer[2], outer[3] * rig.fillRatio];
86
+ o._FillOuter = outer;
87
+ o._FillInner = inner;
88
+ o._FillClamp = 0;
89
+ o._FillSoft = 0;
90
+ o._DissolveFactor = 0;
91
+ // inclination about the horizon, azimuth negated — float math as the game's
92
+ const incl = (rig.localLightInclination * Math.PI) / 180;
93
+ const az = (-rig.localLightAzimuth * Math.PI) / 180;
94
+ const c = Math.cos(incl);
95
+ o._LocalLightDir = [Math.sin(az) * -c, Math.sin(incl), Math.cos(az) * c, 0];
96
+ const k = rig.localLightIntensity;
97
+ o._LocalLightColor = [rig.localLightColor[0] * k, rig.localLightColor[1] * k, rig.localLightColor[2] * k, rig.localLightColor[3] * k];
98
+ o._UseFaceReceiveShadow = rig.faceReceiveShadow ? 1 : 0;
99
+ o._DisableFaceSDFShadow = rig.disableFaceSDFShadow ? 1 : 0;
100
+ o._RimlightColor = rig.rimlightColor;
101
+ o._RimlightThreshold = rig.rimlightThreshold;
102
+ o._RimlightFade = rig.rimlightFade;
103
+ o._RimlightRange = rig.rimlightRange;
104
+ const zr = rig.rimlightInclination;
105
+ const sr = Math.sqrt(1 - zr * zr);
106
+ const a1 = rig.rimlightAzimuth1 * Math.PI, a2 = rig.rimlightAzimuth2 * Math.PI;
107
+ o._RimlightDir1 = [Math.cos(a1) * sr, Math.sin(a1) * sr, zr, 0];
108
+ o._RimlightDir2 = [Math.cos(a2) * sr, Math.sin(a2) * sr, zr, 0];
109
+ o._ZOffset_Unit = rig.characterLayer;
110
+ o._DitherAlpha = 1;
111
+ // EnvironmentEffect: the character's own ambient, in both names it is read by
112
+ const sh = probeConstants(trilightProbe(rig.skyColor, rig.equatorColor, rig.groundColor));
113
+ for (const [n, v] of Object.entries(sh)) {
114
+ o[`unity_${n}`] = v;
115
+ o[`_Replica_${n}`] = v;
116
+ }
117
+ // CharacterEffect.OnEnable: every character renderer on rendering layer 0x40000001
118
+ o.unity_RenderingLayer = new Uint32Array([0x40000001, 0, 0, 0]);
119
+ return o;
120
+ }
@@ -0,0 +1,93 @@
1
+ import type { NativeValue } from "./host";
2
+ export type Mat4Like = ArrayLike<number>;
3
+ export type UnityLight = {
4
+ /** World position, engine units. */
5
+ position: [number, number, number];
6
+ /** Reach, engine units. */
7
+ radius: number;
8
+ /** Linear colour times intensity. */
9
+ color: [number, number, number];
10
+ /** Unit aim, away from the light; zero for a point light. */
11
+ aim: [number, number, number];
12
+ cosOuter: number;
13
+ cosInner: number;
14
+ /** Rendering-layer bits it reaches. */
15
+ layers: number;
16
+ /** What the game's pipeline packs beside a light it lit with, when the light
17
+ * came from a game stage (stage_native.py): spot direction with w = 1 /
18
+ * its sphere's radius (the area term reads it), colour alpha, Extra,
19
+ * WorldToLight and type — all in the game's world, used as they are. */
20
+ unity?: {
21
+ spotDir: number[];
22
+ colorW: number;
23
+ extra: number[];
24
+ worldToLight: number[] | null;
25
+ lightType: number;
26
+ simSpotDir?: number[] | null;
27
+ simColorW?: number;
28
+ };
29
+ };
30
+ export type UnityFrameInput = {
31
+ /** Engine units per game unit. */
32
+ scale: number;
33
+ view: Mat4Like;
34
+ proj: Mat4Like;
35
+ eye: [number, number, number];
36
+ near: number;
37
+ far: number;
38
+ width: number;
39
+ height: number;
40
+ /** Scene clock and its step, seconds. */
41
+ time: number;
42
+ dt: number;
43
+ /** The direction the main light travels, unit; null for none. */
44
+ sunDirection: [number, number, number] | null;
45
+ sunColor: [number, number, number];
46
+ sunShadow: number;
47
+ lights: UnityLight[];
48
+ /** Ambient as the engine's folded SH9 (27 floats, ibl.ts), or null for flat. */
49
+ ambientSH: ArrayLike<number> | null;
50
+ ambientFlat: [number, number, number];
51
+ /** The cascades as the engine drew them: render view-projections (engine
52
+ * units, the atlas's own depth convention), their tiles, and the spheres
53
+ * they were fitted to. */
54
+ shadow: {
55
+ viewProj: Mat4Like[];
56
+ tiles: [number, number][];
57
+ tileSize: number;
58
+ atlasSize: number;
59
+ spheres: {
60
+ center: [number, number, number];
61
+ radius: number;
62
+ }[];
63
+ } | null;
64
+ /** What a loaded game stage sets for its scene (stage.ts): fog, tint, ambient
65
+ * SH, the environment cube's numbers — the pipeline's per-scene globals, in
66
+ * place of the defaults below. Never camera, time, lights or shadows. */
67
+ settings?: Record<string, NativeValue> | null;
68
+ };
69
+ /** Column-major 4x4 multiply. */
70
+ export declare function mul4(a: Mat4Like, b: Mat4Like): Float32Array;
71
+ export declare function invert4(m: Mat4Like): Float32Array;
72
+ /** Game world -> engine world: `s` PMX per unit, half a turn about Y. */
73
+ export declare function gameToEngine(s: number): Float32Array;
74
+ /** Engine world -> game world. */
75
+ export declare function engineToGame(s: number): Float32Array;
76
+ /** An engine point in the game's world. */
77
+ export declare function gamePoint(p: ArrayLike<number>, s: number): [number, number, number];
78
+ /** An engine direction in the game's world. */
79
+ export declare function gameDir(d: ArrayLike<number>): [number, number, number];
80
+ /** Uniform scale, as a column-major matrix. */
81
+ export declare function scale4(s: number): Float32Array;
82
+ /** The projection with clip y negated — undone by naga's own negation. */
83
+ export declare function flipY(p: Mat4Like): Float32Array;
84
+ /**
85
+ * The engine's folded SH9 (ibl.ts: c0 + c1·y + c2·z + c3·x + c4·xy + c5·yz +
86
+ * c6·(3z²−1) + c7·xz + c8·(x²−y²)) as Unity's seven shader constants —
87
+ * SHA = (x, y, z, constant), SHB = (xy, yz, z², zx), SHC = (x²−y²) — the same
88
+ * polynomial, regrouped.
89
+ */
90
+ export declare function unitySH(sh: ArrayLike<number> | null, flat: [number, number, number]): Record<string, number[]>;
91
+ /** Everything the frame sets, by name. */
92
+ export declare function unityFrameGlobals(f: UnityFrameInput): Record<string, NativeValue>;
93
+ //# sourceMappingURL=globals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../../src/unity/globals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,340 @@
1
+ // The game pipeline's per-frame globals, computed from the engine's scene.
2
+ //
3
+ // The game's shaders read what its render pipeline (UnityEngine.Rendering.
4
+ // Replica / ReplicaExt, decompiled from GameAssembly.dll) sets before a camera
5
+ // renders: the camera, the main light, the Forward+ light table and its bins,
6
+ // the cascades, the ambient SH, fog, time. ag-rip's AGSimPipeline.cs and
7
+ // AGSimShadows.cs reproduce those calls in Unity, method by method, and the
8
+ // compare page proved them against the game's frames; this is that code again,
9
+ // fed by the engine instead of by a Unity scene.
10
+ //
11
+ // SPACE. The game's shaders work in the game's world, and the engine's scene
12
+ // is that world as ag-rip exports it to MMD: `scale` PMX units per game unit
13
+ // (8: the one-scene-scale rule) and turned half round Y — game (x, y, z) is
14
+ // PMX (-x, y, -z), the convention its camera and motion VMDs and its stages
15
+ // share. Everything positional is handed over through that map (gameToEngine
16
+ // and back) — the camera, the lights and their reach, the cascades — so a
17
+ // shader's distances (outline widths, light ranges, fog) and directions mean
18
+ // what they meant in the game. A half turn keeps handedness: both are
19
+ // left-handed and Y-up.
20
+ //
21
+ // CLIP SPACE. naga negates y on the way out of every vertex shader (Vulkan to
22
+ // WebGPU), so the projection handed to the shaders is the engine's with y
23
+ // flipped: the two flips cancel and a native draw lands exactly where the
24
+ // engine's own draw of the same triangle would, on the same depth.
25
+ const PLUS_MAX = 255;
26
+ /** Column-major 4x4 multiply. */
27
+ export function mul4(a, b) {
28
+ const o = new Float32Array(16);
29
+ for (let c = 0; c < 4; c++)
30
+ for (let r = 0; r < 4; r++) {
31
+ let s = 0;
32
+ for (let k = 0; k < 4; k++)
33
+ s += a[k * 4 + r] * b[c * 4 + k];
34
+ o[c * 4 + r] = s;
35
+ }
36
+ return o;
37
+ }
38
+ export function invert4(m) {
39
+ const a = m;
40
+ const o = new Float32Array(16);
41
+ const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
42
+ const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
43
+ const b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10;
44
+ const b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12;
45
+ const b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30;
46
+ const b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32;
47
+ let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
48
+ if (!det)
49
+ return Float32Array.from([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
50
+ det = 1 / det;
51
+ o[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
52
+ o[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
53
+ o[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
54
+ o[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
55
+ o[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
56
+ o[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
57
+ o[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
58
+ o[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
59
+ o[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
60
+ o[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
61
+ o[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
62
+ o[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
63
+ o[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
64
+ o[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
65
+ o[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
66
+ o[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
67
+ return o;
68
+ }
69
+ /** Game world -> engine world: `s` PMX per unit, half a turn about Y. */
70
+ export function gameToEngine(s) {
71
+ return Float32Array.from([-s, 0, 0, 0, 0, s, 0, 0, 0, 0, -s, 0, 0, 0, 0, 1]);
72
+ }
73
+ /** Engine world -> game world. */
74
+ export function engineToGame(s) {
75
+ return Float32Array.from([-1 / s, 0, 0, 0, 0, 1 / s, 0, 0, 0, 0, -1 / s, 0, 0, 0, 0, 1]);
76
+ }
77
+ /** An engine point in the game's world. */
78
+ export function gamePoint(p, s) {
79
+ return [-p[0] / s, p[1] / s, -p[2] / s];
80
+ }
81
+ /** An engine direction in the game's world. */
82
+ export function gameDir(d) {
83
+ return [-d[0], d[1], -d[2]];
84
+ }
85
+ /** Uniform scale, as a column-major matrix. */
86
+ export function scale4(s) {
87
+ return Float32Array.from([s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1]);
88
+ }
89
+ /** The projection with clip y negated — undone by naga's own negation. */
90
+ export function flipY(p) {
91
+ const o = Float32Array.from(p);
92
+ for (let c = 0; c < 4; c++)
93
+ o[c * 4 + 1] = -p[c * 4 + 1];
94
+ return o;
95
+ }
96
+ /**
97
+ * The engine's folded SH9 (ibl.ts: c0 + c1·y + c2·z + c3·x + c4·xy + c5·yz +
98
+ * c6·(3z²−1) + c7·xz + c8·(x²−y²)) as Unity's seven shader constants —
99
+ * SHA = (x, y, z, constant), SHB = (xy, yz, z², zx), SHC = (x²−y²) — the same
100
+ * polynomial, regrouped.
101
+ */
102
+ export function unitySH(sh, flat) {
103
+ const out = {};
104
+ const ch = ["r", "g", "b"];
105
+ const c = (i, k) => (sh ? sh[i * 3 + k] : i === 0 ? flat[k] : 0);
106
+ for (let k = 0; k < 3; k++) {
107
+ out[`_Replica_SHA${ch[k]}`] = [c(3, k), c(1, k), c(2, k), c(0, k) - c(6, k)];
108
+ out[`_Replica_SHB${ch[k]}`] = [c(4, k), c(5, k), 3 * c(6, k), c(7, k)];
109
+ }
110
+ out._Replica_SHC = [c(8, 0), c(8, 1), c(8, 2), 1];
111
+ return out;
112
+ }
113
+ /** Everything the frame sets, by name. */
114
+ export function unityFrameGlobals(f) {
115
+ const s = f.scale;
116
+ const g = {};
117
+ // ---- the camera (UnityPerFrame), in game units
118
+ // Unity's own pair, so what a shader rebuilds from them means what it did
119
+ // in the game: the view in game units, right-handed and looking down -Z;
120
+ // the projection on game-unit depths (the engine's, conjugated by the scale
121
+ // and the z flip, and divided through by s, which leaves the same NDC). Their
122
+ // product puts every vertex exactly where the engine's own draw of it lands.
123
+ const zFlip = Float32Array.from([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1]);
124
+ const view = mul4(zFlip, mul4(scale4(1 / s), mul4(f.view, gameToEngine(s))));
125
+ const toEngineView = mul4(scale4(s), zFlip);
126
+ const proj = flipY(mul4(f.proj, toEngineView));
127
+ for (let i = 0; i < 16; i++)
128
+ proj[i] /= s;
129
+ const vp = mul4(proj, view);
130
+ const w = Math.max(1, f.width);
131
+ const h = Math.max(1, f.height);
132
+ const near = f.near / s;
133
+ const far = f.far / s;
134
+ g._WorldSpaceCameraPos = [...gamePoint(f.eye, s), 1];
135
+ g.unity_MatrixV = view;
136
+ g.unity_MatrixInvV = invert4(view);
137
+ g.unity_MatrixP = proj;
138
+ g.glstate_matrix_projection = proj;
139
+ g.unity_MatrixVP = vp;
140
+ g._NonJitteredViewProjMatrix = vp;
141
+ // x = -1: a flipped projection, as Unity's into a texture.
142
+ g._ProjectionParams = [-1, near, far, 1 / far];
143
+ g._ScreenParams = [w, h, 1 + 1 / w, 1 + 1 / h];
144
+ g._ScaledScreenParams = [w, h, 1 + 1 / w, 1 + 1 / h];
145
+ g._ZBufferParams = [-1 + far / near, 1, (-1 + far / near) / far, 1 / far];
146
+ g.unity_OrthoParams = [0, 0, 0, 0];
147
+ const t = f.time;
148
+ g._Time = [t / 20, t, t * 2, t * 3];
149
+ g._SinTime = [Math.sin(t / 8), Math.sin(t / 4), Math.sin(t / 2), Math.sin(t)];
150
+ g._CosTime = [Math.cos(t / 8), Math.cos(t / 4), Math.cos(t / 2), Math.cos(t)];
151
+ const dt = Math.max(f.dt, 1e-4);
152
+ g.unity_DeltaTime = [dt, 1 / dt, dt, 1 / dt];
153
+ // UpdateRenderSettings: sim_Time = (t - floor t, floor t)
154
+ g.sim_Time = [t - Math.floor(t), Math.floor(t), 0, 0];
155
+ g.identity_matrix = scale4(1);
156
+ // ---- LightingFeatures.SetupLightings / PlusLighting SetupMainLightConstants
157
+ if (f.sunDirection) {
158
+ const d = gameDir(f.sunDirection);
159
+ g.SimMainLightDir = [-d[0], -d[1], -d[2], 0];
160
+ g.SimMainLightColor = [...f.sunColor, 1];
161
+ g.SimMainLightColorNoInt = [...f.sunColor, 1];
162
+ // As the game's pipeline leaves it (recorded): the way the light travels.
163
+ g._MainLightPosition = [d[0], d[1], d[2], 0];
164
+ g.sim_ShadowLightDirection = [-d[0], -d[1], -d[2], 0];
165
+ g._MainLightColor = [...f.sunColor, 1];
166
+ }
167
+ else {
168
+ g.SimMainLightDir = [0, 0, 0, 0];
169
+ g.SimMainLightColor = [0, 0, 0, 0];
170
+ g.SimMainLightColorNoInt = [0, 0, 0, 0];
171
+ g._MainLightPosition = [0, 1, 0, 0];
172
+ g._MainLightColor = [0, 0, 0, 0];
173
+ }
174
+ // ---- ForwardFeature.SetupRenderFeature, UpdateRenderSettings: a scene
175
+ // with no stage of the game's has no settings of its own — the defaults the
176
+ // game's SceneSetting starts from.
177
+ g.SimSceneTint = [1, 1, 1, 1];
178
+ g._ReceiveSceneShadow = 1;
179
+ g.sim_FogRotation = 0;
180
+ g.sim_FogInverseRootRotationMatrix = scale4(1);
181
+ g.sim_FogColor = [0, 0, 0, 0];
182
+ g.sim_FogColor2 = [0, 0, 0, 0];
183
+ g.sim_FogParams = [0, 1, 0, 1];
184
+ g.sim_FogDirectionalColor = [0, 0, 0, 0];
185
+ g.sim_FogDirectionalDir = [0, 0, 1, 0];
186
+ g.sim_FogDirectionalFalloff = 0;
187
+ g.sim_DYN_FOG_LINEAR_Variable = 0;
188
+ g.sim_DynFogColor = [0, 0, 0, 0];
189
+ g.sim_DynFogParams = [0, 1, 0, 1];
190
+ g._ProbeLightingBase = [1, 1, 1, 1];
191
+ g._ProbeLightingScale = 0;
192
+ g._AcceptLightProbe = 0;
193
+ g.sim_VertexAmbientScale = 1;
194
+ g._EnableShadowScatter = 0;
195
+ g._ShadowScatterColor = [0, 0, 0, 0];
196
+ g._ShadowScatterRange = 0;
197
+ // realtimeShadowColor's default grey, made linear (ConvertSRGBToActiveColorSpace)
198
+ g._SubtractiveShadowColor = [0.2158605, 0.2158605, 0.2158605, 1];
199
+ g._AmbientOcclusionParam = [0, 0, 0, 0];
200
+ g.sim_EnvCubeScale = 0;
201
+ g.sim_EnvCube_BoxMin = [0, 0, 0, 0];
202
+ g.sim_EnvCube_BoxMax = [0, 0, 0, 0];
203
+ g.sim_EnvCube_ProbePosition = [0, 0, 0, 0];
204
+ // ---- SetupEnvironmentLighting: the scene's ambient SH
205
+ // The half turn negates x and z: the terms odd in either (z, x, xy, yz) flip.
206
+ const sh = f.ambientSH ? Array.from(f.ambientSH) : null;
207
+ if (sh)
208
+ for (const i of [2, 3, 4, 5])
209
+ for (let k = 0; k < 3; k++)
210
+ sh[i * 3 + k] = -sh[i * 3 + k];
211
+ Object.assign(g, unitySH(sh, f.ambientFlat));
212
+ // ---- a game stage's own settings over all of the above
213
+ if (f.settings)
214
+ Object.assign(g, f.settings);
215
+ // ---- PlusLightingFeature: the light table, one tile and one depth bin
216
+ // holding every light (binning only culls; the shader applies each range)
217
+ const n = Math.min(f.lights.length, PLUS_MAX);
218
+ const pos = [], col = [], att = [], dir = [];
219
+ const extra = [], w2l = [];
220
+ const simDir = [], simColorW = [];
221
+ const types = new Float32Array(PLUS_MAX);
222
+ const masks = new Uint32Array(PLUS_MAX);
223
+ const ident = scale4(1);
224
+ for (let i = 0; i < PLUS_MAX; i++) {
225
+ const l = f.lights[i];
226
+ if (i >= n || !l) {
227
+ pos.push([0, 0, 0, 0]);
228
+ col.push([0, 0, 0, 0]);
229
+ att.push([0, 1, 0, 1]);
230
+ dir.push([0, 0, 0, 0]);
231
+ extra.push([0, 0, 0, 0]);
232
+ w2l.push(ident);
233
+ continue;
234
+ }
235
+ const spot = l.aim[0] !== 0 || l.aim[1] !== 0 || l.aim[2] !== 0;
236
+ const r = l.radius / s;
237
+ const r2 = r * r;
238
+ // LightInfo.GetPunctualLightDistanceAttenuation / GetSpotAngleAttenuation
239
+ let z = 0;
240
+ let ww = 1;
241
+ if (spot) {
242
+ const inv = 1 / Math.max(l.cosInner - l.cosOuter, 0.001);
243
+ z = inv;
244
+ ww = -l.cosOuter * inv;
245
+ }
246
+ pos.push([...gamePoint(l.position, s), 1]);
247
+ // The engine's lamp brightness is per its own unit; the inverse square in
248
+ // game units is s² larger at the same place, so the colour carries 1/s².
249
+ col.push([l.color[0] / (s * s), l.color[1] / (s * s), l.color[2] / (s * s), 1]);
250
+ att.push([1 / Math.max(r2, 1e-4), -r2 / (r2 * 0.64 - r2), z, ww]);
251
+ const u = l.unity;
252
+ if (u) {
253
+ col[col.length - 1][3] = u.colorW;
254
+ dir.push(u.spotDir.slice(0, 4));
255
+ extra.push(u.extra.slice(0, 4));
256
+ w2l.push(u.worldToLight ? Float32Array.from(u.worldToLight) : ident);
257
+ simDir.push((u.simSpotDir ?? u.spotDir).slice(0, 4));
258
+ simColorW.push(u.simColorW ?? 1);
259
+ types[i] = u.lightType;
260
+ }
261
+ else {
262
+ // The pipeline's own defaults for a light it did not author: forward
263
+ // along the aim, w = 1 / the 0.1 sphere every game lamp of these stages has.
264
+ const a = gameDir(l.aim);
265
+ dir.push(spot ? [...a, 10] : [0, 0, 1, 10]);
266
+ extra.push([0, 0, 0, 0]);
267
+ w2l.push(ident);
268
+ simDir.push(spot ? [...a, 10] : [0, 0, 1, 10]);
269
+ simColorW.push(1);
270
+ types[i] = spot ? 0 : 2;
271
+ }
272
+ masks[i] = l.layers >>> 0;
273
+ }
274
+ g._AdditionalLightsPosition = pos;
275
+ g._AdditionalLightsColor = col;
276
+ g._AdditionalLightsAttenuation = att;
277
+ g._AdditionalLightsSpotDir = dir;
278
+ g._AdditionalLightsExtra = extra;
279
+ g._AdditionalLightsWorldToLights = w2l;
280
+ g._AdditionalLightsLightTypes = types;
281
+ g._AdditionalLightsLayerMasks = masks;
282
+ g._AdditionalLightsCookieEnableBits = new Float32Array(8);
283
+ const words = Math.max(1, Math.ceil(n / 32));
284
+ const zb = new Uint32Array(4096);
285
+ const tiles = new Uint32Array(16384);
286
+ zb[0] = n > 0 ? ((n - 1) << 16) >>> 0 : 0xffff;
287
+ for (let k = 0; k < words; k++) {
288
+ const bits = Math.min(Math.max(n - k * 32, 0), 32);
289
+ const m = bits >= 32 ? 0xffffffff : ((1 << bits) >>> 0) - 1;
290
+ zb[1 + k] = m >>> 0;
291
+ tiles[k] = m >>> 0;
292
+ }
293
+ // LightingFeatures' own copy for the per-vertex lights: the first 16, colour
294
+ // without the intensity in w.
295
+ g.SimAdditionalLightPosition = pos.slice(0, 16);
296
+ g.SimAdditionalLightColor = col
297
+ .slice(0, 16)
298
+ .map((c, i) => (c[0] || c[1] || c[2] ? [c[0], c[1], c[2], simColorW[i] ?? 1] : [0, 0, 0, 0]));
299
+ g.SimAdditionalLightAttenuation = att.slice(0, 16);
300
+ g.SimAdditionalLightSpotDir = simDir.slice(0, 16).concat(dir.slice(simDir.length, 16));
301
+ g._PlusLighting_ZBins = zb;
302
+ g._PlusLighting_Tiles = tiles;
303
+ g._PlusLightingParams0 = [0, 0, 1, words];
304
+ g._PlusLightingParams1 = [1000, 0, 0, 0];
305
+ // ---- the main light's cascades, sampled from the engine's atlas
306
+ if (f.shadow) {
307
+ const sh = f.shadow;
308
+ const unit = sh.tileSize / sh.atlasSize;
309
+ const w2s = [];
310
+ for (let i = 0; i < 4; i++) {
311
+ const vpe = sh.viewProj[i];
312
+ const tile = sh.tiles[i];
313
+ if (!vpe || !tile) {
314
+ w2s.push(scale4(0));
315
+ continue;
316
+ }
317
+ // ndc -> the tile's uv (y down, as the atlas is addressed), depth as drawn
318
+ const tx = tile[0] / sh.atlasSize;
319
+ const ty = tile[1] / sh.atlasSize;
320
+ const toTile = Float32Array.from([0.5 * unit, 0, 0, 0, 0, -0.5 * unit, 0, 0, 0, 0, 1, 0, 0.5 * unit + tx, 0.5 * unit + ty, 0, 1]);
321
+ w2s.push(mul4(toTile, mul4(vpe, gameToEngine(s))));
322
+ }
323
+ g._MainLightWorldToShadow = w2s;
324
+ for (let i = 0; i < 4; i++) {
325
+ const sp = sh.spheres[i];
326
+ g[`_CascadeShadowSplitSpheres${i}`] = sp ? [...gamePoint(sp.center, s), sp.radius / s] : [0, 0, 0, 0];
327
+ }
328
+ const r = (i) => (sh.spheres[i] ? (sh.spheres[i].radius / s) ** 2 : 0);
329
+ g._CascadeShadowSplitSphereRadii = [r(0), r(1), r(2), r(3)];
330
+ const half = 0.5 / sh.atlasSize;
331
+ g._MainLightShadowOffset0 = [-half, -half, half, -half];
332
+ g._MainLightShadowOffset1 = [-half, half, half, half];
333
+ g._MainLightShadowmapSize = [1 / sh.atlasSize, 1 / sh.atlasSize, sh.atlasSize, sh.atlasSize];
334
+ g._MainLightShadowParams = [f.sunShadow, 1, 0, 0];
335
+ }
336
+ else {
337
+ g._MainLightShadowParams = [0, 0, 0, 0];
338
+ }
339
+ return g;
340
+ }