UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

167 lines 7.47 kB
import { DeviceUtilities, getParam } from "../engine_utils.js"; const debug = getParam("debugoverlay"); export const arContainerClassName = "ar"; export const quitARClassName = "quit-ar"; // https://developers.google.com/web/fundamentals/web-components/customelements /** @internal */ export class AROverlayHandler { get ARContainer() { return this.arContainer; } arContainer = null; currentSession = null; _createdAROnlyElements = []; _reparentedObjects = []; contentElement = null; originalDomOverlayParent = null; requestEndAR = () => { this.onRequestedEndAR(); }; onBegin(context, overlayContainer, session) { this.currentSession = session; this.arContainer = overlayContainer; if (DeviceUtilities.isMozillaXR()) { const arElements = context.domElement.children; for (let i = 0; i < arElements?.length; i++) { const el = arElements[i]; if (!el) return; if (el === this.arContainer) return; this._reparentedObjects.push({ el: el, previousParent: el.parentElement }); this.arContainer?.appendChild(el); } if (overlayContainer) { this.originalDomOverlayParent = overlayContainer.parentNode; if (this.originalDomOverlayParent) { console.log("Reparent DOM Overlay to body", overlayContainer, overlayContainer.style.display); // mozilla webxr does hide elements on session start // this is only necessary if we generated the overlay element overlayContainer.style.display = ""; overlayContainer.style.visibility = ""; document.body.appendChild(overlayContainer); } } else { console.warn("WebXRViewer: No DOM Overlay found"); } } this.ensureQuitARButton(this.arContainer); } onEnd(_context) { // if (this.arContainer) // this.arContainer.classList.remove("ar-session-active"); for (const created of this._createdAROnlyElements) { if (created.remove) { created.remove(); } } for (const prev of this._reparentedObjects) { const el = prev.el; prev.previousParent?.appendChild(el); } this._reparentedObjects.length = 0; // mozilla XR exit AR fixes if (DeviceUtilities.isMozillaXR()) { // without the timeout we get errors in mozillas code and can not enter XR again // not sure why we have to wait setTimeout(() => { // Canvas is not in DOM anymore after AR using Mozilla XR const canvas = _context.renderer.domElement; if (canvas) { _context.domElement.shadowRoot?.prepend(canvas); } // Fix visibility const elements = document.querySelectorAll("*"); for (var i = 0; i < elements.length; i++) { const child = elements[i]; if (child && child._displayChanged !== undefined && child._displayWas !== undefined) { child.style.display = child._displayWas; } } }, 10); } } createOverlayContainer(needleEngineElement) { if (this.contentElement) return this.contentElement; if (debug) console.log("Setup overlay container"); const contentElement = needleEngineElement.shadowRoot.querySelector(".content"); this.contentElement = contentElement; const overlaySlot = needleEngineElement.shadowRoot.querySelector(".overlay-content"); if (overlaySlot) contentElement.appendChild(overlaySlot); if (debug && !DeviceUtilities.isMobileDevice()) this.ensureQuitARButton(contentElement); return contentElement; } onRequestedEndAR() { if (!this.currentSession) return; this.currentSession.end(); this.currentSession = null; } ensureQuitARButton(element) { const quitARSlot = document.createElement("slot"); quitARSlot.setAttribute("name", "quit-ar"); this.appendElement(quitARSlot, element); this._createdAROnlyElements.push(quitARSlot); // for mozilla XR reparenting we have to make sure the close button is clickable so we set it on the element directly // it's in general perhaps more safe to set it on the element to ensure it's clickable quitARSlot.style.pointerEvents = "auto"; // We want to search the document if there's a quit-ar button // In which case we don't want to populate the default button (slot) with any content const quitARElement = document.querySelector(`.${quitARClassName}`); if (quitARElement) { quitARElement.addEventListener('click', this.requestEndAR); if (debug) quitARElement.addEventListener('click', () => console.log("Clicked quit-ar button")); // We found a explicit quit-ar element return; } quitARSlot.addEventListener('click', this.requestEndAR); if (debug) quitARSlot.addEventListener('click', () => console.log("Clicked fallback close button")); // we need another container to make sure the button is always on top const fixedButtonContainer = document.createElement("div"); fixedButtonContainer.style.cssText = ` position: fixed; top: 0; right: 0; z-index: 600; pointer-events: all; `; this.appendElement(fixedButtonContainer, quitARSlot); var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.classList.add("quit-ar-button"); svg.setAttribute('width', "40px"); svg.setAttribute('height', "40px"); svg.style.cssText = ` background: rgba(255, 255, 255, .4); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border-radius: 50%; box-shadow: 0 0 5px rgba(0,0,0,.3); outline: 1px solid rgba(255, 255, 255, .6); display: flex; justify-content: center; align-items: center; `; fixedButtonContainer.appendChild(svg); var path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('d', 'M 12,12 L 28,28 M 28,12 12,28'); path.setAttribute('stroke', '#000000'); path.setAttribute('stroke-width', "2px"); path.style.cssText = ` /**filter: drop-shadow(0 0px 1.2px rgba(0,0,0,.7));**/ `; svg.appendChild(path); if (debug) console.log("Created fallback close button", svg, element); } appendElement(element, parent) { if (parent.shadowRoot) return parent.shadowRoot.appendChild(element); return parent.appendChild(element); } } //# sourceMappingURL=needle-engine.ar-overlay.js.map