@vgai/engine 0.2.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/LICENSE +202 -0
- package/README.md +35 -0
- package/package.json +55 -0
- package/src/adapter/authoring.ts +402 -0
- package/src/adapter/colyseus-networking-adapter.ts +72 -0
- package/src/adapter/first-party-systems.ts +103 -0
- package/src/adapter/game-adapter.ts +151 -0
- package/src/adapter/host-context.ts +77 -0
- package/src/adapter/index.ts +85 -0
- package/src/adapter/ingest/game-contract.ts +59 -0
- package/src/adapter/ingest/overlay-applier.ts +207 -0
- package/src/adapter/ingest/overlay-apply.ts +124 -0
- package/src/adapter/ingest/overlay-file.ts +126 -0
- package/src/adapter/ingest/overlay-report.ts +176 -0
- package/src/adapter/ingest/scene-capture.ts +307 -0
- package/src/adapter/ingest/upstream-pin.ts +52 -0
- package/src/adapter/loop-gate-report.ts +54 -0
- package/src/adapter/rapier-physics-adapter.ts +56 -0
- package/src/adapter/system-adapter.ts +154 -0
- package/src/adapter/transform.ts +18 -0
- package/src/adapter/vgai-scene-game-adapter.ts +886 -0
- package/src/adapter/world-kind.ts +34 -0
- package/src/ai/navigation.ts +164 -0
- package/src/animation/anim-graph-types.ts +56 -0
- package/src/animation/anim-graph.ts +406 -0
- package/src/animation/anim-system.ts +28 -0
- package/src/animation/blend-node.ts +119 -0
- package/src/animation/property-track.ts +178 -0
- package/src/animation/schema.ts +204 -0
- package/src/assets.ts +80 -0
- package/src/audio/ambient.ts +300 -0
- package/src/audio/impacts.ts +212 -0
- package/src/audio/index.ts +7 -0
- package/src/audio/movement.ts +140 -0
- package/src/audio/musical.ts +200 -0
- package/src/audio/ui-sounds.ts +171 -0
- package/src/audio/vehicle.ts +235 -0
- package/src/audio/weapons.ts +152 -0
- package/src/core/game-loop.ts +127 -0
- package/src/core/system-runner.ts +298 -0
- package/src/core/types.ts +58 -0
- package/src/dev/console-bridge.ts +83 -0
- package/src/dev/debug-draw.ts +80 -0
- package/src/dev/logger.ts +119 -0
- package/src/ecs/component-manager.ts +748 -0
- package/src/ecs/game-component.ts +147 -0
- package/src/ecs/hmr-swap-report.ts +65 -0
- package/src/input/input-manager.ts +439 -0
- package/src/input/input-types.ts +19 -0
- package/src/input/schema.ts +129 -0
- package/src/loader.ts +70 -0
- package/src/manifest/index.ts +24 -0
- package/src/manifest/load-file.ts +16 -0
- package/src/manifest/load.ts +378 -0
- package/src/manifest/schema.ts +375 -0
- package/src/physics/collision-system.ts +76 -0
- package/src/physics/physics-registry.ts +83 -0
- package/src/physics/transform-writer.ts +41 -0
- package/src/physics/trigger-dispatch.ts +97 -0
- package/src/react/game-state.tsx +172 -0
- package/src/render/auto-batcher.ts +169 -0
- package/src/render/render-batch-system.ts +268 -0
- package/src/render/render-features.ts +146 -0
- package/src/render/render-settings.ts +72 -0
- package/src/runtime/create-runtime.ts +1152 -0
- package/src/runtime/frame-selector-cache.ts +81 -0
- package/src/runtime/game.ts +1003 -0
- package/src/runtime/input-router.ts +213 -0
- package/src/runtime/mount-game.ts +269 -0
- package/src/runtime/mount-manifest.ts +361 -0
- package/src/runtime/scene-ui-bridge.ts +86 -0
- package/src/runtime/scene-ui-data.ts +119 -0
- package/src/runtime/state-bridge.ts +79 -0
- package/src/runtime/types.ts +196 -0
- package/src/scene/asset-loaders.ts +195 -0
- package/src/scene/asset-paths.ts +123 -0
- package/src/scene/asset-registry.ts +67 -0
- package/src/scene/collider-dimensions.ts +125 -0
- package/src/scene/component-registry.ts +40 -0
- package/src/scene/defaults.ts +164 -0
- package/src/scene/geometries/index.ts +7 -0
- package/src/scene/geometries/terrain.ts +42 -0
- package/src/scene/geometry-registry.ts +42 -0
- package/src/scene/instance-registry.ts +84 -0
- package/src/scene/instancers/grid.ts +38 -0
- package/src/scene/instancers/index.ts +7 -0
- package/src/scene/light-camera-factory.ts +97 -0
- package/src/scene/material-factory.ts +211 -0
- package/src/scene/material-registry.ts +73 -0
- package/src/scene/materials/index.ts +7 -0
- package/src/scene/materials/water.ts +56 -0
- package/src/scene/parse.ts +71 -0
- package/src/scene/particles-factory.ts +383 -0
- package/src/scene/scene-apply.ts +356 -0
- package/src/scene/scene-diff-schema.ts +115 -0
- package/src/scene/scene-diff-types.ts +29 -0
- package/src/scene/scene-loader.ts +1533 -0
- package/src/scene/scene-query.ts +63 -0
- package/src/scene/scene-types.ts +34 -0
- package/src/scene/scene-version.ts +40 -0
- package/src/scene/schema/animation.ts +95 -0
- package/src/scene/schema/audio.ts +25 -0
- package/src/scene/schema/camera.ts +21 -0
- package/src/scene/schema/collider.ts +69 -0
- package/src/scene/schema/entity-ref.ts +78 -0
- package/src/scene/schema/entity.ts +169 -0
- package/src/scene/schema/environment.ts +384 -0
- package/src/scene/schema/index.ts +95 -0
- package/src/scene/schema/instances.ts +35 -0
- package/src/scene/schema/joint.ts +26 -0
- package/src/scene/schema/light.ts +38 -0
- package/src/scene/schema/material.ts +113 -0
- package/src/scene/schema/mesh.ts +108 -0
- package/src/scene/schema/particles.ts +398 -0
- package/src/scene/schema/physics.ts +49 -0
- package/src/scene/schema/scene-file.ts +299 -0
- package/src/scene/schema/shadow.ts +24 -0
- package/src/scene/schema/spline.ts +21 -0
- package/src/scene/schema/tuples.ts +21 -0
- package/src/scene/schema/ui.ts +602 -0
- package/src/scene/user-data.ts +203 -0
- package/src/setup/setup-audio.ts +60 -0
- package/src/setup/setup-particles.ts +23 -0
- package/src/setup/setup-physics.ts +67 -0
- package/src/setup/setup-renderer.ts +529 -0
- package/src/types-n8ao.d.ts +37 -0
- package/src/types-realism-effects.d.ts +61 -0
- package/src/world2d/authoring-2d.ts +208 -0
- package/src/world2d/capture-to-scene2d.ts +52 -0
- package/src/world2d/collision-2d.ts +106 -0
- package/src/world2d/components-2d.ts +86 -0
- package/src/world2d/index.ts +66 -0
- package/src/world2d/ingest-iframe-2d.ts +255 -0
- package/src/world2d/ingest2d.ts +131 -0
- package/src/world2d/physics2d-registry.ts +49 -0
- package/src/world2d/pixi-game-adapter.ts +325 -0
- package/src/world2d/pixi-surface.ts +78 -0
- package/src/world2d/scene-capture-2d.ts +117 -0
- package/src/world2d/scene2d-loader.ts +308 -0
- package/src/world2d/schema/entity2d.ts +145 -0
- package/src/world2d/schema/physics2d.ts +53 -0
- package/src/world2d/schema/sprite.ts +71 -0
- package/src/world2d/schema/tilemap.ts +22 -0
- package/src/world2d/schema/tuples2d.ts +25 -0
- package/src/world2d/system-adapters-2d.ts +49 -0
- package/src/world2d/transform-writer-2d.ts +24 -0
- package/src/world2d/types.ts +55 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { N8AOPostPass } from 'n8ao';
|
|
2
|
+
import {
|
|
3
|
+
BloomEffect,
|
|
4
|
+
BrightnessContrastEffect,
|
|
5
|
+
ChromaticAberrationEffect,
|
|
6
|
+
ColorDepthEffect,
|
|
7
|
+
DepthOfFieldEffect,
|
|
8
|
+
DotScreenEffect,
|
|
9
|
+
type Effect,
|
|
10
|
+
EffectComposer,
|
|
11
|
+
EffectPass,
|
|
12
|
+
GlitchEffect,
|
|
13
|
+
GodRaysEffect,
|
|
14
|
+
GridEffect,
|
|
15
|
+
HueSaturationEffect,
|
|
16
|
+
LensDistortionEffect,
|
|
17
|
+
NoiseEffect,
|
|
18
|
+
NormalPass,
|
|
19
|
+
OutlineEffect,
|
|
20
|
+
PixelationEffect,
|
|
21
|
+
RenderPass,
|
|
22
|
+
ScanlineEffect,
|
|
23
|
+
SepiaEffect,
|
|
24
|
+
ShockWaveEffect,
|
|
25
|
+
SMAAEffect,
|
|
26
|
+
SMAAPreset,
|
|
27
|
+
SSAOEffect,
|
|
28
|
+
TiltShiftEffect,
|
|
29
|
+
VignetteEffect,
|
|
30
|
+
} from 'postprocessing';
|
|
31
|
+
import { MotionBlurEffect, SSGIEffect, SSREffect, VelocityDepthNormalPass } from 'realism-effects';
|
|
32
|
+
import * as THREE from 'three';
|
|
33
|
+
import { type RenderScope, resolveRenderSettings } from '../render/render-settings';
|
|
34
|
+
import { DEFAULTS } from '../scene/defaults';
|
|
35
|
+
import type { SceneEnvironment } from '../scene/scene-types';
|
|
36
|
+
|
|
37
|
+
export interface RendererContext {
|
|
38
|
+
renderer: THREE.WebGLRenderer;
|
|
39
|
+
scene: THREE.Scene;
|
|
40
|
+
camera: THREE.PerspectiveCamera;
|
|
41
|
+
composer: EffectComposer;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Apply the live renderer-level render settings (shadows, resolution scale) from
|
|
46
|
+
* the resolved cascade. `antialias`/`backend` are reload-only (renderer re-init)
|
|
47
|
+
* and are handled at construction, not here. Pass the scene `rendering` scope
|
|
48
|
+
* (e.g. `env.rendering`); absent values fall back to the registry defaults so
|
|
49
|
+
* behavior is unchanged when a scene omits `rendering`.
|
|
50
|
+
*
|
|
51
|
+
* `basePixelRatio` is the call-site's native pixel-ratio policy (the runtime caps
|
|
52
|
+
* at 2; the editor uses the raw devicePixelRatio); `resolutionScale` multiplies it.
|
|
53
|
+
*/
|
|
54
|
+
export function applyRendererSettings(
|
|
55
|
+
renderer: THREE.WebGLRenderer,
|
|
56
|
+
rendering?: RenderScope,
|
|
57
|
+
basePixelRatio = Math.min(window.devicePixelRatio, 2),
|
|
58
|
+
): void {
|
|
59
|
+
const settings = resolveRenderSettings(rendering);
|
|
60
|
+
renderer.shadowMap.enabled = Boolean(settings['shadows']);
|
|
61
|
+
const scale = Number(settings['resolutionScale']) || 1;
|
|
62
|
+
renderer.setPixelRatio(basePixelRatio * scale);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create ONLY the WebGL renderer. The HOST owns the renderer (see
|
|
67
|
+
* `HostContext.renderer`); the game/adapter owns the scene/camera/composer it
|
|
68
|
+
* renders into. Split out of `setupRenderer` for the adapter inversion.
|
|
69
|
+
*
|
|
70
|
+
* `rendering` is the scene-level render scope (`env.rendering`); it seeds the
|
|
71
|
+
* reload-only `antialias` option at construction and the live shadow/resolution
|
|
72
|
+
* settings (via {@link applyRendererSettings}). Absent → registry defaults.
|
|
73
|
+
*
|
|
74
|
+
* `opts` is additive (T6.1 slice 1, COMPOSITION-DESIGN.md D5 §1/§4): omitted
|
|
75
|
+
* entirely, construction is byte-identical to before (no `alpha`/
|
|
76
|
+
* `preserveDrawingBuffer` keys at all) — the legacy single-canvas host never
|
|
77
|
+
* passes it. The worlds path passes `alpha:true` for every stacked canvas
|
|
78
|
+
* above the bottom one (so its clear-alpha-0 shows the layer below through
|
|
79
|
+
* it) and `preserveDrawingBuffer:true` for every stacked canvas (the
|
|
80
|
+
* recorded capture-tier cost, paid once here rather than re-derived later).
|
|
81
|
+
*/
|
|
82
|
+
export function createHostRenderer(
|
|
83
|
+
canvas: HTMLCanvasElement,
|
|
84
|
+
width = window.innerWidth,
|
|
85
|
+
height = window.innerHeight,
|
|
86
|
+
rendering?: RenderScope,
|
|
87
|
+
opts?: { alpha?: boolean; preserveDrawingBuffer?: boolean },
|
|
88
|
+
): THREE.WebGLRenderer {
|
|
89
|
+
const settings = resolveRenderSettings(rendering);
|
|
90
|
+
const renderer = new THREE.WebGLRenderer({
|
|
91
|
+
canvas,
|
|
92
|
+
antialias: Boolean(settings['antialias']),
|
|
93
|
+
powerPreference: 'high-performance',
|
|
94
|
+
...(opts?.alpha ? { alpha: true } : {}),
|
|
95
|
+
...(opts?.preserveDrawingBuffer ? { preserveDrawingBuffer: true } : {}),
|
|
96
|
+
});
|
|
97
|
+
renderer.setSize(width, height);
|
|
98
|
+
applyRendererSettings(renderer, rendering);
|
|
99
|
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
100
|
+
renderer.toneMapping = toneMappingModes[DEFAULTS.toneMapping.mode] ?? THREE.ACESFilmicToneMapping;
|
|
101
|
+
renderer.toneMappingExposure = DEFAULTS.toneMapping.exposure;
|
|
102
|
+
return renderer;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create the scene, camera, and postprocessing composer that a first-party game
|
|
107
|
+
* renders into, given a host-provided renderer. The GPU-only `composer` is
|
|
108
|
+
* created here; a headless host (no GPU) skips this entirely.
|
|
109
|
+
*/
|
|
110
|
+
export function createSceneView(
|
|
111
|
+
renderer: THREE.WebGLRenderer,
|
|
112
|
+
width = window.innerWidth,
|
|
113
|
+
height = window.innerHeight,
|
|
114
|
+
): { scene: THREE.Scene; camera: THREE.PerspectiveCamera; composer: EffectComposer } {
|
|
115
|
+
const scene = new THREE.Scene();
|
|
116
|
+
scene.background = new THREE.Color(0x1a1a2e);
|
|
117
|
+
|
|
118
|
+
const camera = new THREE.PerspectiveCamera(
|
|
119
|
+
DEFAULTS.camera.fov,
|
|
120
|
+
width / height,
|
|
121
|
+
DEFAULTS.camera.near,
|
|
122
|
+
DEFAULTS.camera.far,
|
|
123
|
+
);
|
|
124
|
+
camera.position.set(0, 5, 10);
|
|
125
|
+
camera.lookAt(0, 0, 0);
|
|
126
|
+
|
|
127
|
+
const composer = new EffectComposer(renderer);
|
|
128
|
+
composer.addPass(new RenderPass(scene, camera));
|
|
129
|
+
composer.addPass(
|
|
130
|
+
new EffectPass(
|
|
131
|
+
camera,
|
|
132
|
+
new BloomEffect({ luminanceThreshold: DEFAULTS.postProcessing.bloom.luminanceThreshold }),
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return { scene, camera, composer };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Creates the Three.js renderer, scene, camera, and postprocessing pipeline.
|
|
141
|
+
*
|
|
142
|
+
* This is direct Three.js setup — no abstraction. Edit freely. (Now a thin
|
|
143
|
+
* combination of {@link createHostRenderer} + {@link createSceneView}.)
|
|
144
|
+
*/
|
|
145
|
+
export function setupRenderer(
|
|
146
|
+
canvas: HTMLCanvasElement,
|
|
147
|
+
width = window.innerWidth,
|
|
148
|
+
height = window.innerHeight,
|
|
149
|
+
): RendererContext {
|
|
150
|
+
const renderer = createHostRenderer(canvas, width, height);
|
|
151
|
+
const { scene, camera, composer } = createSceneView(renderer, width, height);
|
|
152
|
+
return { renderer, scene, camera, composer };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const toneMappingModes: Record<string, THREE.ToneMapping> = {
|
|
156
|
+
none: THREE.NoToneMapping,
|
|
157
|
+
linear: THREE.LinearToneMapping,
|
|
158
|
+
reinhard: THREE.ReinhardToneMapping,
|
|
159
|
+
cineon: THREE.CineonToneMapping,
|
|
160
|
+
aces: THREE.ACESFilmicToneMapping,
|
|
161
|
+
agx: THREE.AgXToneMapping,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const smaaPresets: Record<string, SMAAPreset> = {
|
|
165
|
+
low: SMAAPreset.LOW,
|
|
166
|
+
medium: SMAAPreset.MEDIUM,
|
|
167
|
+
high: SMAAPreset.HIGH,
|
|
168
|
+
ultra: SMAAPreset.ULTRA,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Rebuild composer passes from scene environment settings.
|
|
173
|
+
* Scenes without toneMapping default to ACES (the runtime default).
|
|
174
|
+
* Code can further modify the composer after this call.
|
|
175
|
+
*
|
|
176
|
+
* Pass objectMap to enable entity-referencing effects (godRays, outline).
|
|
177
|
+
* Pass entities to resolve tag-based references (outline).
|
|
178
|
+
*/
|
|
179
|
+
export function applyScenePostProcessing(
|
|
180
|
+
composer: EffectComposer,
|
|
181
|
+
renderer: THREE.WebGLRenderer,
|
|
182
|
+
scene: THREE.Scene,
|
|
183
|
+
camera: THREE.Camera,
|
|
184
|
+
env?: SceneEnvironment,
|
|
185
|
+
objectMap?: Map<string, THREE.Object3D>,
|
|
186
|
+
entityTags?: Map<string, string[]>,
|
|
187
|
+
): void {
|
|
188
|
+
// Dispose the existing passes before discarding them — removeAllPasses() just
|
|
189
|
+
// drops the references, so their GPU render targets would otherwise leak on
|
|
190
|
+
// every env edit / scene switch.
|
|
191
|
+
for (const pass of composer.passes) {
|
|
192
|
+
(pass as { dispose?: () => void }).dispose?.();
|
|
193
|
+
}
|
|
194
|
+
composer.removeAllPasses();
|
|
195
|
+
composer.addPass(new RenderPass(scene, camera));
|
|
196
|
+
|
|
197
|
+
// ── Tone mapping ──────────────────────────────────────────────────────
|
|
198
|
+
const tm = env?.toneMapping;
|
|
199
|
+
renderer.toneMapping =
|
|
200
|
+
toneMappingModes[tm?.mode ?? DEFAULTS.toneMapping.mode] ?? THREE.ACESFilmicToneMapping;
|
|
201
|
+
renderer.toneMappingExposure = tm?.exposure ?? DEFAULTS.toneMapping.exposure;
|
|
202
|
+
|
|
203
|
+
// ── Master switch ─────────────────────────────────────────────────────
|
|
204
|
+
// When post-processing is disabled at the scene/engine level, render the
|
|
205
|
+
// plain scene (the RenderPass added above) and skip every effect pass. The
|
|
206
|
+
// per-effect `enabled` flags below still apply when the master switch is on.
|
|
207
|
+
if (!resolveRenderSettings(env?.rendering as RenderScope | undefined)['postProcessing']) return;
|
|
208
|
+
|
|
209
|
+
const pp = env?.postProcessing;
|
|
210
|
+
const d = DEFAULTS.postProcessing;
|
|
211
|
+
|
|
212
|
+
// ── Prerequisite passes ───────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
// NormalPass — needed by SSAO
|
|
215
|
+
let normalPass: NormalPass | undefined;
|
|
216
|
+
if (pp?.ssao?.enabled) {
|
|
217
|
+
normalPass = new NormalPass(scene, camera);
|
|
218
|
+
composer.addPass(normalPass);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// VelocityDepthNormalPass — needed by SSR, SSGI, MotionBlur
|
|
222
|
+
let velocityPass: VelocityDepthNormalPass | undefined;
|
|
223
|
+
if (pp?.ssr?.enabled || pp?.ssgi?.enabled || pp?.motionBlur?.enabled) {
|
|
224
|
+
velocityPass = new VelocityDepthNormalPass(scene, camera);
|
|
225
|
+
composer.addPass(velocityPass);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// N8AOPostPass — separate Pass, not an Effect
|
|
229
|
+
if (pp?.n8ao?.enabled) {
|
|
230
|
+
const n8aoPass = new N8AOPostPass(scene, camera as THREE.Camera);
|
|
231
|
+
const cfg = n8aoPass.configuration;
|
|
232
|
+
cfg.aoSamples = pp.n8ao.aoSamples ?? d.n8ao.aoSamples;
|
|
233
|
+
cfg.aoRadius = pp.n8ao.aoRadius ?? d.n8ao.aoRadius;
|
|
234
|
+
cfg.intensity = pp.n8ao.intensity ?? d.n8ao.intensity;
|
|
235
|
+
cfg.denoiseSamples = pp.n8ao.denoiseSamples ?? d.n8ao.denoiseSamples;
|
|
236
|
+
cfg.denoiseRadius = pp.n8ao.denoiseRadius ?? d.n8ao.denoiseRadius;
|
|
237
|
+
cfg.distanceFalloff = pp.n8ao.distanceFalloff ?? d.n8ao.distanceFalloff;
|
|
238
|
+
if (pp.n8ao.screenSpaceRadius !== undefined) cfg.screenSpaceRadius = pp.n8ao.screenSpaceRadius;
|
|
239
|
+
if (pp.n8ao.halfRes !== undefined) cfg.halfRes = pp.n8ao.halfRes;
|
|
240
|
+
if (pp.n8ao.color) cfg.color = new THREE.Color(pp.n8ao.color);
|
|
241
|
+
composer.addPass(n8aoPass);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ── Collect Effect instances ──────────────────────────────────────────
|
|
245
|
+
const effects: Effect[] = [];
|
|
246
|
+
|
|
247
|
+
// Anti-aliasing
|
|
248
|
+
if (pp?.smaa?.enabled) {
|
|
249
|
+
const preset = smaaPresets[pp.smaa.preset ?? d.smaa.preset] ?? SMAAPreset.HIGH;
|
|
250
|
+
effects.push(new SMAAEffect({ preset }));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Ambient occlusion
|
|
254
|
+
if (pp?.ssao?.enabled && normalPass) {
|
|
255
|
+
effects.push(
|
|
256
|
+
new SSAOEffect(camera, normalPass.texture, {
|
|
257
|
+
radius: pp.ssao.radius ?? d.ssao.radius,
|
|
258
|
+
intensity: pp.ssao.intensity ?? d.ssao.intensity,
|
|
259
|
+
bias: pp.ssao.bias ?? d.ssao.bias,
|
|
260
|
+
samples: pp.ssao.samples ?? d.ssao.samples,
|
|
261
|
+
rings: pp.ssao.rings ?? d.ssao.rings,
|
|
262
|
+
fade: pp.ssao.fade ?? d.ssao.fade,
|
|
263
|
+
...(pp.ssao.color ? { color: new THREE.Color(pp.ssao.color) } : {}),
|
|
264
|
+
}),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// SSR (realism-effects)
|
|
269
|
+
if (pp?.ssr?.enabled && velocityPass) {
|
|
270
|
+
effects.push(
|
|
271
|
+
new SSREffect(scene, camera, velocityPass, {
|
|
272
|
+
intensity: pp.ssr.intensity ?? d.ssr.intensity,
|
|
273
|
+
exponent: pp.ssr.exponent ?? d.ssr.exponent,
|
|
274
|
+
distance: pp.ssr.distance ?? d.ssr.distance,
|
|
275
|
+
thickness: pp.ssr.thickness ?? d.ssr.thickness,
|
|
276
|
+
maxRoughness: pp.ssr.maxRoughness ?? d.ssr.maxRoughness,
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// SSGI (realism-effects)
|
|
282
|
+
if (pp?.ssgi?.enabled && velocityPass) {
|
|
283
|
+
effects.push(
|
|
284
|
+
new SSGIEffect(scene, camera, velocityPass, {
|
|
285
|
+
intensity: pp.ssgi.intensity ?? d.ssgi.intensity,
|
|
286
|
+
distance: pp.ssgi.distance ?? d.ssgi.distance,
|
|
287
|
+
thickness: pp.ssgi.thickness ?? d.ssgi.thickness,
|
|
288
|
+
maxRoughness: pp.ssgi.maxRoughness ?? d.ssgi.maxRoughness,
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Motion blur (realism-effects)
|
|
294
|
+
if (pp?.motionBlur?.enabled && velocityPass) {
|
|
295
|
+
effects.push(
|
|
296
|
+
new MotionBlurEffect(velocityPass, {
|
|
297
|
+
intensity: pp.motionBlur.intensity ?? d.motionBlur.intensity,
|
|
298
|
+
jitter: pp.motionBlur.jitter ?? d.motionBlur.jitter,
|
|
299
|
+
samples: pp.motionBlur.samples ?? d.motionBlur.samples,
|
|
300
|
+
}),
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Bloom
|
|
305
|
+
if (pp?.bloom?.enabled) {
|
|
306
|
+
effects.push(
|
|
307
|
+
new BloomEffect({
|
|
308
|
+
intensity: pp.bloom.intensity ?? d.bloom.intensity,
|
|
309
|
+
luminanceThreshold: pp.bloom.luminanceThreshold ?? d.bloom.luminanceThreshold,
|
|
310
|
+
luminanceSmoothing: pp.bloom.luminanceSmoothing ?? d.bloom.luminanceSmoothing,
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Depth of field
|
|
316
|
+
if (pp?.depthOfField?.enabled) {
|
|
317
|
+
effects.push(
|
|
318
|
+
new DepthOfFieldEffect(camera, {
|
|
319
|
+
worldFocusDistance: pp.depthOfField.worldFocusDistance ?? d.depthOfField.worldFocusDistance,
|
|
320
|
+
worldFocusRange: pp.depthOfField.worldFocusRange ?? d.depthOfField.worldFocusRange,
|
|
321
|
+
bokehScale: pp.depthOfField.bokehScale ?? d.depthOfField.bokehScale,
|
|
322
|
+
focalLength: pp.depthOfField.focalLength ?? d.depthOfField.focalLength,
|
|
323
|
+
}),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Tilt shift
|
|
328
|
+
if (pp?.tiltShift?.enabled) {
|
|
329
|
+
effects.push(
|
|
330
|
+
new TiltShiftEffect({
|
|
331
|
+
offset: pp.tiltShift.offset ?? d.tiltShift.offset,
|
|
332
|
+
rotation: pp.tiltShift.rotation ?? d.tiltShift.rotation,
|
|
333
|
+
focusArea: pp.tiltShift.focusArea ?? d.tiltShift.focusArea,
|
|
334
|
+
feather: pp.tiltShift.feather ?? d.tiltShift.feather,
|
|
335
|
+
}),
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// God rays (needs entity reference)
|
|
340
|
+
if (pp?.godRays?.enabled && pp.godRays.sourceEntity && objectMap) {
|
|
341
|
+
const lightObj = objectMap.get(pp.godRays.sourceEntity);
|
|
342
|
+
if (lightObj instanceof THREE.Mesh || lightObj instanceof THREE.Points) {
|
|
343
|
+
effects.push(
|
|
344
|
+
new GodRaysEffect(camera, lightObj, {
|
|
345
|
+
density: pp.godRays.density ?? d.godRays.density,
|
|
346
|
+
decay: pp.godRays.decay ?? d.godRays.decay,
|
|
347
|
+
weight: pp.godRays.weight ?? d.godRays.weight,
|
|
348
|
+
exposure: pp.godRays.exposure ?? d.godRays.exposure,
|
|
349
|
+
samples: pp.godRays.samples ?? d.godRays.samples,
|
|
350
|
+
}),
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Outline (needs entity tag references)
|
|
356
|
+
if (pp?.outline?.enabled && objectMap) {
|
|
357
|
+
const outlineEffect = new OutlineEffect(scene, camera, {
|
|
358
|
+
edgeStrength: pp.outline.edgeStrength ?? d.outline.edgeStrength,
|
|
359
|
+
pulseSpeed: pp.outline.pulseSpeed ?? d.outline.pulseSpeed,
|
|
360
|
+
visibleEdgeColor: pp.outline.visibleEdgeColor
|
|
361
|
+
? new THREE.Color(pp.outline.visibleEdgeColor).getHex()
|
|
362
|
+
: new THREE.Color(d.outline.visibleEdgeColor).getHex(),
|
|
363
|
+
hiddenEdgeColor: pp.outline.hiddenEdgeColor
|
|
364
|
+
? new THREE.Color(pp.outline.hiddenEdgeColor).getHex()
|
|
365
|
+
: new THREE.Color(d.outline.hiddenEdgeColor).getHex(),
|
|
366
|
+
xRay: pp.outline.xRay ?? false,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Add entities matching tags to selection
|
|
370
|
+
const tags = pp.outline.tags ?? [];
|
|
371
|
+
if (tags.length > 0 && entityTags) {
|
|
372
|
+
for (const [entityId, eTags] of entityTags) {
|
|
373
|
+
if (tags.some((t) => eTags.includes(t))) {
|
|
374
|
+
const obj = objectMap.get(entityId);
|
|
375
|
+
if (obj) outlineEffect.selection.add(obj);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
effects.push(outlineEffect);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Chromatic aberration
|
|
384
|
+
if (pp?.chromaticAberration?.enabled) {
|
|
385
|
+
effects.push(
|
|
386
|
+
new ChromaticAberrationEffect({
|
|
387
|
+
offset: new THREE.Vector2(
|
|
388
|
+
pp.chromaticAberration.offsetX ?? d.chromaticAberration.offsetX,
|
|
389
|
+
pp.chromaticAberration.offsetY ?? d.chromaticAberration.offsetY,
|
|
390
|
+
),
|
|
391
|
+
radialModulation: pp.chromaticAberration.radialModulation ?? false,
|
|
392
|
+
modulationOffset:
|
|
393
|
+
pp.chromaticAberration.modulationOffset ?? d.chromaticAberration.modulationOffset,
|
|
394
|
+
}),
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Lens distortion
|
|
399
|
+
if (pp?.lensDistortion?.enabled) {
|
|
400
|
+
effects.push(
|
|
401
|
+
new LensDistortionEffect({
|
|
402
|
+
distortion: new THREE.Vector2(
|
|
403
|
+
pp.lensDistortion.distortionX ?? d.lensDistortion.distortionX,
|
|
404
|
+
pp.lensDistortion.distortionY ?? d.lensDistortion.distortionY,
|
|
405
|
+
),
|
|
406
|
+
principalPoint: new THREE.Vector2(0, 0),
|
|
407
|
+
focalLength: new THREE.Vector2(
|
|
408
|
+
pp.lensDistortion.focalLengthX ?? d.lensDistortion.focalLengthX,
|
|
409
|
+
pp.lensDistortion.focalLengthY ?? d.lensDistortion.focalLengthY,
|
|
410
|
+
),
|
|
411
|
+
skew: pp.lensDistortion.skew ?? d.lensDistortion.skew,
|
|
412
|
+
}),
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Shock wave
|
|
417
|
+
if (pp?.shockWave?.enabled) {
|
|
418
|
+
effects.push(
|
|
419
|
+
new ShockWaveEffect(
|
|
420
|
+
camera,
|
|
421
|
+
new THREE.Vector3(
|
|
422
|
+
pp.shockWave.positionX ?? 0,
|
|
423
|
+
pp.shockWave.positionY ?? 0,
|
|
424
|
+
pp.shockWave.positionZ ?? 0,
|
|
425
|
+
),
|
|
426
|
+
{
|
|
427
|
+
speed: pp.shockWave.speed ?? d.shockWave.speed,
|
|
428
|
+
maxRadius: pp.shockWave.maxRadius ?? d.shockWave.maxRadius,
|
|
429
|
+
waveSize: pp.shockWave.waveSize ?? d.shockWave.waveSize,
|
|
430
|
+
amplitude: pp.shockWave.amplitude ?? d.shockWave.amplitude,
|
|
431
|
+
},
|
|
432
|
+
),
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Color adjustments
|
|
437
|
+
if (pp?.brightnessContrast?.enabled) {
|
|
438
|
+
effects.push(
|
|
439
|
+
new BrightnessContrastEffect({
|
|
440
|
+
brightness: pp.brightnessContrast.brightness ?? d.brightnessContrast.brightness,
|
|
441
|
+
contrast: pp.brightnessContrast.contrast ?? d.brightnessContrast.contrast,
|
|
442
|
+
}),
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (pp?.hueSaturation?.enabled) {
|
|
447
|
+
effects.push(
|
|
448
|
+
new HueSaturationEffect({
|
|
449
|
+
hue: pp.hueSaturation.hue ?? d.hueSaturation.hue,
|
|
450
|
+
saturation: pp.hueSaturation.saturation ?? d.hueSaturation.saturation,
|
|
451
|
+
}),
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (pp?.sepia?.enabled) {
|
|
456
|
+
effects.push(new SepiaEffect({ intensity: pp.sepia.intensity ?? d.sepia.intensity }));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (pp?.colorDepth?.enabled) {
|
|
460
|
+
effects.push(new ColorDepthEffect({ bits: pp.colorDepth.bits ?? d.colorDepth.bits }));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Film / retro
|
|
464
|
+
if (pp?.noise?.enabled) {
|
|
465
|
+
effects.push(new NoiseEffect({ premultiply: pp.noise.premultiply ?? d.noise.premultiply }));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (pp?.scanline?.enabled) {
|
|
469
|
+
effects.push(new ScanlineEffect({ density: pp.scanline.density ?? d.scanline.density }));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (pp?.dotScreen?.enabled) {
|
|
473
|
+
effects.push(
|
|
474
|
+
new DotScreenEffect({
|
|
475
|
+
angle: pp.dotScreen.angle ?? d.dotScreen.angle,
|
|
476
|
+
scale: pp.dotScreen.scale ?? d.dotScreen.scale,
|
|
477
|
+
}),
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (pp?.grid?.enabled) {
|
|
482
|
+
effects.push(
|
|
483
|
+
new GridEffect({
|
|
484
|
+
scale: pp.grid.scale ?? d.grid.scale,
|
|
485
|
+
lineWidth: pp.grid.lineWidth ?? d.grid.lineWidth,
|
|
486
|
+
}),
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (pp?.pixelation?.enabled) {
|
|
491
|
+
effects.push(new PixelationEffect(pp.pixelation.granularity ?? d.pixelation.granularity));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (pp?.glitch?.enabled) {
|
|
495
|
+
effects.push(
|
|
496
|
+
new GlitchEffect({
|
|
497
|
+
delay: new THREE.Vector2(
|
|
498
|
+
pp.glitch.delayX ?? d.glitch.delayX,
|
|
499
|
+
pp.glitch.delayY ?? d.glitch.delayY,
|
|
500
|
+
),
|
|
501
|
+
duration: new THREE.Vector2(
|
|
502
|
+
pp.glitch.durationX ?? d.glitch.durationX,
|
|
503
|
+
pp.glitch.durationY ?? d.glitch.durationY,
|
|
504
|
+
),
|
|
505
|
+
strength: new THREE.Vector2(
|
|
506
|
+
pp.glitch.strengthX ?? d.glitch.strengthX,
|
|
507
|
+
pp.glitch.strengthY ?? d.glitch.strengthY,
|
|
508
|
+
),
|
|
509
|
+
columns: pp.glitch.columns ?? d.glitch.columns,
|
|
510
|
+
ratio: pp.glitch.ratio ?? d.glitch.ratio,
|
|
511
|
+
}),
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Vignette (last color effect)
|
|
516
|
+
if (pp?.vignette?.enabled) {
|
|
517
|
+
effects.push(
|
|
518
|
+
new VignetteEffect({
|
|
519
|
+
darkness: pp.vignette.darkness ?? d.vignette.darkness,
|
|
520
|
+
offset: pp.vignette.offset ?? d.vignette.offset,
|
|
521
|
+
}),
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ── Merge all effects into a single EffectPass ────────────────────────
|
|
526
|
+
if (effects.length > 0) {
|
|
527
|
+
composer.addPass(new EffectPass(camera, ...effects));
|
|
528
|
+
}
|
|
529
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
declare module 'n8ao' {
|
|
2
|
+
import type { Pass } from 'postprocessing';
|
|
3
|
+
import type { Camera, Color, Scene, Texture, WebGLRenderer, WebGLRenderTarget } from 'three';
|
|
4
|
+
|
|
5
|
+
interface N8AOConfiguration {
|
|
6
|
+
aoSamples: number;
|
|
7
|
+
aoRadius: number;
|
|
8
|
+
denoiseSamples: number;
|
|
9
|
+
denoiseRadius: number;
|
|
10
|
+
distanceFalloff: number;
|
|
11
|
+
intensity: number;
|
|
12
|
+
denoiseIterations: number;
|
|
13
|
+
renderMode: 0 | 1 | 2 | 3 | 4;
|
|
14
|
+
color: Color;
|
|
15
|
+
gammaCorrection: boolean;
|
|
16
|
+
screenSpaceRadius: boolean;
|
|
17
|
+
halfRes: boolean;
|
|
18
|
+
depthAwareUpsampling: boolean;
|
|
19
|
+
colorMultiply: boolean;
|
|
20
|
+
transparencyAware: boolean;
|
|
21
|
+
accumulate: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class N8AOPostPass extends Pass {
|
|
25
|
+
constructor(scene: Scene, camera: Camera, width?: number, height?: number);
|
|
26
|
+
configuration: N8AOConfiguration;
|
|
27
|
+
setDepthTexture(depthTexture: Texture): void;
|
|
28
|
+
setDisplayMode(mode: 'Combined' | 'AO' | 'No AO' | 'Split' | 'Split AO'): void;
|
|
29
|
+
setQualityMode(mode: 'Performance' | 'Low' | 'Medium' | 'High' | 'Ultra'): void;
|
|
30
|
+
setSize(width: number, height: number): void;
|
|
31
|
+
render(
|
|
32
|
+
renderer: WebGLRenderer,
|
|
33
|
+
inputBuffer: WebGLRenderTarget,
|
|
34
|
+
outputBuffer: WebGLRenderTarget,
|
|
35
|
+
): void;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
declare module 'realism-effects' {
|
|
2
|
+
import type { Effect, EffectComposer, Pass } from 'postprocessing';
|
|
3
|
+
import type { Camera, Scene } from 'three';
|
|
4
|
+
|
|
5
|
+
export class VelocityDepthNormalPass extends Pass {
|
|
6
|
+
constructor(scene: Scene, camera: Camera);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class VelocityPass extends Pass {
|
|
10
|
+
constructor(scene: Scene, camera: Camera);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SSGIOptions {
|
|
14
|
+
intensity?: number;
|
|
15
|
+
exponent?: number;
|
|
16
|
+
distance?: number;
|
|
17
|
+
thickness?: number;
|
|
18
|
+
maxRoughness?: number;
|
|
19
|
+
specularOnly?: boolean;
|
|
20
|
+
diffuseOnly?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class SSGIEffect extends Effect {
|
|
24
|
+
constructor(
|
|
25
|
+
scene: Scene,
|
|
26
|
+
camera: Camera,
|
|
27
|
+
velocityDepthNormalPass: VelocityDepthNormalPass,
|
|
28
|
+
options?: SSGIOptions,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class SSREffect extends SSGIEffect {
|
|
33
|
+
constructor(
|
|
34
|
+
scene: Scene,
|
|
35
|
+
camera: Camera,
|
|
36
|
+
velocityDepthNormalPass: VelocityDepthNormalPass,
|
|
37
|
+
options?: SSGIOptions,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface MotionBlurOptions {
|
|
42
|
+
intensity?: number;
|
|
43
|
+
jitter?: number;
|
|
44
|
+
samples?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class MotionBlurEffect extends Effect {
|
|
48
|
+
constructor(velocityPass: VelocityDepthNormalPass, options?: MotionBlurOptions);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface AOOptions {
|
|
52
|
+
spp?: number;
|
|
53
|
+
distance?: number;
|
|
54
|
+
distancePower?: number;
|
|
55
|
+
power?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class HBAOEffect extends Effect {
|
|
59
|
+
constructor(composer: EffectComposer, camera: Camera, scene: Scene, options?: AOOptions);
|
|
60
|
+
}
|
|
61
|
+
}
|