three-live 1.2.3 → 1.2.4

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/dist/three-xr.cjs CHANGED
@@ -316,4 +316,4 @@ interpolation = "vertex"
316
316
  }
317
317
  `}return a+=`}
318
318
  }
319
- `,a}function ze(n,e){if(n&&typeof n.traverse=="function"){let i=Vt(n,e);return Oe(i)}let t=xe(n),r=yt(t);return Oe(r)}var Ee=null;function He(n){if(!Ee){Ee=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let i=0;i<8;i++)r=r&1?3988292384^r>>>1:r>>>1;Ee[t]=r}}let e=4294967295;for(let t=0;t<n.length;t++)e=Ee[(e^n[t])&255]^e>>>8;return(e^4294967295)>>>0}function Le(n){let e=[],t=0,r=[];for(let u of n){let d=Ge(u.filename),f=He(u.data),T=u.data.length,p=30+d.length,h=t+p,b=0,R=h%64;R!==0&&(b=64-R);let A=new Uint8Array(p+b),v=new DataView(A.buffer);v.setUint32(0,67324752,!0),v.setUint16(4,20,!0),v.setUint16(6,0,!0),v.setUint16(8,0,!0),v.setUint16(10,0,!0),v.setUint16(12,0,!0),v.setUint32(14,f,!0),v.setUint32(18,T,!0),v.setUint32(22,T,!0),v.setUint16(26,d.length,!0),v.setUint16(28,b,!0),A.set(d,30),b>0&&A.fill(0,30+d.length),e.push({offset:t,size:T,crc:f,filenameBytes:d,entry:u}),r.push(A),r.push(u.data),t+=A.length+u.data.length}let i=t;for(let u of e){let d=new Uint8Array(46+u.filenameBytes.length),f=new DataView(d.buffer);f.setUint32(0,33639248,!0),f.setUint16(4,20,!0),f.setUint16(6,20,!0),f.setUint16(8,0,!0),f.setUint16(10,0,!0),f.setUint16(12,0,!0),f.setUint16(14,0,!0),f.setUint32(16,u.crc,!0),f.setUint32(20,u.size,!0),f.setUint32(24,u.size,!0),f.setUint16(28,u.filenameBytes.length,!0),f.setUint16(30,0,!0),f.setUint16(32,0,!0),f.setUint16(34,0,!0),f.setUint16(36,0,!0),f.setUint32(38,0,!0),f.setUint32(42,u.offset,!0),d.set(u.filenameBytes,46),r.push(d),t+=d.length}let s=t-i,a=new Uint8Array(22),c=new DataView(a.buffer);c.setUint32(0,101010256,!0),c.setUint16(4,0,!0),c.setUint16(6,0,!0),c.setUint16(8,e.length,!0),c.setUint16(10,e.length,!0),c.setUint32(12,s,!0),c.setUint32(16,i,!0),c.setUint16(20,0,!0),r.push(a),t+=22;let o=new Uint8Array(t),l=0;for(let u of r)o.set(u,l),l+=u.length;return o}function zt(n){let e=ze(n),t=Ge(e);return Le([{filename:"model.usda",data:t}])}function We(n){let e=xe(n),t=yt(e),r=Oe(t),i=Ge(r);return Le([{filename:"model.usda",data:i}])}function Xe(n){return n&&typeof n.toUSDZ=="function"?n.toUSDZ():n&&typeof n.traverse=="function"?zt(n):We(n)}function $e(n){let e=Xe(n);return new Blob([e],{type:"model/vnd.usdz+zip"})}function Ye(){if(typeof window>"u"||typeof navigator>"u")return!1;let n=navigator.userAgent||"";if(!(/iPad|iPhone|iPod/.test(n)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1))return!1;let t=document.createElement("a");return t.relList!==void 0&&t.relList.supports!==void 0&&t.relList.supports("ar")}function Ke(){if(typeof navigator>"u")return!1;let n=navigator.userAgent||"";return/android/i.test(n)}async function Ze(){if(typeof navigator>"u")return!1;let n=navigator;if(!n.xr||typeof n.xr.isSessionSupported!="function")return!1;try{return await n.xr.isSessionSupported("immersive-ar")?!0:await n.xr.isSessionSupported("immersive-vr")}catch{return!1}}async function Wt(){let n=[];return await Ze()&&n.push("webxr"),Ye()&&n.push("quick-look"),Ke()&&n.push("scene-viewer"),n}async function Xt(){return(await je()).bestMode}async function je(){let[n,e,t]=await Promise.all([Ze(),Promise.resolve(Ye()),Promise.resolve(Ke())]),r=[];e&&r.push("quick-look"),n&&r.push("webxr"),t&&r.push("scene-viewer");let i=null;return e?i="quick-look":n?i="webxr":t&&(i="scene-viewer"),{webxr:n,quickLook:e,sceneViewer:t,availableModes:r,bestMode:i}}function qe(n,e={}){if(typeof document>"u")throw new Error("launchQuickLook requires a browser DOM environment");let t,r=!1;if(typeof n=="string"){if(t=$t(n),!t)throw new Error("[XR-Live Security] Quick Look URL is empty.")}else t=URL.createObjectURL(n),r=!0;let i=[];if(e.allowScale===!1&&i.push("allowsContentScaling=0"),e.canonicalWebPageUrl){let c=Se(e.canonicalWebPageUrl,!1);c&&i.push(`canonicalWebPageURL=${encodeURIComponent(c)}`)}e.checkoutTitle&&i.push(`checkoutTitle=${encodeURIComponent(e.checkoutTitle)}`),e.checkoutSubtitle&&i.push(`checkoutSubtitle=${encodeURIComponent(e.checkoutSubtitle)}`),e.price&&i.push(`price=${encodeURIComponent(e.price)}`),i.length>0&&(t+=`#${i.join("&")}`);let s=document.createElement("a");s.setAttribute("rel","ar"),s.setAttribute("href",t);let a=document.createElement("img");a.setAttribute("src","data:image/svg+xml;utf8,<svg/>"),a.style.display="none",s.appendChild(a),document.body.appendChild(s),s.click(),setTimeout(()=>{document.body.removeChild(s),r&&URL.revokeObjectURL(t.split("#")[0])},3e3)}function Se(n,e=!1){if(typeof n!="string")return"";let t=n.replace(/[\x00-\x1f\x7f]+/g,"").trim();if(!t)return"";if(/^[\\/]{2}/.test(t))throw new Error(`[XR-Live Security] Rejected protocol-relative URL in Scene Viewer intent: ${t.slice(0,32)}`);let r=t.toLowerCase();if(r.startsWith("https://")||r.startsWith("http://")||e&&r.startsWith("blob:")||!/^[a-z][a-z0-9+.-]*:/i.test(t))return t;throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Scene Viewer intent: ${t.slice(0,32)}`)}function $t(n){try{return Se(n,!0)}catch{throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Quick Look launch: ${(n||"").trim().slice(0,32)}`)}}function xt(n,e={}){let t=Se(n,!0),r=e.mode||"ar_preferred",i=new URLSearchParams({file:t,mode:r,resizable:e.resizable!==!1?"true":"false",enable_vertical_placement:e.enableVerticalPlacement?"true":"false"});return e.title&&i.set("title",e.title),e.link&&i.set("link",Se(e.link,!1)),`intent://arvr.google.com/scene-viewer/1.0?${i.toString()}#Intent;scheme=https;package=com.google.ar.core;action=android.intent.action.VIEW;end;`}function vt(n,e={}){if(typeof window>"u")throw new Error("launchSceneViewer requires a browser environment");let t=xt(n,e);window.location.href=t}var de=null;function qt(n,e){let r=new Float32Array(144),i=new Float32Array(24*6),s=new Float32Array(24*4),a=new Uint16Array(24*6);for(let d=0;d<24;d++){let f=d/24*Math.PI*2,T=Math.cos(f),p=Math.sin(f),h=d*2,b=h+1,R=(d+1)%24*2;r[h*3]=T*.11,r[h*3+1]=.002,r[h*3+2]=p*.11,r[b*3]=T*.145,r[b*3+1]=.002,r[b*3+2]=p*.145,i[h*3+1]=i[b*3+1]=1,a.set([h,R,b,b,R,R+1],d*6)}let c=n.createVertexArray();n.bindVertexArray(c);let o=(d,f,T)=>{let p=n.createBuffer();return n.bindBuffer(34962,p),n.bufferData(34962,d,35044),n.enableVertexAttribArray(f),n.vertexAttribPointer(f,T,5126,!1,0,0),p},l=[o(r,0,3),o(i,1,3),o(s,2,2)],u=n.createBuffer();return l.push(u),n.bindBuffer(34963,u),n.bufferData(34963,a,35044),n.bindVertexArray(null),{name:"__xr_reticle__",nodeIndex:-1,worldSpace:!0,visible:!1,transform:new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),normalMatrix:new Float32Array([1,0,0,0,1,0,0,0,1]),primitives:[{vao:c,indexCount:a.length,indexType:5123,baseColor:e&&e.length>=3?[e[0],e[1],e[2],e[3]??.95]:[.22,.74,.97,.95],metallic:0,roughness:.2,positions:r,indices:a,texture:null,metallicRoughnessTexture:null,normalTexture:null,occlusionTexture:null,normalScale:1,occlusionStrength:1,buffers:l}]}}function gt(n,e,t,r,i){let s=Math.cos(r),a=Math.sin(r),c=Math.max(.01,i);n.fill(0),n[0]=c*s,n[2]=-c*a,n[5]=c,n[8]=c*a,n[10]=c*s,n[12]=e[0]-c*(s*t[0]+a*t[2]),n[13]=e[1]-c*t[1],n[14]=e[2]-c*(-a*t[0]+s*t[2]),n[15]=1}var Pe=class{constructor(e={}){this.s=null;this.placed=!1;this.placedMat=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);this.cleanFn=null;this.opts=e}isSessionActive(){return this.s!==null}hasPlacedModel(){return this.placed}getPlacedMatrix(){return this.placedMat}async start(e,t){if(typeof navigator>"u"||!navigator.xr)throw new Error("WebXR is not supported on this browser.");de&&de!==this&&de.isSessionActive()&&await de.stop().catch(()=>{});let r=navigator.xr,i=this.opts,s=t||e?.container,a=String(s?.tagName||"").toLowerCase()==="xr-live",c=e?.bounds,o=Number(c?.radius)||1,l=Array.isArray(c?.center)?c.center:[0,0,0],u=Array.isArray(c?.min)?c.min:[-.5,-.5,-.5],d=typeof navigator<"u"&&navigator.userAgent||"",f=/Android|iPhone|iPad|iPod/i.test(d)&&!/Quest|Oculus|Pico|Vision/i.test(d),T=a?i.arScale==="fixed"?1:f?.55:1:i.arScale==="fixed"?1:o>.45?.3/o:o<.05?.12/Math.max(o,.001):1,p=a?[0,-.25,0]:[l[0]||0,i.arPlacement==="wall"?l[1]:u[1]??0,l[2]||0],h="immersive-ar";try{await r.isSessionSupported("immersive-ar")||(h="immersive-vr")}catch{h="immersive-vr"}let b=s?.querySelector?.('[slot="ar-overlay"]')||null,R=!1,A=null,v=()=>{R&&b?.parentNode&&b.parentNode.removeChild(b),b=null,A=null,R=!1};if(h==="immersive-ar"){if(b)b.addEventListener?.("beforexrselect",x=>x.preventDefault());else if(typeof document<"u"&&document.body&&typeof document.createElement=="function"){let x=document.createElement("div");x.style.cssText="position:fixed;inset:0;pointer-events:none;background:transparent!important;z-index:2147483647;font-family:system-ui,sans-serif;user-select:none";let I=document.createElement("button");I.type="button",I.textContent="\u2715 Exit AR",I.style.cssText="position:absolute;top:max(16px,env(safe-area-inset-top,16px));right:16px;pointer-events:auto;padding:8px 14px;border-radius:99px;border:1px solid rgba(255,255,255,.3);background:rgba(15,23,42,.8);color:#f8fafc;font:600 13px system-ui,sans-serif",I.addEventListener("beforexrselect",N=>N.preventDefault()),I.addEventListener("click",N=>{N.stopPropagation(),this.stop().catch(()=>{})}),A=document.createElement("div"),A.style.cssText="position:absolute;bottom:max(28px,env(safe-area-inset-bottom,28px));left:50%;transform:translateX(-50%);padding:8px 15px;border-radius:99px;border:1px solid rgba(56,189,248,.4);background:rgba(15,23,42,.82);color:#f8fafc;font:500 12.5px system-ui,sans-serif;text-align:center;max-width:88vw",A.textContent="Move phone to detect a surface \u2022 Tap to place",x.appendChild(I),x.appendChild(A),document.body.appendChild(x),b=x,R=!0}}let D=new Set(i.optionalFeatures||["local","local-floor","hit-test","light-estimation"]),g={requiredFeatures:i.requiredFeatures?[...i.requiredFeatures]:[],optionalFeatures:Array.from(D)};b&&(D.add("dom-overlay"),g.optionalFeatures=Array.from(D),g.domOverlay={root:b});for(let x of ae)x.onConfigureXRSessionInit?.(s,g);Array.isArray(g.optionalFeatures)&&g.optionalFeatures.includes("dom-overlay")&&!g.domOverlay?.root&&(g.optionalFeatures=g.optionalFeatures.filter(x=>x!=="dom-overlay"));let P=x=>{let I=String(x?.name||"");return I==="NotAllowedError"||I==="SecurityError"};try{this.s=await r.requestSession(h,g)}catch(x){if(v(),P(x))throw i.onError?.(x),x;try{this.s=await r.requestSession(h,{requiredFeatures:[],optionalFeatures:["local","hit-test"]})}catch(I){if(P(I))throw i.onError?.(I),I;try{this.s=await r.requestSession(h)}catch(N){throw i.onError?.(N),N}}}let E=this.s;de=this,this.placed=!1;let y=null,F=null,L=null,C=null,m=null,w=[0,-.25,-1.2],S=[0,-.25,-1.2],M=new Float32Array(16),k=new Float32Array(16),_=!1,K=1,U=0,O=!1,B=null,q=null,Y=0,W=null,j=()=>{de===this&&(de=null),W?.(),W=null;try{F?.cancel?.()}catch{}if(F=null,L){let x=e?.meshes;if(Array.isArray(x)){let N=x.indexOf(L);N!==-1&&x.splice(N,1)}let I=e.getGLContext?.()||e?.gl;if(I)for(let N of L.primitives||[]){I.deleteVertexArray?.(N.vao);for(let z of N.buffers||[])I.deleteBuffer?.(z)}L=null}e?.setXRSessionActive?.(!1),v(),this.s=null,this.placed=!1,this.cleanFn=null};this.cleanFn=j,E.addEventListener("end",()=>{j(),i.onExit?.()}),E.addEventListener("select",()=>{if(!this.placed){let x=m||w;S=[x[0],x[1],x[2]],gt(this.placedMat,S,p,U,T*K),this.placed=!0,L&&(L.visible=!1),A&&(A.textContent="Placed \u2022 Drag to move \u2022 Pinch to scale"),i.onPlaced?.(this.placedMat)}});try{let x=e.getGLContext?.()||e?.gl;x&&typeof x.makeXRCompatible=="function"&&await x.makeXRCompatible();let I=typeof XRWebGLLayer<"u"?XRWebGLLayer:globalThis?.XRWebGLLayer;x&&typeof I=="function"&&typeof E.updateRenderState=="function"&&E.updateRenderState({baseLayer:new I(E,x,{alpha:!0})});try{y=await E.requestReferenceSpace("local")}catch{try{y=await E.requestReferenceSpace("local-floor")}catch{y=await E.requestReferenceSpace("viewer")}}let N=await E.requestReferenceSpace("viewer").catch(()=>y);if(typeof E.requestHitTestSource=="function"&&N&&(F=await E.requestHitTestSource({space:N}).catch(()=>null)),h==="immersive-ar"&&x&&typeof x.createVertexArray=="function"&&Array.isArray(e.meshes)&&(L=qt(x,i.reticleColor),e.meshes.push(L)),typeof window<"u"&&typeof window.addEventListener=="function"){let ee=G=>Math.hypot(G.touches[1].clientX-G.touches[0].clientX,G.touches[1].clientY-G.touches[0].clientY),ie=G=>Math.atan2(G.touches[1].clientY-G.touches[0].clientY,G.touches[1].clientX-G.touches[0].clientX),oe=G=>{this.s&&(G.touches.length===1&&this.placed?O=!0:G.touches.length>=2&&(O=!1,B=ee(G),q=ie(G)))},H=G=>{if(!this.s||G.touches.length<2)return;let te=ee(G),V=ie(G);if(B&&B>4&&i.arScale!=="fixed"&&(K=Math.max(.15,Math.min(6,K*(te/B)))),q!==null){let J=V-q;J>Math.PI&&(J-=Math.PI*2),J<-Math.PI&&(J+=Math.PI*2),U-=J}B=te,q=V},$=G=>{(!G.touches||G.touches.length===0)&&(O=!1),(!G.touches||G.touches.length<2)&&(B=null,q=null)};window.addEventListener("touchstart",oe,{passive:!0}),window.addEventListener("touchmove",H,{passive:!0}),window.addEventListener("touchend",$,{passive:!0}),window.addEventListener("touchcancel",$,{passive:!0}),W=()=>{window.removeEventListener("touchstart",oe),window.removeEventListener("touchmove",H),window.removeEventListener("touchend",$),window.removeEventListener("touchcancel",$)}}e.setXRSessionActive?.(!0);let z=(ee,ie)=>{if(this.s!==E)return;E.requestAnimationFrame(z);let oe=Y>0?Math.min(Math.max((ee-Y)/1e3,0),.1):1/60;Y=ee,e?.stepXRUpdate?.(oe);let H=ie.getViewerPose(y);if(!H)return;let $=H.views?.[0];if(!_&&$?.transform){let Z=$.transform.position,ne=$.transform.matrix,le=Z?.x??(ne?ne[12]:0),ce=Z?.y??(ne?ne[13]:0),ue=Z?.z??(ne?ne[14]:0),fe=ne?-ne[8]:0,be=ne?-ne[10]:-1,De=Math.hypot(fe,be);De>1e-4?(fe/=De,be/=De):(fe=0,be=-1),w=[le+fe*1.2,ce-.25,ue+be*1.2],U=Math.atan2(-fe,-be),_=!0}if(F&&typeof ie.getHitTestResults=="function"&&(!this.placed||O)){let Z=ie.getHitTestResults(F)||[],ne=!1;for(let le of Z){let ce=le.getPose(y);if(ce?.transform?.matrix){let ue=ce.transform.matrix,fe=ue[5];if(i.arPlacement==="wall"&&Math.abs(fe)>.35||i.arPlacement==="floor"&&Math.abs(fe)<.65)continue;C||(C=new Float32Array(16)),C.set(ue),m=[ue[12],ue[13],ue[14]],this.placed&&O&&(S=[ue[12],ue[13],ue[14]]),L&&(L.visible=!this.placed||O,L.transform.set(ue)),!this.placed&&A&&(A.textContent="Surface found \u2022 Tap to place \u2022 Pinch to scale"),i.onHit?.(C),ne=!0;break}}!ne&&L&&(L.visible=!1)}gt(M,this.placed?S:m||w,p,U,T*K),this.placed&&this.placedMat.set(M);let G=e.xrRigMatrix;G instanceof Float32Array&&G.length===16?se(k,G,M):k.set(M),e.modelRootMatrix&&e.modelRootMatrix.set(k);for(let Z of ae)Z.onXRFrame?.(e,E,ie,y);let te=e.getGLContext?.()||e?.gl,V=E.renderState?.baseLayer;te&&V&&V.framebuffer!==void 0&&te.bindFramebuffer(36160,V.framebuffer);let J=h==="immersive-ar",Q=H.views||[];for(let Z=0;Z<Q.length;Z++){let ne=Q[Z];if(typeof e.renderXRView=="function"){let le;if(V&&typeof V.getViewport=="function"){let ce=V.getViewport(ne);ce&&ce.width>0&&ce.height>0&&(le=[ce.x,ce.y,ce.width,ce.height])}else V?.framebufferWidth>0&&V?.framebufferHeight>0&&(le=[0,0,V.framebufferWidth,V.framebufferHeight]);e.renderXRView(ne,k,this.placed,le,J,Z)}}te&&V&&V.framebuffer&&te.bindFramebuffer(36160,null)};E.requestAnimationFrame(z)}catch(x){throw typeof E.end=="function"&&await E.end().catch(()=>{}),j(),i.onError?.(x),x}}async stop(){let e=this.s;if(e)try{await e.end()}catch{}finally{this.cleanFn?.()}else this.cleanFn?.()}};async function Yt(n){let e=await je(),t=n.preferredModes||["quick-look","webxr","scene-viewer"],r=null;for(let c of t)if(e.availableModes.includes(c)){r=c;break}if(r||(r=e.bestMode),!r)return{mode:"quick-look",success:!1,error:new Error("No augmented reality viewing mode is supported on this device.")};let i={...n.quickLookOptions,allowScale:n.arScale==="fixed"?!1:n.quickLookOptions?.allowScale},s={...n.sceneViewerOptions,resizable:n.arScale==="fixed"?!1:n.sceneViewerOptions?.resizable,enableVerticalPlacement:n.arPlacement==="wall"?!0:n.sceneViewerOptions?.enableVerticalPlacement},a={...n.webXROptions,arScale:n.arScale||n.webXROptions?.arScale,arPlacement:n.arPlacement||n.webXROptions?.arPlacement};try{if(r==="quick-look"){let c;if(n.model instanceof Blob&&n.model.type==="model/vnd.usdz+zip")c=n.model;else{if(typeof n.model=="string")return qe(n.model,i),{mode:"quick-look",success:!0};c=$e(n.model)}return qe(c,i),{mode:"quick-look",success:!0}}if(r==="scene-viewer"){let c=n.glbUrl||(typeof n.model=="string"?n.model:null);if(!c)throw new Error("Scene Viewer requires a hosted GLB URL (glbUrl option).");return vt(c,s),{mode:"scene-viewer",success:!0}}if(r==="webxr"){if(!n.view)throw new Error("WebXR AR mode requires an active ThreeView instance.");let c=new Pe(a);return await c.start(n.view,n.element),{mode:"webxr",success:!0,session:c}}}catch(c){return{mode:r,success:!1,error:c}}return{mode:r,success:!1,error:new Error(`Unhandled AR mode: ${r}`)}}
319
+ `,a}function ze(n,e){if(n&&typeof n.traverse=="function"){let i=Vt(n,e);return Oe(i)}let t=xe(n),r=yt(t);return Oe(r)}var Ee=null;function He(n){if(!Ee){Ee=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let i=0;i<8;i++)r=r&1?3988292384^r>>>1:r>>>1;Ee[t]=r}}let e=4294967295;for(let t=0;t<n.length;t++)e=Ee[(e^n[t])&255]^e>>>8;return(e^4294967295)>>>0}function Le(n){let e=[],t=0,r=[];for(let u of n){let d=Ge(u.filename),f=He(u.data),T=u.data.length,p=30+d.length,h=t+p,b=0,R=h%64;R!==0&&(b=64-R);let A=new Uint8Array(p+b),v=new DataView(A.buffer);v.setUint32(0,67324752,!0),v.setUint16(4,20,!0),v.setUint16(6,0,!0),v.setUint16(8,0,!0),v.setUint16(10,0,!0),v.setUint16(12,0,!0),v.setUint32(14,f,!0),v.setUint32(18,T,!0),v.setUint32(22,T,!0),v.setUint16(26,d.length,!0),v.setUint16(28,b,!0),A.set(d,30),b>0&&A.fill(0,30+d.length),e.push({offset:t,size:T,crc:f,filenameBytes:d,entry:u}),r.push(A),r.push(u.data),t+=A.length+u.data.length}let i=t;for(let u of e){let d=new Uint8Array(46+u.filenameBytes.length),f=new DataView(d.buffer);f.setUint32(0,33639248,!0),f.setUint16(4,20,!0),f.setUint16(6,20,!0),f.setUint16(8,0,!0),f.setUint16(10,0,!0),f.setUint16(12,0,!0),f.setUint16(14,0,!0),f.setUint32(16,u.crc,!0),f.setUint32(20,u.size,!0),f.setUint32(24,u.size,!0),f.setUint16(28,u.filenameBytes.length,!0),f.setUint16(30,0,!0),f.setUint16(32,0,!0),f.setUint16(34,0,!0),f.setUint16(36,0,!0),f.setUint32(38,0,!0),f.setUint32(42,u.offset,!0),d.set(u.filenameBytes,46),r.push(d),t+=d.length}let s=t-i,a=new Uint8Array(22),c=new DataView(a.buffer);c.setUint32(0,101010256,!0),c.setUint16(4,0,!0),c.setUint16(6,0,!0),c.setUint16(8,e.length,!0),c.setUint16(10,e.length,!0),c.setUint32(12,s,!0),c.setUint32(16,i,!0),c.setUint16(20,0,!0),r.push(a),t+=22;let o=new Uint8Array(t),l=0;for(let u of r)o.set(u,l),l+=u.length;return o}function zt(n){let e=ze(n),t=Ge(e);return Le([{filename:"model.usda",data:t}])}function We(n){let e=xe(n),t=yt(e),r=Oe(t),i=Ge(r);return Le([{filename:"model.usda",data:i}])}function Xe(n){return n&&typeof n.toUSDZ=="function"?n.toUSDZ():n&&typeof n.traverse=="function"?zt(n):We(n)}function $e(n){let e=Xe(n);return new Blob([e],{type:"model/vnd.usdz+zip"})}function Ye(){if(typeof window>"u"||typeof navigator>"u")return!1;let n=navigator.userAgent||"";if(!(/iPad|iPhone|iPod/.test(n)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1))return!1;let t=document.createElement("a");return t.relList!==void 0&&t.relList.supports!==void 0&&t.relList.supports("ar")}function Ke(){if(typeof navigator>"u")return!1;let n=navigator.userAgent||"";return/android/i.test(n)}async function Ze(){if(typeof navigator>"u")return!1;let n=navigator;if(!n.xr||typeof n.xr.isSessionSupported!="function")return!1;try{return await n.xr.isSessionSupported("immersive-ar")?!0:await n.xr.isSessionSupported("immersive-vr")}catch{return!1}}async function Wt(){let n=[];return await Ze()&&n.push("webxr"),Ye()&&n.push("quick-look"),Ke()&&n.push("scene-viewer"),n}async function Xt(){return(await je()).bestMode}async function je(){let[n,e,t]=await Promise.all([Ze(),Promise.resolve(Ye()),Promise.resolve(Ke())]),r=[];e&&r.push("quick-look"),n&&r.push("webxr"),t&&r.push("scene-viewer");let i=null;return e?i="quick-look":n?i="webxr":t&&(i="scene-viewer"),{webxr:n,quickLook:e,sceneViewer:t,availableModes:r,bestMode:i}}function qe(n,e={}){if(typeof document>"u")throw new Error("launchQuickLook requires a browser DOM environment");let t,r=!1;if(typeof n=="string"){if(t=$t(n),!t)throw new Error("[XR-Live Security] Quick Look URL is empty.")}else t=URL.createObjectURL(n),r=!0;let i=[];if(e.allowScale===!1&&i.push("allowsContentScaling=0"),e.canonicalWebPageUrl){let c=Se(e.canonicalWebPageUrl,!1);c&&i.push(`canonicalWebPageURL=${encodeURIComponent(c)}`)}e.checkoutTitle&&i.push(`checkoutTitle=${encodeURIComponent(e.checkoutTitle)}`),e.checkoutSubtitle&&i.push(`checkoutSubtitle=${encodeURIComponent(e.checkoutSubtitle)}`),e.price&&i.push(`price=${encodeURIComponent(e.price)}`),i.length>0&&(t+=`#${i.join("&")}`);let s=document.createElement("a");s.setAttribute("rel","ar"),s.setAttribute("href",t);let a=document.createElement("img");a.setAttribute("src","data:image/svg+xml;utf8,<svg/>"),a.style.display="none",s.appendChild(a),document.body.appendChild(s),s.click(),setTimeout(()=>{document.body.removeChild(s),r&&URL.revokeObjectURL(t.split("#")[0])},3e3)}function Se(n,e=!1){if(typeof n!="string")return"";let t=n.replace(/[\x00-\x1f\x7f]+/g,"").trim();if(!t)return"";if(/^[\\/]{2}/.test(t))throw new Error(`[XR-Live Security] Rejected protocol-relative URL in Scene Viewer intent: ${t.slice(0,32)}`);let r=t.toLowerCase();if(r.startsWith("https://")||r.startsWith("http://")||e&&r.startsWith("blob:")||!/^[a-z][a-z0-9+.-]*:/i.test(t))return t;throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Scene Viewer intent: ${t.slice(0,32)}`)}function $t(n){try{return Se(n,!0)}catch{throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Quick Look launch: ${(n||"").trim().slice(0,32)}`)}}function xt(n,e={}){let t=Se(n,!0),r=e.mode||"ar_preferred",i=new URLSearchParams({file:t,mode:r,resizable:e.resizable!==!1?"true":"false",enable_vertical_placement:e.enableVerticalPlacement?"true":"false"});return e.title&&i.set("title",e.title),e.link&&i.set("link",Se(e.link,!1)),`intent://arvr.google.com/scene-viewer/1.0?${i.toString()}#Intent;scheme=https;package=com.google.ar.core;action=android.intent.action.VIEW;end;`}function vt(n,e={}){if(typeof window>"u")throw new Error("launchSceneViewer requires a browser environment");let t=xt(n,e);window.location.href=t}var de=null;function qt(n,e){let r=new Float32Array(144),i=new Float32Array(144),s=new Float32Array(96),a=new Uint16Array(144);for(let d=0;d<24;d++){let f=d/24*Math.PI*2,T=Math.cos(f),p=Math.sin(f),h=d*2,b=h+1,R=(d+1)%24*2;r[h*3]=T*.11,r[h*3+1]=.002,r[h*3+2]=p*.11,r[b*3]=T*.145,r[b*3+1]=.002,r[b*3+2]=p*.145,i[h*3+1]=i[b*3+1]=1,a.set([h,R,b,b,R,R+1],d*6)}let c=n.createVertexArray();n.bindVertexArray(c);let o=(d,f,T)=>{let p=n.createBuffer();return n.bindBuffer(34962,p),n.bufferData(34962,d,35044),n.enableVertexAttribArray(f),n.vertexAttribPointer(f,T,5126,!1,0,0),p},l=[o(r,0,3),o(i,1,3),o(s,2,2)],u=n.createBuffer();return l.push(u),n.bindBuffer(34963,u),n.bufferData(34963,a,35044),n.bindVertexArray(null),{name:"__xr_reticle__",nodeIndex:-1,worldSpace:!0,visible:!1,transform:new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),normalMatrix:new Float32Array([1,0,0,0,1,0,0,0,1]),primitives:[{vao:c,indexCount:a.length,indexType:5123,baseColor:e&&e.length>=3?[e[0],e[1],e[2],e[3]??.95]:[.22,.74,.97,.95],metallic:0,roughness:.2,positions:r,indices:a,texture:null,metallicRoughnessTexture:null,normalTexture:null,occlusionTexture:null,normalScale:1,occlusionStrength:1,buffers:l}]}}function gt(n,e,t,r,i){let s=Math.cos(r),a=Math.sin(r),c=Math.max(.01,i);n.fill(0),n[0]=c*s,n[2]=-c*a,n[5]=c,n[8]=c*a,n[10]=c*s,n[12]=e[0]-c*(s*t[0]+a*t[2]),n[13]=e[1]-c*t[1],n[14]=e[2]-c*(-a*t[0]+s*t[2]),n[15]=1}var Pe=class{constructor(e={}){this.s=null;this.placed=!1;this.placedMat=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);this.cleanFn=null;this.opts=e}isSessionActive(){return this.s!==null}hasPlacedModel(){return this.placed}getPlacedMatrix(){return this.placedMat}async start(e,t){if(typeof navigator>"u"||!navigator.xr)throw new Error("WebXR is not supported on this browser.");de&&de!==this&&de.isSessionActive()&&await de.stop().catch(()=>{});let r=navigator.xr,i=this.opts,s=t||e?.container,a=String(s?.tagName||"").toLowerCase()==="xr-live",c=e?.bounds,o=Number(c?.radius)||1,l=Array.isArray(c?.center)?c.center:[0,0,0],u=Array.isArray(c?.min)?c.min:[-.5,-.5,-.5],d=typeof navigator<"u"&&navigator.userAgent||"",f=/Android|iPhone|iPad|iPod/i.test(d)&&!/Quest|Oculus|Pico|Vision/i.test(d),T=a?i.arScale==="fixed"?1:f?.55:1:i.arScale==="fixed"?1:o>.45?.3/o:o<.05?.12/Math.max(o,.001):1,p=a?[0,-.25,0]:[l[0]||0,i.arPlacement==="wall"?l[1]:u[1]??0,l[2]||0],h="immersive-ar";try{await r.isSessionSupported("immersive-ar")||(h="immersive-vr")}catch{h="immersive-vr"}let b=s?.querySelector?.('[slot="ar-overlay"]')||null,R=!1,A=null,v=()=>{R&&b?.parentNode&&b.parentNode.removeChild(b),b=null,A=null,R=!1};if(h==="immersive-ar"){if(b)b.addEventListener?.("beforexrselect",x=>x.preventDefault());else if(typeof document<"u"&&document.body&&typeof document.createElement=="function"){let x=document.createElement("div");x.style.cssText="position:fixed;inset:0;pointer-events:none;background:transparent!important;z-index:2147483647;font-family:system-ui,sans-serif;user-select:none";let I=document.createElement("button");I.type="button",I.textContent="\u2715 Exit AR",I.style.cssText="position:absolute;top:max(16px,env(safe-area-inset-top,16px));right:16px;pointer-events:auto;padding:8px 14px;border-radius:99px;border:1px solid rgba(255,255,255,.3);background:rgba(15,23,42,.8);color:#f8fafc;font:600 13px system-ui,sans-serif",I.addEventListener("beforexrselect",N=>N.preventDefault()),I.addEventListener("click",N=>{N.stopPropagation(),this.stop().catch(()=>{})}),A=document.createElement("div"),A.style.cssText="position:absolute;bottom:max(28px,env(safe-area-inset-bottom,28px));left:50%;transform:translateX(-50%);padding:8px 15px;border-radius:99px;border:1px solid rgba(56,189,248,.4);background:rgba(15,23,42,.82);color:#f8fafc;font:500 12.5px system-ui,sans-serif;text-align:center;max-width:88vw",A.textContent="Move phone to detect a surface \u2022 Tap to place",x.appendChild(I),x.appendChild(A),document.body.appendChild(x),b=x,R=!0}}let D=new Set(i.optionalFeatures||["local","local-floor","hit-test","light-estimation"]),g={requiredFeatures:i.requiredFeatures?[...i.requiredFeatures]:[],optionalFeatures:Array.from(D)};b&&(D.add("dom-overlay"),g.optionalFeatures=Array.from(D),g.domOverlay={root:b});for(let x of ae)x.onConfigureXRSessionInit?.(s,g);Array.isArray(g.optionalFeatures)&&g.optionalFeatures.includes("dom-overlay")&&!g.domOverlay?.root&&(g.optionalFeatures=g.optionalFeatures.filter(x=>x!=="dom-overlay"));let P=x=>{let I=String(x?.name||"");return I==="NotAllowedError"||I==="SecurityError"};try{this.s=await r.requestSession(h,g)}catch(x){if(v(),P(x))throw i.onError?.(x),x;try{this.s=await r.requestSession(h,{requiredFeatures:[],optionalFeatures:["local","hit-test"]})}catch(I){if(P(I))throw i.onError?.(I),I;try{this.s=await r.requestSession(h)}catch(N){throw i.onError?.(N),N}}}let E=this.s;de=this,this.placed=!1;let y=null,F=null,L=null,C=null,m=null,w=[0,-.25,-1.2],S=[0,-.25,-1.2],M=new Float32Array(16),k=new Float32Array(16),_=!1,K=1,U=0,O=!1,B=null,q=null,Y=0,W=null,j=()=>{de===this&&(de=null),W?.(),W=null;try{F?.cancel?.()}catch{}if(F=null,L){let x=e?.meshes;if(Array.isArray(x)){let N=x.indexOf(L);N!==-1&&x.splice(N,1)}let I=e.getGLContext?.()||e?.gl;if(I)for(let N of L.primitives||[]){I.deleteVertexArray?.(N.vao);for(let z of N.buffers||[])I.deleteBuffer?.(z)}L=null}e?.setXRSessionActive?.(!1),v(),this.s=null,this.placed=!1,this.cleanFn=null};this.cleanFn=j,E.addEventListener("end",()=>{j(),i.onExit?.()}),E.addEventListener("select",()=>{if(!this.placed){let x=m||w;S=[x[0],x[1],x[2]],gt(this.placedMat,S,p,U,T*K),this.placed=!0,L&&(L.visible=!1),A&&(A.textContent="Placed \u2022 Drag to move \u2022 Pinch to scale"),i.onPlaced?.(this.placedMat)}});try{let x=e.getGLContext?.()||e?.gl;x&&typeof x.makeXRCompatible=="function"&&await x.makeXRCompatible();let I=typeof XRWebGLLayer<"u"?XRWebGLLayer:globalThis?.XRWebGLLayer;x&&typeof I=="function"&&typeof E.updateRenderState=="function"&&E.updateRenderState({baseLayer:new I(E,x,{alpha:!0})});try{y=await E.requestReferenceSpace("local")}catch{try{y=await E.requestReferenceSpace("local-floor")}catch{y=await E.requestReferenceSpace("viewer")}}let N=await E.requestReferenceSpace("viewer").catch(()=>y);if(typeof E.requestHitTestSource=="function"&&N&&(F=await E.requestHitTestSource({space:N}).catch(()=>null)),h==="immersive-ar"&&x&&typeof x.createVertexArray=="function"&&Array.isArray(e.meshes)&&(L=qt(x,i.reticleColor),e.meshes.push(L)),typeof window<"u"&&typeof window.addEventListener=="function"){let ee=G=>Math.hypot(G.touches[1].clientX-G.touches[0].clientX,G.touches[1].clientY-G.touches[0].clientY),ie=G=>Math.atan2(G.touches[1].clientY-G.touches[0].clientY,G.touches[1].clientX-G.touches[0].clientX),oe=G=>{this.s&&(G.touches.length===1&&this.placed?O=!0:G.touches.length>=2&&(O=!1,B=ee(G),q=ie(G)))},H=G=>{if(!this.s||G.touches.length<2)return;let te=ee(G),V=ie(G);if(B&&B>4&&i.arScale!=="fixed"&&(K=Math.max(.15,Math.min(6,K*(te/B)))),q!==null){let J=V-q;J>Math.PI&&(J-=Math.PI*2),J<-Math.PI&&(J+=Math.PI*2),U-=J}B=te,q=V},$=G=>{(!G.touches||G.touches.length===0)&&(O=!1),(!G.touches||G.touches.length<2)&&(B=null,q=null)};window.addEventListener("touchstart",oe,{passive:!0}),window.addEventListener("touchmove",H,{passive:!0}),window.addEventListener("touchend",$,{passive:!0}),window.addEventListener("touchcancel",$,{passive:!0}),W=()=>{window.removeEventListener("touchstart",oe),window.removeEventListener("touchmove",H),window.removeEventListener("touchend",$),window.removeEventListener("touchcancel",$)}}e.setXRSessionActive?.(!0);let z=(ee,ie)=>{if(this.s!==E)return;E.requestAnimationFrame(z);let oe=Y>0?Math.min(Math.max((ee-Y)/1e3,0),.1):1/60;Y=ee,e?.stepXRUpdate?.(oe);let H=ie.getViewerPose(y);if(!H)return;let $=H.views?.[0];if(!_&&$?.transform){let Z=$.transform.position,ne=$.transform.matrix,le=Z?.x??(ne?ne[12]:0),ce=Z?.y??(ne?ne[13]:0),ue=Z?.z??(ne?ne[14]:0),fe=ne?-ne[8]:0,be=ne?-ne[10]:-1,De=Math.hypot(fe,be);De>1e-4?(fe/=De,be/=De):(fe=0,be=-1),w=[le+fe*1.2,ce-.25,ue+be*1.2],U=Math.atan2(-fe,-be),_=!0}if(F&&typeof ie.getHitTestResults=="function"&&(!this.placed||O)){let Z=ie.getHitTestResults(F)||[],ne=!1;for(let le of Z){let ce=le.getPose(y);if(ce?.transform?.matrix){let ue=ce.transform.matrix,fe=ue[5];if(i.arPlacement==="wall"&&Math.abs(fe)>.35||i.arPlacement==="floor"&&Math.abs(fe)<.65)continue;C||(C=new Float32Array(16)),C.set(ue),m=[ue[12],ue[13],ue[14]],this.placed&&O&&(S=[ue[12],ue[13],ue[14]]),L&&(L.visible=!this.placed||O,L.transform.set(ue)),!this.placed&&A&&(A.textContent="Surface found \u2022 Tap to place \u2022 Pinch to scale"),i.onHit?.(C),ne=!0;break}}!ne&&L&&(L.visible=!1)}gt(M,this.placed?S:m||w,p,U,T*K),this.placed&&this.placedMat.set(M);let G=e.xrRigMatrix;G instanceof Float32Array&&G.length===16?se(k,G,M):k.set(M),e.modelRootMatrix&&e.modelRootMatrix.set(k);for(let Z of ae)Z.onXRFrame?.(e,E,ie,y);let te=e.getGLContext?.()||e?.gl,V=E.renderState?.baseLayer;te&&V&&V.framebuffer!==void 0&&te.bindFramebuffer(36160,V.framebuffer);let J=h==="immersive-ar",Q=H.views||[];for(let Z=0;Z<Q.length;Z++){let ne=Q[Z];if(typeof e.renderXRView=="function"){let le;if(V&&typeof V.getViewport=="function"){let ce=V.getViewport(ne);ce&&ce.width>0&&ce.height>0&&(le=[ce.x,ce.y,ce.width,ce.height])}else V?.framebufferWidth>0&&V?.framebufferHeight>0&&(le=[0,0,V.framebufferWidth,V.framebufferHeight]);e.renderXRView(ne,k,this.placed,le,J,Z)}}te&&V&&V.framebuffer&&te.bindFramebuffer(36160,null)};E.requestAnimationFrame(z)}catch(x){throw typeof E.end=="function"&&await E.end().catch(()=>{}),j(),i.onError?.(x),x}}async stop(){let e=this.s;if(e)try{await e.end()}catch{}finally{this.cleanFn?.()}else this.cleanFn?.()}};async function Yt(n){let e=await je(),t=n.preferredModes||["quick-look","webxr","scene-viewer"],r=null;for(let c of t)if(e.availableModes.includes(c)){r=c;break}if(r||(r=e.bestMode),!r)return{mode:"quick-look",success:!1,error:new Error("No augmented reality viewing mode is supported on this device.")};let i={...n.quickLookOptions,allowScale:n.arScale==="fixed"?!1:n.quickLookOptions?.allowScale},s={...n.sceneViewerOptions,resizable:n.arScale==="fixed"?!1:n.sceneViewerOptions?.resizable,enableVerticalPlacement:n.arPlacement==="wall"?!0:n.sceneViewerOptions?.enableVerticalPlacement},a={...n.webXROptions,arScale:n.arScale||n.webXROptions?.arScale,arPlacement:n.arPlacement||n.webXROptions?.arPlacement};try{if(r==="quick-look"){let c;if(n.model instanceof Blob&&n.model.type==="model/vnd.usdz+zip")c=n.model;else{if(typeof n.model=="string")return qe(n.model,i),{mode:"quick-look",success:!0};c=$e(n.model)}return qe(c,i),{mode:"quick-look",success:!0}}if(r==="scene-viewer"){let c=n.glbUrl||(typeof n.model=="string"?n.model:null);if(!c)throw new Error("Scene Viewer requires a hosted GLB URL (glbUrl option).");return vt(c,s),{mode:"scene-viewer",success:!0}}if(r==="webxr"){if(!n.view)throw new Error("WebXR AR mode requires an active ThreeView instance.");let c=new Pe(a);return await c.start(n.view,n.element),{mode:"webxr",success:!0,session:c}}}catch(c){return{mode:r,success:!1,error:c}}return{mode:r,success:!1,error:new Error(`Unhandled AR mode: ${r}`)}}
package/dist/three-xr.js CHANGED
@@ -316,4 +316,4 @@ interpolation = "vertex"
316
316
  }
