extens model-viewer so it exposes the models anchor to allow moving the model in AR Context
This commit is contained in:
1 parent
5ce6d73279
commit
c6f789a7c9
6 files changed
+300
-2
No files matched your search
@@ -18154,6 +18154,7 @@ class ARRenderer extends EventDispatcher {
|
|||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.currentSession = null;
|
this.currentSession = null;
|
||||||
this.placementMode = 'floor';
|
this.placementMode = 'floor';
|
||||||
|
this.anchorOffset = null;
|
||||||
this.placementBox = null;
|
this.placementBox = null;
|
||||||
this.menuPanel = null;
|
this.menuPanel = null;
|
||||||
this.lastTick = null;
|
this.lastTick = null;
|
||||||
@@ -18172,6 +18173,7 @@ class ARRenderer extends EventDispatcher {
|
|||||||
this.xrController1 = null;
|
this.xrController1 = null;
|
||||||
this.xrController2 = null;
|
this.xrController2 = null;
|
||||||
this.selectedXRController = null;
|
this.selectedXRController = null;
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
this.tracking = true;
|
this.tracking = true;
|
||||||
this.frames = 0;
|
this.frames = 0;
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
@@ -18384,6 +18386,35 @@ class ARRenderer extends EventDispatcher {
|
|||||||
get presentedScene() {
|
get presentedScene() {
|
||||||
return this._presentedScene;
|
return this._presentedScene;
|
||||||
}
|
}
|
||||||
|
get currentGoalPosition() {
|
||||||
|
return this.goalPosition;
|
||||||
|
}
|
||||||
|
get isObjectPlaced() {
|
||||||
|
return this.placementComplete;
|
||||||
|
}
|
||||||
|
updateAnchor(newAnchor) {
|
||||||
|
var _a;
|
||||||
|
if (newAnchor) {
|
||||||
|
const parts = newAnchor.split(' ').map(Number);
|
||||||
|
if (parts.length === 3 && parts.every(p => !isNaN(p))) {
|
||||||
|
const newPosition = new Vector3(parts[0], parts[1], parts[2]);
|
||||||
|
this.goalPosition.copy(newPosition);
|
||||||
|
this.parsedAnchorOffset = newPosition;
|
||||||
|
if (!this.placementComplete) {
|
||||||
|
this.placementComplete = true;
|
||||||
|
this.worldSpaceInitialPlacementDone = true;
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
(_a = this.presentedScene) === null || _a === void 0 ? void 0 : _a.setShadowIntensity(AR_SHADOW_INTENSITY);
|
||||||
|
this.dispatchEvent({ type: 'status', status: ARStatus.OBJECT_PLACED });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`Invalid dynamic ar-anchor value: "${newAnchor}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Resolves to true if the renderer has detected all the necessary qualities
|
* Resolves to true if the renderer has detected all the necessary qualities
|
||||||
* to support presentation in AR.
|
* to support presentation in AR.
|
||||||
@@ -18407,6 +18438,16 @@ class ARRenderer extends EventDispatcher {
|
|||||||
if (this.isPresenting) {
|
if (this.isPresenting) {
|
||||||
console.warn('Cannot present while a model is already presenting');
|
console.warn('Cannot present while a model is already presenting');
|
||||||
}
|
}
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
|
if (this.anchorOffset) {
|
||||||
|
const parts = this.anchorOffset.split(' ').map(Number);
|
||||||
|
if (parts.length === 3 && parts.every(p => !isNaN(p))) {
|
||||||
|
this.parsedAnchorOffset = new Vector3(parts[0], parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`Invalid ar-anchor value: "${this.anchorOffset}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
let waitForAnimationFrame = new Promise((resolve, _reject) => {
|
let waitForAnimationFrame = new Promise((resolve, _reject) => {
|
||||||
requestAnimationFrame(() => resolve());
|
requestAnimationFrame(() => resolve());
|
||||||
});
|
});
|
||||||
@@ -18680,6 +18721,8 @@ class ARRenderer extends EventDispatcher {
|
|||||||
this.inputSource = null;
|
this.inputSource = null;
|
||||||
this.overlay = null;
|
this.overlay = null;
|
||||||
this.worldSpaceInitialPlacementDone = false;
|
this.worldSpaceInitialPlacementDone = false;
|
||||||
|
this.anchorOffset = null;
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
if (this.resolveCleanup != null) {
|
if (this.resolveCleanup != null) {
|
||||||
this.resolveCleanup();
|
this.resolveCleanup();
|
||||||
}
|
}
|
||||||
@@ -18714,6 +18757,40 @@ class ARRenderer extends EventDispatcher {
|
|||||||
const { pivot, element } = scene;
|
const { pivot, element } = scene;
|
||||||
const { position } = pivot;
|
const { position } = pivot;
|
||||||
const xrCamera = scene.getCamera();
|
const xrCamera = scene.getCamera();
|
||||||
|
if (this.parsedAnchorOffset != null) {
|
||||||
|
// Set position directly from the provided anchor offset.
|
||||||
|
// This position is relative to the initial XR reference space origin.
|
||||||
|
position.copy(this.parsedAnchorOffset);
|
||||||
|
this.goalPosition.copy(this.parsedAnchorOffset);
|
||||||
|
// Set up the scene for presentation
|
||||||
|
scene.setHotspotsVisibility(true);
|
||||||
|
scene.visible = true;
|
||||||
|
scene.setShadowIntensity(AR_SHADOW_INTENSITY);
|
||||||
|
// Mark placement as complete to bypass further automatic placement.
|
||||||
|
this.placementComplete = true;
|
||||||
|
this.worldSpaceInitialPlacementDone = true;
|
||||||
|
// Hide the placement box as it's not needed.
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
this.dispatchEvent({ type: 'status', status: ARStatus.OBJECT_PLACED });
|
||||||
|
// Enable user interaction controls for the appropriate mode.
|
||||||
|
if (this.xrMode === XRMode.SCREEN_SPACE) {
|
||||||
|
const { session } = this.frame;
|
||||||
|
session.addEventListener('selectstart', this.onSelectStart);
|
||||||
|
session.addEventListener('selectend', this.onSelectEnd);
|
||||||
|
session.requestHitTestSourceForTransientInput({ profile: 'generic-touchscreen' })
|
||||||
|
.then(hitTestSource => { this.transientHitTestSource = hitTestSource; });
|
||||||
|
}
|
||||||
|
else { // WORLD_SPACE
|
||||||
|
this.enableWorldSpaceUserInteraction();
|
||||||
|
// Hide the placement box again as it's not needed for manual anchoring.
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // Skip the rest of the automatic placement logic.
|
||||||
|
}
|
||||||
const { width, height } = this.overlay.getBoundingClientRect();
|
const { width, height } = this.overlay.getBoundingClientRect();
|
||||||
scene.setSize(width, height);
|
scene.setSize(width, height);
|
||||||
xrCamera.projectionMatrixInverse.copy(xrCamera.projectionMatrix).invert();
|
xrCamera.projectionMatrixInverse.copy(xrCamera.projectionMatrix).invert();
|
||||||
@@ -18866,6 +18943,9 @@ class ARRenderer extends EventDispatcher {
|
|||||||
* until a ceiling hit arrives (no premature floor placement).
|
* until a ceiling hit arrives (no premature floor placement).
|
||||||
*/
|
*/
|
||||||
moveToAnchor(frame) {
|
moveToAnchor(frame) {
|
||||||
|
if (this.parsedAnchorOffset != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Handle deferred initial placement for ceiling mode
|
// Handle deferred initial placement for ceiling mode
|
||||||
if (this.placeOnCeiling &&
|
if (this.placeOnCeiling &&
|
||||||
this.xrMode === XRMode.WORLD_SPACE &&
|
this.xrMode === XRMode.WORLD_SPACE &&
|
||||||
@@ -28491,6 +28571,7 @@ const ARMixin = (ModelViewerElement) => {
|
|||||||
this.arScale = 'auto';
|
this.arScale = 'auto';
|
||||||
this.arUsdzMaxTextureSize = 'auto';
|
this.arUsdzMaxTextureSize = 'auto';
|
||||||
this.arPlacement = 'floor';
|
this.arPlacement = 'floor';
|
||||||
|
this.arAnchor = null;
|
||||||
this.arModes = DEFAULT_AR_MODES;
|
this.arModes = DEFAULT_AR_MODES;
|
||||||
this.iosSrc = null;
|
this.iosSrc = null;
|
||||||
this.xrEnvironment = false;
|
this.xrEnvironment = false;
|
||||||
@@ -28554,6 +28635,9 @@ const ARMixin = (ModelViewerElement) => {
|
|||||||
this[$scene].updateShadow();
|
this[$scene].updateShadow();
|
||||||
this[$needsRender]();
|
this[$needsRender]();
|
||||||
}
|
}
|
||||||
|
if (changedProperties.has('arAnchor') && this[$renderer].arRenderer.isPresenting) {
|
||||||
|
this[$renderer].arRenderer.updateAnchor(this.arAnchor);
|
||||||
|
}
|
||||||
if (changedProperties.has('arModes')) {
|
if (changedProperties.has('arModes')) {
|
||||||
this[$arModes] = deserializeARModes(this.arModes);
|
this[$arModes] = deserializeARModes(this.arModes);
|
||||||
}
|
}
|
||||||
@@ -28563,6 +28647,14 @@ const ARMixin = (ModelViewerElement) => {
|
|||||||
this[$selectARMode]();
|
this[$selectARMode]();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
getAnchor() {
|
||||||
|
const arRenderer = this[$renderer].arRenderer;
|
||||||
|
if (arRenderer.isPresenting && arRenderer.isObjectPlaced) {
|
||||||
|
const position = arRenderer.currentGoalPosition;
|
||||||
|
return `${position.x} ${position.y} ${position.z}`;
|
||||||
|
}
|
||||||
|
return 'Model not placed in AR yet.';
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Activates AR. Note that for any mode that is not WebXR-based, this
|
* Activates AR. Note that for any mode that is not WebXR-based, this
|
||||||
* method most likely has to be called synchronous from a user
|
* method most likely has to be called synchronous from a user
|
||||||
@@ -28649,6 +28741,7 @@ configuration or device capabilities');
|
|||||||
else {
|
else {
|
||||||
arRenderer.placementMode = 'floor';
|
arRenderer.placementMode = 'floor';
|
||||||
}
|
}
|
||||||
|
arRenderer.anchorOffset = this.arAnchor;
|
||||||
await arRenderer.present(this[$scene], this.xrEnvironment);
|
await arRenderer.present(this[$scene], this.xrEnvironment);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -28822,6 +28915,9 @@ configuration or device capabilities');
|
|||||||
__decorate$2([
|
__decorate$2([
|
||||||
property({ type: String, attribute: 'ar-placement' })
|
property({ type: String, attribute: 'ar-placement' })
|
||||||
], ARModelViewerElement.prototype, "arPlacement", void 0);
|
], ARModelViewerElement.prototype, "arPlacement", void 0);
|
||||||
|
__decorate$2([
|
||||||
|
property({ type: String, attribute: 'ar-anchor' })
|
||||||
|
], ARModelViewerElement.prototype, "arAnchor", void 0);
|
||||||
__decorate$2([
|
__decorate$2([
|
||||||
property({ type: String, attribute: 'ar-modes' })
|
property({ type: String, attribute: 'ar-modes' })
|
||||||
], ARModelViewerElement.prototype, "arModes", void 0);
|
], ARModelViewerElement.prototype, "arModes", void 0);
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"model-viewer-module.js","sources":["../../../node_modules/@lit/reactive-element/development/css-tag.js","../../../node_modules/@lit/reactive-element/development/reactive-element.js","../../../node_modules/@lit/reactive-element/development/decorators/property.js","../../../node_modules/@lit/reactive-element/development/decorators/query.js","../../../node_modules/lit-html/development/lit-html.js","../../../node_modules/lit-element/development/lit-element.js","../lib/constants.js","../lib/utilities.js","../lib/features/environment.js","../lib/assets/close-material-svg.js","../lib/assets/controls-svg.js","../lib/assets/view-in-ar-material-svg.js","../lib/template.js","../../../node_modules/three/examples/jsm/libs/meshopt_decoder.module.js","../../../node_modules/three/examples/jsm/loaders/DRACOLoader.js","../../../node_modules/three/examples/jsm/utils/BufferGeometryUtils.js","../../../node_modules/three/examples/jsm/loaders/GLTFLoader.js","../../../node_modules/three/examples/jsm/utils/WorkerPool.js","../../../node_modules/three/examples/jsm/libs/ktx-parse.module.js","../../../node_modules/three/examples/jsm/libs/zstddec.module.js","../../../node_modules/three/examples/jsm/math/ColorSpaces.js","../../../node_modules/three/examples/jsm/loaders/KTX2Loader.js","../lib/utilities/cache-eviction-policy.js","../lib/three-components/gltf-instance/VariantMaterialLoaderPlugin.js","../lib/three-components/CachingGLTFLoader.js","../../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js","../../../node_modules/three/examples/jsm/utils/SceneUtils.js","../../../node_modules/three/examples/jsm/exporters/GLTFExporter.js","../lib/three-components/gltf-instance/VariantMaterialExporterPlugin.js","../../../node_modules/three/examples/jsm/webxr/XREstimatedLight.js","../lib/three-components/Damper.js","../lib/three-components/PlacementBox.js","../lib/styles/parsers.js","../lib/styles/conversions.js","../lib/styles/evaluators.js","../lib/styles/style-effector.js","../lib/decorators.js","../lib/utilities/animation.js","../lib/features/controls.js","../lib/three-components/SmoothControls.js","../lib/three-components/XRMenuPanel.js","../lib/three-components/ARRenderer.js","../../../node_modules/three/examples/jsm/utils/SkeletonUtils.js","../lib/three-components/GLTFInstance.js","../lib/three-components/gltf-instance/correlated-scene-graph.js","../lib/three-components/gltf-instance/ModelViewerGLTFInstance.js","../../../node_modules/@monogrid/gainmap-js/dist/QuadRenderer-DuOPRGA4.js","../../../node_modules/@monogrid/gainmap-js/dist/decode.js","../../../node_modules/three/examples/jsm/loaders/RGBELoader.js","../lib/three-components/EnvironmentScene.js","../lib/three-components/TextureUtils.js","../lib/three-components/Renderer.js","../lib/features/scene-graph/three-dom-element.js","../lib/features/scene-graph/image.js","../lib/three-components/gltf-instance/gltf-2.0.js","../lib/features/scene-graph/sampler.js","../lib/features/scene-graph/texture.js","../lib/features/scene-graph/texture-info.js","../lib/features/scene-graph/pbr-metallic-roughness.js","../lib/features/scene-graph/material.js","../lib/features/scene-graph/nodes/primitive-node.js","../lib/features/scene-graph/model.js","../lib/features/scene-graph.js","../lib/three-components/GroundedSkybox.js","../lib/three-components/Hotspot.js","../../../node_modules/three/examples/jsm/shaders/HorizontalBlurShader.js","../../../node_modules/three/examples/jsm/shaders/VerticalBlurShader.js","../../../node_modules/three/src/math/MathUtils.js","../lib/three-components/Shadow.js","../lib/three-components/ModelScene.js","../lib/utilities/progress-tracker.js","../lib/model-viewer-base.js","../lib/features/animation.js","../lib/features/annotation.js","../../../node_modules/three/examples/jsm/libs/fflate.module.js","../../../node_modules/three/examples/jsm/exporters/USDZExporter.js","../lib/styles/deserializers.js","../lib/features/ar.js","../lib/features/loading.js","../lib/features/staging.js","../lib/model-viewer.js"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst NODE_MODE = false;\n// Allows minifiers to rename references to globalThis\nconst global = globalThis;\n/**\n * Whether the current browser supports `adoptedStyleSheets`.\n */\nexport const supportsAdoptingStyleSheets = global.ShadowRoot &&\n (global.ShadyCSS === undefined || global.ShadyCSS.nativeShadow) &&\n 'adoptedStyleSheets' in Document.prototype &&\n 'replace' in CSSStyleSheet.prototype;\nconst constructionToken = Symbol();\nconst cssTagCache = new WeakMap();\n/**\n * A container for a string of CSS text, that may be used to create a CSSStyleSheet.\n *\n * CSSResult is the return value of `css`-tagged template literals and\n * `unsafeCSS()`. In order to ensure that CSSResults are only created via the\n * `css` tag and `unsafeCSS()`, CSSResult cannot be constructed directly.\n */\nexport class CSSResult {\n constrLine truncated
|
{"version":3,"file":"model-viewer-module.js","sources":["../../../node_modules/@lit/reactive-element/development/css-tag.js","../../../node_modules/@lit/reactive-element/development/reactive-element.js","../../../node_modules/@lit/reactive-element/development/decorators/property.js","../../../node_modules/@lit/reactive-element/development/decorators/query.js","../../../node_modules/lit-html/development/lit-html.js","../../../node_modules/lit-element/development/lit-element.js","../lib/constants.js","../lib/utilities.js","../lib/features/environment.js","../lib/assets/close-material-svg.js","../lib/assets/controls-svg.js","../lib/assets/view-in-ar-material-svg.js","../lib/template.js","../../../node_modules/three/examples/jsm/libs/meshopt_decoder.module.js","../../../node_modules/three/examples/jsm/loaders/DRACOLoader.js","../../../node_modules/three/examples/jsm/utils/BufferGeometryUtils.js","../../../node_modules/three/examples/jsm/loaders/GLTFLoader.js","../../../node_modules/three/examples/jsm/utils/WorkerPool.js","../../../node_modules/three/examples/jsm/libs/ktx-parse.module.js","../../../node_modules/three/examples/jsm/libs/zstddec.module.js","../../../node_modules/three/examples/jsm/math/ColorSpaces.js","../../../node_modules/three/examples/jsm/loaders/KTX2Loader.js","../lib/utilities/cache-eviction-policy.js","../lib/three-components/gltf-instance/VariantMaterialLoaderPlugin.js","../lib/three-components/CachingGLTFLoader.js","../../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js","../../../node_modules/three/examples/jsm/utils/SceneUtils.js","../../../node_modules/three/examples/jsm/exporters/GLTFExporter.js","../lib/three-components/gltf-instance/VariantMaterialExporterPlugin.js","../../../node_modules/three/examples/jsm/webxr/XREstimatedLight.js","../lib/three-components/Damper.js","../lib/three-components/PlacementBox.js","../lib/styles/parsers.js","../lib/styles/conversions.js","../lib/styles/evaluators.js","../lib/styles/style-effector.js","../lib/decorators.js","../lib/utilities/animation.js","../lib/features/controls.js","../lib/three-components/SmoothControls.js","../lib/three-components/XRMenuPanel.js","../lib/three-components/ARRenderer.js","../../../node_modules/three/examples/jsm/utils/SkeletonUtils.js","../lib/three-components/GLTFInstance.js","../lib/three-components/gltf-instance/correlated-scene-graph.js","../lib/three-components/gltf-instance/ModelViewerGLTFInstance.js","../../../node_modules/@monogrid/gainmap-js/dist/QuadRenderer-DuOPRGA4.js","../../../node_modules/@monogrid/gainmap-js/dist/decode.js","../../../node_modules/three/examples/jsm/loaders/RGBELoader.js","../lib/three-components/EnvironmentScene.js","../lib/three-components/TextureUtils.js","../lib/three-components/Renderer.js","../lib/features/scene-graph/three-dom-element.js","../lib/features/scene-graph/image.js","../lib/three-components/gltf-instance/gltf-2.0.js","../lib/features/scene-graph/sampler.js","../lib/features/scene-graph/texture.js","../lib/features/scene-graph/texture-info.js","../lib/features/scene-graph/pbr-metallic-roughness.js","../lib/features/scene-graph/material.js","../lib/features/scene-graph/nodes/primitive-node.js","../lib/features/scene-graph/model.js","../lib/features/scene-graph.js","../lib/three-components/GroundedSkybox.js","../lib/three-components/Hotspot.js","../../../node_modules/three/examples/jsm/shaders/HorizontalBlurShader.js","../../../node_modules/three/examples/jsm/shaders/VerticalBlurShader.js","../../../node_modules/three/src/math/MathUtils.js","../lib/three-components/Shadow.js","../lib/three-components/ModelScene.js","../lib/utilities/progress-tracker.js","../lib/model-viewer-base.js","../lib/features/animation.js","../lib/features/annotation.js","../../../node_modules/three/examples/jsm/libs/fflate.module.js","../../../node_modules/three/examples/jsm/exporters/USDZExporter.js","../lib/styles/deserializers.js","../lib/features/ar.js","../lib/features/loading.js","../lib/features/staging.js","../lib/model-viewer.js"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst NODE_MODE = false;\n// Allows minifiers to rename references to globalThis\nconst global = globalThis;\n/**\n * Whether the current browser supports `adoptedStyleSheets`.\n */\nexport const supportsAdoptingStyleSheets = global.ShadowRoot &&\n (global.ShadyCSS === undefined || global.ShadyCSS.nativeShadow) &&\n 'adoptedStyleSheets' in Document.prototype &&\n 'replace' in CSSStyleSheet.prototype;\nconst constructionToken = Symbol();\nconst cssTagCache = new WeakMap();\n/**\n * A container for a string of CSS text, that may be used to create a CSSStyleSheet.\n *\n * CSSResult is the return value of `css`-tagged template literals and\n * `unsafeCSS()`. In order to ensure that CSSResults are only created via the\n * `css` tag and `unsafeCSS()`, CSSResult cannot be constructed directly.\n */\nexport class CSSResult {\n constrLine truncated
|
||||||
+96
@@ -69779,6 +69779,7 @@ class ARRenderer extends EventDispatcher {
|
|||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.currentSession = null;
|
this.currentSession = null;
|
||||||
this.placementMode = 'floor';
|
this.placementMode = 'floor';
|
||||||
|
this.anchorOffset = null;
|
||||||
this.placementBox = null;
|
this.placementBox = null;
|
||||||
this.menuPanel = null;
|
this.menuPanel = null;
|
||||||
this.lastTick = null;
|
this.lastTick = null;
|
||||||
@@ -69797,6 +69798,7 @@ class ARRenderer extends EventDispatcher {
|
|||||||
this.xrController1 = null;
|
this.xrController1 = null;
|
||||||
this.xrController2 = null;
|
this.xrController2 = null;
|
||||||
this.selectedXRController = null;
|
this.selectedXRController = null;
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
this.tracking = true;
|
this.tracking = true;
|
||||||
this.frames = 0;
|
this.frames = 0;
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
@@ -70009,6 +70011,35 @@ class ARRenderer extends EventDispatcher {
|
|||||||
get presentedScene() {
|
get presentedScene() {
|
||||||
return this._presentedScene;
|
return this._presentedScene;
|
||||||
}
|
}
|
||||||
|
get currentGoalPosition() {
|
||||||
|
return this.goalPosition;
|
||||||
|
}
|
||||||
|
get isObjectPlaced() {
|
||||||
|
return this.placementComplete;
|
||||||
|
}
|
||||||
|
updateAnchor(newAnchor) {
|
||||||
|
var _a;
|
||||||
|
if (newAnchor) {
|
||||||
|
const parts = newAnchor.split(' ').map(Number);
|
||||||
|
if (parts.length === 3 && parts.every(p => !isNaN(p))) {
|
||||||
|
const newPosition = new Vector3(parts[0], parts[1], parts[2]);
|
||||||
|
this.goalPosition.copy(newPosition);
|
||||||
|
this.parsedAnchorOffset = newPosition;
|
||||||
|
if (!this.placementComplete) {
|
||||||
|
this.placementComplete = true;
|
||||||
|
this.worldSpaceInitialPlacementDone = true;
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
(_a = this.presentedScene) === null || _a === void 0 ? void 0 : _a.setShadowIntensity(AR_SHADOW_INTENSITY);
|
||||||
|
this.dispatchEvent({ type: 'status', status: ARStatus.OBJECT_PLACED });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`Invalid dynamic ar-anchor value: "${newAnchor}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Resolves to true if the renderer has detected all the necessary qualities
|
* Resolves to true if the renderer has detected all the necessary qualities
|
||||||
* to support presentation in AR.
|
* to support presentation in AR.
|
||||||
@@ -70032,6 +70063,16 @@ class ARRenderer extends EventDispatcher {
|
|||||||
if (this.isPresenting) {
|
if (this.isPresenting) {
|
||||||
console.warn('Cannot present while a model is already presenting');
|
console.warn('Cannot present while a model is already presenting');
|
||||||
}
|
}
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
|
if (this.anchorOffset) {
|
||||||
|
const parts = this.anchorOffset.split(' ').map(Number);
|
||||||
|
if (parts.length === 3 && parts.every(p => !isNaN(p))) {
|
||||||
|
this.parsedAnchorOffset = new Vector3(parts[0], parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`Invalid ar-anchor value: "${this.anchorOffset}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
let waitForAnimationFrame = new Promise((resolve, _reject) => {
|
let waitForAnimationFrame = new Promise((resolve, _reject) => {
|
||||||
requestAnimationFrame(() => resolve());
|
requestAnimationFrame(() => resolve());
|
||||||
});
|
});
|
||||||
@@ -70305,6 +70346,8 @@ class ARRenderer extends EventDispatcher {
|
|||||||
this.inputSource = null;
|
this.inputSource = null;
|
||||||
this.overlay = null;
|
this.overlay = null;
|
||||||
this.worldSpaceInitialPlacementDone = false;
|
this.worldSpaceInitialPlacementDone = false;
|
||||||
|
this.anchorOffset = null;
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
if (this.resolveCleanup != null) {
|
if (this.resolveCleanup != null) {
|
||||||
this.resolveCleanup();
|
this.resolveCleanup();
|
||||||
}
|
}
|
||||||
@@ -70339,6 +70382,40 @@ class ARRenderer extends EventDispatcher {
|
|||||||
const { pivot, element } = scene;
|
const { pivot, element } = scene;
|
||||||
const { position } = pivot;
|
const { position } = pivot;
|
||||||
const xrCamera = scene.getCamera();
|
const xrCamera = scene.getCamera();
|
||||||
|
if (this.parsedAnchorOffset != null) {
|
||||||
|
// Set position directly from the provided anchor offset.
|
||||||
|
// This position is relative to the initial XR reference space origin.
|
||||||
|
position.copy(this.parsedAnchorOffset);
|
||||||
|
this.goalPosition.copy(this.parsedAnchorOffset);
|
||||||
|
// Set up the scene for presentation
|
||||||
|
scene.setHotspotsVisibility(true);
|
||||||
|
scene.visible = true;
|
||||||
|
scene.setShadowIntensity(AR_SHADOW_INTENSITY);
|
||||||
|
// Mark placement as complete to bypass further automatic placement.
|
||||||
|
this.placementComplete = true;
|
||||||
|
this.worldSpaceInitialPlacementDone = true;
|
||||||
|
// Hide the placement box as it's not needed.
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
this.dispatchEvent({ type: 'status', status: ARStatus.OBJECT_PLACED });
|
||||||
|
// Enable user interaction controls for the appropriate mode.
|
||||||
|
if (this.xrMode === XRMode.SCREEN_SPACE) {
|
||||||
|
const { session } = this.frame;
|
||||||
|
session.addEventListener('selectstart', this.onSelectStart);
|
||||||
|
session.addEventListener('selectend', this.onSelectEnd);
|
||||||
|
session.requestHitTestSourceForTransientInput({ profile: 'generic-touchscreen' })
|
||||||
|
.then(hitTestSource => { this.transientHitTestSource = hitTestSource; });
|
||||||
|
}
|
||||||
|
else { // WORLD_SPACE
|
||||||
|
this.enableWorldSpaceUserInteraction();
|
||||||
|
// Hide the placement box again as it's not needed for manual anchoring.
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // Skip the rest of the automatic placement logic.
|
||||||
|
}
|
||||||
const { width, height } = this.overlay.getBoundingClientRect();
|
const { width, height } = this.overlay.getBoundingClientRect();
|
||||||
scene.setSize(width, height);
|
scene.setSize(width, height);
|
||||||
xrCamera.projectionMatrixInverse.copy(xrCamera.projectionMatrix).invert();
|
xrCamera.projectionMatrixInverse.copy(xrCamera.projectionMatrix).invert();
|
||||||
@@ -70491,6 +70568,9 @@ class ARRenderer extends EventDispatcher {
|
|||||||
* until a ceiling hit arrives (no premature floor placement).
|
* until a ceiling hit arrives (no premature floor placement).
|
||||||
*/
|
*/
|
||||||
moveToAnchor(frame) {
|
moveToAnchor(frame) {
|
||||||
|
if (this.parsedAnchorOffset != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Handle deferred initial placement for ceiling mode
|
// Handle deferred initial placement for ceiling mode
|
||||||
if (this.placeOnCeiling &&
|
if (this.placeOnCeiling &&
|
||||||
this.xrMode === XRMode.WORLD_SPACE &&
|
this.xrMode === XRMode.WORLD_SPACE &&
|
||||||
@@ -80116,6 +80196,7 @@ const ARMixin = (ModelViewerElement) => {
|
|||||||
this.arScale = 'auto';
|
this.arScale = 'auto';
|
||||||
this.arUsdzMaxTextureSize = 'auto';
|
this.arUsdzMaxTextureSize = 'auto';
|
||||||
this.arPlacement = 'floor';
|
this.arPlacement = 'floor';
|
||||||
|
this.arAnchor = null;
|
||||||
this.arModes = DEFAULT_AR_MODES;
|
this.arModes = DEFAULT_AR_MODES;
|
||||||
this.iosSrc = null;
|
this.iosSrc = null;
|
||||||
this.xrEnvironment = false;
|
this.xrEnvironment = false;
|
||||||
@@ -80179,6 +80260,9 @@ const ARMixin = (ModelViewerElement) => {
|
|||||||
this[$scene].updateShadow();
|
this[$scene].updateShadow();
|
||||||
this[$needsRender]();
|
this[$needsRender]();
|
||||||
}
|
}
|
||||||
|
if (changedProperties.has('arAnchor') && this[$renderer].arRenderer.isPresenting) {
|
||||||
|
this[$renderer].arRenderer.updateAnchor(this.arAnchor);
|
||||||
|
}
|
||||||
if (changedProperties.has('arModes')) {
|
if (changedProperties.has('arModes')) {
|
||||||
this[$arModes] = deserializeARModes(this.arModes);
|
this[$arModes] = deserializeARModes(this.arModes);
|
||||||
}
|
}
|
||||||
@@ -80188,6 +80272,14 @@ const ARMixin = (ModelViewerElement) => {
|
|||||||
this[$selectARMode]();
|
this[$selectARMode]();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
getAnchor() {
|
||||||
|
const arRenderer = this[$renderer].arRenderer;
|
||||||
|
if (arRenderer.isPresenting && arRenderer.isObjectPlaced) {
|
||||||
|
const position = arRenderer.currentGoalPosition;
|
||||||
|
return `${position.x} ${position.y} ${position.z}`;
|
||||||
|
}
|
||||||
|
return 'Model not placed in AR yet.';
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Activates AR. Note that for any mode that is not WebXR-based, this
|
* Activates AR. Note that for any mode that is not WebXR-based, this
|
||||||
* method most likely has to be called synchronous from a user
|
* method most likely has to be called synchronous from a user
|
||||||
@@ -80274,6 +80366,7 @@ configuration or device capabilities');
|
|||||||
else {
|
else {
|
||||||
arRenderer.placementMode = 'floor';
|
arRenderer.placementMode = 'floor';
|
||||||
}
|
}
|
||||||
|
arRenderer.anchorOffset = this.arAnchor;
|
||||||
await arRenderer.present(this[$scene], this.xrEnvironment);
|
await arRenderer.present(this[$scene], this.xrEnvironment);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
@@ -80447,6 +80540,9 @@ configuration or device capabilities');
|
|||||||
__decorate$2([
|
__decorate$2([
|
||||||
property({ type: String, attribute: 'ar-placement' })
|
property({ type: String, attribute: 'ar-placement' })
|
||||||
], ARModelViewerElement.prototype, "arPlacement", void 0);
|
], ARModelViewerElement.prototype, "arPlacement", void 0);
|
||||||
|
__decorate$2([
|
||||||
|
property({ type: String, attribute: 'ar-anchor' })
|
||||||
|
], ARModelViewerElement.prototype, "arAnchor", void 0);
|
||||||
__decorate$2([
|
__decorate$2([
|
||||||
property({ type: String, attribute: 'ar-modes' })
|
property({ type: String, attribute: 'ar-modes' })
|
||||||
], ARModelViewerElement.prototype, "arModes", void 0);
|
], ARModelViewerElement.prototype, "arModes", void 0);
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"model-viewer.js","sources":["../../../node_modules/@lit/reactive-element/development/css-tag.js","../../../node_modules/@lit/reactive-element/development/reactive-element.js","../../../node_modules/@lit/reactive-element/development/decorators/property.js","../../../node_modules/@lit/reactive-element/development/decorators/query.js","../../../node_modules/three/build/three.core.js","../../../node_modules/three/build/three.module.js","../../../node_modules/lit-html/development/lit-html.js","../../../node_modules/lit-element/development/lit-element.js","../lib/constants.js","../lib/utilities.js","../lib/features/environment.js","../lib/assets/close-material-svg.js","../lib/assets/controls-svg.js","../lib/assets/view-in-ar-material-svg.js","../lib/template.js","../../../node_modules/three/examples/jsm/libs/meshopt_decoder.module.js","../../../node_modules/three/examples/jsm/loaders/DRACOLoader.js","../../../node_modules/three/examples/jsm/utils/BufferGeometryUtils.js","../../../node_modules/three/examples/jsm/loaders/GLTFLoader.js","../../../node_modules/three/examples/jsm/utils/WorkerPool.js","../../../node_modules/three/examples/jsm/libs/ktx-parse.module.js","../../../node_modules/three/examples/jsm/libs/zstddec.module.js","../../../node_modules/three/examples/jsm/math/ColorSpaces.js","../../../node_modules/three/examples/jsm/loaders/KTX2Loader.js","../lib/utilities/cache-eviction-policy.js","../lib/three-components/gltf-instance/VariantMaterialLoaderPlugin.js","../lib/three-components/CachingGLTFLoader.js","../../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js","../../../node_modules/three/examples/jsm/utils/SceneUtils.js","../../../node_modules/three/examples/jsm/exporters/GLTFExporter.js","../lib/three-components/gltf-instance/VariantMaterialExporterPlugin.js","../../../node_modules/three/examples/jsm/webxr/XREstimatedLight.js","../lib/three-components/Damper.js","../lib/three-components/PlacementBox.js","../lib/styles/parsers.js","../lib/styles/conversions.js","../lib/styles/evaluators.js","../lib/styles/style-effector.js","../lib/decorators.js","../lib/utilities/animation.js","../lib/features/controls.js","../lib/three-components/SmoothControls.js","../lib/three-components/XRMenuPanel.js","../lib/three-components/ARRenderer.js","../../../node_modules/three/examples/jsm/utils/SkeletonUtils.js","../lib/three-components/GLTFInstance.js","../lib/three-components/gltf-instance/correlated-scene-graph.js","../lib/three-components/gltf-instance/ModelViewerGLTFInstance.js","../../../node_modules/@monogrid/gainmap-js/dist/QuadRenderer-DuOPRGA4.js","../../../node_modules/@monogrid/gainmap-js/dist/decode.js","../../../node_modules/three/examples/jsm/loaders/RGBELoader.js","../lib/three-components/EnvironmentScene.js","../lib/three-components/TextureUtils.js","../lib/three-components/Renderer.js","../lib/features/scene-graph/three-dom-element.js","../lib/features/scene-graph/image.js","../lib/three-components/gltf-instance/gltf-2.0.js","../lib/features/scene-graph/sampler.js","../lib/features/scene-graph/texture.js","../lib/features/scene-graph/texture-info.js","../lib/features/scene-graph/pbr-metallic-roughness.js","../lib/features/scene-graph/material.js","../lib/features/scene-graph/nodes/primitive-node.js","../lib/features/scene-graph/model.js","../lib/features/scene-graph.js","../lib/three-components/GroundedSkybox.js","../lib/three-components/Hotspot.js","../../../node_modules/three/examples/jsm/shaders/HorizontalBlurShader.js","../../../node_modules/three/examples/jsm/shaders/VerticalBlurShader.js","../../../node_modules/three/src/math/MathUtils.js","../lib/three-components/Shadow.js","../lib/three-components/ModelScene.js","../lib/utilities/progress-tracker.js","../lib/model-viewer-base.js","../lib/features/animation.js","../lib/features/annotation.js","../../../node_modules/three/examples/jsm/libs/fflate.module.js","../../../node_modules/three/examples/jsm/exporters/USDZExporter.js","../lib/styles/deserializers.js","../lib/features/ar.js","../lib/features/loading.js","../lib/features/staging.js","../lib/model-viewer.js"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst NODE_MODE = false;\n// Allows minifiers to rename references to globalThis\nconst global = globalThis;\n/**\n * Whether the current browser supports `adoptedStyleSheets`.\n */\nexport const supportsAdoptingStyleSheets = global.ShadowRoot &&\n (global.ShadyCSS === undefined || global.ShadyCSS.nativeShadow) &&\n 'adoptedStyleSheets' in Document.prototype &&\n 'replace' in CSSStyleSheet.prototype;\nconst constructionToken = Symbol();\nconst cssTagCache = new WeakMap();\n/**\n * A container for a string of CSS text, that may be used to create a CSSStyleSheet.\n *\n * CSSResult is the return value of `css`-tagged template literals and\n * `unsafeCSS()`. In order to ensure that CSSResults are only created via the\n * `css` tag and `unsLine truncated
|
{"version":3,"file":"model-viewer.js","sources":["../../../node_modules/@lit/reactive-element/development/css-tag.js","../../../node_modules/@lit/reactive-element/development/reactive-element.js","../../../node_modules/@lit/reactive-element/development/decorators/property.js","../../../node_modules/@lit/reactive-element/development/decorators/query.js","../../../node_modules/three/build/three.core.js","../../../node_modules/three/build/three.module.js","../../../node_modules/lit-html/development/lit-html.js","../../../node_modules/lit-element/development/lit-element.js","../lib/constants.js","../lib/utilities.js","../lib/features/environment.js","../lib/assets/close-material-svg.js","../lib/assets/controls-svg.js","../lib/assets/view-in-ar-material-svg.js","../lib/template.js","../../../node_modules/three/examples/jsm/libs/meshopt_decoder.module.js","../../../node_modules/three/examples/jsm/loaders/DRACOLoader.js","../../../node_modules/three/examples/jsm/utils/BufferGeometryUtils.js","../../../node_modules/three/examples/jsm/loaders/GLTFLoader.js","../../../node_modules/three/examples/jsm/utils/WorkerPool.js","../../../node_modules/three/examples/jsm/libs/ktx-parse.module.js","../../../node_modules/three/examples/jsm/libs/zstddec.module.js","../../../node_modules/three/examples/jsm/math/ColorSpaces.js","../../../node_modules/three/examples/jsm/loaders/KTX2Loader.js","../lib/utilities/cache-eviction-policy.js","../lib/three-components/gltf-instance/VariantMaterialLoaderPlugin.js","../lib/three-components/CachingGLTFLoader.js","../../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js","../../../node_modules/three/examples/jsm/utils/SceneUtils.js","../../../node_modules/three/examples/jsm/exporters/GLTFExporter.js","../lib/three-components/gltf-instance/VariantMaterialExporterPlugin.js","../../../node_modules/three/examples/jsm/webxr/XREstimatedLight.js","../lib/three-components/Damper.js","../lib/three-components/PlacementBox.js","../lib/styles/parsers.js","../lib/styles/conversions.js","../lib/styles/evaluators.js","../lib/styles/style-effector.js","../lib/decorators.js","../lib/utilities/animation.js","../lib/features/controls.js","../lib/three-components/SmoothControls.js","../lib/three-components/XRMenuPanel.js","../lib/three-components/ARRenderer.js","../../../node_modules/three/examples/jsm/utils/SkeletonUtils.js","../lib/three-components/GLTFInstance.js","../lib/three-components/gltf-instance/correlated-scene-graph.js","../lib/three-components/gltf-instance/ModelViewerGLTFInstance.js","../../../node_modules/@monogrid/gainmap-js/dist/QuadRenderer-DuOPRGA4.js","../../../node_modules/@monogrid/gainmap-js/dist/decode.js","../../../node_modules/three/examples/jsm/loaders/RGBELoader.js","../lib/three-components/EnvironmentScene.js","../lib/three-components/TextureUtils.js","../lib/three-components/Renderer.js","../lib/features/scene-graph/three-dom-element.js","../lib/features/scene-graph/image.js","../lib/three-components/gltf-instance/gltf-2.0.js","../lib/features/scene-graph/sampler.js","../lib/features/scene-graph/texture.js","../lib/features/scene-graph/texture-info.js","../lib/features/scene-graph/pbr-metallic-roughness.js","../lib/features/scene-graph/material.js","../lib/features/scene-graph/nodes/primitive-node.js","../lib/features/scene-graph/model.js","../lib/features/scene-graph.js","../lib/three-components/GroundedSkybox.js","../lib/three-components/Hotspot.js","../../../node_modules/three/examples/jsm/shaders/HorizontalBlurShader.js","../../../node_modules/three/examples/jsm/shaders/VerticalBlurShader.js","../../../node_modules/three/src/math/MathUtils.js","../lib/three-components/Shadow.js","../lib/three-components/ModelScene.js","../lib/utilities/progress-tracker.js","../lib/model-viewer-base.js","../lib/features/animation.js","../lib/features/annotation.js","../../../node_modules/three/examples/jsm/libs/fflate.module.js","../../../node_modules/three/examples/jsm/exporters/USDZExporter.js","../lib/styles/deserializers.js","../lib/features/ar.js","../lib/features/loading.js","../lib/features/staging.js","../lib/model-viewer.js"],"sourcesContent":["/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\nconst NODE_MODE = false;\n// Allows minifiers to rename references to globalThis\nconst global = globalThis;\n/**\n * Whether the current browser supports `adoptedStyleSheets`.\n */\nexport const supportsAdoptingStyleSheets = global.ShadowRoot &&\n (global.ShadyCSS === undefined || global.ShadyCSS.nativeShadow) &&\n 'adoptedStyleSheets' in Document.prototype &&\n 'replace' in CSSStyleSheet.prototype;\nconst constructionToken = Symbol();\nconst cssTagCache = new WeakMap();\n/**\n * A container for a string of CSS text, that may be used to create a CSSStyleSheet.\n *\n * CSSResult is the return value of `css`-tagged template literals and\n * `unsafeCSS()`. In order to ensure that CSSResults are only created via the\n * `css` tag and `unsLine truncated
|
||||||
@@ -70,11 +70,13 @@ export declare interface ARInterface {
|
|||||||
arModes: string;
|
arModes: string;
|
||||||
arScale: string;
|
arScale: string;
|
||||||
arPlacement: 'floor'|'wall'|'ceiling';
|
arPlacement: 'floor'|'wall'|'ceiling';
|
||||||
|
arAnchor: string|null;
|
||||||
iosSrc: string|null;
|
iosSrc: string|null;
|
||||||
xrEnvironment: boolean;
|
xrEnvironment: boolean;
|
||||||
arUsdzMaxTextureSize: string;
|
arUsdzMaxTextureSize: string;
|
||||||
readonly canActivateAR: boolean;
|
readonly canActivateAR: boolean;
|
||||||
activateAR(): Promise<void>;
|
activateAR(): Promise<void>;
|
||||||
|
getAnchor(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ARMixin = <T extends Constructor<ModelViewerElementBase>>(
|
export const ARMixin = <T extends Constructor<ModelViewerElementBase>>(
|
||||||
@@ -90,6 +92,9 @@ export const ARMixin = <T extends Constructor<ModelViewerElementBase>>(
|
|||||||
@property({type: String, attribute: 'ar-placement'})
|
@property({type: String, attribute: 'ar-placement'})
|
||||||
arPlacement: 'floor'|'wall'|'ceiling' = 'floor';
|
arPlacement: 'floor'|'wall'|'ceiling' = 'floor';
|
||||||
|
|
||||||
|
@property({type: String, attribute: 'ar-anchor'})
|
||||||
|
arAnchor: string|null = null;
|
||||||
|
|
||||||
@property({type: String, attribute: 'ar-modes'})
|
@property({type: String, attribute: 'ar-modes'})
|
||||||
arModes: string = DEFAULT_AR_MODES;
|
arModes: string = DEFAULT_AR_MODES;
|
||||||
|
|
||||||
@@ -181,6 +186,10 @@ export const ARMixin = <T extends Constructor<ModelViewerElementBase>>(
|
|||||||
this[$needsRender]();
|
this[$needsRender]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has('arAnchor') && this[$renderer].arRenderer.isPresenting) {
|
||||||
|
this[$renderer].arRenderer.updateAnchor(this.arAnchor);
|
||||||
|
}
|
||||||
|
|
||||||
if (changedProperties.has('arModes')) {
|
if (changedProperties.has('arModes')) {
|
||||||
this[$arModes] = deserializeARModes(this.arModes);
|
this[$arModes] = deserializeARModes(this.arModes);
|
||||||
}
|
}
|
||||||
@@ -192,6 +201,15 @@ export const ARMixin = <T extends Constructor<ModelViewerElementBase>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAnchor(): string {
|
||||||
|
const arRenderer = this[$renderer].arRenderer;
|
||||||
|
if (arRenderer.isPresenting && arRenderer.isObjectPlaced) {
|
||||||
|
const position = arRenderer.currentGoalPosition;
|
||||||
|
return `${position.x} ${position.y} ${position.z}`;
|
||||||
|
}
|
||||||
|
return 'Model not placed in AR yet.';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activates AR. Note that for any mode that is not WebXR-based, this
|
* Activates AR. Note that for any mode that is not WebXR-based, this
|
||||||
* method most likely has to be called synchronous from a user
|
* method most likely has to be called synchronous from a user
|
||||||
@@ -286,6 +304,7 @@ configuration or device capabilities');
|
|||||||
} else {
|
} else {
|
||||||
arRenderer.placementMode = 'floor';
|
arRenderer.placementMode = 'floor';
|
||||||
}
|
}
|
||||||
|
arRenderer.anchorOffset = this.arAnchor;
|
||||||
await arRenderer.present(this[$scene], this.xrEnvironment);
|
await arRenderer.present(this[$scene], this.xrEnvironment);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Error while trying to present in AR with WebXR');
|
console.warn('Error while trying to present in AR with WebXR');
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ export class ARRenderer extends EventDispatcher<
|
|||||||
public threeRenderer: WebGLRenderer;
|
public threeRenderer: WebGLRenderer;
|
||||||
public currentSession: XRSession|null = null;
|
public currentSession: XRSession|null = null;
|
||||||
public placementMode:'floor'|'wall'|'ceiling' = 'floor';
|
public placementMode:'floor'|'wall'|'ceiling' = 'floor';
|
||||||
|
public anchorOffset: string|null = null;
|
||||||
|
|
||||||
private placementBox: PlacementBox|null = null;
|
private placementBox: PlacementBox|null = null;
|
||||||
private menuPanel: XRMenuPanel|null = null;
|
private menuPanel: XRMenuPanel|null = null;
|
||||||
@@ -149,6 +150,7 @@ export class ARRenderer extends EventDispatcher<
|
|||||||
private xrController1: XRController|null = null;
|
private xrController1: XRController|null = null;
|
||||||
private xrController2: XRController|null = null;
|
private xrController2: XRController|null = null;
|
||||||
private selectedXRController: XRController|null = null;
|
private selectedXRController: XRController|null = null;
|
||||||
|
private parsedAnchorOffset: Vector3|null = null;
|
||||||
|
|
||||||
private tracking = true;
|
private tracking = true;
|
||||||
private frames = 0;
|
private frames = 0;
|
||||||
@@ -224,6 +226,37 @@ export class ARRenderer extends EventDispatcher<
|
|||||||
return this._presentedScene;
|
return this._presentedScene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get currentGoalPosition(): Vector3 {
|
||||||
|
return this.goalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isObjectPlaced(): boolean {
|
||||||
|
return this.placementComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateAnchor(newAnchor: string|null) {
|
||||||
|
if (newAnchor) {
|
||||||
|
const parts = newAnchor.split(' ').map(Number);
|
||||||
|
if (parts.length === 3 && parts.every(p => !isNaN(p))) {
|
||||||
|
const newPosition = new Vector3(parts[0], parts[1], parts[2]);
|
||||||
|
this.goalPosition.copy(newPosition);
|
||||||
|
this.parsedAnchorOffset = newPosition;
|
||||||
|
|
||||||
|
if (!this.placementComplete) {
|
||||||
|
this.placementComplete = true;
|
||||||
|
this.worldSpaceInitialPlacementDone = true;
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
this.presentedScene?.setShadowIntensity(AR_SHADOW_INTENSITY);
|
||||||
|
this.dispatchEvent({type: 'status', status: ARStatus.OBJECT_PLACED});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`Invalid dynamic ar-anchor value: "${newAnchor}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves to true if the renderer has detected all the necessary qualities
|
* Resolves to true if the renderer has detected all the necessary qualities
|
||||||
* to support presentation in AR.
|
* to support presentation in AR.
|
||||||
@@ -249,6 +282,16 @@ export class ARRenderer extends EventDispatcher<
|
|||||||
console.warn('Cannot present while a model is already presenting');
|
console.warn('Cannot present while a model is already presenting');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
|
if (this.anchorOffset) {
|
||||||
|
const parts = this.anchorOffset.split(' ').map(Number);
|
||||||
|
if (parts.length === 3 && parts.every(p => !isNaN(p))) {
|
||||||
|
this.parsedAnchorOffset = new Vector3(parts[0], parts[1], parts[2]);
|
||||||
|
} else {
|
||||||
|
console.warn(`Invalid ar-anchor value: "${this.anchorOffset}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let waitForAnimationFrame = new Promise<void>((resolve, _reject) => {
|
let waitForAnimationFrame = new Promise<void>((resolve, _reject) => {
|
||||||
requestAnimationFrame(() => resolve());
|
requestAnimationFrame(() => resolve());
|
||||||
});
|
});
|
||||||
@@ -711,6 +754,8 @@ export class ARRenderer extends EventDispatcher<
|
|||||||
this.inputSource = null;
|
this.inputSource = null;
|
||||||
this.overlay = null;
|
this.overlay = null;
|
||||||
this.worldSpaceInitialPlacementDone = false;
|
this.worldSpaceInitialPlacementDone = false;
|
||||||
|
this.anchorOffset = null;
|
||||||
|
this.parsedAnchorOffset = null;
|
||||||
|
|
||||||
if (this.resolveCleanup != null) {
|
if (this.resolveCleanup != null) {
|
||||||
this.resolveCleanup!();
|
this.resolveCleanup!();
|
||||||
@@ -754,6 +799,45 @@ export class ARRenderer extends EventDispatcher<
|
|||||||
const {position} = pivot;
|
const {position} = pivot;
|
||||||
const xrCamera = scene.getCamera();
|
const xrCamera = scene.getCamera();
|
||||||
|
|
||||||
|
if (this.parsedAnchorOffset != null) {
|
||||||
|
// Set position directly from the provided anchor offset.
|
||||||
|
// This position is relative to the initial XR reference space origin.
|
||||||
|
position.copy(this.parsedAnchorOffset);
|
||||||
|
this.goalPosition.copy(this.parsedAnchorOffset);
|
||||||
|
|
||||||
|
// Set up the scene for presentation
|
||||||
|
scene.setHotspotsVisibility(true);
|
||||||
|
scene.visible = true;
|
||||||
|
scene.setShadowIntensity(AR_SHADOW_INTENSITY);
|
||||||
|
|
||||||
|
// Mark placement as complete to bypass further automatic placement.
|
||||||
|
this.placementComplete = true;
|
||||||
|
this.worldSpaceInitialPlacementDone = true;
|
||||||
|
|
||||||
|
// Hide the placement box as it's not needed.
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchEvent({type: 'status', status: ARStatus.OBJECT_PLACED});
|
||||||
|
|
||||||
|
// Enable user interaction controls for the appropriate mode.
|
||||||
|
if (this.xrMode === XRMode.SCREEN_SPACE) {
|
||||||
|
const {session} = this.frame!;
|
||||||
|
session.addEventListener('selectstart', this.onSelectStart);
|
||||||
|
session.addEventListener('selectend', this.onSelectEnd);
|
||||||
|
session.requestHitTestSourceForTransientInput!({profile: 'generic-touchscreen'})!
|
||||||
|
.then(hitTestSource => { this.transientHitTestSource = hitTestSource; });
|
||||||
|
} else { // WORLD_SPACE
|
||||||
|
this.enableWorldSpaceUserInteraction();
|
||||||
|
// Hide the placement box again as it's not needed for manual anchoring.
|
||||||
|
if (this.placementBox) {
|
||||||
|
this.placementBox.show = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // Skip the rest of the automatic placement logic.
|
||||||
|
}
|
||||||
|
|
||||||
const {width, height} = this.overlay!.getBoundingClientRect();
|
const {width, height} = this.overlay!.getBoundingClientRect();
|
||||||
scene.setSize(width, height);
|
scene.setSize(width, height);
|
||||||
|
|
||||||
@@ -931,6 +1015,9 @@ export class ARRenderer extends EventDispatcher<
|
|||||||
* until a ceiling hit arrives (no premature floor placement).
|
* until a ceiling hit arrives (no premature floor placement).
|
||||||
*/
|
*/
|
||||||
public moveToAnchor(frame: XRFrame) {
|
public moveToAnchor(frame: XRFrame) {
|
||||||
|
if (this.parsedAnchorOffset != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Handle deferred initial placement for ceiling mode
|
// Handle deferred initial placement for ceiling mode
|
||||||
if (this.placeOnCeiling &&
|
if (this.placeOnCeiling &&
|
||||||
this.xrMode === XRMode.WORLD_SPACE &&
|
this.xrMode === XRMode.WORLD_SPACE &&
|
||||||
|
|||||||
Reference in new issue
Block a user