UNPKG

@pmndrs/xr

Version:
90 lines (89 loc) 3.96 kB
import { Mesh, MeshBasicMaterial, WebGLRenderTarget } from 'three'; import { createXRLayer, createXRLayerGeometry, getXRLayerSrcTexture, setupXRImageLayer, updateXRLayerProperties, updateXRLayerTransform, waitForXRLayerSrcSize, } from '../layer.js'; export class XRLayer extends Mesh { store; options; properties; layerRenderOrder; layerEntry; cleanup; cleanupSubscription; constructor(store, renderer, options, properties = {}, layerRenderOrder = 0) { super(createXRLayerGeometry(options.shape ?? 'quad', properties), new MeshBasicMaterial({ toneMapped: false })); this.store = store; this.options = options; this.properties = properties; this.layerRenderOrder = layerRenderOrder; this.frustumCulled = false; //to prevent onBeforeRender from not beeing called let aborted = false; this.cleanup = () => (aborted = true); waitForXRLayerSrcSize(options.src).then(() => { if (aborted) { return; } const update = ({ session, originReferenceSpace }, prevState) => { if (originReferenceSpace == null) { return; } if (prevState != null && session === prevState.session) { return; } this.cleanup?.(); const layersEnabled = session?.enabledFeatures?.includes('layers') === true; this.material.colorWrite = !layersEnabled; this.renderOrder = layersEnabled ? -Infinity : 0; if (!layersEnabled) { this.material.colorWrite = true; const texture = getXRLayerSrcTexture(options.src); this.material.map = texture; this.material.needsUpdate = true; this.cleanup = options.src instanceof WebGLRenderTarget ? () => { } : () => texture.dispose(); return; } this.material.map = null; const layer = createXRLayer(options.src, store.getState(), originReferenceSpace, renderer.xr, this, options, properties); if (layer == null) { this.cleanup = () => { }; return; } const layerEntry = (this.layerEntry = { layer, renderOrder: this.layerRenderOrder, object3D: this }); store.addLayerEntry(this.layerEntry); if (options.src instanceof HTMLVideoElement || options.src instanceof WebGLRenderTarget) { this.cleanup = () => this.store.removeLayerEntry(layerEntry); return; } const cleanupXRImageLayer = setupXRImageLayer(renderer, store, layer, options.src); this.cleanup = () => { this.store.removeLayerEntry(layerEntry); cleanupXRImageLayer(); }; }; update(store.getState()); this.cleanupSubscription = store.subscribe(update); }); } setLayerRenderOrder(layerRenderOrder) { this.layerRenderOrder = layerRenderOrder; if (this.layerEntry != null) { this.layerEntry.renderOrder = layerRenderOrder; } } setProperties(properties = {}) { this.properties = properties; this.geometry.dispose(); this.geometry = createXRLayerGeometry(this.options.shape ?? 'quad', properties); if (this.layerEntry != null) { updateXRLayerProperties(this.layerEntry.layer, properties); } } destroy() { this.cleanupSubscription?.(); this.cleanup?.(); } onBeforeRender() { if (this.layerEntry == null) { return; } updateXRLayerTransform(this.store.getState(), this.layerEntry?.layer, this.properties.centralAngle, this); } }