reze-engine 0.63.0 → 0.65.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/engine.d.ts +226 -96
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1187 -732
- package/dist/graph/compile.d.ts.map +1 -1
- package/dist/graph/compile.js +4 -1
- package/dist/graph/registry.d.ts +3 -0
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +133 -0
- package/dist/graph/schema.d.ts +12 -0
- package/dist/graph/schema.d.ts.map +1 -1
- package/dist/graph/slots.d.ts.map +1 -1
- package/dist/graph/slots.js +15 -18
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +10 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +15 -1
- package/dist/outline-normals.d.ts +12 -0
- package/dist/outline-normals.d.ts.map +1 -0
- package/dist/outline-normals.js +100 -0
- package/dist/pmx-loader.d.ts +3 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +7 -2
- package/dist/shaders/lights.d.ts +3 -2
- package/dist/shaders/lights.d.ts.map +1 -1
- package/dist/shaders/lights.js +16 -3
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +65 -56
- package/dist/shaders/materials/nodes.d.ts +1 -1
- package/dist/shaders/materials/nodes.d.ts.map +1 -1
- package/dist/shaders/materials/nodes.js +210 -0
- package/dist/shaders/passes/bloom.d.ts +4 -3
- package/dist/shaders/passes/bloom.d.ts.map +1 -1
- package/dist/shaders/passes/bloom.js +58 -87
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +122 -64
- package/dist/shaders/passes/cull.d.ts +1 -1
- package/dist/shaders/passes/cull.d.ts.map +1 -1
- package/dist/shaders/passes/cull.js +7 -3
- package/dist/shaders/passes/ground.d.ts.map +1 -1
- package/dist/shaders/passes/ground.js +32 -55
- package/dist/shaders/passes/mirror.d.ts +18 -12
- package/dist/shaders/passes/mirror.d.ts.map +1 -1
- package/dist/shaders/passes/mirror.js +59 -13
- package/dist/shaders/passes/outline.d.ts.map +1 -1
- package/dist/shaders/passes/outline.js +71 -14
- package/dist/shaders/passes/particles.d.ts +1 -1
- package/dist/shaders/passes/particles.js +1 -1
- package/dist/shaders/scene-light-api.d.ts +18 -10
- package/dist/shaders/scene-light-api.d.ts.map +1 -1
- package/dist/shaders/scene-light-api.js +58 -46
- package/dist/shadow-cascades.d.ts +47 -20
- package/dist/shadow-cascades.d.ts.map +1 -1
- package/dist/shadow-cascades.js +36 -13
- package/dist/unity/character.d.ts +59 -0
- package/dist/unity/character.d.ts.map +1 -0
- package/dist/unity/character.js +120 -0
- package/dist/unity/globals.d.ts +93 -0
- package/dist/unity/globals.d.ts.map +1 -0
- package/dist/unity/globals.js +340 -0
- package/dist/unity/host.d.ts +152 -0
- package/dist/unity/host.d.ts.map +1 -0
- package/dist/unity/host.js +529 -0
- package/dist/unity/looks.d.ts +93 -0
- package/dist/unity/looks.d.ts.map +1 -0
- package/dist/unity/looks.js +312 -0
- package/dist/unity/skin.d.ts +9 -0
- package/dist/unity/skin.d.ts.map +1 -0
- package/dist/unity/skin.js +120 -0
- package/dist/unity/stage.d.ts +155 -0
- package/dist/unity/stage.d.ts.map +1 -0
- package/dist/unity/stage.js +410 -0
- package/dist/unity/state.d.ts +22 -0
- package/dist/unity/state.d.ts.map +1 -0
- package/dist/unity/state.js +170 -0
- package/package.json +1 -1
- package/src/engine.ts +1535 -964
- package/src/graph/compile.ts +3 -1
- package/src/graph/registry.ts +142 -0
- package/src/graph/schema.ts +9 -0
- package/src/graph/slots.ts +15 -18
- package/src/index.ts +12 -0
- package/src/model.ts +18 -1
- package/src/outline-normals.ts +110 -0
- package/src/pmx-loader.ts +8 -2
- package/src/shaders/lights.ts +16 -3
- package/src/shaders/materials/common.ts +65 -57
- package/src/shaders/materials/nodes.ts +210 -0
- package/src/shaders/passes/bloom.ts +59 -87
- package/src/shaders/passes/composite.ts +122 -64
- package/src/shaders/passes/cull.ts +7 -3
- package/src/shaders/passes/ground.ts +32 -55
- package/src/shaders/passes/mirror.ts +60 -13
- package/src/shaders/passes/outline.ts +71 -14
- package/src/shaders/passes/particles.ts +1 -1
- package/src/shaders/scene-light-api.ts +59 -46
- package/src/shadow-cascades.ts +61 -32
- package/src/unity/character.ts +169 -0
- package/src/unity/globals.ts +443 -0
- package/src/unity/host.ts +630 -0
- package/src/unity/looks.ts +391 -0
- package/src/unity/skin.ts +131 -0
- package/src/unity/stage.ts +604 -0
- package/src/unity/state.ts +172 -0
- package/src/shaders/agx-lut.ts +0 -30
|
@@ -0,0 +1,443 @@
|
|
|
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
|
+
|
|
26
|
+
import type { NativeValue } from "./host"
|
|
27
|
+
|
|
28
|
+
export type Mat4Like = ArrayLike<number>
|
|
29
|
+
|
|
30
|
+
export type UnityLight = {
|
|
31
|
+
/** World position, engine units. */
|
|
32
|
+
position: [number, number, number]
|
|
33
|
+
/** Reach, engine units. */
|
|
34
|
+
radius: number
|
|
35
|
+
/** Linear colour times intensity. */
|
|
36
|
+
color: [number, number, number]
|
|
37
|
+
/** Unit aim, away from the light; zero for a point light. */
|
|
38
|
+
aim: [number, number, number]
|
|
39
|
+
cosOuter: number
|
|
40
|
+
cosInner: number
|
|
41
|
+
/** Rendering-layer bits it reaches. */
|
|
42
|
+
layers: number
|
|
43
|
+
/** What the game's pipeline packs beside a light it lit with, when the light
|
|
44
|
+
* came from a game stage (stage_native.py): spot direction with w = 1 /
|
|
45
|
+
* its sphere's radius (the area term reads it), colour alpha, Extra,
|
|
46
|
+
* WorldToLight and type — all in the game's world, used as they are. */
|
|
47
|
+
unity?: {
|
|
48
|
+
spotDir: number[]
|
|
49
|
+
colorW: number
|
|
50
|
+
extra: number[]
|
|
51
|
+
worldToLight: number[] | null
|
|
52
|
+
lightType: number
|
|
53
|
+
simSpotDir?: number[] | null
|
|
54
|
+
simColorW?: number
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type UnityFrameInput = {
|
|
59
|
+
/** Engine units per game unit. */
|
|
60
|
+
scale: number
|
|
61
|
+
view: Mat4Like
|
|
62
|
+
proj: Mat4Like
|
|
63
|
+
eye: [number, number, number]
|
|
64
|
+
near: number
|
|
65
|
+
far: number
|
|
66
|
+
width: number
|
|
67
|
+
height: number
|
|
68
|
+
/** Scene clock and its step, seconds. */
|
|
69
|
+
time: number
|
|
70
|
+
dt: number
|
|
71
|
+
/** The direction the main light travels, unit; null for none. */
|
|
72
|
+
sunDirection: [number, number, number] | null
|
|
73
|
+
sunColor: [number, number, number]
|
|
74
|
+
sunShadow: number
|
|
75
|
+
lights: UnityLight[]
|
|
76
|
+
/** Ambient as the engine's folded SH9 (27 floats, ibl.ts), or null for flat. */
|
|
77
|
+
ambientSH: ArrayLike<number> | null
|
|
78
|
+
ambientFlat: [number, number, number]
|
|
79
|
+
/** The cascades as the engine drew them: render view-projections (engine
|
|
80
|
+
* units, the atlas's own depth convention), their tiles, and the spheres
|
|
81
|
+
* they were fitted to. */
|
|
82
|
+
shadow: {
|
|
83
|
+
viewProj: Mat4Like[]
|
|
84
|
+
tiles: [number, number][]
|
|
85
|
+
tileSize: number
|
|
86
|
+
atlasSize: number
|
|
87
|
+
spheres: { center: [number, number, number]; radius: number }[]
|
|
88
|
+
} | null
|
|
89
|
+
/** What a loaded game stage sets for its scene (stage.ts): fog, tint, ambient
|
|
90
|
+
* SH, the environment cube's numbers — the pipeline's per-scene globals, in
|
|
91
|
+
* place of the defaults below. Never camera, time, lights or shadows. */
|
|
92
|
+
settings?: Record<string, NativeValue> | null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const PLUS_MAX = 255
|
|
96
|
+
|
|
97
|
+
/** Column-major 4x4 multiply. */
|
|
98
|
+
export function mul4(a: Mat4Like, b: Mat4Like): Float32Array {
|
|
99
|
+
const o = new Float32Array(16)
|
|
100
|
+
for (let c = 0; c < 4; c++)
|
|
101
|
+
for (let r = 0; r < 4; r++) {
|
|
102
|
+
let s = 0
|
|
103
|
+
for (let k = 0; k < 4; k++) s += a[k * 4 + r] * b[c * 4 + k]
|
|
104
|
+
o[c * 4 + r] = s
|
|
105
|
+
}
|
|
106
|
+
return o
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function invert4(m: Mat4Like): Float32Array {
|
|
110
|
+
const a = m
|
|
111
|
+
const o = new Float32Array(16)
|
|
112
|
+
const a00 = a[0],
|
|
113
|
+
a01 = a[1],
|
|
114
|
+
a02 = a[2],
|
|
115
|
+
a03 = a[3],
|
|
116
|
+
a10 = a[4],
|
|
117
|
+
a11 = a[5],
|
|
118
|
+
a12 = a[6],
|
|
119
|
+
a13 = a[7]
|
|
120
|
+
const a20 = a[8],
|
|
121
|
+
a21 = a[9],
|
|
122
|
+
a22 = a[10],
|
|
123
|
+
a23 = a[11],
|
|
124
|
+
a30 = a[12],
|
|
125
|
+
a31 = a[13],
|
|
126
|
+
a32 = a[14],
|
|
127
|
+
a33 = a[15]
|
|
128
|
+
const b00 = a00 * a11 - a01 * a10,
|
|
129
|
+
b01 = a00 * a12 - a02 * a10,
|
|
130
|
+
b02 = a00 * a13 - a03 * a10
|
|
131
|
+
const b03 = a01 * a12 - a02 * a11,
|
|
132
|
+
b04 = a01 * a13 - a03 * a11,
|
|
133
|
+
b05 = a02 * a13 - a03 * a12
|
|
134
|
+
const b06 = a20 * a31 - a21 * a30,
|
|
135
|
+
b07 = a20 * a32 - a22 * a30,
|
|
136
|
+
b08 = a20 * a33 - a23 * a30
|
|
137
|
+
const b09 = a21 * a32 - a22 * a31,
|
|
138
|
+
b10 = a21 * a33 - a23 * a31,
|
|
139
|
+
b11 = a22 * a33 - a23 * a32
|
|
140
|
+
let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
|
|
141
|
+
if (!det) return Float32Array.from([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
|
|
142
|
+
det = 1 / det
|
|
143
|
+
o[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det
|
|
144
|
+
o[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det
|
|
145
|
+
o[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det
|
|
146
|
+
o[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det
|
|
147
|
+
o[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det
|
|
148
|
+
o[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det
|
|
149
|
+
o[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det
|
|
150
|
+
o[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det
|
|
151
|
+
o[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det
|
|
152
|
+
o[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det
|
|
153
|
+
o[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det
|
|
154
|
+
o[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det
|
|
155
|
+
o[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det
|
|
156
|
+
o[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det
|
|
157
|
+
o[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det
|
|
158
|
+
o[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det
|
|
159
|
+
return o
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Game world -> engine world: `s` PMX per unit, half a turn about Y. */
|
|
163
|
+
export function gameToEngine(s: number): Float32Array {
|
|
164
|
+
return Float32Array.from([-s, 0, 0, 0, 0, s, 0, 0, 0, 0, -s, 0, 0, 0, 0, 1])
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Engine world -> game world. */
|
|
168
|
+
export function engineToGame(s: number): Float32Array {
|
|
169
|
+
return Float32Array.from([-1 / s, 0, 0, 0, 0, 1 / s, 0, 0, 0, 0, -1 / s, 0, 0, 0, 0, 1])
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** An engine point in the game's world. */
|
|
173
|
+
export function gamePoint(p: ArrayLike<number>, s: number): [number, number, number] {
|
|
174
|
+
return [-p[0] / s, p[1] / s, -p[2] / s]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** An engine direction in the game's world. */
|
|
178
|
+
export function gameDir(d: ArrayLike<number>): [number, number, number] {
|
|
179
|
+
return [-d[0], d[1], -d[2]]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Uniform scale, as a column-major matrix. */
|
|
183
|
+
export function scale4(s: number): Float32Array {
|
|
184
|
+
return Float32Array.from([s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1])
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The projection with clip y negated — undone by naga's own negation. */
|
|
188
|
+
export function flipY(p: Mat4Like): Float32Array {
|
|
189
|
+
const o = Float32Array.from(p)
|
|
190
|
+
for (let c = 0; c < 4; c++) o[c * 4 + 1] = -p[c * 4 + 1]
|
|
191
|
+
return o
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The engine's folded SH9 (ibl.ts: c0 + c1·y + c2·z + c3·x + c4·xy + c5·yz +
|
|
196
|
+
* c6·(3z²−1) + c7·xz + c8·(x²−y²)) as Unity's seven shader constants —
|
|
197
|
+
* SHA = (x, y, z, constant), SHB = (xy, yz, z², zx), SHC = (x²−y²) — the same
|
|
198
|
+
* polynomial, regrouped.
|
|
199
|
+
*/
|
|
200
|
+
export function unitySH(sh: ArrayLike<number> | null, flat: [number, number, number]): Record<string, number[]> {
|
|
201
|
+
const out: Record<string, number[]> = {}
|
|
202
|
+
const ch = ["r", "g", "b"]
|
|
203
|
+
const c = (i: number, k: number) => (sh ? sh[i * 3 + k] : i === 0 ? flat[k] : 0)
|
|
204
|
+
for (let k = 0; k < 3; k++) {
|
|
205
|
+
out[`_Replica_SHA${ch[k]}`] = [c(3, k), c(1, k), c(2, k), c(0, k) - c(6, k)]
|
|
206
|
+
out[`_Replica_SHB${ch[k]}`] = [c(4, k), c(5, k), 3 * c(6, k), c(7, k)]
|
|
207
|
+
}
|
|
208
|
+
out._Replica_SHC = [c(8, 0), c(8, 1), c(8, 2), 1]
|
|
209
|
+
return out
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Everything the frame sets, by name. */
|
|
213
|
+
export function unityFrameGlobals(f: UnityFrameInput): Record<string, NativeValue> {
|
|
214
|
+
const s = f.scale
|
|
215
|
+
const g: Record<string, NativeValue> = {}
|
|
216
|
+
|
|
217
|
+
// ---- the camera (UnityPerFrame), in game units
|
|
218
|
+
// Unity's own pair, so what a shader rebuilds from them means what it did
|
|
219
|
+
// in the game: the view in game units, right-handed and looking down -Z;
|
|
220
|
+
// the projection on game-unit depths (the engine's, conjugated by the scale
|
|
221
|
+
// and the z flip, and divided through by s, which leaves the same NDC). Their
|
|
222
|
+
// product puts every vertex exactly where the engine's own draw of it lands.
|
|
223
|
+
const zFlip = Float32Array.from([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1])
|
|
224
|
+
const view = mul4(zFlip, mul4(scale4(1 / s), mul4(f.view, gameToEngine(s))))
|
|
225
|
+
const toEngineView = mul4(scale4(s), zFlip)
|
|
226
|
+
const proj = flipY(mul4(f.proj, toEngineView))
|
|
227
|
+
for (let i = 0; i < 16; i++) proj[i] /= s
|
|
228
|
+
const vp = mul4(proj, view)
|
|
229
|
+
const w = Math.max(1, f.width)
|
|
230
|
+
const h = Math.max(1, f.height)
|
|
231
|
+
const near = f.near / s
|
|
232
|
+
const far = f.far / s
|
|
233
|
+
g._WorldSpaceCameraPos = [...gamePoint(f.eye, s), 1]
|
|
234
|
+
g.unity_MatrixV = view
|
|
235
|
+
g.unity_MatrixInvV = invert4(view)
|
|
236
|
+
g.unity_MatrixP = proj
|
|
237
|
+
g.glstate_matrix_projection = proj
|
|
238
|
+
g.unity_MatrixVP = vp
|
|
239
|
+
g._NonJitteredViewProjMatrix = vp
|
|
240
|
+
// x = -1: a flipped projection, as Unity's into a texture.
|
|
241
|
+
g._ProjectionParams = [-1, near, far, 1 / far]
|
|
242
|
+
g._ScreenParams = [w, h, 1 + 1 / w, 1 + 1 / h]
|
|
243
|
+
g._ScaledScreenParams = [w, h, 1 + 1 / w, 1 + 1 / h]
|
|
244
|
+
g._ZBufferParams = [-1 + far / near, 1, (-1 + far / near) / far, 1 / far]
|
|
245
|
+
g.unity_OrthoParams = [0, 0, 0, 0]
|
|
246
|
+
const t = f.time
|
|
247
|
+
g._Time = [t / 20, t, t * 2, t * 3]
|
|
248
|
+
g._SinTime = [Math.sin(t / 8), Math.sin(t / 4), Math.sin(t / 2), Math.sin(t)]
|
|
249
|
+
g._CosTime = [Math.cos(t / 8), Math.cos(t / 4), Math.cos(t / 2), Math.cos(t)]
|
|
250
|
+
const dt = Math.max(f.dt, 1e-4)
|
|
251
|
+
g.unity_DeltaTime = [dt, 1 / dt, dt, 1 / dt]
|
|
252
|
+
// UpdateRenderSettings: sim_Time = (t - floor t, floor t)
|
|
253
|
+
g.sim_Time = [t - Math.floor(t), Math.floor(t), 0, 0]
|
|
254
|
+
g.identity_matrix = scale4(1)
|
|
255
|
+
|
|
256
|
+
// ---- LightingFeatures.SetupLightings / PlusLighting SetupMainLightConstants
|
|
257
|
+
if (f.sunDirection) {
|
|
258
|
+
const d = gameDir(f.sunDirection)
|
|
259
|
+
g.SimMainLightDir = [-d[0], -d[1], -d[2], 0]
|
|
260
|
+
g.SimMainLightColor = [...f.sunColor, 1]
|
|
261
|
+
g.SimMainLightColorNoInt = [...f.sunColor, 1]
|
|
262
|
+
// As the game's pipeline leaves it (recorded): the way the light travels.
|
|
263
|
+
g._MainLightPosition = [d[0], d[1], d[2], 0]
|
|
264
|
+
g.sim_ShadowLightDirection = [-d[0], -d[1], -d[2], 0]
|
|
265
|
+
g._MainLightColor = [...f.sunColor, 1]
|
|
266
|
+
} else {
|
|
267
|
+
g.SimMainLightDir = [0, 0, 0, 0]
|
|
268
|
+
g.SimMainLightColor = [0, 0, 0, 0]
|
|
269
|
+
g.SimMainLightColorNoInt = [0, 0, 0, 0]
|
|
270
|
+
g._MainLightPosition = [0, 1, 0, 0]
|
|
271
|
+
g._MainLightColor = [0, 0, 0, 0]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ---- ForwardFeature.SetupRenderFeature, UpdateRenderSettings: a scene
|
|
275
|
+
// with no stage of the game's has no settings of its own — the defaults the
|
|
276
|
+
// game's SceneSetting starts from.
|
|
277
|
+
g.SimSceneTint = [1, 1, 1, 1]
|
|
278
|
+
g._ReceiveSceneShadow = 1
|
|
279
|
+
g.sim_FogRotation = 0
|
|
280
|
+
g.sim_FogInverseRootRotationMatrix = scale4(1)
|
|
281
|
+
g.sim_FogColor = [0, 0, 0, 0]
|
|
282
|
+
g.sim_FogColor2 = [0, 0, 0, 0]
|
|
283
|
+
g.sim_FogParams = [0, 1, 0, 1]
|
|
284
|
+
g.sim_FogDirectionalColor = [0, 0, 0, 0]
|
|
285
|
+
g.sim_FogDirectionalDir = [0, 0, 1, 0]
|
|
286
|
+
g.sim_FogDirectionalFalloff = 0
|
|
287
|
+
g.sim_DYN_FOG_LINEAR_Variable = 0
|
|
288
|
+
g.sim_DynFogColor = [0, 0, 0, 0]
|
|
289
|
+
g.sim_DynFogParams = [0, 1, 0, 1]
|
|
290
|
+
g._ProbeLightingBase = [1, 1, 1, 1]
|
|
291
|
+
g._ProbeLightingScale = 0
|
|
292
|
+
g._AcceptLightProbe = 0
|
|
293
|
+
g.sim_VertexAmbientScale = 1
|
|
294
|
+
g._EnableShadowScatter = 0
|
|
295
|
+
g._ShadowScatterColor = [0, 0, 0, 0]
|
|
296
|
+
g._ShadowScatterRange = 0
|
|
297
|
+
// realtimeShadowColor's default grey, made linear (ConvertSRGBToActiveColorSpace)
|
|
298
|
+
g._SubtractiveShadowColor = [0.2158605, 0.2158605, 0.2158605, 1]
|
|
299
|
+
g._AmbientOcclusionParam = [0, 0, 0, 0]
|
|
300
|
+
g.sim_EnvCubeScale = 0
|
|
301
|
+
g.sim_EnvCube_BoxMin = [0, 0, 0, 0]
|
|
302
|
+
g.sim_EnvCube_BoxMax = [0, 0, 0, 0]
|
|
303
|
+
g.sim_EnvCube_ProbePosition = [0, 0, 0, 0]
|
|
304
|
+
|
|
305
|
+
// ---- SetupEnvironmentLighting: the scene's ambient SH
|
|
306
|
+
// The half turn negates x and z: the terms odd in either (z, x, xy, yz) flip.
|
|
307
|
+
const sh = f.ambientSH ? Array.from(f.ambientSH) : null
|
|
308
|
+
if (sh) for (const i of [2, 3, 4, 5]) for (let k = 0; k < 3; k++) sh[i * 3 + k] = -sh[i * 3 + k]
|
|
309
|
+
Object.assign(g, unitySH(sh, f.ambientFlat))
|
|
310
|
+
|
|
311
|
+
// ---- a game stage's own settings over all of the above
|
|
312
|
+
if (f.settings) Object.assign(g, f.settings)
|
|
313
|
+
|
|
314
|
+
// ---- PlusLightingFeature: the light table, one tile and one depth bin
|
|
315
|
+
// holding every light (binning only culls; the shader applies each range)
|
|
316
|
+
const n = Math.min(f.lights.length, PLUS_MAX)
|
|
317
|
+
const pos: number[][] = [],
|
|
318
|
+
col: number[][] = [],
|
|
319
|
+
att: number[][] = [],
|
|
320
|
+
dir: number[][] = []
|
|
321
|
+
const extra: number[][] = [],
|
|
322
|
+
w2l: Float32Array[] = []
|
|
323
|
+
const simDir: number[][] = [],
|
|
324
|
+
simColorW: number[] = []
|
|
325
|
+
const types = new Float32Array(PLUS_MAX)
|
|
326
|
+
const masks = new Uint32Array(PLUS_MAX)
|
|
327
|
+
const ident = scale4(1)
|
|
328
|
+
for (let i = 0; i < PLUS_MAX; i++) {
|
|
329
|
+
const l = f.lights[i]
|
|
330
|
+
if (i >= n || !l) {
|
|
331
|
+
pos.push([0, 0, 0, 0])
|
|
332
|
+
col.push([0, 0, 0, 0])
|
|
333
|
+
att.push([0, 1, 0, 1])
|
|
334
|
+
dir.push([0, 0, 0, 0])
|
|
335
|
+
extra.push([0, 0, 0, 0])
|
|
336
|
+
w2l.push(ident)
|
|
337
|
+
continue
|
|
338
|
+
}
|
|
339
|
+
const spot = l.aim[0] !== 0 || l.aim[1] !== 0 || l.aim[2] !== 0
|
|
340
|
+
const r = l.radius / s
|
|
341
|
+
const r2 = r * r
|
|
342
|
+
// LightInfo.GetPunctualLightDistanceAttenuation / GetSpotAngleAttenuation
|
|
343
|
+
let z = 0
|
|
344
|
+
let ww = 1
|
|
345
|
+
if (spot) {
|
|
346
|
+
const inv = 1 / Math.max(l.cosInner - l.cosOuter, 0.001)
|
|
347
|
+
z = inv
|
|
348
|
+
ww = -l.cosOuter * inv
|
|
349
|
+
}
|
|
350
|
+
pos.push([...gamePoint(l.position, s), 1])
|
|
351
|
+
// The engine's lamp brightness is per its own unit; the inverse square in
|
|
352
|
+
// game units is s² larger at the same place, so the colour carries 1/s².
|
|
353
|
+
col.push([l.color[0] / (s * s), l.color[1] / (s * s), l.color[2] / (s * s), 1])
|
|
354
|
+
att.push([1 / Math.max(r2, 1e-4), -r2 / (r2 * 0.64 - r2), z, ww])
|
|
355
|
+
const u = l.unity
|
|
356
|
+
if (u) {
|
|
357
|
+
col[col.length - 1][3] = u.colorW
|
|
358
|
+
dir.push(u.spotDir.slice(0, 4))
|
|
359
|
+
extra.push(u.extra.slice(0, 4))
|
|
360
|
+
w2l.push(u.worldToLight ? Float32Array.from(u.worldToLight) : ident)
|
|
361
|
+
simDir.push((u.simSpotDir ?? u.spotDir).slice(0, 4))
|
|
362
|
+
simColorW.push(u.simColorW ?? 1)
|
|
363
|
+
types[i] = u.lightType
|
|
364
|
+
} else {
|
|
365
|
+
// The pipeline's own defaults for a light it did not author: forward
|
|
366
|
+
// along the aim, w = 1 / the 0.1 sphere every game lamp of these stages has.
|
|
367
|
+
const a = gameDir(l.aim)
|
|
368
|
+
dir.push(spot ? [...a, 10] : [0, 0, 1, 10])
|
|
369
|
+
extra.push([0, 0, 0, 0])
|
|
370
|
+
w2l.push(ident)
|
|
371
|
+
simDir.push(spot ? [...a, 10] : [0, 0, 1, 10])
|
|
372
|
+
simColorW.push(1)
|
|
373
|
+
types[i] = spot ? 0 : 2
|
|
374
|
+
}
|
|
375
|
+
masks[i] = l.layers >>> 0
|
|
376
|
+
}
|
|
377
|
+
g._AdditionalLightsPosition = pos
|
|
378
|
+
g._AdditionalLightsColor = col
|
|
379
|
+
g._AdditionalLightsAttenuation = att
|
|
380
|
+
g._AdditionalLightsSpotDir = dir
|
|
381
|
+
g._AdditionalLightsExtra = extra
|
|
382
|
+
g._AdditionalLightsWorldToLights = w2l
|
|
383
|
+
g._AdditionalLightsLightTypes = types
|
|
384
|
+
g._AdditionalLightsLayerMasks = masks
|
|
385
|
+
g._AdditionalLightsCookieEnableBits = new Float32Array(8)
|
|
386
|
+
const words = Math.max(1, Math.ceil(n / 32))
|
|
387
|
+
const zb = new Uint32Array(4096)
|
|
388
|
+
const tiles = new Uint32Array(16384)
|
|
389
|
+
zb[0] = n > 0 ? ((n - 1) << 16) >>> 0 : 0xffff
|
|
390
|
+
for (let k = 0; k < words; k++) {
|
|
391
|
+
const bits = Math.min(Math.max(n - k * 32, 0), 32)
|
|
392
|
+
const m = bits >= 32 ? 0xffffffff : ((1 << bits) >>> 0) - 1
|
|
393
|
+
zb[1 + k] = m >>> 0
|
|
394
|
+
tiles[k] = m >>> 0
|
|
395
|
+
}
|
|
396
|
+
// LightingFeatures' own copy for the per-vertex lights: the first 16, colour
|
|
397
|
+
// without the intensity in w.
|
|
398
|
+
g.SimAdditionalLightPosition = pos.slice(0, 16)
|
|
399
|
+
g.SimAdditionalLightColor = col
|
|
400
|
+
.slice(0, 16)
|
|
401
|
+
.map((c, i) => (c[0] || c[1] || c[2] ? [c[0], c[1], c[2], simColorW[i] ?? 1] : [0, 0, 0, 0]))
|
|
402
|
+
g.SimAdditionalLightAttenuation = att.slice(0, 16)
|
|
403
|
+
g.SimAdditionalLightSpotDir = simDir.slice(0, 16).concat(dir.slice(simDir.length, 16))
|
|
404
|
+
g._PlusLighting_ZBins = zb
|
|
405
|
+
g._PlusLighting_Tiles = tiles
|
|
406
|
+
g._PlusLightingParams0 = [0, 0, 1, words]
|
|
407
|
+
g._PlusLightingParams1 = [1000, 0, 0, 0]
|
|
408
|
+
|
|
409
|
+
// ---- the main light's cascades, sampled from the engine's atlas
|
|
410
|
+
if (f.shadow) {
|
|
411
|
+
const sh = f.shadow
|
|
412
|
+
const unit = sh.tileSize / sh.atlasSize
|
|
413
|
+
const w2s: Float32Array[] = []
|
|
414
|
+
for (let i = 0; i < 4; i++) {
|
|
415
|
+
const vpe = sh.viewProj[i]
|
|
416
|
+
const tile = sh.tiles[i]
|
|
417
|
+
if (!vpe || !tile) {
|
|
418
|
+
w2s.push(scale4(0))
|
|
419
|
+
continue
|
|
420
|
+
}
|
|
421
|
+
// ndc -> the tile's uv (y down, as the atlas is addressed), depth as drawn
|
|
422
|
+
const tx = tile[0] / sh.atlasSize
|
|
423
|
+
const ty = tile[1] / sh.atlasSize
|
|
424
|
+
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])
|
|
425
|
+
w2s.push(mul4(toTile, mul4(vpe, gameToEngine(s))))
|
|
426
|
+
}
|
|
427
|
+
g._MainLightWorldToShadow = w2s
|
|
428
|
+
for (let i = 0; i < 4; i++) {
|
|
429
|
+
const sp = sh.spheres[i]
|
|
430
|
+
g[`_CascadeShadowSplitSpheres${i}`] = sp ? [...gamePoint(sp.center, s), sp.radius / s] : [0, 0, 0, 0]
|
|
431
|
+
}
|
|
432
|
+
const r = (i: number) => (sh.spheres[i] ? (sh.spheres[i].radius / s) ** 2 : 0)
|
|
433
|
+
g._CascadeShadowSplitSphereRadii = [r(0), r(1), r(2), r(3)]
|
|
434
|
+
const half = 0.5 / sh.atlasSize
|
|
435
|
+
g._MainLightShadowOffset0 = [-half, -half, half, -half]
|
|
436
|
+
g._MainLightShadowOffset1 = [-half, half, half, half]
|
|
437
|
+
g._MainLightShadowmapSize = [1 / sh.atlasSize, 1 / sh.atlasSize, sh.atlasSize, sh.atlasSize]
|
|
438
|
+
g._MainLightShadowParams = [f.sunShadow, 1, 0, 0]
|
|
439
|
+
} else {
|
|
440
|
+
g._MainLightShadowParams = [0, 0, 0, 0]
|
|
441
|
+
}
|
|
442
|
+
return g
|
|
443
|
+
}
|