rayzee 8.8.2 → 9.1.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/README.md +210 -20
- package/dist/{NeuralRetouch-CYOJ21Wn.js → NeuralRetouch-BRquqfmZ.js} +1 -1
- package/dist/{NeuralSuperRes-Dc6jRYke.js → NeuralSuperRes-CbNvQjCw.js} +5 -2
- package/dist/{ToneMapGPU-BVE852iN.js → ToneMapGPU-DpUZDMbQ.js} +2 -1
- package/dist/rayzee.es.js +10795 -5324
- package/dist/rayzee.umd.js +279 -268
- package/package.json +1 -1
- package/src/AssetConfig.js +4 -0
- package/src/EngineDefaults.js +60 -76
- package/src/EngineEvents.js +7 -0
- package/src/EngineIssues.js +6 -0
- package/src/Headless.js +3 -1
- package/src/Passes/AIUpscaler.js +2 -1
- package/src/Passes/OIDNDenoiser.js +3 -6
- package/src/Passes/OIDNTemporalHistory.js +25 -7
- package/src/PathTracerApp.js +313 -25
- package/src/Processor/ArchiveCache.js +120 -0
- package/src/Processor/ArchiveReader.js +179 -32
- package/src/Processor/AssetLoader.js +523 -76
- package/src/Processor/AtmosphereModel.js +465 -0
- package/src/Processor/BVHBuilder.js +4 -1
- package/src/Processor/BVHLeafFold.js +100 -0
- package/src/Processor/BVHRefitter.js +98 -214
- package/src/Processor/ChunkedRecords.js +189 -4
- package/src/Processor/EmissiveTriangleBuilder.js +8 -0
- package/src/Processor/GeometryExtractor.js +238 -40
- package/src/Processor/InstanceTable.js +55 -37
- package/src/Processor/PBRT/PBRTCurves.js +104 -27
- package/src/Processor/PBRT/PBRTParser.js +132 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +210 -55
- package/src/Processor/PBRT/buffers.js +27 -0
- package/src/Processor/PBRT/index.js +55 -15
- package/src/Processor/PackedRayBuffer.js +10 -13
- package/src/Processor/ParallelBVHBuilder.js +36 -15
- package/src/Processor/PhysicalSky.js +308 -0
- package/src/Processor/QueueManager.js +1 -0
- package/src/Processor/SceneProcessor.js +1132 -181
- package/src/Processor/SplitBLAS.js +249 -0
- package/src/Processor/SunPosition.js +41 -0
- package/src/Processor/TextureCreator.js +21 -0
- package/src/Processor/Workers/AIUpscalerWorker.js +79 -52
- package/src/Processor/Workers/BVHWorker.js +17 -13
- package/src/Processor/ZipReader.js +207 -0
- package/src/RenderSettings.js +49 -8
- package/src/SceneState/SceneState.js +367 -0
- package/src/SceneState/portable.js +52 -0
- package/src/Stages/MotionVector.js +8 -42
- package/src/Stages/NRD.js +30 -21
- package/src/Stages/NormalDepth.js +12 -23
- package/src/Stages/PathTracer.js +114 -6
- package/src/Stages/PathTracerStage.js +251 -104
- package/src/Storage/BLASCache.js +190 -0
- package/src/Storage/CDFCache.js +77 -0
- package/src/Storage/DownloadCache.js +266 -0
- package/src/Storage/GeometrySpill.js +217 -0
- package/src/Storage/SceneGraphCodec.js +503 -0
- package/src/Storage/SpillStore.js +112 -0
- package/src/Storage/StorageManager.js +934 -0
- package/src/Storage/StorageOps.js +137 -0
- package/src/Storage/StorageWorker.js +37 -0
- package/src/Storage/events.js +29 -0
- package/src/Storage/identity.js +77 -0
- package/src/Storage/index.js +9 -0
- package/src/Storage/inlineTransport.js +24 -0
- package/src/Storage/locks.js +99 -0
- package/src/Storage/openStorage.js +87 -0
- package/src/Storage/sceneCachePolicy.js +12 -0
- package/src/Storage/shared.js +9 -0
- package/src/Storage/transport.js +62 -0
- package/src/TSL/Atmosphere.js +684 -0
- package/src/TSL/BVHTraversal.js +288 -239
- package/src/TSL/CameraRay.js +99 -14
- package/src/TSL/Common.js +11 -2
- package/src/TSL/DebugKernel.js +2 -2
- package/src/TSL/Displacement.js +6 -6
- package/src/TSL/EmissiveSampling.js +5 -5
- package/src/TSL/EnvironmentCDF.js +176 -0
- package/src/TSL/GenerateKernel.js +5 -5
- package/src/TSL/HitFacet.js +4 -4
- package/src/TSL/LightsDirect.js +8 -8
- package/src/TSL/LightsSampling.js +55 -37
- package/src/TSL/Random.js +1 -0
- package/src/TSL/ShadeKernel.js +75 -19
- package/src/TSL/Sun.js +40 -0
- package/src/TSL/TextureSampling.js +6 -7
- package/src/index.js +22 -0
- package/src/managers/CameraManager.js +412 -56
- package/src/managers/DenoisingManager.js +4 -3
- package/src/managers/EnvironmentManager.js +263 -33
- package/src/managers/GoboManager.js +19 -8
- package/src/managers/IESManager.js +3 -3
- package/src/managers/InteractionManager.js +30 -2
- package/src/managers/LightManager.js +98 -5
- package/src/managers/MaterialDataManager.js +22 -2
- package/src/managers/TransformManager.js +26 -0
- package/src/managers/UniformManager.js +11 -7
- package/src/managers/VideoRenderManager.js +38 -17
- package/src/managers/ViewCamera.js +77 -0
- package/src/managers/WalkControls.js +262 -0
- package/src/managers/helpers/OutlineHelper.js +22 -2
- package/src/managers/timeline/CameraTrack.js +235 -0
- package/src/managers/timeline/TimelineManager.js +146 -0
- package/src/version.js +3 -0
- package/src/Processor/ProceduralSky.js +0 -291
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import { EventDispatcher, PerspectiveCamera, Vector3 } from 'three';
|
|
1
|
+
import { EventDispatcher, MathUtils, OrthographicCamera, PerspectiveCamera, Quaternion, Vector3 } from 'three';
|
|
2
2
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
3
3
|
import { EngineEvents, } from '../EngineEvents.js';
|
|
4
|
-
import { AF_DEFAULTS } from '../EngineDefaults.js';
|
|
4
|
+
import { AF_DEFAULTS, CAMERA_PROJECTION_IDS } from '../EngineDefaults.js';
|
|
5
|
+
import { viewDepth } from './InteractionManager.js';
|
|
6
|
+
import { ViewCamera } from './ViewCamera.js';
|
|
7
|
+
import { WalkControls } from './WalkControls.js';
|
|
8
|
+
import { toPortable, fromPortable } from '../SceneState/portable.js';
|
|
5
9
|
|
|
6
10
|
const DEFAULT_CAMERA_SCALE = Object.freeze( new Vector3( 1, 1, 1 ) );
|
|
7
11
|
|
|
12
|
+
/** The height of an orthographic camera's view in world units, or null for any other camera. */
|
|
13
|
+
const orthoHeightOf = camera => camera?.isOrthographicCamera ? ( camera.top - camera.bottom ) / camera.zoom : null;
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* Manages camera creation, switching, auto-focus, and AF point placement.
|
|
10
17
|
*
|
|
11
|
-
* Owns the
|
|
18
|
+
* Owns the {@link ViewCamera} and OrbitControls instances.
|
|
12
19
|
* Dispatches events that PathTracerApp relays to external consumers.
|
|
13
20
|
*/
|
|
14
21
|
export class CameraManager extends EventDispatcher {
|
|
@@ -23,7 +30,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
23
30
|
const width = canvas.clientWidth;
|
|
24
31
|
const height = canvas.clientHeight;
|
|
25
32
|
|
|
26
|
-
this.camera = new
|
|
33
|
+
this.camera = new ViewCamera( 60, width / height || 1, 0.01, 1000 );
|
|
27
34
|
this.camera.position.set( 0, 0, 5 );
|
|
28
35
|
|
|
29
36
|
this.controls = new OrbitControls( this.camera, canvas );
|
|
@@ -31,9 +38,16 @@ export class CameraManager extends EventDispatcher {
|
|
|
31
38
|
this.controls.zoomToCursor = true;
|
|
32
39
|
this.controls.saveState();
|
|
33
40
|
|
|
41
|
+
// The wheel zooms an orthographic view: report its height.
|
|
42
|
+
this._reportedOrthoHeight = null;
|
|
43
|
+
this._reportOrthoHeight = this._reportOrthoHeight.bind( this );
|
|
44
|
+
this.controls.addEventListener( 'change', this._reportOrthoHeight );
|
|
45
|
+
|
|
46
|
+
this.walkControls = new WalkControls( this.camera, canvas, this.controls );
|
|
47
|
+
|
|
34
48
|
this.interactionManager = null;
|
|
35
49
|
|
|
36
|
-
/** @type {import('three').
|
|
50
|
+
/** @type {import('three').Camera[]} */
|
|
37
51
|
this.cameras = [ this.camera ];
|
|
38
52
|
this.currentCameraIndex = 0;
|
|
39
53
|
|
|
@@ -62,19 +76,20 @@ export class CameraManager extends EventDispatcher {
|
|
|
62
76
|
|
|
63
77
|
/**
|
|
64
78
|
* Sets the list of available cameras (default + extracted from model).
|
|
65
|
-
* @param {import('three').
|
|
79
|
+
* @param {import('three').Camera[]} cameras - perspective or orthographic
|
|
66
80
|
*/
|
|
67
81
|
setCameras( cameras ) {
|
|
68
82
|
|
|
69
83
|
this.cameras = cameras;
|
|
70
84
|
this._userCameraCounter = 0;
|
|
85
|
+
this.resetAutoFocus();
|
|
71
86
|
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
/**
|
|
75
90
|
* Adds a new user camera that snapshots the current render camera's pose,
|
|
76
|
-
* FOV, clip planes, and orbit target. The snapshot is a standalone
|
|
77
|
-
* appended to the list; switching to it restores this exact framing.
|
|
91
|
+
* FOV or orthographic size, clip planes, and orbit target. The snapshot is a standalone
|
|
92
|
+
* template appended to the list; switching to it restores this exact framing.
|
|
78
93
|
*
|
|
79
94
|
* @param {string} [name] - Optional display name (auto-generated otherwise).
|
|
80
95
|
* @returns {number} The index of the newly added camera.
|
|
@@ -82,7 +97,10 @@ export class CameraManager extends EventDispatcher {
|
|
|
82
97
|
addCameraFromView( name ) {
|
|
83
98
|
|
|
84
99
|
const src = this.camera;
|
|
85
|
-
const cam =
|
|
100
|
+
const cam = src.orthographic
|
|
101
|
+
? new OrthographicCamera( src.left, src.right, src.top, src.bottom, src.near, src.far )
|
|
102
|
+
: new PerspectiveCamera( src.fov, src.aspect, src.near, src.far );
|
|
103
|
+
cam.zoom = src.zoom;
|
|
86
104
|
cam.position.copy( src.position );
|
|
87
105
|
cam.quaternion.copy( src.quaternion );
|
|
88
106
|
cam.scale.copy( src.scale );
|
|
@@ -193,8 +211,10 @@ export class CameraManager extends EventDispatcher {
|
|
|
193
211
|
focalLength: get( 'focalLength' ),
|
|
194
212
|
apertureScale: get( 'apertureScale' ),
|
|
195
213
|
anamorphicRatio: get( 'anamorphicRatio' ),
|
|
214
|
+
dofBlur: get( 'dofBlur' ),
|
|
196
215
|
autoFocusMode: this.autoFocusMode,
|
|
197
216
|
afScreenPoint: { ...this.afScreenPoint },
|
|
217
|
+
orthoHeight: this.orthoHeight,
|
|
198
218
|
};
|
|
199
219
|
|
|
200
220
|
}
|
|
@@ -214,6 +234,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
214
234
|
focalLength: eff.focalLength,
|
|
215
235
|
apertureScale: eff.apertureScale,
|
|
216
236
|
anamorphicRatio: eff.anamorphicRatio,
|
|
237
|
+
dofBlur: eff.dofBlur,
|
|
217
238
|
} );
|
|
218
239
|
|
|
219
240
|
if ( eff.autoFocusMode !== undefined ) this.setAutoFocusMode( eff.autoFocusMode );
|
|
@@ -221,13 +242,6 @@ export class CameraManager extends EventDispatcher {
|
|
|
221
242
|
|
|
222
243
|
}
|
|
223
244
|
|
|
224
|
-
/** Scene scale used to convert scaled focusDistance to UI-facing units. */
|
|
225
|
-
_getSceneScale() {
|
|
226
|
-
|
|
227
|
-
return this._afContext?.assetLoader?.getSceneScale?.() || 1;
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
245
|
/**
|
|
232
246
|
* Switches the active camera by index.
|
|
233
247
|
* Uses stored callbacks from initCallbacks() for resize/reset.
|
|
@@ -305,7 +319,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
305
319
|
// quaternion can express — a pbrt scene's `Scale -1 1 1`, for one. Copying
|
|
306
320
|
// pose alone would silently un-mirror the view.
|
|
307
321
|
this.camera.scale.copy( sourceCamera.scale );
|
|
308
|
-
this.camera.fov = sourceCamera.fov;
|
|
322
|
+
if ( sourceCamera.isPerspectiveCamera ) this.camera.fov = sourceCamera.fov;
|
|
309
323
|
this.camera.near = sourceCamera.near;
|
|
310
324
|
this.camera.far = sourceCamera.far;
|
|
311
325
|
this.camera.updateProjectionMatrix();
|
|
@@ -334,18 +348,121 @@ export class CameraManager extends EventDispatcher {
|
|
|
334
348
|
|
|
335
349
|
}
|
|
336
350
|
|
|
351
|
+
this.resetAutoFocus();
|
|
352
|
+
|
|
353
|
+
// A camera left orthographic comes back orthographic; one never visited is what it was made as.
|
|
354
|
+
const incoming = this.cameras[ index ];
|
|
355
|
+
const saved = incoming?.userData?.__rayzeeEffects;
|
|
356
|
+
this._showOrthographic( saved ? saved.orthoHeight : orthoHeightOf( incoming ) );
|
|
357
|
+
|
|
337
358
|
// Restore the incoming camera's own DOF/focus effects (if it has any saved;
|
|
338
359
|
// otherwise it inherits the current global config).
|
|
339
|
-
this._applyEffects(
|
|
360
|
+
this._applyEffects( saved );
|
|
340
361
|
|
|
341
362
|
onResize?.();
|
|
342
363
|
onReset?.();
|
|
343
364
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
365
|
+
this.dispatchEvent( {
|
|
366
|
+
type: 'CameraSwitched', cameraIndex: index, effects: this._captureEffects(), fov: this.camera.fov,
|
|
367
|
+
cameraProjection: this._getSettings?.( 'cameraProjection' ),
|
|
368
|
+
} );
|
|
369
|
+
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Makes the camera orthographic at `height`, or perspective for null, then hands the `cameraProjection`
|
|
374
|
+
* setting the outcome — after, so its handler finds the camera already there. A 360° panorama stays on
|
|
375
|
+
* across perspective cameras.
|
|
376
|
+
* @param {?number} height - in world units
|
|
377
|
+
*/
|
|
378
|
+
_showOrthographic( height ) {
|
|
379
|
+
|
|
380
|
+
if ( height > 0 ) this._setView( true, height / 2 );
|
|
381
|
+
else this._setView( false );
|
|
382
|
+
|
|
383
|
+
const current = this._getSettings?.( 'cameraProjection' );
|
|
384
|
+
const next = height > 0 ? 'orthographic' : current === 'orthographic' ? 'perspective' : current;
|
|
385
|
+
if ( next !== current ) this._applySettings?.( { cameraProjection: next } );
|
|
386
|
+
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* The camera's side of the `cameraProjection` setting: set that, not this. Turning orthographic
|
|
391
|
+
* keeps what the view shows at the orbit target, and turning back moves the camera to keep it.
|
|
392
|
+
* @param {'perspective' | 'orthographic' | 'equirectangular'} projection
|
|
393
|
+
*/
|
|
394
|
+
applyProjection( projection ) {
|
|
395
|
+
|
|
396
|
+
const camera = this.camera;
|
|
397
|
+
const orthographic = projection === 'orthographic';
|
|
398
|
+
if ( camera.orthographic === orthographic ) return;
|
|
399
|
+
|
|
400
|
+
if ( orthographic ) {
|
|
401
|
+
|
|
402
|
+
this._setView( true, this._fittedHalfHeight() );
|
|
403
|
+
|
|
404
|
+
} else {
|
|
405
|
+
|
|
406
|
+
const target = this.controls.target;
|
|
407
|
+
const distance = this.orthoHeight / 2 / Math.tan( MathUtils.degToRad( camera.fov ) / 2 );
|
|
408
|
+
camera.position.sub( target ).setLength( distance ).add( target );
|
|
409
|
+
this._setView( false );
|
|
410
|
+
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
this.controls.update();
|
|
414
|
+
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** The height of the view in world units while orthographic, the wheel's zoom included; null otherwise. */
|
|
418
|
+
get orthoHeight() {
|
|
419
|
+
|
|
420
|
+
return orthoHeightOf( this.camera );
|
|
421
|
+
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** @param {number} height - the orthographic view's height, in world units */
|
|
425
|
+
setOrthoHeight( height ) {
|
|
426
|
+
|
|
427
|
+
if ( ! ( height > 0 ) ) return;
|
|
428
|
+
this._setView( this.camera.orthographic, height / 2 );
|
|
429
|
+
this._onReset?.();
|
|
430
|
+
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** Sizes an orthographic view to show what a perspective one would at the orbit target. */
|
|
434
|
+
fitOrthographic() {
|
|
435
|
+
|
|
436
|
+
if ( this.camera.orthographic ) this.setOrthoHeight( 2 * this._fittedHalfHeight() );
|
|
437
|
+
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// The one writer of the camera's projection. The wheel's zoom starts over.
|
|
441
|
+
_setView( orthographic, halfHeight = this.camera.orthoHalfHeight ) {
|
|
442
|
+
|
|
443
|
+
const camera = this.camera;
|
|
444
|
+
camera.orthographic = orthographic;
|
|
445
|
+
camera.orthoHalfHeight = halfHeight;
|
|
446
|
+
camera.zoom = 1;
|
|
447
|
+
camera.updateProjectionMatrix();
|
|
448
|
+
this._reportOrthoHeight();
|
|
449
|
+
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Half of what a perspective view shows at the orbit target.
|
|
453
|
+
_fittedHalfHeight() {
|
|
454
|
+
|
|
455
|
+
return this.camera.position.distanceTo( this.controls.target ) * Math.tan( MathUtils.degToRad( this.camera.fov ) / 2 );
|
|
456
|
+
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
_reportOrthoHeight() {
|
|
460
|
+
|
|
461
|
+
if ( ! this.camera.orthographic ) return;
|
|
462
|
+
const height = this.orthoHeight;
|
|
463
|
+
if ( height === this._reportedOrthoHeight ) return;
|
|
464
|
+
this._reportedOrthoHeight = height;
|
|
465
|
+
this.dispatchEvent( { type: EngineEvents.ORTHO_HEIGHT_UPDATED, height } );
|
|
349
466
|
|
|
350
467
|
}
|
|
351
468
|
|
|
@@ -362,9 +479,255 @@ export class CameraManager extends EventDispatcher {
|
|
|
362
479
|
|
|
363
480
|
}
|
|
364
481
|
|
|
482
|
+
/**
|
|
483
|
+
* Orbit around a target, or walk through the scene first-person (see {@link WalkControls}).
|
|
484
|
+
* Leaving walk mode orbits around the surface at the centre of the view.
|
|
485
|
+
* @param {'orbit' | 'walk'} mode
|
|
486
|
+
*/
|
|
487
|
+
setNavigationMode( mode ) {
|
|
488
|
+
|
|
489
|
+
const walk = mode === 'walk';
|
|
490
|
+
if ( this.walkControls.enabled === walk ) return;
|
|
491
|
+
|
|
492
|
+
const controls = this.controls;
|
|
493
|
+
controls.enableRotate = controls.enablePan = controls.enableZoom = ! walk;
|
|
494
|
+
this.walkControls.enabled = walk;
|
|
495
|
+
if ( walk ) return;
|
|
496
|
+
|
|
497
|
+
this.walkControls.release();
|
|
498
|
+
const hit = this.interactionManager?.pickSurface( 0, 0 );
|
|
499
|
+
if ( ! hit ) return;
|
|
500
|
+
|
|
501
|
+
const distance = MathUtils.clamp( hit.distance, controls.minDistance, controls.maxDistance );
|
|
502
|
+
controls.target.copy( this.camera.position ).addScaledVector( this.camera.getWorldDirection( new Vector3() ), distance );
|
|
503
|
+
controls.update();
|
|
504
|
+
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** @returns {'orbit' | 'walk'} */
|
|
508
|
+
get navigationMode() {
|
|
509
|
+
|
|
510
|
+
return this.walkControls.enabled ? 'walk' : 'orbit';
|
|
511
|
+
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** Per frame, before rendering. */
|
|
515
|
+
updateControls() {
|
|
516
|
+
|
|
517
|
+
this.walkControls.update();
|
|
518
|
+
this.controls.update();
|
|
519
|
+
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* The current view as a pose: where the camera is, what it looks at, and how much it shows — the
|
|
524
|
+
* height an orthographic view covers, or what a perspective one covers at its orbit target.
|
|
525
|
+
* @returns {import('./timeline/CameraTrack.js').CameraPose}
|
|
526
|
+
*/
|
|
527
|
+
captureView() {
|
|
528
|
+
|
|
529
|
+
const camera = this.camera;
|
|
530
|
+
const orthoHeight = this.orthoHeight ?? 2 * this._fittedHalfHeight();
|
|
531
|
+
return { position: camera.position.clone(), target: this.controls.target.clone(), fov: camera.fov, orthoHeight };
|
|
532
|
+
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Puts the camera at a pose, looking at its target, sized for the projection in use.
|
|
537
|
+
* @param {import('./timeline/CameraTrack.js').CameraPose} pose
|
|
538
|
+
*/
|
|
539
|
+
applyPose( pose ) {
|
|
540
|
+
|
|
541
|
+
const camera = this.camera;
|
|
542
|
+
camera.position.copy( pose.position );
|
|
543
|
+
camera.lookAt( pose.target );
|
|
544
|
+
this.controls.target.copy( pose.target );
|
|
545
|
+
|
|
546
|
+
if ( camera.orthographic ) {
|
|
547
|
+
|
|
548
|
+
this._setView( true, pose.orthoHeight / 2 );
|
|
549
|
+
|
|
550
|
+
} else {
|
|
551
|
+
|
|
552
|
+
camera.fov = pose.fov;
|
|
553
|
+
camera.updateProjectionMatrix();
|
|
554
|
+
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
camera.updateMatrixWorld();
|
|
558
|
+
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// ── Saved sessions ────────────────────────────────────────────
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* The live view, every camera's own effects, the cameras the user added and which one is
|
|
565
|
+
* active, as plain data. The active camera's effects are the render settings themselves.
|
|
566
|
+
* @returns {Object}
|
|
567
|
+
*/
|
|
568
|
+
serialize() {
|
|
569
|
+
|
|
570
|
+
const camera = this.camera;
|
|
571
|
+
const saved = this._defaultCameraState;
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
current: this.currentCameraIndex,
|
|
575
|
+
view: {
|
|
576
|
+
position: camera.position.toArray(),
|
|
577
|
+
quaternion: camera.quaternion.toArray(),
|
|
578
|
+
scale: camera.scale.toArray(),
|
|
579
|
+
fov: camera.fov,
|
|
580
|
+
near: camera.near,
|
|
581
|
+
far: camera.far,
|
|
582
|
+
zoom: camera.zoom,
|
|
583
|
+
orthographic: !! camera.orthographic,
|
|
584
|
+
orthoHalfHeight: camera.orthoHalfHeight,
|
|
585
|
+
target: this.controls.target.toArray(),
|
|
586
|
+
},
|
|
587
|
+
defaultView: saved ? {
|
|
588
|
+
position: saved.position.toArray(),
|
|
589
|
+
quaternion: saved.quaternion.toArray(),
|
|
590
|
+
scale: saved.scale.toArray(),
|
|
591
|
+
fov: saved.fov,
|
|
592
|
+
near: saved.near,
|
|
593
|
+
far: saved.far,
|
|
594
|
+
target: saved.target?.toArray() ?? null,
|
|
595
|
+
} : null,
|
|
596
|
+
navigationMode: this.navigationMode,
|
|
597
|
+
autoFocus: { mode: this.autoFocusMode, point: { ...this.afScreenPoint } },
|
|
598
|
+
cameras: this.cameras.map( ( cam, index ) => {
|
|
599
|
+
|
|
600
|
+
const entry = { index, name: cam.name ?? '', effects: toPortable( cam.userData?.__rayzeeEffects ) ?? null };
|
|
601
|
+
if ( index === 0 || ! cam.userData?.__rayzeeUserCamera ) return entry;
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
...entry,
|
|
605
|
+
user: true,
|
|
606
|
+
orthographic: !! cam.isOrthographicCamera,
|
|
607
|
+
frustum: cam.isOrthographicCamera ? [ cam.left, cam.right, cam.top, cam.bottom ] : null,
|
|
608
|
+
fov: cam.fov ?? null,
|
|
609
|
+
aspect: cam.aspect ?? null,
|
|
610
|
+
near: cam.near,
|
|
611
|
+
far: cam.far,
|
|
612
|
+
zoom: cam.zoom,
|
|
613
|
+
position: cam.position.toArray(),
|
|
614
|
+
quaternion: cam.quaternion.toArray(),
|
|
615
|
+
scale: cam.scale.toArray(),
|
|
616
|
+
orbitTarget: cam.userData.__rayzeeOrbitTarget?.toArray() ?? null,
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
} ),
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Puts back what {@link serialize} recorded, against the cameras this load produced. A model
|
|
626
|
+
* camera is matched by index and name; one that no longer matches keeps its own effects.
|
|
627
|
+
* Restore render settings first: this sets the exact view after any projection change they made.
|
|
628
|
+
* @param {Object} state
|
|
629
|
+
* @returns {{mismatched: string[]}} model cameras whose saved effects were not applied
|
|
630
|
+
*/
|
|
631
|
+
restore( state ) {
|
|
632
|
+
|
|
633
|
+
const mismatched = [];
|
|
634
|
+
if ( ! state ) return { mismatched };
|
|
635
|
+
|
|
636
|
+
this.cameras = this.cameras.filter( cam => ! cam.userData?.__rayzeeUserCamera );
|
|
637
|
+
const indexMap = new Map();
|
|
638
|
+
|
|
639
|
+
for ( const entry of state.cameras ?? [] ) {
|
|
640
|
+
|
|
641
|
+
if ( entry.user ) {
|
|
642
|
+
|
|
643
|
+
const cam = entry.orthographic
|
|
644
|
+
? new OrthographicCamera( ...entry.frustum, entry.near, entry.far )
|
|
645
|
+
: new PerspectiveCamera( entry.fov, entry.aspect, entry.near, entry.far );
|
|
646
|
+
cam.name = entry.name;
|
|
647
|
+
cam.zoom = entry.zoom ?? 1;
|
|
648
|
+
cam.position.fromArray( entry.position );
|
|
649
|
+
cam.quaternion.fromArray( entry.quaternion );
|
|
650
|
+
cam.scale.fromArray( entry.scale );
|
|
651
|
+
cam.updateProjectionMatrix();
|
|
652
|
+
cam.updateMatrixWorld( true );
|
|
653
|
+
cam.userData.__rayzeeUserCamera = true;
|
|
654
|
+
cam.userData.__rayzeeEffects = fromPortable( entry.effects );
|
|
655
|
+
if ( entry.orbitTarget ) cam.userData.__rayzeeOrbitTarget = new Vector3().fromArray( entry.orbitTarget );
|
|
656
|
+
indexMap.set( entry.index, this.cameras.length );
|
|
657
|
+
this.cameras.push( cam );
|
|
658
|
+
continue;
|
|
659
|
+
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const cam = this.cameras[ entry.index ];
|
|
663
|
+
if ( ! cam || ( entry.index > 0 && ( cam.name ?? '' ) !== entry.name ) ) {
|
|
664
|
+
|
|
665
|
+
mismatched.push( entry.name );
|
|
666
|
+
continue;
|
|
667
|
+
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
indexMap.set( entry.index, entry.index );
|
|
671
|
+
if ( entry.effects ) cam.userData.__rayzeeEffects = fromPortable( entry.effects );
|
|
672
|
+
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
this._userCameraCounter = this.cameras.filter( cam => cam.userData?.__rayzeeUserCamera ).length;
|
|
676
|
+
this.currentCameraIndex = indexMap.get( state.current ) ?? 0;
|
|
677
|
+
|
|
678
|
+
const d = state.defaultView;
|
|
679
|
+
this._defaultCameraState = d ? {
|
|
680
|
+
position: new Vector3().fromArray( d.position ),
|
|
681
|
+
quaternion: new Quaternion().fromArray( d.quaternion ),
|
|
682
|
+
scale: new Vector3().fromArray( d.scale ),
|
|
683
|
+
fov: d.fov,
|
|
684
|
+
near: d.near,
|
|
685
|
+
far: d.far,
|
|
686
|
+
target: d.target ? new Vector3().fromArray( d.target ) : null,
|
|
687
|
+
} : null;
|
|
688
|
+
|
|
689
|
+
if ( state.navigationMode ) this.setNavigationMode( state.navigationMode );
|
|
690
|
+
|
|
691
|
+
const v = state.view;
|
|
692
|
+
if ( v ) {
|
|
693
|
+
|
|
694
|
+
const camera = this.camera;
|
|
695
|
+
camera.position.fromArray( v.position );
|
|
696
|
+
camera.quaternion.fromArray( v.quaternion );
|
|
697
|
+
camera.scale.fromArray( v.scale );
|
|
698
|
+
camera.fov = v.fov;
|
|
699
|
+
camera.near = v.near;
|
|
700
|
+
camera.far = v.far;
|
|
701
|
+
this._setView( v.orthographic, v.orthoHalfHeight );
|
|
702
|
+
camera.zoom = v.zoom ?? 1;
|
|
703
|
+
camera.updateProjectionMatrix();
|
|
704
|
+
camera.updateMatrixWorld( true );
|
|
705
|
+
this.controls.target.fromArray( v.target );
|
|
706
|
+
this.controls.update();
|
|
707
|
+
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if ( state.autoFocus ) {
|
|
711
|
+
|
|
712
|
+
this.setAutoFocusMode( state.autoFocus.mode );
|
|
713
|
+
this.setAFScreenPoint( state.autoFocus.point.x, state.autoFocus.point.y );
|
|
714
|
+
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
this.resetAutoFocus();
|
|
718
|
+
this._reportOrthoHeight();
|
|
719
|
+
this.dispatchEvent( {
|
|
720
|
+
type: 'CameraSwitched', cameraIndex: this.currentCameraIndex, effects: this._captureEffects(), fov: this.camera.fov,
|
|
721
|
+
cameraProjection: this._getSettings?.( 'cameraProjection' ),
|
|
722
|
+
} );
|
|
723
|
+
|
|
724
|
+
return { mismatched };
|
|
725
|
+
|
|
726
|
+
}
|
|
727
|
+
|
|
365
728
|
// ── Aliases (match Sub-API surface) ───────────────────────────
|
|
366
729
|
|
|
367
|
-
/** The active
|
|
730
|
+
/** The active camera, a {@link ViewCamera}. */
|
|
368
731
|
get active() {
|
|
369
732
|
|
|
370
733
|
return this.camera;
|
|
@@ -393,6 +756,15 @@ export class CameraManager extends EventDispatcher {
|
|
|
393
756
|
|
|
394
757
|
}
|
|
395
758
|
|
|
759
|
+
/** Forget what auto-focus last measured: it belonged to another model or viewpoint. */
|
|
760
|
+
resetAutoFocus() {
|
|
761
|
+
|
|
762
|
+
this._lastValidFocusDistance = null;
|
|
763
|
+
this._smoothedFocusDistance = null;
|
|
764
|
+
this._afPointDirty = true;
|
|
765
|
+
|
|
766
|
+
}
|
|
767
|
+
|
|
396
768
|
setAFScreenPoint( x, y ) {
|
|
397
769
|
|
|
398
770
|
this.afScreenPoint = { x, y };
|
|
@@ -420,9 +792,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
420
792
|
* Per-frame auto-focus update. Called in animate() before pipeline.render().
|
|
421
793
|
*
|
|
422
794
|
* @param {Object} params
|
|
423
|
-
* @param {import('three').Scene} params.meshScene
|
|
424
795
|
* @param {Object} params.assetLoader
|
|
425
|
-
* @param {import('three').Mesh} params.floorPlane
|
|
426
796
|
* @param {number} params.currentFocusDistance
|
|
427
797
|
* @param {import('../Stages/PathTracer.js').PathTracer} params.pathTracer
|
|
428
798
|
* @param {Function} params.setFocusDistance - Callback to update uniform + settings
|
|
@@ -431,16 +801,18 @@ export class CameraManager extends EventDispatcher {
|
|
|
431
801
|
*/
|
|
432
802
|
updateAutoFocus( ctx ) {
|
|
433
803
|
|
|
434
|
-
const
|
|
435
|
-
if ( !
|
|
804
|
+
const context = ctx || this._afContext;
|
|
805
|
+
if ( ! context || ! this.interactionManager ) return;
|
|
806
|
+
const { assetLoader, currentFocusDistance, pathTracer, setFocusDistance, softReset, hardReset } = context;
|
|
436
807
|
|
|
437
808
|
if ( this.autoFocusMode === 'manual' ) return;
|
|
438
809
|
|
|
439
810
|
// Depth-of-field is the only consumer of the auto-focus distance. With DOF
|
|
440
811
|
// off (the default) the per-frame scene raycast is pure waste, so skip it.
|
|
441
|
-
//
|
|
812
|
+
// A panorama pauses it too: Raycaster.setFromCamera only knows a frustum.
|
|
813
|
+
// Re-snap on the frame it resumes so focus is correct immediately
|
|
442
814
|
// rather than racking from a stale smoothed value.
|
|
443
|
-
if ( ! pathTracer?.enableDOF?.value ) {
|
|
815
|
+
if ( ! pathTracer?.enableDOF?.value || pathTracer.cameraProjection?.value === CAMERA_PROJECTION_IDS.equirectangular ) {
|
|
444
816
|
|
|
445
817
|
this._afSuspended = true;
|
|
446
818
|
return;
|
|
@@ -461,27 +833,13 @@ export class CameraManager extends EventDispatcher {
|
|
|
461
833
|
&& stage.frameCount > 0
|
|
462
834
|
&& ! stage.isComplete ) return;
|
|
463
835
|
|
|
464
|
-
//
|
|
465
|
-
const
|
|
466
|
-
const ndcY = - ( this.afScreenPoint.y * 2 - 1 );
|
|
467
|
-
|
|
468
|
-
const raycaster = this.interactionManager?.raycaster;
|
|
469
|
-
if ( ! raycaster ) return;
|
|
470
|
-
|
|
471
|
-
raycaster.setFromCamera( { x: ndcX, y: ndcY }, this.camera );
|
|
472
|
-
const intersects = raycaster.intersectObjects( meshScene.children, true );
|
|
473
|
-
|
|
474
|
-
const validHit = intersects.find( hit =>
|
|
475
|
-
hit.object !== this.interactionManager?.focusPointIndicator &&
|
|
476
|
-
hit.object !== floorPlane &&
|
|
477
|
-
! hit.object.name.includes( 'Helper' ) &&
|
|
478
|
-
hit.object.type === 'Mesh'
|
|
479
|
-
);
|
|
836
|
+
// AF screen point (0 to 1, y down) to NDC.
|
|
837
|
+
const validHit = this.interactionManager.pickSurface( this.afScreenPoint.x * 2 - 1, 1 - this.afScreenPoint.y * 2 );
|
|
480
838
|
|
|
481
839
|
let rawDistance;
|
|
482
840
|
if ( validHit ) {
|
|
483
841
|
|
|
484
|
-
rawDistance = validHit.
|
|
842
|
+
rawDistance = viewDepth( validHit.point, this.camera );
|
|
485
843
|
this._lastValidFocusDistance = rawDistance;
|
|
486
844
|
|
|
487
845
|
} else {
|
|
@@ -492,8 +850,9 @@ export class CameraManager extends EventDispatcher {
|
|
|
492
850
|
|
|
493
851
|
} else {
|
|
494
852
|
|
|
495
|
-
|
|
496
|
-
|
|
853
|
+
// Nothing under the AF point on a fresh view: focus where the camera orbits.
|
|
854
|
+
const depth = this.controls ? viewDepth( this.controls.target, this.camera ) : 0;
|
|
855
|
+
rawDistance = depth > 0 ? depth : AF_DEFAULTS.FALLBACK_DISTANCE * ( assetLoader?.getSceneScale() || 1.0 );
|
|
497
856
|
this._lastValidFocusDistance = rawDistance;
|
|
498
857
|
|
|
499
858
|
}
|
|
@@ -533,9 +892,8 @@ export class CameraManager extends EventDispatcher {
|
|
|
533
892
|
|
|
534
893
|
setFocusDistance( newFocus );
|
|
535
894
|
|
|
536
|
-
// Update store for UI display (unscaled value)
|
|
537
895
|
const scale = assetLoader?.getSceneScale() || 1.0;
|
|
538
|
-
this.dispatchEvent( { type: EngineEvents.AUTO_FOCUS_UPDATED, distance: newFocus / scale } );
|
|
896
|
+
this.dispatchEvent( { type: EngineEvents.AUTO_FOCUS_UPDATED, distance: newFocus / scale, worldDistance: newFocus } );
|
|
539
897
|
|
|
540
898
|
const changeRatio = Math.abs( newFocus - prevFocus ) / Math.max( prevFocus, 0.001 );
|
|
541
899
|
if ( forceReset ) {
|
|
@@ -569,20 +927,16 @@ export class CameraManager extends EventDispatcher {
|
|
|
569
927
|
* and `updateAutoFocus()` reads from it each frame — no per-frame allocation.
|
|
570
928
|
*
|
|
571
929
|
* @param {Object} deps
|
|
572
|
-
* @param {import('three').Scene} deps.meshScene
|
|
573
930
|
* @param {import('../Processor/AssetLoader.js').AssetLoader} deps.assetLoader
|
|
574
|
-
* @param {import('three').Mesh} deps.floorPlane
|
|
575
931
|
* @param {import('../Stages/PathTracer.js').PathTracer} deps.pathTracer
|
|
576
932
|
* @param {import('../RenderSettings.js').RenderSettings} deps.settings
|
|
577
933
|
* @param {Function} deps.softReset
|
|
578
934
|
* @param {Function} deps.hardReset
|
|
579
935
|
*/
|
|
580
|
-
initAutoFocus( {
|
|
936
|
+
initAutoFocus( { assetLoader, pathTracer, settings, softReset, hardReset } ) {
|
|
581
937
|
|
|
582
938
|
this._afContext = {
|
|
583
|
-
meshScene,
|
|
584
939
|
assetLoader,
|
|
585
|
-
floorPlane,
|
|
586
940
|
pathTracer,
|
|
587
941
|
setFocusDistance: ( d ) => settings.set( 'focusDistance', d, { silent: true } ),
|
|
588
942
|
softReset,
|
|
@@ -598,6 +952,8 @@ export class CameraManager extends EventDispatcher {
|
|
|
598
952
|
|
|
599
953
|
dispose() {
|
|
600
954
|
|
|
955
|
+
this.walkControls?.dispose();
|
|
956
|
+
this.controls?.removeEventListener( 'change', this._reportOrthoHeight );
|
|
601
957
|
this.controls?.dispose();
|
|
602
958
|
|
|
603
959
|
}
|
|
@@ -8,7 +8,7 @@ import { createLogger } from '../utils/Logger.js';
|
|
|
8
8
|
// The neural passes live in `../neural/` but report through the manager that drives them, so they
|
|
9
9
|
// share one namespace: `rayzee.log.only( 'neural' )` shows the whole chain.
|
|
10
10
|
const neuralLog = createLogger( 'neural' );
|
|
11
|
-
import { ENGINE_DEFAULTS as DEFAULT_STATE, ASVGF_QUALITY_PRESETS, NRD_DEFAULTS, NRD_QUALITY_PRESETS, NRD_PRESET_KEYS } from '../EngineDefaults.js';
|
|
11
|
+
import { ENGINE_DEFAULTS as DEFAULT_STATE, ASVGF_QUALITY_PRESETS, NRD_DEFAULTS, NRD_QUALITY_PRESETS, NRD_PRESET_KEYS, CAMERA_PROJECTION_IDS } from '../EngineDefaults.js';
|
|
12
12
|
|
|
13
13
|
// A refresh slower than this is a slideshow, not a live view, so the cadence swaps to a cheaper
|
|
14
14
|
// model and puts the chosen one back for the finished image. Resolution-aware by construction:
|
|
@@ -164,7 +164,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
164
164
|
this._knownResetCount = 0;
|
|
165
165
|
this._seenTracedFrames = 0;
|
|
166
166
|
this._viewProj = new Matrix4();
|
|
167
|
-
this._historyCamera = { world: null, projInv: null, viewProj: null };
|
|
167
|
+
this._historyCamera = { world: null, projInv: null, viewProj: null, orthographic: false };
|
|
168
168
|
// TLAS leaf → { world, offset, prev }: placements moved since the last traced frame.
|
|
169
169
|
this._movedPlacements = new Map();
|
|
170
170
|
this._movedUpload = { count: 0, leaves: new Uint32Array( 0 ), toPrev: new Float32Array( 0 ) };
|
|
@@ -870,7 +870,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
870
870
|
|
|
871
871
|
const pt = this._stages.pathTracer;
|
|
872
872
|
return this.historyWanted && ! this._cadenceSuspended && !! this._stages.normalDepth?.enabled
|
|
873
|
-
&&
|
|
873
|
+
&& pt?.uniforms?.get( 'cameraProjection' )?.value !== CAMERA_PROJECTION_IDS.equirectangular
|
|
874
874
|
&& ! ( pt?.visMode?.value > 0 );
|
|
875
875
|
|
|
876
876
|
}
|
|
@@ -1043,6 +1043,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1043
1043
|
camera.world = pt.cameraWorldMatrix.value.elements;
|
|
1044
1044
|
camera.projInv = pt.cameraProjectionMatrixInverse.value.elements;
|
|
1045
1045
|
camera.viewProj = this._viewProj.elements;
|
|
1046
|
+
camera.orthographic = pt.uniforms?.get( 'cameraProjection' )?.value === CAMERA_PROJECTION_IDS.orthographic;
|
|
1046
1047
|
return camera;
|
|
1047
1048
|
|
|
1048
1049
|
}
|