317
317
  `}return a+=`}
318
318
  }
319
- `,a}function ot(n,e){if(n&&typeof n.traverse=="function"){let i=Dt(n,e);return ke(i)}let t=xe(n),r=at(t);return ke(r)}var Ee=null;function ct(n){if(!Ee){Ee=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let i=0;i<8;i++)r=r&1?3988292384^r>>>1:r>>>1;Ee[t]=r}}let e=4294967295;for(let t=0;t<n.length;t++)e=Ee[(e^n[t])&255]^e>>>8;return(e^4294967295)>>>0}function Ve(n){let e=[],t=0,r=[];for(let u of n){let d=Be(u.filename),f=ct(u.data),T=u.data.length,p=30+d.length,h=t+p,b=0,R=h%64;R!==0&&(b=64-R);let A=new Uint8Array(p+b),v=new DataView(A.buffer);v.setUint32(0,67324752,!0),v.setUint16(4,20,!0),v.setUint16(6,0,!0),v.setUint16(8,0,!0),v.setUint16(10,0,!0),v.setUint16(12,0,!0),v.setUint32(14,f,!0),v.setUint32(18,T,!0),v.setUint32(22,T,!0),v.setUint16(26,d.length,!0),v.setUint16(28,b,!0),A.set(d,30),b>0&&A.fill(0,30+d.length),e.push({offset:t,size:T,crc:f,filenameBytes:d,entry:u}),r.push(A),r.push(u.data),t+=A.length+u.data.length}let i=t;for(let u of e){let d=new Uint8Array(46+u.filenameBytes.length),f=new DataView(d.buffer);f.setUint32(0,33639248,!0),f.setUint16(4,20,!0),f.setUint16(6,20,!0),f.setUint16(8,0,!0),f.setUint16(10,0,!0),f.setUint16(12,0,!0),f.setUint16(14,0,!0),f.setUint32(16,u.crc,!0),f.setUint32(20,u.size,!0),f.setUint32(24,u.size,!0),f.setUint16(28,u.filenameBytes.length,!0),f.setUint16(30,0,!0),f.setUint16(32,0,!0),f.setUint16(34,0,!0),f.setUint16(36,0,!0),f.setUint32(38,0,!0),f.setUint32(42,u.offset,!0),d.set(u.filenameBytes,46),r.push(d),t+=d.length}let s=t-i,a=new Uint8Array(22),c=new DataView(a.buffer);c.setUint32(0,101010256,!0),c.setUint16(4,0,!0),c.setUint16(6,0,!0),c.setUint16(8,e.length,!0),c.setUint16(10,e.length,!0),c.setUint32(12,s,!0),c.setUint32(16,i,!0),c.setUint16(20,0,!0),r.push(a),t+=22;let o=new Uint8Array(t),l=0;for(let u of r)o.set(u,l),l+=u.length;return o}function It(n){let e=ot(n),t=Be(e);return Ve([{filename:"model.usda",data:t}])}function ut(n){let e=xe(n),t=at(e),r=ke(t),i=Be(r);return Ve([{filename:"model.usda",data:i}])}function lt(n){return n&&typeof n.toUSDZ=="function"?n.toUSDZ():n&&typeof n.traverse=="function"?It(n):ut(n)}function mt(n){let e=lt(n);return new Blob([e],{type:"model/vnd.usdz+zip"})}function dt(){if(typeof window>"u"||typeof navigator>"u")return!1;let n=navigator.userAgent||"";if(!(/iPad|iPhone|iPod/.test(n)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1))return!1;let t=document.createElement("a");return t.relList!==void 0&&t.relList.supports!==void 0&&t.relList.supports("ar")}function pt(){if(typeof navigator>"u")return!1;let n=navigator.userAgent||"";return/android/i.test(n)}async function bt(){if(typeof navigator>"u")return!1;let n=navigator;if(!n.xr||typeof n.xr.isSessionSupported!="function")return!1;try{return await n.xr.isSessionSupported("immersive-ar")?!0:await n.xr.isSessionSupported("immersive-vr")}catch{return!1}}async function Jt(){let n=[];return await bt()&&n.push("webxr"),dt()&&n.push("quick-look"),pt()&&n.push("scene-viewer"),n}async function en(){return(await yt()).bestMode}async function yt(){let[n,e,t]=await Promise.all([bt(),Promise.resolve(dt()),Promise.resolve(pt())]),r=[];e&&r.push("quick-look"),n&&r.push("webxr"),t&&r.push("scene-viewer");let i=null;return e?i="quick-look":n?i="webxr":t&&(i="scene-viewer"),{webxr:n,quickLook:e,sceneViewer:t,availableModes:r,bestMode:i}}function ht(n,e={}){if(typeof document>"u")throw new Error("launchQuickLook requires a browser DOM environment");let t,r=!1;if(typeof n=="string"){if(t=Nt(n),!t)throw new Error("[XR-Live Security] Quick Look URL is empty.")}else t=URL.createObjectURL(n),r=!0;let i=[];if(e.allowScale===!1&&i.push("allowsContentScaling=0"),e.canonicalWebPageUrl){let c=Le(e.canonicalWebPageUrl,!1);c&&i.push(`canonicalWebPageURL=${encodeURIComponent(c)}`)}e.checkoutTitle&&i.push(`checkoutTitle=${encodeURIComponent(e.checkoutTitle)}`),e.checkoutSubtitle&&i.push(`checkoutSubtitle=${encodeURIComponent(e.checkoutSubtitle)}`),e.price&&i.push(`price=${encodeURIComponent(e.price)}`),i.length>0&&(t+=`#${i.join("&")}`);let s=document.createElement("a");s.setAttribute("rel","ar"),s.setAttribute("href",t);let a=document.createElement("img");a.setAttribute("src","data:image/svg+xml;utf8,<svg/>"),a.style.display="none",s.appendChild(a),document.body.appendChild(s),s.click(),setTimeout(()=>{document.body.removeChild(s),r&&URL.revokeObjectURL(t.split("#")[0])},3e3)}function Le(n,e=!1){if(typeof n!="string")return"";let t=n.replace(/[\x00-\x1f\x7f]+/g,"").trim();if(!t)return"";if(/^[\\/]{2}/.test(t))throw new Error(`[XR-Live Security] Rejected protocol-relative URL in Scene Viewer intent: ${t.slice(0,32)}`);let r=t.toLowerCase();if(r.startsWith("https://")||r.startsWith("http://")||e&&r.startsWith("blob:")||!/^[a-z][a-z0-9+.-]*:/i.test(t))return t;throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Scene Viewer intent: ${t.slice(0,32)}`)}function Nt(n){try{return Le(n,!0)}catch{throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Quick Look launch: ${(n||"").trim().slice(0,32)}`)}}function Ut(n,e={}){let t=Le(n,!0),r=e.mode||"ar_preferred",i=new URLSearchParams({file:t,mode:r,resizable:e.resizable!==!1?"true":"false",enable_vertical_placement:e.enableVerticalPlacement?"true":"false"});return e.title&&i.set("title",e.title),e.link&&i.set("link",Le(e.link,!1)),`intent://arvr.google.com/scene-viewer/1.0?${i.toString()}#Intent;scheme=https;package=com.google.ar.core;action=android.intent.action.VIEW;end;`}function kt(n,e={}){if(typeof window>"u")throw new Error("launchSceneViewer requires a browser environment");let t=Ut(n,e);window.location.href=t}var de=null;function Bt(n,e){let r=new Float32Array(144),i=new Float32Array(24*6),s=new Float32Array(24*4),a=new Uint16Array(24*6);for(let d=0;d<24;d++){let f=d/24*Math.PI*2,T=Math.cos(f),p=Math.sin(f),h=d*2,b=h+1,R=(d+1)%24*2;r[h*3]=T*.11,r[h*3+1]=.002,r[h*3+2]=p*.11,r[b*3]=T*.145,r[b*3+1]=.002,r[b*3+2]=p*.145,i[h*3+1]=i[b*3+1]=1,a.set([h,R,b,b,R,R+1],d*6)}let c=n.createVertexArray();n.bindVertexArray(c);let o=(d,f,T)=>{let p=n.createBuffer();return n.bindBuffer(34962,p),n.bufferData(34962,d,35044),n.enableVertexAttribArray(f),n.vertexAttribPointer(f,T,5126,!1,0,0),p},l=[o(r,0,3),o(i,1,3),o(s,2,2)],u=n.createBuffer();return l.push(u),n.bindBuffer(34963,u),n.bufferData(34963,a,35044),n.bindVertexArray(null),{name:"__xr_reticle__",nodeIndex:-1,worldSpace:!0,visible:!1,transform:new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),normalMatrix:new Float32Array([1,0,0,0,1,0,0,0,1]),primitives:[{vao:c,indexCount:a.length,indexType:5123,baseColor:e&&e.length>=3?[e[0],e[1],e[2],e[3]??.95]:[.22,.74,.97,.95],metallic:0,roughness:.2,positions:r,indices:a,texture:null,metallicRoughnessTexture:null,normalTexture:null,occlusionTexture:null,normalScale:1,occlusionStrength:1,buffers:l}]}}function ft(n,e,t,r,i){let s=Math.cos(r),a=Math.sin(r),c=Math.max(.01,i);n.fill(0),n[0]=c*s,n[2]=-c*a,n[5]=c,n[8]=c*a,n[10]=c*s,n[12]=e[0]-c*(s*t[0]+a*t[2]),n[13]=e[1]-c*t[1],n[14]=e[2]-c*(-a*t[0]+s*t[2]),n[15]=1}var Oe=class{constructor(e={}){this.s=null;this.placed=!1;this.placedMat=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);this.cleanFn=null;this.opts=e}isSessionActive(){return this.s!==null}hasPlacedModel(){return this.placed}getPlacedMatrix(){return this.placedMat}async start(e,t){if(typeof navigator>"u"||!navigator.xr)throw new Error("WebXR is not supported on this browser.");de&&de!==this&&de.isSessionActive()&&await de.stop().catch(()=>{});let r=navigator.xr,i=this.opts,s=t||e?.container,a=String(s?.tagName||"").toLowerCase()==="xr-live",c=e?.bounds,o=Number(c?.radius)||1,l=Array.isArray(c?.center)?c.center:[0,0,0],u=Array.isArray(c?.min)?c.min:[-.5,-.5,-.5],d=typeof navigator<"u"&&navigator.userAgent||"",f=/Android|iPhone|iPad|iPod/i.test(d)&&!/Quest|Oculus|Pico|Vision/i.test(d),T=a?i.arScale==="fixed"?1:f?.55:1:i.arScale==="fixed"?1:o>.45?.3/o:o<.05?.12/Math.max(o,.001):1,p=a?[0,-.25,0]:[l[0]||0,i.arPlacement==="wall"?l[1]:u[1]??0,l[2]||0],h="immersive-ar";try{await r.isSessionSupported("immersive-ar")||(h="immersive-vr")}catch{h="immersive-vr"}let b=s?.querySelector?.('[slot="ar-overlay"]')||null,R=!1,A=null,v=()=>{R&&b?.parentNode&&b.parentNode.removeChild(b),b=null,A=null,R=!1};if(h==="immersive-ar"){if(b)b.addEventListener?.("beforexrselect",x=>x.preventDefault());else if(typeof document<"u"&&document.body&&typeof document.createElement=="function"){let x=document.createElement("div");x.style.cssText="position:fixed;inset:0;pointer-events:none;background:transparent!important;z-index:2147483647;font-family:system-ui,sans-serif;user-select:none";let I=document.createElement("button");I.type="button",I.textContent="\u2715 Exit AR",I.style.cssText="position:absolute;top:max(16px,env(safe-area-inset-top,16px));right:16px;pointer-events:auto;padding:8px 14px;border-radius:99px;border:1px solid rgba(255,255,255,.3);background:rgba(15,23,42,.8);color:#f8fafc;font:600 13px system-ui,sans-serif",I.addEventListener("beforexrselect",N=>N.preventDefault()),I.addEventListener("click",N=>{N.stopPropagation(),this.stop().catch(()=>{})}),A=document.createElement("div"),A.style.cssText="position:absolute;bottom:max(28px,env(safe-area-inset-bottom,28px));left:50%;transform:translateX(-50%);padding:8px 15px;border-radius:99px;border:1px solid rgba(56,189,248,.4);background:rgba(15,23,42,.82);color:#f8fafc;font:500 12.5px system-ui,sans-serif;text-align:center;max-width:88vw",A.textContent="Move phone to detect a surface \u2022 Tap to place",x.appendChild(I),x.appendChild(A),document.body.appendChild(x),b=x,R=!0}}let D=new Set(i.optionalFeatures||["local","local-floor","hit-test","light-estimation"]),g={requiredFeatures:i.requiredFeatures?[...i.requiredFeatures]:[],optionalFeatures:Array.from(D)};b&&(D.add("dom-overlay"),g.optionalFeatures=Array.from(D),g.domOverlay={root:b});for(let x of ae)x.onConfigureXRSessionInit?.(s,g);Array.isArray(g.optionalFeatures)&&g.optionalFeatures.includes("dom-overlay")&&!g.domOverlay?.root&&(g.optionalFeatures=g.optionalFeatures.filter(x=>x!=="dom-overlay"));let P=x=>{let I=String(x?.name||"");return I==="NotAllowedError"||I==="SecurityError"};try{this.s=await r.requestSession(h,g)}catch(x){if(v(),P(x))throw i.onError?.(x),x;try{this.s=await r.requestSession(h,{requiredFeatures:[],optionalFeatures:["local","hit-test"]})}catch(I){if(P(I))throw i.onError?.(I),I;try{this.s=await r.requestSession(h)}catch(N){throw i.onError?.(N),N}}}let E=this.s;de=this,this.placed=!1;let y=null,F=null,L=null,C=null,m=null,w=[0,-.25,-1.2],S=[0,-.25,-1.2],M=new Float32Array(16),k=new Float32Array(16),_=!1,K=1,U=0,O=!1,B=null,q=null,Y=0,W=null,j=()=>{de===this&&(de=null),W?.(),W=null;try{F?.cancel?.()}catch{}if(F=null,L){let x=e?.meshes;if(Array.isArray(x)){let N=x.indexOf(L);N!==-1&&x.splice(N,1)}let I=e.getGLContext?.()||e?.gl;if(I)for(let N of L.primitives||[]){I.deleteVertexArray?.(N.vao);for(let z of N.buffers||[])I.deleteBuffer?.(z)}L=null}e?.setXRSessionActive?.(!1),v(),this.s=null,this.placed=!1,this.cleanFn=null};this.cleanFn=j,E.addEventListener("end",()=>{j(),i.onExit?.()}),E.addEventListener("select",()=>{if(!this.placed){let x=m||w;S=[x[0],x[1],x[2]],ft(this.placedMat,S,p,U,T*K),this.placed=!0,L&&(L.visible=!1),A&&(A.textContent="Placed \u2022 Drag to move \u2022 Pinch to scale"),i.onPlaced?.(this.placedMat)}});try{let x=e.getGLContext?.()||e?.gl;x&&typeof x.makeXRCompatible=="function"&&await x.makeXRCompatible();let I=typeof XRWebGLLayer<"u"?XRWebGLLayer:globalThis?.XRWebGLLayer;x&&typeof I=="function"&&typeof E.updateRenderState=="function"&&E.updateRenderState({baseLayer:new I(E,x,{alpha:!0})});try{y=await E.requestReferenceSpace("local")}catch{try{y=await E.requestReferenceSpace("local-floor")}catch{y=await E.requestReferenceSpace("viewer")}}let N=await E.requestReferenceSpace("viewer").catch(()=>y);if(typeof E.requestHitTestSource=="function"&&N&&(F=await E.requestHitTestSource({space:N}).catch(()=>null)),h==="immersive-ar"&&x&&typeof x.createVertexArray=="function"&&Array.isArray(e.meshes)&&(L=Bt(x,i.reticleColor),e.meshes.push(L)),typeof window<"u"&&typeof window.addEventListener=="function"){let ee=G=>Math.hypot(G.touches[1].clientX-G.touches[0].clientX,G.touches[1].clientY-G.touches[0].clientY),ie=G=>Math.atan2(G.touches[1].clientY-G.touches[0].clientY,G.touches[1].clientX-G.touches[0].clientX),oe=G=>{this.s&&(G.touches.length===1&&this.placed?O=!0:G.touches.length>=2&&(O=!1,B=ee(G),q=ie(G)))},H=G=>{if(!this.s||G.touches.length<2)return;let te=ee(G),V=ie(G);if(B&&B>4&&i.arScale!=="fixed"&&(K=Math.max(.15,Math.min(6,K*(te/B)))),q!==null){let J=V-q;J>Math.PI&&(J-=Math.PI*2),J<-Math.PI&&(J+=Math.PI*2),U-=J}B=te,q=V},$=G=>{(!G.touches||G.touches.length===0)&&(O=!1),(!G.touches||G.touches.length<2)&&(B=null,q=null)};window.addEventListener("touchstart",oe,{passive:!0}),window.addEventListener("touchmove",H,{passive:!0}),window.addEventListener("touchend",$,{passive:!0}),window.addEventListener("touchcancel",$,{passive:!0}),W=()=>{window.removeEventListener("touchstart",oe),window.removeEventListener("touchmove",H),window.removeEventListener("touchend",$),window.removeEventListener("touchcancel",$)}}e.setXRSessionActive?.(!0);let z=(ee,ie)=>{if(this.s!==E)return;E.requestAnimationFrame(z);let oe=Y>0?Math.min(Math.max((ee-Y)/1e3,0),.1):1/60;Y=ee,e?.stepXRUpdate?.(oe);let H=ie.getViewerPose(y);if(!H)return;let $=H.views?.[0];if(!_&&$?.transform){let Z=$.transform.position,ne=$.transform.matrix,le=Z?.x??(ne?ne[12]:0),ce=Z?.y??(ne?ne[13]:0),ue=Z?.z??(ne?ne[14]:0),fe=ne?-ne[8]:0,be=ne?-ne[10]:-1,Se=Math.hypot(fe,be);Se>1e-4?(fe/=Se,be/=Se):(fe=0,be=-1),w=[le+fe*1.2,ce-.25,ue+be*1.2],U=Math.atan2(-fe,-be),_=!0}if(F&&typeof ie.getHitTestResults=="function"&&(!this.placed||O)){let Z=ie.getHitTestResults(F)||[],ne=!1;for(let le of Z){let ce=le.getPose(y);if(ce?.transform?.matrix){let ue=ce.transform.matrix,fe=ue[5];if(i.arPlacement==="wall"&&Math.abs(fe)>.35||i.arPlacement==="floor"&&Math.abs(fe)<.65)continue;C||(C=new Float32Array(16)),C.set(ue),m=[ue[12],ue[13],ue[14]],this.placed&&O&&(S=[ue[12],ue[13],ue[14]]),L&&(L.visible=!this.placed||O,L.transform.set(ue)),!this.placed&&A&&(A.textContent="Surface found \u2022 Tap to place \u2022 Pinch to scale"),i.onHit?.(C),ne=!0;break}}!ne&&L&&(L.visible=!1)}ft(M,this.placed?S:m||w,p,U,T*K),this.placed&&this.placedMat.set(M);let G=e.xrRigMatrix;G instanceof Float32Array&&G.length===16?se(k,G,M):k.set(M),e.modelRootMatrix&&e.modelRootMatrix.set(k);for(let Z of ae)Z.onXRFrame?.(e,E,ie,y);let te=e.getGLContext?.()||e?.gl,V=E.renderState?.baseLayer;te&&V&&V.framebuffer!==void 0&&te.bindFramebuffer(36160,V.framebuffer);let J=h==="immersive-ar",Q=H.views||[];for(let Z=0;Z<Q.length;Z++){let ne=Q[Z];if(typeof e.renderXRView=="function"){let le;if(V&&typeof V.getViewport=="function"){let ce=V.getViewport(ne);ce&&ce.width>0&&ce.height>0&&(le=[ce.x,ce.y,ce.width,ce.height])}else V?.framebufferWidth>0&&V?.framebufferHeight>0&&(le=[0,0,V.framebufferWidth,V.framebufferHeight]);e.renderXRView(ne,k,this.placed,le,J,Z)}}te&&V&&V.framebuffer&&te.bindFramebuffer(36160,null)};E.requestAnimationFrame(z)}catch(x){throw typeof E.end=="function"&&await E.end().catch(()=>{}),j(),i.onError?.(x),x}}async stop(){let e=this.s;if(e)try{await e.end()}catch{}finally{this.cleanFn?.()}else this.cleanFn?.()}};async function tn(n){let e=await yt(),t=n.preferredModes||["quick-look","webxr","scene-viewer"],r=null;for(let c of t)if(e.availableModes.includes(c)){r=c;break}if(r||(r=e.bestMode),!r)return{mode:"quick-look",success:!1,error:new Error("No augmented reality viewing mode is supported on this device.")};let i={...n.quickLookOptions,allowScale:n.arScale==="fixed"?!1:n.quickLookOptions?.allowScale},s={...n.sceneViewerOptions,resizable:n.arScale==="fixed"?!1:n.sceneViewerOptions?.resizable,enableVerticalPlacement:n.arPlacement==="wall"?!0:n.sceneViewerOptions?.enableVerticalPlacement},a={...n.webXROptions,arScale:n.arScale||n.webXROptions?.arScale,arPlacement:n.arPlacement||n.webXROptions?.arPlacement};try{if(r==="quick-look"){let c;if(n.model instanceof Blob&&n.model.type==="model/vnd.usdz+zip")c=n.model;else{if(typeof n.model=="string")return ht(n.model,i),{mode:"quick-look",success:!0};c=mt(n.model)}return ht(c,i),{mode:"quick-look",success:!0}}if(r==="scene-viewer"){let c=n.glbUrl||(typeof n.model=="string"?n.model:null);if(!c)throw new Error("Scene Viewer requires a hosted GLB URL (glbUrl option).");return kt(c,s),{mode:"scene-viewer",success:!0}}if(r==="webxr"){if(!n.view)throw new Error("WebXR AR mode requires an active ThreeView instance.");let c=new Oe(a);return await c.start(n.view,n.element),{mode:"webxr",success:!0,session:c}}}catch(c){return{mode:r,success:!1,error:c}}return{mode:r,success:!1,error:new Error(`Unhandled AR mode: ${r}`)}}export{Oe as WebXRSessionManager,tn as activateAR,Ve as buildUSDZZip,ct as calculateCRC32,Ut as createSceneViewerIntent,mt as createUSDZBlob,ot as generateUSDA,lt as generateUSDZ,yt as getARCapabilities,Jt as getAvailableARModes,en as getBestARMode,ut as glbToUSDZ,dt as isQuickLookSupported,pt as isSceneViewerSupported,bt as isWebXRSupported,ht as launchQuickLook,kt as launchSceneViewer};
319
+ `,a}function ot(n,e){if(n&&typeof n.traverse=="function"){let i=Dt(n,e);return ke(i)}let t=xe(n),r=at(t);return ke(r)}var Ee=null;function ct(n){if(!Ee){Ee=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let i=0;i<8;i++)r=r&1?3988292384^r>>>1:r>>>1;Ee[t]=r}}let e=4294967295;for(let t=0;t<n.length;t++)e=Ee[(e^n[t])&255]^e>>>8;return(e^4294967295)>>>0}function Ve(n){let e=[],t=0,r=[];for(let u of n){let d=Be(u.filename),f=ct(u.data),T=u.data.length,p=30+d.length,h=t+p,b=0,R=h%64;R!==0&&(b=64-R);let A=new Uint8Array(p+b),v=new DataView(A.buffer);v.setUint32(0,67324752,!0),v.setUint16(4,20,!0),v.setUint16(6,0,!0),v.setUint16(8,0,!0),v.setUint16(10,0,!0),v.setUint16(12,0,!0),v.setUint32(14,f,!0),v.setUint32(18,T,!0),v.setUint32(22,T,!0),v.setUint16(26,d.length,!0),v.setUint16(28,b,!0),A.set(d,30),b>0&&A.fill(0,30+d.length),e.push({offset:t,size:T,crc:f,filenameBytes:d,entry:u}),r.push(A),r.push(u.data),t+=A.length+u.data.length}let i=t;for(let u of e){let d=new Uint8Array(46+u.filenameBytes.length),f=new DataView(d.buffer);f.setUint32(0,33639248,!0),f.setUint16(4,20,!0),f.setUint16(6,20,!0),f.setUint16(8,0,!0),f.setUint16(10,0,!0),f.setUint16(12,0,!0),f.setUint16(14,0,!0),f.setUint32(16,u.crc,!0),f.setUint32(20,u.size,!0),f.setUint32(24,u.size,!0),f.setUint16(28,u.filenameBytes.length,!0),f.setUint16(30,0,!0),f.setUint16(32,0,!0),f.setUint16(34,0,!0),f.setUint16(36,0,!0),f.setUint32(38,0,!0),f.setUint32(42,u.offset,!0),d.set(u.filenameBytes,46),r.push(d),t+=d.length}let s=t-i,a=new Uint8Array(22),c=new DataView(a.buffer);c.setUint32(0,101010256,!0),c.setUint16(4,0,!0),c.setUint16(6,0,!0),c.setUint16(8,e.length,!0),c.setUint16(10,e.length,!0),c.setUint32(12,s,!0),c.setUint32(16,i,!0),c.setUint16(20,0,!0),r.push(a),t+=22;let o=new Uint8Array(t),l=0;for(let u of r)o.set(u,l),l+=u.length;return o}function It(n){let e=ot(n),t=Be(e);return Ve([{filename:"model.usda",data:t}])}function ut(n){let e=xe(n),t=at(e),r=ke(t),i=Be(r);return Ve([{filename:"model.usda",data:i}])}function lt(n){return n&&typeof n.toUSDZ=="function"?n.toUSDZ():n&&typeof n.traverse=="function"?It(n):ut(n)}function mt(n){let e=lt(n);return new Blob([e],{type:"model/vnd.usdz+zip"})}function dt(){if(typeof window>"u"||typeof navigator>"u")return!1;let n=navigator.userAgent||"";if(!(/iPad|iPhone|iPod/.test(n)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1))return!1;let t=document.createElement("a");return t.relList!==void 0&&t.relList.supports!==void 0&&t.relList.supports("ar")}function pt(){if(typeof navigator>"u")return!1;let n=navigator.userAgent||"";return/android/i.test(n)}async function bt(){if(typeof navigator>"u")return!1;let n=navigator;if(!n.xr||typeof n.xr.isSessionSupported!="function")return!1;try{return await n.xr.isSessionSupported("immersive-ar")?!0:await n.xr.isSessionSupported("immersive-vr")}catch{return!1}}async function Jt(){let n=[];return await bt()&&n.push("webxr"),dt()&&n.push("quick-look"),pt()&&n.push("scene-viewer"),n}async function en(){return(await yt()).bestMode}async function yt(){let[n,e,t]=await Promise.all([bt(),Promise.resolve(dt()),Promise.resolve(pt())]),r=[];e&&r.push("quick-look"),n&&r.push("webxr"),t&&r.push("scene-viewer");let i=null;return e?i="quick-look":n?i="webxr":t&&(i="scene-viewer"),{webxr:n,quickLook:e,sceneViewer:t,availableModes:r,bestMode:i}}function ht(n,e={}){if(typeof document>"u")throw new Error("launchQuickLook requires a browser DOM environment");let t,r=!1;if(typeof n=="string"){if(t=Nt(n),!t)throw new Error("[XR-Live Security] Quick Look URL is empty.")}else t=URL.createObjectURL(n),r=!0;let i=[];if(e.allowScale===!1&&i.push("allowsContentScaling=0"),e.canonicalWebPageUrl){let c=Le(e.canonicalWebPageUrl,!1);c&&i.push(`canonicalWebPageURL=${encodeURIComponent(c)}`)}e.checkoutTitle&&i.push(`checkoutTitle=${encodeURIComponent(e.checkoutTitle)}`),e.checkoutSubtitle&&i.push(`checkoutSubtitle=${encodeURIComponent(e.checkoutSubtitle)}`),e.price&&i.push(`price=${encodeURIComponent(e.price)}`),i.length>0&&(t+=`#${i.join("&")}`);let s=document.createElement("a");s.setAttribute("rel","ar"),s.setAttribute("href",t);let a=document.createElement("img");a.setAttribute("src","data:image/svg+xml;utf8,<svg/>"),a.style.display="none",s.appendChild(a),document.body.appendChild(s),s.click(),setTimeout(()=>{document.body.removeChild(s),r&&URL.revokeObjectURL(t.split("#")[0])},3e3)}function Le(n,e=!1){if(typeof n!="string")return"";let t=n.replace(/[\x00-\x1f\x7f]+/g,"").trim();if(!t)return"";if(/^[\\/]{2}/.test(t))throw new Error(`[XR-Live Security] Rejected protocol-relative URL in Scene Viewer intent: ${t.slice(0,32)}`);let r=t.toLowerCase();if(r.startsWith("https://")||r.startsWith("http://")||e&&r.startsWith("blob:")||!/^[a-z][a-z0-9+.-]*:/i.test(t))return t;throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Scene Viewer intent: ${t.slice(0,32)}`)}function Nt(n){try{return Le(n,!0)}catch{throw new Error(`[XR-Live Security] Rejected unsafe URL scheme in Quick Look launch: ${(n||"").trim().slice(0,32)}`)}}function Ut(n,e={}){let t=Le(n,!0),r=e.mode||"ar_preferred",i=new URLSearchParams({file:t,mode:r,resizable:e.resizable!==!1?"true":"false",enable_vertical_placement:e.enableVerticalPlacement?"true":"false"});return e.title&&i.set("title",e.title),e.link&&i.set("link",Le(e.link,!1)),`intent://arvr.google.com/scene-viewer/1.0?${i.toString()}#Intent;scheme=https;package=com.google.ar.core;action=android.intent.action.VIEW;end;`}function kt(n,e={}){if(typeof window>"u")throw new Error("launchSceneViewer requires a browser environment");let t=Ut(n,e);window.location.href=t}var de=null;function Bt(n,e){let r=new Float32Array(144),i=new Float32Array(144),s=new Float32Array(96),a=new Uint16Array(144);for(let d=0;d<24;d++){let f=d/24*Math.PI*2,T=Math.cos(f),p=Math.sin(f),h=d*2,b=h+1,R=(d+1)%24*2;r[h*3]=T*.11,r[h*3+1]=.002,r[h*3+2]=p*.11,r[b*3]=T*.145,r[b*3+1]=.002,r[b*3+2]=p*.145,i[h*3+1]=i[b*3+1]=1,a.set([h,R,b,b,R,R+1],d*6)}let c=n.createVertexArray();n.bindVertexArray(c);let o=(d,f,T)=>{let p=n.createBuffer();return n.bindBuffer(34962,p),n.bufferData(34962,d,35044),n.enableVertexAttribArray(f),n.vertexAttribPointer(f,T,5126,!1,0,0),p},l=[o(r,0,3),o(i,1,3),o(s,2,2)],u=n.createBuffer();return l.push(u),n.bindBuffer(34963,u),n.bufferData(34963,a,35044),n.bindVertexArray(null),{name:"__xr_reticle__",nodeIndex:-1,worldSpace:!0,visible:!1,transform:new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),normalMatrix:new Float32Array([1,0,0,0,1,0,0,0,1]),primitives:[{vao:c,indexCount:a.length,indexType:5123,baseColor:e&&e.length>=3?[e[0],e[1],e[2],e[3]??.95]:[.22,.74,.97,.95],metallic:0,roughness:.2,positions:r,indices:a,texture:null,metallicRoughnessTexture:null,normalTexture:null,occlusionTexture:null,normalScale:1,occlusionStrength:1,buffers:l}]}}function ft(n,e,t,r,i){let s=Math.cos(r),a=Math.sin(r),c=Math.max(.01,i);n.fill(0),n[0]=c*s,n[2]=-c*a,n[5]=c,n[8]=c*a,n[10]=c*s,n[12]=e[0]-c*(s*t[0]+a*t[2]),n[13]=e[1]-c*t[1],n[14]=e[2]-c*(-a*t[0]+s*t[2]),n[15]=1}var Oe=class{constructor(e={}){this.s=null;this.placed=!1;this.placedMat=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);this.cleanFn=null;this.opts=e}isSessionActive(){return this.s!==null}hasPlacedModel(){return this.placed}getPlacedMatrix(){return this.placedMat}async start(e,t){if(typeof navigator>"u"||!navigator.xr)throw new Error("WebXR is not supported on this browser.");de&&de!==this&&de.isSessionActive()&&await de.stop().catch(()=>{});let r=navigator.xr,i=this.opts,s=t||e?.container,a=String(s?.tagName||"").toLowerCase()==="xr-live",c=e?.bounds,o=Number(c?.radius)||1,l=Array.isArray(c?.center)?c.center:[0,0,0],u=Array.isArray(c?.min)?c.min:[-.5,-.5,-.5],d=typeof navigator<"u"&&navigator.userAgent||"",f=/Android|iPhone|iPad|iPod/i.test(d)&&!/Quest|Oculus|Pico|Vision/i.test(d),T=a?i.arScale==="fixed"?1:f?.55:1:i.arScale==="fixed"?1:o>.45?.3/o:o<.05?.12/Math.max(o,.001):1,p=a?[0,-.25,0]:[l[0]||0,i.arPlacement==="wall"?l[1]:u[1]??0,l[2]||0],h="immersive-ar";try{await r.isSessionSupported("immersive-ar")||(h="immersive-vr")}catch{h="immersive-vr"}let b=s?.querySelector?.('[slot="ar-overlay"]')||null,R=!1,A=null,v=()=>{R&&b?.parentNode&&b.parentNode.removeChild(b),b=null,A=null,R=!1};if(h==="immersive-ar"){if(b)b.addEventListener?.("beforexrselect",x=>x.preventDefault());else if(typeof document<"u"&&document.body&&typeof document.createElement=="function"){let x=document.createElement("div");x.style.cssText="position:fixed;inset:0;pointer-events:none;background:transparent!important;z-index:2147483647;font-family:system-ui,sans-serif;user-select:none";let I=document.createElement("button");I.type="button",I.textContent="\u2715 Exit AR",I.style.cssText="position:absolute;top:max(16px,env(safe-area-inset-top,16px));right:16px;pointer-events:auto;padding:8px 14px;border-radius:99px;border:1px solid rgba(255,255,255,.3);background:rgba(15,23,42,.8);color:#f8fafc;font:600 13px system-ui,sans-serif",I.addEventListener("beforexrselect",N=>N.preventDefault()),I.addEventListener("click",N=>{N.stopPropagation(),this.stop().catch(()=>{})}),A=document.createElement("div"),A.style.cssText="position:absolute;bottom:max(28px,env(safe-area-inset-bottom,28px));left:50%;transform:translateX(-50%);padding:8px 15px;border-radius:99px;border:1px solid rgba(56,189,248,.4);background:rgba(15,23,42,.82);color:#f8fafc;font:500 12.5px system-ui,sans-serif;text-align:center;max-width:88vw",A.textContent="Move phone to detect a surface \u2022 Tap to place",x.appendChild(I),x.appendChild(A),document.body.appendChild(x),b=x,R=!0}}let D=new Set(i.optionalFeatures||["local","local-floor","hit-test","light-estimation"]),g={requiredFeatures:i.requiredFeatures?[...i.requiredFeatures]:[],optionalFeatures:Array.from(D)};b&&(D.add("dom-overlay"),g.optionalFeatures=Array.from(D),g.domOverlay={root:b});for(let x of ae)x.onConfigureXRSessionInit?.(s,g);Array.isArray(g.optionalFeatures)&&g.optionalFeatures.includes("dom-overlay")&&!g.domOverlay?.root&&(g.optionalFeatures=g.optionalFeatures.filter(x=>x!=="dom-overlay"));let P=x=>{let I=String(x?.name||"");return I==="NotAllowedError"||I==="SecurityError"};try{this.s=await r.requestSession(h,g)}catch(x){if(v(),P(x))throw i.onError?.(x),x;try{this.s=await r.requestSession(h,{requiredFeatures:[],optionalFeatures:["local","hit-test"]})}catch(I){if(P(I))throw i.onError?.(I),I;try{this.s=await r.requestSession(h)}catch(N){throw i.onError?.(N),N}}}let E=this.s;de=this,this.placed=!1;let y=null,F=null,L=null,C=null,m=null,w=[0,-.25,-1.2],S=[0,-.25,-1.2],M=new Float32Array(16),k=new Float32Array(16),_=!1,K=1,U=0,O=!1,B=null,q=null,Y=0,W=null,j=()=>{de===this&&(de=null),W?.(),W=null;try{F?.cancel?.()}catch{}if(F=null,L){let x=e?.meshes;if(Array.isArray(x)){let N=x.indexOf(L);N!==-1&&x.splice(N,1)}let I=e.getGLContext?.()||e?.gl;if(I)for(let N of L.primitives||[]){I.deleteVertexArray?.(N.vao);for(let z of N.buffers||[])I.deleteBuffer?.(z)}L=null}e?.setXRSessionActive?.(!1),v(),this.s=null,this.placed=!1,this.cleanFn=null};this.cleanFn=j,E.addEventListener("end",()=>{j(),i.onExit?.()}),E.addEventListener("select",()=>{if(!this.placed){let x=m||w;S=[x[0],x[1],x[2]],ft(this.placedMat,S,p,U,T*K),this.placed=!0,L&&(L.visible=!1),A&&(A.textContent="Placed \u2022 Drag to move \u2022 Pinch to scale"),i.onPlaced?.(this.placedMat)}});try{let x=e.getGLContext?.()||e?.gl;x&&typeof x.makeXRCompatible=="function"&&await x.makeXRCompatible();let I=typeof XRWebGLLayer<"u"?XRWebGLLayer:globalThis?.XRWebGLLayer;x&&typeof I=="function"&&typeof E.updateRenderState=="function"&&E.updateRenderState({baseLayer:new I(E,x,{alpha:!0})});try{y=await E.requestReferenceSpace("local")}catch{try{y=await E.requestReferenceSpace("local-floor")}catch{y=await E.requestReferenceSpace("viewer")}}let N=await E.requestReferenceSpace("viewer").catch(()=>y);if(typeof E.requestHitTestSource=="function"&&N&&(F=await E.requestHitTestSource({space:N}).catch(()=>null)),h==="immersive-ar"&&x&&typeof x.createVertexArray=="function"&&Array.isArray(e.meshes)&&(L=Bt(x,i.reticleColor),e.meshes.push(L)),typeof window<"u"&&typeof window.addEventListener=="function"){let ee=G=>Math.hypot(G.touches[1].clientX-G.touches[0].clientX,G.touches[1].clientY-G.touches[0].clientY),ie=G=>Math.atan2(G.touches[1].clientY-G.touches[0].clientY,G.touches[1].clientX-G.touches[0].clientX),oe=G=>{this.s&&(G.touches.length===1&&this.placed?O=!0:G.touches.length>=2&&(O=!1,B=ee(G),q=ie(G)))},H=G=>{if(!this.s||G.touches.length<2)return;let te=ee(G),V=ie(G);if(B&&B>4&&i.arScale!=="fixed"&&(K=Math.max(.15,Math.min(6,K*(te/B)))),q!==null){let J=V-q;J>Math.PI&&(J-=Math.PI*2),J<-Math.PI&&(J+=Math.PI*2),U-=J}B=te,q=V},$=G=>{(!G.touches||G.touches.length===0)&&(O=!1),(!G.touches||G.touches.length<2)&&(B=null,q=null)};window.addEventListener("touchstart",oe,{passive:!0}),window.addEventListener("touchmove",H,{passive:!0}),window.addEventListener("touchend",$,{passive:!0}),window.addEventListener("touchcancel",$,{passive:!0}),W=()=>{window.removeEventListener("touchstart",oe),window.removeEventListener("touchmove",H),window.removeEventListener("touchend",$),window.removeEventListener("touchcancel",$)}}e.setXRSessionActive?.(!0);let z=(ee,ie)=>{if(this.s!==E)return;E.requestAnimationFrame(z);let oe=Y>0?Math.min(Math.max((ee-Y)/1e3,0),.1):1/60;Y=ee,e?.stepXRUpdate?.(oe);let H=ie.getViewerPose(y);if(!H)return;let $=H.views?.[0];if(!_&&$?.transform){let Z=$.transform.position,ne=$.transform.matrix,le=Z?.x??(ne?ne[12]:0),ce=Z?.y??(ne?ne[13]:0),ue=Z?.z??(ne?ne[14]:0),fe=ne?-ne[8]:0,be=ne?-ne[10]:-1,Se=Math.hypot(fe,be);Se>1e-4?(fe/=Se,be/=Se):(fe=0,be=-1),w=[le+fe*1.2,ce-.25,ue+be*1.2],U=Math.atan2(-fe,-be),_=!0}if(F&&typeof ie.getHitTestResults=="function"&&(!this.placed||O)){let Z=ie.getHitTestResults(F)||[],ne=!1;for(let le of Z){let ce=le.getPose(y);if(ce?.transform?.matrix){let ue=ce.transform.matrix,fe=ue[5];if(i.arPlacement==="wall"&&Math.abs(fe)>.35||i.arPlacement==="floor"&&Math.abs(fe)<.65)continue;C||(C=new Float32Array(16)),C.set(ue),m=[ue[12],ue[13],ue[14]],this.placed&&O&&(S=[ue[12],ue[13],ue[14]]),L&&(L.visible=!this.placed||O,L.transform.set(ue)),!this.placed&&A&&(A.textContent="Surface found \u2022 Tap to place \u2022 Pinch to scale"),i.onHit?.(C),ne=!0;break}}!ne&&L&&(L.visible=!1)}ft(M,this.placed?S:m||w,p,U,T*K),this.placed&&this.placedMat.set(M);let G=e.xrRigMatrix;G instanceof Float32Array&&G.length===16?se(k,G,M):k.set(M),e.modelRootMatrix&&e.modelRootMatrix.set(k);for(let Z of ae)Z.onXRFrame?.(e,E,ie,y);let te=e.getGLContext?.()||e?.gl,V=E.renderState?.baseLayer;te&&V&&V.framebuffer!==void 0&&te.bindFramebuffer(36160,V.framebuffer);let J=h==="immersive-ar",Q=H.views||[];for(let Z=0;Z<Q.length;Z++){let ne=Q[Z];if(typeof e.renderXRView=="function"){let le;if(V&&typeof V.getViewport=="function"){let ce=V.getViewport(ne);ce&&ce.width>0&&ce.height>0&&(le=[ce.x,ce.y,ce.width,ce.height])}else V?.framebufferWidth>0&&V?.framebufferHeight>0&&(le=[0,0,V.framebufferWidth,V.framebufferHeight]);e.renderXRView(ne,k,this.placed,le,J,Z)}}te&&V&&V.framebuffer&&te.bindFramebuffer(36160,null)};E.requestAnimationFrame(z)}catch(x){throw typeof E.end=="function"&&await E.end().catch(()=>{}),j(),i.onError?.(x),x}}async stop(){let e=this.s;if(e)try{await e.end()}catch{}finally{this.cleanFn?.()}else this.cleanFn?.()}};async function tn(n){let e=await yt(),t=n.preferredModes||["quick-look","webxr","scene-viewer"],r=null;for(let c of t)if(e.availableModes.includes(c)){r=c;break}if(r||(r=e.bestMode),!r)return{mode:"quick-look",success:!1,error:new Error("No augmented reality viewing mode is supported on this device.")};let i={...n.quickLookOptions,allowScale:n.arScale==="fixed"?!1:n.quickLookOptions?.allowScale},s={...n.sceneViewerOptions,resizable:n.arScale==="fixed"?!1:n.sceneViewerOptions?.resizable,enableVerticalPlacement:n.arPlacement==="wall"?!0:n.sceneViewerOptions?.enableVerticalPlacement},a={...n.webXROptions,arScale:n.arScale||n.webXROptions?.arScale,arPlacement:n.arPlacement||n.webXROptions?.arPlacement};try{if(r==="quick-look"){let c;if(n.model instanceof Blob&&n.model.type==="model/vnd.usdz+zip")c=n.model;else{if(typeof n.model=="string")return ht(n.model,i),{mode:"quick-look",success:!0};c=mt(n.model)}return ht(c,i),{mode:"quick-look",success:!0}}if(r==="scene-viewer"){let c=n.glbUrl||(typeof n.model=="string"?n.model:null);if(!c)throw new Error("Scene Viewer requires a hosted GLB URL (glbUrl option).");return kt(c,s),{mode:"scene-viewer",success:!0}}if(r==="webxr"){if(!n.view)throw new Error("WebXR AR mode requires an active ThreeView instance.");let c=new Oe(a);return await c.start(n.view,n.element),{mode:"webxr",success:!0,session:c}}}catch(c){return{mode:r,success:!1,error:c}}return{mode:r,success:!1,error:new Error(`Unhandled AR mode: ${r}`)}}export{Oe as WebXRSessionManager,tn as activateAR,Ve as buildUSDZZip,ct as calculateCRC32,Ut as createSceneViewerIntent,mt as createUSDZBlob,ot as generateUSDA,lt as generateUSDZ,yt as getARCapabilities,Jt as getAvailableARModes,en as getBestARMode,ut as glbToUSDZ,dt as isQuickLookSupported,pt as isSceneViewerSupported,bt as isWebXRSupported,ht as launchQuickLook,kt as launchSceneViewer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-live",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Lightweight 3D, WebXR, and procedural Three.js runtime",
5
5
  "author": "Google LLC",
6
6
  "license": "Apache-2.0",