@liveroom-tech/react-immersive 1.1.6 → 1.1.7

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 CHANGED
@@ -701,6 +701,8 @@ type Props = {
701
701
  measurementUnit?: string;
702
702
  enableXR?: boolean;
703
703
  usdzUrl?: string;
704
+ arScaleMode?: "normalized" | "real-world";
705
+ mobileHandoffUrl?: string;
704
706
  showAnnotationNavigation?: boolean;
705
707
  };
706
708
  ```
@@ -1448,7 +1450,11 @@ Default: disabled.
1448
1450
 
1449
1451
  Optional boolean that enables XR entry points. On WebXR-capable browsers, the viewer shows a **"View in your space"** (AR) and/or **"Enter VR"** button once `navigator.xr.isSessionSupported(...)` resolves `true` for that mode. On iPhone/iPad, WebXR AR is not available, but if you also pass [`usdzUrl`](#usdzUrl), the viewer shows a **"View in AR"** button that launches Apple Quick Look instead.
1450
1452
 
1451
- On entering a session the model is normalized (scaled to ~1 m on its longest axis) and rested on the floor a short distance in front of the viewer, so large/real-scale scenes are framed instead of engulfing the viewer.
1453
+ On entering a session the model is rested on the floor a short distance in front
1454
+ of the viewer. By default it is normalized to ~1 m on its longest axis so
1455
+ arbitrary uploads and room scenes are framed safely. Set
1456
+ [`arScaleMode`](#arscalemode) to `"real-world"` for product AR, which preserves
1457
+ the GLB's authored metre scale.
1452
1458
 
1453
1459
  **AR placement gestures** — inside an AR session the viewer now uses an explicit placement flow:
1454
1460
 
@@ -1485,6 +1491,21 @@ Notes:
1485
1491
  - Android / WebXR-capable devices ignore this and continue using the normal WebXR AR flow
1486
1492
  - if `usdzUrl` is omitted, Apple handheld devices simply won't get an AR button
1487
1493
 
1494
+ ### `arScaleMode`
1495
+
1496
+ Controls the initial model size inside WebXR AR and VR sessions.
1497
+
1498
+ ```ts
1499
+ arScaleMode?: "normalized" | "real-world";
1500
+ ```
1501
+
1502
+ - `"normalized"` (default) fits the longest model dimension to approximately
1503
+ 1 metre. Use this for arbitrary uploads and room-scale scenes.
1504
+ - `"real-world"` preserves the GLB's authored scale. glTF units are metres, so
1505
+ use this for product models exported at their real dimensions.
1506
+
1507
+ Users can still pinch to resize after placement in either mode.
1508
+
1488
1509
  ### `showAnnotationNavigation`
1489
1510
 
1490
1511
  Optional boolean that shows the "Guided Tour" control cluster when the model has at least one annotation: a single "Guided Tour" button that, once started, becomes Previous/Stop/Next controls for stepping through annotations in order.
package/dist/index.d.mts CHANGED
@@ -344,6 +344,8 @@ declare function Viewer({ children, camera, backgroundColor, shadows, sceneConfi
344
344
 
345
345
  type PerformanceProfile = "auto" | "high" | "low";
346
346
 
347
+ type XRScaleMode = "normalized" | "real-world";
348
+
347
349
  type CinematicWaypoint = {
348
350
  position: [number, number, number];
349
351
  target: [number, number, number];
@@ -604,10 +606,11 @@ type ModelViewerProps = {
604
606
  measurementUnit?: string;
605
607
  enableXR?: boolean;
606
608
  usdzUrl?: string;
609
+ arScaleMode?: XRScaleMode;
607
610
  mobileHandoffUrl?: string;
608
611
  showAnnotationNavigation?: boolean;
609
612
  };
610
- declare function ModelViewer({ modelUrl, licenseKey, objectBindings, selectedObject, panelHoveredObject, onObjectBindingsChange, onObjectSelect, onObjectHover, onModelLoaded, onLoadError, onAction, onHiddenObjectsChange, onCameraChange, onViewerReady, onTextureUpload, onAnimationsReady, onAnnotationsChange, activeAnnotation: controlledActiveAnnotation, onActiveAnnotationChange, lights, camera, backgroundColor, shadows, showObjectBindingDataPanel, customObjectBindingDataPanel, customSceneObjectsPanel, showSceneObjectsPanel, showDownloadButton, downloadFilename, showResetButton, showLoadingOverlay, showMouseController, mouseControllerPosition, mouseControllerOpacity, moveSensitivity, zoomSensitivity, sceneConfig, disableZoom, zoomOnSelected, highlightOnHover, onAutoFit, refitOnResize, renderMode, maxDpr, performanceProfile, dracoDecoderPath, ktx2TranscoderPath, meshopt, showMeasureTools, showExplodeControls, cinematic, measurementUnit, enableXR, usdzUrl, mobileHandoffUrl, showAnnotationNavigation, }: ModelViewerProps): react_jsx_runtime.JSX.Element;
613
+ declare function ModelViewer({ modelUrl, licenseKey, objectBindings, selectedObject, panelHoveredObject, onObjectBindingsChange, onObjectSelect, onObjectHover, onModelLoaded, onLoadError, onAction, onHiddenObjectsChange, onCameraChange, onViewerReady, onTextureUpload, onAnimationsReady, onAnnotationsChange, activeAnnotation: controlledActiveAnnotation, onActiveAnnotationChange, lights, camera, backgroundColor, shadows, showObjectBindingDataPanel, customObjectBindingDataPanel, customSceneObjectsPanel, showSceneObjectsPanel, showDownloadButton, downloadFilename, showResetButton, showLoadingOverlay, showMouseController, mouseControllerPosition, mouseControllerOpacity, moveSensitivity, zoomSensitivity, sceneConfig, disableZoom, zoomOnSelected, highlightOnHover, onAutoFit, refitOnResize, renderMode, maxDpr, performanceProfile, dracoDecoderPath, ktx2TranscoderPath, meshopt, showMeasureTools, showExplodeControls, cinematic, measurementUnit, enableXR, usdzUrl, arScaleMode, mobileHandoffUrl, showAnnotationNavigation, }: ModelViewerProps): react_jsx_runtime.JSX.Element;
611
614
 
612
615
  declare function BindingBuilder({ licenseKey }: {
613
616
  licenseKey: string;
@@ -712,4 +715,4 @@ type ShareableViewerStateProps = {
712
715
  };
713
716
  declare function useShareableViewerState({ cameraState, setCameraState, objectBindings, onObjectBindingsChange, selectedObjectBinding, setSelectedObjectBinding, paramKey, debounceMs, autoSync, }: UseShareableViewerStateOptions): ShareableViewerStateProps;
714
717
 
715
- export { type AnimationClipInfo, type AnimationControls, type AnimationLoopMode, type AnimationPlaybackState, BindingBuilder, type CaptureImageOptions, type CinematicConfig, type CinematicWaypoint, type CustomObjectBindingDataPanelProps, type CustomSceneObjectsPanelProps, type HiddenObjectsState, type HomeCameraState, MATERIAL_BLENDING_MODES, MATERIAL_SIDES, ModelViewer, type ModelViewerProps, type ObjectActionEvent, type ObjectBinding, type ObjectBindingAction, type ObjectBindingMaterial, type ObjectBindingMaterialBlendingMode, type ObjectBindingMaterialSide, type ObjectBindingTextureSlot, type ObjectVisibilityProps, type SceneAnimationClipConfig, type SceneAnimationConfig, type SceneCinematicConfig, type SceneCinematicWaypoint, type SceneConfig, SimpleModelViewer, type SimpleModelViewerProps, type ViewerActionsOptions, type ViewerActionsProps, type ViewerAnimationsProps, type ViewerCameraProps, type ViewerCameraState, type ViewerModelBounds, type ViewerModelProps, type ViewerReadyState, type ViewerSelectionProps, useObjectBinding, useObjectBindingIds, useObjectVisibility, useShareableViewerState, useViewerActions, useViewerAnimations, useViewerCamera, useViewerHover, useViewerModel, useViewerSelection };
718
+ export { type AnimationClipInfo, type AnimationControls, type AnimationLoopMode, type AnimationPlaybackState, BindingBuilder, type CaptureImageOptions, type CinematicConfig, type CinematicWaypoint, type CustomObjectBindingDataPanelProps, type CustomSceneObjectsPanelProps, type HiddenObjectsState, type HomeCameraState, MATERIAL_BLENDING_MODES, MATERIAL_SIDES, ModelViewer, type ModelViewerProps, type ObjectActionEvent, type ObjectBinding, type ObjectBindingAction, type ObjectBindingMaterial, type ObjectBindingMaterialBlendingMode, type ObjectBindingMaterialSide, type ObjectBindingTextureSlot, type ObjectVisibilityProps, type SceneAnimationClipConfig, type SceneAnimationConfig, type SceneCinematicConfig, type SceneCinematicWaypoint, type SceneConfig, SimpleModelViewer, type SimpleModelViewerProps, type ViewerActionsOptions, type ViewerActionsProps, type ViewerAnimationsProps, type ViewerCameraProps, type ViewerCameraState, type ViewerModelBounds, type ViewerModelProps, type ViewerReadyState, type ViewerSelectionProps, type XRScaleMode, useObjectBinding, useObjectBindingIds, useObjectVisibility, useShareableViewerState, useViewerActions, useViewerAnimations, useViewerCamera, useViewerHover, useViewerModel, useViewerSelection };
package/dist/index.d.ts CHANGED
@@ -344,6 +344,8 @@ declare function Viewer({ children, camera, backgroundColor, shadows, sceneConfi
344
344
 
345
345
  type PerformanceProfile = "auto" | "high" | "low";
346
346
 
347
+ type XRScaleMode = "normalized" | "real-world";
348
+
347
349
  type CinematicWaypoint = {
348
350
  position: [number, number, number];
349
351
  target: [number, number, number];
@@ -604,10 +606,11 @@ type ModelViewerProps = {
604
606
  measurementUnit?: string;
605
607
  enableXR?: boolean;
606
608
  usdzUrl?: string;
609
+ arScaleMode?: XRScaleMode;
607
610
  mobileHandoffUrl?: string;
608
611
  showAnnotationNavigation?: boolean;
609
612
  };
610
- declare function ModelViewer({ modelUrl, licenseKey, objectBindings, selectedObject, panelHoveredObject, onObjectBindingsChange, onObjectSelect, onObjectHover, onModelLoaded, onLoadError, onAction, onHiddenObjectsChange, onCameraChange, onViewerReady, onTextureUpload, onAnimationsReady, onAnnotationsChange, activeAnnotation: controlledActiveAnnotation, onActiveAnnotationChange, lights, camera, backgroundColor, shadows, showObjectBindingDataPanel, customObjectBindingDataPanel, customSceneObjectsPanel, showSceneObjectsPanel, showDownloadButton, downloadFilename, showResetButton, showLoadingOverlay, showMouseController, mouseControllerPosition, mouseControllerOpacity, moveSensitivity, zoomSensitivity, sceneConfig, disableZoom, zoomOnSelected, highlightOnHover, onAutoFit, refitOnResize, renderMode, maxDpr, performanceProfile, dracoDecoderPath, ktx2TranscoderPath, meshopt, showMeasureTools, showExplodeControls, cinematic, measurementUnit, enableXR, usdzUrl, mobileHandoffUrl, showAnnotationNavigation, }: ModelViewerProps): react_jsx_runtime.JSX.Element;
613
+ declare function ModelViewer({ modelUrl, licenseKey, objectBindings, selectedObject, panelHoveredObject, onObjectBindingsChange, onObjectSelect, onObjectHover, onModelLoaded, onLoadError, onAction, onHiddenObjectsChange, onCameraChange, onViewerReady, onTextureUpload, onAnimationsReady, onAnnotationsChange, activeAnnotation: controlledActiveAnnotation, onActiveAnnotationChange, lights, camera, backgroundColor, shadows, showObjectBindingDataPanel, customObjectBindingDataPanel, customSceneObjectsPanel, showSceneObjectsPanel, showDownloadButton, downloadFilename, showResetButton, showLoadingOverlay, showMouseController, mouseControllerPosition, mouseControllerOpacity, moveSensitivity, zoomSensitivity, sceneConfig, disableZoom, zoomOnSelected, highlightOnHover, onAutoFit, refitOnResize, renderMode, maxDpr, performanceProfile, dracoDecoderPath, ktx2TranscoderPath, meshopt, showMeasureTools, showExplodeControls, cinematic, measurementUnit, enableXR, usdzUrl, arScaleMode, mobileHandoffUrl, showAnnotationNavigation, }: ModelViewerProps): react_jsx_runtime.JSX.Element;
611
614
 
612
615
  declare function BindingBuilder({ licenseKey }: {
613
616
  licenseKey: string;
@@ -712,4 +715,4 @@ type ShareableViewerStateProps = {
712
715
  };
713
716
  declare function useShareableViewerState({ cameraState, setCameraState, objectBindings, onObjectBindingsChange, selectedObjectBinding, setSelectedObjectBinding, paramKey, debounceMs, autoSync, }: UseShareableViewerStateOptions): ShareableViewerStateProps;
714
717
 
715
- export { type AnimationClipInfo, type AnimationControls, type AnimationLoopMode, type AnimationPlaybackState, BindingBuilder, type CaptureImageOptions, type CinematicConfig, type CinematicWaypoint, type CustomObjectBindingDataPanelProps, type CustomSceneObjectsPanelProps, type HiddenObjectsState, type HomeCameraState, MATERIAL_BLENDING_MODES, MATERIAL_SIDES, ModelViewer, type ModelViewerProps, type ObjectActionEvent, type ObjectBinding, type ObjectBindingAction, type ObjectBindingMaterial, type ObjectBindingMaterialBlendingMode, type ObjectBindingMaterialSide, type ObjectBindingTextureSlot, type ObjectVisibilityProps, type SceneAnimationClipConfig, type SceneAnimationConfig, type SceneCinematicConfig, type SceneCinematicWaypoint, type SceneConfig, SimpleModelViewer, type SimpleModelViewerProps, type ViewerActionsOptions, type ViewerActionsProps, type ViewerAnimationsProps, type ViewerCameraProps, type ViewerCameraState, type ViewerModelBounds, type ViewerModelProps, type ViewerReadyState, type ViewerSelectionProps, useObjectBinding, useObjectBindingIds, useObjectVisibility, useShareableViewerState, useViewerActions, useViewerAnimations, useViewerCamera, useViewerHover, useViewerModel, useViewerSelection };
718
+ export { type AnimationClipInfo, type AnimationControls, type AnimationLoopMode, type AnimationPlaybackState, BindingBuilder, type CaptureImageOptions, type CinematicConfig, type CinematicWaypoint, type CustomObjectBindingDataPanelProps, type CustomSceneObjectsPanelProps, type HiddenObjectsState, type HomeCameraState, MATERIAL_BLENDING_MODES, MATERIAL_SIDES, ModelViewer, type ModelViewerProps, type ObjectActionEvent, type ObjectBinding, type ObjectBindingAction, type ObjectBindingMaterial, type ObjectBindingMaterialBlendingMode, type ObjectBindingMaterialSide, type ObjectBindingTextureSlot, type ObjectVisibilityProps, type SceneAnimationClipConfig, type SceneAnimationConfig, type SceneCinematicConfig, type SceneCinematicWaypoint, type SceneConfig, SimpleModelViewer, type SimpleModelViewerProps, type ViewerActionsOptions, type ViewerActionsProps, type ViewerAnimationsProps, type ViewerCameraProps, type ViewerCameraState, type ViewerModelBounds, type ViewerModelProps, type ViewerReadyState, type ViewerSelectionProps, type XRScaleMode, useObjectBinding, useObjectBindingIds, useObjectVisibility, useShareableViewerState, useViewerActions, useViewerAnimations, useViewerCamera, useViewerHover, useViewerModel, useViewerSelection };
package/dist/index.js CHANGED
@@ -3631,12 +3631,15 @@ var TARGET_SIZE = 1;
3631
3631
  var FORWARD_DISTANCE = 1;
3632
3632
  var MIN_USER_SCALE = 0.1;
3633
3633
  var MAX_USER_SCALE = 10;
3634
- function computeNormalization(box) {
3634
+ function computeNormalization(box, scaleMode) {
3635
3635
  const [sx, sy, sz] = box.size;
3636
3636
  const cx = (box.min[0] + box.max[0]) / 2;
3637
3637
  const cz = (box.min[2] + box.max[2]) / 2;
3638
3638
  const maxDim = Math.max(sx, sy, sz) || 1;
3639
- return { scale: TARGET_SIZE / maxDim, offset: [-cx, -box.min[1], -cz] };
3639
+ return {
3640
+ scale: scaleMode === "real-world" ? 1 : TARGET_SIZE / maxDim,
3641
+ offset: [-cx, -box.min[1], -cz]
3642
+ };
3640
3643
  }
3641
3644
  function touchDistance(touches) {
3642
3645
  const dx = touches[0].clientX - touches[1].clientX;
@@ -3649,15 +3652,15 @@ function touchAngle(touches) {
3649
3652
  return Math.atan2(dy, dx);
3650
3653
  }
3651
3654
  var XRModelPresenter = (0, import_react14.forwardRef)(
3652
- function XRModelPresenter2({ box, children, onARStateChange }, ref) {
3655
+ function XRModelPresenter2({ box, scaleMode, children, onARStateChange }, ref) {
3653
3656
  const placementRef = (0, import_react14.useRef)(null);
3654
3657
  const scaleRef = (0, import_react14.useRef)(null);
3655
3658
  const reticleRef = (0, import_react14.useRef)(null);
3656
3659
  const inSession = (0, import_xr3.useXR)((state) => state.session != null);
3657
3660
  const isAR = (0, import_xr3.useXR)((state) => state.mode === "immersive-ar");
3658
3661
  const normalization = (0, import_react14.useMemo)(
3659
- () => box ? computeNormalization(box) : null,
3660
- [box]
3662
+ () => box ? computeNormalization(box, scaleMode) : null,
3663
+ [box, scaleMode]
3661
3664
  );
3662
3665
  const placedPosition = (0, import_react14.useRef)(new import_three16.Vector3(0, 0, -FORWARD_DISTANCE));
3663
3666
  const userScale = (0, import_react14.useRef)(1);
@@ -5221,6 +5224,7 @@ function ModelViewer({
5221
5224
  measurementUnit = "m",
5222
5225
  enableXR = false,
5223
5226
  usdzUrl,
5227
+ arScaleMode = "normalized",
5224
5228
  mobileHandoffUrl,
5225
5229
  showAnnotationNavigation = true
5226
5230
  }) {
@@ -6784,6 +6788,7 @@ function ModelViewer({
6784
6788
  {
6785
6789
  ref: xrPresenterRef,
6786
6790
  box: measureBox,
6791
+ scaleMode: arScaleMode,
6787
6792
  onARStateChange: handleARStateChange,
6788
6793
  children: modelContent
6789
6794
  }
package/dist/index.mjs CHANGED
@@ -3672,12 +3672,15 @@ var TARGET_SIZE = 1;
3672
3672
  var FORWARD_DISTANCE = 1;
3673
3673
  var MIN_USER_SCALE = 0.1;
3674
3674
  var MAX_USER_SCALE = 10;
3675
- function computeNormalization(box) {
3675
+ function computeNormalization(box, scaleMode) {
3676
3676
  const [sx, sy, sz] = box.size;
3677
3677
  const cx = (box.min[0] + box.max[0]) / 2;
3678
3678
  const cz = (box.min[2] + box.max[2]) / 2;
3679
3679
  const maxDim = Math.max(sx, sy, sz) || 1;
3680
- return { scale: TARGET_SIZE / maxDim, offset: [-cx, -box.min[1], -cz] };
3680
+ return {
3681
+ scale: scaleMode === "real-world" ? 1 : TARGET_SIZE / maxDim,
3682
+ offset: [-cx, -box.min[1], -cz]
3683
+ };
3681
3684
  }
3682
3685
  function touchDistance(touches) {
3683
3686
  const dx = touches[0].clientX - touches[1].clientX;
@@ -3690,15 +3693,15 @@ function touchAngle(touches) {
3690
3693
  return Math.atan2(dy, dx);
3691
3694
  }
3692
3695
  var XRModelPresenter = forwardRef2(
3693
- function XRModelPresenter2({ box, children, onARStateChange }, ref) {
3696
+ function XRModelPresenter2({ box, scaleMode, children, onARStateChange }, ref) {
3694
3697
  const placementRef = useRef7(null);
3695
3698
  const scaleRef = useRef7(null);
3696
3699
  const reticleRef = useRef7(null);
3697
3700
  const inSession = useXR2((state) => state.session != null);
3698
3701
  const isAR = useXR2((state) => state.mode === "immersive-ar");
3699
3702
  const normalization = useMemo10(
3700
- () => box ? computeNormalization(box) : null,
3701
- [box]
3703
+ () => box ? computeNormalization(box, scaleMode) : null,
3704
+ [box, scaleMode]
3702
3705
  );
3703
3706
  const placedPosition = useRef7(new Vector35(0, 0, -FORWARD_DISTANCE));
3704
3707
  const userScale = useRef7(1);
@@ -5287,6 +5290,7 @@ function ModelViewer({
5287
5290
  measurementUnit = "m",
5288
5291
  enableXR = false,
5289
5292
  usdzUrl,
5293
+ arScaleMode = "normalized",
5290
5294
  mobileHandoffUrl,
5291
5295
  showAnnotationNavigation = true
5292
5296
  }) {
@@ -6850,6 +6854,7 @@ function ModelViewer({
6850
6854
  {
6851
6855
  ref: xrPresenterRef,
6852
6856
  box: measureBox,
6857
+ scaleMode: arScaleMode,
6853
6858
  onARStateChange: handleARStateChange,
6854
6859
  children: modelContent
6855
6860
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liveroom-tech/react-immersive",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",