rayzee 7.10.0 → 7.10.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rayzee",
3
- "version": "7.10.0",
3
+ "version": "7.10.1",
4
4
  "type": "module",
5
5
  "description": "Real-time WebGPU path tracing engine built on Three.js",
6
6
  "main": "dist/rayzee.umd.js",
@@ -927,6 +927,7 @@ export class PathTracerApp extends EventDispatcher {
927
927
  async _finishRebuildNoReframe( eventPayload ) {
928
928
 
929
929
  await this.loadSceneData(); // emits 'SceneRebuild'
930
+ this._recalibrateControlLimits(); // scene bounds changed — retune zoom limits + near/far (no camera move)
930
931
  this.pipeline?.eventBus.emit( 'autoexposure:resetHistory' );
931
932
  this.reset();
932
933
  if ( eventPayload ) this.dispatchEvent( eventPayload );
@@ -2023,6 +2024,63 @@ export class PathTracerApp extends EventDispatcher {
2023
2024
 
2024
2025
  }
2025
2026
 
2027
+ /**
2028
+ * Recompute OrbitControls zoom limits (+ default-camera near/far) from the CURRENT
2029
+ * model bounds without moving the camera or its target. Called after a dynamic
2030
+ * add/remove (the reframe-free path) so an enlarged scene stays reachable and
2031
+ * unclipped, and a shrunken one re-tightens. The replace-load (reframe) path owns
2032
+ * this via onModelLoad(). Bounds cover only the loaded model roots
2033
+ * (__rayzeeSceneObject) so the oversized, usually-hidden Ground plane can't inflate them.
2034
+ */
2035
+ _recalibrateControlLimits() {
2036
+
2037
+ if ( ! this.meshScene || ! this.cameraManager ) return;
2038
+
2039
+ const bounds = new Box3();
2040
+ const tmp = new Box3();
2041
+ for ( const child of this.meshScene.children ) {
2042
+
2043
+ if ( ! child.userData?.__rayzeeSceneObject ) continue;
2044
+ tmp.setFromObject( child );
2045
+ if ( ! tmp.isEmpty() ) bounds.union( tmp );
2046
+
2047
+ }
2048
+
2049
+ if ( bounds.isEmpty() ) return;
2050
+
2051
+ const maxDim = Math.max(
2052
+ bounds.max.x - bounds.min.x,
2053
+ bounds.max.y - bounds.min.y,
2054
+ bounds.max.z - bounds.min.z,
2055
+ );
2056
+ if ( ! Number.isFinite( maxDim ) || maxDim <= 0 ) return;
2057
+
2058
+ const { camera, controls } = this.cameraManager;
2059
+
2060
+ // Same framing distance onModelLoad() uses for the initial reframe.
2061
+ const fov = camera.fov * ( Math.PI / 180 );
2062
+ const cameraDistance = Math.abs( maxDim / Math.sin( fov / 2 ) / 2 );
2063
+
2064
+ // Keep the (grown/shrunken) scene inside the frustum. Only touch near/far when the
2065
+ // default orbit camera is active — don't stomp an authored model camera's frustum.
2066
+ if ( this.cameraManager.currentCameraIndex === 0 ) {
2067
+
2068
+ camera.near = maxDim / 100;
2069
+ camera.far = maxDim * 100;
2070
+ camera.updateProjectionMatrix();
2071
+
2072
+ }
2073
+
2074
+ // Reframe-free: never clamp past where the camera currently sits, so the rebuild
2075
+ // can't yank it (e.g. when the scene shrinks after a removal).
2076
+ const currentDist = camera.position.distanceTo( controls.target );
2077
+ controls.minDistance = Math.min( maxDim / 1000, currentDist );
2078
+ controls.maxDistance = Math.max( cameraDistance * 10, currentDist * 1.1 );
2079
+
2080
+ controls.update();
2081
+
2082
+ }
2083
+
2026
2084
  /**
2027
2085
  * Forwards events from a source EventDispatcher to this app instance.
2028
2086
  */