@driftengine/core 4.8.7 → 4.10.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/CHANGELOG.json +123 -11
- package/README.md +43 -38
- package/dist/index.d.ts +8 -1
- package/dist/index.js +11 -3
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +19 -4
- package/dist/render/backend/createRenderer.d.ts +6 -0
- package/dist/render/backend/createRenderer.js +4 -1
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +180 -4
- package/dist/render/backend/webgl2/renderer.js +550 -40
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +19 -4
- package/dist/render/backend/webgpu/buffers.js +18 -11
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/device.d.ts +7 -1
- package/dist/render/backend/webgpu/device.js +31 -3
- package/dist/render/backend/webgpu/drawCommand.d.ts +18 -4
- package/dist/render/backend/webgpu/drawCommand.js +10 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +100 -42
- package/dist/render/backend/webgpu/flatPass.js +191 -43
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +6 -0
- package/dist/render/backend/webgpu/pipelineCache.js +7 -0
- package/dist/render/backend/webgpu/postPass.d.ts +51 -9
- package/dist/render/backend/webgpu/postPass.js +16 -2
- package/dist/render/backend/webgpu/reflectionSurfacePass.d.ts +30 -0
- package/dist/render/backend/webgpu/reflectionSurfacePass.js +100 -0
- package/dist/render/backend/webgpu/renderer.d.ts +283 -37
- package/dist/render/backend/webgpu/renderer.js +1363 -202
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +241 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- package/dist/render/filmicCurve.d.ts +74 -0
- package/dist/render/filmicCurve.js +203 -0
- package/dist/render/fog.d.ts +4 -1
- package/dist/render/fog.js +12 -2
- package/dist/render/frame/resources.d.ts +2 -2
- package/dist/render/frame/resources.js +6 -0
- package/dist/render/frameReflections.d.ts +40 -0
- package/dist/render/frameReflections.js +48 -0
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +5 -0
- package/dist/render/mesh.js +6 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +39 -8
- package/dist/render/renderQuality.js +4 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +24 -1
- package/dist/render/screenSpaceReflection.d.ts +7 -1
- package/dist/render/screenSpaceReflection.js +5 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/filmLook.js +2 -1
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +2 -0
- package/dist/render/shaders/flat/index.d.ts +16 -0
- package/dist/render/shaders/flat/index.js +33 -5
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +43 -5
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/overlay.d.ts +16 -0
- package/dist/render/shaders/flat/overlay.js +117 -0
- package/dist/render/shaders/flat/pointShadow.js +3 -3
- package/dist/render/shaders/flat/preamble.js +38 -24
- package/dist/render/shaders/flat/reflectionSurface.d.ts +21 -0
- package/dist/render/shaders/flat/reflectionSurface.js +61 -0
- package/dist/render/shaders/fog.d.ts +1 -1
- package/dist/render/shaders/fog.js +9 -2
- package/dist/render/shaders/fringe.d.ts +29 -0
- package/dist/render/shaders/fringe.js +55 -0
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.js +1 -1
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/film.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/film.wgsl.js +1 -1
- package/dist/render/shaders/generated/fire.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/fire.wgsl.js +1 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3254 -2920
- package/dist/render/shaders/generated/flat.wgsl.js +3292 -2957
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/particle.wgsl.js +4 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +56 -26
- package/dist/render/shaders/generated/rush.wgsl.js +56 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/scatter.wgsl.js +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +1 -1
- package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +22 -2
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +22 -2
- package/dist/render/shaders/generated/water.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/water.wgsl.js +1 -1
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +2 -2
- package/dist/render/shaders/gpudriven/fog.wgsl.js +11 -3
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/rush.js +54 -11
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/ssrResolve.d.ts +17 -0
- package/dist/render/shaders/ssrResolve.js +51 -0
- package/dist/render/shaders/ssrTrace.js +26 -0
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +106 -1
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +57 -7
- package/dist/render/surfaceTexture.js +125 -17
- package/dist/render/temporalAa.d.ts +14 -0
- package/dist/render/temporalAa.js +20 -0
- package/dist/render/vertexDefaults.d.ts +17 -0
- package/dist/render/vertexDefaults.js +24 -0
- package/package.json +6 -6
- package/src/index.ts +24 -2
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +22 -3
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +672 -37
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +42 -16
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/drawCommand.ts +22 -4
- package/src/render/backend/webgpu/flatPass.ts +250 -40
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/pipelineCache.ts +7 -0
- package/src/render/backend/webgpu/postPass.ts +18 -0
- package/src/render/backend/webgpu/reflectionSurfacePass.ts +114 -0
- package/src/render/backend/webgpu/renderer.ts +1655 -230
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +363 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/displayRange.ts +61 -0
- package/src/render/filmicCurve.ts +229 -0
- package/src/render/fog.ts +19 -3
- package/src/render/frame/resources.ts +6 -0
- package/src/render/frameReflections.ts +72 -0
- package/src/render/glCompressed.ts +83 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +6 -0
- package/src/render/pass.ts +53 -1
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +44 -8
- package/src/render/sceneTarget.ts +30 -2
- package/src/render/screenSpaceReflection.ts +7 -1
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/glassTint.ts +5 -1
- package/src/render/shaders/flat/index.ts +49 -5
- package/src/render/shaders/flat/main.ts +43 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +3 -3
- package/src/render/shaders/flat/preamble.ts +38 -24
- package/src/render/shaders/flat/reflectionSurface.ts +61 -0
- package/src/render/shaders/fog.ts +9 -2
- package/src/render/shaders/fringe.ts +59 -0
- package/src/render/shaders/generated/arcane.wgsl.ts +1 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/caustics.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/film.wgsl.ts +1 -1
- package/src/render/shaders/generated/fire.wgsl.ts +1 -1
- package/src/render/shaders/generated/flat.wgsl.ts +3292 -2957
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +4 -4
- package/src/render/shaders/generated/rush.wgsl.ts +56 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +1 -1
- package/src/render/shaders/generated/smoke.wgsl.ts +1 -1
- package/src/render/shaders/generated/ssrResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +22 -2
- package/src/render/shaders/generated/water.wgsl.ts +1 -1
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +26 -0
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/ssrPass.ts +138 -1
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +161 -27
- package/src/render/temporalAa.ts +20 -0
- package/src/render/vertexDefaults.ts +28 -0
package/src/render/ssrPass.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { DEPTH_01_TO_CLIP } from './lightVolumeDraw.ts';
|
|
|
6
6
|
import type { ReflectionQueue } from './screenSpaceReflection.ts';
|
|
7
7
|
import { compileProgram, uniformLocations } from './shader.ts';
|
|
8
8
|
import { FULLSCREEN_VERT } from './shaders/fullscreen.ts';
|
|
9
|
-
import { SSR_RESOLVE_FRAG } from './shaders/ssrResolve.ts';
|
|
9
|
+
import { SSR_MATERIAL_RESOLVE_FRAG, SSR_RESOLVE_FRAG } from './shaders/ssrResolve.ts';
|
|
10
|
+
import type { ResolvedFrameReflections } from './frameReflections.ts';
|
|
10
11
|
import { SSR_TRACE_FRAG } from './shaders/ssrTrace.ts';
|
|
11
12
|
import { blendKeeping } from './sceneCoverage.ts';
|
|
12
13
|
|
|
@@ -36,6 +37,10 @@ export class SsrPass {
|
|
|
36
37
|
private height = 0;
|
|
37
38
|
|
|
38
39
|
/** `inverse(viewProjection) * DEPTH_01_TO_CLIP`, rebuilt once a frame rather than per surface. */
|
|
40
|
+
/** The frame's materials' resolve, compiled the first time a frame traces them. */
|
|
41
|
+
private materialResolve: WebGLProgram | null = null;
|
|
42
|
+
private materialUniforms: Record<string, WebGLUniformLocation | null> = {};
|
|
43
|
+
|
|
39
44
|
private readonly depthToWorld = new Float32Array(16);
|
|
40
45
|
private readonly scissor = new Int32Array(4);
|
|
41
46
|
|
|
@@ -150,6 +155,11 @@ export class SsrPass {
|
|
|
150
155
|
gl.activeTexture(gl.TEXTURE1);
|
|
151
156
|
gl.bindTexture(gl.TEXTURE_2D, scene);
|
|
152
157
|
gl.uniform1i(u['uSsrScene'] ?? null, 1);
|
|
158
|
+
/* The materials' maps unread in this mode, and pointed at a bound texture all the same: a
|
|
159
|
+
driver may fetch a sampler's descriptor before the branch that skips it. */
|
|
160
|
+
gl.uniform1i(u['uSsrProbeMap'] ?? null, 0);
|
|
161
|
+
gl.uniform1i(u['uSsrTintMap'] ?? null, 0);
|
|
162
|
+
gl.uniform1f(u['uSsrMaterial'] ?? null, 0);
|
|
153
163
|
gl.uniformMatrix4fv(u['uSsrDepthToWorld'] ?? null, false, this.depthToWorld);
|
|
154
164
|
/* No Y flip on this backend: `gl.scissor` and the framebuffer both count rows from the bottom,
|
|
155
165
|
which is the space the raw matrix already emits. See `CLIP_Y_FLIP`. */
|
|
@@ -195,6 +205,131 @@ export class SsrPass {
|
|
|
195
205
|
}
|
|
196
206
|
|
|
197
207
|
/** Composite what the trace found over whatever framebuffer is bound. */
|
|
208
|
+
/**
|
|
209
|
+
* Every pixel the lit stage left a reflection for, marched against the frame's depth by the
|
|
210
|
+
* trace's material mode, into this pass's own target; `resolveMaterials` then swaps the probe's
|
|
211
|
+
* share for it. See `frameReflections.ts`. Ends on the default framebuffer, as the surfaces' does.
|
|
212
|
+
*/
|
|
213
|
+
traceMaterials(
|
|
214
|
+
scene: WebGLTexture,
|
|
215
|
+
depth: WebGLTexture,
|
|
216
|
+
probe: WebGLTexture,
|
|
217
|
+
tint: WebGLTexture,
|
|
218
|
+
viewProjection: ReadonlyMat4,
|
|
219
|
+
eye: ArrayLike<number>,
|
|
220
|
+
width: number,
|
|
221
|
+
height: number,
|
|
222
|
+
edgeFade: number,
|
|
223
|
+
reflections: ResolvedFrameReflections,
|
|
224
|
+
): void {
|
|
225
|
+
const { gl } = this;
|
|
226
|
+
const u = this.traceUniforms;
|
|
227
|
+
this.ensureSize(width, height);
|
|
228
|
+
mat4.invert(this.depthToWorld, viewProjection);
|
|
229
|
+
mat4.multiply(this.depthToWorld, this.depthToWorld, DEPTH_01_TO_CLIP);
|
|
230
|
+
|
|
231
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
232
|
+
gl.viewport(0, 0, width, height);
|
|
233
|
+
gl.disable(gl.DEPTH_TEST);
|
|
234
|
+
gl.depthMask(false);
|
|
235
|
+
gl.disable(gl.CULL_FACE);
|
|
236
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
237
|
+
gl.disable(gl.BLEND);
|
|
238
|
+
gl.clearBufferfv(gl.COLOR, 0, [0, 0, 0, 0]);
|
|
239
|
+
|
|
240
|
+
gl.useProgram(this.trace);
|
|
241
|
+
gl.bindVertexArray(this.vao);
|
|
242
|
+
const textures = [depth, scene, probe, tint];
|
|
243
|
+
const names = ['uSsrDepth', 'uSsrScene', 'uSsrProbeMap', 'uSsrTintMap'];
|
|
244
|
+
for (let unit = 0; unit < 4; unit++) {
|
|
245
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
246
|
+
gl.bindTexture(gl.TEXTURE_2D, textures[unit] ?? null);
|
|
247
|
+
gl.uniform1i(u[names[unit] ?? ''] ?? null, unit);
|
|
248
|
+
}
|
|
249
|
+
gl.uniformMatrix4fv(u['uSsrDepthToWorld'] ?? null, false, this.depthToWorld);
|
|
250
|
+
gl.uniformMatrix4fv(u['uSsrViewProj'] ?? null, false, viewProjection as Float32Array);
|
|
251
|
+
gl.uniform3f(u['uSsrEye'] ?? null, eye[0] ?? 0, eye[1] ?? 0, eye[2] ?? 0);
|
|
252
|
+
gl.uniform1f(u['uSsrEdgeFade'] ?? null, edgeFade);
|
|
253
|
+
gl.uniform3f(u['uSsrAxis'] ?? null, 0, 0, 0);
|
|
254
|
+
gl.uniform3f(u['uSsrTint'] ?? null, 1, 1, 1);
|
|
255
|
+
gl.uniform1f(u['uSsrStrength'] ?? null, 1);
|
|
256
|
+
gl.uniform2f(u['uSsrFresnel'] ?? null, 0, 0);
|
|
257
|
+
gl.uniform1f(u['uSsrFacingCos'] ?? null, -1);
|
|
258
|
+
gl.uniform1f(u['uSsrReach'] ?? null, reflections.reachM);
|
|
259
|
+
gl.uniform1f(u['uSsrThickness'] ?? null, reflections.thicknessM);
|
|
260
|
+
gl.uniform1f(u['uSsrSteps'] ?? null, reflections.steps);
|
|
261
|
+
gl.uniform1f(u['uSsrMaterial'] ?? null, 1);
|
|
262
|
+
gl.uniform1f(u['uSsrMaxRoughness'] ?? null, reflections.maxRoughness);
|
|
263
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
264
|
+
/* Back to the surfaces' mode, which every other trace in the frame is. */
|
|
265
|
+
gl.uniform1f(u['uSsrMaterial'] ?? null, 0);
|
|
266
|
+
|
|
267
|
+
gl.bindVertexArray(null);
|
|
268
|
+
for (let unit = 3; unit >= 0; unit--) {
|
|
269
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
270
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
271
|
+
}
|
|
272
|
+
gl.depthMask(true);
|
|
273
|
+
gl.enable(gl.DEPTH_TEST);
|
|
274
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The probe's share of each pixel swapped for what `traceMaterials` found, blurred by the
|
|
279
|
+
* surface's roughness, added onto whatever framebuffer is bound — the scene's, with the two maps
|
|
280
|
+
* already detached from it. See `SSR_MATERIAL_RESOLVE_FRAG`.
|
|
281
|
+
*/
|
|
282
|
+
resolveMaterials(
|
|
283
|
+
probe: WebGLTexture,
|
|
284
|
+
tint: WebGLTexture,
|
|
285
|
+
width: number,
|
|
286
|
+
height: number,
|
|
287
|
+
reflections: ResolvedFrameReflections,
|
|
288
|
+
): void {
|
|
289
|
+
const { gl } = this;
|
|
290
|
+
if (this.reflection === null) return;
|
|
291
|
+
if (this.materialResolve === null) {
|
|
292
|
+
this.materialResolve = compileProgram(
|
|
293
|
+
gl,
|
|
294
|
+
FULLSCREEN_VERT,
|
|
295
|
+
SSR_MATERIAL_RESOLVE_FRAG,
|
|
296
|
+
'ssrMaterialResolve',
|
|
297
|
+
);
|
|
298
|
+
this.materialUniforms = uniformLocations(gl, this.materialResolve, 'ssrMaterialResolve');
|
|
299
|
+
}
|
|
300
|
+
const u = this.materialUniforms;
|
|
301
|
+
gl.disable(gl.DEPTH_TEST);
|
|
302
|
+
gl.depthMask(false);
|
|
303
|
+
gl.disable(gl.CULL_FACE);
|
|
304
|
+
gl.enable(gl.BLEND);
|
|
305
|
+
/* Added, a negative addend subtracting, and the scene's alpha kept. */
|
|
306
|
+
blendKeeping(gl, gl.ONE, gl.ONE);
|
|
307
|
+
gl.useProgram(this.materialResolve);
|
|
308
|
+
const textures = [this.reflection, probe, tint];
|
|
309
|
+
const names = ['uSsrReflection', 'uSsrProbeMap', 'uSsrTintMap'];
|
|
310
|
+
for (let unit = 0; unit < 3; unit++) {
|
|
311
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
312
|
+
gl.bindTexture(gl.TEXTURE_2D, textures[unit] ?? null);
|
|
313
|
+
gl.uniform1i(u[names[unit] ?? ''] ?? null, unit);
|
|
314
|
+
}
|
|
315
|
+
gl.uniform3f(
|
|
316
|
+
u['uSsrBlur'] ?? null,
|
|
317
|
+
1 / Math.max(width, 1),
|
|
318
|
+
1 / Math.max(height, 1),
|
|
319
|
+
reflections.blur * height,
|
|
320
|
+
);
|
|
321
|
+
gl.bindVertexArray(this.vao);
|
|
322
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
323
|
+
gl.bindVertexArray(null);
|
|
324
|
+
for (let unit = 2; unit >= 0; unit--) {
|
|
325
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
326
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
327
|
+
}
|
|
328
|
+
gl.disable(gl.BLEND);
|
|
329
|
+
gl.depthMask(true);
|
|
330
|
+
gl.enable(gl.DEPTH_TEST);
|
|
331
|
+
}
|
|
332
|
+
|
|
198
333
|
resolveReflections(): void {
|
|
199
334
|
const { gl } = this;
|
|
200
335
|
if (this.reflection === null) return;
|
|
@@ -234,6 +369,8 @@ export class SsrPass {
|
|
|
234
369
|
if (this.framebuffer !== null) gl.deleteFramebuffer(this.framebuffer);
|
|
235
370
|
gl.deleteProgram(this.trace);
|
|
236
371
|
gl.deleteProgram(this.resolve);
|
|
372
|
+
if (this.materialResolve !== null) gl.deleteProgram(this.materialResolve);
|
|
373
|
+
this.materialResolve = null;
|
|
237
374
|
gl.deleteVertexArray(this.vao);
|
|
238
375
|
this.reflection = null;
|
|
239
376
|
this.framebuffer = null;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draws that do not change between frames, captured once from an enumeration and replayed into any
|
|
3
|
+
* colour pass: what `createStaticDraws` keeps, and the one rule both backends replay it by.
|
|
4
|
+
*
|
|
5
|
+
* **Captured, not referenced.** An enumeration is a closure over the caller's state, written to
|
|
6
|
+
* allocate nothing, so it hands a sink its scratch: the matrix it fills for every entry and the
|
|
7
|
+
* count it rewrites for the next batch. A list kept across frames copies both when it is made, so
|
|
8
|
+
* what it draws is what the enumeration said then. Meshes, batches and materials are held by
|
|
9
|
+
* reference: they are the caller's, and disposing one a list still holds is the caller's error.
|
|
10
|
+
*
|
|
11
|
+
* **Rigid meshes and instanced batches only.** A skinned mesh is posed every frame and a scatter
|
|
12
|
+
* batch bends with every frame's wind, so neither can be kept, and both are refused by name rather
|
|
13
|
+
* than captured in the pose they happened to have. A caller draws them each frame as it always has.
|
|
14
|
+
*
|
|
15
|
+
* What a backend does with the list is its own: WebGL2 replays the entries every frame through
|
|
16
|
+
* the scene-caster path, and WebGPU records them once into a render bundle per view. See
|
|
17
|
+
* `RendererApi.createStaticDraws` for what a caller is promised, which is the same on both.
|
|
18
|
+
*/
|
|
19
|
+
import type { InstancedHandle, MeshHandle } from './backend/api.ts';
|
|
20
|
+
import type { MeshInstances } from './instances.ts';
|
|
21
|
+
import type { SceneCasterMaterial, ShadowCasterSink, ShadowCasters } from './shadowCasters.ts';
|
|
22
|
+
|
|
23
|
+
/** A list from `createStaticDraws`. Opaque but for its size. */
|
|
24
|
+
export interface StaticDrawsHandle {
|
|
25
|
+
/** How many entries it holds: meshes and batches, a batch counting once. */
|
|
26
|
+
readonly draws: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** One captured entry. */
|
|
30
|
+
export type StaticEntry =
|
|
31
|
+
| {
|
|
32
|
+
readonly kind: 'mesh';
|
|
33
|
+
readonly mesh: MeshHandle;
|
|
34
|
+
readonly model: Float32Array;
|
|
35
|
+
readonly material: SceneCasterMaterial;
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
readonly kind: 'instanced';
|
|
39
|
+
readonly batch: InstancedHandle;
|
|
40
|
+
readonly data: MeshInstances;
|
|
41
|
+
readonly material: SceneCasterMaterial;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** What both backends hold: the entries, and whether the list has been disposed. */
|
|
45
|
+
export interface StaticDrawsList extends StaticDrawsHandle {
|
|
46
|
+
readonly entries: readonly StaticEntry[];
|
|
47
|
+
disposed: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Enumerate `casters` once into a list. Allocates: it runs when the list is made, not per frame. */
|
|
51
|
+
export function captureStaticDraws(casters: ShadowCasters): StaticDrawsList {
|
|
52
|
+
const entries: StaticEntry[] = [];
|
|
53
|
+
const sink: ShadowCasterSink = {
|
|
54
|
+
mesh: (mesh, model, material) => {
|
|
55
|
+
entries.push({
|
|
56
|
+
kind: 'mesh',
|
|
57
|
+
mesh,
|
|
58
|
+
model: new Float32Array(model as Float32Array),
|
|
59
|
+
material: material ?? null,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
instanced: (batch, data, material) => {
|
|
63
|
+
/* The batch's placements are on the device already; the count is the caller's scratch. */
|
|
64
|
+
entries.push({ kind: 'instanced', batch, data: { ...data }, material: material ?? null });
|
|
65
|
+
},
|
|
66
|
+
skinnedMesh: () => {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'createStaticDraws: a skinned mesh is posed every frame, so a list kept across frames ' +
|
|
69
|
+
'cannot hold one. Draw it each frame, with drawMesh or drawSceneCasters.',
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
scatter: () => {
|
|
73
|
+
throw new Error(
|
|
74
|
+
'createStaticDraws: a scatter batch bends with every frame’s wind, so a list kept across ' +
|
|
75
|
+
'frames cannot hold one. Draw it each frame with drawScatter.',
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
casters(sink);
|
|
80
|
+
return {
|
|
81
|
+
entries,
|
|
82
|
+
get draws() {
|
|
83
|
+
return entries.length;
|
|
84
|
+
},
|
|
85
|
+
disposed: false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Hand every entry to `sink`, in the order it was captured. */
|
|
90
|
+
export function replayStaticDraws(list: StaticDrawsList, sink: ShadowCasterSink): void {
|
|
91
|
+
for (const entry of list.entries) {
|
|
92
|
+
if (entry.kind === 'mesh') sink.mesh(entry.mesh, entry.model, entry.material);
|
|
93
|
+
else sink.instanced?.(entry.batch, entry.data, entry.material);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a draw lays over its surface beyond its material: a rim of light, an emissive overlay that
|
|
3
|
+
* dissolves the surface away, and a second normal map blended in by region. Set per draw with
|
|
4
|
+
* `RendererApi.setSurfaceOverlay`, so one character's whole set of materials changes together
|
|
5
|
+
* while another's does not.
|
|
6
|
+
*
|
|
7
|
+
* **Per draw and held, like `setEnvironmentGain`**: set before a character's draws, cleared after,
|
|
8
|
+
* and every draw between them wears it. **Images come from one atlas** (`maps`), each named by the
|
|
9
|
+
* rectangle it occupies — a scale and an offset in the atlas's uv — because the lit stage binds every
|
|
10
|
+
* sampler a device is guaranteed and has none left for a texture of its own: the atlas is bound in
|
|
11
|
+
* the slot the frame's refraction copy takes, which an opaque surface never reads. A draw that both
|
|
12
|
+
* refracts and wears an overlay's images keeps its refraction and loses the images, said once.
|
|
13
|
+
*
|
|
14
|
+
* **Fifteen vectors of numbers**, in the material's uniform block. WebGPU has the room; WebGL2 has
|
|
15
|
+
* room where its device reports more than the lit stage's 254 vectors, and refuses the overlay in
|
|
16
|
+
* words where it does not. See `packSurfaceOverlay` for the layout.
|
|
17
|
+
*/
|
|
18
|
+
import type { Vec3 } from '../math/color.ts';
|
|
19
|
+
import type { SurfaceTextureHandle } from './backend/api.ts';
|
|
20
|
+
|
|
21
|
+
/** Where an image sits in the atlas: its size and its corner, each in the atlas's uv. */
|
|
22
|
+
export interface OverlayRegion {
|
|
23
|
+
readonly scale: readonly [number, number];
|
|
24
|
+
readonly offset: readonly [number, number];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Light along the silhouette: the edge `(1 - N·V)^falloff`, leaning toward normals that face up,
|
|
29
|
+
* raised to a contrast, times a noise laid in screen space, a slow pulse and a mask, in a colour.
|
|
30
|
+
* Added to what the surface emits, and not gated on the night as emission is: it is a glow the
|
|
31
|
+
* surface wears, not light it gives off.
|
|
32
|
+
*/
|
|
33
|
+
export interface SurfaceRim {
|
|
34
|
+
readonly colour: Vec3;
|
|
35
|
+
/** How bright, 0 for none. Multiplied with `alpha`. */
|
|
36
|
+
readonly intensity: number;
|
|
37
|
+
readonly alpha?: number;
|
|
38
|
+
/** The edge's exponent: larger keeps the rim nearer the silhouette. 1.5 unless given. */
|
|
39
|
+
readonly falloff?: number;
|
|
40
|
+
/**
|
|
41
|
+
* How far the rim leans toward normals that face up: the weight is `(N·up · ½ + ½)^upward`, so
|
|
42
|
+
* 0 is no lean. 0.4 unless given.
|
|
43
|
+
*/
|
|
44
|
+
readonly upward?: number;
|
|
45
|
+
/** The exponent the whole edge is raised to. 1 unless given. */
|
|
46
|
+
readonly contrast?: number;
|
|
47
|
+
/** A noise image laid in screen space and scrolled, the rim taken times it, clamped to 1. */
|
|
48
|
+
readonly noise?: {
|
|
49
|
+
readonly region: OverlayRegion;
|
|
50
|
+
/** Screens a second, along x and y. */
|
|
51
|
+
readonly scroll?: readonly [number, number];
|
|
52
|
+
/** Repeats across the screen's height. 1 unless given. */
|
|
53
|
+
readonly tiling?: number;
|
|
54
|
+
};
|
|
55
|
+
/** A pulse `½ + ½ sin(rate · t)`, held between `low` and `high`, on `Environment.surfaceTime`. */
|
|
56
|
+
readonly pulse?: { readonly rate: number; readonly low: number; readonly high: number };
|
|
57
|
+
/** A mask in the mesh's uv where the rim is erased: the rim times `1 - weight · mask`. */
|
|
58
|
+
readonly mask?: { readonly region: OverlayRegion; readonly weight: number };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The surface cut away where a noise in its uv falls under `threshold`, with a glowing band just
|
|
63
|
+
* above it, and a colour laid over the surface through the same noise.
|
|
64
|
+
*/
|
|
65
|
+
export interface SurfaceDissolve {
|
|
66
|
+
readonly noise: { readonly region: OverlayRegion; readonly tiling?: readonly [number, number] };
|
|
67
|
+
/** 0 keeps everything; 1 cuts everything. */
|
|
68
|
+
readonly threshold: number;
|
|
69
|
+
/** The band's width, in the noise's own units. 0.05 unless given. */
|
|
70
|
+
readonly edge?: number;
|
|
71
|
+
/** The band's light. */
|
|
72
|
+
readonly edgeColour?: Vec3;
|
|
73
|
+
readonly edgeIntensity?: number;
|
|
74
|
+
/** A colour laid over the surface through the noise, as light, and how much of it. */
|
|
75
|
+
readonly overlay?: { readonly colour: Vec3; readonly amount: number };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A second normal map blended into the surface's by six region weights, read through two masks:
|
|
80
|
+
* the first mask's red, green and blue carry regions 1 to 3, the second's regions 4 to 6. A face's
|
|
81
|
+
* expressions, or veins brought up all over.
|
|
82
|
+
*/
|
|
83
|
+
export interface SurfaceWrinkle {
|
|
84
|
+
readonly normal: OverlayRegion;
|
|
85
|
+
readonly masks: readonly [OverlayRegion, OverlayRegion];
|
|
86
|
+
readonly weights: readonly [number, number, number, number, number, number];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* What a draw lays over its surface beyond its material, for `setSurfaceOverlay`: a rim, a dissolve
|
|
91
|
+
* and wrinkles, each optional, and the atlas their images are regions of.
|
|
92
|
+
*/
|
|
93
|
+
export interface SurfaceOverlay {
|
|
94
|
+
/**
|
|
95
|
+
* The atlas every image above is a region of. Absent: no image is read whatever regions are
|
|
96
|
+
* named — rims are smooth, nothing dissolves and nothing wrinkles — because the slot then holds a
|
|
97
|
+
* stand-in whose colour would read as a noise and a mask.
|
|
98
|
+
*/
|
|
99
|
+
readonly maps?: SurfaceTextureHandle;
|
|
100
|
+
readonly rim?: SurfaceRim;
|
|
101
|
+
readonly dissolve?: SurfaceDissolve;
|
|
102
|
+
readonly wrinkle?: SurfaceWrinkle;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Vectors the overlay takes in the material's block: `uOverlay[OVERLAY_VECTORS]`. */
|
|
106
|
+
export const OVERLAY_VECTORS = 15;
|
|
107
|
+
/** Floats `packSurfaceOverlay` fills. */
|
|
108
|
+
export const OVERLAY_FLOATS = OVERLAY_VECTORS * 4;
|
|
109
|
+
|
|
110
|
+
const finite = (value: number | undefined, fallback: number): number =>
|
|
111
|
+
value !== undefined && Number.isFinite(value) ? value : fallback;
|
|
112
|
+
|
|
113
|
+
function region(out: Float32Array, at: number, r: OverlayRegion | undefined | null): void {
|
|
114
|
+
out[at] = finite(r?.scale[0], 0);
|
|
115
|
+
out[at + 1] = finite(r?.scale[1], 0);
|
|
116
|
+
out[at + 2] = finite(r?.offset[0], 0);
|
|
117
|
+
out[at + 3] = finite(r?.offset[1], 0);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* An overlay as the shader reads it, into `out` (`OVERLAY_FLOATS`); all zeros for `null`, which is
|
|
122
|
+
* no overlay. A region with a zero scale is an image not given, and its term reads as none — a
|
|
123
|
+
* noise of 1, a mask of 0, no wrinkle — and every region is one without `maps`. Fifteen vectors:
|
|
124
|
+
*
|
|
125
|
+
* - 0: rim colour, rim strength (intensity times alpha; 0 is no rim)
|
|
126
|
+
* - 1: falloff, upward lean, contrast, mask weight
|
|
127
|
+
* - 2: the rim's noise region; 3: its scroll along x and y, its tiling, the pulse's rate
|
|
128
|
+
* - 4: the pulse's low and high (1 and 1 where none)
|
|
129
|
+
* - 5: the rim's mask region
|
|
130
|
+
* - 6: the dissolve's noise region (a zero scale is no dissolve); 7: its tiling, threshold, edge
|
|
131
|
+
* - 8: the edge's light and its strength; 9: the laid-over colour and its amount
|
|
132
|
+
* - 10: the wrinkle normal's region; 11 and 12: the two masks'; 13 and 14: the six weights
|
|
133
|
+
*/
|
|
134
|
+
export function packSurfaceOverlay(overlay: SurfaceOverlay | null, out: Float32Array): void {
|
|
135
|
+
out.fill(0);
|
|
136
|
+
if (overlay === null) return;
|
|
137
|
+
/* No atlas, no region: see `SurfaceOverlay.maps`. */
|
|
138
|
+
const images = overlay.maps !== undefined;
|
|
139
|
+
const rim = overlay.rim;
|
|
140
|
+
if (rim !== undefined) {
|
|
141
|
+
out[0] = finite(rim.colour[0], 0);
|
|
142
|
+
out[1] = finite(rim.colour[1], 0);
|
|
143
|
+
out[2] = finite(rim.colour[2], 0);
|
|
144
|
+
out[3] = Math.max(0, finite(rim.intensity, 0) * finite(rim.alpha, 1));
|
|
145
|
+
out[4] = Math.max(0, finite(rim.falloff, 1.5));
|
|
146
|
+
out[5] = Math.max(0, finite(rim.upward, 0.4));
|
|
147
|
+
out[6] = Math.max(0, finite(rim.contrast, 1));
|
|
148
|
+
out[7] = rim.mask === undefined ? 0 : Math.min(Math.max(finite(rim.mask.weight, 0), 0), 1);
|
|
149
|
+
region(out, 8, images ? rim.noise?.region : null);
|
|
150
|
+
out[12] = finite(rim.noise?.scroll?.[0], 0);
|
|
151
|
+
out[13] = finite(rim.noise?.scroll?.[1], 0);
|
|
152
|
+
out[14] = Math.max(0, finite(rim.noise?.tiling, 1));
|
|
153
|
+
out[15] = finite(rim.pulse?.rate, 0);
|
|
154
|
+
out[16] = rim.pulse === undefined ? 1 : finite(rim.pulse.low, 1);
|
|
155
|
+
out[17] = rim.pulse === undefined ? 1 : finite(rim.pulse.high, 1);
|
|
156
|
+
region(out, 20, images ? rim.mask?.region : null);
|
|
157
|
+
}
|
|
158
|
+
const dissolve = overlay.dissolve;
|
|
159
|
+
if (dissolve !== undefined) {
|
|
160
|
+
region(out, 24, images ? dissolve.noise.region : null);
|
|
161
|
+
out[28] = finite(dissolve.noise.tiling?.[0], 1);
|
|
162
|
+
out[29] = finite(dissolve.noise.tiling?.[1], 1);
|
|
163
|
+
out[30] = Math.min(Math.max(finite(dissolve.threshold, 0), 0), 1);
|
|
164
|
+
out[31] = Math.max(1e-4, finite(dissolve.edge, 0.05));
|
|
165
|
+
const edge = dissolve.edgeColour ?? [0, 0, 0];
|
|
166
|
+
out[32] = finite(edge[0], 0);
|
|
167
|
+
out[33] = finite(edge[1], 0);
|
|
168
|
+
out[34] = finite(edge[2], 0);
|
|
169
|
+
out[35] = Math.max(0, finite(dissolve.edgeIntensity, 1));
|
|
170
|
+
const laid = dissolve.overlay;
|
|
171
|
+
if (laid !== undefined) {
|
|
172
|
+
out[36] = finite(laid.colour[0], 0);
|
|
173
|
+
out[37] = finite(laid.colour[1], 0);
|
|
174
|
+
out[38] = finite(laid.colour[2], 0);
|
|
175
|
+
out[39] = Math.max(0, finite(laid.amount, 0));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const wrinkle = overlay.wrinkle;
|
|
179
|
+
if (wrinkle !== undefined) {
|
|
180
|
+
region(out, 40, images ? wrinkle.normal : null);
|
|
181
|
+
region(out, 44, images ? wrinkle.masks[0] : null);
|
|
182
|
+
region(out, 48, images ? wrinkle.masks[1] : null);
|
|
183
|
+
for (let i = 0; i < 6; i++) {
|
|
184
|
+
out[52 + i] = Math.min(Math.max(finite(wrinkle.weights[i], 0), 0), 1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Whether an overlay lays anything at all, so a renderer can leave the lit switch off. */
|
|
190
|
+
export function overlayLays(overlay: SurfaceOverlay | null): boolean {
|
|
191
|
+
return (
|
|
192
|
+
overlay !== null &&
|
|
193
|
+
(overlay.rim !== undefined || overlay.dissolve !== undefined || overlay.wrinkle !== undefined)
|
|
194
|
+
);
|
|
195
|
+
}
|