rayzee 7.15.2 → 7.16.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 +1 -1
- package/dist/rayzee.es.js +102 -39
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +2 -2
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/PathTracerApp.js +54 -5
- package/src/RenderSettings.js +4 -2
- package/src/managers/CameraManager.js +160 -9
package/package.json
CHANGED
package/src/PathTracerApp.js
CHANGED
|
@@ -775,11 +775,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
775
775
|
this.reset();
|
|
776
776
|
this.cameraManager.currentCameraIndex = 0;
|
|
777
777
|
this.dispatchEvent( eventPayload );
|
|
778
|
-
this.
|
|
779
|
-
type: 'CamerasUpdated',
|
|
780
|
-
cameras: this.cameraManager.cameras,
|
|
781
|
-
cameraNames: this.cameraManager.getCameraNames()
|
|
782
|
-
} );
|
|
778
|
+
this._dispatchCamerasUpdated();
|
|
783
779
|
|
|
784
780
|
} finally {
|
|
785
781
|
|
|
@@ -1093,6 +1089,57 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1093
1089
|
|
|
1094
1090
|
}
|
|
1095
1091
|
|
|
1092
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1093
|
+
// Dynamic cameras (add / remove)
|
|
1094
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1095
|
+
|
|
1096
|
+
/** The index of the currently active camera (0 = built-in default). */
|
|
1097
|
+
get currentCameraIndex() {
|
|
1098
|
+
|
|
1099
|
+
return this.cameraManager?.currentCameraIndex ?? 0;
|
|
1100
|
+
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Snapshot the current view as a new named camera and switch to it.
|
|
1105
|
+
* @param {Object} [opts]
|
|
1106
|
+
* @param {string} [opts.name] - Display name (auto-generated otherwise).
|
|
1107
|
+
* @returns {number} The index of the newly added camera.
|
|
1108
|
+
*/
|
|
1109
|
+
addCamera( { name } = {} ) {
|
|
1110
|
+
|
|
1111
|
+
const index = this.cameraManager.addCameraFromView( name );
|
|
1112
|
+
this.cameraManager.switchCamera( index );
|
|
1113
|
+
this._dispatchCamerasUpdated();
|
|
1114
|
+
return index;
|
|
1115
|
+
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Remove a user-added camera by index. Built-in and model-embedded cameras
|
|
1120
|
+
* are protected. Falls back to the default camera if the active one is removed.
|
|
1121
|
+
* @param {number} index
|
|
1122
|
+
* @returns {boolean} true if a camera was removed.
|
|
1123
|
+
*/
|
|
1124
|
+
removeCamera( index ) {
|
|
1125
|
+
|
|
1126
|
+
const removed = this.cameraManager.removeCamera( index );
|
|
1127
|
+
if ( removed ) this._dispatchCamerasUpdated();
|
|
1128
|
+
return removed;
|
|
1129
|
+
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/** Notify consumers that the camera list changed (names / count). */
|
|
1133
|
+
_dispatchCamerasUpdated() {
|
|
1134
|
+
|
|
1135
|
+
this.dispatchEvent( {
|
|
1136
|
+
type: 'CamerasUpdated',
|
|
1137
|
+
cameras: this.cameraManager.cameras,
|
|
1138
|
+
cameraNames: this.cameraManager.getCameraNames(),
|
|
1139
|
+
} );
|
|
1140
|
+
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1096
1143
|
// ═══════════════════════════════════════════════════════════════
|
|
1097
1144
|
// BVH Refit (Animation)
|
|
1098
1145
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -1874,6 +1921,8 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1874
1921
|
onResize: () => this.onResize(),
|
|
1875
1922
|
onReset: () => this.reset(),
|
|
1876
1923
|
getSettings: ( k ) => this.settings.get( k ),
|
|
1924
|
+
// Per-camera DOF restore — silent + reset:false so switchCamera's own onReset() is the single reset.
|
|
1925
|
+
applySettings: ( updates ) => this.settings.setMany( updates, { silent: true, reset: false } ),
|
|
1877
1926
|
} );
|
|
1878
1927
|
|
|
1879
1928
|
// Auto-focus context — CameraManager stores it, reads it each frame
|
package/src/RenderSettings.js
CHANGED
|
@@ -252,8 +252,9 @@ export class RenderSettings extends EventDispatcher {
|
|
|
252
252
|
* @param {Object} updates - Key/value pairs
|
|
253
253
|
* @param {Object} [options]
|
|
254
254
|
* @param {boolean} [options.silent] - Suppress settingChanged events
|
|
255
|
+
* @param {boolean} [options.reset] - Override the routes' default reset behavior
|
|
255
256
|
*/
|
|
256
|
-
setMany( updates, { silent } = {} ) {
|
|
257
|
+
setMany( updates, { silent, reset } = {} ) {
|
|
257
258
|
|
|
258
259
|
let needsReset = false;
|
|
259
260
|
|
|
@@ -279,7 +280,8 @@ export class RenderSettings extends EventDispatcher {
|
|
|
279
280
|
|
|
280
281
|
}
|
|
281
282
|
|
|
282
|
-
|
|
283
|
+
const shouldReset = reset !== undefined ? reset : needsReset;
|
|
284
|
+
if ( shouldReset ) this._resetCallback?.();
|
|
283
285
|
|
|
284
286
|
}
|
|
285
287
|
|
|
@@ -35,6 +35,9 @@ export class CameraManager extends EventDispatcher {
|
|
|
35
35
|
this.cameras = [ this.camera ];
|
|
36
36
|
this.currentCameraIndex = 0;
|
|
37
37
|
|
|
38
|
+
// Monotonic counter for naming user-added cameras (reset when the list is replaced).
|
|
39
|
+
this._userCameraCounter = 0;
|
|
40
|
+
|
|
38
41
|
// Auto-focus state
|
|
39
42
|
this.autoFocusMode = AF_DEFAULTS.SMOOTHING_FACTOR ? 'auto' : 'manual';
|
|
40
43
|
this.afScreenPoint = { x: 0.5, y: 0.5 };
|
|
@@ -51,6 +54,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
51
54
|
this._onResize = null;
|
|
52
55
|
this._onReset = null;
|
|
53
56
|
this._getSettings = null;
|
|
57
|
+
this._applySettings = null;
|
|
54
58
|
|
|
55
59
|
}
|
|
56
60
|
|
|
@@ -61,6 +65,70 @@ export class CameraManager extends EventDispatcher {
|
|
|
61
65
|
setCameras( cameras ) {
|
|
62
66
|
|
|
63
67
|
this.cameras = cameras;
|
|
68
|
+
this._userCameraCounter = 0;
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Adds a new user camera that snapshots the current render camera's pose,
|
|
74
|
+
* FOV, clip planes, and orbit target. The snapshot is a standalone template
|
|
75
|
+
* appended to the list; switching to it restores this exact framing.
|
|
76
|
+
*
|
|
77
|
+
* @param {string} [name] - Optional display name (auto-generated otherwise).
|
|
78
|
+
* @returns {number} The index of the newly added camera.
|
|
79
|
+
*/
|
|
80
|
+
addCameraFromView( name ) {
|
|
81
|
+
|
|
82
|
+
const src = this.camera;
|
|
83
|
+
const cam = new PerspectiveCamera( src.fov, src.aspect, src.near, src.far );
|
|
84
|
+
cam.position.copy( src.position );
|
|
85
|
+
cam.quaternion.copy( src.quaternion );
|
|
86
|
+
cam.updateMatrixWorld( true );
|
|
87
|
+
|
|
88
|
+
cam.userData.__rayzeeUserCamera = true;
|
|
89
|
+
cam.userData.__rayzeeEffects = this._captureEffects();
|
|
90
|
+
if ( this.controls ) cam.userData.__rayzeeOrbitTarget = this.controls.target.clone();
|
|
91
|
+
|
|
92
|
+
this._userCameraCounter += 1;
|
|
93
|
+
cam.name = name || `View Camera ${this._userCameraCounter}`;
|
|
94
|
+
|
|
95
|
+
this.cameras.push( cam );
|
|
96
|
+
return this.cameras.length - 1;
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Removes a user-added camera by index. Built-in (index 0) and
|
|
102
|
+
* model-embedded cameras are protected and cannot be removed.
|
|
103
|
+
* If the active camera is removed, falls back to the default camera.
|
|
104
|
+
*
|
|
105
|
+
* @param {number} index
|
|
106
|
+
* @returns {boolean} true if a camera was removed.
|
|
107
|
+
*/
|
|
108
|
+
removeCamera( index ) {
|
|
109
|
+
|
|
110
|
+
if ( ! Number.isInteger( index ) || index <= 0 || index >= this.cameras.length ) return false;
|
|
111
|
+
if ( ! this.cameras[ index ]?.userData?.__rayzeeUserCamera ) return false;
|
|
112
|
+
|
|
113
|
+
const wasActive = this.currentCameraIndex === index;
|
|
114
|
+
this.cameras.splice( index, 1 );
|
|
115
|
+
|
|
116
|
+
if ( wasActive ) {
|
|
117
|
+
|
|
118
|
+
// The active camera is gone. Invalidate currentCameraIndex before falling
|
|
119
|
+
// back so switchCamera(0) skips both the (deleted) outgoing-effects save and
|
|
120
|
+
// the default-state save, and runs the restore path instead.
|
|
121
|
+
this.currentCameraIndex = - 1;
|
|
122
|
+
this.switchCamera( 0 );
|
|
123
|
+
|
|
124
|
+
} else if ( this.currentCameraIndex > index ) {
|
|
125
|
+
|
|
126
|
+
// Indices after the removed slot shift down by one.
|
|
127
|
+
this.currentCameraIndex -= 1;
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true;
|
|
64
132
|
|
|
65
133
|
}
|
|
66
134
|
|
|
@@ -86,15 +154,74 @@ export class CameraManager extends EventDispatcher {
|
|
|
86
154
|
* Call once after all managers are ready.
|
|
87
155
|
*
|
|
88
156
|
* @param {Object} callbacks
|
|
89
|
-
* @param {Function} callbacks.onResize
|
|
90
|
-
* @param {Function} callbacks.onReset
|
|
91
|
-
* @param {Function} callbacks.getSettings
|
|
157
|
+
* @param {Function} callbacks.onResize - Trigger viewport resize
|
|
158
|
+
* @param {Function} callbacks.onReset - Trigger accumulation reset
|
|
159
|
+
* @param {Function} callbacks.getSettings - (key) => value
|
|
160
|
+
* @param {Function} callbacks.applySettings - (updates) => void, batch-writes render settings
|
|
92
161
|
*/
|
|
93
|
-
initCallbacks( { onResize, onReset, getSettings } ) {
|
|
162
|
+
initCallbacks( { onResize, onReset, getSettings, applySettings } ) {
|
|
94
163
|
|
|
95
164
|
this._onResize = onResize;
|
|
96
165
|
this._onReset = onReset;
|
|
97
166
|
this._getSettings = getSettings;
|
|
167
|
+
this._applySettings = applySettings;
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── Per-camera effects (DOF / focus) ──────────────────────────
|
|
172
|
+
// Each camera keeps its own depth-of-field + focus configuration so effects
|
|
173
|
+
// defined on one camera don't leak to another. State is stored on the camera
|
|
174
|
+
// object's userData and swapped in/out on switchCamera().
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Snapshot the current global DOF/focus settings into a plain object.
|
|
178
|
+
* focusDistance is stored scaled (as held in RenderSettings).
|
|
179
|
+
* @returns {Object|null}
|
|
180
|
+
*/
|
|
181
|
+
_captureEffects() {
|
|
182
|
+
|
|
183
|
+
const get = this._getSettings;
|
|
184
|
+
if ( ! get ) return null;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
enableDOF: get( 'enableDOF' ),
|
|
188
|
+
focusDistance: get( 'focusDistance' ),
|
|
189
|
+
aperture: get( 'aperture' ),
|
|
190
|
+
focalLength: get( 'focalLength' ),
|
|
191
|
+
apertureScale: get( 'apertureScale' ),
|
|
192
|
+
anamorphicRatio: get( 'anamorphicRatio' ),
|
|
193
|
+
autoFocusMode: this.autoFocusMode,
|
|
194
|
+
afScreenPoint: { ...this.afScreenPoint },
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Apply a previously-captured effects object to the global settings + focus state.
|
|
201
|
+
* @param {Object} [eff]
|
|
202
|
+
*/
|
|
203
|
+
_applyEffects( eff ) {
|
|
204
|
+
|
|
205
|
+
if ( ! eff ) return;
|
|
206
|
+
|
|
207
|
+
this._applySettings?.( {
|
|
208
|
+
enableDOF: eff.enableDOF,
|
|
209
|
+
focusDistance: eff.focusDistance,
|
|
210
|
+
aperture: eff.aperture,
|
|
211
|
+
focalLength: eff.focalLength,
|
|
212
|
+
apertureScale: eff.apertureScale,
|
|
213
|
+
anamorphicRatio: eff.anamorphicRatio,
|
|
214
|
+
} );
|
|
215
|
+
|
|
216
|
+
if ( eff.autoFocusMode !== undefined ) this.setAutoFocusMode( eff.autoFocusMode );
|
|
217
|
+
if ( eff.afScreenPoint ) this.setAFScreenPoint( eff.afScreenPoint.x, eff.afScreenPoint.y );
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Scene scale used to convert scaled focusDistance to UI-facing units. */
|
|
222
|
+
_getSceneScale() {
|
|
223
|
+
|
|
224
|
+
return this._afContext?.assetLoader?.getSceneScale?.() || 1;
|
|
98
225
|
|
|
99
226
|
}
|
|
100
227
|
|
|
@@ -122,6 +249,10 @@ export class CameraManager extends EventDispatcher {
|
|
|
122
249
|
|
|
123
250
|
}
|
|
124
251
|
|
|
252
|
+
// Save the outgoing camera's DOF/focus effects so they can be restored later.
|
|
253
|
+
const outgoing = this.cameras[ this.currentCameraIndex ];
|
|
254
|
+
if ( outgoing ) outgoing.userData.__rayzeeEffects = this._captureEffects();
|
|
255
|
+
|
|
125
256
|
// Save default camera state before switching away from it
|
|
126
257
|
if ( this.currentCameraIndex === 0 && index !== 0 ) {
|
|
127
258
|
|
|
@@ -169,21 +300,41 @@ export class CameraManager extends EventDispatcher {
|
|
|
169
300
|
this.camera.updateProjectionMatrix();
|
|
170
301
|
this.camera.updateMatrixWorld( true );
|
|
171
302
|
|
|
172
|
-
//
|
|
303
|
+
// Restore the saved orbit target for user cameras; otherwise place it
|
|
304
|
+
// along the forward direction at the focus distance.
|
|
173
305
|
if ( this.controls ) {
|
|
174
306
|
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
307
|
+
const savedTarget = sourceCamera.userData?.__rayzeeOrbitTarget;
|
|
308
|
+
if ( savedTarget ) {
|
|
309
|
+
|
|
310
|
+
this.controls.target.copy( savedTarget );
|
|
311
|
+
|
|
312
|
+
} else {
|
|
313
|
+
|
|
314
|
+
const forward = new Vector3( 0, 0, - 1 ).applyQuaternion( sourceCamera.quaternion );
|
|
315
|
+
const focusDist = focusDistance || 5.0;
|
|
316
|
+
this.controls.target.copy( this.camera.position ).addScaledVector( forward, focusDist );
|
|
317
|
+
|
|
318
|
+
}
|
|
319
|
+
|
|
178
320
|
this.controls.update();
|
|
179
321
|
|
|
180
322
|
}
|
|
181
323
|
|
|
182
324
|
}
|
|
183
325
|
|
|
326
|
+
// Restore the incoming camera's own DOF/focus effects (if it has any saved;
|
|
327
|
+
// otherwise it inherits the current global config).
|
|
328
|
+
this._applyEffects( this.cameras[ index ]?.userData?.__rayzeeEffects );
|
|
329
|
+
|
|
184
330
|
onResize?.();
|
|
185
331
|
onReset?.();
|
|
186
|
-
|
|
332
|
+
|
|
333
|
+
// Emit UI-facing effects: focusDistance unscaled (matching AUTO_FOCUS_UPDATED),
|
|
334
|
+
// so the adapter is a plain passthrough with no scene-scale awareness.
|
|
335
|
+
const effects = this._captureEffects();
|
|
336
|
+
if ( effects ) effects.focusDistance /= this._getSceneScale();
|
|
337
|
+
this.dispatchEvent( { type: 'CameraSwitched', cameraIndex: index, effects, fov: this.camera.fov } );
|
|
187
338
|
|
|
188
339
|
}
|
|
189
340
|
|