@jgengine/shell 0.18.1 → 0.21.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.md +504 -7
- package/dist/GameHost.d.ts +12 -1
- package/dist/GameHost.js +14 -1
- package/dist/GamePlayerShell.js +132 -17
- package/dist/Shell3dPresentation.d.ts +5 -11
- package/dist/Shell3dPresentation.js +73 -11
- package/dist/ShellChrome.d.ts +2 -1
- package/dist/ShellChrome.js +22 -6
- package/dist/ShellHudPresentation.d.ts +20 -2
- package/dist/ShellHudPresentation.js +41 -6
- package/dist/audio/AudioComponents.js +6 -2
- package/dist/audio/audioEngine.d.ts +24 -0
- package/dist/audio/audioEngine.js +231 -83
- package/dist/audio/audioWire.d.ts +3 -0
- package/dist/audio/audioWire.js +9 -3
- package/dist/audio/loopParams.d.ts +39 -0
- package/dist/audio/loopParams.js +43 -0
- package/dist/audio/synthEngine.d.ts +7 -1
- package/dist/audio/synthEngine.js +103 -38
- package/dist/camera/GameCameraRig.js +3 -2
- package/dist/camera/GameFirstPersonCamera.d.ts +6 -4
- package/dist/camera/GameFirstPersonCamera.js +68 -27
- package/dist/camera/Viewports.d.ts +34 -0
- package/dist/camera/Viewports.js +123 -0
- package/dist/camera/cameraRigs.js +76 -62
- package/dist/camera/chaseHeading.d.ts +9 -0
- package/dist/camera/chaseHeading.js +10 -0
- package/dist/camera/customMuzzle.d.ts +14 -0
- package/dist/camera/customMuzzle.js +68 -0
- package/dist/camera/index.d.ts +1 -0
- package/dist/camera/index.js +1 -0
- package/dist/camera/orbitCameraMath.js +2 -2
- package/dist/camera/rigMath.d.ts +82 -4
- package/dist/camera/rigMath.js +133 -15
- package/dist/camera/rigResolve.d.ts +3 -0
- package/dist/camera/rigResolve.js +11 -0
- package/dist/combat/ProjectileModels.d.ts +31 -0
- package/dist/combat/ProjectileModels.js +70 -0
- package/dist/commandSink.d.ts +1 -2
- package/dist/commandSink.js +3 -2
- package/dist/defineGame.d.ts +19 -3
- package/dist/defineGame.js +39 -7
- package/dist/devtools/DevtoolsOverlay.d.ts +2 -69
- package/dist/devtools/DevtoolsOverlay.js +15 -167
- package/dist/devtools/DevtoolsRuntime.d.ts +7 -0
- package/dist/devtools/DevtoolsRuntime.js +61 -0
- package/dist/devtools/agentBridge.d.ts +6 -0
- package/dist/devtools/agentBridge.js +16 -2
- package/dist/devtools/devtoolsReports.d.ts +68 -0
- package/dist/devtools/devtoolsReports.js +100 -0
- package/dist/devtools/latencyInstrumentation.d.ts +3 -0
- package/dist/devtools/latencyInstrumentation.js +11 -0
- package/dist/devtools/playtestProbe.d.ts +4 -0
- package/dist/devtools/playtestProbe.js +22 -0
- package/dist/diagnostics/FpsCounter.d.ts +17 -0
- package/dist/diagnostics/FpsCounter.js +32 -0
- package/dist/drivers/FrameDriver.js +35 -20
- package/dist/drivers/FrameRateLimiter.d.ts +9 -0
- package/dist/drivers/FrameRateLimiter.js +39 -0
- package/dist/drivers/HudOnlyDriver.d.ts +6 -1
- package/dist/drivers/HudOnlyDriver.js +32 -14
- package/dist/environment/DayNightSky.js +5 -5
- package/dist/environment/Daylight.d.ts +35 -1
- package/dist/environment/Daylight.js +98 -28
- package/dist/environment/EnvironmentScene.js +11 -10
- package/dist/environment/VolumetricClouds.js +20 -2
- package/dist/environment/daylightCycle.d.ts +20 -0
- package/dist/environment/daylightCycle.js +59 -7
- package/dist/environment/sunShadowMath.d.ts +45 -0
- package/dist/environment/sunShadowMath.js +50 -0
- package/dist/input/gamepadPoll.d.ts +47 -0
- package/dist/input/gamepadPoll.js +142 -0
- package/dist/input/gamepadSource.d.ts +11 -1
- package/dist/input/gamepadSource.js +117 -71
- package/dist/input/mouseLook.js +2 -1
- package/dist/input/padHaptics.d.ts +17 -0
- package/dist/input/padHaptics.js +32 -0
- package/dist/input/pointerLock.d.ts +11 -0
- package/dist/input/pointerLock.js +19 -0
- package/dist/inputSink.d.ts +6 -6
- package/dist/inputSink.js +60 -21
- package/dist/lazyShell3dPresentation.d.ts +9 -0
- package/dist/lazyShell3dPresentation.js +24 -0
- package/dist/materialOverride.d.ts +42 -0
- package/dist/materialOverride.js +95 -36
- package/dist/multiplayer.d.ts +33 -3
- package/dist/multiplayer.js +105 -22
- package/dist/pointer/pointerService.d.ts +2 -0
- package/dist/pointer/pointerService.js +12 -2
- package/dist/postfx/PostProcessing.d.ts +11 -1
- package/dist/postfx/PostProcessing.js +14 -42
- package/dist/postfx/gradeShader.d.ts +1 -1
- package/dist/postfx/gradeShader.js +30 -2
- package/dist/postfx/shadowPrepass.d.ts +3 -0
- package/dist/postfx/shadowPrepass.js +18 -0
- package/dist/postfx/stylizePass.d.ts +23 -0
- package/dist/postfx/stylizePass.js +216 -0
- package/dist/presentationRecovery.d.ts +75 -0
- package/dist/presentationRecovery.js +204 -0
- package/dist/presentationRecovery3d.d.ts +4 -0
- package/dist/presentationRecovery3d.js +32 -0
- package/dist/presentationResolve.d.ts +8 -1
- package/dist/presentationResolve.js +5 -2
- package/dist/render/CascadedShadows.d.ts +7 -2
- package/dist/render/CascadedShadows.js +98 -20
- package/dist/render/EnvironmentLighting.js +74 -35
- package/dist/render/HairCards.d.ts +7 -0
- package/dist/render/HairCards.js +23 -0
- package/dist/render/SceneModels.js +52 -78
- package/dist/render/SkinnedInstances.d.ts +91 -0
- package/dist/render/SkinnedInstances.js +249 -0
- package/dist/render/TextureFiltering.d.ts +16 -0
- package/dist/render/TextureFiltering.js +64 -0
- package/dist/render/authoredSurfaceMaterial.d.ts +43 -0
- package/dist/render/authoredSurfaceMaterial.js +108 -0
- package/dist/render/displayColor.d.ts +33 -0
- package/dist/render/displayColor.js +138 -0
- package/dist/render/materialAppearanceSignals.d.ts +23 -0
- package/dist/render/materialAppearanceSignals.js +58 -0
- package/dist/render/materialAsset.d.ts +98 -0
- package/dist/render/materialAsset.js +454 -0
- package/dist/render/measureBounds.js +16 -3
- package/dist/render/measureCollisionMesh.js +8 -1
- package/dist/render/modelAssets.d.ts +8 -0
- package/dist/render/modelAssets.js +43 -0
- package/dist/render/modelBindPose.d.ts +5 -0
- package/dist/render/modelBindPose.js +35 -0
- package/dist/render/modelLoad.d.ts +32 -0
- package/dist/render/modelLoad.js +70 -1
- package/dist/render/modelNodeVisibility.d.ts +6 -0
- package/dist/render/modelNodeVisibility.js +18 -0
- package/dist/render/modelRender.d.ts +18 -1
- package/dist/render/modelRender.js +81 -3
- package/dist/render/projectedOverdraw.d.ts +35 -0
- package/dist/render/projectedOverdraw.js +204 -0
- package/dist/render/resolveModel.d.ts +12 -2
- package/dist/render/resolveModel.js +25 -4
- package/dist/render/rigNode.d.ts +26 -0
- package/dist/render/rigNode.js +32 -0
- package/dist/render/spriteRender.d.ts +4 -0
- package/dist/render/spriteRender.js +5 -0
- package/dist/render/staticShapeInstances.d.ts +34 -0
- package/dist/render/staticShapeInstances.js +78 -0
- package/dist/render/useFootIk.d.ts +53 -4
- package/dist/render/useFootIk.js +262 -70
- package/dist/render/useModelAnimation.d.ts +50 -0
- package/dist/render/useModelAnimation.js +245 -99
- package/dist/render/useModelInstance.d.ts +25 -0
- package/dist/render/useModelInstance.js +43 -0
- package/dist/scatter/scatterModels.d.ts +3 -2
- package/dist/scatter/scatterModels.js +15 -7
- package/dist/scene/AuthoredScene.d.ts +23 -6
- package/dist/scene/AuthoredScene.js +46 -9
- package/dist/scene/CityRenderer.js +6 -6
- package/dist/scene/GeneratedAssetRenderer.js +11 -42
- package/dist/settings/QuickControls.js +1 -1
- package/dist/settings/SettingsMenu.js +11 -18
- package/dist/settings/SettingsRuntime.d.ts +5 -0
- package/dist/settings/SettingsRuntime.js +29 -2
- package/dist/settings/appliedSettings.d.ts +9 -3
- package/dist/settings/appliedSettings.js +6 -5
- package/dist/settings/settingsController.d.ts +3 -0
- package/dist/settings/settingsController.js +47 -3
- package/dist/shellControlSuspension.d.ts +18 -0
- package/dist/shellControlSuspension.js +28 -0
- package/dist/shellInputPublication.d.ts +28 -0
- package/dist/shellInputPublication.js +49 -0
- package/dist/shellPointerInput.d.ts +18 -0
- package/dist/shellPointerInput.js +49 -0
- package/dist/structures/BuildingKitBatch.d.ts +3 -1
- package/dist/structures/BuildingKitBatch.js +35 -56
- package/dist/structures/BuildingRenderBudget.d.ts +23 -0
- package/dist/structures/BuildingRenderBudget.js +54 -0
- package/dist/structures/GeneratedBuilding.d.ts +3 -15
- package/dist/structures/GeneratedBuilding.js +33 -22
- package/dist/structures/buildingKitMaterials.d.ts +20 -0
- package/dist/structures/buildingKitMaterials.js +70 -0
- package/dist/structures/buildingSpatialBatch.d.ts +11 -0
- package/dist/structures/buildingSpatialBatch.js +45 -0
- package/dist/structures/buildingSurface.d.ts +21 -0
- package/dist/structures/buildingSurface.js +134 -0
- package/dist/terrain/GrassField.d.ts +2 -1
- package/dist/terrain/GrassField.js +59 -5
- package/dist/terrain/grassGeometry.d.ts +10 -0
- package/dist/terrain/grassGeometry.js +55 -0
- package/dist/terrain/grassMaterial.js +2 -2
- package/dist/terrain/terrainDetailMaterial.d.ts +3 -3
- package/dist/terrain/terrainDetailMaterial.js +7 -7
- package/dist/touch/TouchControlsOverlay.js +4 -4
- package/dist/useShellMultiplayerSync.d.ts +1 -1
- package/dist/useShellMultiplayerSync.js +9 -3
- package/dist/vfx/ParticleField.d.ts +23 -20
- package/dist/vfx/ParticleField.js +168 -50
- package/dist/visibility/CullingProvider.js +2 -62
- package/dist/visibility/cullingDriver.d.ts +9 -0
- package/dist/visibility/cullingDriver.js +68 -0
- package/dist/weather/AuthoredWeatherLayer.d.ts +16 -0
- package/dist/weather/AuthoredWeatherLayer.js +31 -0
- package/dist/weather/DustField.d.ts +2 -1
- package/dist/weather/DustField.js +17 -3
- package/dist/weather/FireSpreadLayer.d.ts +4 -2
- package/dist/weather/FireSpreadLayer.js +5 -11
- package/dist/weather/LightningStrike.d.ts +4 -1
- package/dist/weather/LightningStrike.js +13 -17
- package/dist/weather/RainField.d.ts +2 -1
- package/dist/weather/RainField.js +17 -3
- package/dist/weather/RainImpactField.d.ts +16 -0
- package/dist/weather/RainImpactField.js +89 -0
- package/dist/weather/SnowField.d.ts +2 -1
- package/dist/weather/SnowField.js +17 -3
- package/dist/weather/WeatherLayer.d.ts +11 -2
- package/dist/weather/WeatherLayer.js +30 -6
- package/dist/weather/index.d.ts +2 -0
- package/dist/weather/index.js +2 -0
- package/dist/weather/weatherMath.d.ts +2 -0
- package/dist/weather/weatherMath.js +8 -0
- package/dist/weather/weatherShelter.d.ts +18 -0
- package/dist/weather/weatherShelter.js +75 -0
- package/dist/weather/weatherUniforms.d.ts +40 -1
- package/dist/weather/weatherUniforms.js +29 -9
- package/dist/world/AuthoredFlocks.d.ts +22 -0
- package/dist/world/AuthoredFlocks.js +87 -0
- package/dist/world/StaticObjectModels.d.ts +8 -0
- package/dist/world/StaticObjectModels.js +75 -0
- package/dist/world/WorldHud.d.ts +14 -6
- package/dist/world/WorldHud.js +25 -12
- package/dist/world/WorldParticles.d.ts +10 -23
- package/dist/world/WorldParticles.js +210 -62
- package/dist/world/WorldScene.d.ts +3 -1
- package/dist/world/WorldScene.js +41 -26
- package/dist/world/staticObjectBatches.d.ts +35 -0
- package/dist/world/staticObjectBatches.js +163 -0
- package/dist/world/worldBarSamples.d.ts +37 -33
- package/dist/world/worldBarSamples.js +117 -75
- package/package.json +4 -4
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/game-design/SKILL.md +2 -2
- package/skills/jgengine/SKILL.md +28 -6
- package/skills/jgengine/api.md +139 -99
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +10 -2
- package/skills/jgengine-ui/SKILL.md +16 -17
- package/skills/jgengine-ui/api.md +603 -365
- package/skills/jgengine-ui/capabilities.md +131 -1
- package/skills/jgengine-ui/reference-primitives.md +179 -0
- package/skills/jgengine-ui/reference.md +4 -81
- package/skills/jgengine-world/SKILL.md +25 -4
- package/skills/jgengine-world/api.md +1045 -655
- package/skills/jgengine-world/capabilities.md +246 -9
- package/skills/jgengine-world/recipes/character-animation.md +62 -0
- package/skills/jgengine-world/recipes/controls.md +48 -0
- package/skills/jgengine-world/recipes/flight-feel.md +76 -0
- package/skills/jgengine-world/recipes/npc-senses.md +55 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +87 -0
- package/skills/jgengine-world/reference.md +180 -4
- package/skills/jgengine-world/visibility.md +139 -0
- package/skills/level-design/SKILL.md +1 -1
- package/dist/render/testFixtures/proceduralSpriteAtlas.d.ts +0 -15
- package/dist/render/testFixtures/proceduralSpriteAtlas.js +0 -30
- package/dist/render/testFixtures/proceduralTileLayer.d.ts +0 -13
- package/dist/render/testFixtures/proceduralTileLayer.js +0 -30
|
@@ -7,7 +7,8 @@ import { GTAOPass } from "three/examples/jsm/postprocessing/GTAOPass.js";
|
|
|
7
7
|
import { BokehPass } from "three/examples/jsm/postprocessing/BokehPass.js";
|
|
8
8
|
import { SMAAPass } from "three/examples/jsm/postprocessing/SMAAPass.js";
|
|
9
9
|
import { createGradePass } from "./gradeShader.js";
|
|
10
|
-
import {
|
|
10
|
+
import { StylizePass } from "./stylizePass.js";
|
|
11
|
+
import { renderShadowlessPrepass } from "./shadowPrepass.js";
|
|
11
12
|
/**
|
|
12
13
|
* GTAO renders the scene with an `overrideMaterial` normal/depth prepass, which stamps
|
|
13
14
|
* transparent overlay quads (additive VFX sprites, telegraphs, debug gizmos) into the AO
|
|
@@ -18,18 +19,14 @@ import { hidePostfxOverlays, restorePostfxOverlays } from "./postfxOverlay.js";
|
|
|
18
19
|
class OverlayAwareGTAOPass extends GTAOPass {
|
|
19
20
|
hiddenOverlays = [];
|
|
20
21
|
render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) {
|
|
21
|
-
|
|
22
|
-
super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive);
|
|
23
|
-
restorePostfxOverlays(this.hiddenOverlays);
|
|
22
|
+
renderShadowlessPrepass(renderer, this.scene, this.hiddenOverlays, () => super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive));
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
/** DOF's depth prepass has the same `overrideMaterial` overlay-stamping problem as GTAO. */
|
|
27
26
|
class OverlayAwareBokehPass extends BokehPass {
|
|
28
27
|
hiddenOverlays = [];
|
|
29
28
|
render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) {
|
|
30
|
-
|
|
31
|
-
super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive);
|
|
32
|
-
restorePostfxOverlays(this.hiddenOverlays);
|
|
29
|
+
renderShadowlessPrepass(renderer, this.scene, this.hiddenOverlays, () => super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive));
|
|
33
30
|
}
|
|
34
31
|
}
|
|
35
32
|
const TONE_MAPPING = {
|
|
@@ -40,27 +37,21 @@ const TONE_MAPPING = {
|
|
|
40
37
|
linear: THREE.LinearToneMapping,
|
|
41
38
|
none: THREE.NoToneMapping,
|
|
42
39
|
};
|
|
43
|
-
|
|
40
|
+
/** Release each pass and the composer's owned targets. @internal */
|
|
41
|
+
export function disposeGraph(built) {
|
|
44
42
|
for (const pass of built.composer.passes)
|
|
45
43
|
pass.dispose();
|
|
46
|
-
if (built.dof !== null) {
|
|
47
|
-
built.dof.renderTargetDepth.dispose();
|
|
48
|
-
built.dof.materialDepth.dispose();
|
|
49
|
-
built.dof.materialBokeh.dispose();
|
|
50
|
-
built.dof.fsQuad.dispose();
|
|
51
|
-
}
|
|
52
44
|
built.composer.dispose();
|
|
53
45
|
}
|
|
54
|
-
|
|
46
|
+
/** Resize the composer and its passes at the renderer's pixel ratio. @internal */
|
|
47
|
+
export function syncSize(built, width, height, pixelRatio) {
|
|
55
48
|
built.composer.setPixelRatio(pixelRatio);
|
|
56
49
|
built.composer.setSize(width, height);
|
|
57
|
-
if (built.dof !== null)
|
|
58
|
-
built.dof.renderTargetDepth.setSize(width * pixelRatio, height * pixelRatio);
|
|
59
50
|
}
|
|
60
51
|
/**
|
|
61
52
|
* Mounts an `EffectComposer` inside the shell Canvas and takes over rendering
|
|
62
53
|
* (priority-1 `useFrame`, which disables R3F auto-render) to run the configured
|
|
63
|
-
* post chain: RenderPass → GTAO → UnrealBloom → SMAA → OutputPass → Grade. Rendered only
|
|
54
|
+
* post chain: RenderPass → GTAO → UnrealBloom → SMAA → OutputPass → Stylize → Grade. Rendered only
|
|
64
55
|
* when `PlayableGame.postProcessing` is set, so games without it draw unchanged.
|
|
65
56
|
*
|
|
66
57
|
* `stages` (resolved from the player's graphics profile) controls expensive passes
|
|
@@ -101,10 +92,9 @@ export function PostProcessing({ config, quality = "high", stages }) {
|
|
|
101
92
|
const b = config.bloom ?? {};
|
|
102
93
|
composer.addPass(new UnrealBloomPass(new THREE.Vector2(width, height), b.strength ?? 0.32, b.radius ?? 0.55, b.threshold ?? 0.85));
|
|
103
94
|
}
|
|
104
|
-
let dof = null;
|
|
105
95
|
if (resolvedStages.dof && config.dof !== undefined && config.dof !== false) {
|
|
106
96
|
const d = config.dof;
|
|
107
|
-
dof = new OverlayAwareBokehPass(scene, camera, {
|
|
97
|
+
const dof = new OverlayAwareBokehPass(scene, camera, {
|
|
108
98
|
focus: d.focus ?? 18,
|
|
109
99
|
aperture: d.aperture ?? (quality === "high" ? 0.00025 : 0.00012),
|
|
110
100
|
maxblur: d.maxBlur ?? 0.01,
|
|
@@ -116,12 +106,15 @@ export function PostProcessing({ config, quality = "high", stages }) {
|
|
|
116
106
|
composer.addPass(new SMAAPass());
|
|
117
107
|
}
|
|
118
108
|
composer.addPass(new OutputPass());
|
|
109
|
+
if (config.stylize !== undefined && config.stylize !== false) {
|
|
110
|
+
composer.addPass(new StylizePass(scene, camera, config.stylize));
|
|
111
|
+
}
|
|
119
112
|
let grade = null;
|
|
120
113
|
if (config.grade !== false) {
|
|
121
114
|
grade = createGradePass(config.grade ?? {});
|
|
122
115
|
composer.addPass(grade);
|
|
123
116
|
}
|
|
124
|
-
const graph = { composer, grade
|
|
117
|
+
const graph = { composer, grade };
|
|
125
118
|
syncSize(graph, width, height, gl.getPixelRatio());
|
|
126
119
|
builtRef.current = graph;
|
|
127
120
|
return () => {
|
|
@@ -129,27 +122,6 @@ export function PostProcessing({ config, quality = "high", stages }) {
|
|
|
129
122
|
builtRef.current = null;
|
|
130
123
|
};
|
|
131
124
|
}, [gl, scene, camera, config, quality, stages]);
|
|
132
|
-
useEffect(() => {
|
|
133
|
-
const lut = config.grade !== undefined && config.grade !== false ? config.grade.lut : undefined;
|
|
134
|
-
const grade = builtRef.current?.grade;
|
|
135
|
-
if (lut === undefined || grade === null || grade === undefined)
|
|
136
|
-
return;
|
|
137
|
-
let cancelled = false;
|
|
138
|
-
const loader = new THREE.TextureLoader();
|
|
139
|
-
loader.loadAsync(lut.url).then((texture) => {
|
|
140
|
-
if (cancelled) {
|
|
141
|
-
texture.dispose();
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
texture.colorSpace = THREE.SRGBColorSpace;
|
|
145
|
-
texture.minFilter = THREE.NearestFilter;
|
|
146
|
-
texture.magFilter = THREE.NearestFilter;
|
|
147
|
-
texture.generateMipmaps = false;
|
|
148
|
-
grade.uniforms.uLut.value = texture;
|
|
149
|
-
grade.uniforms.uLutSize.value = lut.size ?? 32;
|
|
150
|
-
}).catch(() => undefined);
|
|
151
|
-
return () => { cancelled = true; };
|
|
152
|
-
}, [config.grade]);
|
|
153
125
|
useEffect(() => {
|
|
154
126
|
const prevTone = gl.toneMapping;
|
|
155
127
|
const prevExposure = gl.toneMappingExposure;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ShaderPass } from "three-stdlib";
|
|
2
2
|
import type { GradeConfig } from "@jgengine/core/render/postProcessing";
|
|
3
|
-
/** Build the display-space colour-grade pass
|
|
3
|
+
/** Build the display-space colour-grade pass and load its optional LUT. Dispose the pass to release its LUT; advance `uniforms.uTime.value` each frame to animate grain. */
|
|
4
4
|
export declare function createGradePass(config?: GradeConfig): ShaderPass;
|
|
@@ -61,11 +61,11 @@ const vertexShader = /* glsl */ `
|
|
|
61
61
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
62
62
|
}
|
|
63
63
|
`;
|
|
64
|
-
/** Build the display-space colour-grade pass
|
|
64
|
+
/** Build the display-space colour-grade pass and load its optional LUT. Dispose the pass to release its LUT; advance `uniforms.uTime.value` each frame to animate grain. */
|
|
65
65
|
export function createGradePass(config = {}) {
|
|
66
66
|
const lift = config.lift ?? DEFAULT_LIFT;
|
|
67
67
|
const gain = config.gain ?? DEFAULT_GAIN;
|
|
68
|
-
|
|
68
|
+
const pass = new ShaderPass({
|
|
69
69
|
uniforms: {
|
|
70
70
|
tDiffuse: { value: null },
|
|
71
71
|
uLift: { value: new THREE.Vector3(lift[0], lift[1], lift[2]) },
|
|
@@ -82,4 +82,32 @@ export function createGradePass(config = {}) {
|
|
|
82
82
|
vertexShader,
|
|
83
83
|
fragmentShader,
|
|
84
84
|
});
|
|
85
|
+
let disposed = false;
|
|
86
|
+
let lutTexture = null;
|
|
87
|
+
const dispose = pass.dispose.bind(pass);
|
|
88
|
+
pass.dispose = () => {
|
|
89
|
+
if (disposed)
|
|
90
|
+
return;
|
|
91
|
+
disposed = true;
|
|
92
|
+
lutTexture?.dispose();
|
|
93
|
+
lutTexture = null;
|
|
94
|
+
dispose();
|
|
95
|
+
};
|
|
96
|
+
if (config.lut !== undefined) {
|
|
97
|
+
const lut = config.lut;
|
|
98
|
+
void new THREE.TextureLoader().loadAsync(lut.url).then((texture) => {
|
|
99
|
+
if (disposed) {
|
|
100
|
+
texture.dispose();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
lutTexture = texture;
|
|
104
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
105
|
+
texture.minFilter = THREE.NearestFilter;
|
|
106
|
+
texture.magFilter = THREE.NearestFilter;
|
|
107
|
+
texture.generateMipmaps = false;
|
|
108
|
+
pass.uniforms.uLut.value = texture;
|
|
109
|
+
pass.uniforms.uLutSize.value = lut.size ?? 32;
|
|
110
|
+
}).catch(() => undefined);
|
|
111
|
+
}
|
|
112
|
+
return pass;
|
|
85
113
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type * as THREE from "three";
|
|
2
|
+
/** Reuse the beauty pass's shadow maps while drawing a scene prepass. @internal */
|
|
3
|
+
export declare function renderShadowlessPrepass<T>(renderer: Pick<THREE.WebGLRenderer, "shadowMap">, scene: THREE.Object3D, hiddenOverlays: THREE.Object3D[], render: () => T): T;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { hidePostfxOverlays, restorePostfxOverlays } from "./postfxOverlay.js";
|
|
2
|
+
/** Reuse the beauty pass's shadow maps while drawing a scene prepass. @internal */
|
|
3
|
+
export function renderShadowlessPrepass(renderer, scene, hiddenOverlays, render) {
|
|
4
|
+
const shadowMap = renderer.shadowMap;
|
|
5
|
+
const autoUpdate = shadowMap.autoUpdate;
|
|
6
|
+
const needsUpdate = shadowMap.needsUpdate;
|
|
7
|
+
hidePostfxOverlays(scene, hiddenOverlays);
|
|
8
|
+
shadowMap.autoUpdate = false;
|
|
9
|
+
shadowMap.needsUpdate = false;
|
|
10
|
+
try {
|
|
11
|
+
return render();
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
shadowMap.autoUpdate = autoUpdate;
|
|
15
|
+
shadowMap.needsUpdate = needsUpdate;
|
|
16
|
+
restorePostfxOverlays(hiddenOverlays);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { Pass } from "three-stdlib";
|
|
3
|
+
import type { StylizeConfig } from "@jgengine/core/render/postProcessing";
|
|
4
|
+
/**
|
|
5
|
+
* Art-style post pass: ink outlines from a normal+depth prepass, cel luminance bands, and
|
|
6
|
+
* pixelation. Runs on the display-space frame after `OutputPass`. The prepass renders only
|
|
7
|
+
* when outlines are on, and reuses the frame's shadow maps instead of re-rendering them.
|
|
8
|
+
*/
|
|
9
|
+
export declare class StylizePass extends Pass {
|
|
10
|
+
private readonly scene;
|
|
11
|
+
private readonly camera;
|
|
12
|
+
private readonly quad;
|
|
13
|
+
private readonly material;
|
|
14
|
+
private readonly normalTarget;
|
|
15
|
+
private readonly normalMaterial;
|
|
16
|
+
private readonly hidden;
|
|
17
|
+
private readonly clearColor;
|
|
18
|
+
constructor(scene: THREE.Scene, camera: THREE.Camera, config: StylizeConfig);
|
|
19
|
+
setSize(width: number, height: number): void;
|
|
20
|
+
render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget): void;
|
|
21
|
+
private renderPrepass;
|
|
22
|
+
dispose(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { FullScreenQuad, Pass } from "three-stdlib";
|
|
3
|
+
import { isPostfxOverlay } from "./postfxOverlay.js";
|
|
4
|
+
const DEFAULT_OUTLINE_COLOR = "#101014";
|
|
5
|
+
const DEFAULT_DEPTH_THRESHOLD = 0.08;
|
|
6
|
+
const DEFAULT_NORMAL_THRESHOLD = 0.35;
|
|
7
|
+
const DEFAULT_FADE_DISTANCE = 160;
|
|
8
|
+
const fragmentShader = /* glsl */ `
|
|
9
|
+
#include <packing>
|
|
10
|
+
uniform sampler2D tDiffuse;
|
|
11
|
+
uniform sampler2D tNormal;
|
|
12
|
+
uniform sampler2D tDepth;
|
|
13
|
+
uniform vec2 uResolution;
|
|
14
|
+
uniform float uNear;
|
|
15
|
+
uniform float uFar;
|
|
16
|
+
uniform bool uOrtho;
|
|
17
|
+
uniform bool uOutline;
|
|
18
|
+
uniform vec3 uOutlineColor;
|
|
19
|
+
uniform float uOutlineOpacity;
|
|
20
|
+
uniform float uThickness;
|
|
21
|
+
uniform float uDepthThreshold;
|
|
22
|
+
uniform float uNormalThreshold;
|
|
23
|
+
uniform float uFade;
|
|
24
|
+
uniform float uBands;
|
|
25
|
+
uniform float uPixel;
|
|
26
|
+
varying vec2 vUv;
|
|
27
|
+
|
|
28
|
+
float viewDepth(vec2 uv) {
|
|
29
|
+
float d = texture2D(tDepth, uv).x;
|
|
30
|
+
return -(uOrtho ? orthographicDepthToViewZ(d, uNear, uFar) : perspectiveDepthToViewZ(d, uNear, uFar));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
vec3 viewNormal(vec2 uv) {
|
|
34
|
+
return texture2D(tNormal, uv).xyz * 2.0 - 1.0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Ink a normal crease only on its nearer side, so a thin or tiny feature stays one line wide
|
|
38
|
+
// instead of stamping a plus-shaped blot onto the surface behind it.
|
|
39
|
+
float crease(vec3 nc, float dc, vec2 uv, float dn) {
|
|
40
|
+
return (1.0 - dot(nc, viewNormal(uv))) * step(dc, dn + dc * 0.02);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void main() {
|
|
44
|
+
vec2 uv = vUv;
|
|
45
|
+
if (uPixel > 1.0) {
|
|
46
|
+
vec2 block = uPixel / uResolution;
|
|
47
|
+
uv = (floor(uv / block) + 0.5) * block;
|
|
48
|
+
}
|
|
49
|
+
vec3 c = texture2D(tDiffuse, uv).rgb;
|
|
50
|
+
|
|
51
|
+
if (uBands > 0.5) {
|
|
52
|
+
float luma = dot(c, vec3(0.2126, 0.7152, 0.0722));
|
|
53
|
+
float x = min(luma * uBands, uBands - 0.001);
|
|
54
|
+
// Each band takes its centre brightness: the darkest band keeps colour instead of going black
|
|
55
|
+
// and the top band never clips a bright sky to flat white. A narrow soft step hides aliasing.
|
|
56
|
+
float q = (floor(x) + 0.5 + smoothstep(0.92, 1.0, fract(x))) / uBands;
|
|
57
|
+
c *= q / max(luma, 1e-4);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (uOutline) {
|
|
61
|
+
vec2 px = uThickness / uResolution;
|
|
62
|
+
float dc = viewDepth(uv);
|
|
63
|
+
float dl = viewDepth(uv - vec2(px.x, 0.0));
|
|
64
|
+
float dr = viewDepth(uv + vec2(px.x, 0.0));
|
|
65
|
+
float dd = viewDepth(uv - vec2(0.0, px.y));
|
|
66
|
+
float du = viewDepth(uv + vec2(0.0, px.y));
|
|
67
|
+
// Laplacian is zero across flat planes at any angle, so only real depth breaks draw lines.
|
|
68
|
+
float lap = abs(dl + dr + dd + du - 4.0 * dc) / max(dc, 1e-3);
|
|
69
|
+
float nearest = min(dc, min(min(dl, dr), min(dd, du)));
|
|
70
|
+
// Same nearer-side rule as creases: the background around a silhouette stays clean.
|
|
71
|
+
float depthEdge = smoothstep(uDepthThreshold, uDepthThreshold * 2.0, lap) * step(dc, nearest + dc * 0.03);
|
|
72
|
+
|
|
73
|
+
vec3 nc = viewNormal(uv);
|
|
74
|
+
float nd = max(
|
|
75
|
+
max(crease(nc, dc, uv - vec2(px.x, 0.0), dl), crease(nc, dc, uv + vec2(px.x, 0.0), dr)),
|
|
76
|
+
max(crease(nc, dc, uv - vec2(0.0, px.y), dd), crease(nc, dc, uv + vec2(0.0, px.y), du))
|
|
77
|
+
);
|
|
78
|
+
float normalEdge = smoothstep(uNormalThreshold, uNormalThreshold + 0.25, nd);
|
|
79
|
+
|
|
80
|
+
float fade = 1.0 - smoothstep(uFade * 0.5, uFade, nearest);
|
|
81
|
+
float edge = max(depthEdge, normalEdge) * fade * uOutlineOpacity;
|
|
82
|
+
c = mix(c, uOutlineColor, clamp(edge, 0.0, 1.0));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
gl_FragColor = vec4(c, 1.0);
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
const vertexShader = /* glsl */ `
|
|
89
|
+
varying vec2 vUv;
|
|
90
|
+
void main() {
|
|
91
|
+
vUv = uv;
|
|
92
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
function skipsDepthPrepass(material) {
|
|
96
|
+
const list = Array.isArray(material) ? material : [material];
|
|
97
|
+
// Shader materials place vertices in their own code, which the override material cannot reproduce.
|
|
98
|
+
return list.some((m) => m.transparent || m.alphaTest > 0 || !m.depthWrite || !m.colorWrite || m.isShaderMaterial === true);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Hide what the outline prepass must not see: marked overlays, points/lines/sprites, custom
|
|
102
|
+
* shader meshes, and transparent or alpha-cut meshes (foliage cards would otherwise outline as
|
|
103
|
+
* solid quads).
|
|
104
|
+
*/
|
|
105
|
+
function hideForPrepass(scene, out) {
|
|
106
|
+
out.length = 0;
|
|
107
|
+
scene.traverse((object) => {
|
|
108
|
+
if (!object.visible)
|
|
109
|
+
return;
|
|
110
|
+
const drawable = object;
|
|
111
|
+
const hide = isPostfxOverlay(object) ||
|
|
112
|
+
object.isPoints === true ||
|
|
113
|
+
object.isLine === true ||
|
|
114
|
+
object.isSprite === true ||
|
|
115
|
+
(drawable.isMesh === true && skipsDepthPrepass(drawable.material));
|
|
116
|
+
if (!hide)
|
|
117
|
+
return;
|
|
118
|
+
object.visible = false;
|
|
119
|
+
out.push(object);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Art-style post pass: ink outlines from a normal+depth prepass, cel luminance bands, and
|
|
124
|
+
* pixelation. Runs on the display-space frame after `OutputPass`. The prepass renders only
|
|
125
|
+
* when outlines are on, and reuses the frame's shadow maps instead of re-rendering them.
|
|
126
|
+
*/
|
|
127
|
+
export class StylizePass extends Pass {
|
|
128
|
+
scene;
|
|
129
|
+
camera;
|
|
130
|
+
quad;
|
|
131
|
+
material;
|
|
132
|
+
normalTarget;
|
|
133
|
+
normalMaterial = new THREE.MeshNormalMaterial();
|
|
134
|
+
hidden = [];
|
|
135
|
+
clearColor = new THREE.Color();
|
|
136
|
+
constructor(scene, camera, config) {
|
|
137
|
+
super();
|
|
138
|
+
this.scene = scene;
|
|
139
|
+
this.camera = camera;
|
|
140
|
+
const outline = config.outline === undefined || config.outline === false ? null : config.outline;
|
|
141
|
+
this.normalTarget = outline === null
|
|
142
|
+
? null
|
|
143
|
+
: new THREE.WebGLRenderTarget(1, 1, { depthTexture: new THREE.DepthTexture(1, 1), type: THREE.UnsignedByteType });
|
|
144
|
+
this.material = new THREE.ShaderMaterial({
|
|
145
|
+
uniforms: {
|
|
146
|
+
tDiffuse: { value: null },
|
|
147
|
+
tNormal: { value: this.normalTarget?.texture ?? null },
|
|
148
|
+
tDepth: { value: this.normalTarget?.depthTexture ?? null },
|
|
149
|
+
uResolution: { value: new THREE.Vector2(1, 1) },
|
|
150
|
+
uNear: { value: 0.1 },
|
|
151
|
+
uFar: { value: 1000 },
|
|
152
|
+
uOrtho: { value: false },
|
|
153
|
+
uOutline: { value: outline !== null },
|
|
154
|
+
uOutlineColor: { value: new THREE.Color(outline?.color ?? DEFAULT_OUTLINE_COLOR) },
|
|
155
|
+
uOutlineOpacity: { value: outline?.opacity ?? 1 },
|
|
156
|
+
uThickness: { value: Math.max(0.5, outline?.thickness ?? 1) },
|
|
157
|
+
uDepthThreshold: { value: outline?.depthThreshold ?? DEFAULT_DEPTH_THRESHOLD },
|
|
158
|
+
uNormalThreshold: { value: outline?.normalThreshold ?? DEFAULT_NORMAL_THRESHOLD },
|
|
159
|
+
uFade: { value: outline?.fadeDistance ?? DEFAULT_FADE_DISTANCE },
|
|
160
|
+
uBands: { value: Math.max(0, Math.floor(config.bands ?? 0)) },
|
|
161
|
+
uPixel: { value: Math.max(0, config.pixelSize ?? 0) },
|
|
162
|
+
},
|
|
163
|
+
vertexShader,
|
|
164
|
+
fragmentShader,
|
|
165
|
+
depthTest: false,
|
|
166
|
+
depthWrite: false,
|
|
167
|
+
});
|
|
168
|
+
this.quad = new FullScreenQuad(this.material);
|
|
169
|
+
}
|
|
170
|
+
setSize(width, height) {
|
|
171
|
+
this.material.uniforms.uResolution.value.set(width, height);
|
|
172
|
+
this.normalTarget?.setSize(width, height);
|
|
173
|
+
}
|
|
174
|
+
render(renderer, writeBuffer, readBuffer) {
|
|
175
|
+
const uniforms = this.material.uniforms;
|
|
176
|
+
if (this.normalTarget !== null)
|
|
177
|
+
this.renderPrepass(renderer, this.normalTarget);
|
|
178
|
+
const camera = this.camera;
|
|
179
|
+
uniforms.uNear.value = camera.near;
|
|
180
|
+
uniforms.uFar.value = camera.far;
|
|
181
|
+
uniforms.uOrtho.value = camera.isOrthographicCamera === true;
|
|
182
|
+
uniforms.tDiffuse.value = readBuffer.texture;
|
|
183
|
+
renderer.setRenderTarget(this.renderToScreen ? null : writeBuffer);
|
|
184
|
+
this.quad.render(renderer);
|
|
185
|
+
}
|
|
186
|
+
renderPrepass(renderer, target) {
|
|
187
|
+
const scene = this.scene;
|
|
188
|
+
const previousOverride = scene.overrideMaterial;
|
|
189
|
+
const previousBackground = scene.background;
|
|
190
|
+
const previousShadowUpdate = renderer.shadowMap.autoUpdate;
|
|
191
|
+
const previousClearAlpha = renderer.getClearAlpha();
|
|
192
|
+
renderer.getClearColor(this.clearColor);
|
|
193
|
+
hideForPrepass(scene, this.hidden);
|
|
194
|
+
scene.overrideMaterial = this.normalMaterial;
|
|
195
|
+
scene.background = null;
|
|
196
|
+
renderer.shadowMap.autoUpdate = false;
|
|
197
|
+
renderer.setRenderTarget(target);
|
|
198
|
+
renderer.setClearColor(0x8080ff, 1);
|
|
199
|
+
renderer.clear();
|
|
200
|
+
renderer.render(scene, this.camera);
|
|
201
|
+
renderer.setClearColor(this.clearColor, previousClearAlpha);
|
|
202
|
+
renderer.shadowMap.autoUpdate = previousShadowUpdate;
|
|
203
|
+
scene.background = previousBackground;
|
|
204
|
+
scene.overrideMaterial = previousOverride;
|
|
205
|
+
for (const object of this.hidden)
|
|
206
|
+
object.visible = true;
|
|
207
|
+
this.hidden.length = 0;
|
|
208
|
+
}
|
|
209
|
+
dispose() {
|
|
210
|
+
this.material.dispose();
|
|
211
|
+
this.normalMaterial.dispose();
|
|
212
|
+
this.quad.dispose();
|
|
213
|
+
this.normalTarget?.depthTexture?.dispose();
|
|
214
|
+
this.normalTarget?.dispose();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactNode } from "react";
|
|
2
|
+
import type { GameContext } from "@jgengine/core/runtime/gameContext";
|
|
3
|
+
/** Marks only a rejected presentation import for page reload; browser module failures are cached. @internal */
|
|
4
|
+
export declare function markPresentationCodeLoadFailure(error: unknown): Error;
|
|
5
|
+
/** @internal Distinguishes module loading from recoverable assets without guessing from error text. */
|
|
6
|
+
export declare function presentationRecoveryAction(error: unknown): "reload" | "retry";
|
|
7
|
+
/** @internal Classifies a reported React error after its owning boundary commits. */
|
|
8
|
+
export declare function deferUnhandledPresentationError(error: unknown, report: () => void): void;
|
|
9
|
+
/** @internal Exact pending texture groups owned by one presentation, including uncommitted Suspense. */
|
|
10
|
+
export declare function createPresentationRetryRegistry(defaultClear?: (inputs: string[]) => void): {
|
|
11
|
+
register(inputs: readonly string[], clear?: ((inputs: string[]) => void) | undefined): () => void;
|
|
12
|
+
retry(): void;
|
|
13
|
+
};
|
|
14
|
+
type RetryRegistry = ReturnType<typeof createPresentationRetryRegistry>;
|
|
15
|
+
type PresentationRetryScope = {
|
|
16
|
+
registry: RetryRegistry;
|
|
17
|
+
fail: (error: unknown, stack?: string) => void;
|
|
18
|
+
recoveredDraw: () => void;
|
|
19
|
+
};
|
|
20
|
+
/** Renderer-only loader adapters share the DOM owner without importing their libraries here. @internal */
|
|
21
|
+
export declare function usePresentationRetryScope(): PresentationRetryScope | null;
|
|
22
|
+
/** @internal Forwards renderer-local loader errors to the owning DOM presentation. */
|
|
23
|
+
export declare function PresentationAssetBoundary({ children }: {
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}): ReactNode;
|
|
26
|
+
/** @internal Retires only diagnostics belonging to this exact handled presentation error. */
|
|
27
|
+
export declare function createPresentationDiagnosticOwnership(): {
|
|
28
|
+
register(error: unknown, id: number, phase: string): void;
|
|
29
|
+
recovered(error: unknown): ReadonlySet<number>;
|
|
30
|
+
};
|
|
31
|
+
/** Restores an owned recovered surface only after Suspense has attached and revealed it. @internal */
|
|
32
|
+
export declare function createPlaySurfaceFocusOwnership<Owner>(): {
|
|
33
|
+
request(owner: Owner): void;
|
|
34
|
+
reveal(owner: Owner, surface: HTMLElement | null, active: Element | null, body: HTMLElement): boolean;
|
|
35
|
+
};
|
|
36
|
+
/** Reports the owning presentation attempt and recovery lifecycle. @internal */
|
|
37
|
+
export type PresentationRecoveryEvent = {
|
|
38
|
+
phase: string;
|
|
39
|
+
attempt: number;
|
|
40
|
+
scope: number;
|
|
41
|
+
sameOwner: boolean;
|
|
42
|
+
pending: boolean;
|
|
43
|
+
failed: boolean;
|
|
44
|
+
};
|
|
45
|
+
type RecoveryProps = {
|
|
46
|
+
ctx: GameContext;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
onRuntimeError: (error: unknown, phase: string, componentStack?: string) => void;
|
|
49
|
+
onRetryCommitted?: () => void;
|
|
50
|
+
onRecoveredDraw?: (error: unknown) => void;
|
|
51
|
+
onRecoveryEvent?: (event: PresentationRecoveryEvent) => void;
|
|
52
|
+
};
|
|
53
|
+
type RecoveryState = {
|
|
54
|
+
owner: GameContext;
|
|
55
|
+
failed: boolean;
|
|
56
|
+
error: unknown;
|
|
57
|
+
registry: RetryRegistry;
|
|
58
|
+
};
|
|
59
|
+
/** @internal Retains the live context for asset Retry; failed browser modules require page reload. */
|
|
60
|
+
export declare class PresentationRecovery extends Component<RecoveryProps, RecoveryState> {
|
|
61
|
+
state: RecoveryState;
|
|
62
|
+
static getDerivedStateFromProps(props: RecoveryProps, state: RecoveryState): Partial<RecoveryState> | null;
|
|
63
|
+
static getDerivedStateFromError(error: unknown): Partial<RecoveryState>;
|
|
64
|
+
componentDidCatch(error: unknown, info: ErrorInfo): void;
|
|
65
|
+
componentDidUpdate(previousProps: RecoveryProps, previousState: RecoveryState): void;
|
|
66
|
+
componentWillUnmount(): void;
|
|
67
|
+
private attempt;
|
|
68
|
+
private scopeId;
|
|
69
|
+
private trace;
|
|
70
|
+
private scope;
|
|
71
|
+
private pendingRecovery;
|
|
72
|
+
private retry;
|
|
73
|
+
render(): ReactNode;
|
|
74
|
+
}
|
|
75
|
+
export {};
|