scad-gltf 0.1.0

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.
Files changed (37) hide show
  1. package/README.md +489 -0
  2. package/bin/scad-convert.js +221 -0
  3. package/bin/scad-godot.js +247 -0
  4. package/bin/scad-mcp.js +508 -0
  5. package/bin/scad-serve.js +225 -0
  6. package/bin/scad-web.js +206 -0
  7. package/editor/dist/aristea_wreck_puresky_2k.hdr +0 -0
  8. package/editor/dist/assets/OutputPass-Bvl6NigM.js +4317 -0
  9. package/editor/dist/assets/index-V9cEH4KX.js +4318 -0
  10. package/editor/dist/assets/index-t9MYrExo.css +1 -0
  11. package/editor/dist/assets/openscad-CdBCY4mx.wasm +0 -0
  12. package/editor/dist/assets/preview-C1zc24MJ.js +1 -0
  13. package/editor/dist/assets/preview-fQfL_FJ-.css +1 -0
  14. package/editor/dist/assets/prompt-ui-8EFRz0ju.js +126 -0
  15. package/editor/dist/content-loader.js +4 -0
  16. package/editor/dist/content.css +255 -0
  17. package/editor/dist/content.js +24 -0
  18. package/editor/dist/icon.png +0 -0
  19. package/editor/dist/index.html +187 -0
  20. package/editor/dist/manifest.json +23 -0
  21. package/editor/dist/manifest.webmanifest +1 -0
  22. package/editor/dist/preview.html +27 -0
  23. package/editor/dist/registerSW.js +1 -0
  24. package/editor/dist/sw.js +1 -0
  25. package/editor/dist/workbox-9c191d2f.js +1 -0
  26. package/godot/README.md +64 -0
  27. package/godot/addons/scad_importer/plugin.cfg +7 -0
  28. package/godot/addons/scad_importer/scad_importer.gd +197 -0
  29. package/godot/addons/scad_importer/scad_plugin.gd +12 -0
  30. package/godot/examples/README.md +82 -0
  31. package/godot/examples/fruit_fusion_3d.js +1574 -0
  32. package/godot/examples/package.json +5 -0
  33. package/package.json +63 -0
  34. package/src/convert.js +94 -0
  35. package/src/ext/openscad.js +14 -0
  36. package/src/ext/openscad.wasm +0 -0
  37. package/src/prompt.js +229 -0
@@ -0,0 +1,4318 @@
1
+ import{$ as e,A as t,B as n,C as r,D as i,E as a,F as o,G as s,H as c,I as l,J as u,K as d,L as f,M as p,N as m,O as h,P as g,Q as _,R as v,S as y,T as b,U as x,V as S,W as C,X as w,Y as ee,Z as te,_ as T,a as ne,at as re,b as ie,c as ae,ct as oe,d as se,dt as ce,et as le,f as ue,ft as de,g as fe,h as pe,ht as me,i as he,it as ge,j as _e,k as ve,l as ye,lt as E,m as be,mt as xe,n as Se,nt as Ce,o as we,ot as Te,p as Ee,pt as De,q as Oe,r as ke,rt as Ae,s as je,st as Me,t as Ne,tt as Pe,u as Fe,ut as D,v as Ie,w as Le,x as Re,y as ze,z as Be}from"./OutputPass-Bvl6NigM.js";import{i as Ve,n as He,r as Ue,t as We}from"./prompt-ui-8EFRz0ju.js";var Ge=1.25,Ke=65535,qe=-65536,Je=2**-24,Ye=Symbol(`SKIP_GENERATION`),Xe={strategy:0,maxDepth:40,targetLeafSize:10,useSharedArrayBuffer:!1,setBoundingBox:!0,onProgress:null,indirect:!1,verbose:!0,range:null,[Ye]:!1};function O(e,t,n){return n.min.x=t[e],n.min.y=t[e+1],n.min.z=t[e+2],n.max.x=t[e+3],n.max.y=t[e+4],n.max.z=t[e+5],n}function Ze(e){let t=-1,n=-1/0;for(let r=0;r<3;r++){let i=e[r+3]-e[r];i>n&&(n=i,t=r)}return t}function Qe(e,t){t.set(e)}function $e(e,t,n){let r,i;for(let a=0;a<3;a++){let o=a+3;r=e[a],i=t[a],n[a]=r<i?r:i,r=e[o],i=t[o],n[o]=r>i?r:i}}function et(e,t,n){for(let r=0;r<3;r++){let i=t[e+2*r],a=t[e+2*r+1],o=i-a,s=i+a;o<n[r]&&(n[r]=o),s>n[r+3]&&(n[r+3]=s)}}function tt(e){let t=e[3]-e[0],n=e[4]-e[1],r=e[5]-e[2];return 2*(t*n+n*r+r*t)}function k(e,t){return t[e+15]===Ke}function A(e,t){return t[e+6]}function j(e,t){return t[e+14]}function M(e){return e+8}function N(e,t){return e+t[e+6]*8}function nt(e,t){return t[e+7]}function P(e){return e}function rt(e,t,n,r,i){let a=1/0,o=1/0,s=1/0,c=-1/0,l=-1/0,u=-1/0,d=1/0,f=1/0,p=1/0,m=-1/0,h=-1/0,g=-1/0,_=e.offset||0;for(let r=(t-_)*6,i=(t+n-_)*6;r<i;r+=6){let t=e[r+0],n=e[r+1],i=t-n,_=t+n;i<a&&(a=i),_>c&&(c=_),t<d&&(d=t),t>m&&(m=t);let v=e[r+2],y=e[r+3],b=v-y,x=v+y;b<o&&(o=b),x>l&&(l=x),v<f&&(f=v),v>h&&(h=v);let S=e[r+4],C=e[r+5],w=S-C,ee=S+C;w<s&&(s=w),ee>u&&(u=ee),S<p&&(p=S),S>g&&(g=S)}r[0]=a,r[1]=o,r[2]=s,r[3]=c,r[4]=l,r[5]=u,i[0]=d,i[1]=f,i[2]=p,i[3]=m,i[4]=h,i[5]=g}var it=32,at=(e,t)=>e.candidate-t.candidate,ot=Array(it).fill().map(()=>({count:0,bounds:new Float32Array(6),rightCacheBounds:new Float32Array(6),leftCacheBounds:new Float32Array(6),candidate:0})),st=new Float32Array(6);function ct(e,t,n,r,i,a){let o=-1,s=0;if(a===0)o=Ze(t),o!==-1&&(s=(t[o]+t[o+3])/2);else if(a===1)o=Ze(e),o!==-1&&(s=lt(n,r,i,o));else if(a===2){let a=tt(e),c=Ge*i,l=n.offset||0,u=(r-l)*6,d=(r+i-l)*6;for(let e=0;e<3;e++){let r=t[e],l=(t[e+3]-r)/it;if(i<it/4){let t=[...ot];t.length=i;let r=0;for(let i=u;i<d;i+=6,r++){let a=t[r];a.candidate=n[i+2*e],a.count=0;let{bounds:o,leftCacheBounds:s,rightCacheBounds:c}=a;for(let e=0;e<3;e++)c[e]=1/0,c[e+3]=-1/0,s[e]=1/0,s[e+3]=-1/0,o[e]=1/0,o[e+3]=-1/0;et(i,n,o)}t.sort(at);let l=i;for(let e=0;e<l;e++){let n=t[e];for(;e+1<l&&t[e+1].candidate===n.candidate;)t.splice(e+1,1),l--}for(let r=u;r<d;r+=6){let i=n[r+2*e];for(let e=0;e<l;e++){let a=t[e];i>=a.candidate?et(r,n,a.rightCacheBounds):(et(r,n,a.leftCacheBounds),a.count++)}}for(let n=0;n<l;n++){let r=t[n],l=r.count,u=i-r.count,d=r.leftCacheBounds,f=r.rightCacheBounds,p=0;l!==0&&(p=tt(d)/a);let m=0;u!==0&&(m=tt(f)/a);let h=1+Ge*(p*l+m*u);h<c&&(o=e,c=h,s=r.candidate)}}else{for(let e=0;e<it;e++){let t=ot[e];t.count=0,t.candidate=r+l+e*l;let n=t.bounds;for(let e=0;e<3;e++)n[e]=1/0,n[e+3]=-1/0}for(let t=u;t<d;t+=6){let i=~~((n[t+2*e]-r)/l);i>=it&&(i=31);let a=ot[i];a.count++,et(t,n,a.bounds)}let t=ot[31];Qe(t.bounds,t.rightCacheBounds);for(let e=30;e>=0;e--){let t=ot[e],n=ot[e+1];$e(t.bounds,n.rightCacheBounds,t.rightCacheBounds)}let f=0;for(let t=0;t<31;t++){let n=ot[t],r=n.count,l=n.bounds,u=ot[t+1].rightCacheBounds;r!==0&&(f===0?Qe(l,st):$e(l,st,st)),f+=r;let d=0,p=0;f!==0&&(d=tt(st)/a);let m=i-f;m!==0&&(p=tt(u)/a);let h=1+Ge*(d*f+p*m);h<c&&(o=e,c=h,s=n.candidate)}}}}else console.warn(`BVH: Invalid build strategy value ${a} used.`);return{axis:o,pos:s}}function lt(e,t,n,r){let i=0,a=e.offset;for(let o=t,s=t+n;o<s;o++)i+=e[(o-a)*6+r*2];return i/n}var ut=class{constructor(){this.boundingData=new Float32Array(6)}};function dt(e,t,n,r,i,a){let o=r,s=r+i-1,c=a.pos,l=a.axis*2,u=n.offset||0;for(;;){for(;o<=s&&n[(o-u)*6+l]<c;)o++;for(;o<=s&&n[(s-u)*6+l]>=c;)s--;if(o<s){for(let n=0;n<t;n++){let r=e[o*t+n];e[o*t+n]=e[s*t+n],e[s*t+n]=r}for(let e=0;e<6;e++){let t=o-u,r=s-u,i=n[t*6+e];n[t*6+e]=n[r*6+e],n[r*6+e]=i}o++,s--}else return o}}var ft,pt,mt,ht,gt=2**32;function _t(e){return`count`in e?1:1+_t(e.left)+_t(e.right)}function vt(e,t,n){return ft=new Float32Array(n),pt=new Uint32Array(n),mt=new Uint16Array(n),ht=new Uint8Array(n),yt(e,t)}function yt(e,t){let n=e/4,r=e/2,i=`count`in t,a=t.boundingData;for(let e=0;e<6;e++)ft[n+e]=a[e];if(i)return t.buffer?(ht.set(new Uint8Array(t.buffer),e),e+t.buffer.byteLength):(pt[n+6]=t.offset,mt[r+14]=t.count,mt[r+15]=Ke,e+32);{let{left:r,right:i,splitAxis:a}=t,o=yt(e+32,r),s=e/32,c=o/32-s;if(c>gt)throw Error(`MeshBVH: Cannot store relative child node offset greater than 32 bits.`);return pt[n+6]=c,pt[n+7]=a,yt(o,i)}}function bt(e,t,n,r,i,a){let{maxDepth:o,verbose:s,targetLeafSize:c,_strictLeafSize:l=1/0,strategy:u,onProgress:d}=i,f=e.primitiveBuffer,p=e.primitiveBufferStride,m=new Float32Array(6),h=!1,g=new ut;return rt(t,n,r,g.boundingData,m),v(g,n,r,m),g;function _(e){d&&d((e-a.offset)/a.count)}function v(e,n,r,i=null,a=0){!h&&a>=o&&(h=!0,s&&console.warn(`BVH: Max depth of ${o} reached when generating BVH. Consider increasing maxDepth.`));let d=r>l;if(r<=c&&!d||a>=o)return _(n+r),e.offset=n,e.count=r,e;let g=ct(e.boundingData,i,t,n,r,u),y=g.axis===-1?-1:dt(f,p,t,n,r,g);if(g.axis===-1||y===n||y===n+r){if(!d)return _(n+r),e.offset=n,e.count=r,e;g.axis=Math.max(0,Ze(e.boundingData)),y=n+Math.max(1,Math.floor(r/2))}e.splitAxis=g.axis;let b=new ut,x=n,S=y-n;e.left=b,rt(t,x,S,b.boundingData,m),v(b,x,S,m,a+1);let C=new ut,w=y,ee=r-S;return e.right=C,rt(t,w,ee,C.boundingData,m),v(C,w,ee,m,a+1),e}}function xt(e,t){let n=t.useSharedArrayBuffer?SharedArrayBuffer:ArrayBuffer,r=e.getRootRanges(t.range),i=r[0],a=r[r.length-1],o={offset:i.offset,count:a.offset+a.count-i.offset},s=new Float32Array(6*o.count);s.offset=o.offset,e.computePrimitiveBounds(o.offset,o.count,s),e._roots=r.map(r=>{let i=bt(e,s,r.offset,r.count,t,o),a=_t(i),c=new n(32*a);return vt(0,i,c),c})}var St=class{constructor(e){this._getNewPrimitive=e,this._primitives=[]}getPrimitive(){let e=this._primitives;return e.length===0?this._getNewPrimitive():e.pop()}releasePrimitive(e){this._primitives.push(e)}},F=new class{constructor(){this.float32Array=null,this.uint16Array=null,this.uint32Array=null;let e=[],t=null;this.setBuffer=n=>{t&&e.push(t),t=n,this.float32Array=new Float32Array(n),this.uint16Array=new Uint16Array(n),this.uint32Array=new Uint32Array(n)},this.clearBuffer=()=>{t=null,this.float32Array=null,this.uint16Array=null,this.uint32Array=null,e.length!==0&&this.setBuffer(e.pop())}}},Ct,wt,Tt=[],Et=new St(()=>new Ee);function Dt(e,t,n,r,i,a){Ct=Et.getPrimitive(),wt=Et.getPrimitive(),Tt.push(Ct,wt),F.setBuffer(e._roots[t]);let o=Ot(0,e.geometry,n,r,i,a);F.clearBuffer(),Et.releasePrimitive(Ct),Et.releasePrimitive(wt),Tt.pop(),Tt.pop();let s=Tt.length;return s>0&&(wt=Tt[s-1],Ct=Tt[s-2]),o}function Ot(e,t,n,r,i=null,a=0,o=0){let{float32Array:s,uint16Array:c,uint32Array:l}=F,u=e*2;if(k(u,c)){let t=A(e,l),n=j(u,c);return O(P(e),s,Ct),r(t,n,!1,o,a+e/8,Ct)}{let u=M(e),d=N(e,l),f=u,p=d,m,h,g,_;if(i&&(g=Ct,_=wt,O(P(f),s,g),O(P(p),s,_),m=i(g),h=i(_),h<m)){f=d,p=u;let e=m;m=h,h=e,g=_}g||(g=Ct,O(P(f),s,g));let v=k(f*2,c),y=n(g,v,m,o+1,a+f/8),b;if(y===2){let e=w(f);b=r(e,ee(f)-e,!0,o+1,a+f/8,g)}else b=y&&Ot(f,t,n,r,i,a,o+1);if(b)return!0;_=wt,O(P(p),s,_);let x=k(p*2,c),S=n(_,x,h,o+1,a+p/8),C;if(S===2){let e=w(p);C=r(e,ee(p)-e,!0,o+1,a+p/8,_)}else C=S&&Ot(p,t,n,r,i,a,o+1);if(C)return!0;return!1;function w(e){let{uint16Array:t,uint32Array:n}=F,r=e*2;for(;!k(r,t);)e=M(e),r=e*2;return A(e,n)}function ee(e){let{uint16Array:t,uint32Array:n}=F,r=e*2;for(;!k(r,t);)e=N(e,n),r=e*2;return A(e,n)+j(r,t)}}}var kt=new F.constructor,At=new F.constructor,jt=new St(()=>new Ee),Mt=new Ee,Nt=new Ee,Pt=new Ee,Ft=new Ee,It=!1;function Lt(e,t,n,r){if(It)throw Error(`MeshBVH: Recursive calls to bvhcast not supported.`);It=!0;let i=e._roots,a=t._roots,o,s=0,c=0,l=new f().copy(n).invert();for(let e=0,t=i.length;e<t;e++){kt.setBuffer(i[e]),c=0;let t=jt.getPrimitive();O(P(0),kt.float32Array,t),t.applyMatrix4(l);for(let e=0,i=a.length;e<i&&(At.setBuffer(a[e]),o=Rt(0,0,n,l,r,s,c,0,0,t),At.clearBuffer(),c+=a[e].byteLength/32,!o);e++);if(jt.releasePrimitive(t),kt.clearBuffer(),s+=i[e].byteLength/32,o)break}return It=!1,o}function Rt(e,t,n,r,i,a=0,o=0,s=0,c=0,l=null,u=!1){let d,f;u?(d=At,f=kt):(d=kt,f=At);let p=d.float32Array,m=d.uint32Array,h=d.uint16Array,g=f.float32Array,_=f.uint32Array,v=f.uint16Array,y=e*2,b=t*2,x=k(y,h),S=k(b,v),C=!1;if(S&&x)C=u?i(A(t,_),j(t*2,v),A(e,m),j(e*2,h),c,o+t/8,s,a+e/8):i(A(e,m),j(e*2,h),A(t,_),j(t*2,v),s,a+e/8,c,o+t/8);else if(S){let l=jt.getPrimitive();O(P(t),g,l),l.applyMatrix4(n);let d=M(e),f=N(e,m);O(P(d),p,Mt),O(P(f),p,Nt);let h=l.intersectsBox(Mt),_=l.intersectsBox(Nt);C=h&&Rt(t,d,r,n,i,o,a,c,s+1,l,!u)||_&&Rt(t,f,r,n,i,o,a,c,s+1,l,!u),jt.releasePrimitive(l)}else{let d=M(t),f=N(t,_);O(P(d),g,Pt),O(P(f),g,Ft);let h=l.intersectsBox(Pt),v=l.intersectsBox(Ft);if(h&&v)C=Rt(e,d,n,r,i,a,o,s,c+1,l,u)||Rt(e,f,n,r,i,a,o,s,c+1,l,u);else if(h){if(x)C=Rt(e,d,n,r,i,a,o,s,c+1,l,u);else{let t=jt.getPrimitive();t.copy(Pt).applyMatrix4(n);let l=M(e),f=N(e,m);O(P(l),p,Mt),O(P(f),p,Nt);let h=t.intersectsBox(Mt),g=t.intersectsBox(Nt);C=h&&Rt(d,l,r,n,i,o,a,c,s+1,t,!u)||g&&Rt(d,f,r,n,i,o,a,c,s+1,t,!u),jt.releasePrimitive(t)}}else if(v){if(x)C=Rt(e,f,n,r,i,a,o,s,c+1,l,u);else{let t=jt.getPrimitive();t.copy(Ft).applyMatrix4(n);let l=M(e),d=N(e,m);O(P(l),p,Mt),O(P(d),p,Nt);let h=t.intersectsBox(Mt),g=t.intersectsBox(Nt);C=h&&Rt(f,l,r,n,i,o,a,c,s+1,t,!u)||g&&Rt(f,d,r,n,i,o,a,c,s+1,t,!u),jt.releasePrimitive(t)}}}return C}var zt=new class{constructor(){let e=null,t=null,n=null,r=!1;this.root=null,this.buffer=null,this.uint32Array=null,this.uint16Array=null,this.setBVH=(i,a)=>{if(r)throw Error(`BVHTraversalHelper: cannot call setBVH during an active traversal.`);this.root=a,this.buffer=e=i._roots[a],this.uint16Array=n=new Uint16Array(e),this.uint32Array=t=new Uint32Array(e)},this.reset=()=>{this.root=null,this.buffer=e=null,this.uint16Array=n=null,this.uint32Array=t=null},this.getRangeStart=e=>{let r=e*2;for(;!k(r,n);)e=M(e),r=e*2;return A(e,t)},this.getRangeEnd=e=>{let r=e*2;for(;!k(r,n);)e=N(e,t),r=e*2;return A(e,t)+j(r,n)};let i=(e,r,a)=>{let o=k(r*2,n);if(!e(a,o,r)&&!o){let n=M(r),o=N(r,t);i(e,n,a+1),i(e,o,a+1)}};this.traverseBuffer=e=>{if(r)throw Error(`BVHTraversalHelper: cannot start a traversal during an active traversal.`);r=!0;try{i(e,0,0)}finally{r=!1}},this.traverse=r=>{this.traverseBuffer((i,a,o)=>{if(a){let s=o*2,c=t[o+6],l=n[s+14];return r(i,a,new Float32Array(e,o*4,6),c,l)}{let n=nt(o,t);return r(i,a,new Float32Array(e,o*4,6),n)}})}}},Bt=new Ee,Vt=new Float32Array(6),Ht=class{constructor(){this._roots=null,this.primitiveBuffer=null,this.primitiveBufferStride=null}init(e){e={...Xe,...e},`maxLeafSize`in e&&(console.warn(`BVH: "maxLeafSize" option has been deprecated. Use "targetLeafSize", instead.`),e={...e,targetLeafSize:e.maxLeafSize}),xt(this,e)}getRootRanges(){throw Error(`BVH: getRootRanges() not implemented`)}writePrimitiveBounds(){throw Error(`BVH: writePrimitiveBounds() not implemented`)}writePrimitiveRangeBounds(e,t,n,r){let i=1/0,a=1/0,o=1/0,s=-1/0,c=-1/0,l=-1/0;for(let n=e,r=e+t;n<r;n++){this.writePrimitiveBounds(n,Vt,0);let[e,t,r,u,d,f]=Vt;e<i&&(i=e),u>s&&(s=u),t<a&&(a=t),d>c&&(c=d),r<o&&(o=r),f>l&&(l=f)}return n[r+0]=i,n[r+1]=a,n[r+2]=o,n[r+3]=s,n[r+4]=c,n[r+5]=l,n}computePrimitiveBounds(e,t,n){let r=n.offset||0;for(let i=e,a=e+t;i<a;i++){this.writePrimitiveBounds(i,Vt,0);let[e,t,a,o,s,c]=Vt,l=(e+o)/2,u=(t+s)/2,d=(a+c)/2,f=(o-e)/2,p=(s-t)/2,m=(c-a)/2,h=(i-r)*6;n[h+0]=l,n[h+1]=f+(Math.abs(l)+f)*Je,n[h+2]=u,n[h+3]=p+(Math.abs(u)+p)*Je,n[h+4]=d,n[h+5]=m+(Math.abs(d)+m)*Je}return n}shiftPrimitiveOffsets(e){let t=this._indirectBuffer;if(t)for(let n=0,r=t.length;n<r;n++)t[n]+=e;else{let t=this._roots;for(let n=0;n<t.length;n++){let r=t[n],i=new Uint32Array(r),a=new Uint16Array(r),o=r.byteLength/32;for(let t=0;t<o;t++){let n=8*t;k(2*n,a)&&(i[n+6]+=e)}}}}traverse(e,t=0){zt.setBVH(this,t),zt.traverse(e),zt.reset()}refit(){let e=this._roots;for(let t=0,n=e.length;t<n;t++){let n=e[t],r=new Uint32Array(n),i=new Uint16Array(n),a=new Float32Array(n),o=n.byteLength/32;for(let e=o-1;e>=0;e--){let t=e*8,n=t*2;if(k(n,i)){let e=A(t,r),o=j(n,i);this.writePrimitiveRangeBounds(e,o,Vt,0),a.set(Vt,t)}else{let e=M(t),n=N(t,r);for(let r=0;r<3;r++){let i=a[e+r],o=a[e+r+3],s=a[n+r],c=a[n+r+3];a[t+r]=i<s?i:s,a[t+r+3]=o>c?o:c}}}}}getBoundingBox(e){return e.makeEmpty(),this._roots.forEach(t=>{O(0,new Float32Array(t),Bt),e.union(Bt)}),e}shapecast(e){let{boundsTraverseOrder:t,intersectsBounds:n,intersectsRange:r,intersectsPrimitive:i,scratchPrimitive:a,iterate:o}=e;if(r&&i){let e=r;r=(t,n,r,s,c)=>e(t,n,r,s,c)?!0:o(t,n,this,i,r,s,a)}else r||=i?(e,t,n,r)=>o(e,t,this,i,n,r,a):(e,t,n)=>n;let s=!1,c=0,l=this._roots;for(let e=0,i=l.length;e<i;e++){let i=l[e];if(s=Dt(this,e,n,r,t,c),s)break;c+=i.byteLength/32}return s}bvhcast(e,t,n){let{intersectsRanges:r}=n;return Lt(this,e,t,r)}};function Ut(){return typeof SharedArrayBuffer<`u`}function Wt(e){return e.index?e.index.count:e.attributes.position.count}function Gt(e){return Wt(e)/3}function Kt(e,t=ArrayBuffer){return e>65535?new Uint32Array(new t(4*e)):new Uint16Array(new t(2*e))}function qt(e,t){if(!e.index){let n=e.attributes.position.count,r=Kt(n,t.useSharedArrayBuffer?SharedArrayBuffer:ArrayBuffer);e.setIndex(new be(r,1));for(let e=0;e<n;e++)r[e]=e}}function Jt(e,t,n){let r=Wt(e)/n,i=t||e.drawRange,a=i.start/n,o=(i.start+i.count)/n,s=Math.max(0,a),c=Math.min(r,o)-s;return{offset:Math.floor(s),count:Math.floor(c)}}function Yt(e,t){return e.groups.map(e=>({offset:e.start/t,count:e.count/t}))}function Xt(e,t,n){let r=Jt(e,t,n),i=Yt(e,n);if(!i.length)return[r];let a=[],o=r.offset,s=r.offset+r.count,c=Wt(e)/n,l=[];for(let e of i){let{offset:t,count:n}=e,r=t,i=t+(isFinite(n)?n:c-t);r<s&&i>o&&(l.push({pos:Math.max(o,r),isStart:!0}),l.push({pos:Math.min(s,i),isStart:!1}))}l.sort((e,t)=>e.pos===t.pos?e.type===`end`?-1:1:e.pos-t.pos);let u=0,d=null;for(let e of l){let t=e.pos;u!==0&&t!==d&&a.push({offset:d,count:t-d}),u+=e.isStart?1:-1,d=t}return a}function Zt(e,t){let n=e[e.length-1],r=n.offset+n.count>2**16,i=e.reduce((e,t)=>e+t.count,0),a=r?4:2,o=t?new SharedArrayBuffer(i*a):new ArrayBuffer(i*a),s=r?new Uint32Array(o):new Uint16Array(o),c=0;for(let t=0;t<e.length;t++){let{offset:n,count:r}=e[t];for(let e=0;e<r;e++)s[c+e]=n+e;c+=r}return s}var Qt=class extends Ht{get indirect(){return!!this._indirectBuffer}get primitiveStride(){return null}get primitiveBufferStride(){return this.indirect?1:this.primitiveStride}set primitiveBufferStride(e){}get primitiveBuffer(){return this.indirect?this._indirectBuffer:this.geometry.index.array}set primitiveBuffer(e){}constructor(e,t={}){if(!e.isBufferGeometry)throw Error(`BVH: Only BufferGeometries are supported.`);if(e.index&&e.index.isInterleavedBufferAttribute)throw Error(`BVH: InterleavedBufferAttribute is not supported for the index attribute.`);if(t.useSharedArrayBuffer&&!Ut())throw Error(`BVH: SharedArrayBuffer is not available.`);super(),this.geometry=e,this.resolvePrimitiveIndex=t.indirect?e=>this._indirectBuffer[e]:e=>e,this.primitiveBuffer=null,this.primitiveBufferStride=null,this._indirectBuffer=null,t={...Xe,...t},t[Ye]||this.init(t)}init(e){let{geometry:t,primitiveStride:n}=this;if(e.indirect){let r=Zt(Xt(t,e.range,n),e.useSharedArrayBuffer);this._indirectBuffer=r}else qt(t,e);super.init(e),!t.boundingBox&&e.setBoundingBox&&(t.boundingBox=this.getBoundingBox(new Ee))}getRootRanges(e){return this.indirect?[{offset:0,count:this._indirectBuffer.length}]:Xt(this.geometry,e,this.primitiveStride)}raycastObject3D(){throw Error(`BVH: raycastObject3D() not implemented`)}},$t=class{constructor(){this.min=1/0,this.max=-1/0}setFromPointsField(e,t){let n=1/0,r=-1/0;for(let i=0,a=e.length;i<a;i++){let a=e[i][t];n=a<n?a:n,r=a>r?a:r}this.min=n,this.max=r}setFromPoints(e,t){let n=1/0,r=-1/0;for(let i=0,a=t.length;i<a;i++){let a=t[i],o=e.dot(a);n=o<n?o:n,r=o>r?o:r}this.min=n,this.max=r}isSeparated(e){return this.min>e.max||e.min>this.max}};$t.prototype.setFromBox=(function(){let e=new D;return function(t,n){let r=n.min,i=n.max,a=1/0,o=-1/0;for(let n=0;n<=1;n++)for(let s=0;s<=1;s++)for(let c=0;c<=1;c++){e.x=r.x*n+i.x*(1-n),e.y=r.y*s+i.y*(1-s),e.z=r.z*c+i.z*(1-c);let l=t.dot(e);a=Math.min(l,a),o=Math.max(l,o)}this.min=a,this.max=o}})();var en=(function(){let e=new D,t=new D,n=new D;return function(r,i,a){let o=r.start,s=e,c=i.start,l=t;n.subVectors(o,c),e.subVectors(r.end,r.start),t.subVectors(i.end,i.start);let u=n.dot(l),d=l.dot(s),f=l.dot(l),p=n.dot(s),m=s.dot(s)*f-d*d,h,g;h=m===0?0:(u*d-p*f)/m,g=(u+h*d)/f,a.x=h,a.y=g}})(),tn=(function(){let e=new E,t=new D,n=new D;return function(r,i,a,o){en(r,i,e);let s=e.x,c=e.y;if(s>=0&&s<=1&&c>=0&&c<=1){r.at(s,a),i.at(c,o);return}if(s>=0&&s<=1){c<0?i.at(0,o):i.at(1,o),r.closestPointToPoint(o,!0,a);return}if(c>=0&&c<=1){s<0?r.at(0,a):r.at(1,a),i.closestPointToPoint(a,!0,o);return}{let e;e=s<0?r.start:r.end;let l;l=c<0?i.start:i.end;let u=t,d=n;if(r.closestPointToPoint(l,!0,t),i.closestPointToPoint(e,!0,n),u.distanceToSquared(l)<=d.distanceToSquared(e)){a.copy(u),o.copy(l);return}a.copy(e),o.copy(d);return}}})(),nn=(function(){let e=new D,t=new D,n=new x,r=new p;return function(i,a){let{radius:o,center:s}=i,{a:c,b:l,c:u}=a;if(r.start=c,r.end=l,r.closestPointToPoint(s,!0,e).distanceTo(s)<=o||(r.start=c,r.end=u,r.closestPointToPoint(s,!0,e).distanceTo(s)<=o)||(r.start=l,r.end=u,r.closestPointToPoint(s,!0,e).distanceTo(s)<=o))return!0;let d=a.getPlane(n);if(Math.abs(d.distanceToPoint(s))<=o){let e=d.projectPoint(s,t);if(a.containsPoint(e))return!0}return!1}})(),rn=[`x`,`y`,`z`],an=1e-15,on=an*an;function sn(e){return Math.abs(e)<an}var cn=class extends re{constructor(...e){super(...e),this.isExtendedTriangle=!0,this.satAxes=[,,,,].fill().map(()=>new D),this.satBounds=[,,,,].fill().map(()=>new $t),this.points=[this.a,this.b,this.c],this.plane=new x,this.isDegenerateIntoSegment=!1,this.isDegenerateIntoPoint=!1,this.degenerateSegment=new p,this.needsUpdate=!0}intersectsSphere(e){return nn(e,this)}update(){let e=this.a,t=this.b,n=this.c,r=this.points,i=this.satAxes,a=this.satBounds,o=i[0],s=a[0];this.getNormal(o),s.setFromPoints(o,r);let c=i[1],l=a[1];c.subVectors(e,t),l.setFromPoints(c,r);let u=i[2],d=a[2];u.subVectors(t,n),d.setFromPoints(u,r);let f=i[3],p=a[3];f.subVectors(n,e),p.setFromPoints(f,r);let m=c.length(),h=u.length(),g=f.length();this.isDegenerateIntoPoint=!1,this.isDegenerateIntoSegment=!1,m<an?h<an||g<an?this.isDegenerateIntoPoint=!0:(this.isDegenerateIntoSegment=!0,this.degenerateSegment.start.copy(e),this.degenerateSegment.end.copy(n)):h<an?g<an?this.isDegenerateIntoPoint=!0:(this.isDegenerateIntoSegment=!0,this.degenerateSegment.start.copy(t),this.degenerateSegment.end.copy(e)):g<an&&(this.isDegenerateIntoSegment=!0,this.degenerateSegment.start.copy(n),this.degenerateSegment.end.copy(t)),this.plane.setFromNormalAndCoplanarPoint(o,e),this.needsUpdate=!1}};cn.prototype.closestPointToSegment=(function(){let e=new D,t=new D,n=new p;return function(r,i=null,a=null){let{start:o,end:s}=r,c=this.points,l,u=1/0;for(let o=0;o<3;o++){let s=(o+1)%3;n.start.copy(c[o]),n.end.copy(c[s]),tn(n,r,e,t),l=e.distanceToSquared(t),l<u&&(u=l,i&&i.copy(e),a&&a.copy(t))}return this.closestPointToPoint(o,e),l=o.distanceToSquared(e),l<u&&(u=l,i&&i.copy(e),a&&a.copy(o)),this.closestPointToPoint(s,e),l=s.distanceToSquared(e),l<u&&(u=l,i&&i.copy(e),a&&a.copy(s)),Math.sqrt(u)}})(),cn.prototype.intersectsTriangle=(function(){let e=new cn,t=new $t,n=new $t,r=new D,i=new D,a=new D,o=new D,s=new p,c=new p,l=new D,u=new E,d=new E;function f(e,i,a,s){let c=r;!e.isDegenerateIntoPoint&&!e.isDegenerateIntoSegment?c.copy(e.plane.normal):c.copy(i.plane.normal);let l=e.satBounds,u=e.satAxes;for(let r=1;r<4;r++){let a=l[r],s=u[r];if(t.setFromPoints(s,i.points),a.isSeparated(t)||(o.copy(c).cross(s),t.setFromPoints(o,e.points),n.setFromPoints(o,i.points),t.isSeparated(n)))return!1}let d=i.satBounds,f=i.satAxes;for(let r=1;r<4;r++){let a=d[r],s=f[r];if(t.setFromPoints(s,e.points),a.isSeparated(t)||(o.crossVectors(c,s),t.setFromPoints(o,e.points),n.setFromPoints(o,i.points),t.isSeparated(n)))return!1}return a&&(s||console.warn(`ExtendedTriangle.intersectsTriangle: Triangles are coplanar which does not support an output edge. Setting edge to 0, 0, 0.`),a.start.set(0,0,0),a.end.set(0,0,0)),!0}function m(e,t,n,r,i,a,o,s,c,l,u){let d=o/(o-s);l.x=r+(i-r)*d,u.start.subVectors(t,e).multiplyScalar(d).add(e),d=o/(o-c),l.y=r+(a-r)*d,u.end.subVectors(n,e).multiplyScalar(d).add(e)}function h(e,t,n,r,i,a,o,s,c,l,u){if(i>0)m(e.c,e.a,e.b,r,t,n,c,o,s,l,u);else if(a>0)m(e.b,e.a,e.c,n,t,r,s,o,c,l,u);else if(s*c>0||o!=0)m(e.a,e.b,e.c,t,n,r,o,s,c,l,u);else if(s!=0)m(e.b,e.a,e.c,n,t,r,s,o,c,l,u);else if(c!=0)m(e.c,e.a,e.b,r,t,n,c,o,s,l,u);else return!0;return!1}function g(e,t,n,i){let a=t.degenerateSegment,o=e.plane.distanceToPoint(a.start),s=e.plane.distanceToPoint(a.end);return sn(o)?sn(s)?f(e,t,n,i):(n&&(n.start.copy(a.start),n.end.copy(a.start)),e.containsPoint(a.start)):sn(s)?(n&&(n.start.copy(a.end),n.end.copy(a.end)),e.containsPoint(a.end)):e.plane.intersectLine(a,r)!=null&&(n&&(n.start.copy(r),n.end.copy(r)),e.containsPoint(r))}function _(e,t,n){let r=t.a;return sn(e.plane.distanceToPoint(r))&&e.containsPoint(r)?(n&&(n.start.copy(r),n.end.copy(r)),!0):!1}function v(e,t,n){let i=e.degenerateSegment,a=t.a;return i.closestPointToPoint(a,!0,r),a.distanceToSquared(r)<on&&(n&&(n.start.copy(a),n.end.copy(a)),!0)}function y(e,t,n,o){if(e.isDegenerateIntoSegment){if(t.isDegenerateIntoSegment){let o=e.degenerateSegment,s=t.degenerateSegment,c=i,l=a;o.delta(c),s.delta(l);let u=r.subVectors(s.start,o.start),d=c.x*l.y-c.y*l.x;if(sn(d))return!1;let f=(u.x*l.y-u.y*l.x)/d,p=-(c.x*u.y-c.y*u.x)/d;return f<0||f>1||p<0||p>1?!1:sn(o.start.z+c.z*f-(s.start.z+l.z*p))?(n&&(n.start.copy(o.start).addScaledVector(c,f),n.end.copy(o.start).addScaledVector(c,f)),!0):!1}return t.isDegenerateIntoPoint?v(e,t,n):g(t,e,n,o)}if(e.isDegenerateIntoPoint)return t.isDegenerateIntoPoint?t.a.distanceToSquared(e.a)<on&&(n&&(n.start.copy(e.a),n.end.copy(e.a)),!0):t.isDegenerateIntoSegment?v(t,e,n):_(t,e,n);if(t.isDegenerateIntoPoint)return _(e,t,n);if(t.isDegenerateIntoSegment)return g(e,t,n,o)}return function(t,n=null,r=!1){this.needsUpdate&&this.update(),t.isExtendedTriangle?t.needsUpdate&&t.update():(e.copy(t),e.update(),t=e);let o=y(this,t,n,r);if(o!==void 0)return o;let p=this.plane,m=t.plane,g=m.distanceToPoint(this.a),_=m.distanceToPoint(this.b),v=m.distanceToPoint(this.c);sn(g)&&(g=0),sn(_)&&(_=0),sn(v)&&(v=0);let b=g*_,x=g*v;if(b>0&&x>0)return!1;let S=p.distanceToPoint(t.a),C=p.distanceToPoint(t.b),w=p.distanceToPoint(t.c);sn(S)&&(S=0),sn(C)&&(C=0),sn(w)&&(w=0);let ee=S*C,te=S*w;if(ee>0&&te>0)return!1;i.copy(p.normal),a.copy(m.normal);let T=i.cross(a),ne=0,re=Math.abs(T.x),ie=Math.abs(T.y);ie>re&&(re=ie,ne=1),Math.abs(T.z)>re&&(ne=2);let ae=rn[ne],oe=this.a[ae],se=this.b[ae],ce=this.c[ae],le=t.a[ae],ue=t.b[ae],de=t.c[ae];if(h(this,oe,se,ce,b,x,g,_,v,u,s)||h(t,le,ue,de,ee,te,S,C,w,d,c))return f(this,t,n,r);if(u.y<u.x){let e=u.y;u.y=u.x,u.x=e,l.copy(s.start),s.start.copy(s.end),s.end.copy(l)}if(d.y<d.x){let e=d.y;d.y=d.x,d.x=e,l.copy(c.start),c.start.copy(c.end),c.end.copy(l)}return u.y<d.x||d.y<u.x?!1:(n&&(d.x>u.x?n.start.copy(c.start):n.start.copy(s.start),d.y<u.y?n.end.copy(c.end):n.end.copy(s.end)),!0)}})(),cn.prototype.distanceToPoint=(function(){let e=new D;return function(t){return this.closestPointToPoint(t,e),t.distanceTo(e)}})(),cn.prototype.distanceToTriangle=(function(){let e=new D,t=new D,n=[`a`,`b`,`c`],r=new p,i=new p;return function(a,o=null,s=null){let c=o||s?r:null;if(this.intersectsTriangle(a,c,!0))return(o||s)&&(o&&c.getCenter(o),s&&c.getCenter(s)),0;let l=1/0;for(let t=0;t<3;t++){let r,i=n[t],c=a[i];this.closestPointToPoint(c,e),r=c.distanceToSquared(e),r<l&&(l=r,o&&o.copy(e),s&&s.copy(c));let u=this[i];a.closestPointToPoint(u,e),r=u.distanceToSquared(e),r<l&&(l=r,o&&o.copy(u),s&&s.copy(e))}for(let c=0;c<3;c++){let u=n[c],d=n[(c+1)%3];r.set(this[u],this[d]);for(let c=0;c<3;c++){let u=n[c],d=n[(c+1)%3];i.set(a[u],a[d]),tn(r,i,e,t);let f=e.distanceToSquared(t);f<l&&(l=f,o&&o.copy(e),s&&s.copy(t))}}return Math.sqrt(l)}})();var I=class{constructor(e,t,n){this.isOrientedBox=!0,this.min=new D,this.max=new D,this.matrix=new f,this.invMatrix=new f,this.points=Array(8).fill().map(()=>new D),this.satAxes=[,,,].fill().map(()=>new D),this.satBounds=[,,,].fill().map(()=>new $t),this.alignedSatBounds=[,,,].fill().map(()=>new $t),this.needsUpdate=!1,e&&this.min.copy(e),t&&this.max.copy(t),n&&this.matrix.copy(n)}set(e,t,n){this.min.copy(e),this.max.copy(t),this.matrix.copy(n),this.needsUpdate=!0}copy(e){this.min.copy(e.min),this.max.copy(e.max),this.matrix.copy(e.matrix),this.needsUpdate=!0}};I.prototype.update=(function(){return function(){let e=this.matrix,t=this.min,n=this.max,r=this.points;for(let i=0;i<=1;i++)for(let a=0;a<=1;a++)for(let o=0;o<=1;o++){let s=r[1*i|2*a|4*o];s.x=i?n.x:t.x,s.y=a?n.y:t.y,s.z=o?n.z:t.z,s.applyMatrix4(e)}let i=this.satBounds,a=this.satAxes,o=r[0];for(let e=0;e<3;e++){let t=a[e],n=i[e],s=r[1<<e];t.subVectors(o,s),n.setFromPoints(t,r)}let s=this.alignedSatBounds;s[0].setFromPointsField(r,`x`),s[1].setFromPointsField(r,`y`),s[2].setFromPointsField(r,`z`),this.invMatrix.copy(this.matrix).invert(),this.needsUpdate=!1}})(),I.prototype.intersectsBox=(function(){let e=new $t;return function(t){this.needsUpdate&&this.update();let n=t.min,r=t.max,i=this.satBounds,a=this.satAxes,o=this.alignedSatBounds;if(e.min=n.x,e.max=r.x,o[0].isSeparated(e)||(e.min=n.y,e.max=r.y,o[1].isSeparated(e))||(e.min=n.z,e.max=r.z,o[2].isSeparated(e)))return!1;for(let n=0;n<3;n++){let r=a[n],o=i[n];if(e.setFromBox(r,t),o.isSeparated(e))return!1}return!0}})(),I.prototype.intersectsTriangle=(function(){let e=new cn,t=[,,,],n=new $t,r=new $t,i=new D;return function(a){this.needsUpdate&&this.update(),a.isExtendedTriangle?a.needsUpdate&&a.update():(e.copy(a),e.update(),a=e);let o=this.satBounds,s=this.satAxes;t[0]=a.a,t[1]=a.b,t[2]=a.c;for(let e=0;e<3;e++){let r=o[e],i=s[e];if(n.setFromPoints(i,t),r.isSeparated(n))return!1}let c=a.satBounds,l=a.satAxes,u=this.points;for(let e=0;e<3;e++){let t=c[e],r=l[e];if(n.setFromPoints(r,u),t.isSeparated(n))return!1}for(let e=0;e<3;e++){let a=s[e];for(let e=0;e<4;e++){let o=l[e];if(i.crossVectors(a,o),n.setFromPoints(i,t),r.setFromPoints(i,u),n.isSeparated(r))return!1}}return!0}})(),I.prototype.closestPointToPoint=(function(){return function(e,t){return this.needsUpdate&&this.update(),t.copy(e).applyMatrix4(this.invMatrix).clamp(this.min,this.max).applyMatrix4(this.matrix),t}})(),I.prototype.distanceToPoint=(function(){let e=new D;return function(t){return this.closestPointToPoint(t,e),t.distanceTo(e)}})(),I.prototype.distanceToBox=(function(){let e=[`x`,`y`,`z`],t=Array(12).fill().map(()=>new p),n=Array(12).fill().map(()=>new p),r=new D,i=new D;return function(a,o=0,s=null,c=null){if(this.needsUpdate&&this.update(),this.intersectsBox(a))return(s||c)&&(a.getCenter(i),this.closestPointToPoint(i,r),a.closestPointToPoint(r,i),s&&s.copy(r),c&&c.copy(i)),0;let l=o*o,u=a.min,d=a.max,f=this.points,p=1/0;for(let e=0;e<8;e++){let t=f[e];i.copy(t).clamp(u,d);let n=t.distanceToSquared(i);if(n<p&&(p=n,s&&s.copy(t),c&&c.copy(i),n<l))return Math.sqrt(n)}let m=0;for(let r=0;r<3;r++)for(let i=0;i<=1;i++)for(let a=0;a<=1;a++){let o=(r+1)%3,s=(r+2)%3,c=i<<o|a<<s,l=1<<r|i<<o|a<<s,p=f[c],h=f[l];t[m].set(p,h);let g=e[r],_=e[o],v=e[s],y=n[m],b=y.start,x=y.end;b[g]=u[g],b[_]=i?u[_]:d[_],b[v]=a?u[v]:d[_],x[g]=d[g],x[_]=i?u[_]:d[_],x[v]=a?u[v]:d[_],m++}for(let e=0;e<=1;e++)for(let t=0;t<=1;t++)for(let n=0;n<=1;n++){i.x=e?d.x:u.x,i.y=t?d.y:u.y,i.z=n?d.z:u.z,this.closestPointToPoint(i,r);let a=i.distanceToSquared(r);if(a<p&&(p=a,s&&s.copy(r),c&&c.copy(i),a<l))return Math.sqrt(a)}for(let e=0;e<12;e++){let a=t[e];for(let e=0;e<12;e++){let t=n[e];tn(a,t,r,i);let o=r.distanceToSquared(i);if(o<p&&(p=o,s&&s.copy(r),c&&c.copy(i),o<l))return Math.sqrt(o)}}return Math.sqrt(p)}})();var ln=new class extends St{constructor(){super(()=>new cn)}},un=new D,dn=new D;function fn(e,t,n={},r=0,i=1/0){let a=r*r,o=i*i,s=1/0,c=null;if(e.shapecast({boundsTraverseOrder:e=>(un.copy(t).clamp(e.min,e.max),un.distanceToSquared(t)),intersectsBounds:(e,t,n)=>n<s&&n<o,intersectsTriangle:(e,n)=>{e.closestPointToPoint(t,un);let r=t.distanceToSquared(un);return r<s&&(dn.copy(un),s=r,c=n),r<a}}),s===1/0)return null;let l=Math.sqrt(s);return n.point?n.point.copy(dn):n.point=dn.clone(),n.distance=l,n.faceIndex=c,n}var pn=!0,mn=new D,hn=new D,gn=new D,_n=new E,vn=new E,yn=new E,bn=new D,xn=new D,Sn=new D,Cn=new D;function wn(e,t,n,r,i,a,o,s){let c;if(c=a===1?e.intersectTriangle(r,n,t,!0,i):e.intersectTriangle(t,n,r,a!==2,i),c===null)return null;let l=e.origin.distanceTo(i);return l<o||l>s?null:{distance:l,point:i.clone()}}function Tn(e,t,n,r,i,a,o,s,c,l,u){mn.fromBufferAttribute(t,a),hn.fromBufferAttribute(t,o),gn.fromBufferAttribute(t,s);let d=wn(e,mn,hn,gn,Cn,c,l,u);if(d){if(r){_n.fromBufferAttribute(r,a),vn.fromBufferAttribute(r,o),yn.fromBufferAttribute(r,s),d.uv=new E;let e=re.getInterpolation(Cn,mn,hn,gn,_n,vn,yn,d.uv);pn||(d.uv=e)}if(i){_n.fromBufferAttribute(i,a),vn.fromBufferAttribute(i,o),yn.fromBufferAttribute(i,s),d.uv1=new E;let e=re.getInterpolation(Cn,mn,hn,gn,_n,vn,yn,d.uv1);pn||(d.uv1=e)}if(n){bn.fromBufferAttribute(n,a),xn.fromBufferAttribute(n,o),Sn.fromBufferAttribute(n,s),d.normal=new D;let t=re.getInterpolation(Cn,mn,hn,gn,bn,xn,Sn,d.normal);d.normal.dot(e.direction)>0&&d.normal.multiplyScalar(-1),pn||(d.normal=t)}let t={a,b:o,c:s,normal:new D,materialIndex:0};if(re.getNormal(mn,hn,gn,t.normal),d.face=t,d.faceIndex=a,pn){let e=new D;re.getBarycoord(Cn,mn,hn,gn,e),d.barycoord=e}}return d}function En(e){return e&&e.isMaterial?e.side:e}function Dn(e,t,n,r,i,a,o){let s=r*3,c=s+0,l=s+1,u=s+2,{index:d,groups:f}=e;e.index&&(c=d.getX(c),l=d.getX(l),u=d.getX(u));let{position:p,normal:m,uv:h,uv1:g}=e.attributes;if(Array.isArray(t)){let e=r*3;for(let s=0,d=f.length;s<d;s++){let{start:d,count:_,materialIndex:v}=f[s];if(e>=d&&e<d+_){let e=En(t[v]),s=Tn(n,p,m,h,g,c,l,u,e,a,o);if(s){if(s.faceIndex=r,s.face.materialIndex=v,i)i.push(s);else return s}}}}else{let e=En(t),s=Tn(n,p,m,h,g,c,l,u,e,a,o);if(s){if(s.faceIndex=r,s.face.materialIndex=0,i)i.push(s);else return s}}return null}function L(e,t,n,r){let i=e.a,a=e.b,o=e.c,s=t,c=t+1,l=t+2;n&&(s=n.getX(s),c=n.getX(c),l=n.getX(l)),i.x=r.getX(s),i.y=r.getY(s),i.z=r.getZ(s),a.x=r.getX(c),a.y=r.getY(c),a.z=r.getZ(c),o.x=r.getX(l),o.y=r.getY(l),o.z=r.getZ(l)}function On(e,t,n,r,i,a,o,s){let{geometry:c,_indirectBuffer:l}=e;for(let e=r,l=r+i;e<l;e++)Dn(c,t,n,e,a,o,s)}function kn(e,t,n,r,i,a,o){let{geometry:s,_indirectBuffer:c}=e,l=1/0,u=null;for(let e=r,c=r+i;e<c;e++){let r;r=Dn(s,t,n,e,null,a,o),r&&r.distance<l&&(u=r,l=r.distance)}return u}function An(e,t,n,r,i,a,o){let{geometry:s}=n,{index:c}=s,l=s.attributes.position;for(let n=e,s=t+e;n<s;n++){let e;if(e=n,L(o,e*3,c,l),o.needsUpdate=!0,r(o,e,i,a))return!0}return!1}function jn(e,t=null){t&&Array.isArray(t)&&(t=new Set(t));let n=e.geometry,r=n.index?n.index.array:null,i=n.attributes.position,a,o,s,c,l=0,u=e._roots;for(let e=0,t=u.length;e<t;e++)a=u[e],o=new Uint32Array(a),s=new Uint16Array(a),c=new Float32Array(a),d(0,l),l+=a.byteLength;function d(e,n,a=!1){let l=e*2;if(k(l,s)){let t=A(e,o),n=j(l,s),a=1/0,u=1/0,d=1/0,f=-1/0,p=-1/0,m=-1/0;for(let e=3*t,o=3*(t+n);e<o;e++){let t=r[e],n=i.getX(t),o=i.getY(t),s=i.getZ(t);n<a&&(a=n),n>f&&(f=n),o<u&&(u=o),o>p&&(p=o),s<d&&(d=s),s>m&&(m=s)}return c[e+0]!==a||c[e+1]!==u||c[e+2]!==d||c[e+3]!==f||c[e+4]!==p||c[e+5]!==m?(c[e+0]=a,c[e+1]=u,c[e+2]=d,c[e+3]=f,c[e+4]=p,c[e+5]=m,!0):!1}{let r=M(e),i=N(e,o),s=a,l=!1,u=!1;if(t){if(!s){let e=r/8+n/32,a=i/8+n/32;l=t.has(e),u=t.has(a),s=!l&&!u}}else l=!0,u=!0;let f=s||l,p=s||u,m=!1;f&&(m=d(r,n,s));let h=!1;p&&(h=d(i,n,s));let g=m||h;if(g)for(let t=0;t<3;t++){let n=r+t,a=i+t,o=c[n],s=c[n+3],l=c[a],u=c[a+3];c[e+t]=o<l?o:l,c[e+t+3]=s>u?s:u}return g}}}function Mn(e,t,n,r,i){let a,o,s,c,l,u,d=1/n.direction.x,f=1/n.direction.y,p=1/n.direction.z,m=n.origin.x,h=n.origin.y,g=n.origin.z,_=t[e],v=t[e+3],y=t[e+1],b=t[e+3+1],x=t[e+2],S=t[e+3+2];return d>=0?(a=(_-m)*d,o=(v-m)*d):(a=(v-m)*d,o=(_-m)*d),f>=0?(s=(y-h)*f,c=(b-h)*f):(s=(b-h)*f,c=(y-h)*f),a>c||s>o||((s>a||isNaN(a))&&(a=s),(c<o||isNaN(o))&&(o=c),p>=0?(l=(x-g)*p,u=(S-g)*p):(l=(S-g)*p,u=(x-g)*p),a>u||l>o)?!1:((l>a||a!==a)&&(a=l),(u<o||o!==o)&&(o=u),a<=i&&o>=r)}function Nn(e,t,n,r,i,a,o,s){let{geometry:c,_indirectBuffer:l}=e;for(let e=r,u=r+i;e<u;e++)Dn(c,t,n,l?l[e]:e,a,o,s)}function Pn(e,t,n,r,i,a,o){let{geometry:s,_indirectBuffer:c}=e,l=1/0,u=null;for(let e=r,d=r+i;e<d;e++){let r;r=Dn(s,t,n,c?c[e]:e,null,a,o),r&&r.distance<l&&(u=r,l=r.distance)}return u}function Fn(e,t,n,r,i,a,o){let{geometry:s}=n,{index:c}=s,l=s.attributes.position;for(let s=e,u=t+e;s<u;s++){let e;if(e=n.resolveTriangleIndex(s),L(o,e*3,c,l),o.needsUpdate=!0,r(o,e,i,a))return!0}return!1}function In(e,t,n,r,i,a,o){F.setBuffer(e._roots[t]),Ln(0,e,n,r,i,a,o),F.clearBuffer()}function Ln(e,t,n,r,i,a,o){let{float32Array:s,uint16Array:c,uint32Array:l}=F,u=e*2;if(k(u,c))On(t,n,r,A(e,l),j(u,c),i,a,o);else{let c=M(e);Mn(c,s,r,a,o)&&Ln(c,t,n,r,i,a,o);let u=N(e,l);Mn(u,s,r,a,o)&&Ln(u,t,n,r,i,a,o)}}var Rn=[`x`,`y`,`z`];function zn(e,t,n,r,i,a){F.setBuffer(e._roots[t]);let o=Bn(0,e,n,r,i,a);return F.clearBuffer(),o}function Bn(e,t,n,r,i,a){let{float32Array:o,uint16Array:s,uint32Array:c}=F,l=e*2;if(k(l,s))return kn(t,n,r,A(e,c),j(l,s),i,a);{let s=nt(e,c),l=Rn[s],u=r.direction[l]>=0,d,f;u?(d=M(e),f=N(e,c)):(d=N(e,c),f=M(e));let p=Mn(d,o,r,i,a)?Bn(d,t,n,r,i,a):null;if(p){let e=p.point[l];if(u?e<=o[f+s]:e>=o[f+s+3])return p}let m=Mn(f,o,r,i,a)?Bn(f,t,n,r,i,a):null;return p&&m?p.distance<=m.distance?p:m:p||m||null}}var Vn=new Ee,Hn=new cn,Un=new cn,Wn=new f,Gn=new I,Kn=new I;function qn(e,t,n,r){F.setBuffer(e._roots[t]);let i=Jn(0,e,n,r);return F.clearBuffer(),i}function Jn(e,t,n,r,i=null){let{float32Array:a,uint16Array:o,uint32Array:s}=F,c=e*2;if(i===null&&(n.boundingBox||n.computeBoundingBox(),Gn.set(n.boundingBox.min,n.boundingBox.max,r),i=Gn),k(c,o)){let i=t.geometry,l=i.index,u=i.attributes.position,d=n.index,f=n.attributes.position,p=A(e,s),m=j(c,o);if(Wn.copy(r).invert(),n.boundsTree)return O(P(e),a,Kn),Kn.matrix.copy(Wn),Kn.needsUpdate=!0,n.boundsTree.shapecast({intersectsBounds:e=>Kn.intersectsBox(e),intersectsTriangle:e=>{e.a.applyMatrix4(r),e.b.applyMatrix4(r),e.c.applyMatrix4(r),e.needsUpdate=!0;for(let t=p*3,n=(m+p)*3;t<n;t+=3)if(L(Un,t,l,u),Un.needsUpdate=!0,e.intersectsTriangle(Un))return!0;return!1}});{let e=Gt(n);for(let t=p*3,n=(m+p)*3;t<n;t+=3){L(Hn,t,l,u),Hn.a.applyMatrix4(Wn),Hn.b.applyMatrix4(Wn),Hn.c.applyMatrix4(Wn),Hn.needsUpdate=!0;for(let t=0,n=e*3;t<n;t+=3)if(L(Un,t,d,f),Un.needsUpdate=!0,Hn.intersectsTriangle(Un))return!0}}}else{let o=M(e),c=N(e,s);return O(P(o),a,Vn),!!(i.intersectsBox(Vn)&&Jn(o,t,n,r,i)||(O(P(c),a,Vn),i.intersectsBox(Vn)&&Jn(c,t,n,r,i)))}}var Yn=new f,Xn=new I,Zn=new I,Qn=new D,$n=new D,er=new D,tr=new D;function nr(e,t,n,r={},i={},a=0,o=1/0){t.boundingBox||t.computeBoundingBox(),Xn.set(t.boundingBox.min,t.boundingBox.max,n),Xn.needsUpdate=!0;let s=e.geometry,c=s.attributes.position,l=s.index,u=t.attributes.position,d=t.index,f=ln.getPrimitive(),p=ln.getPrimitive(),m=Qn,h=$n,g=null,_=null;i&&(g=er,_=tr);let v=1/0,y=null,b=null;return Yn.copy(n).invert(),Zn.matrix.copy(Yn),e.shapecast({boundsTraverseOrder:e=>Xn.distanceToBox(e),intersectsBounds:(e,t,n)=>n<v&&n<o&&(t&&(Zn.min.copy(e.min),Zn.max.copy(e.max),Zn.needsUpdate=!0),!0),intersectsRange:(e,r)=>{if(t.boundsTree)return t.boundsTree.shapecast({boundsTraverseOrder:e=>Zn.distanceToBox(e),intersectsBounds:(e,t,n)=>n<v&&n<o,intersectsRange:(t,i)=>{for(let o=t,s=t+i;o<s;o++){L(p,3*o,d,u),p.a.applyMatrix4(n),p.b.applyMatrix4(n),p.c.applyMatrix4(n),p.needsUpdate=!0;for(let t=e,n=e+r;t<n;t++){L(f,3*t,l,c),f.needsUpdate=!0;let e=f.distanceToTriangle(p,m,g);if(e<v&&(h.copy(m),_&&_.copy(g),v=e,y=t,b=o),e<a)return!0}}}});{let i=Gt(t);for(let t=0,o=i;t<o;t++){L(p,3*t,d,u),p.a.applyMatrix4(n),p.b.applyMatrix4(n),p.c.applyMatrix4(n),p.needsUpdate=!0;for(let n=e,i=e+r;n<i;n++){L(f,3*n,l,c),f.needsUpdate=!0;let e=f.distanceToTriangle(p,m,g);if(e<v&&(h.copy(m),_&&_.copy(g),v=e,y=n,b=t),e<a)return!0}}}}}),ln.releasePrimitive(f),ln.releasePrimitive(p),v===1/0?null:(r.point?r.point.copy(h):r.point=h.clone(),r.distance=v,r.faceIndex=y,i&&(i.point?i.point.copy(_):i.point=_.clone(),i.point.applyMatrix4(Yn),h.applyMatrix4(Yn),i.distance=h.sub(i.point).length(),i.faceIndex=b),r)}function rr(e,t=null){t&&Array.isArray(t)&&(t=new Set(t));let n=e.geometry,r=n.index?n.index.array:null,i=n.attributes.position,a,o,s,c,l=0,u=e._roots;for(let e=0,t=u.length;e<t;e++)a=u[e],o=new Uint32Array(a),s=new Uint16Array(a),c=new Float32Array(a),d(0,l),l+=a.byteLength;function d(n,a,l=!1){let u=n*2;if(k(u,s)){let t=A(n,o),a=j(u,s),l=1/0,d=1/0,f=1/0,p=-1/0,m=-1/0,h=-1/0;for(let n=t,o=t+a;n<o;n++){let t=3*e.resolveTriangleIndex(n);for(let e=0;e<3;e++){let n=t+e;n=r?r[n]:n;let a=i.getX(n),o=i.getY(n),s=i.getZ(n);a<l&&(l=a),a>p&&(p=a),o<d&&(d=o),o>m&&(m=o),s<f&&(f=s),s>h&&(h=s)}}return c[n+0]!==l||c[n+1]!==d||c[n+2]!==f||c[n+3]!==p||c[n+4]!==m||c[n+5]!==h?(c[n+0]=l,c[n+1]=d,c[n+2]=f,c[n+3]=p,c[n+4]=m,c[n+5]=h,!0):!1}{let e=M(n),r=N(n,o),i=l,s=!1,u=!1;if(t){if(!i){let n=e/8+a/32,o=r/8+a/32;s=t.has(n),u=t.has(o),i=!s&&!u}}else s=!0,u=!0;let f=i||s,p=i||u,m=!1;f&&(m=d(e,a,i));let h=!1;p&&(h=d(r,a,i));let g=m||h;if(g)for(let t=0;t<3;t++){let i=e+t,a=r+t,o=c[i],s=c[i+3],l=c[a],u=c[a+3];c[n+t]=o<l?o:l,c[n+t+3]=s>u?s:u}return g}}}function ir(e,t,n,r,i,a,o){F.setBuffer(e._roots[t]),ar(0,e,n,r,i,a,o),F.clearBuffer()}function ar(e,t,n,r,i,a,o){let{float32Array:s,uint16Array:c,uint32Array:l}=F,u=e*2;if(k(u,c))Nn(t,n,r,A(e,l),j(u,c),i,a,o);else{let c=M(e);Mn(c,s,r,a,o)&&ar(c,t,n,r,i,a,o);let u=N(e,l);Mn(u,s,r,a,o)&&ar(u,t,n,r,i,a,o)}}var or=[`x`,`y`,`z`];function sr(e,t,n,r,i,a){F.setBuffer(e._roots[t]);let o=cr(0,e,n,r,i,a);return F.clearBuffer(),o}function cr(e,t,n,r,i,a){let{float32Array:o,uint16Array:s,uint32Array:c}=F,l=e*2;if(k(l,s))return Pn(t,n,r,A(e,c),j(l,s),i,a);{let s=nt(e,c),l=or[s],u=r.direction[l]>=0,d,f;u?(d=M(e),f=N(e,c)):(d=N(e,c),f=M(e));let p=Mn(d,o,r,i,a)?cr(d,t,n,r,i,a):null;if(p){let e=p.point[l];if(u?e<=o[f+s]:e>=o[f+s+3])return p}let m=Mn(f,o,r,i,a)?cr(f,t,n,r,i,a):null;return p&&m?p.distance<=m.distance?p:m:p||m||null}}var lr=new Ee,ur=new cn,dr=new cn,fr=new f,pr=new I,mr=new I;function hr(e,t,n,r){F.setBuffer(e._roots[t]);let i=gr(0,e,n,r);return F.clearBuffer(),i}function gr(e,t,n,r,i=null){let{float32Array:a,uint16Array:o,uint32Array:s}=F,c=e*2;if(i===null&&(n.boundingBox||n.computeBoundingBox(),pr.set(n.boundingBox.min,n.boundingBox.max,r),i=pr),k(c,o)){let i=t.geometry,l=i.index,u=i.attributes.position,d=n.index,f=n.attributes.position,p=A(e,s),m=j(c,o);if(fr.copy(r).invert(),n.boundsTree)return O(P(e),a,mr),mr.matrix.copy(fr),mr.needsUpdate=!0,n.boundsTree.shapecast({intersectsBounds:e=>mr.intersectsBox(e),intersectsTriangle:e=>{e.a.applyMatrix4(r),e.b.applyMatrix4(r),e.c.applyMatrix4(r),e.needsUpdate=!0;for(let n=p,r=m+p;n<r;n++)if(L(dr,3*t.resolveTriangleIndex(n),l,u),dr.needsUpdate=!0,e.intersectsTriangle(dr))return!0;return!1}});{let e=Gt(n);for(let n=p,r=m+p;n<r;n++){L(ur,3*t.resolveTriangleIndex(n),l,u),ur.a.applyMatrix4(fr),ur.b.applyMatrix4(fr),ur.c.applyMatrix4(fr),ur.needsUpdate=!0;for(let t=0,n=e*3;t<n;t+=3)if(L(dr,t,d,f),dr.needsUpdate=!0,ur.intersectsTriangle(dr))return!0}}}else{let o=M(e),c=N(e,s);return O(P(o),a,lr),!!(i.intersectsBox(lr)&&gr(o,t,n,r,i)||(O(P(c),a,lr),i.intersectsBox(lr)&&gr(c,t,n,r,i)))}}var _r=new f,vr=new I,yr=new I,br=new D,xr=new D,Sr=new D,Cr=new D;function wr(e,t,n,r={},i={},a=0,o=1/0){t.boundingBox||t.computeBoundingBox(),vr.set(t.boundingBox.min,t.boundingBox.max,n),vr.needsUpdate=!0;let s=e.geometry,c=s.attributes.position,l=s.index,u=t.attributes.position,d=t.index,f=ln.getPrimitive(),p=ln.getPrimitive(),m=br,h=xr,g=null,_=null;i&&(g=Sr,_=Cr);let v=1/0,y=null,b=null;return _r.copy(n).invert(),yr.matrix.copy(_r),e.shapecast({boundsTraverseOrder:e=>vr.distanceToBox(e),intersectsBounds:(e,t,n)=>n<v&&n<o&&(t&&(yr.min.copy(e.min),yr.max.copy(e.max),yr.needsUpdate=!0),!0),intersectsRange:(r,i)=>{if(t.boundsTree){let s=t.boundsTree;return s.shapecast({boundsTraverseOrder:e=>yr.distanceToBox(e),intersectsBounds:(e,t,n)=>n<v&&n<o,intersectsRange:(t,o)=>{for(let x=t,S=t+o;x<S;x++){let t=s.resolveTriangleIndex(x);L(p,3*t,d,u),p.a.applyMatrix4(n),p.b.applyMatrix4(n),p.c.applyMatrix4(n),p.needsUpdate=!0;for(let t=r,n=r+i;t<n;t++){let n=e.resolveTriangleIndex(t);L(f,3*n,l,c),f.needsUpdate=!0;let r=f.distanceToTriangle(p,m,g);if(r<v&&(h.copy(m),_&&_.copy(g),v=r,y=t,b=x),r<a)return!0}}}})}{let o=Gt(t);for(let t=0,s=o;t<s;t++){L(p,3*t,d,u),p.a.applyMatrix4(n),p.b.applyMatrix4(n),p.c.applyMatrix4(n),p.needsUpdate=!0;for(let n=r,o=r+i;n<o;n++){let r=e.resolveTriangleIndex(n);L(f,3*r,l,c),f.needsUpdate=!0;let i=f.distanceToTriangle(p,m,g);if(i<v&&(h.copy(m),_&&_.copy(g),v=i,y=n,b=t),i<a)return!0}}}}}),ln.releasePrimitive(f),ln.releasePrimitive(p),v===1/0?null:(r.point?r.point.copy(h):r.point=h.clone(),r.distance=v,r.faceIndex=y,i&&(i.point?i.point.copy(_):i.point=_.clone(),i.point.applyMatrix4(_r),h.applyMatrix4(_r),i.distance=h.sub(i.point).length(),i.faceIndex=b),r)}function Tr(e,t,n){return e===null?null:(e.point.applyMatrix4(t.matrixWorld),e.distance=e.point.distanceTo(n.ray.origin),e.object=t,e)}var Er=new I,Dr=new w,Or=new D,kr=new f,Ar=new D,jr=[`getX`,`getY`,`getZ`],Mr=class e extends Qt{static serialize(e,t={}){t={cloneBuffers:!0,...t};let n=e.geometry,r=e._roots,i=e._indirectBuffer,a=n.getIndex(),o={version:1,roots:null,index:null,indirectBuffer:null};return t.cloneBuffers?(o.roots=r.map(e=>e.slice()),o.index=a?a.array.slice():null,o.indirectBuffer=i?i.slice():null):(o.roots=r,o.index=a?a.array:null,o.indirectBuffer=i),o}static deserialize(t,n,r={}){r={setIndex:!0,indirect:!!t.indirectBuffer,...r};let{index:i,roots:a,indirectBuffer:o}=t;t.version||(console.warn(`MeshBVH.deserialize: Serialization format has been changed and will be fixed up. It is recommended to regenerate any stored serialized data.`),c(a));let s=new e(n,{...r,[Ye]:!0});if(s._roots=a,s._indirectBuffer=o||null,r.setIndex){let e=n.getIndex();if(e===null){let e=new be(t.index,1,!1);n.setIndex(e)}else e.array!==i&&(e.array.set(i),e.needsUpdate=!0)}return s;function c(e){for(let t=0;t<e.length;t++){let n=e[t],r=new Uint32Array(n),i=new Uint16Array(n);for(let e=0,t=n.byteLength/32;e<t;e++){let t=8*e;k(2*t,i)||(r[t+6]=r[t+6]/8-e)}}}}get primitiveStride(){return 3}get resolveTriangleIndex(){return this.resolvePrimitiveIndex}constructor(e,t={}){t.maxLeafTris&&(console.warn(`MeshBVH: "maxLeafTris" option has been deprecated. Use "targetLeafSize", instead.`),t={...t,targetLeafSize:t.maxLeafTris}),super(e,t)}shiftTriangleOffsets(e){return super.shiftPrimitiveOffsets(e)}writePrimitiveBounds(e,t,n){let r=this.geometry,i=this._indirectBuffer,a=r.attributes.position,o=r.index?r.index.array:null,s=(i?i[e]:e)*3,c=s+0,l=s+1,u=s+2;o&&(c=o[c],l=o[l],u=o[u]);for(let e=0;e<3;e++){let r=a[jr[e]](c),i=a[jr[e]](l),o=a[jr[e]](u),s=r;i<s&&(s=i),o<s&&(s=o);let d=r;i>d&&(d=i),o>d&&(d=o),t[n+e]=s,t[n+e+3]=d}return t}computePrimitiveBounds(e,t,n){let r=this.geometry,i=this._indirectBuffer,a=r.attributes.position,o=r.index?r.index.array:null,s=a.normalized;if(e<0||t+e-n.offset>n.length/6)throw Error(`MeshBVH: compute triangle bounds range is invalid.`);let c=a.array,l=a.offset||0,u=3;a.isInterleavedBufferAttribute&&(u=a.data.stride);let d=[`getX`,`getY`,`getZ`],f=n.offset;for(let r=e,p=e+t;r<p;r++){let e=(i?i[r]:r)*3,t=(r-f)*6,p=e+0,m=e+1,h=e+2;o&&(p=o[p],m=o[m],h=o[h]),s||(p=p*u+l,m=m*u+l,h=h*u+l);for(let e=0;e<3;e++){let r,i,o;s?(r=a[d[e]](p),i=a[d[e]](m),o=a[d[e]](h)):(r=c[p+e],i=c[m+e],o=c[h+e]);let l=r;i<l&&(l=i),o<l&&(l=o);let u=r;i>u&&(u=i),o>u&&(u=o);let f=(u-l)/2,g=e*2;n[t+g+0]=l+f,n[t+g+1]=f+(Math.abs(l)+f)*Je}}return n}raycastObject3D(e,t,n=[]){let{material:r}=e;if(r===void 0)return;kr.copy(e.matrixWorld).invert(),Dr.copy(t.ray).applyMatrix4(kr),Ar.setFromMatrixScale(e.matrixWorld),Or.copy(Dr.direction).multiply(Ar);let i=Or.length(),a=t.near/i,o=t.far/i;if(t.firstHitOnly===!0){let i=this.raycastFirst(Dr,r,a,o);i=Tr(i,e,t),i&&n.push(i)}else{let i=this.raycast(Dr,r,a,o);for(let r=0,a=i.length;r<a;r++){let a=Tr(i[r],e,t);a&&n.push(a)}}return n}refit(e=null){return(this.indirect?rr:jn)(this,e)}raycast(e,t=0,n=0,r=1/0){let i=this._roots,a=[],o=this.indirect?ir:In;for(let s=0,c=i.length;s<c;s++)o(this,s,t,e,a,n,r);return a}raycastFirst(e,t=0,n=0,r=1/0){let i=this._roots,a=null,o=this.indirect?sr:zn;for(let s=0,c=i.length;s<c;s++){let i=o(this,s,t,e,n,r);i!=null&&(a==null||i.distance<a.distance)&&(a=i)}return a}intersectsGeometry(e,t){let n=!1,r=this._roots,i=this.indirect?hr:qn;for(let a=0,o=r.length;a<o&&(n=i(this,a,e,t),!n);a++);return n}shapecast(e){let t=ln.getPrimitive(),n=super.shapecast({...e,intersectsPrimitive:e.intersectsTriangle,scratchPrimitive:t,iterate:this.indirect?Fn:An});return ln.releasePrimitive(t),n}bvhcast(t,n,r){let{intersectsRanges:i,intersectsTriangles:a}=r,o=ln.getPrimitive(),s=this.geometry.index,c=this.geometry.attributes.position,l=this.indirect?e=>{let t=this.resolveTriangleIndex(e);L(o,t*3,s,c)}:e=>{L(o,e*3,s,c)},u=ln.getPrimitive(),d=t.geometry.index,f=t.geometry.attributes.position,p=t.indirect?e=>{let n=t.resolveTriangleIndex(e);L(u,n*3,d,f)}:e=>{L(u,e*3,d,f)};if(a){if(!(t instanceof e))throw Error(`MeshBVH: "intersectsTriangles" callback can only be used with another MeshBVH.`);let r=(e,t,r,i,s,c,d,f)=>{for(let m=r,h=r+i;m<h;m++){p(m),u.a.applyMatrix4(n),u.b.applyMatrix4(n),u.c.applyMatrix4(n),u.needsUpdate=!0;for(let n=e,r=e+t;n<r;n++)if(l(n),o.needsUpdate=!0,a(o,u,n,m,s,c,d,f))return!0}return!1};if(i){let e=i;i=function(t,n,i,a,o,s,c,l){return e(t,n,i,a,o,s,c,l)?!0:r(t,n,i,a,o,s,c,l)}}else i=r}return super.bvhcast(t,n,{intersectsRanges:i})}intersectsBox(e,t){return Er.set(e.min,e.max,t),Er.needsUpdate=!0,this.shapecast({intersectsBounds:e=>Er.intersectsBox(e),intersectsTriangle:e=>Er.intersectsTriangle(e)})}intersectsSphere(e){return this.shapecast({intersectsBounds:t=>e.intersectsBox(t),intersectsTriangle:t=>t.intersectsSphere(e)})}closestPointToGeometry(e,t,n={},r={},i=0,a=1/0){return(this.indirect?wr:nr)(this,e,t,n,r,i,a)}closestPointToPoint(e,t={},n=0,r=1/0){return fn(this,e,t,n,r)}};function Nr(e){switch(e){case 1:return`R`;case 2:return`RG`;case 3:return`RGBA`;case 4:return`RGBA`}throw Error()}function Pr(e){switch(e){case 1:return te;case 2:return u;case 3:return d;case 4:return d}}function Fr(e){switch(e){case 1:return _;case 2:return ee;case 3:return Oe;case 4:return Oe}}var Ir=class extends Re{constructor(){super(),this.minFilter=S,this.magFilter=S,this.generateMipmaps=!1,this.overrideItemSize=null,this._forcedType=null}updateFrom(e){let t=this.overrideItemSize,n=e.itemSize,r=e.count;if(t!==null){if(n*r%t!==0)throw Error(`VertexAttributeTexture: overrideItemSize must divide evenly into buffer length.`);e.itemSize=t,e.count=r*n/t}let i=e.itemSize,a=e.count,o=e.normalized,s=e.array.constructor,c=s.BYTES_PER_ELEMENT,l=this._forcedType,u=i;if(l===null)switch(s){case Float32Array:l=b;break;case Uint8Array:case Uint16Array:case Uint32Array:l=Me;break;case Int8Array:case Int16Array:case Int32Array:l=_e}let d,f,p,m,h=Nr(i);switch(l){case b:p=1,f=Pr(i),o&&c===1?(m=s,h+=`8`,s===Uint8Array?d=Te:(d=fe,h+=`_SNORM`)):(m=Float32Array,h+=`32F`,d=b);break;case _e:h+=c*8+`I`,p=o?2**(s.BYTES_PER_ELEMENT*8-1):1,f=Fr(i),c===1?(m=Int8Array,d=fe):c===2?(m=Int16Array,d=Ce):(m=Int32Array,d=_e);break;case Me:h+=c*8+`UI`,p=o?2**(s.BYTES_PER_ELEMENT*8-1):1,f=Fr(i),c===1?(m=Uint8Array,d=Te):c===2?(m=Uint16Array,d=oe):(m=Uint32Array,d=Me)}u===3&&(f===1023||f===1033)&&(u=4);let g=Math.ceil(Math.sqrt(a))||1,_=u*g*g,v=new m(_),y=e.normalized;e.normalized=!1;for(let t=0;t<a;t++){let n=u*t;v[n]=e.getX(t)/p,i>=2&&(v[n+1]=e.getY(t)/p),i>=3&&(v[n+2]=e.getZ(t)/p,u===4&&(v[n+3]=1)),i>=4&&(v[n+3]=e.getW(t)/p)}e.normalized=y,this.internalFormat=h,this.format=f,this.type=d,this.image.width=g,this.image.height=g,this.image.data=v,this.needsUpdate=!0,this.dispose(),e.itemSize=n,e.count=r}},Lr=class extends Ir{constructor(){super(),this._forcedType=Me}},Rr=class extends Ir{constructor(){super(),this._forcedType=b}},zr=class{constructor(){this.index=new Lr,this.position=new Rr,this.bvhBounds=new Re,this.bvhContents=new Re,this._cachedIndexAttr=null,this.index.overrideItemSize=3}updateFrom(e){let{geometry:t}=e;if(Vr(e,this.bvhBounds,this.bvhContents),this.position.updateFrom(t.attributes.position),e.indirect){let n=e._indirectBuffer;if(this._cachedIndexAttr===null||this._cachedIndexAttr.count!==n.length){if(t.index)this._cachedIndexAttr=t.index.clone();else{let e=Kt(Wt(t));this._cachedIndexAttr=new be(e,1,!1)}}Br(t,n,this._cachedIndexAttr),this.index.updateFrom(this._cachedIndexAttr)}else this.index.updateFrom(t.index)}dispose(){let{index:e,position:t,bvhBounds:n,bvhContents:r}=this;e&&e.dispose(),t&&t.dispose(),n&&n.dispose(),r&&r.dispose()}};function Br(e,t,n){let r=n.array,i=e.index?e.index.array:null;for(let e=0,n=t.length;e<n;e++){let n=3*e,a=3*t[e];for(let e=0;e<3;e++)r[n+e]=i?i[a+e]:a+e}}function Vr(e,t,n){let r=e._roots;if(r.length!==1)throw Error(`MeshBVHUniformStruct: Multi-root BVHs not supported.`);let i=r[0],a=new Uint16Array(i),o=new Uint32Array(i),s=new Float32Array(i),c=i.byteLength/32,l=2*Math.ceil(Math.sqrt(c/2)),u=new Float32Array(4*l*l),f=Math.ceil(Math.sqrt(c)),p=new Uint32Array(2*f*f);for(let e=0;e<c;e++){let t=e*32/4,n=t*2,r=P(t);for(let t=0;t<3;t++)u[8*e+0+t]=s[r+0+t],u[8*e+4+t]=s[r+3+t];if(k(n,a)){let r=j(n,a),i=A(t,o),s=qe|r;p[e*2+0]=s,p[e*2+1]=i}else{let n=o[t+6],r=nt(t,o);p[e*2+0]=r,p[e*2+1]=n}}t.image.data=u,t.image.width=l,t.image.height=l,t.format=d,t.type=b,t.internalFormat=`RGBA32F`,t.minFilter=S,t.magFilter=S,t.generateMipmaps=!1,t.needsUpdate=!0,t.dispose(),n.image.data=p,n.image.width=f,n.image.height=f,n.format=ee,n.type=Me,n.internalFormat=`RG32UI`,n.minFilter=S,n.magFilter=S,n.generateMipmaps=!1,n.needsUpdate=!0,n.dispose()}var Hr=`
2
+
3
+ // A stack of uint32 indices can can store the indices for
4
+ // a perfectly balanced tree with a depth up to 31. Lower stack
5
+ // depth gets higher performance.
6
+ //
7
+ // However not all trees are balanced. Best value to set this to
8
+ // is the trees max depth.
9
+ #ifndef BVH_STACK_DEPTH
10
+ #define BVH_STACK_DEPTH 60
11
+ #endif
12
+
13
+ #ifndef INFINITY
14
+ #define INFINITY 1e20
15
+ #endif
16
+
17
+ // Utilities
18
+ uvec4 uTexelFetch1D( usampler2D tex, uint index ) {
19
+
20
+ uint width = uint( textureSize( tex, 0 ).x );
21
+ uvec2 uv;
22
+ uv.x = index % width;
23
+ uv.y = index / width;
24
+
25
+ return texelFetch( tex, ivec2( uv ), 0 );
26
+
27
+ }
28
+
29
+ ivec4 iTexelFetch1D( isampler2D tex, uint index ) {
30
+
31
+ uint width = uint( textureSize( tex, 0 ).x );
32
+ uvec2 uv;
33
+ uv.x = index % width;
34
+ uv.y = index / width;
35
+
36
+ return texelFetch( tex, ivec2( uv ), 0 );
37
+
38
+ }
39
+
40
+ vec4 texelFetch1D( sampler2D tex, uint index ) {
41
+
42
+ uint width = uint( textureSize( tex, 0 ).x );
43
+ uvec2 uv;
44
+ uv.x = index % width;
45
+ uv.y = index / width;
46
+
47
+ return texelFetch( tex, ivec2( uv ), 0 );
48
+
49
+ }
50
+
51
+ vec4 textureSampleBarycoord( sampler2D tex, vec3 barycoord, uvec3 faceIndices ) {
52
+
53
+ return
54
+ barycoord.x * texelFetch1D( tex, faceIndices.x ) +
55
+ barycoord.y * texelFetch1D( tex, faceIndices.y ) +
56
+ barycoord.z * texelFetch1D( tex, faceIndices.z );
57
+
58
+ }
59
+
60
+ void ndcToCameraRay(
61
+ vec2 coord, mat4 cameraWorld, mat4 invProjectionMatrix,
62
+ out vec3 rayOrigin, out vec3 rayDirection
63
+ ) {
64
+
65
+ // get camera look direction and near plane for camera clipping
66
+ vec4 lookDirection = cameraWorld * vec4( 0.0, 0.0, - 1.0, 0.0 );
67
+ vec4 nearVector = invProjectionMatrix * vec4( 0.0, 0.0, - 1.0, 1.0 );
68
+ float near = abs( nearVector.z / nearVector.w );
69
+
70
+ // get the camera direction and position from camera matrices
71
+ vec4 origin = cameraWorld * vec4( 0.0, 0.0, 0.0, 1.0 );
72
+ vec4 direction = invProjectionMatrix * vec4( coord, 0.5, 1.0 );
73
+ direction /= direction.w;
74
+ direction = cameraWorld * direction - origin;
75
+
76
+ // slide the origin along the ray until it sits at the near clip plane position
77
+ origin.xyz += direction.xyz * near / dot( direction, lookDirection );
78
+
79
+ rayOrigin = origin.xyz;
80
+ rayDirection = direction.xyz;
81
+
82
+ }
83
+ `,Ur=`
84
+
85
+ #ifndef TRI_INTERSECT_EPSILON
86
+ #define TRI_INTERSECT_EPSILON 1e-5
87
+ #endif
88
+
89
+ // Raycasting
90
+ bool intersectsBounds( vec3 rayOrigin, vec3 rayDirection, vec3 boundsMin, vec3 boundsMax, out float dist ) {
91
+
92
+ // https://www.reddit.com/r/opengl/comments/8ntzz5/fast_glsl_ray_box_intersection/
93
+ // https://tavianator.com/2011/ray_box.html
94
+ vec3 invDir = 1.0 / rayDirection;
95
+
96
+ // find intersection distances for each plane
97
+ vec3 tMinPlane = invDir * ( boundsMin - rayOrigin );
98
+ vec3 tMaxPlane = invDir * ( boundsMax - rayOrigin );
99
+
100
+ // get the min and max distances from each intersection
101
+ vec3 tMinHit = min( tMaxPlane, tMinPlane );
102
+ vec3 tMaxHit = max( tMaxPlane, tMinPlane );
103
+
104
+ // get the furthest hit distance
105
+ vec2 t = max( tMinHit.xx, tMinHit.yz );
106
+ float t0 = max( t.x, t.y );
107
+
108
+ // get the minimum hit distance
109
+ t = min( tMaxHit.xx, tMaxHit.yz );
110
+ float t1 = min( t.x, t.y );
111
+
112
+ // set distance to 0.0 if the ray starts inside the box
113
+ dist = max( t0, 0.0 );
114
+
115
+ return t1 >= dist;
116
+
117
+ }
118
+
119
+ bool intersectsTriangle(
120
+ vec3 rayOrigin, vec3 rayDirection, vec3 a, vec3 b, vec3 c,
121
+ out vec3 barycoord, out vec3 norm, out float dist, out float side
122
+ ) {
123
+
124
+ // https://stackoverflow.com/questions/42740765/intersection-between-line-and-triangle-in-3d
125
+ vec3 edge1 = b - a;
126
+ vec3 edge2 = c - a;
127
+ norm = cross( edge1, edge2 );
128
+
129
+ float det = - dot( rayDirection, norm );
130
+ float invdet = 1.0 / det;
131
+
132
+ vec3 AO = rayOrigin - a;
133
+ vec3 DAO = cross( AO, rayDirection );
134
+
135
+ vec4 uvt;
136
+ uvt.x = dot( edge2, DAO ) * invdet;
137
+ uvt.y = - dot( edge1, DAO ) * invdet;
138
+ uvt.z = dot( AO, norm ) * invdet;
139
+ uvt.w = 1.0 - uvt.x - uvt.y;
140
+
141
+ // set the hit information
142
+ barycoord = uvt.wxy; // arranged in A, B, C order
143
+ dist = uvt.z;
144
+ side = sign( det );
145
+ norm = side * normalize( norm );
146
+
147
+ // add an epsilon to avoid misses between triangles
148
+ uvt += vec4( TRI_INTERSECT_EPSILON );
149
+
150
+ return all( greaterThanEqual( uvt, vec4( 0.0 ) ) );
151
+
152
+ }
153
+
154
+ bool intersectTriangles(
155
+ // geometry info and triangle range
156
+ sampler2D positionAttr, usampler2D indexAttr, uint offset, uint count,
157
+
158
+ // ray
159
+ vec3 rayOrigin, vec3 rayDirection,
160
+
161
+ // outputs
162
+ inout float minDistance, inout uvec4 faceIndices, inout vec3 faceNormal, inout vec3 barycoord,
163
+ inout float side, inout float dist
164
+ ) {
165
+
166
+ bool found = false;
167
+ vec3 localBarycoord, localNormal;
168
+ float localDist, localSide;
169
+ for ( uint i = offset, l = offset + count; i < l; i ++ ) {
170
+
171
+ uvec3 indices = uTexelFetch1D( indexAttr, i ).xyz;
172
+ vec3 a = texelFetch1D( positionAttr, indices.x ).rgb;
173
+ vec3 b = texelFetch1D( positionAttr, indices.y ).rgb;
174
+ vec3 c = texelFetch1D( positionAttr, indices.z ).rgb;
175
+
176
+ if (
177
+ intersectsTriangle( rayOrigin, rayDirection, a, b, c, localBarycoord, localNormal, localDist, localSide )
178
+ && localDist < minDistance
179
+ ) {
180
+
181
+ found = true;
182
+ minDistance = localDist;
183
+
184
+ faceIndices = uvec4( indices.xyz, i );
185
+ faceNormal = localNormal;
186
+
187
+ side = localSide;
188
+ barycoord = localBarycoord;
189
+ dist = localDist;
190
+
191
+ }
192
+
193
+ }
194
+
195
+ return found;
196
+
197
+ }
198
+
199
+ bool intersectsBVHNodeBounds( vec3 rayOrigin, vec3 rayDirection, sampler2D bvhBounds, uint currNodeIndex, out float dist ) {
200
+
201
+ uint cni2 = currNodeIndex * 2u;
202
+ vec3 boundsMin = texelFetch1D( bvhBounds, cni2 ).xyz;
203
+ vec3 boundsMax = texelFetch1D( bvhBounds, cni2 + 1u ).xyz;
204
+ return intersectsBounds( rayOrigin, rayDirection, boundsMin, boundsMax, dist );
205
+
206
+ }
207
+
208
+ // use a macro to hide the fact that we need to expand the struct into separate fields
209
+ #define bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) _bvhIntersectFirstHit( bvh.position, bvh.index, bvh.bvhBounds, bvh.bvhContents, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist )
210
+
211
+ bool _bvhIntersectFirstHit(
212
+ // bvh info
213
+ sampler2D bvh_position, usampler2D bvh_index, sampler2D bvh_bvhBounds, usampler2D bvh_bvhContents,
214
+
215
+ // ray
216
+ vec3 rayOrigin, vec3 rayDirection,
217
+
218
+ // output variables split into separate variables due to output precision
219
+ inout uvec4 faceIndices, inout vec3 faceNormal, inout vec3 barycoord,
220
+ inout float side, inout float dist
221
+ ) {
222
+
223
+ // stack needs to be twice as long as the deepest tree we expect because
224
+ // we push both the left and right child onto the stack every traversal
225
+ int pointer = 0;
226
+ uint stack[ BVH_STACK_DEPTH ];
227
+ stack[ 0 ] = 0u;
228
+
229
+ float triangleDistance = INFINITY;
230
+ bool found = false;
231
+ while ( pointer > - 1 && pointer < BVH_STACK_DEPTH ) {
232
+
233
+ uint currNodeIndex = stack[ pointer ];
234
+ pointer --;
235
+
236
+ // check if we intersect the current bounds
237
+ float boundsHitDistance;
238
+ if (
239
+ ! intersectsBVHNodeBounds( rayOrigin, rayDirection, bvh_bvhBounds, currNodeIndex, boundsHitDistance )
240
+ || boundsHitDistance > triangleDistance
241
+ ) {
242
+
243
+ continue;
244
+
245
+ }
246
+
247
+ uvec2 boundsInfo = uTexelFetch1D( bvh_bvhContents, currNodeIndex ).xy;
248
+ bool isLeaf = bool( boundsInfo.x & 0xffff0000u );
249
+
250
+ if ( isLeaf ) {
251
+
252
+ uint count = boundsInfo.x & 0x0000ffffu;
253
+ uint offset = boundsInfo.y;
254
+
255
+ found = intersectTriangles(
256
+ bvh_position, bvh_index, offset, count,
257
+ rayOrigin, rayDirection, triangleDistance,
258
+ faceIndices, faceNormal, barycoord, side, dist
259
+ ) || found;
260
+
261
+ } else {
262
+
263
+ uint leftIndex = currNodeIndex + 1u;
264
+ uint splitAxis = boundsInfo.x & 0x0000ffffu;
265
+ uint rightIndex = currNodeIndex + boundsInfo.y;
266
+
267
+ bool leftToRight = rayDirection[ splitAxis ] >= 0.0;
268
+ uint c1 = leftToRight ? leftIndex : rightIndex;
269
+ uint c2 = leftToRight ? rightIndex : leftIndex;
270
+
271
+ // set c2 in the stack so we traverse it later. We need to keep track of a pointer in
272
+ // the stack while we traverse. The second pointer added is the one that will be
273
+ // traversed first
274
+ pointer ++;
275
+ stack[ pointer ] = c2;
276
+
277
+ pointer ++;
278
+ stack[ pointer ] = c1;
279
+
280
+ }
281
+
282
+ }
283
+
284
+ return found;
285
+
286
+ }
287
+ `,Wr=`
288
+ struct BVH {
289
+
290
+ usampler2D index;
291
+ sampler2D position;
292
+
293
+ sampler2D bvhBounds;
294
+ usampler2D bvhContents;
295
+
296
+ };
297
+ `;function Gr(e,t,n=0){if(e.isInterleavedBufferAttribute){let r=e.itemSize;for(let i=0,a=e.count;i<a;i++){let a=i+n;t.setX(a,e.getX(i)),r>=2&&t.setY(a,e.getY(i)),r>=3&&t.setZ(a,e.getZ(i)),r>=4&&t.setW(a,e.getW(i))}}else{let r=t.array,i=r.constructor,a=r.BYTES_PER_ELEMENT*e.itemSize*n;new i(r.buffer,a,e.array.length).set(e.array)}}function Kr(e,t=null){let n=e.array.constructor,r=e.normalized,i=e.itemSize,a=t===null?e.count:t;return new be(new n(i*a),i,r)}function qr(e,t){if(!e&&!t)return!0;if(!!e!=!!t)return!1;let n=e.count===t.count,r=e.normalized===t.normalized,i=e.array.constructor===t.array.constructor,a=e.itemSize===t.itemSize;return!(!n||!r||!i||!a)}function Jr(e){let t=e[0].index!==null,n=new Set(Object.keys(e[0].attributes));if(!e[0].getAttribute(`position`))throw Error(`StaticGeometryGenerator: position attribute is required.`);for(let r=0;r<e.length;++r){let i=e[r],a=0;if(t!==(i.index!==null))throw Error(`StaticGeometryGenerator: All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.`);for(let e in i.attributes){if(!n.has(e))throw Error(`StaticGeometryGenerator: All geometries must have compatible attributes; make sure "`+e+`" attribute exists among all geometries, or in none of them.`);a++}if(a!==n.size)throw Error(`StaticGeometryGenerator: All geometries must have the same number of attributes.`)}}function Yr(e){let t=0;for(let n=0,r=e.length;n<r;n++)t+=e[n].getIndex().count;return t}function Xr(e){let t=0;for(let n=0,r=e.length;n<r;n++)t+=e[n].getAttribute(`position`).count;return t}function Zr(e,t,n){e.index&&e.index.count!==t&&e.setIndex(null);let r=e.attributes;for(let t in r)r[t].count!==n&&e.deleteAttribute(t)}function Qr(e,t={},n=new pe){let{useGroups:r=!1,forceUpdate:i=!1,skipAssigningAttributes:a=[],overwriteIndex:o=!0}=t;Jr(e);let s=e[0].index!==null,c=s?Yr(e):-1,l=Xr(e);if(Zr(n,c,l),r){let t=0;for(let r=0,i=e.length;r<i;r++){let i=e[r],a;a=s?i.getIndex().count:i.getAttribute(`position`).count,n.addGroup(t,a,r),t+=a}}if(s){let t=!1;if(n.index||(n.setIndex(new be(new Uint32Array(c),1,!1)),t=!0),t||o){let r=0,o=0,s=n.getIndex();for(let n=0,c=e.length;n<c;n++){let c=e[n],l=c.getIndex();if(!(!i&&!t&&a[n]))for(let e=0;e<l.count;++e)s.setX(r+e,l.getX(e)+o);r+=l.count,o+=c.getAttribute(`position`).count}}}let u=Object.keys(e[0].attributes);for(let t=0,r=u.length;t<r;t++){let r=!1,o=u[t];if(!n.getAttribute(o)){let t=e[0].getAttribute(o);n.setAttribute(o,Kr(t,l)),r=!0}let s=0,c=n.getAttribute(o);for(let t=0,n=e.length;t<n;t++){let n=e[t],l=!i&&!r&&a[t],u=n.getAttribute(o);if(!l){if(o===`color`&&c.itemSize!==u.itemSize)for(let e=s,t=u.count;e<t;e++)u.setXYZW(e,c.getX(e),c.getY(e),c.getZ(e),1);else Gr(u,c,s)}s+=u.count}}}function $r(e,t,n){let r=e.index,i=e.attributes.position.count,a=r?r.count:i,o=e.groups;o.length===0&&(o=[{count:a,start:0,materialIndex:0}]);let s=e.getAttribute(`materialIndex`);if(!s||s.count!==i){let t;t=n.length<=255?new Uint8Array(i):new Uint16Array(i),s=new be(t,1,!1),e.deleteAttribute(`materialIndex`),e.setAttribute(`materialIndex`,s)}let c=s.array;for(let e=0;e<o.length;e++){let i=o[e],s=i.start,l=i.count,u=Math.min(l,a-s),d=Array.isArray(t)?t[i.materialIndex]:t,f=n.indexOf(d);for(let e=0;e<u;e++){let t=s+e;r&&(t=r.getX(t)),c[t]=f}}}function ei(e,t){if(!e.index){let t=e.attributes.position.count,n=Array(t);for(let e=0;e<t;e++)n[e]=e;e.setIndex(n)}if(!e.attributes.normal&&t&&t.includes(`normal`)&&e.computeVertexNormals(),!e.attributes.uv&&t&&t.includes(`uv`)){let t=e.attributes.position.count;e.setAttribute(`uv`,new be(new Float32Array(t*2),2,!1))}if(!e.attributes.uv2&&t&&t.includes(`uv2`)){let t=e.attributes.position.count;e.setAttribute(`uv2`,new be(new Float32Array(t*2),2,!1))}if(!e.attributes.tangent&&t&&t.includes(`tangent`)){if(e.attributes.uv&&e.attributes.normal)e.computeTangents();else{let t=e.attributes.position.count;e.setAttribute(`tangent`,new be(new Float32Array(t*4),4,!1))}}if(!e.attributes.color&&t&&t.includes(`color`)){let t=e.attributes.position.count,n=new Float32Array(t*4);n.fill(1),e.setAttribute(`color`,new be(n,4))}}function ti(e){let t=0;if(e.byteLength!==0){let n=new Uint8Array(e);for(let r=0;r<e.byteLength;r++){let e=n[r];t=(t<<5)-t+e,t|=0}}return t}function ni(e){let t=e.uuid,n=Object.values(e.attributes);e.index&&(n.push(e.index),t+=`index|${e.index.version}`);let r=Object.keys(n).sort();for(let e of r){let r=n[e];t+=`${e}_${r.version}|`}return t}function ri(e){let t=e.skeleton;return t?(t.boneTexture||t.computeBoneTexture(),`${ti(t.boneTexture.image.data.buffer)}_${t.boneTexture.uuid}`):null}var ii=class{constructor(e=null){this.matrixWorld=new f,this.geometryHash=null,this.skeletonHash=null,this.primitiveCount=-1,e!==null&&this.updateFrom(e)}updateFrom(e){let t=e.geometry,n=(t.index?t.index.count:t.attributes.position.count)/3;this.matrixWorld.copy(e.matrixWorld),this.geometryHash=ni(t),this.primitiveCount=n,this.skeletonHash=ri(e)}didChange(e){let t=e.geometry,n=(t.index?t.index.count:t.attributes.position.count)/3;return!(this.matrixWorld.equals(e.matrixWorld)&&this.geometryHash===ni(t)&&this.skeletonHash===ri(e)&&this.primitiveCount===n)}},ai=new D,oi=new D,si=new D,ci=new ce,li=new D,ui=new D,di=new ce,fi=new ce,pi=new f,mi=new f;function hi(e,t,n){let r=e.skeleton,i=e.geometry,a=r.bones,o=r.boneInverses;di.fromBufferAttribute(i.attributes.skinIndex,t),fi.fromBufferAttribute(i.attributes.skinWeight,t),pi.elements.fill(0);for(let e=0;e<4;e++){let t=fi.getComponent(e);if(t!==0){let n=di.getComponent(e);mi.multiplyMatrices(a[n].matrixWorld,o[n]),_i(pi,mi,t)}}return pi.multiply(e.bindMatrix).premultiply(e.bindMatrixInverse),n.transformDirection(pi),n}function gi(e,t,n,r,i){li.set(0,0,0);for(let a=0,o=e.length;a<o;a++){let o=t[a],s=e[a];o!==0&&(ui.fromBufferAttribute(s,r),n?li.addScaledVector(ui,o):li.addScaledVector(ui.sub(i),o))}i.add(li)}function _i(e,t,n){let r=e.elements,i=t.elements;for(let e=0,t=i.length;e<t;e++)r[e]+=i[e]*n}function vi(e){let{index:t,attributes:n}=e;if(t)for(let e=0,n=t.count;e<n;e+=3){let n=t.getX(e),r=t.getX(e+2);t.setX(e,r),t.setX(e+2,n)}else for(let e in n){let t=n[e],r=t.itemSize;for(let e=0,n=t.count;e<n;e+=3)for(let n=0;n<r;n++){let r=t.getComponent(e,n),i=t.getComponent(e+2,n);t.setComponent(e,n,i),t.setComponent(e+2,n,r)}}return e}function yi(e,t={},n=new pe){t={applyWorldTransforms:!0,attributes:[],...t};let r=e.geometry,i=t.applyWorldTransforms,a=t.attributes.includes(`normal`),o=t.attributes.includes(`tangent`),s=r.attributes,c=n.attributes;for(let e in n.attributes)(!t.attributes.includes(e)||!(e in r.attributes))&&n.deleteAttribute(e);!n.index&&r.index&&(n.index=r.index.clone()),c.position||n.setAttribute(`position`,Kr(s.position)),a&&!c.normal&&s.normal&&n.setAttribute(`normal`,Kr(s.normal)),o&&!c.tangent&&s.tangent&&n.setAttribute(`tangent`,Kr(s.tangent)),qr(r.index,n.index),qr(s.position,c.position),a&&qr(s.normal,c.normal),o&&qr(s.tangent,c.tangent);let u=s.position,d=a?s.normal:null,f=o?s.tangent:null,p=r.morphAttributes.position,m=r.morphAttributes.normal,h=r.morphAttributes.tangent,g=r.morphTargetsRelative,_=e.morphTargetInfluences,v=new l;v.getNormalMatrix(e.matrixWorld),r.index&&n.index.array.set(r.index.array);for(let t=0,n=s.position.count;t<n;t++)ai.fromBufferAttribute(u,t),d&&oi.fromBufferAttribute(d,t),f&&(ci.fromBufferAttribute(f,t),si.fromBufferAttribute(f,t)),_&&(p&&gi(p,_,g,t,ai),m&&gi(m,_,g,t,oi),h&&gi(h,_,g,t,si)),e.isSkinnedMesh&&(e.applyBoneTransform(t,ai),d&&hi(e,t,oi),f&&hi(e,t,si)),i&&ai.applyMatrix4(e.matrixWorld),c.position.setXYZ(t,ai.x,ai.y,ai.z),d&&(i&&oi.applyNormalMatrix(v),c.normal.setXYZ(t,oi.x,oi.y,oi.z)),f&&(i&&si.transformDirection(e.matrixWorld),c.tangent.setXYZW(t,si.x,si.y,si.z,ci.w));for(let e in t.attributes){let r=t.attributes[e];r===`position`||r===`tangent`||r===`normal`||!(r in s)||(c[r]||n.setAttribute(r,Kr(s[r])),qr(s[r],c[r]),Gr(s[r],c[r]))}return e.matrixWorld.determinant()<0&&vi(n),n}var bi=class extends pe{constructor(){super(),this.version=0,this.hash=null,this._diff=new ii}isCompatible(e,t){let n=e.geometry;for(let e=0;e<t.length;e++){let r=t[e],i=n.attributes[r],a=this.attributes[r];if(i&&!qr(i,a))return!1}return!0}updateFrom(e,t){let n=this._diff;return n.didChange(e)?(yi(e,t,this),n.updateFrom(e),this.version++,this.hash=`${this.uuid}_${this.version}`,!0):!1}};function xi(e,t){for(let n=0,r=e.length;n<r;n++)e[n].traverseVisible(e=>{e.isMesh&&t(e)})}function Si(e){let t=[];for(let n=0,r=e.length;n<r;n++){let r=e[n];Array.isArray(r.material)?t.push(...r.material):t.push(r.material)}return t}function Ci(e,t,n){if(e.length===0){t.setIndex(null);let e=t.attributes;for(let n in e)t.deleteAttribute(n);for(let e in n.attributes)t.setAttribute(n.attributes[e],new be(new Float32Array,4,!1))}else Qr(e,n,t);for(let e in t.attributes)t.attributes[e].needsUpdate=!0}var wi=class{constructor(e){this.objects=null,this.useGroups=!0,this.applyWorldTransforms=!0,this.generateMissingAttributes=!0,this.overwriteIndex=!0,this.attributes=[`position`,`normal`,`color`,`tangent`,`uv`,`uv2`],this._intermediateGeometry=new Map,this._geometryMergeSets=new WeakMap,this._mergeOrder=[],this._dummyMesh=null,this.setObjects(e||[])}_getDummyMesh(){if(!this._dummyMesh){let e=new Be,t=new pe;t.setAttribute(`position`,new be(new Float32Array(9),3)),this._dummyMesh=new v(t,e)}return this._dummyMesh}_getMeshes(){let e=[];return xi(this.objects,t=>{e.push(t)}),e.sort((e,t)=>e.uuid>t.uuid?1:e.uuid<t.uuid?-1:0),e.length===0&&e.push(this._getDummyMesh()),e}_updateIntermediateGeometries(){let{_intermediateGeometry:e}=this,t=this._getMeshes(),n=new Set(e.keys()),r={attributes:this.attributes,applyWorldTransforms:this.applyWorldTransforms};for(let i=0,a=t.length;i<a;i++){let a=t[i],o=a.uuid;n.delete(o);let s=e.get(o);(!s||!s.isCompatible(a,this.attributes))&&(s&&s.dispose(),s=new bi,e.set(o,s)),s.updateFrom(a,r)&&this.generateMissingAttributes&&ei(s,this.attributes)}n.forEach(t=>{e.delete(t)})}setObjects(e){this.objects=Array.isArray(e)?[...e]:[e]}generate(e=new pe){let{useGroups:t,overwriteIndex:n,_intermediateGeometry:r,_geometryMergeSets:i}=this,a=this._getMeshes(),o=[],s=[],c=i.get(e)||[];this._updateIntermediateGeometries();let l=!1;a.length!==c.length&&(l=!0);for(let e=0,t=a.length;e<t;e++){let t=a[e],n=r.get(t.uuid);s.push(n);let i=c[e];!i||i.uuid!==n.uuid?(o.push(!1),l=!0):i.version===n.version?o.push(!0):o.push(!1)}Ci(s,e,{useGroups:t,forceUpdate:l,skipAssigningAttributes:o,overwriteIndex:n}),l&&e.dispose(),i.set(e,s.map(e=>({version:e.version,uuid:e.uuid})));let u=0;return l?u=2:o.includes(!1)&&(u=1),{changeType:u,materials:Si(a),geometry:e}}};function Ti(e){let t=new Set;for(let n=0,r=e.length;n<r;n++){let r=e[n];for(let e in r){let n=r[e];n&&n.isTexture&&t.add(n)}}return Array.from(t)}function Ei(e){let t=[],n=new Set;for(let r=0,i=e.length;r<i;r++)e[r].traverse(e=>{e.visible&&(e.isRectAreaLight||e.isSpotLight||e.isPointLight||e.isDirectionalLight)&&(t.push(e),e.iesMap&&n.add(e.iesMap))});return{lights:t,iesTextures:Array.from(n).sort((e,t)=>e.uuid<t.uuid?1:e.uuid>t.uuid?-1:0)}}var Di=class{get initialized(){return!!this.bvh}constructor(e){this.bvhOptions={},this.attributes=[`position`,`normal`,`tangent`,`color`,`uv`,`uv2`],this.generateBVH=!0,this.bvh=null,this.geometry=new pe,this.staticGeometryGenerator=new wi(e),this._bvhWorker=null,this._pendingGenerate=null,this._buildAsync=!1,this._materialUuids=null}setObjects(e){this.staticGeometryGenerator.setObjects(e)}setBVHWorker(e){this._bvhWorker=e}async generateAsync(e=null){if(!this._bvhWorker)throw Error(`PathTracingSceneGenerator: "setBVHWorker" must be called before "generateAsync" can be called.`);if(this.bvh instanceof Promise)return this._pendingGenerate||=new Promise(async()=>(await this.bvh,this._pendingGenerate=null,this.generateAsync(e))),this._pendingGenerate;{this._buildAsync=!0;let t=this.generate(e);return this._buildAsync=!1,t.bvh=this.bvh=await t.bvh,t}}generate(e=null){let{staticGeometryGenerator:t,geometry:n,attributes:r}=this,i=t.objects;t.attributes=r,i.forEach(e=>{e.traverse(e=>{e.isSkinnedMesh&&e.skeleton&&e.skeleton.update()})});let a=t.generate(n),o=a.materials,s=a.changeType!==0||this._materialUuids===null||this._materialUuids.length!==length;if(!s){for(let e=0,t=o.length;e<t;e++)if(o[e].uuid!==this._materialUuids[e]){s=!0;break}}let c=Ti(o),{lights:l,iesTextures:u}=Ei(i);if(s&&($r(n,o,o),this._materialUuids=o.map(e=>e.uuid)),this.generateBVH){if(this.bvh instanceof Promise)throw Error(`PathTracingSceneGenerator: BVH is already building asynchronously.`);if(a.changeType===2){let t={strategy:2,maxLeafTris:1,indirect:!0,onProgress:e,...this.bvhOptions};this.bvh=this._buildAsync?this._bvhWorker.generate(n,t):new Mr(n,t)}else a.changeType===1&&this.bvh.refit()}return{bvhChanged:a.changeType!==0,bvh:this.bvh,needsMaterialIndexUpdate:s,lights:l,iesTextures:u,geometry:n,materials:o,textures:c,objects:i}}},Oi=class extends Pe{set needsUpdate(e){super.needsUpdate=!0,this.dispatchEvent({type:`recompilation`})}constructor(e){super(e);for(let e in this.uniforms)Object.defineProperty(this,e,{get(){return this.uniforms[e].value},set(t){this.uniforms[e].value=t}})}setDefine(e,t=void 0){if(t==null){if(e in this.defines)return delete this.defines[e],this.needsUpdate=!0,!0}else if(this.defines[e]!==t)return this.defines[e]=t,this.needsUpdate=!0,!0;return!1}},ki=class extends Oi{constructor(e){super({blending:0,uniforms:{target1:{value:null},target2:{value:null},opacity:{value:1}},vertexShader:`
298
+
299
+ varying vec2 vUv;
300
+
301
+ void main() {
302
+
303
+ vUv = uv;
304
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
305
+
306
+ }`,fragmentShader:`
307
+
308
+ uniform float opacity;
309
+
310
+ uniform sampler2D target1;
311
+ uniform sampler2D target2;
312
+
313
+ varying vec2 vUv;
314
+
315
+ void main() {
316
+
317
+ vec4 color1 = texture2D( target1, vUv );
318
+ vec4 color2 = texture2D( target2, vUv );
319
+
320
+ float invOpacity = 1.0 - opacity;
321
+ float totalAlpha = color1.a * invOpacity + color2.a * opacity;
322
+
323
+ if ( color1.a != 0.0 || color2.a != 0.0 ) {
324
+
325
+ gl_FragColor.rgb = color1.rgb * ( invOpacity * color1.a / totalAlpha ) + color2.rgb * ( opacity * color2.a / totalAlpha );
326
+ gl_FragColor.a = totalAlpha;
327
+
328
+ } else {
329
+
330
+ gl_FragColor = vec4( 0.0 );
331
+
332
+ }
333
+
334
+ }`}),this.setValues(e)}};function Ai(e=1){let t=`uint`;return e>1&&(t=`uvec`+e),`
335
+ ${t} sobolReverseBits( ${t} x ) {
336
+
337
+ x = ( ( ( x & 0xaaaaaaaau ) >> 1 ) | ( ( x & 0x55555555u ) << 1 ) );
338
+ x = ( ( ( x & 0xccccccccu ) >> 2 ) | ( ( x & 0x33333333u ) << 2 ) );
339
+ x = ( ( ( x & 0xf0f0f0f0u ) >> 4 ) | ( ( x & 0x0f0f0f0fu ) << 4 ) );
340
+ x = ( ( ( x & 0xff00ff00u ) >> 8 ) | ( ( x & 0x00ff00ffu ) << 8 ) );
341
+ return ( ( x >> 16 ) | ( x << 16 ) );
342
+
343
+ }
344
+
345
+ ${t} sobolHashCombine( uint seed, ${t} v ) {
346
+
347
+ return seed ^ ( v + ${t}( ( seed << 6 ) + ( seed >> 2 ) ) );
348
+
349
+ }
350
+
351
+ ${t} sobolLaineKarrasPermutation( ${t} x, ${t} seed ) {
352
+
353
+ x += seed;
354
+ x ^= x * 0x6c50b47cu;
355
+ x ^= x * 0xb82f1e52u;
356
+ x ^= x * 0xc7afe638u;
357
+ x ^= x * 0x8d22f6e6u;
358
+ return x;
359
+
360
+ }
361
+
362
+ ${t} nestedUniformScrambleBase2( ${t} x, ${t} seed ) {
363
+
364
+ x = sobolLaineKarrasPermutation( x, seed );
365
+ x = sobolReverseBits( x );
366
+ return x;
367
+
368
+ }
369
+ `}function ji(e=1){let t=`uint`,n=`float`,r=``,i=`.r`,a=`1u`;return e>1&&(t=`uvec`+e,n=`vec`+e,r=e+``,e===2?(i=`.rg`,a=`uvec2( 1u, 2u )`):e===3?(i=`.rgb`,a=`uvec3( 1u, 2u, 3u )`):(i=``,a=`uvec4( 1u, 2u, 3u, 4u )`)),`
370
+
371
+ ${n} sobol${r}( int effect ) {
372
+
373
+ uint seed = sobolGetSeed( sobolBounceIndex, uint( effect ) );
374
+ uint index = sobolPathIndex;
375
+
376
+ uint shuffle_seed = sobolHashCombine( seed, 0u );
377
+ uint shuffled_index = nestedUniformScrambleBase2( sobolReverseBits( index ), shuffle_seed );
378
+ ${n} sobol_pt = sobolGetTexturePoint( shuffled_index )${i};
379
+ ${t} result = ${t}( sobol_pt * 16777216.0 );
380
+
381
+ ${t} seed2 = sobolHashCombine( seed, ${a} );
382
+ result = nestedUniformScrambleBase2( result, seed2 );
383
+
384
+ return SOBOL_FACTOR * ${n}( result >> 8 );
385
+
386
+ }
387
+ `}var Mi=`
388
+
389
+ // Utils
390
+ const float SOBOL_FACTOR = 1.0 / 16777216.0;
391
+ const uint SOBOL_MAX_POINTS = 256u * 256u;
392
+
393
+ ${Ai(1)}
394
+ ${Ai(2)}
395
+ ${Ai(3)}
396
+ ${Ai(4)}
397
+
398
+ uint sobolHash( uint x ) {
399
+
400
+ // finalizer from murmurhash3
401
+ x ^= x >> 16;
402
+ x *= 0x85ebca6bu;
403
+ x ^= x >> 13;
404
+ x *= 0xc2b2ae35u;
405
+ x ^= x >> 16;
406
+ return x;
407
+
408
+ }
409
+
410
+ `,Ni=`
411
+
412
+ const uint SOBOL_DIRECTIONS_1[ 32 ] = uint[ 32 ](
413
+ 0x80000000u, 0xc0000000u, 0xa0000000u, 0xf0000000u,
414
+ 0x88000000u, 0xcc000000u, 0xaa000000u, 0xff000000u,
415
+ 0x80800000u, 0xc0c00000u, 0xa0a00000u, 0xf0f00000u,
416
+ 0x88880000u, 0xcccc0000u, 0xaaaa0000u, 0xffff0000u,
417
+ 0x80008000u, 0xc000c000u, 0xa000a000u, 0xf000f000u,
418
+ 0x88008800u, 0xcc00cc00u, 0xaa00aa00u, 0xff00ff00u,
419
+ 0x80808080u, 0xc0c0c0c0u, 0xa0a0a0a0u, 0xf0f0f0f0u,
420
+ 0x88888888u, 0xccccccccu, 0xaaaaaaaau, 0xffffffffu
421
+ );
422
+
423
+ const uint SOBOL_DIRECTIONS_2[ 32 ] = uint[ 32 ](
424
+ 0x80000000u, 0xc0000000u, 0x60000000u, 0x90000000u,
425
+ 0xe8000000u, 0x5c000000u, 0x8e000000u, 0xc5000000u,
426
+ 0x68800000u, 0x9cc00000u, 0xee600000u, 0x55900000u,
427
+ 0x80680000u, 0xc09c0000u, 0x60ee0000u, 0x90550000u,
428
+ 0xe8808000u, 0x5cc0c000u, 0x8e606000u, 0xc5909000u,
429
+ 0x6868e800u, 0x9c9c5c00u, 0xeeee8e00u, 0x5555c500u,
430
+ 0x8000e880u, 0xc0005cc0u, 0x60008e60u, 0x9000c590u,
431
+ 0xe8006868u, 0x5c009c9cu, 0x8e00eeeeu, 0xc5005555u
432
+ );
433
+
434
+ const uint SOBOL_DIRECTIONS_3[ 32 ] = uint[ 32 ](
435
+ 0x80000000u, 0xc0000000u, 0x20000000u, 0x50000000u,
436
+ 0xf8000000u, 0x74000000u, 0xa2000000u, 0x93000000u,
437
+ 0xd8800000u, 0x25400000u, 0x59e00000u, 0xe6d00000u,
438
+ 0x78080000u, 0xb40c0000u, 0x82020000u, 0xc3050000u,
439
+ 0x208f8000u, 0x51474000u, 0xfbea2000u, 0x75d93000u,
440
+ 0xa0858800u, 0x914e5400u, 0xdbe79e00u, 0x25db6d00u,
441
+ 0x58800080u, 0xe54000c0u, 0x79e00020u, 0xb6d00050u,
442
+ 0x800800f8u, 0xc00c0074u, 0x200200a2u, 0x50050093u
443
+ );
444
+
445
+ const uint SOBOL_DIRECTIONS_4[ 32 ] = uint[ 32 ](
446
+ 0x80000000u, 0x40000000u, 0x20000000u, 0xb0000000u,
447
+ 0xf8000000u, 0xdc000000u, 0x7a000000u, 0x9d000000u,
448
+ 0x5a800000u, 0x2fc00000u, 0xa1600000u, 0xf0b00000u,
449
+ 0xda880000u, 0x6fc40000u, 0x81620000u, 0x40bb0000u,
450
+ 0x22878000u, 0xb3c9c000u, 0xfb65a000u, 0xddb2d000u,
451
+ 0x78022800u, 0x9c0b3c00u, 0x5a0fb600u, 0x2d0ddb00u,
452
+ 0xa2878080u, 0xf3c9c040u, 0xdb65a020u, 0x6db2d0b0u,
453
+ 0x800228f8u, 0x400b3cdcu, 0x200fb67au, 0xb00ddb9du
454
+ );
455
+
456
+ uint getMaskedSobol( uint index, uint directions[ 32 ] ) {
457
+
458
+ uint X = 0u;
459
+ for ( int bit = 0; bit < 32; bit ++ ) {
460
+
461
+ uint mask = ( index >> bit ) & 1u;
462
+ X ^= mask * directions[ bit ];
463
+
464
+ }
465
+ return X;
466
+
467
+ }
468
+
469
+ vec4 generateSobolPoint( uint index ) {
470
+
471
+ if ( index >= SOBOL_MAX_POINTS ) {
472
+
473
+ return vec4( 0.0 );
474
+
475
+ }
476
+
477
+ // NOTE: this sobol "direction" is also available but we can't write out 5 components
478
+ // uint x = index & 0x00ffffffu;
479
+ uint x = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_1 ) ) & 0x00ffffffu;
480
+ uint y = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_2 ) ) & 0x00ffffffu;
481
+ uint z = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_3 ) ) & 0x00ffffffu;
482
+ uint w = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_4 ) ) & 0x00ffffffu;
483
+
484
+ return vec4( x, y, z, w ) * SOBOL_FACTOR;
485
+
486
+ }
487
+
488
+ `,Pi=`
489
+
490
+ // Seeds
491
+ uniform sampler2D sobolTexture;
492
+ uint sobolPixelIndex = 0u;
493
+ uint sobolPathIndex = 0u;
494
+ uint sobolBounceIndex = 0u;
495
+
496
+ uint sobolGetSeed( uint bounce, uint effect ) {
497
+
498
+ return sobolHash(
499
+ sobolHashCombine(
500
+ sobolHashCombine(
501
+ sobolHash( bounce ),
502
+ sobolPixelIndex
503
+ ),
504
+ effect
505
+ )
506
+ );
507
+
508
+ }
509
+
510
+ vec4 sobolGetTexturePoint( uint index ) {
511
+
512
+ if ( index >= SOBOL_MAX_POINTS ) {
513
+
514
+ index = index % SOBOL_MAX_POINTS;
515
+
516
+ }
517
+
518
+ uvec2 dim = uvec2( textureSize( sobolTexture, 0 ).xy );
519
+ uint y = index / dim.x;
520
+ uint x = index - y * dim.x;
521
+ vec2 uv = vec2( x, y ) / vec2( dim );
522
+ return texture( sobolTexture, uv );
523
+
524
+ }
525
+
526
+ ${ji(1)}
527
+ ${ji(2)}
528
+ ${ji(3)}
529
+ ${ji(4)}
530
+
531
+ `,Fi=class extends Oi{constructor(){super({blending:0,uniforms:{resolution:{value:new E}},vertexShader:`
532
+
533
+ varying vec2 vUv;
534
+ void main() {
535
+
536
+ vUv = uv;
537
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
538
+
539
+ }
540
+ `,fragmentShader:`
541
+
542
+ ${Mi}
543
+ ${Ni}
544
+
545
+ varying vec2 vUv;
546
+ uniform vec2 resolution;
547
+ void main() {
548
+
549
+ uint index = uint( gl_FragCoord.y ) * uint( resolution.x ) + uint( gl_FragCoord.x );
550
+ gl_FragColor = generateSobolPoint( index );
551
+
552
+ }
553
+ `})}},Ii=class{generate(e,t=256){let n=new De(t,t,{type:b,format:d,minFilter:S,magFilter:S,generateMipmaps:!1}),r=e.getRenderTarget();e.setRenderTarget(n);let i=new ae(new Fi);return i.material.resolution.set(t,t),i.render(e),e.setRenderTarget(r),i.dispose(),n}},Li=class extends c{set bokehSize(e){this.fStop=this.getFocalLength()/e}get bokehSize(){return this.getFocalLength()/this.fStop}constructor(...e){super(...e),this.fStop=1.4,this.apertureBlades=0,this.apertureRotation=0,this.focusDistance=25,this.anamorphicRatio=1}copy(e,t){return super.copy(e,t),this.fStop=e.fStop,this.apertureBlades=e.apertureBlades,this.apertureRotation=e.apertureRotation,this.focusDistance=e.focusDistance,this.anamorphicRatio=e.anamorphicRatio,this}},Ri=class{constructor(){this.bokehSize=0,this.apertureBlades=0,this.apertureRotation=0,this.focusDistance=10,this.anamorphicRatio=1}updateFrom(e){e instanceof Li?(this.bokehSize=e.bokehSize,this.apertureBlades=e.apertureBlades,this.apertureRotation=e.apertureRotation,this.focusDistance=e.focusDistance,this.anamorphicRatio=e.anamorphicRatio):(this.bokehSize=0,this.apertureRotation=0,this.apertureBlades=0,this.focusDistance=10,this.anamorphicRatio=1)}};function zi(e){let t=new Uint16Array(e.length);for(let n=0,i=e.length;n<i;++n)t[n]=r.toHalfFloat(e[n]);return t}function Bi(e,t,n=0,r=e.length){let i=n,a=n+r-1;for(;i<a;){let n=i+a>>1;e[n]<t?i=n+1:a=n}return i-n}function Vi(e,t,n){return .2126*e+.7152*t+.0722*n}function Hi(e,t=ve){let n=e.clone();n.source=new Ae({...n.image});let{width:i,height:a,data:o}=n.image,s=o;if(n.type!==t){s=t===1016?new Uint16Array(o.length):new Float32Array(o.length);let e;e=o instanceof Int8Array||o instanceof Int16Array||o instanceof Int32Array?2**(8*o.BYTES_PER_ELEMENT-1)-1:2**(8*o.BYTES_PER_ELEMENT)-1;for(let i=0,a=o.length;i<a;i++){let a=o[i];n.type===1016&&(a=r.fromHalfFloat(o[i])),n.type!==1015&&n.type!==1016&&(a/=e),t===1016&&(s[i]=r.toHalfFloat(a))}n.image.data=s,n.type=t}if(n.flipY){let e=s;s=s.slice();for(let t=0;t<a;t++)for(let n=0;n<i;n++){let r=a-t-1,o=4*(t*i+n),c=4*(r*i+n);s[c+0]=e[o+0],s[c+1]=e[o+1],s[c+2]=e[o+2],s[c+3]=e[o+3]}n.flipY=!1,n.image.data=s}return n}var Ui=class{constructor(){let t=new Re(zi(new Float32Array([0,0,0,0])),1,1);t.type=ve,t.format=d,t.minFilter=m,t.magFilter=m,t.wrapS=e,t.wrapT=e,t.generateMipmaps=!1,t.needsUpdate=!0;let n=new Re(zi(new Float32Array([0,1])),1,2);n.type=ve,n.format=te,n.minFilter=m,n.magFilter=m,n.generateMipmaps=!1,n.needsUpdate=!0;let r=new Re(zi(new Float32Array([0,0,1,1])),2,2);r.type=ve,r.format=te,r.minFilter=m,r.magFilter=m,r.generateMipmaps=!1,r.needsUpdate=!0,this.map=t,this.marginalWeights=n,this.conditionalWeights=r,this.totalSum=0}dispose(){this.marginalWeights.dispose(),this.conditionalWeights.dispose(),this.map.dispose()}updateFrom(t){let n=Hi(t);n.wrapS=e,n.wrapT=T;let{width:i,height:a,data:o}=n.image,s=new Float32Array(i*a),c=new Float32Array(i*a),l=new Float32Array(a),u=new Float32Array(a),d=0,f=0;for(let e=0;e<a;e++){let t=0;for(let n=0;n<i;n++){let a=e*i+n,l=Vi(r.fromHalfFloat(o[4*a+0]),r.fromHalfFloat(o[4*a+1]),r.fromHalfFloat(o[4*a+2]));t+=l,d+=l,s[a]=l,c[a]=t}if(t!==0)for(let n=e*i,r=e*i+i;n<r;n++)s[n]/=t,c[n]/=t;f+=t,l[e]=t,u[e]=f}if(f!==0)for(let e=0,t=l.length;e<t;e++)l[e]/=f,u[e]/=f;let p=new Uint16Array(a),m=new Uint16Array(i*a);for(let e=0;e<a;e++){let t=Bi(u,(e+1)/a);p[e]=r.toHalfFloat((t+.5)/a)}for(let e=0;e<a;e++)for(let t=0;t<i;t++){let n=e*i+t,a=Bi(c,(t+1)/i,e*i,i);m[n]=r.toHalfFloat((a+.5)/i)}this.dispose();let{marginalWeights:h,conditionalWeights:g}=this;h.image={width:a,height:1,data:p},h.needsUpdate=!0,g.image={width:i,height:a,data:m},g.needsUpdate=!0,this.totalSum=d,this.map=n}},Wi=6,Gi=0,Ki=1,qi=2,Ji=3,Yi=4,Xi=new D,R=new D,Zi=new f,Qi=new s,$i=new D,ea=new D,ta=new D(0,1,0),na=class{constructor(){let e=new Re(new Float32Array(4),1,1);e.format=d,e.type=b,e.wrapS=T,e.wrapT=T,e.generateMipmaps=!1,e.minFilter=S,e.magFilter=S,this.tex=e,this.count=0}updateFrom(e,t=[]){let n=this.tex,r=Math.max(e.length*Wi,1),i=Math.ceil(Math.sqrt(r));n.image.width!==i&&(n.dispose(),n.image.data=new Float32Array(i*i*4),n.image.width=i,n.image.height=i);let a=n.image.data;for(let n=0,r=e.length;n<r;n++){let r=e[n],i=n*Wi*4,o=0;for(let e=0;e<24;e++)a[i+e]=0;r.getWorldPosition(R),a[i+o++]=R.x,a[i+o++]=R.y,a[i+o++]=R.z;let s=Gi;if(r.isRectAreaLight&&r.isCircular?s=Ki:r.isSpotLight?s=qi:r.isDirectionalLight?s=Ji:r.isPointLight&&(s=Yi),a[i+o++]=s,a[i+o++]=r.color.r,a[i+o++]=r.color.g,a[i+o++]=r.color.b,a[i+o++]=r.intensity,r.getWorldQuaternion(Qi),r.isRectAreaLight)Xi.set(r.width,0,0).applyQuaternion(Qi),a[i+o++]=Xi.x,a[i+o++]=Xi.y,a[i+o++]=Xi.z,o++,R.set(0,r.height,0).applyQuaternion(Qi),a[i+o++]=R.x,a[i+o++]=R.y,a[i+o++]=R.z,a[i+o++]=Xi.cross(R).length()*(r.isCircular?Math.PI/4:1);else if(r.isSpotLight){let e=r.radius||0;$i.setFromMatrixPosition(r.matrixWorld),ea.setFromMatrixPosition(r.target.matrixWorld),Zi.lookAt($i,ea,ta),Qi.setFromRotationMatrix(Zi),Xi.set(1,0,0).applyQuaternion(Qi),a[i+o++]=Xi.x,a[i+o++]=Xi.y,a[i+o++]=Xi.z,o++,R.set(0,1,0).applyQuaternion(Qi),a[i+o++]=R.x,a[i+o++]=R.y,a[i+o++]=R.z,a[i+o++]=Math.PI*e*e,a[i+o++]=e,a[i+o++]=r.decay,a[i+o++]=r.distance,a[i+o++]=Math.cos(r.angle),a[i+o++]=Math.cos(r.angle*(1-r.penumbra)),a[i+o++]=r.iesMap?t.indexOf(r.iesMap):-1}else if(r.isPointLight){let e=Xi.setFromMatrixPosition(r.matrixWorld);a[i+o++]=e.x,a[i+o++]=e.y,a[i+o++]=e.z,o++,o+=4,o+=1,a[i+o++]=r.decay,a[i+o++]=r.distance}else if(r.isDirectionalLight){let e=Xi.setFromMatrixPosition(r.matrixWorld),t=R.setFromMatrixPosition(r.target.matrixWorld);ea.subVectors(e,t).normalize(),a[i+o++]=ea.x,a[i+o++]=ea.y,a[i+o++]=ea.z}}this.count=e.length;let o=ti(a.buffer);return this.hash!==o&&(this.hash=o,n.needsUpdate=!0,!0)}};function ra(e,t,n,r,i){if(t>r)throw Error();let a=e.length/t,o=e.constructor.BYTES_PER_ELEMENT*8,s=1;switch(e.constructor){case Uint8Array:case Uint16Array:case Uint32Array:s=2**o-1;break;case Int8Array:case Int16Array:case Int32Array:s=2**(o-1)-1}for(let o=0;o<a;o++){let a=4*o,c=t*o;for(let o=0;o<r;o++)n[i+a+o]=t>=o+1?e[c+o]/s:0}}var ia=class extends ie{constructor(){super(),this._textures=[],this.type=b,this.format=d,this.internalFormat=`RGBA32F`}updateAttribute(e,t){let n=this._textures[e];n.updateFrom(t);let r=n.image,i=this.image;if(r.width!==i.width||r.height!==i.height)throw Error(`FloatAttributeTextureArray: Attribute must be the same dimensions when updating single layer.`);let{width:a,height:o,data:s}=i,c=a*o*4*e,l=t.itemSize;l===3&&(l=4),ra(n.image.data,l,s,4,c),this.dispose(),this.needsUpdate=!0}setAttributes(e){let t=e[0].count,n=e.length;for(let r=0,i=n;r<i;r++)if(e[r].count!==t)throw Error(`FloatAttributeTextureArray: All attributes must have the same item count.`);let r=this._textures;for(;r.length<n;){let e=new Rr;r.push(e)}for(;r.length>n;)r.pop();for(let t=0,i=n;t<i;t++)r[t].updateFrom(e[t]);let i=r[0].image,a=this.image;(i.width!==a.width||i.height!==a.height||i.depth!==n)&&(a.width=i.width,a.height=i.height,a.depth=n,a.data=new Float32Array(a.width*a.height*a.depth*4));let{data:o,width:s,height:c}=a;for(let t=0,i=n;t<i;t++){let n=r[t],i=s*c*4*t,a=e[t].itemSize;a===3&&(a=4),ra(n.image.data,a,o,4,i)}this.dispose(),this.needsUpdate=!0}},aa=class extends ia{updateNormalAttribute(e){this.updateAttribute(0,e)}updateTangentAttribute(e){this.updateAttribute(1,e)}updateUvAttribute(e){this.updateAttribute(2,e)}updateColorAttribute(e){this.updateAttribute(3,e)}updateFrom(e,t,n,r){this.setAttributes([e,t,n,r])}};function oa(e,t){return e.uuid<t.uuid?1:e.uuid>t.uuid?-1:0}function sa(e){return`${e.source.uuid}:${e.colorSpace}`}function ca(e){let t=new Set,n=[];for(let r=0,i=e.length;r<i;r++){let i=e[r],a=sa(i);t.has(a)||(t.add(a),n.push(i))}return n}function la(e){let t=e.map(e=>e.iesMap||null).filter(e=>e),n=new Set(t);return Array.from(n).sort(oa)}function ua(e){let t=new Set;for(let n=0,r=e.length;n<r;n++){let r=e[n];for(let e in r){let n=r[e];n&&n.isTexture&&t.add(n)}}return ca(Array.from(t)).sort(oa)}function da(e){let t=[];return e.traverse(e=>{e.visible&&(e.isRectAreaLight||e.isSpotLight||e.isPointLight||e.isDirectionalLight)&&t.push(e)}),t.sort(oa)}var fa=188,pa=class{constructor(){this._features={}}isUsed(e){return e in this._features}setUsed(e,t=!0){t===!1?delete this._features[e]:this._features[e]=!0}reset(){this._features={}}},ma=class extends Re{constructor(){super(new Float32Array(4),1,1),this.format=d,this.type=b,this.wrapS=T,this.wrapT=T,this.minFilter=S,this.magFilter=S,this.generateMipmaps=!1,this.features=new pa}updateFrom(e,t){function n(e,t,n=-1){if(t in e&&e[t]){let n=sa(e[t]);return u[n]}return n}function r(e,t,n){return t in e?e[t]:n}function i(e,t,n,r){let i=e[t]&&e[t].isTexture?e[t]:null;if(i){i.matrixAutoUpdate&&i.updateMatrix();let e=i.matrix.elements,t=0;n[r+t++]=e[0],n[r+t++]=e[3],n[r+t++]=e[6],t++,n[r+t++]=e[1],n[r+t++]=e[4],n[r+t++]=e[7],t++}return 8}let a=0,o=e.length*47,s=Math.ceil(Math.sqrt(o))||1,{image:c,features:l}=this,u={};for(let e=0,n=t.length;e<n;e++)u[sa(t[e])]=e;c.width!==s&&(this.dispose(),c.data=new Float32Array(s*s*4),c.width=s,c.height=s);let d=c.data;l.reset();for(let t=0,o=e.length;t<o;t++){let o=e[t];if(o.isFogVolumeMaterial){l.setUsed(`FOG`);for(let e=0;e<fa;e++)d[a+e]=0;d[a+0+0]=o.color.r,d[a+0+1]=o.color.g,d[a+0+2]=o.color.b,d[a+8+3]=r(o,`emissiveIntensity`,0),d[a+12+0]=o.emissive.r,d[a+12+1]=o.emissive.g,d[a+12+2]=o.emissive.b,d[a+52+1]=o.density,d[a+52+3]=0,d[a+56+2]=4,a+=fa;continue}d[a++]=o.color.r,d[a++]=o.color.g,d[a++]=o.color.b,d[a++]=n(o,`map`),d[a++]=r(o,`metalness`,0),d[a++]=n(o,`metalnessMap`),d[a++]=r(o,`roughness`,0),d[a++]=n(o,`roughnessMap`),d[a++]=r(o,`ior`,1.5),d[a++]=r(o,`transmission`,0),d[a++]=n(o,`transmissionMap`),d[a++]=r(o,`emissiveIntensity`,0),`emissive`in o?(d[a++]=o.emissive.r,d[a++]=o.emissive.g,d[a++]=o.emissive.b):(d[a++]=0,d[a++]=0,d[a++]=0),d[a++]=n(o,`emissiveMap`),d[a++]=n(o,`normalMap`),`normalScale`in o?(d[a++]=o.normalScale.x,d[a++]=o.normalScale.y):(d[a++]=1,d[a++]=1),d[a++]=r(o,`clearcoat`,0),d[a++]=n(o,`clearcoatMap`),d[a++]=r(o,`clearcoatRoughness`,0),d[a++]=n(o,`clearcoatRoughnessMap`),d[a++]=n(o,`clearcoatNormalMap`),`clearcoatNormalScale`in o?(d[a++]=o.clearcoatNormalScale.x,d[a++]=o.clearcoatNormalScale.y):(d[a++]=1,d[a++]=1),a++,d[a++]=r(o,`sheen`,0),`sheenColor`in o?(d[a++]=o.sheenColor.r,d[a++]=o.sheenColor.g,d[a++]=o.sheenColor.b):(d[a++]=0,d[a++]=0,d[a++]=0),d[a++]=n(o,`sheenColorMap`),d[a++]=r(o,`sheenRoughness`,0),d[a++]=n(o,`sheenRoughnessMap`),d[a++]=n(o,`iridescenceMap`),d[a++]=n(o,`iridescenceThicknessMap`),d[a++]=r(o,`iridescence`,0),d[a++]=r(o,`iridescenceIOR`,1.3);let s=r(o,`iridescenceThicknessRange`,[100,400]);d[a++]=s[0],d[a++]=s[1],`specularColor`in o?(d[a++]=o.specularColor.r,d[a++]=o.specularColor.g,d[a++]=o.specularColor.b):(d[a++]=1,d[a++]=1,d[a++]=1),d[a++]=n(o,`specularColorMap`),d[a++]=r(o,`specularIntensity`,1),d[a++]=n(o,`specularIntensityMap`);let c=r(o,`thickness`,0)===0&&r(o,`attenuationDistance`,1/0)===1/0;if(d[a++]=Number(c),a++,`attenuationColor`in o?(d[a++]=o.attenuationColor.r,d[a++]=o.attenuationColor.g,d[a++]=o.attenuationColor.b):(d[a++]=1,d[a++]=1,d[a++]=1),d[a++]=r(o,`attenuationDistance`,1/0),d[a++]=n(o,`alphaMap`),d[a++]=o.opacity,d[a++]=o.alphaTest,!c&&o.transmission>0)d[a++]=0;else switch(o.side){case 0:d[a++]=1;break;case 1:d[a++]=-1;break;case 2:d[a++]=0}d[a++]=Number(r(o,`matte`,!1)),d[a++]=Number(r(o,`castShadow`,!0)),d[a++]=Number(o.vertexColors)|Number(o.flatShading)<<1,d[a++]=Number(o.transparent),a+=i(o,`map`,d,a),a+=i(o,`metalnessMap`,d,a),a+=i(o,`roughnessMap`,d,a),a+=i(o,`transmissionMap`,d,a),a+=i(o,`emissiveMap`,d,a),a+=i(o,`normalMap`,d,a),a+=i(o,`clearcoatMap`,d,a),a+=i(o,`clearcoatNormalMap`,d,a),a+=i(o,`clearcoatRoughnessMap`,d,a),a+=i(o,`sheenColorMap`,d,a),a+=i(o,`sheenRoughnessMap`,d,a),a+=i(o,`iridescenceMap`,d,a),a+=i(o,`iridescenceThicknessMap`,d,a),a+=i(o,`specularColorMap`,d,a),a+=i(o,`specularIntensityMap`,d,a),a+=i(o,`alphaMap`,d,a)}let f=ti(d.buffer);return this.hash!==f&&(this.hash=f,this.needsUpdate=!0,!0)}},ha=new ze;function ga(e){return e?`${e.uuid}:${e.version}`:null}function _a(e,t){for(let n in t)n in e&&(e[n]=t[n])}var va=class extends de{constructor(t,n,r){let i={format:d,type:Te,minFilter:m,magFilter:m,wrapS:e,wrapT:e,generateMipmaps:!1,...r};super(t,n,1,i),_a(this.texture,i),this.texture.setTextures=(...e)=>{this.setTextures(...e)},this.hashes=[null];let a=new ae(new ya);this.fsQuad=a}setTextures(e,t,n=this.width,r=this.height){let i=e.getRenderTarget(),a=e.toneMapping,o=e.getClearAlpha();e.getClearColor(ha);let s=t.length||1;(n!==this.width||r!==this.height||this.depth!==s)&&(this.setSize(n,r,s),this.hashes=Array(s).fill(null)),e.setClearColor(0,0),e.toneMapping=0;let c=this.fsQuad,l=this.hashes,u=!1;for(let n=0,r=s;n<r;n++){let r=t[n],i=ga(r);r&&(l[n]!==i||r.isWebGLRenderTarget)&&(r.matrixAutoUpdate=!1,r.matrix.identity(),c.material.map=r,e.setRenderTarget(this,n),c.render(e),r.updateMatrix(),r.matrixAutoUpdate=!0,l[n]=i,u=!0)}return c.material.map=null,e.setClearColor(ha,o),e.setRenderTarget(i),e.toneMapping=a,u}dispose(){super.dispose(),this.fsQuad.dispose()}},ya=class extends Pe{get map(){return this.uniforms.map.value}set map(e){this.uniforms.map.value=e}constructor(){super({uniforms:{map:{value:null}},vertexShader:`
554
+ varying vec2 vUv;
555
+ void main() {
556
+
557
+ vUv = uv;
558
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
559
+
560
+ }
561
+ `,fragmentShader:`
562
+ uniform sampler2D map;
563
+ varying vec2 vUv;
564
+ void main() {
565
+
566
+ gl_FragColor = texture2D( map, vUv );
567
+
568
+ }
569
+ `})}};function ba(e,t=Math.random()){for(let n=e.length-1;n>0;n--){let r=Math.floor(t()*(n+1)),i=e[n];e[n]=e[r],e[r]=i}return e}var xa=class{constructor(e,t,n=Math.random){let r=e**t,i=new Uint16Array(r),a=r;for(let e=0;e<r;e++)i[e]=e;this.samples=new Float32Array(t),this.strataCount=e,this.reset=function(){for(let e=0;e<r;e++)i[e]=e;a=0},this.reshuffle=function(){a=0},this.next=function(){let{samples:r}=this;a>=i.length&&(ba(i,n),this.reshuffle());let o=i[a++];for(let i=0;i<t;i++)r[i]=(o%e+n())/e,o=Math.floor(o/e);return r}}},Sa=class{constructor(e,t,n=Math.random){let r=0;for(let e of t)r+=e;let i=new Float32Array(r),a=[],o=0;for(let r of t){let t=new xa(e,r,n);t.samples=new Float32Array(i.buffer,o,t.samples.length),o+=t.samples.length*4,a.push(t)}this.samples=i,this.strataCount=e,this.next=function(){for(let e of a)e.next();return i},this.reshuffle=function(){for(let e of a)e.reshuffle()},this.reset=function(){for(let e of a)e.reset()}}},Ca=class{constructor(e=0){this.m=2147483648,this.a=1103515245,this.c=12345,this.seed=e}nextInt(){return this.seed=(this.a*this.seed+this.c)%this.m,this.seed}nextFloat(){return this.nextInt()/(this.m-1)}},wa=class extends Re{constructor(e=1,t=1,n=8){super(new Float32Array(1),1,1,d,b),this.minFilter=S,this.magFilter=S,this.strata=n,this.sampler=null,this.generator=new Ca,this.stableNoise=!1,this.random=()=>this.stableNoise?this.generator.nextFloat():Math.random(),this.init(e,t,n)}init(e=this.image.height,t=this.image.width,n=this.strata){let{image:r}=this;if(r.width===t&&r.height===e&&this.sampler!==null)return;let i=new Sa(n,Array(e*t).fill(4),this.random);r.width=t,r.height=e,r.data=i.samples,this.sampler=i,this.dispose(),this.next()}next(){this.sampler.next(),this.needsUpdate=!0}reset(){this.sampler.reset(),this.generator.seed=0}};function Ta(e,t=Math.random){for(let n=e.length-1;n>0;n--){let r=~~((t()-1e-6)*n),i=e[n];e[n]=e[r],e[r]=i}}function Ea(e,t){e.fill(0);for(let n=0;n<t;n++)e[n]=1}var Da=class{constructor(e){this.count=0,this.size=-1,this.sigma=-1,this.radius=-1,this.lookupTable=null,this.score=null,this.binaryPattern=null,this.resize(e),this.setSigma(1.5)}findVoid(){let{score:e,binaryPattern:t}=this,n=1/0,r=-1;for(let i=0,a=t.length;i<a;i++){if(t[i]!==0)continue;let a=e[i];a<n&&(n=a,r=i)}return r}findCluster(){let{score:e,binaryPattern:t}=this,n=-1/0,r=-1;for(let i=0,a=t.length;i<a;i++){if(t[i]!==1)continue;let a=e[i];a>n&&(n=a,r=i)}return r}setSigma(e){if(e===this.sigma)return;let t=~~(Math.sqrt(20*e**2)+1),n=2*t+1,r=new Float32Array(n*n),i=e*e;for(let e=-t;e<=t;e++)for(let a=-t;a<=t;a++){let o=(t+a)*n+e+t,s=e*e+a*a;r[o]=Math.E**(-s/(2*i))}this.lookupTable=r,this.sigma=e,this.radius=t}resize(e){this.size!==e&&(this.size=e,this.score=new Float32Array(e*e),this.binaryPattern=new Uint8Array(e*e))}invert(){let{binaryPattern:e,score:t,size:n}=this;t.fill(0);for(let t=0,r=e.length;t<r;t++)if(e[t]===0){let r=~~(t/n),i=t-r*n;this.updateScore(i,r,1),e[t]=1}else e[t]=0}updateScore(e,t,n){let{size:r,score:i,lookupTable:a}=this,o=this.radius,s=2*o+1;for(let c=-o;c<=o;c++)for(let l=-o;l<=o;l++){let u=a[(o+l)*s+c+o],d=e+c;d=d<0?r+d:d%r;let f=t+l;f=f<0?r+f:f%r;let p=f*r+d;i[p]+=n*u}}addPointIndex(e){this.binaryPattern[e]=1;let t=this.size,n=~~(e/t),r=e-n*t;this.updateScore(r,n,1),this.count++}removePointIndex(e){this.binaryPattern[e]=0;let t=this.size,n=~~(e/t),r=e-n*t;this.updateScore(r,n,-1),this.count--}copy(e){this.resize(e.size),this.score.set(e.score),this.binaryPattern.set(e.binaryPattern),this.setSigma(e.sigma),this.count=e.count}},Oa=class{constructor(){this.random=Math.random,this.sigma=1.5,this.size=64,this.majorityPointsRatio=.1,this.samples=new Da(1),this.savedSamples=new Da(1)}generate(){let{samples:e,savedSamples:t,sigma:n,majorityPointsRatio:r,size:i}=this;e.resize(i),e.setSigma(n);let a=Math.floor(i*i*r),o=e.binaryPattern;Ea(o,a),Ta(o,this.random);for(let t=0,n=o.length;t<n;t++)o[t]===1&&e.addPointIndex(t);for(;;){let t=e.findCluster();e.removePointIndex(t);let n=e.findVoid();if(t===n){e.addPointIndex(t);break}e.addPointIndex(n)}let s=new Uint32Array(i*i);t.copy(e);let c;for(c=e.count-1;c>=0;){let t=e.findCluster();e.removePointIndex(t),s[t]=c,c--}let l=i*i;for(c=t.count;c<l/2;){let e=t.findVoid();t.addPointIndex(e),s[e]=c,c++}for(t.invert();c<l;){let e=t.findCluster();t.removePointIndex(e),s[e]=c,c++}return{data:s,maxValue:l}}};function ka(e){return e>=3?4:e}function Aa(e){switch(e){case 1:return te;case 2:return u;default:return d}}var ja=class extends Re{constructor(e=64,t=1){super(new Float32Array(4),1,1,d,b),this.minFilter=S,this.magFilter=S,this.size=e,this.channels=t,this.update()}update(){let e=this.channels,t=this.size,n=new Oa;n.channels=e,n.size=t;let r=ka(e),i=Aa(r);(this.image.width!==t||i!==this.format)&&(this.image.width=t,this.image.height=t,this.image.data=new Float32Array(t**2*r),this.format=i,this.dispose());let a=this.image.data;for(let t=0,i=e;t<i;t++){let e=n.generate(),i=e.data,o=e.maxValue;for(let e=0,n=i.length;e<n;e++){let n=i[e]/o;a[e*r+t]=n}}this.needsUpdate=!0}},Ma=`
570
+
571
+ struct PhysicalCamera {
572
+
573
+ float focusDistance;
574
+ float anamorphicRatio;
575
+ float bokehSize;
576
+ int apertureBlades;
577
+ float apertureRotation;
578
+
579
+ };
580
+
581
+ `,Na=`
582
+
583
+ struct EquirectHdrInfo {
584
+
585
+ sampler2D marginalWeights;
586
+ sampler2D conditionalWeights;
587
+ sampler2D map;
588
+
589
+ float totalSum;
590
+
591
+ };
592
+
593
+ `,Pa=`
594
+
595
+ #define RECT_AREA_LIGHT_TYPE 0
596
+ #define CIRC_AREA_LIGHT_TYPE 1
597
+ #define SPOT_LIGHT_TYPE 2
598
+ #define DIR_LIGHT_TYPE 3
599
+ #define POINT_LIGHT_TYPE 4
600
+
601
+ struct LightsInfo {
602
+
603
+ sampler2D tex;
604
+ uint count;
605
+
606
+ };
607
+
608
+ struct Light {
609
+
610
+ vec3 position;
611
+ int type;
612
+
613
+ vec3 color;
614
+ float intensity;
615
+
616
+ vec3 u;
617
+ vec3 v;
618
+ float area;
619
+
620
+ // spot light fields
621
+ float radius;
622
+ float near;
623
+ float decay;
624
+ float distance;
625
+ float coneCos;
626
+ float penumbraCos;
627
+ int iesProfile;
628
+
629
+ };
630
+
631
+ Light readLightInfo( sampler2D tex, uint index ) {
632
+
633
+ uint i = index * 6u;
634
+
635
+ vec4 s0 = texelFetch1D( tex, i + 0u );
636
+ vec4 s1 = texelFetch1D( tex, i + 1u );
637
+ vec4 s2 = texelFetch1D( tex, i + 2u );
638
+ vec4 s3 = texelFetch1D( tex, i + 3u );
639
+
640
+ Light l;
641
+ l.position = s0.rgb;
642
+ l.type = int( round( s0.a ) );
643
+
644
+ l.color = s1.rgb;
645
+ l.intensity = s1.a;
646
+
647
+ l.u = s2.rgb;
648
+ l.v = s3.rgb;
649
+ l.area = s3.a;
650
+
651
+ if ( l.type == SPOT_LIGHT_TYPE || l.type == POINT_LIGHT_TYPE ) {
652
+
653
+ vec4 s4 = texelFetch1D( tex, i + 4u );
654
+ vec4 s5 = texelFetch1D( tex, i + 5u );
655
+ l.radius = s4.r;
656
+ l.decay = s4.g;
657
+ l.distance = s4.b;
658
+ l.coneCos = s4.a;
659
+
660
+ l.penumbraCos = s5.r;
661
+ l.iesProfile = int( round( s5.g ) );
662
+
663
+ } else {
664
+
665
+ l.radius = 0.0;
666
+ l.decay = 0.0;
667
+ l.distance = 0.0;
668
+
669
+ l.coneCos = 0.0;
670
+ l.penumbraCos = 0.0;
671
+ l.iesProfile = - 1;
672
+
673
+ }
674
+
675
+ return l;
676
+
677
+ }
678
+
679
+ `,Fa=`
680
+
681
+ struct Material {
682
+
683
+ vec3 color;
684
+ int map;
685
+
686
+ float metalness;
687
+ int metalnessMap;
688
+
689
+ float roughness;
690
+ int roughnessMap;
691
+
692
+ float ior;
693
+ float transmission;
694
+ int transmissionMap;
695
+
696
+ float emissiveIntensity;
697
+ vec3 emissive;
698
+ int emissiveMap;
699
+
700
+ int normalMap;
701
+ vec2 normalScale;
702
+
703
+ float clearcoat;
704
+ int clearcoatMap;
705
+ int clearcoatNormalMap;
706
+ vec2 clearcoatNormalScale;
707
+ float clearcoatRoughness;
708
+ int clearcoatRoughnessMap;
709
+
710
+ int iridescenceMap;
711
+ int iridescenceThicknessMap;
712
+ float iridescence;
713
+ float iridescenceIor;
714
+ float iridescenceThicknessMinimum;
715
+ float iridescenceThicknessMaximum;
716
+
717
+ vec3 specularColor;
718
+ int specularColorMap;
719
+
720
+ float specularIntensity;
721
+ int specularIntensityMap;
722
+ bool thinFilm;
723
+
724
+ vec3 attenuationColor;
725
+ float attenuationDistance;
726
+
727
+ int alphaMap;
728
+
729
+ bool castShadow;
730
+ float opacity;
731
+ float alphaTest;
732
+
733
+ float side;
734
+ bool matte;
735
+
736
+ float sheen;
737
+ vec3 sheenColor;
738
+ int sheenColorMap;
739
+ float sheenRoughness;
740
+ int sheenRoughnessMap;
741
+
742
+ bool vertexColors;
743
+ bool flatShading;
744
+ bool transparent;
745
+ bool fogVolume;
746
+
747
+ mat3 mapTransform;
748
+ mat3 metalnessMapTransform;
749
+ mat3 roughnessMapTransform;
750
+ mat3 transmissionMapTransform;
751
+ mat3 emissiveMapTransform;
752
+ mat3 normalMapTransform;
753
+ mat3 clearcoatMapTransform;
754
+ mat3 clearcoatNormalMapTransform;
755
+ mat3 clearcoatRoughnessMapTransform;
756
+ mat3 sheenColorMapTransform;
757
+ mat3 sheenRoughnessMapTransform;
758
+ mat3 iridescenceMapTransform;
759
+ mat3 iridescenceThicknessMapTransform;
760
+ mat3 specularColorMapTransform;
761
+ mat3 specularIntensityMapTransform;
762
+ mat3 alphaMapTransform;
763
+
764
+ };
765
+
766
+ mat3 readTextureTransform( sampler2D tex, uint index ) {
767
+
768
+ mat3 textureTransform;
769
+
770
+ vec4 row1 = texelFetch1D( tex, index );
771
+ vec4 row2 = texelFetch1D( tex, index + 1u );
772
+
773
+ textureTransform[0] = vec3(row1.r, row2.r, 0.0);
774
+ textureTransform[1] = vec3(row1.g, row2.g, 0.0);
775
+ textureTransform[2] = vec3(row1.b, row2.b, 1.0);
776
+
777
+ return textureTransform;
778
+
779
+ }
780
+
781
+ Material readMaterialInfo( sampler2D tex, uint index ) {
782
+
783
+ uint i = index * uint( MATERIAL_PIXELS );
784
+
785
+ vec4 s0 = texelFetch1D( tex, i + 0u );
786
+ vec4 s1 = texelFetch1D( tex, i + 1u );
787
+ vec4 s2 = texelFetch1D( tex, i + 2u );
788
+ vec4 s3 = texelFetch1D( tex, i + 3u );
789
+ vec4 s4 = texelFetch1D( tex, i + 4u );
790
+ vec4 s5 = texelFetch1D( tex, i + 5u );
791
+ vec4 s6 = texelFetch1D( tex, i + 6u );
792
+ vec4 s7 = texelFetch1D( tex, i + 7u );
793
+ vec4 s8 = texelFetch1D( tex, i + 8u );
794
+ vec4 s9 = texelFetch1D( tex, i + 9u );
795
+ vec4 s10 = texelFetch1D( tex, i + 10u );
796
+ vec4 s11 = texelFetch1D( tex, i + 11u );
797
+ vec4 s12 = texelFetch1D( tex, i + 12u );
798
+ vec4 s13 = texelFetch1D( tex, i + 13u );
799
+ vec4 s14 = texelFetch1D( tex, i + 14u );
800
+
801
+ Material m;
802
+ m.color = s0.rgb;
803
+ m.map = int( round( s0.a ) );
804
+
805
+ m.metalness = s1.r;
806
+ m.metalnessMap = int( round( s1.g ) );
807
+ m.roughness = s1.b;
808
+ m.roughnessMap = int( round( s1.a ) );
809
+
810
+ m.ior = s2.r;
811
+ m.transmission = s2.g;
812
+ m.transmissionMap = int( round( s2.b ) );
813
+ m.emissiveIntensity = s2.a;
814
+
815
+ m.emissive = s3.rgb;
816
+ m.emissiveMap = int( round( s3.a ) );
817
+
818
+ m.normalMap = int( round( s4.r ) );
819
+ m.normalScale = s4.gb;
820
+
821
+ m.clearcoat = s4.a;
822
+ m.clearcoatMap = int( round( s5.r ) );
823
+ m.clearcoatRoughness = s5.g;
824
+ m.clearcoatRoughnessMap = int( round( s5.b ) );
825
+ m.clearcoatNormalMap = int( round( s5.a ) );
826
+ m.clearcoatNormalScale = s6.rg;
827
+
828
+ m.sheen = s6.a;
829
+ m.sheenColor = s7.rgb;
830
+ m.sheenColorMap = int( round( s7.a ) );
831
+ m.sheenRoughness = s8.r;
832
+ m.sheenRoughnessMap = int( round( s8.g ) );
833
+
834
+ m.iridescenceMap = int( round( s8.b ) );
835
+ m.iridescenceThicknessMap = int( round( s8.a ) );
836
+ m.iridescence = s9.r;
837
+ m.iridescenceIor = s9.g;
838
+ m.iridescenceThicknessMinimum = s9.b;
839
+ m.iridescenceThicknessMaximum = s9.a;
840
+
841
+ m.specularColor = s10.rgb;
842
+ m.specularColorMap = int( round( s10.a ) );
843
+
844
+ m.specularIntensity = s11.r;
845
+ m.specularIntensityMap = int( round( s11.g ) );
846
+ m.thinFilm = bool( s11.b );
847
+
848
+ m.attenuationColor = s12.rgb;
849
+ m.attenuationDistance = s12.a;
850
+
851
+ m.alphaMap = int( round( s13.r ) );
852
+
853
+ m.opacity = s13.g;
854
+ m.alphaTest = s13.b;
855
+ m.side = s13.a;
856
+
857
+ m.matte = bool( s14.r );
858
+ m.castShadow = bool( s14.g );
859
+ m.vertexColors = bool( int( s14.b ) & 1 );
860
+ m.flatShading = bool( int( s14.b ) & 2 );
861
+ m.fogVolume = bool( int( s14.b ) & 4 );
862
+ m.transparent = bool( s14.a );
863
+
864
+ uint firstTextureTransformIdx = i + 15u;
865
+
866
+ // mat3( 1.0 ) is an identity matrix
867
+ m.mapTransform = m.map == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx );
868
+ m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
869
+ m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
870
+ m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
871
+ m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
872
+ m.normalMapTransform = m.normalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
873
+ m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
874
+ m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
875
+ m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
876
+ m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
877
+ m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
878
+ m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
879
+ m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
880
+ m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
881
+ m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
882
+ m.alphaMapTransform = m.alphaMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 30u );
883
+
884
+ return m;
885
+
886
+ }
887
+
888
+ `,Ia=`
889
+
890
+ struct SurfaceRecord {
891
+
892
+ // surface type
893
+ bool volumeParticle;
894
+
895
+ // geometry
896
+ vec3 faceNormal;
897
+ bool frontFace;
898
+ vec3 normal;
899
+ mat3 normalBasis;
900
+ mat3 normalInvBasis;
901
+
902
+ // cached properties
903
+ float eta;
904
+ float f0;
905
+
906
+ // material
907
+ float roughness;
908
+ float filteredRoughness;
909
+ float metalness;
910
+ vec3 color;
911
+ vec3 emission;
912
+
913
+ // transmission
914
+ float ior;
915
+ float transmission;
916
+ bool thinFilm;
917
+ vec3 attenuationColor;
918
+ float attenuationDistance;
919
+
920
+ // clearcoat
921
+ vec3 clearcoatNormal;
922
+ mat3 clearcoatBasis;
923
+ mat3 clearcoatInvBasis;
924
+ float clearcoat;
925
+ float clearcoatRoughness;
926
+ float filteredClearcoatRoughness;
927
+
928
+ // sheen
929
+ float sheen;
930
+ vec3 sheenColor;
931
+ float sheenRoughness;
932
+
933
+ // iridescence
934
+ float iridescence;
935
+ float iridescenceIor;
936
+ float iridescenceThickness;
937
+
938
+ // specular
939
+ vec3 specularColor;
940
+ float specularIntensity;
941
+ };
942
+
943
+ struct ScatterRecord {
944
+ float specularPdf;
945
+ float pdf;
946
+ vec3 direction;
947
+ vec3 color;
948
+ };
949
+
950
+ `,La=`
951
+
952
+ // samples the the given environment map in the given direction
953
+ vec3 sampleEquirectColor( sampler2D envMap, vec3 direction ) {
954
+
955
+ return texture2D( envMap, equirectDirectionToUv( direction ) ).rgb;
956
+
957
+ }
958
+
959
+ // gets the pdf of the given direction to sample
960
+ float equirectDirectionPdf( vec3 direction ) {
961
+
962
+ vec2 uv = equirectDirectionToUv( direction );
963
+ float theta = uv.y * PI;
964
+ float sinTheta = sin( theta );
965
+ if ( sinTheta == 0.0 ) {
966
+
967
+ return 0.0;
968
+
969
+ }
970
+
971
+ return 1.0 / ( 2.0 * PI * PI * sinTheta );
972
+
973
+ }
974
+
975
+ // samples the color given env map with CDF and returns the pdf of the direction
976
+ float sampleEquirect( vec3 direction, inout vec3 color ) {
977
+
978
+ float totalSum = envMapInfo.totalSum;
979
+ if ( totalSum == 0.0 ) {
980
+
981
+ color = vec3( 0.0 );
982
+ return 1.0;
983
+
984
+ }
985
+
986
+ vec2 uv = equirectDirectionToUv( direction );
987
+ color = texture2D( envMapInfo.map, uv ).rgb;
988
+
989
+ float lum = luminance( color );
990
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
991
+ float pdf = lum / totalSum;
992
+
993
+ return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
994
+
995
+ }
996
+
997
+ // samples a direction of the envmap with color and retrieves pdf
998
+ float sampleEquirectProbability( vec2 r, inout vec3 color, inout vec3 direction ) {
999
+
1000
+ // sample env map cdf
1001
+ float v = texture2D( envMapInfo.marginalWeights, vec2( r.x, 0.0 ) ).x;
1002
+ float u = texture2D( envMapInfo.conditionalWeights, vec2( r.y, v ) ).x;
1003
+ vec2 uv = vec2( u, v );
1004
+
1005
+ vec3 derivedDirection = equirectUvToDirection( uv );
1006
+ direction = derivedDirection;
1007
+ color = texture2D( envMapInfo.map, uv ).rgb;
1008
+
1009
+ float totalSum = envMapInfo.totalSum;
1010
+ float lum = luminance( color );
1011
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
1012
+ float pdf = lum / totalSum;
1013
+
1014
+ return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
1015
+
1016
+ }
1017
+ `,Ra=`
1018
+
1019
+ float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {
1020
+
1021
+ return smoothstep( coneCosine, penumbraCosine, angleCosine );
1022
+
1023
+ }
1024
+
1025
+ float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
1026
+
1027
+ // based upon Frostbite 3 Moving to Physically-based Rendering
1028
+ // page 32, equation 26: E[window1]
1029
+ // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
1030
+ float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), EPSILON );
1031
+
1032
+ if ( cutoffDistance > 0.0 ) {
1033
+
1034
+ distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );
1035
+
1036
+ }
1037
+
1038
+ return distanceFalloff;
1039
+
1040
+ }
1041
+
1042
+ float getPhotometricAttenuation( sampler2DArray iesProfiles, int iesProfile, vec3 posToLight, vec3 lightDir, vec3 u, vec3 v ) {
1043
+
1044
+ float cosTheta = dot( posToLight, lightDir );
1045
+ float angle = acos( cosTheta ) / PI;
1046
+
1047
+ return texture2D( iesProfiles, vec3( angle, 0.0, iesProfile ) ).r;
1048
+
1049
+ }
1050
+
1051
+ struct LightRecord {
1052
+
1053
+ float dist;
1054
+ vec3 direction;
1055
+ float pdf;
1056
+ vec3 emission;
1057
+ int type;
1058
+
1059
+ };
1060
+
1061
+ bool intersectLightAtIndex( sampler2D lights, vec3 rayOrigin, vec3 rayDirection, uint l, inout LightRecord lightRec ) {
1062
+
1063
+ bool didHit = false;
1064
+ Light light = readLightInfo( lights, l );
1065
+
1066
+ vec3 u = light.u;
1067
+ vec3 v = light.v;
1068
+
1069
+ // check for backface
1070
+ vec3 normal = normalize( cross( u, v ) );
1071
+ if ( dot( normal, rayDirection ) > 0.0 ) {
1072
+
1073
+ u *= 1.0 / dot( u, u );
1074
+ v *= 1.0 / dot( v, v );
1075
+
1076
+ float dist;
1077
+
1078
+ // MIS / light intersection is not supported for punctual lights.
1079
+ if(
1080
+ ( light.type == RECT_AREA_LIGHT_TYPE && intersectsRectangle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) ) ||
1081
+ ( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
1082
+ ) {
1083
+
1084
+ float cosTheta = dot( rayDirection, normal );
1085
+ didHit = true;
1086
+ lightRec.dist = dist;
1087
+ lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
1088
+ lightRec.emission = light.color * light.intensity;
1089
+ lightRec.direction = rayDirection;
1090
+ lightRec.type = light.type;
1091
+
1092
+ }
1093
+
1094
+ }
1095
+
1096
+ return didHit;
1097
+
1098
+ }
1099
+
1100
+ LightRecord randomAreaLightSample( Light light, vec3 rayOrigin, vec2 ruv ) {
1101
+
1102
+ vec3 randomPos;
1103
+ if( light.type == RECT_AREA_LIGHT_TYPE ) {
1104
+
1105
+ // rectangular area light
1106
+ randomPos = light.position + light.u * ( ruv.x - 0.5 ) + light.v * ( ruv.y - 0.5 );
1107
+
1108
+ } else if( light.type == CIRC_AREA_LIGHT_TYPE ) {
1109
+
1110
+ // circular area light
1111
+ float r = 0.5 * sqrt( ruv.x );
1112
+ float theta = ruv.y * 2.0 * PI;
1113
+ float x = r * cos( theta );
1114
+ float y = r * sin( theta );
1115
+
1116
+ randomPos = light.position + light.u * x + light.v * y;
1117
+
1118
+ }
1119
+
1120
+ vec3 toLight = randomPos - rayOrigin;
1121
+ float lightDistSq = dot( toLight, toLight );
1122
+ float dist = sqrt( lightDistSq );
1123
+ vec3 direction = toLight / dist;
1124
+ vec3 lightNormal = normalize( cross( light.u, light.v ) );
1125
+
1126
+ LightRecord lightRec;
1127
+ lightRec.type = light.type;
1128
+ lightRec.emission = light.color * light.intensity;
1129
+ lightRec.dist = dist;
1130
+ lightRec.direction = direction;
1131
+
1132
+ // TODO: the denominator is potentially zero
1133
+ lightRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
1134
+
1135
+ return lightRec;
1136
+
1137
+ }
1138
+
1139
+ LightRecord randomSpotLightSample( Light light, sampler2DArray iesProfiles, vec3 rayOrigin, vec2 ruv ) {
1140
+
1141
+ float radius = light.radius * sqrt( ruv.x );
1142
+ float theta = ruv.y * 2.0 * PI;
1143
+ float x = radius * cos( theta );
1144
+ float y = radius * sin( theta );
1145
+
1146
+ vec3 u = light.u;
1147
+ vec3 v = light.v;
1148
+ vec3 normal = normalize( cross( u, v ) );
1149
+
1150
+ float angle = acos( light.coneCos );
1151
+ float angleTan = tan( angle );
1152
+ float startDistance = light.radius / max( angleTan, EPSILON );
1153
+
1154
+ vec3 randomPos = light.position - normal * startDistance + u * x + v * y;
1155
+ vec3 toLight = randomPos - rayOrigin;
1156
+ float lightDistSq = dot( toLight, toLight );
1157
+ float dist = sqrt( lightDistSq );
1158
+
1159
+ vec3 direction = toLight / max( dist, EPSILON );
1160
+ float cosTheta = dot( direction, normal );
1161
+
1162
+ float spotAttenuation = light.iesProfile != - 1 ?
1163
+ getPhotometricAttenuation( iesProfiles, light.iesProfile, direction, normal, u, v ) :
1164
+ getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
1165
+
1166
+ float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
1167
+ LightRecord lightRec;
1168
+ lightRec.type = light.type;
1169
+ lightRec.dist = dist;
1170
+ lightRec.direction = direction;
1171
+ lightRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
1172
+ lightRec.pdf = 1.0;
1173
+
1174
+ return lightRec;
1175
+
1176
+ }
1177
+
1178
+ LightRecord randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin, vec3 ruv ) {
1179
+
1180
+ LightRecord result;
1181
+
1182
+ // pick a random light
1183
+ uint l = uint( ruv.x * float( lightCount ) );
1184
+ Light light = readLightInfo( lights, l );
1185
+
1186
+ if ( light.type == SPOT_LIGHT_TYPE ) {
1187
+
1188
+ result = randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
1189
+
1190
+ } else if ( light.type == POINT_LIGHT_TYPE ) {
1191
+
1192
+ vec3 lightRay = light.u - rayOrigin;
1193
+ float lightDist = length( lightRay );
1194
+ float cutoffDistance = light.distance;
1195
+ float distanceFalloff = 1.0 / max( pow( lightDist, light.decay ), 0.01 );
1196
+ if ( cutoffDistance > 0.0 ) {
1197
+
1198
+ distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDist / cutoffDistance ) ) );
1199
+
1200
+ }
1201
+
1202
+ LightRecord rec;
1203
+ rec.direction = normalize( lightRay );
1204
+ rec.dist = length( lightRay );
1205
+ rec.pdf = 1.0;
1206
+ rec.emission = light.color * light.intensity * distanceFalloff;
1207
+ rec.type = light.type;
1208
+ result = rec;
1209
+
1210
+ } else if ( light.type == DIR_LIGHT_TYPE ) {
1211
+
1212
+ LightRecord rec;
1213
+ rec.dist = 1e10;
1214
+ rec.direction = light.u;
1215
+ rec.pdf = 1.0;
1216
+ rec.emission = light.color * light.intensity;
1217
+ rec.type = light.type;
1218
+
1219
+ result = rec;
1220
+
1221
+ } else {
1222
+
1223
+ // sample the light
1224
+ result = randomAreaLightSample( light, rayOrigin, ruv.yz );
1225
+
1226
+ }
1227
+
1228
+ return result;
1229
+
1230
+ }
1231
+
1232
+ `,za=`
1233
+
1234
+ vec3 sampleHemisphere( vec3 n, vec2 uv ) {
1235
+
1236
+ // https://www.rorydriscoll.com/2009/01/07/better-sampling/
1237
+ // https://graphics.pixar.com/library/OrthonormalB/paper.pdf
1238
+ float sign = n.z == 0.0 ? 1.0 : sign( n.z );
1239
+ float a = - 1.0 / ( sign + n.z );
1240
+ float b = n.x * n.y * a;
1241
+ vec3 b1 = vec3( 1.0 + sign * n.x * n.x * a, sign * b, - sign * n.x );
1242
+ vec3 b2 = vec3( b, sign + n.y * n.y * a, - n.y );
1243
+
1244
+ float r = sqrt( uv.x );
1245
+ float theta = 2.0 * PI * uv.y;
1246
+ float x = r * cos( theta );
1247
+ float y = r * sin( theta );
1248
+ return x * b1 + y * b2 + sqrt( 1.0 - uv.x ) * n;
1249
+
1250
+ }
1251
+
1252
+ vec2 sampleTriangle( vec2 a, vec2 b, vec2 c, vec2 r ) {
1253
+
1254
+ // get the edges of the triangle and the diagonal across the
1255
+ // center of the parallelogram
1256
+ vec2 e1 = a - b;
1257
+ vec2 e2 = c - b;
1258
+ vec2 diag = normalize( e1 + e2 );
1259
+
1260
+ // pick the point in the parallelogram
1261
+ if ( r.x + r.y > 1.0 ) {
1262
+
1263
+ r = vec2( 1.0 ) - r;
1264
+
1265
+ }
1266
+
1267
+ return e1 * r.x + e2 * r.y;
1268
+
1269
+ }
1270
+
1271
+ vec2 sampleCircle( vec2 uv ) {
1272
+
1273
+ float angle = 2.0 * PI * uv.x;
1274
+ float radius = sqrt( uv.y );
1275
+ return vec2( cos( angle ), sin( angle ) ) * radius;
1276
+
1277
+ }
1278
+
1279
+ vec3 sampleSphere( vec2 uv ) {
1280
+
1281
+ float u = ( uv.x - 0.5 ) * 2.0;
1282
+ float t = uv.y * PI * 2.0;
1283
+ float f = sqrt( 1.0 - u * u );
1284
+
1285
+ return vec3( f * cos( t ), f * sin( t ), u );
1286
+
1287
+ }
1288
+
1289
+ vec2 sampleRegularPolygon( int sides, vec3 uvw ) {
1290
+
1291
+ sides = max( sides, 3 );
1292
+
1293
+ vec3 r = uvw;
1294
+ float anglePerSegment = 2.0 * PI / float( sides );
1295
+ float segment = floor( float( sides ) * r.x );
1296
+
1297
+ float angle1 = anglePerSegment * segment;
1298
+ float angle2 = angle1 + anglePerSegment;
1299
+ vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
1300
+ vec2 b = vec2( 0.0, 0.0 );
1301
+ vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
1302
+
1303
+ return sampleTriangle( a, b, c, r.yz );
1304
+
1305
+ }
1306
+
1307
+ // samples an aperture shape with the given number of sides. 0 means circle
1308
+ vec2 sampleAperture( int blades, vec3 uvw ) {
1309
+
1310
+ return blades == 0 ?
1311
+ sampleCircle( uvw.xy ) :
1312
+ sampleRegularPolygon( blades, uvw );
1313
+
1314
+ }
1315
+
1316
+
1317
+ `,Ba=`
1318
+
1319
+ bool totalInternalReflection( float cosTheta, float eta ) {
1320
+
1321
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
1322
+ return eta * sinTheta > 1.0;
1323
+
1324
+ }
1325
+
1326
+ // https://google.github.io/filament/Filament.md.html#materialsystem/diffusebrdf
1327
+ float schlickFresnel( float cosine, float f0 ) {
1328
+
1329
+ return f0 + ( 1.0 - f0 ) * pow( 1.0 - cosine, 5.0 );
1330
+
1331
+ }
1332
+
1333
+ vec3 schlickFresnel( float cosine, vec3 f0 ) {
1334
+
1335
+ return f0 + ( 1.0 - f0 ) * pow( 1.0 - cosine, 5.0 );
1336
+
1337
+ }
1338
+
1339
+ vec3 schlickFresnel( float cosine, vec3 f0, vec3 f90 ) {
1340
+
1341
+ return f0 + ( f90 - f0 ) * pow( 1.0 - cosine, 5.0 );
1342
+
1343
+ }
1344
+
1345
+ float dielectricFresnel( float cosThetaI, float eta ) {
1346
+
1347
+ // https://schuttejoe.github.io/post/disneybsdf/
1348
+ float ni = eta;
1349
+ float nt = 1.0;
1350
+
1351
+ // Check for total internal reflection
1352
+ float sinThetaISq = 1.0f - cosThetaI * cosThetaI;
1353
+ float sinThetaTSq = eta * eta * sinThetaISq;
1354
+ if( sinThetaTSq >= 1.0 ) {
1355
+
1356
+ return 1.0;
1357
+
1358
+ }
1359
+
1360
+ float sinThetaT = sqrt( sinThetaTSq );
1361
+
1362
+ float cosThetaT = sqrt( max( 0.0, 1.0f - sinThetaT * sinThetaT ) );
1363
+ float rParallel = ( ( nt * cosThetaI ) - ( ni * cosThetaT ) ) / ( ( nt * cosThetaI ) + ( ni * cosThetaT ) );
1364
+ float rPerpendicular = ( ( ni * cosThetaI ) - ( nt * cosThetaT ) ) / ( ( ni * cosThetaI ) + ( nt * cosThetaT ) );
1365
+ return ( rParallel * rParallel + rPerpendicular * rPerpendicular ) / 2.0;
1366
+
1367
+ }
1368
+
1369
+ // https://raytracing.github.io/books/RayTracingInOneWeekend.html#dielectrics/schlickapproximation
1370
+ float iorRatioToF0( float eta ) {
1371
+
1372
+ return pow( ( 1.0 - eta ) / ( 1.0 + eta ), 2.0 );
1373
+
1374
+ }
1375
+
1376
+ vec3 evaluateFresnel( float cosTheta, float eta, vec3 f0, vec3 f90 ) {
1377
+
1378
+ if ( totalInternalReflection( cosTheta, eta ) ) {
1379
+
1380
+ return f90;
1381
+
1382
+ }
1383
+
1384
+ return schlickFresnel( cosTheta, f0, f90 );
1385
+
1386
+ }
1387
+
1388
+ // TODO: disney fresnel was removed and replaced with this fresnel function to better align with
1389
+ // the glTF but is causing blown out pixels. Should be revisited
1390
+ // float evaluateFresnelWeight( float cosTheta, float eta, float f0 ) {
1391
+
1392
+ // if ( totalInternalReflection( cosTheta, eta ) ) {
1393
+
1394
+ // return 1.0;
1395
+
1396
+ // }
1397
+
1398
+ // return schlickFresnel( cosTheta, f0 );
1399
+
1400
+ // }
1401
+
1402
+ // https://schuttejoe.github.io/post/disneybsdf/
1403
+ float disneyFresnel( vec3 wo, vec3 wi, vec3 wh, float f0, float eta, float metalness ) {
1404
+
1405
+ float dotHV = dot( wo, wh );
1406
+ if ( totalInternalReflection( dotHV, eta ) ) {
1407
+
1408
+ return 1.0;
1409
+
1410
+ }
1411
+
1412
+ float dotHL = dot( wi, wh );
1413
+ float dielectricFresnel = dielectricFresnel( abs( dotHV ), eta );
1414
+ float metallicFresnel = schlickFresnel( dotHL, f0 );
1415
+
1416
+ return mix( dielectricFresnel, metallicFresnel, metalness );
1417
+
1418
+ }
1419
+
1420
+ `,Va=`
1421
+
1422
+ // Fast arccos approximation used to remove banding artifacts caused by numerical errors in acos.
1423
+ // This is a cubic Lagrange interpolating polynomial for x = [-1, -1/2, 0, 1/2, 1].
1424
+ // For more information see: https://github.com/gkjohnson/three-gpu-pathtracer/pull/171#issuecomment-1152275248
1425
+ float acosApprox( float x ) {
1426
+
1427
+ x = clamp( x, -1.0, 1.0 );
1428
+ return ( - 0.69813170079773212 * x * x - 0.87266462599716477 ) * x + 1.5707963267948966;
1429
+
1430
+ }
1431
+
1432
+ // An acos with input values bound to the range [-1, 1].
1433
+ float acosSafe( float x ) {
1434
+
1435
+ return acos( clamp( x, -1.0, 1.0 ) );
1436
+
1437
+ }
1438
+
1439
+ float saturateCos( float val ) {
1440
+
1441
+ return clamp( val, 0.001, 1.0 );
1442
+
1443
+ }
1444
+
1445
+ float square( float t ) {
1446
+
1447
+ return t * t;
1448
+
1449
+ }
1450
+
1451
+ vec2 square( vec2 t ) {
1452
+
1453
+ return t * t;
1454
+
1455
+ }
1456
+
1457
+ vec3 square( vec3 t ) {
1458
+
1459
+ return t * t;
1460
+
1461
+ }
1462
+
1463
+ vec4 square( vec4 t ) {
1464
+
1465
+ return t * t;
1466
+
1467
+ }
1468
+
1469
+ vec2 rotateVector( vec2 v, float t ) {
1470
+
1471
+ float ac = cos( t );
1472
+ float as = sin( t );
1473
+ return vec2(
1474
+ v.x * ac - v.y * as,
1475
+ v.x * as + v.y * ac
1476
+ );
1477
+
1478
+ }
1479
+
1480
+ // forms a basis with the normal vector as Z
1481
+ mat3 getBasisFromNormal( vec3 normal ) {
1482
+
1483
+ vec3 other;
1484
+ if ( abs( normal.x ) > 0.5 ) {
1485
+
1486
+ other = vec3( 0.0, 1.0, 0.0 );
1487
+
1488
+ } else {
1489
+
1490
+ other = vec3( 1.0, 0.0, 0.0 );
1491
+
1492
+ }
1493
+
1494
+ vec3 ortho = normalize( cross( normal, other ) );
1495
+ vec3 ortho2 = normalize( cross( normal, ortho ) );
1496
+ return mat3( ortho2, ortho, normal );
1497
+
1498
+ }
1499
+
1500
+ `,Ha=`
1501
+
1502
+ // Finds the point where the ray intersects the plane defined by u and v and checks if this point
1503
+ // falls in the bounds of the rectangle on that same plane.
1504
+ // Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
1505
+ bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
1506
+
1507
+ float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
1508
+
1509
+ if ( t > EPSILON ) {
1510
+
1511
+ vec3 p = rayOrigin + rayDirection * t;
1512
+ vec3 vi = p - center;
1513
+
1514
+ // check if p falls inside the rectangle
1515
+ float a1 = dot( u, vi );
1516
+ if ( abs( a1 ) <= 0.5 ) {
1517
+
1518
+ float a2 = dot( v, vi );
1519
+ if ( abs( a2 ) <= 0.5 ) {
1520
+
1521
+ dist = t;
1522
+ return true;
1523
+
1524
+ }
1525
+
1526
+ }
1527
+
1528
+ }
1529
+
1530
+ return false;
1531
+
1532
+ }
1533
+
1534
+ // Finds the point where the ray intersects the plane defined by u and v and checks if this point
1535
+ // falls in the bounds of the circle on that same plane. See above URL for a description of the plane intersection algorithm.
1536
+ bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
1537
+
1538
+ float t = dot( position - rayOrigin, normal ) / dot( rayDirection, normal );
1539
+
1540
+ if ( t > EPSILON ) {
1541
+
1542
+ vec3 hit = rayOrigin + rayDirection * t;
1543
+ vec3 vi = hit - position;
1544
+
1545
+ float a1 = dot( u, vi );
1546
+ float a2 = dot( v, vi );
1547
+
1548
+ if( length( vec2( a1, a2 ) ) <= 0.5 ) {
1549
+
1550
+ dist = t;
1551
+ return true;
1552
+
1553
+ }
1554
+
1555
+ }
1556
+
1557
+ return false;
1558
+
1559
+ }
1560
+
1561
+ `,Ua=`
1562
+
1563
+ // add texel fetch functions for texture arrays
1564
+ vec4 texelFetch1D( sampler2DArray tex, int layer, uint index ) {
1565
+
1566
+ uint width = uint( textureSize( tex, 0 ).x );
1567
+ uvec2 uv;
1568
+ uv.x = index % width;
1569
+ uv.y = index / width;
1570
+
1571
+ return texelFetch( tex, ivec3( uv, layer ), 0 );
1572
+
1573
+ }
1574
+
1575
+ vec4 textureSampleBarycoord( sampler2DArray tex, int layer, vec3 barycoord, uvec3 faceIndices ) {
1576
+
1577
+ return
1578
+ barycoord.x * texelFetch1D( tex, layer, faceIndices.x ) +
1579
+ barycoord.y * texelFetch1D( tex, layer, faceIndices.y ) +
1580
+ barycoord.z * texelFetch1D( tex, layer, faceIndices.z );
1581
+
1582
+ }
1583
+
1584
+ `,Wa=`
1585
+
1586
+ // TODO: possibly this should be renamed something related to material or path tracing logic
1587
+
1588
+ #ifndef RAY_OFFSET
1589
+ #define RAY_OFFSET 1e-4
1590
+ #endif
1591
+
1592
+ // adjust the hit point by the surface normal by a factor of some offset and the
1593
+ // maximum component-wise value of the current point to accommodate floating point
1594
+ // error as values increase.
1595
+ vec3 stepRayOrigin( vec3 rayOrigin, vec3 rayDirection, vec3 offset, float dist ) {
1596
+
1597
+ vec3 point = rayOrigin + rayDirection * dist;
1598
+ vec3 absPoint = abs( point );
1599
+ float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
1600
+ return point + offset * ( maxPoint + 1.0 ) * RAY_OFFSET;
1601
+
1602
+ }
1603
+
1604
+ // https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md#attenuation
1605
+ vec3 transmissionAttenuation( float dist, vec3 attColor, float attDist ) {
1606
+
1607
+ vec3 ot = - log( attColor ) / attDist;
1608
+ return exp( - ot * dist );
1609
+
1610
+ }
1611
+
1612
+ vec3 getHalfVector( vec3 wi, vec3 wo, float eta ) {
1613
+
1614
+ // get the half vector - assuming if the light incident vector is on the other side
1615
+ // of the that it's transmissive.
1616
+ vec3 h;
1617
+ if ( wi.z > 0.0 ) {
1618
+
1619
+ h = normalize( wi + wo );
1620
+
1621
+ } else {
1622
+
1623
+ // Scale by the ior ratio to retrieve the appropriate half vector
1624
+ // From Section 2.2 on computing the transmission half vector:
1625
+ // https://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf
1626
+ h = normalize( wi + wo * eta );
1627
+
1628
+ }
1629
+
1630
+ h *= sign( h.z );
1631
+ return h;
1632
+
1633
+ }
1634
+
1635
+ vec3 getHalfVector( vec3 a, vec3 b ) {
1636
+
1637
+ return normalize( a + b );
1638
+
1639
+ }
1640
+
1641
+ // The discrepancy between interpolated surface normal and geometry normal can cause issues when a ray
1642
+ // is cast that is on the top side of the geometry normal plane but below the surface normal plane. If
1643
+ // we find a ray like that we ignore it to avoid artifacts.
1644
+ // This function returns if the direction is on the same side of both planes.
1645
+ bool isDirectionValid( vec3 direction, vec3 surfaceNormal, vec3 geometryNormal ) {
1646
+
1647
+ bool aboveSurfaceNormal = dot( direction, surfaceNormal ) > 0.0;
1648
+ bool aboveGeometryNormal = dot( direction, geometryNormal ) > 0.0;
1649
+ return aboveSurfaceNormal == aboveGeometryNormal;
1650
+
1651
+ }
1652
+
1653
+ // ray sampling x and z are swapped to align with expected background view
1654
+ vec2 equirectDirectionToUv( vec3 direction ) {
1655
+
1656
+ // from Spherical.setFromCartesianCoords
1657
+ vec2 uv = vec2( atan( direction.z, direction.x ), acos( direction.y ) );
1658
+ uv /= vec2( 2.0 * PI, PI );
1659
+
1660
+ // apply adjustments to get values in range [0, 1] and y right side up
1661
+ uv.x += 0.5;
1662
+ uv.y = 1.0 - uv.y;
1663
+ return uv;
1664
+
1665
+ }
1666
+
1667
+ vec3 equirectUvToDirection( vec2 uv ) {
1668
+
1669
+ // undo above adjustments
1670
+ uv.x -= 0.5;
1671
+ uv.y = 1.0 - uv.y;
1672
+
1673
+ // from Vector3.setFromSphericalCoords
1674
+ float theta = uv.x * 2.0 * PI;
1675
+ float phi = uv.y * PI;
1676
+
1677
+ float sinPhi = sin( phi );
1678
+
1679
+ return vec3( sinPhi * cos( theta ), cos( phi ), sinPhi * sin( theta ) );
1680
+
1681
+ }
1682
+
1683
+ // power heuristic for multiple importance sampling
1684
+ float misHeuristic( float a, float b ) {
1685
+
1686
+ float aa = a * a;
1687
+ float bb = b * b;
1688
+ return aa / ( aa + bb );
1689
+
1690
+ }
1691
+
1692
+ // tentFilter from Peter Shirley's 'Realistic Ray Tracing (2nd Edition)' book, pg. 60
1693
+ // erichlof/THREE.js-PathTracing-Renderer/
1694
+ float tentFilter( float x ) {
1695
+
1696
+ return x < 0.5 ? sqrt( 2.0 * x ) - 1.0 : 1.0 - sqrt( 2.0 - ( 2.0 * x ) );
1697
+
1698
+ }
1699
+ `,Ga=`
1700
+
1701
+ // https://www.shadertoy.com/view/wltcRS
1702
+ uvec4 WHITE_NOISE_SEED;
1703
+
1704
+ void rng_initialize( vec2 p, int frame ) {
1705
+
1706
+ // white noise seed
1707
+ WHITE_NOISE_SEED = uvec4( p, uint( frame ), uint( p.x ) + uint( p.y ) );
1708
+
1709
+ }
1710
+
1711
+ // https://www.pcg-random.org/
1712
+ void pcg4d( inout uvec4 v ) {
1713
+
1714
+ v = v * 1664525u + 1013904223u;
1715
+ v.x += v.y * v.w;
1716
+ v.y += v.z * v.x;
1717
+ v.z += v.x * v.y;
1718
+ v.w += v.y * v.z;
1719
+ v = v ^ ( v >> 16u );
1720
+ v.x += v.y*v.w;
1721
+ v.y += v.z*v.x;
1722
+ v.z += v.x*v.y;
1723
+ v.w += v.y*v.z;
1724
+
1725
+ }
1726
+
1727
+ // returns [ 0, 1 ]
1728
+ float pcgRand() {
1729
+
1730
+ pcg4d( WHITE_NOISE_SEED );
1731
+ return float( WHITE_NOISE_SEED.x ) / float( 0xffffffffu );
1732
+
1733
+ }
1734
+
1735
+ vec2 pcgRand2() {
1736
+
1737
+ pcg4d( WHITE_NOISE_SEED );
1738
+ return vec2( WHITE_NOISE_SEED.xy ) / float(0xffffffffu);
1739
+
1740
+ }
1741
+
1742
+ vec3 pcgRand3() {
1743
+
1744
+ pcg4d( WHITE_NOISE_SEED );
1745
+ return vec3( WHITE_NOISE_SEED.xyz ) / float( 0xffffffffu );
1746
+
1747
+ }
1748
+
1749
+ vec4 pcgRand4() {
1750
+
1751
+ pcg4d( WHITE_NOISE_SEED );
1752
+ return vec4( WHITE_NOISE_SEED ) / float( 0xffffffffu );
1753
+
1754
+ }
1755
+ `,Ka=`
1756
+
1757
+ uniform sampler2D stratifiedTexture;
1758
+ uniform sampler2D stratifiedOffsetTexture;
1759
+
1760
+ uint sobolPixelIndex = 0u;
1761
+ uint sobolPathIndex = 0u;
1762
+ uint sobolBounceIndex = 0u;
1763
+ vec4 pixelSeed = vec4( 0 );
1764
+
1765
+ vec4 rand4( int v ) {
1766
+
1767
+ ivec2 uv = ivec2( v, sobolBounceIndex );
1768
+ vec4 stratifiedSample = texelFetch( stratifiedTexture, uv, 0 );
1769
+ return fract( stratifiedSample + pixelSeed.r ); // blue noise + stratified samples
1770
+
1771
+ }
1772
+
1773
+ vec3 rand3( int v ) {
1774
+
1775
+ return rand4( v ).xyz;
1776
+
1777
+ }
1778
+
1779
+ vec2 rand2( int v ) {
1780
+
1781
+ return rand4( v ).xy;
1782
+
1783
+ }
1784
+
1785
+ float rand( int v ) {
1786
+
1787
+ return rand4( v ).x;
1788
+
1789
+ }
1790
+
1791
+ void rng_initialize( vec2 screenCoord, int frame ) {
1792
+
1793
+ // tile the small noise texture across the entire screen
1794
+ ivec2 noiseSize = ivec2( textureSize( stratifiedOffsetTexture, 0 ) );
1795
+ ivec2 pixel = ivec2( screenCoord.xy ) % noiseSize;
1796
+ vec2 pixelWidth = 1.0 / vec2( noiseSize );
1797
+ vec2 uv = vec2( pixel ) * pixelWidth + pixelWidth * 0.5;
1798
+
1799
+ // note that using "texelFetch" here seems to break Android for some reason
1800
+ pixelSeed = texture( stratifiedOffsetTexture, uv );
1801
+
1802
+ }
1803
+
1804
+ `,qa=`
1805
+
1806
+ // diffuse
1807
+ float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
1808
+
1809
+ // https://schuttejoe.github.io/post/disneybsdf/
1810
+ float fl = schlickFresnel( wi.z, 0.0 );
1811
+ float fv = schlickFresnel( wo.z, 0.0 );
1812
+
1813
+ float metalFactor = ( 1.0 - surf.metalness );
1814
+ float transFactor = ( 1.0 - surf.transmission );
1815
+ float rr = 0.5 + 2.0 * surf.roughness * fl * fl;
1816
+ float retro = rr * ( fl + fv + fl * fv * ( rr - 1.0f ) );
1817
+ float lambert = ( 1.0f - 0.5f * fl ) * ( 1.0f - 0.5f * fv );
1818
+
1819
+ // TODO: subsurface approx?
1820
+
1821
+ // float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
1822
+ float F = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
1823
+ color = ( 1.0 - F ) * transFactor * metalFactor * wi.z * surf.color * ( retro + lambert ) / PI;
1824
+
1825
+ return wi.z / PI;
1826
+
1827
+ }
1828
+
1829
+ vec3 diffuseDirection( vec3 wo, SurfaceRecord surf ) {
1830
+
1831
+ vec3 lightDirection = sampleSphere( rand2( 11 ) );
1832
+ lightDirection.z += 1.0;
1833
+ lightDirection = normalize( lightDirection );
1834
+
1835
+ return lightDirection;
1836
+
1837
+ }
1838
+
1839
+ // specular
1840
+ float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
1841
+
1842
+ // if roughness is set to 0 then D === NaN which results in black pixels
1843
+ float metalness = surf.metalness;
1844
+ float roughness = surf.filteredRoughness;
1845
+
1846
+ float eta = surf.eta;
1847
+ float f0 = surf.f0;
1848
+
1849
+ vec3 f0Color = mix( f0 * surf.specularColor * surf.specularIntensity, surf.color, surf.metalness );
1850
+ vec3 f90Color = vec3( mix( surf.specularIntensity, 1.0, surf.metalness ) );
1851
+ vec3 F = evaluateFresnel( dot( wo, wh ), eta, f0Color, f90Color );
1852
+
1853
+ vec3 iridescenceF = evalIridescence( 1.0, surf.iridescenceIor, dot( wi, wh ), surf.iridescenceThickness, f0Color );
1854
+ F = mix( F, iridescenceF, surf.iridescence );
1855
+
1856
+ // PDF
1857
+ // See 14.1.1 Microfacet BxDFs in https://www.pbr-book.org/
1858
+ float incidentTheta = acos( wo.z );
1859
+ float G = ggxShadowMaskG2( wi, wo, roughness );
1860
+ float D = ggxDistribution( wh, roughness );
1861
+ float G1 = ggxShadowMaskG1( incidentTheta, roughness );
1862
+ float ggxPdf = D * G1 * max( 0.0, abs( dot( wo, wh ) ) ) / abs ( wo.z );
1863
+
1864
+ color = wi.z * F * G * D / ( 4.0 * abs( wi.z * wo.z ) );
1865
+ return ggxPdf / ( 4.0 * dot( wo, wh ) );
1866
+
1867
+ }
1868
+
1869
+ vec3 specularDirection( vec3 wo, SurfaceRecord surf ) {
1870
+
1871
+ // sample ggx vndf distribution which gives a new normal
1872
+ float roughness = surf.filteredRoughness;
1873
+ vec3 halfVector = ggxDirection(
1874
+ wo,
1875
+ vec2( roughness ),
1876
+ rand2( 12 )
1877
+ );
1878
+
1879
+ // apply to new ray by reflecting off the new normal
1880
+ return - reflect( wo, halfVector );
1881
+
1882
+ }
1883
+
1884
+
1885
+ // transmission
1886
+ /*
1887
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
1888
+
1889
+ // See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
1890
+
1891
+ float filteredRoughness = surf.filteredRoughness;
1892
+ float eta = surf.eta;
1893
+ bool frontFace = surf.frontFace;
1894
+ bool thinFilm = surf.thinFilm;
1895
+
1896
+ color = surf.transmission * surf.color;
1897
+
1898
+ float denom = pow( eta * dot( wi, wh ) + dot( wo, wh ), 2.0 );
1899
+ return ggxPDF( wo, wh, filteredRoughness ) / denom;
1900
+
1901
+ }
1902
+
1903
+ vec3 transmissionDirection( vec3 wo, SurfaceRecord surf ) {
1904
+
1905
+ float filteredRoughness = surf.filteredRoughness;
1906
+ float eta = surf.eta;
1907
+ bool frontFace = surf.frontFace;
1908
+
1909
+ // sample ggx vndf distribution which gives a new normal
1910
+ vec3 halfVector = ggxDirection(
1911
+ wo,
1912
+ vec2( filteredRoughness ),
1913
+ rand2( 13 )
1914
+ );
1915
+
1916
+ vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
1917
+ if ( surf.thinFilm ) {
1918
+
1919
+ lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / eta );
1920
+
1921
+ }
1922
+
1923
+ return normalize( lightDirection );
1924
+
1925
+ }
1926
+ */
1927
+
1928
+ // TODO: This is just using a basic cosine-weighted specular distribution with an
1929
+ // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
1930
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
1931
+
1932
+ color = surf.transmission * surf.color;
1933
+
1934
+ // PDF
1935
+ // float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
1936
+ // float F = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
1937
+ // if ( F >= 1.0 ) {
1938
+
1939
+ // return 0.0;
1940
+
1941
+ // }
1942
+
1943
+ // return 1.0 / ( 1.0 - F );
1944
+
1945
+ // reverted to previous to transmission. The above was causing black pixels
1946
+ float eta = surf.eta;
1947
+ float f0 = surf.f0;
1948
+ float cosTheta = min( wo.z, 1.0 );
1949
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
1950
+ float reflectance = schlickFresnel( cosTheta, f0 );
1951
+ bool cannotRefract = eta * sinTheta > 1.0;
1952
+ if ( cannotRefract ) {
1953
+
1954
+ return 0.0;
1955
+
1956
+ }
1957
+
1958
+ return 1.0 / ( 1.0 - reflectance );
1959
+
1960
+ }
1961
+
1962
+ vec3 transmissionDirection( vec3 wo, SurfaceRecord surf ) {
1963
+
1964
+ float roughness = surf.filteredRoughness;
1965
+ float eta = surf.eta;
1966
+ vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere( rand2( 13 ) ) * roughness );
1967
+ vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
1968
+
1969
+ if ( surf.thinFilm ) {
1970
+
1971
+ lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / eta );
1972
+
1973
+ }
1974
+ return normalize( lightDirection );
1975
+
1976
+ }
1977
+
1978
+ // clearcoat
1979
+ float clearcoatEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
1980
+
1981
+ float ior = 1.5;
1982
+ float f0 = iorRatioToF0( ior );
1983
+ bool frontFace = surf.frontFace;
1984
+ float roughness = surf.filteredClearcoatRoughness;
1985
+
1986
+ float eta = frontFace ? 1.0 / ior : ior;
1987
+ float G = ggxShadowMaskG2( wi, wo, roughness );
1988
+ float D = ggxDistribution( wh, roughness );
1989
+ float F = schlickFresnel( dot( wi, wh ), f0 );
1990
+
1991
+ float fClearcoat = F * D * G / ( 4.0 * abs( wi.z * wo.z ) );
1992
+ color = color * ( 1.0 - surf.clearcoat * F ) + fClearcoat * surf.clearcoat * wi.z;
1993
+
1994
+ // PDF
1995
+ // See equation (27) in http://jcgt.org/published/0003/02/03/
1996
+ return ggxPDF( wo, wh, roughness ) / ( 4.0 * dot( wi, wh ) );
1997
+
1998
+ }
1999
+
2000
+ vec3 clearcoatDirection( vec3 wo, SurfaceRecord surf ) {
2001
+
2002
+ // sample ggx vndf distribution which gives a new normal
2003
+ float roughness = surf.filteredClearcoatRoughness;
2004
+ vec3 halfVector = ggxDirection(
2005
+ wo,
2006
+ vec2( roughness ),
2007
+ rand2( 14 )
2008
+ );
2009
+
2010
+ // apply to new ray by reflecting off the new normal
2011
+ return - reflect( wo, halfVector );
2012
+
2013
+ }
2014
+
2015
+ // sheen
2016
+ vec3 sheenColor( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf ) {
2017
+
2018
+ float cosThetaO = saturateCos( wo.z );
2019
+ float cosThetaI = saturateCos( wi.z );
2020
+ float cosThetaH = wh.z;
2021
+
2022
+ float D = velvetD( cosThetaH, surf.sheenRoughness );
2023
+ float G = velvetG( cosThetaO, cosThetaI, surf.sheenRoughness );
2024
+
2025
+ // See equation (1) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
2026
+ vec3 color = surf.sheenColor;
2027
+ color *= D * G / ( 4.0 * abs( cosThetaO * cosThetaI ) );
2028
+ color *= wi.z;
2029
+
2030
+ return color;
2031
+
2032
+ }
2033
+
2034
+ // bsdf
2035
+ void getLobeWeights(
2036
+ vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRecord surf,
2037
+ inout float diffuseWeight, inout float specularWeight, inout float transmissionWeight, inout float clearcoatWeight
2038
+ ) {
2039
+
2040
+ float metalness = surf.metalness;
2041
+ float transmission = surf.transmission;
2042
+ // float fEstimate = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
2043
+ float fEstimate = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
2044
+
2045
+ float transSpecularProb = mix( max( 0.25, fEstimate ), 1.0, metalness );
2046
+ float diffSpecularProb = 0.5 + 0.5 * metalness;
2047
+
2048
+ diffuseWeight = ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb );
2049
+ specularWeight = transmission * transSpecularProb + ( 1.0 - transmission ) * diffSpecularProb;
2050
+ transmissionWeight = transmission * ( 1.0 - transSpecularProb );
2051
+ clearcoatWeight = surf.clearcoat * schlickFresnel( clearcoatWo.z, 0.04 );
2052
+
2053
+ float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
2054
+ diffuseWeight /= totalWeight;
2055
+ specularWeight /= totalWeight;
2056
+ transmissionWeight /= totalWeight;
2057
+ clearcoatWeight /= totalWeight;
2058
+ }
2059
+
2060
+ float bsdfEval(
2061
+ vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf,
2062
+ float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, inout float specularPdf, inout vec3 color
2063
+ ) {
2064
+
2065
+ float metalness = surf.metalness;
2066
+ float transmission = surf.transmission;
2067
+
2068
+ float spdf = 0.0;
2069
+ float dpdf = 0.0;
2070
+ float tpdf = 0.0;
2071
+ float cpdf = 0.0;
2072
+ color = vec3( 0.0 );
2073
+
2074
+ vec3 halfVector = getHalfVector( wi, wo, surf.eta );
2075
+
2076
+ // diffuse
2077
+ if ( diffuseWeight > 0.0 && wi.z > 0.0 ) {
2078
+
2079
+ dpdf = diffuseEval( wo, wi, halfVector, surf, color );
2080
+ color *= 1.0 - surf.transmission;
2081
+
2082
+ }
2083
+
2084
+ // ggx specular
2085
+ if ( specularWeight > 0.0 && wi.z > 0.0 ) {
2086
+
2087
+ vec3 outColor;
2088
+ spdf = specularEval( wo, wi, getHalfVector( wi, wo ), surf, outColor );
2089
+ color += outColor;
2090
+
2091
+ }
2092
+
2093
+ // transmission
2094
+ if ( transmissionWeight > 0.0 && wi.z < 0.0 ) {
2095
+
2096
+ tpdf = transmissionEval( wo, wi, halfVector, surf, color );
2097
+
2098
+ }
2099
+
2100
+ // sheen
2101
+ color *= mix( 1.0, sheenAlbedoScaling( wo, wi, surf ), surf.sheen );
2102
+ color += sheenColor( wo, wi, halfVector, surf ) * surf.sheen;
2103
+
2104
+ // clearcoat
2105
+ if ( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
2106
+
2107
+ vec3 clearcoatHalfVector = getHalfVector( clearcoatWo, clearcoatWi );
2108
+ cpdf = clearcoatEval( clearcoatWo, clearcoatWi, clearcoatHalfVector, surf, color );
2109
+
2110
+ }
2111
+
2112
+ float pdf =
2113
+ dpdf * diffuseWeight
2114
+ + spdf * specularWeight
2115
+ + tpdf * transmissionWeight
2116
+ + cpdf * clearcoatWeight;
2117
+
2118
+ // retrieve specular rays for the shadows flag
2119
+ specularPdf = spdf * specularWeight + cpdf * clearcoatWeight;
2120
+
2121
+ return pdf;
2122
+
2123
+ }
2124
+
2125
+ float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, inout vec3 color ) {
2126
+
2127
+ if ( surf.volumeParticle ) {
2128
+
2129
+ color = surf.color / ( 4.0 * PI );
2130
+ return 1.0 / ( 4.0 * PI );
2131
+
2132
+ }
2133
+
2134
+ vec3 wo = normalize( surf.normalInvBasis * worldWo );
2135
+ vec3 wi = normalize( surf.normalInvBasis * worldWi );
2136
+
2137
+ vec3 clearcoatWo = normalize( surf.clearcoatInvBasis * worldWo );
2138
+ vec3 clearcoatWi = normalize( surf.clearcoatInvBasis * worldWi );
2139
+
2140
+ vec3 wh = getHalfVector( wo, wi, surf.eta );
2141
+ float diffuseWeight;
2142
+ float specularWeight;
2143
+ float transmissionWeight;
2144
+ float clearcoatWeight;
2145
+ getLobeWeights( wo, wi, wh, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
2146
+
2147
+ float specularPdf;
2148
+ return bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight, specularPdf, color );
2149
+
2150
+ }
2151
+
2152
+ ScatterRecord bsdfSample( vec3 worldWo, SurfaceRecord surf ) {
2153
+
2154
+ if ( surf.volumeParticle ) {
2155
+
2156
+ ScatterRecord sampleRec;
2157
+ sampleRec.specularPdf = 0.0;
2158
+ sampleRec.pdf = 1.0 / ( 4.0 * PI );
2159
+ sampleRec.direction = sampleSphere( rand2( 16 ) );
2160
+ sampleRec.color = surf.color / ( 4.0 * PI );
2161
+ return sampleRec;
2162
+
2163
+ }
2164
+
2165
+ vec3 wo = normalize( surf.normalInvBasis * worldWo );
2166
+ vec3 clearcoatWo = normalize( surf.clearcoatInvBasis * worldWo );
2167
+ mat3 normalBasis = surf.normalBasis;
2168
+ mat3 invBasis = surf.normalInvBasis;
2169
+ mat3 clearcoatNormalBasis = surf.clearcoatBasis;
2170
+ mat3 clearcoatInvBasis = surf.clearcoatInvBasis;
2171
+
2172
+ float diffuseWeight;
2173
+ float specularWeight;
2174
+ float transmissionWeight;
2175
+ float clearcoatWeight;
2176
+ // using normal and basically-reflected ray since we don't have proper half vector here
2177
+ getLobeWeights( wo, wo, vec3( 0, 0, 1 ), clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
2178
+
2179
+ float pdf[4];
2180
+ pdf[0] = diffuseWeight;
2181
+ pdf[1] = specularWeight;
2182
+ pdf[2] = transmissionWeight;
2183
+ pdf[3] = clearcoatWeight;
2184
+
2185
+ float cdf[4];
2186
+ cdf[0] = pdf[0];
2187
+ cdf[1] = pdf[1] + cdf[0];
2188
+ cdf[2] = pdf[2] + cdf[1];
2189
+ cdf[3] = pdf[3] + cdf[2];
2190
+
2191
+ if( cdf[3] != 0.0 ) {
2192
+
2193
+ float invMaxCdf = 1.0 / cdf[3];
2194
+ cdf[0] *= invMaxCdf;
2195
+ cdf[1] *= invMaxCdf;
2196
+ cdf[2] *= invMaxCdf;
2197
+ cdf[3] *= invMaxCdf;
2198
+
2199
+ } else {
2200
+
2201
+ cdf[0] = 1.0;
2202
+ cdf[1] = 0.0;
2203
+ cdf[2] = 0.0;
2204
+ cdf[3] = 0.0;
2205
+
2206
+ }
2207
+
2208
+ vec3 wi;
2209
+ vec3 clearcoatWi;
2210
+
2211
+ float r = rand( 15 );
2212
+ if ( r <= cdf[0] ) { // diffuse
2213
+
2214
+ wi = diffuseDirection( wo, surf );
2215
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
2216
+
2217
+ } else if ( r <= cdf[1] ) { // specular
2218
+
2219
+ wi = specularDirection( wo, surf );
2220
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
2221
+
2222
+ } else if ( r <= cdf[2] ) { // transmission / refraction
2223
+
2224
+ wi = transmissionDirection( wo, surf );
2225
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
2226
+
2227
+ } else if ( r <= cdf[3] ) { // clearcoat
2228
+
2229
+ clearcoatWi = clearcoatDirection( clearcoatWo, surf );
2230
+ wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
2231
+
2232
+ }
2233
+
2234
+ ScatterRecord result;
2235
+ result.pdf = bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight, result.specularPdf, result.color );
2236
+ result.direction = normalize( surf.normalBasis * wi );
2237
+
2238
+ return result;
2239
+
2240
+ }
2241
+
2242
+ `,Ja=`
2243
+
2244
+ // returns the hit distance given the material density
2245
+ float intersectFogVolume( Material material, float u ) {
2246
+
2247
+ // https://raytracing.github.io/books/RayTracingTheNextWeek.html#volumes/constantdensitymediums
2248
+ return material.opacity == 0.0 ? INFINITY : ( - 1.0 / material.opacity ) * log( u );
2249
+
2250
+ }
2251
+
2252
+ ScatterRecord sampleFogVolume( SurfaceRecord surf, vec2 uv ) {
2253
+
2254
+ ScatterRecord sampleRec;
2255
+ sampleRec.specularPdf = 0.0;
2256
+ sampleRec.pdf = 1.0 / ( 2.0 * PI );
2257
+ sampleRec.direction = sampleSphere( uv );
2258
+ sampleRec.color = surf.color;
2259
+ return sampleRec;
2260
+
2261
+ }
2262
+
2263
+ `,Ya=`
2264
+
2265
+ // The GGX functions provide sampling and distribution information for normals as output so
2266
+ // in order to get probability of scatter direction the half vector must be computed and provided.
2267
+ // [0] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
2268
+ // [1] https://hal.archives-ouvertes.fr/hal-01509746/document
2269
+ // [2] http://jcgt.org/published/0007/04/01/
2270
+ // [4] http://jcgt.org/published/0003/02/03/
2271
+
2272
+ // trowbridge-reitz === GGX === GTR
2273
+
2274
+ vec3 ggxDirection( vec3 incidentDir, vec2 roughness, vec2 uv ) {
2275
+
2276
+ // TODO: try GGXVNDF implementation from reference [2], here. Needs to update ggxDistribution
2277
+ // function below, as well
2278
+
2279
+ // Implementation from reference [1]
2280
+ // stretch view
2281
+ vec3 V = normalize( vec3( roughness * incidentDir.xy, incidentDir.z ) );
2282
+
2283
+ // orthonormal basis
2284
+ vec3 T1 = ( V.z < 0.9999 ) ? normalize( cross( V, vec3( 0.0, 0.0, 1.0 ) ) ) : vec3( 1.0, 0.0, 0.0 );
2285
+ vec3 T2 = cross( T1, V );
2286
+
2287
+ // sample point with polar coordinates (r, phi)
2288
+ float a = 1.0 / ( 1.0 + V.z );
2289
+ float r = sqrt( uv.x );
2290
+ float phi = ( uv.y < a ) ? uv.y / a * PI : PI + ( uv.y - a ) / ( 1.0 - a ) * PI;
2291
+ float P1 = r * cos( phi );
2292
+ float P2 = r * sin( phi ) * ( ( uv.y < a ) ? 1.0 : V.z );
2293
+
2294
+ // compute normal
2295
+ vec3 N = P1 * T1 + P2 * T2 + V * sqrt( max( 0.0, 1.0 - P1 * P1 - P2 * P2 ) );
2296
+
2297
+ // unstretch
2298
+ N = normalize( vec3( roughness * N.xy, max( 0.0, N.z ) ) );
2299
+
2300
+ return N;
2301
+
2302
+ }
2303
+
2304
+ // Below are PDF and related functions for use in a Monte Carlo path tracer
2305
+ // as specified in Appendix B of the following paper
2306
+ // See equation (34) from reference [0]
2307
+ float ggxLamda( float theta, float roughness ) {
2308
+
2309
+ float tanTheta = tan( theta );
2310
+ float tanTheta2 = tanTheta * tanTheta;
2311
+ float alpha2 = roughness * roughness;
2312
+
2313
+ float numerator = - 1.0 + sqrt( 1.0 + alpha2 * tanTheta2 );
2314
+ return numerator / 2.0;
2315
+
2316
+ }
2317
+
2318
+ // See equation (34) from reference [0]
2319
+ float ggxShadowMaskG1( float theta, float roughness ) {
2320
+
2321
+ return 1.0 / ( 1.0 + ggxLamda( theta, roughness ) );
2322
+
2323
+ }
2324
+
2325
+ // See equation (125) from reference [4]
2326
+ float ggxShadowMaskG2( vec3 wi, vec3 wo, float roughness ) {
2327
+
2328
+ float incidentTheta = acos( wi.z );
2329
+ float scatterTheta = acos( wo.z );
2330
+ return 1.0 / ( 1.0 + ggxLamda( incidentTheta, roughness ) + ggxLamda( scatterTheta, roughness ) );
2331
+
2332
+ }
2333
+
2334
+ // See equation (33) from reference [0]
2335
+ float ggxDistribution( vec3 halfVector, float roughness ) {
2336
+
2337
+ float a2 = roughness * roughness;
2338
+ a2 = max( EPSILON, a2 );
2339
+ float cosTheta = halfVector.z;
2340
+ float cosTheta4 = pow( cosTheta, 4.0 );
2341
+
2342
+ if ( cosTheta == 0.0 ) return 0.0;
2343
+
2344
+ float theta = acosSafe( halfVector.z );
2345
+ float tanTheta = tan( theta );
2346
+ float tanTheta2 = pow( tanTheta, 2.0 );
2347
+
2348
+ float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
2349
+ return ( a2 / denom );
2350
+
2351
+ }
2352
+
2353
+ // See equation (3) from reference [2]
2354
+ float ggxPDF( vec3 wi, vec3 halfVector, float roughness ) {
2355
+
2356
+ float incidentTheta = acos( wi.z );
2357
+ float D = ggxDistribution( halfVector, roughness );
2358
+ float G1 = ggxShadowMaskG1( incidentTheta, roughness );
2359
+
2360
+ return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
2361
+
2362
+ }
2363
+
2364
+ `,Xa=`
2365
+
2366
+ // XYZ to sRGB color space
2367
+ const mat3 XYZ_TO_REC709 = mat3(
2368
+ 3.2404542, -0.9692660, 0.0556434,
2369
+ -1.5371385, 1.8760108, -0.2040259,
2370
+ -0.4985314, 0.0415560, 1.0572252
2371
+ );
2372
+
2373
+ vec3 fresnel0ToIor( vec3 fresnel0 ) {
2374
+
2375
+ vec3 sqrtF0 = sqrt( fresnel0 );
2376
+ return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );
2377
+
2378
+ }
2379
+
2380
+ // Conversion FO/IOR
2381
+ vec3 iorToFresnel0( vec3 transmittedIor, float incidentIor ) {
2382
+
2383
+ return square( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );
2384
+
2385
+ }
2386
+
2387
+ // ior is a value between 1.0 and 3.0. 1.0 is air interface
2388
+ float iorToFresnel0( float transmittedIor, float incidentIor ) {
2389
+
2390
+ return square( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ) );
2391
+
2392
+ }
2393
+
2394
+ // Fresnel equations for dielectric/dielectric interfaces. See https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html
2395
+ vec3 evalSensitivity( float OPD, vec3 shift ) {
2396
+
2397
+ float phase = 2.0 * PI * OPD * 1.0e-9;
2398
+
2399
+ vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );
2400
+ vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );
2401
+ vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );
2402
+
2403
+ vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - square( phase ) * var );
2404
+ xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * square( phase ) );
2405
+ xyz /= 1.0685e-7;
2406
+
2407
+ vec3 srgb = XYZ_TO_REC709 * xyz;
2408
+ return srgb;
2409
+
2410
+ }
2411
+
2412
+ // See Section 4. Analytic Spectral Integration, A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence, https://hal.archives-ouvertes.fr/hal-01518344/document
2413
+ vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {
2414
+
2415
+ vec3 I;
2416
+
2417
+ // Force iridescenceIor -> outsideIOR when thinFilmThickness -> 0.0
2418
+ float iridescenceIor = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );
2419
+
2420
+ // Evaluate the cosTheta on the base layer (Snell law)
2421
+ float sinTheta2Sq = square( outsideIOR / iridescenceIor ) * ( 1.0 - square( cosTheta1 ) );
2422
+
2423
+ // Handle TIR:
2424
+ float cosTheta2Sq = 1.0 - sinTheta2Sq;
2425
+ if ( cosTheta2Sq < 0.0 ) {
2426
+
2427
+ return vec3( 1.0 );
2428
+
2429
+ }
2430
+
2431
+ float cosTheta2 = sqrt( cosTheta2Sq );
2432
+
2433
+ // First interface
2434
+ float R0 = iorToFresnel0( iridescenceIor, outsideIOR );
2435
+ float R12 = schlickFresnel( cosTheta1, R0 );
2436
+ float R21 = R12;
2437
+ float T121 = 1.0 - R12;
2438
+ float phi12 = 0.0;
2439
+ if ( iridescenceIor < outsideIOR ) {
2440
+
2441
+ phi12 = PI;
2442
+
2443
+ }
2444
+
2445
+ float phi21 = PI - phi12;
2446
+
2447
+ // Second interface
2448
+ vec3 baseIOR = fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0
2449
+ vec3 R1 = iorToFresnel0( baseIOR, iridescenceIor );
2450
+ vec3 R23 = schlickFresnel( cosTheta2, R1 );
2451
+ vec3 phi23 = vec3( 0.0 );
2452
+ if ( baseIOR[0] < iridescenceIor ) {
2453
+
2454
+ phi23[ 0 ] = PI;
2455
+
2456
+ }
2457
+
2458
+ if ( baseIOR[1] < iridescenceIor ) {
2459
+
2460
+ phi23[ 1 ] = PI;
2461
+
2462
+ }
2463
+
2464
+ if ( baseIOR[2] < iridescenceIor ) {
2465
+
2466
+ phi23[ 2 ] = PI;
2467
+
2468
+ }
2469
+
2470
+ // Phase shift
2471
+ float OPD = 2.0 * iridescenceIor * thinFilmThickness * cosTheta2;
2472
+ vec3 phi = vec3( phi21 ) + phi23;
2473
+
2474
+ // Compound terms
2475
+ vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );
2476
+ vec3 r123 = sqrt( R123 );
2477
+ vec3 Rs = square( T121 ) * R23 / ( vec3( 1.0 ) - R123 );
2478
+
2479
+ // Reflectance term for m = 0 (DC term amplitude)
2480
+ vec3 C0 = R12 + Rs;
2481
+ I = C0;
2482
+
2483
+ // Reflectance term for m > 0 (pairs of diracs)
2484
+ vec3 Cm = Rs - T121;
2485
+ for ( int m = 1; m <= 2; ++ m ) {
2486
+
2487
+ Cm *= r123;
2488
+ vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );
2489
+ I += Cm * Sm;
2490
+
2491
+ }
2492
+
2493
+ // Since out of gamut colors might be produced, negative color values are clamped to 0.
2494
+ return max( I, vec3( 0.0 ) );
2495
+
2496
+ }
2497
+
2498
+ `,Za=`
2499
+
2500
+ // See equation (2) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
2501
+ float velvetD( float cosThetaH, float roughness ) {
2502
+
2503
+ float alpha = max( roughness, 0.07 );
2504
+ alpha = alpha * alpha;
2505
+
2506
+ float invAlpha = 1.0 / alpha;
2507
+
2508
+ float sqrCosThetaH = cosThetaH * cosThetaH;
2509
+ float sinThetaH = max( 1.0 - sqrCosThetaH, 0.001 );
2510
+
2511
+ return ( 2.0 + invAlpha ) * pow( sinThetaH, 0.5 * invAlpha ) / ( 2.0 * PI );
2512
+
2513
+ }
2514
+
2515
+ float velvetParamsInterpolate( int i, float oneMinusAlphaSquared ) {
2516
+
2517
+ const float p0[5] = float[5]( 25.3245, 3.32435, 0.16801, -1.27393, -4.85967 );
2518
+ const float p1[5] = float[5]( 21.5473, 3.82987, 0.19823, -1.97760, -4.32054 );
2519
+
2520
+ return mix( p1[i], p0[i], oneMinusAlphaSquared );
2521
+
2522
+ }
2523
+
2524
+ float velvetL( float x, float alpha ) {
2525
+
2526
+ float oneMinusAlpha = 1.0 - alpha;
2527
+ float oneMinusAlphaSquared = oneMinusAlpha * oneMinusAlpha;
2528
+
2529
+ float a = velvetParamsInterpolate( 0, oneMinusAlphaSquared );
2530
+ float b = velvetParamsInterpolate( 1, oneMinusAlphaSquared );
2531
+ float c = velvetParamsInterpolate( 2, oneMinusAlphaSquared );
2532
+ float d = velvetParamsInterpolate( 3, oneMinusAlphaSquared );
2533
+ float e = velvetParamsInterpolate( 4, oneMinusAlphaSquared );
2534
+
2535
+ return a / ( 1.0 + b * pow( abs( x ), c ) ) + d * x + e;
2536
+
2537
+ }
2538
+
2539
+ // See equation (3) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
2540
+ float velvetLambda( float cosTheta, float alpha ) {
2541
+
2542
+ return abs( cosTheta ) < 0.5 ? exp( velvetL( cosTheta, alpha ) ) : exp( 2.0 * velvetL( 0.5, alpha ) - velvetL( 1.0 - cosTheta, alpha ) );
2543
+
2544
+ }
2545
+
2546
+ // See Section 3, Shadowing Term, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
2547
+ float velvetG( float cosThetaO, float cosThetaI, float roughness ) {
2548
+
2549
+ float alpha = max( roughness, 0.07 );
2550
+ alpha = alpha * alpha;
2551
+
2552
+ return 1.0 / ( 1.0 + velvetLambda( cosThetaO, alpha ) + velvetLambda( cosThetaI, alpha ) );
2553
+
2554
+ }
2555
+
2556
+ float directionalAlbedoSheen( float cosTheta, float alpha ) {
2557
+
2558
+ cosTheta = saturate( cosTheta );
2559
+
2560
+ float c = 1.0 - cosTheta;
2561
+ float c3 = c * c * c;
2562
+
2563
+ return 0.65584461 * c3 + 1.0 / ( 4.16526551 + exp( -7.97291361 * sqrt( alpha ) + 6.33516894 ) );
2564
+
2565
+ }
2566
+
2567
+ float sheenAlbedoScaling( vec3 wo, vec3 wi, SurfaceRecord surf ) {
2568
+
2569
+ float alpha = max( surf.sheenRoughness, 0.07 );
2570
+ alpha = alpha * alpha;
2571
+
2572
+ float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
2573
+
2574
+ float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
2575
+ float eWi = directionalAlbedoSheen( saturateCos( wi.z ), alpha );
2576
+
2577
+ return min( 1.0 - maxSheenColor * eWo, 1.0 - maxSheenColor * eWi );
2578
+
2579
+ }
2580
+
2581
+ // See Section 5, Layering, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
2582
+ float sheenAlbedoScaling( vec3 wo, SurfaceRecord surf ) {
2583
+
2584
+ float alpha = max( surf.sheenRoughness, 0.07 );
2585
+ alpha = alpha * alpha;
2586
+
2587
+ float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
2588
+
2589
+ float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
2590
+
2591
+ return 1.0 - maxSheenColor * eWo;
2592
+
2593
+ }
2594
+
2595
+ `,Qa=`
2596
+
2597
+ #ifndef FOG_CHECK_ITERATIONS
2598
+ #define FOG_CHECK_ITERATIONS 30
2599
+ #endif
2600
+
2601
+ // returns whether the given material is a fog material or not
2602
+ bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
2603
+
2604
+ uint i = materialIndex * uint( MATERIAL_PIXELS );
2605
+ vec4 s14 = texelFetch1D( materials, i + 14u );
2606
+ return bool( int( s14.b ) & 4 );
2607
+
2608
+ }
2609
+
2610
+ // returns true if we're within the first fog volume we hit
2611
+ bool bvhIntersectFogVolumeHit(
2612
+ vec3 rayOrigin, vec3 rayDirection,
2613
+ usampler2D materialIndexAttribute, sampler2D materials,
2614
+ inout Material material
2615
+ ) {
2616
+
2617
+ material.fogVolume = false;
2618
+
2619
+ for ( int i = 0; i < FOG_CHECK_ITERATIONS; i ++ ) {
2620
+
2621
+ // find nearest hit
2622
+ uvec4 faceIndices = uvec4( 0u );
2623
+ vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
2624
+ vec3 barycoord = vec3( 0.0 );
2625
+ float side = 1.0;
2626
+ float dist = 0.0;
2627
+ bool hit = bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist );
2628
+ if ( hit ) {
2629
+
2630
+ // if it's a fog volume return whether we hit the front or back face
2631
+ uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
2632
+ if ( isMaterialFogVolume( materials, materialIndex ) ) {
2633
+
2634
+ material = readMaterialInfo( materials, materialIndex );
2635
+ return side == - 1.0;
2636
+
2637
+ } else {
2638
+
2639
+ // move the ray forward
2640
+ rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
2641
+
2642
+ }
2643
+
2644
+ } else {
2645
+
2646
+ return false;
2647
+
2648
+ }
2649
+
2650
+ }
2651
+
2652
+ return false;
2653
+
2654
+ }
2655
+
2656
+ `,$a=`
2657
+
2658
+ // step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
2659
+ // returns true if a solid surface was hit
2660
+ bool attenuateHit(
2661
+ RenderState state,
2662
+ Ray ray, float rayDist,
2663
+ out vec3 color
2664
+ ) {
2665
+
2666
+ // store the original bounce index so we can reset it after
2667
+ uint originalBounceIndex = sobolBounceIndex;
2668
+
2669
+ int traversals = state.traversals;
2670
+ int transmissiveTraversals = state.transmissiveTraversals;
2671
+ bool isShadowRay = state.isShadowRay;
2672
+ Material fogMaterial = state.fogMaterial;
2673
+
2674
+ vec3 startPoint = ray.origin;
2675
+
2676
+ // hit results
2677
+ SurfaceHit surfaceHit;
2678
+
2679
+ color = vec3( 1.0 );
2680
+
2681
+ bool result = true;
2682
+ for ( int i = 0; i < traversals; i ++ ) {
2683
+
2684
+ sobolBounceIndex ++;
2685
+
2686
+ int hitType = traceScene( ray, fogMaterial, surfaceHit );
2687
+
2688
+ if ( hitType == FOG_HIT ) {
2689
+
2690
+ result = true;
2691
+ break;
2692
+
2693
+ } else if ( hitType == SURFACE_HIT ) {
2694
+
2695
+ float totalDist = distance( startPoint, ray.origin + ray.direction * surfaceHit.dist );
2696
+ if ( totalDist > rayDist ) {
2697
+
2698
+ result = false;
2699
+ break;
2700
+
2701
+ }
2702
+
2703
+ // TODO: attenuate the contribution based on the PDF of the resulting ray including refraction values
2704
+ // Should be able to work using the material BSDF functions which will take into account specularity, etc.
2705
+ // TODO: should we account for emissive surfaces here?
2706
+
2707
+ uint materialIndex = uTexelFetch1D( materialIndexAttribute, surfaceHit.faceIndices.x ).r;
2708
+ Material material = readMaterialInfo( materials, materialIndex );
2709
+
2710
+ // adjust the ray to the new surface
2711
+ bool isEntering = surfaceHit.side == 1.0;
2712
+ ray.origin = stepRayOrigin( ray.origin, ray.direction, - surfaceHit.faceNormal, surfaceHit.dist );
2713
+
2714
+ #if FEATURE_FOG
2715
+
2716
+ if ( material.fogVolume ) {
2717
+
2718
+ fogMaterial = material;
2719
+ fogMaterial.fogVolume = surfaceHit.side == 1.0;
2720
+ i -= sign( transmissiveTraversals );
2721
+ transmissiveTraversals --;
2722
+ continue;
2723
+
2724
+ }
2725
+
2726
+ #endif
2727
+
2728
+ if ( ! material.castShadow && isShadowRay ) {
2729
+
2730
+ continue;
2731
+
2732
+ }
2733
+
2734
+ vec2 uv = textureSampleBarycoord( attributesArray, ATTR_UV, surfaceHit.barycoord, surfaceHit.faceIndices.xyz ).xy;
2735
+ vec4 vertexColor = textureSampleBarycoord( attributesArray, ATTR_COLOR, surfaceHit.barycoord, surfaceHit.faceIndices.xyz );
2736
+
2737
+ // albedo
2738
+ vec4 albedo = vec4( material.color, material.opacity );
2739
+ if ( material.map != - 1 ) {
2740
+
2741
+ vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
2742
+ albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
2743
+
2744
+ }
2745
+
2746
+ if ( material.vertexColors ) {
2747
+
2748
+ albedo *= vertexColor;
2749
+
2750
+ }
2751
+
2752
+ // alphaMap
2753
+ if ( material.alphaMap != - 1 ) {
2754
+
2755
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
2756
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
2757
+
2758
+ }
2759
+
2760
+ // transmission
2761
+ float transmission = material.transmission;
2762
+ if ( material.transmissionMap != - 1 ) {
2763
+
2764
+ vec3 uvPrime = material.transmissionMapTransform * vec3( uv, 1 );
2765
+ transmission *= texture2D( textures, vec3( uvPrime.xy, material.transmissionMap ) ).r;
2766
+
2767
+ }
2768
+
2769
+ // metalness
2770
+ float metalness = material.metalness;
2771
+ if ( material.metalnessMap != - 1 ) {
2772
+
2773
+ vec3 uvPrime = material.metalnessMapTransform * vec3( uv, 1 );
2774
+ metalness *= texture2D( textures, vec3( uvPrime.xy, material.metalnessMap ) ).b;
2775
+
2776
+ }
2777
+
2778
+ float alphaTest = material.alphaTest;
2779
+ bool useAlphaTest = alphaTest != 0.0;
2780
+ float transmissionFactor = ( 1.0 - metalness ) * transmission;
2781
+ if (
2782
+ transmissionFactor < rand( 9 ) && ! (
2783
+ // material sidedness
2784
+ material.side != 0.0 && surfaceHit.side == material.side
2785
+
2786
+ // alpha test
2787
+ || useAlphaTest && albedo.a < alphaTest
2788
+
2789
+ // opacity
2790
+ || material.transparent && ! useAlphaTest && albedo.a < rand( 10 )
2791
+ )
2792
+ ) {
2793
+
2794
+ result = true;
2795
+ break;
2796
+
2797
+ }
2798
+
2799
+ if ( surfaceHit.side == 1.0 && isEntering ) {
2800
+
2801
+ // only attenuate by surface color on the way in
2802
+ color *= mix( vec3( 1.0 ), albedo.rgb, transmissionFactor );
2803
+
2804
+ } else if ( surfaceHit.side == - 1.0 ) {
2805
+
2806
+ // attenuate by medium once we hit the opposite side of the model
2807
+ color *= transmissionAttenuation( surfaceHit.dist, material.attenuationColor, material.attenuationDistance );
2808
+
2809
+ }
2810
+
2811
+ bool isTransmissiveRay = dot( ray.direction, surfaceHit.faceNormal * surfaceHit.side ) < 0.0;
2812
+ if ( ( isTransmissiveRay || isEntering ) && transmissiveTraversals > 0 ) {
2813
+
2814
+ i -= sign( transmissiveTraversals );
2815
+ transmissiveTraversals --;
2816
+
2817
+ }
2818
+
2819
+ } else {
2820
+
2821
+ result = false;
2822
+ break;
2823
+
2824
+ }
2825
+
2826
+ }
2827
+
2828
+ // reset the bounce index
2829
+ sobolBounceIndex = originalBounceIndex;
2830
+ return result;
2831
+
2832
+ }
2833
+
2834
+ `,eo=`
2835
+
2836
+ vec3 ndcToRayOrigin( vec2 coord ) {
2837
+
2838
+ vec4 rayOrigin4 = cameraWorldMatrix * invProjectionMatrix * vec4( coord, - 1.0, 1.0 );
2839
+ return rayOrigin4.xyz / rayOrigin4.w;
2840
+ }
2841
+
2842
+ Ray getCameraRay() {
2843
+
2844
+ vec2 ssd = vec2( 1.0 ) / resolution;
2845
+
2846
+ // Jitter the camera ray by finding a uv coordinate at a random sample
2847
+ // around this pixel's UV coordinate for AA
2848
+ vec2 ruv = rand2( 0 );
2849
+ vec2 jitteredUv = vUv + vec2( tentFilter( ruv.x ) * ssd.x, tentFilter( ruv.y ) * ssd.y );
2850
+ Ray ray;
2851
+
2852
+ #if CAMERA_TYPE == 2
2853
+
2854
+ // Equirectangular projection
2855
+ vec4 rayDirection4 = vec4( equirectUvToDirection( jitteredUv ), 0.0 );
2856
+ vec4 rayOrigin4 = vec4( 0.0, 0.0, 0.0, 1.0 );
2857
+
2858
+ rayDirection4 = cameraWorldMatrix * rayDirection4;
2859
+ rayOrigin4 = cameraWorldMatrix * rayOrigin4;
2860
+
2861
+ ray.direction = normalize( rayDirection4.xyz );
2862
+ ray.origin = rayOrigin4.xyz / rayOrigin4.w;
2863
+
2864
+ #else
2865
+
2866
+ // get [- 1, 1] normalized device coordinates
2867
+ vec2 ndc = 2.0 * jitteredUv - vec2( 1.0 );
2868
+ ray.origin = ndcToRayOrigin( ndc );
2869
+
2870
+ #if CAMERA_TYPE == 1
2871
+
2872
+ // Orthographic projection
2873
+ ray.direction = ( cameraWorldMatrix * vec4( 0.0, 0.0, - 1.0, 0.0 ) ).xyz;
2874
+ ray.direction = normalize( ray.direction );
2875
+
2876
+ #else
2877
+
2878
+ // Perspective projection
2879
+ ray.direction = normalize( mat3( cameraWorldMatrix ) * ( invProjectionMatrix * vec4( ndc, 0.0, 1.0 ) ).xyz );
2880
+
2881
+ #endif
2882
+
2883
+ #endif
2884
+
2885
+ #if FEATURE_DOF
2886
+ {
2887
+
2888
+ // depth of field
2889
+ vec3 focalPoint = ray.origin + normalize( ray.direction ) * physicalCamera.focusDistance;
2890
+
2891
+ // get the aperture sample
2892
+ // if blades === 0 then we assume a circle
2893
+ vec3 shapeUVW= rand3( 1 );
2894
+ int blades = physicalCamera.apertureBlades;
2895
+ float anamorphicRatio = physicalCamera.anamorphicRatio;
2896
+ vec2 apertureSample = sampleAperture( blades, shapeUVW );
2897
+ apertureSample *= physicalCamera.bokehSize * 0.5 * 1e-3;
2898
+
2899
+ // rotate the aperture shape
2900
+ apertureSample =
2901
+ rotateVector( apertureSample, physicalCamera.apertureRotation ) *
2902
+ saturate( vec2( anamorphicRatio, 1.0 / anamorphicRatio ) );
2903
+
2904
+ // create the new ray
2905
+ ray.origin += ( cameraWorldMatrix * vec4( apertureSample, 0.0, 0.0 ) ).xyz;
2906
+ ray.direction = focalPoint - ray.origin;
2907
+
2908
+ }
2909
+ #endif
2910
+
2911
+ ray.direction = normalize( ray.direction );
2912
+
2913
+ return ray;
2914
+
2915
+ }
2916
+
2917
+ `,to=`
2918
+
2919
+ vec3 directLightContribution( vec3 worldWo, SurfaceRecord surf, RenderState state, vec3 rayOrigin ) {
2920
+
2921
+ vec3 result = vec3( 0.0 );
2922
+
2923
+ // uniformly pick a light or environment map
2924
+ if( lightsDenom != 0.0 && rand( 5 ) < float( lights.count ) / lightsDenom ) {
2925
+
2926
+ // sample a light or environment
2927
+ LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, rand3( 6 ) );
2928
+
2929
+ bool isSampleBelowSurface = ! surf.volumeParticle && dot( surf.faceNormal, lightRec.direction ) < 0.0;
2930
+ if ( isSampleBelowSurface ) {
2931
+
2932
+ lightRec.pdf = 0.0;
2933
+
2934
+ }
2935
+
2936
+ // check if a ray could even reach the light area
2937
+ Ray lightRay;
2938
+ lightRay.origin = rayOrigin;
2939
+ lightRay.direction = lightRec.direction;
2940
+ vec3 attenuatedColor;
2941
+ if (
2942
+ lightRec.pdf > 0.0 &&
2943
+ isDirectionValid( lightRec.direction, surf.normal, surf.faceNormal ) &&
2944
+ ! attenuateHit( state, lightRay, lightRec.dist, attenuatedColor )
2945
+ ) {
2946
+
2947
+ // get the material pdf
2948
+ vec3 sampleColor;
2949
+ float lightMaterialPdf = bsdfResult( worldWo, lightRec.direction, surf, sampleColor );
2950
+ bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
2951
+ if ( lightMaterialPdf > 0.0 && isValidSampleColor ) {
2952
+
2953
+ // weight the direct light contribution
2954
+ float lightPdf = lightRec.pdf / lightsDenom;
2955
+ float misWeight = lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
2956
+ result = attenuatedColor * lightRec.emission * state.throughputColor * sampleColor * misWeight / lightPdf;
2957
+
2958
+ }
2959
+
2960
+ }
2961
+
2962
+ } else if ( envMapInfo.totalSum != 0.0 && environmentIntensity != 0.0 ) {
2963
+
2964
+ // find a sample in the environment map to include in the contribution
2965
+ vec3 envColor, envDirection;
2966
+ float envPdf = sampleEquirectProbability( rand2( 7 ), envColor, envDirection );
2967
+ envDirection = invEnvRotation3x3 * envDirection;
2968
+
2969
+ // this env sampling is not set up for transmissive sampling and yields overly bright
2970
+ // results so we ignore the sample in this case.
2971
+ // TODO: this should be improved but how? The env samples could traverse a few layers?
2972
+ bool isSampleBelowSurface = ! surf.volumeParticle && dot( surf.faceNormal, envDirection ) < 0.0;
2973
+ if ( isSampleBelowSurface ) {
2974
+
2975
+ envPdf = 0.0;
2976
+
2977
+ }
2978
+
2979
+ // check if a ray could even reach the surface
2980
+ Ray envRay;
2981
+ envRay.origin = rayOrigin;
2982
+ envRay.direction = envDirection;
2983
+ vec3 attenuatedColor;
2984
+ if (
2985
+ envPdf > 0.0 &&
2986
+ isDirectionValid( envDirection, surf.normal, surf.faceNormal ) &&
2987
+ ! attenuateHit( state, envRay, INFINITY, attenuatedColor )
2988
+ ) {
2989
+
2990
+ // get the material pdf
2991
+ vec3 sampleColor;
2992
+ float envMaterialPdf = bsdfResult( worldWo, envDirection, surf, sampleColor );
2993
+ bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
2994
+ if ( envMaterialPdf > 0.0 && isValidSampleColor ) {
2995
+
2996
+ // weight the direct light contribution
2997
+ envPdf /= lightsDenom;
2998
+ float misWeight = misHeuristic( envPdf, envMaterialPdf );
2999
+ result = attenuatedColor * environmentIntensity * envColor * state.throughputColor * sampleColor * misWeight / envPdf;
3000
+
3001
+ }
3002
+
3003
+ }
3004
+
3005
+ }
3006
+
3007
+ // Function changed to have a single return statement to potentially help with crashes on Mac OS.
3008
+ // See issue #470
3009
+ return result;
3010
+
3011
+ }
3012
+
3013
+ `,no=`
3014
+
3015
+ #define SKIP_SURFACE 0
3016
+ #define HIT_SURFACE 1
3017
+ int getSurfaceRecord(
3018
+ Material material, SurfaceHit surfaceHit, sampler2DArray attributesArray,
3019
+ float accumulatedRoughness,
3020
+ inout SurfaceRecord surf
3021
+ ) {
3022
+
3023
+ if ( material.fogVolume ) {
3024
+
3025
+ vec3 normal = vec3( 0, 0, 1 );
3026
+
3027
+ SurfaceRecord fogSurface;
3028
+ fogSurface.volumeParticle = true;
3029
+ fogSurface.color = material.color;
3030
+ fogSurface.emission = material.emissiveIntensity * material.emissive;
3031
+ fogSurface.normal = normal;
3032
+ fogSurface.faceNormal = normal;
3033
+ fogSurface.clearcoatNormal = normal;
3034
+
3035
+ surf = fogSurface;
3036
+ return HIT_SURFACE;
3037
+
3038
+ }
3039
+
3040
+ // uv coord for textures
3041
+ vec2 uv = textureSampleBarycoord( attributesArray, ATTR_UV, surfaceHit.barycoord, surfaceHit.faceIndices.xyz ).xy;
3042
+ vec4 vertexColor = textureSampleBarycoord( attributesArray, ATTR_COLOR, surfaceHit.barycoord, surfaceHit.faceIndices.xyz );
3043
+
3044
+ // albedo
3045
+ vec4 albedo = vec4( material.color, material.opacity );
3046
+ if ( material.map != - 1 ) {
3047
+
3048
+ vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
3049
+ albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
3050
+
3051
+ }
3052
+
3053
+ if ( material.vertexColors ) {
3054
+
3055
+ albedo *= vertexColor;
3056
+
3057
+ }
3058
+
3059
+ // alphaMap
3060
+ if ( material.alphaMap != - 1 ) {
3061
+
3062
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
3063
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
3064
+
3065
+ }
3066
+
3067
+ // possibly skip this sample if it's transparent, alpha test is enabled, or we hit the wrong material side
3068
+ // and it's single sided.
3069
+ // - alpha test is disabled when it === 0
3070
+ // - the material sidedness test is complicated because we want light to pass through the back side but still
3071
+ // be able to see the front side. This boolean checks if the side we hit is the front side on the first ray
3072
+ // and we're rendering the other then we skip it. Do the opposite on subsequent bounces to get incoming light.
3073
+ float alphaTest = material.alphaTest;
3074
+ bool useAlphaTest = alphaTest != 0.0;
3075
+ if (
3076
+ // material sidedness
3077
+ material.side != 0.0 && surfaceHit.side != material.side
3078
+
3079
+ // alpha test
3080
+ || useAlphaTest && albedo.a < alphaTest
3081
+
3082
+ // opacity
3083
+ || material.transparent && ! useAlphaTest && albedo.a < rand( 3 )
3084
+ ) {
3085
+
3086
+ return SKIP_SURFACE;
3087
+
3088
+ }
3089
+
3090
+ // fetch the interpolated smooth normal
3091
+ vec3 normal = normalize( textureSampleBarycoord(
3092
+ attributesArray,
3093
+ ATTR_NORMAL,
3094
+ surfaceHit.barycoord,
3095
+ surfaceHit.faceIndices.xyz
3096
+ ).xyz );
3097
+
3098
+ // roughness
3099
+ float roughness = material.roughness;
3100
+ if ( material.roughnessMap != - 1 ) {
3101
+
3102
+ vec3 uvPrime = material.roughnessMapTransform * vec3( uv, 1 );
3103
+ roughness *= texture2D( textures, vec3( uvPrime.xy, material.roughnessMap ) ).g;
3104
+
3105
+ }
3106
+
3107
+ // metalness
3108
+ float metalness = material.metalness;
3109
+ if ( material.metalnessMap != - 1 ) {
3110
+
3111
+ vec3 uvPrime = material.metalnessMapTransform * vec3( uv, 1 );
3112
+ metalness *= texture2D( textures, vec3( uvPrime.xy, material.metalnessMap ) ).b;
3113
+
3114
+ }
3115
+
3116
+ // emission
3117
+ vec3 emission = material.emissiveIntensity * material.emissive;
3118
+ if ( material.emissiveMap != - 1 ) {
3119
+
3120
+ vec3 uvPrime = material.emissiveMapTransform * vec3( uv, 1 );
3121
+ emission *= texture2D( textures, vec3( uvPrime.xy, material.emissiveMap ) ).xyz;
3122
+
3123
+ }
3124
+
3125
+ // transmission
3126
+ float transmission = material.transmission;
3127
+ if ( material.transmissionMap != - 1 ) {
3128
+
3129
+ vec3 uvPrime = material.transmissionMapTransform * vec3( uv, 1 );
3130
+ transmission *= texture2D( textures, vec3( uvPrime.xy, material.transmissionMap ) ).r;
3131
+
3132
+ }
3133
+
3134
+ // normal
3135
+ if ( material.flatShading ) {
3136
+
3137
+ // if we're rendering a flat shaded object then use the face normals - the face normal
3138
+ // is provided based on the side the ray hits the mesh so flip it to align with the
3139
+ // interpolated vertex normals.
3140
+ normal = surfaceHit.faceNormal * surfaceHit.side;
3141
+
3142
+ }
3143
+
3144
+ vec3 baseNormal = normal;
3145
+ if ( material.normalMap != - 1 ) {
3146
+
3147
+ vec4 tangentSample = textureSampleBarycoord(
3148
+ attributesArray,
3149
+ ATTR_TANGENT,
3150
+ surfaceHit.barycoord,
3151
+ surfaceHit.faceIndices.xyz
3152
+ );
3153
+
3154
+ // some provided tangents can be malformed (0, 0, 0) causing the normal to be degenerate
3155
+ // resulting in NaNs and slow path tracing.
3156
+ if ( length( tangentSample.xyz ) > 0.0 ) {
3157
+
3158
+ vec3 tangent = normalize( tangentSample.xyz );
3159
+ vec3 bitangent = normalize( cross( normal, tangent ) * tangentSample.w );
3160
+ mat3 vTBN = mat3( tangent, bitangent, normal );
3161
+
3162
+ vec3 uvPrime = material.normalMapTransform * vec3( uv, 1 );
3163
+ vec3 texNormal = texture2D( textures, vec3( uvPrime.xy, material.normalMap ) ).xyz * 2.0 - 1.0;
3164
+ texNormal.xy *= material.normalScale;
3165
+ normal = vTBN * texNormal;
3166
+
3167
+ }
3168
+
3169
+ }
3170
+
3171
+ normal *= surfaceHit.side;
3172
+
3173
+ // clearcoat
3174
+ float clearcoat = material.clearcoat;
3175
+ if ( material.clearcoatMap != - 1 ) {
3176
+
3177
+ vec3 uvPrime = material.clearcoatMapTransform * vec3( uv, 1 );
3178
+ clearcoat *= texture2D( textures, vec3( uvPrime.xy, material.clearcoatMap ) ).r;
3179
+
3180
+ }
3181
+
3182
+ // clearcoatRoughness
3183
+ float clearcoatRoughness = material.clearcoatRoughness;
3184
+ if ( material.clearcoatRoughnessMap != - 1 ) {
3185
+
3186
+ vec3 uvPrime = material.clearcoatRoughnessMapTransform * vec3( uv, 1 );
3187
+ clearcoatRoughness *= texture2D( textures, vec3( uvPrime.xy, material.clearcoatRoughnessMap ) ).g;
3188
+
3189
+ }
3190
+
3191
+ // clearcoatNormal
3192
+ vec3 clearcoatNormal = baseNormal;
3193
+ if ( material.clearcoatNormalMap != - 1 ) {
3194
+
3195
+ vec4 tangentSample = textureSampleBarycoord(
3196
+ attributesArray,
3197
+ ATTR_TANGENT,
3198
+ surfaceHit.barycoord,
3199
+ surfaceHit.faceIndices.xyz
3200
+ );
3201
+
3202
+ // some provided tangents can be malformed (0, 0, 0) causing the normal to be degenerate
3203
+ // resulting in NaNs and slow path tracing.
3204
+ if ( length( tangentSample.xyz ) > 0.0 ) {
3205
+
3206
+ vec3 tangent = normalize( tangentSample.xyz );
3207
+ vec3 bitangent = normalize( cross( clearcoatNormal, tangent ) * tangentSample.w );
3208
+ mat3 vTBN = mat3( tangent, bitangent, clearcoatNormal );
3209
+
3210
+ vec3 uvPrime = material.clearcoatNormalMapTransform * vec3( uv, 1 );
3211
+ vec3 texNormal = texture2D( textures, vec3( uvPrime.xy, material.clearcoatNormalMap ) ).xyz * 2.0 - 1.0;
3212
+ texNormal.xy *= material.clearcoatNormalScale;
3213
+ clearcoatNormal = vTBN * texNormal;
3214
+
3215
+ }
3216
+
3217
+ }
3218
+
3219
+ clearcoatNormal *= surfaceHit.side;
3220
+
3221
+ // sheenColor
3222
+ vec3 sheenColor = material.sheenColor;
3223
+ if ( material.sheenColorMap != - 1 ) {
3224
+
3225
+ vec3 uvPrime = material.sheenColorMapTransform * vec3( uv, 1 );
3226
+ sheenColor *= texture2D( textures, vec3( uvPrime.xy, material.sheenColorMap ) ).rgb;
3227
+
3228
+ }
3229
+
3230
+ // sheenRoughness
3231
+ float sheenRoughness = material.sheenRoughness;
3232
+ if ( material.sheenRoughnessMap != - 1 ) {
3233
+
3234
+ vec3 uvPrime = material.sheenRoughnessMapTransform * vec3( uv, 1 );
3235
+ sheenRoughness *= texture2D( textures, vec3( uvPrime.xy, material.sheenRoughnessMap ) ).a;
3236
+
3237
+ }
3238
+
3239
+ // iridescence
3240
+ float iridescence = material.iridescence;
3241
+ if ( material.iridescenceMap != - 1 ) {
3242
+
3243
+ vec3 uvPrime = material.iridescenceMapTransform * vec3( uv, 1 );
3244
+ iridescence *= texture2D( textures, vec3( uvPrime.xy, material.iridescenceMap ) ).r;
3245
+
3246
+ }
3247
+
3248
+ // iridescence thickness
3249
+ float iridescenceThickness = material.iridescenceThicknessMaximum;
3250
+ if ( material.iridescenceThicknessMap != - 1 ) {
3251
+
3252
+ vec3 uvPrime = material.iridescenceThicknessMapTransform * vec3( uv, 1 );
3253
+ float iridescenceThicknessSampled = texture2D( textures, vec3( uvPrime.xy, material.iridescenceThicknessMap ) ).g;
3254
+ iridescenceThickness = mix( material.iridescenceThicknessMinimum, material.iridescenceThicknessMaximum, iridescenceThicknessSampled );
3255
+
3256
+ }
3257
+
3258
+ iridescence = iridescenceThickness == 0.0 ? 0.0 : iridescence;
3259
+
3260
+ // specular color
3261
+ vec3 specularColor = material.specularColor;
3262
+ if ( material.specularColorMap != - 1 ) {
3263
+
3264
+ vec3 uvPrime = material.specularColorMapTransform * vec3( uv, 1 );
3265
+ specularColor *= texture2D( textures, vec3( uvPrime.xy, material.specularColorMap ) ).rgb;
3266
+
3267
+ }
3268
+
3269
+ // specular intensity
3270
+ float specularIntensity = material.specularIntensity;
3271
+ if ( material.specularIntensityMap != - 1 ) {
3272
+
3273
+ vec3 uvPrime = material.specularIntensityMapTransform * vec3( uv, 1 );
3274
+ specularIntensity *= texture2D( textures, vec3( uvPrime.xy, material.specularIntensityMap ) ).a;
3275
+
3276
+ }
3277
+
3278
+ surf.volumeParticle = false;
3279
+
3280
+ surf.faceNormal = surfaceHit.faceNormal;
3281
+ surf.normal = normal;
3282
+
3283
+ surf.metalness = metalness;
3284
+ surf.color = albedo.rgb;
3285
+ surf.emission = emission;
3286
+
3287
+ surf.ior = material.ior;
3288
+ surf.transmission = transmission;
3289
+ surf.thinFilm = material.thinFilm;
3290
+ surf.attenuationColor = material.attenuationColor;
3291
+ surf.attenuationDistance = material.attenuationDistance;
3292
+
3293
+ surf.clearcoatNormal = clearcoatNormal;
3294
+ surf.clearcoat = clearcoat;
3295
+
3296
+ surf.sheen = material.sheen;
3297
+ surf.sheenColor = sheenColor;
3298
+
3299
+ surf.iridescence = iridescence;
3300
+ surf.iridescenceIor = material.iridescenceIor;
3301
+ surf.iridescenceThickness = iridescenceThickness;
3302
+
3303
+ surf.specularColor = specularColor;
3304
+ surf.specularIntensity = specularIntensity;
3305
+
3306
+ // apply perceptual roughness factor from gltf. sheen perceptual roughness is
3307
+ // applied by its brdf function
3308
+ // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#microfacet-surfaces
3309
+ surf.roughness = roughness * roughness;
3310
+ surf.clearcoatRoughness = clearcoatRoughness * clearcoatRoughness;
3311
+ surf.sheenRoughness = sheenRoughness;
3312
+
3313
+ // frontFace is used to determine transmissive properties and PDF. If no transmission is used
3314
+ // then we can just always assume this is a front face.
3315
+ surf.frontFace = surfaceHit.side == 1.0 || transmission == 0.0;
3316
+ surf.eta = material.thinFilm || surf.frontFace ? 1.0 / material.ior : material.ior;
3317
+ surf.f0 = iorRatioToF0( surf.eta );
3318
+
3319
+ // Compute the filtered roughness value to use during specular reflection computations.
3320
+ // The accumulated roughness value is scaled by a user setting and a "magic value" of 5.0.
3321
+ // If we're exiting something transmissive then scale the factor down significantly so we can retain
3322
+ // sharp internal reflections
3323
+ surf.filteredRoughness = applyFilteredGlossy( surf.roughness, accumulatedRoughness );
3324
+ surf.filteredClearcoatRoughness = applyFilteredGlossy( surf.clearcoatRoughness, accumulatedRoughness );
3325
+
3326
+ // get the normal frames
3327
+ surf.normalBasis = getBasisFromNormal( surf.normal );
3328
+ surf.normalInvBasis = inverse( surf.normalBasis );
3329
+
3330
+ surf.clearcoatBasis = getBasisFromNormal( surf.clearcoatNormal );
3331
+ surf.clearcoatInvBasis = inverse( surf.clearcoatBasis );
3332
+
3333
+ return HIT_SURFACE;
3334
+
3335
+ }
3336
+ `,ro=`
3337
+
3338
+ struct Ray {
3339
+
3340
+ vec3 origin;
3341
+ vec3 direction;
3342
+
3343
+ };
3344
+
3345
+ struct SurfaceHit {
3346
+
3347
+ uvec4 faceIndices;
3348
+ vec3 barycoord;
3349
+ vec3 faceNormal;
3350
+ float side;
3351
+ float dist;
3352
+
3353
+ };
3354
+
3355
+ struct RenderState {
3356
+
3357
+ bool firstRay;
3358
+ bool transmissiveRay;
3359
+ bool isShadowRay;
3360
+ float accumulatedRoughness;
3361
+ int transmissiveTraversals;
3362
+ int traversals;
3363
+ uint depth;
3364
+ vec3 throughputColor;
3365
+ Material fogMaterial;
3366
+
3367
+ };
3368
+
3369
+ RenderState initRenderState() {
3370
+
3371
+ RenderState result;
3372
+ result.firstRay = true;
3373
+ result.transmissiveRay = true;
3374
+ result.isShadowRay = false;
3375
+ result.accumulatedRoughness = 0.0;
3376
+ result.transmissiveTraversals = 0;
3377
+ result.traversals = 0;
3378
+ result.throughputColor = vec3( 1.0 );
3379
+ result.depth = 0u;
3380
+ result.fogMaterial.fogVolume = false;
3381
+ return result;
3382
+
3383
+ }
3384
+
3385
+ `,io=`
3386
+
3387
+ #define NO_HIT 0
3388
+ #define SURFACE_HIT 1
3389
+ #define LIGHT_HIT 2
3390
+ #define FOG_HIT 3
3391
+
3392
+ // Passing the global variable 'lights' into this function caused shader program errors.
3393
+ // So global variables like 'lights' and 'bvh' were moved out of the function parameters.
3394
+ // For more information, refer to: https://github.com/gkjohnson/three-gpu-pathtracer/pull/457
3395
+ int traceScene(
3396
+ Ray ray, Material fogMaterial, inout SurfaceHit surfaceHit
3397
+ ) {
3398
+
3399
+ int result = NO_HIT;
3400
+ bool hit = bvhIntersectFirstHit( bvh, ray.origin, ray.direction, surfaceHit.faceIndices, surfaceHit.faceNormal, surfaceHit.barycoord, surfaceHit.side, surfaceHit.dist );
3401
+
3402
+ #if FEATURE_FOG
3403
+
3404
+ if ( fogMaterial.fogVolume ) {
3405
+
3406
+ // offset the distance so we don't run into issues with particles on the same surface
3407
+ // as other objects
3408
+ float particleDist = intersectFogVolume( fogMaterial, rand( 1 ) );
3409
+ if ( particleDist + RAY_OFFSET < surfaceHit.dist ) {
3410
+
3411
+ surfaceHit.side = 1.0;
3412
+ surfaceHit.faceNormal = normalize( - ray.direction );
3413
+ surfaceHit.dist = particleDist;
3414
+ return FOG_HIT;
3415
+
3416
+ }
3417
+
3418
+ }
3419
+
3420
+ #endif
3421
+
3422
+ if ( hit ) {
3423
+
3424
+ result = SURFACE_HIT;
3425
+
3426
+ }
3427
+
3428
+ return result;
3429
+
3430
+ }
3431
+
3432
+ `,ao=class extends Oi{onBeforeRender(){this.setDefine(`FEATURE_DOF`,this.physicalCamera.bokehSize===0?0:1),this.setDefine(`FEATURE_BACKGROUND_MAP`,+!!this.backgroundMap),this.setDefine(`FEATURE_FOG`,+!!this.materials.features.isUsed(`FOG`))}constructor(e){super({transparent:!0,depthWrite:!1,defines:{FEATURE_MIS:1,FEATURE_RUSSIAN_ROULETTE:1,FEATURE_DOF:1,FEATURE_BACKGROUND_MAP:0,FEATURE_FOG:1,RANDOM_TYPE:2,CAMERA_TYPE:0,DEBUG_MODE:0,ATTR_NORMAL:0,ATTR_TANGENT:1,ATTR_UV:2,ATTR_COLOR:3,MATERIAL_PIXELS:47},uniforms:{resolution:{value:new E},opacity:{value:1},bounces:{value:10},transmissiveBounces:{value:10},filterGlossyFactor:{value:0},physicalCamera:{value:new Ri},cameraWorldMatrix:{value:new f},invProjectionMatrix:{value:new f},bvh:{value:new zr},attributesArray:{value:new aa},materialIndexAttribute:{value:new Lr},materials:{value:new ma},textures:{value:new va().texture},lights:{value:new na},iesProfiles:{value:new va(360,180,{type:ve,wrapS:T,wrapT:T}).texture},environmentIntensity:{value:1},environmentRotation:{value:new f},envMapInfo:{value:new Ui},backgroundBlur:{value:0},backgroundMap:{value:null},backgroundAlpha:{value:1},backgroundIntensity:{value:1},backgroundRotation:{value:new f},seed:{value:0},sobolTexture:{value:null},stratifiedTexture:{value:new wa},stratifiedOffsetTexture:{value:new ja(64,1)}},vertexShader:`
3433
+
3434
+ varying vec2 vUv;
3435
+ void main() {
3436
+
3437
+ vec4 mvPosition = vec4( position, 1.0 );
3438
+ mvPosition = modelViewMatrix * mvPosition;
3439
+ gl_Position = projectionMatrix * mvPosition;
3440
+
3441
+ vUv = uv;
3442
+
3443
+ }
3444
+
3445
+ `,fragmentShader:`
3446
+ #define RAY_OFFSET 1e-4
3447
+ #define INFINITY 1e20
3448
+
3449
+ precision highp isampler2D;
3450
+ precision highp usampler2D;
3451
+ precision highp sampler2DArray;
3452
+ vec4 envMapTexelToLinear( vec4 a ) { return a; }
3453
+ #include <common>
3454
+
3455
+ // bvh intersection
3456
+ ${Hr}
3457
+ ${Wr}
3458
+ ${Ur}
3459
+
3460
+ // uniform structs
3461
+ ${Ma}
3462
+ ${Pa}
3463
+ ${Na}
3464
+ ${Fa}
3465
+ ${Ia}
3466
+
3467
+ // random
3468
+ #if RANDOM_TYPE == 2 // Stratified List
3469
+
3470
+ ${Ka}
3471
+
3472
+ #elif RANDOM_TYPE == 1 // Sobol
3473
+
3474
+ ${Ga}
3475
+ ${Mi}
3476
+ ${Pi}
3477
+
3478
+ #define rand(v) sobol(v)
3479
+ #define rand2(v) sobol2(v)
3480
+ #define rand3(v) sobol3(v)
3481
+ #define rand4(v) sobol4(v)
3482
+
3483
+ #else // PCG
3484
+
3485
+ ${Ga}
3486
+
3487
+ // Using the sobol functions seems to break the the compiler on MacOS
3488
+ // - specifically the "sobolReverseBits" function.
3489
+ uint sobolPixelIndex = 0u;
3490
+ uint sobolPathIndex = 0u;
3491
+ uint sobolBounceIndex = 0u;
3492
+
3493
+ #define rand(v) pcgRand()
3494
+ #define rand2(v) pcgRand2()
3495
+ #define rand3(v) pcgRand3()
3496
+ #define rand4(v) pcgRand4()
3497
+
3498
+ #endif
3499
+
3500
+ // common
3501
+ ${Ua}
3502
+ ${Ba}
3503
+ ${Wa}
3504
+ ${Va}
3505
+ ${Ha}
3506
+
3507
+ // environment
3508
+ uniform EquirectHdrInfo envMapInfo;
3509
+ uniform mat4 environmentRotation;
3510
+ uniform float environmentIntensity;
3511
+
3512
+ // lighting
3513
+ uniform sampler2DArray iesProfiles;
3514
+ uniform LightsInfo lights;
3515
+
3516
+ // background
3517
+ uniform float backgroundBlur;
3518
+ uniform float backgroundAlpha;
3519
+ #if FEATURE_BACKGROUND_MAP
3520
+
3521
+ uniform sampler2D backgroundMap;
3522
+ uniform mat4 backgroundRotation;
3523
+ uniform float backgroundIntensity;
3524
+
3525
+ #endif
3526
+
3527
+ // camera
3528
+ uniform mat4 cameraWorldMatrix;
3529
+ uniform mat4 invProjectionMatrix;
3530
+ #if FEATURE_DOF
3531
+
3532
+ uniform PhysicalCamera physicalCamera;
3533
+
3534
+ #endif
3535
+
3536
+ // geometry
3537
+ uniform sampler2DArray attributesArray;
3538
+ uniform usampler2D materialIndexAttribute;
3539
+ uniform sampler2D materials;
3540
+ uniform sampler2DArray textures;
3541
+ uniform BVH bvh;
3542
+
3543
+ // path tracer
3544
+ uniform int bounces;
3545
+ uniform int transmissiveBounces;
3546
+ uniform float filterGlossyFactor;
3547
+ uniform int seed;
3548
+
3549
+ // image
3550
+ uniform vec2 resolution;
3551
+ uniform float opacity;
3552
+
3553
+ varying vec2 vUv;
3554
+
3555
+ // globals
3556
+ mat3 envRotation3x3;
3557
+ mat3 invEnvRotation3x3;
3558
+ float lightsDenom;
3559
+
3560
+ // sampling
3561
+ ${za}
3562
+ ${La}
3563
+ ${Ra}
3564
+
3565
+ ${Qa}
3566
+ ${Ya}
3567
+ ${Za}
3568
+ ${Xa}
3569
+ ${Ja}
3570
+ ${qa}
3571
+
3572
+ float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
3573
+
3574
+ return clamp(
3575
+ max(
3576
+ roughness,
3577
+ accumulatedRoughness * filterGlossyFactor * 5.0 ),
3578
+ 0.0,
3579
+ 1.0
3580
+ );
3581
+
3582
+ }
3583
+
3584
+ vec3 sampleBackground( vec3 direction, vec2 uv ) {
3585
+
3586
+ vec3 sampleDir = sampleHemisphere( direction, uv ) * 0.5 * backgroundBlur;
3587
+
3588
+ #if FEATURE_BACKGROUND_MAP
3589
+
3590
+ sampleDir = normalize( mat3( backgroundRotation ) * direction + sampleDir );
3591
+ return backgroundIntensity * sampleEquirectColor( backgroundMap, sampleDir );
3592
+
3593
+ #else
3594
+
3595
+ sampleDir = normalize( envRotation3x3 * direction + sampleDir );
3596
+ return environmentIntensity * sampleEquirectColor( envMapInfo.map, sampleDir );
3597
+
3598
+ #endif
3599
+
3600
+ }
3601
+
3602
+ ${ro}
3603
+ ${eo}
3604
+ ${io}
3605
+ ${$a}
3606
+ ${to}
3607
+ ${no}
3608
+
3609
+ void main() {
3610
+
3611
+ // init
3612
+ rng_initialize( gl_FragCoord.xy, seed );
3613
+ sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
3614
+ sobolPathIndex = uint( seed );
3615
+
3616
+ // get camera ray
3617
+ Ray ray = getCameraRay();
3618
+
3619
+ // inverse environment rotation
3620
+ envRotation3x3 = mat3( environmentRotation );
3621
+ invEnvRotation3x3 = inverse( envRotation3x3 );
3622
+ lightsDenom =
3623
+ ( environmentIntensity == 0.0 || envMapInfo.totalSum == 0.0 ) && lights.count != 0u ?
3624
+ float( lights.count ) :
3625
+ float( lights.count + 1u );
3626
+
3627
+ // final color
3628
+ gl_FragColor = vec4( 0, 0, 0, 1 );
3629
+
3630
+ // surface results
3631
+ SurfaceHit surfaceHit;
3632
+ ScatterRecord scatterRec;
3633
+
3634
+ // path tracing state
3635
+ RenderState state = initRenderState();
3636
+ state.transmissiveTraversals = transmissiveBounces;
3637
+ #if FEATURE_FOG
3638
+
3639
+ state.fogMaterial.fogVolume = bvhIntersectFogVolumeHit(
3640
+ ray.origin, - ray.direction,
3641
+ materialIndexAttribute, materials,
3642
+ state.fogMaterial
3643
+ );
3644
+
3645
+ #endif
3646
+
3647
+ for ( int i = 0; i < bounces; i ++ ) {
3648
+
3649
+ sobolBounceIndex ++;
3650
+
3651
+ state.depth ++;
3652
+ state.traversals = bounces - i;
3653
+ state.firstRay = i == 0 && state.transmissiveTraversals == transmissiveBounces;
3654
+
3655
+ int hitType = traceScene( ray, state.fogMaterial, surfaceHit );
3656
+
3657
+ // check if we intersect any lights and accumulate the light contribution
3658
+ // TODO: we can add support for light surface rendering in the else condition if we
3659
+ // add the ability to toggle visibility of the the light
3660
+ if ( ! state.firstRay && ! state.transmissiveRay ) {
3661
+
3662
+ LightRecord lightRec;
3663
+ float lightDist = hitType == NO_HIT ? INFINITY : surfaceHit.dist;
3664
+ for ( uint i = 0u; i < lights.count; i ++ ) {
3665
+
3666
+ if (
3667
+ intersectLightAtIndex( lights.tex, ray.origin, ray.direction, i, lightRec ) &&
3668
+ lightRec.dist < lightDist
3669
+ ) {
3670
+
3671
+ #if FEATURE_MIS
3672
+
3673
+ // weight the contribution
3674
+ // NOTE: Only area lights are supported for forward sampling and can be hit
3675
+ float misWeight = misHeuristic( scatterRec.pdf, lightRec.pdf / lightsDenom );
3676
+ gl_FragColor.rgb += lightRec.emission * state.throughputColor * misWeight;
3677
+
3678
+ #else
3679
+
3680
+ gl_FragColor.rgb += lightRec.emission * state.throughputColor;
3681
+
3682
+ #endif
3683
+
3684
+ }
3685
+
3686
+ }
3687
+
3688
+ }
3689
+
3690
+ if ( hitType == NO_HIT ) {
3691
+
3692
+ if ( state.firstRay || state.transmissiveRay ) {
3693
+
3694
+ gl_FragColor.rgb += sampleBackground( ray.direction, rand2( 2 ) ) * state.throughputColor;
3695
+ gl_FragColor.a = backgroundAlpha;
3696
+
3697
+ } else {
3698
+
3699
+ #if FEATURE_MIS
3700
+
3701
+ // get the PDF of the hit envmap point
3702
+ vec3 envColor;
3703
+ float envPdf = sampleEquirect( envRotation3x3 * ray.direction, envColor );
3704
+ envPdf /= lightsDenom;
3705
+
3706
+ // and weight the contribution
3707
+ float misWeight = misHeuristic( scatterRec.pdf, envPdf );
3708
+ gl_FragColor.rgb += environmentIntensity * envColor * state.throughputColor * misWeight;
3709
+
3710
+ #else
3711
+
3712
+ gl_FragColor.rgb +=
3713
+ environmentIntensity *
3714
+ sampleEquirectColor( envMapInfo.map, envRotation3x3 * ray.direction ) *
3715
+ state.throughputColor;
3716
+
3717
+ #endif
3718
+
3719
+ }
3720
+ break;
3721
+
3722
+ }
3723
+
3724
+ uint materialIndex = uTexelFetch1D( materialIndexAttribute, surfaceHit.faceIndices.x ).r;
3725
+ Material material = readMaterialInfo( materials, materialIndex );
3726
+
3727
+ #if FEATURE_FOG
3728
+
3729
+ if ( hitType == FOG_HIT ) {
3730
+
3731
+ material = state.fogMaterial;
3732
+ state.accumulatedRoughness += 0.2;
3733
+
3734
+ } else if ( material.fogVolume ) {
3735
+
3736
+ state.fogMaterial = material;
3737
+ state.fogMaterial.fogVolume = surfaceHit.side == 1.0;
3738
+
3739
+ ray.origin = stepRayOrigin( ray.origin, ray.direction, - surfaceHit.faceNormal, surfaceHit.dist );
3740
+
3741
+ i -= sign( state.transmissiveTraversals );
3742
+ state.transmissiveTraversals -= sign( state.transmissiveTraversals );
3743
+ continue;
3744
+
3745
+ }
3746
+
3747
+ #endif
3748
+
3749
+ // early out if this is a matte material
3750
+ if ( material.matte && state.firstRay ) {
3751
+
3752
+ gl_FragColor = vec4( 0.0 );
3753
+ break;
3754
+
3755
+ }
3756
+
3757
+ // if we've determined that this is a shadow ray and we've hit an item with no shadow casting
3758
+ // then skip it
3759
+ if ( ! material.castShadow && state.isShadowRay ) {
3760
+
3761
+ ray.origin = stepRayOrigin( ray.origin, ray.direction, - surfaceHit.faceNormal, surfaceHit.dist );
3762
+ continue;
3763
+
3764
+ }
3765
+
3766
+ SurfaceRecord surf;
3767
+ if (
3768
+ getSurfaceRecord(
3769
+ material, surfaceHit, attributesArray, state.accumulatedRoughness,
3770
+ surf
3771
+ ) == SKIP_SURFACE
3772
+ ) {
3773
+
3774
+ // only allow a limited number of transparency discards otherwise we could
3775
+ // crash the context with too long a loop.
3776
+ i -= sign( state.transmissiveTraversals );
3777
+ state.transmissiveTraversals -= sign( state.transmissiveTraversals );
3778
+
3779
+ ray.origin = stepRayOrigin( ray.origin, ray.direction, - surfaceHit.faceNormal, surfaceHit.dist );
3780
+ continue;
3781
+
3782
+ }
3783
+
3784
+ scatterRec = bsdfSample( - ray.direction, surf );
3785
+ state.isShadowRay = scatterRec.specularPdf < rand( 4 );
3786
+
3787
+ bool isBelowSurface = ! surf.volumeParticle && dot( scatterRec.direction, surf.faceNormal ) < 0.0;
3788
+ vec3 hitPoint = stepRayOrigin( ray.origin, ray.direction, isBelowSurface ? - surf.faceNormal : surf.faceNormal, surfaceHit.dist );
3789
+
3790
+ // next event estimation
3791
+ #if FEATURE_MIS
3792
+
3793
+ gl_FragColor.rgb += directLightContribution( - ray.direction, surf, state, hitPoint );
3794
+
3795
+ #endif
3796
+
3797
+ // accumulate a roughness value to offset diffuse, specular, diffuse rays that have high contribution
3798
+ // to a single pixel resulting in fireflies
3799
+ // TODO: handle transmissive surfaces
3800
+ if ( ! surf.volumeParticle && ! isBelowSurface ) {
3801
+
3802
+ // determine if this is a rough normal or not by checking how far off straight up it is
3803
+ vec3 halfVector = normalize( - ray.direction + scatterRec.direction );
3804
+ state.accumulatedRoughness += max(
3805
+ sin( acosApprox( dot( halfVector, surf.normal ) ) ),
3806
+ sin( acosApprox( dot( halfVector, surf.clearcoatNormal ) ) )
3807
+ );
3808
+
3809
+ state.transmissiveRay = false;
3810
+
3811
+ }
3812
+
3813
+ // accumulate emissive color
3814
+ gl_FragColor.rgb += ( surf.emission * state.throughputColor );
3815
+
3816
+ // skip the sample if our PDF or ray is impossible
3817
+ if ( scatterRec.pdf <= 0.0 || ! isDirectionValid( scatterRec.direction, surf.normal, surf.faceNormal ) ) {
3818
+
3819
+ break;
3820
+
3821
+ }
3822
+
3823
+ // if we're bouncing around the inside a transmissive material then decrement
3824
+ // perform this separate from a bounce
3825
+ bool isTransmissiveRay = ! surf.volumeParticle && dot( scatterRec.direction, surf.faceNormal * surfaceHit.side ) < 0.0;
3826
+ if ( ( isTransmissiveRay || isBelowSurface ) && state.transmissiveTraversals > 0 ) {
3827
+
3828
+ state.transmissiveTraversals --;
3829
+ i --;
3830
+
3831
+ }
3832
+
3833
+ //
3834
+
3835
+ // handle throughput color transformation
3836
+ // attenuate the throughput color by the medium color
3837
+ if ( ! surf.frontFace ) {
3838
+
3839
+ state.throughputColor *= transmissionAttenuation( surfaceHit.dist, surf.attenuationColor, surf.attenuationDistance );
3840
+
3841
+ }
3842
+
3843
+ #if FEATURE_RUSSIAN_ROULETTE
3844
+
3845
+ // russian roulette path termination
3846
+ // https://www.arnoldrenderer.com/research/physically_based_shader_design_in_arnold.pdf
3847
+ uint minBounces = 3u;
3848
+ float depthProb = float( state.depth < minBounces );
3849
+
3850
+ float rrProb = luminance( state.throughputColor * scatterRec.color / scatterRec.pdf );
3851
+ rrProb /= luminance( state.throughputColor );
3852
+ rrProb = sqrt( rrProb );
3853
+ rrProb = max( rrProb, depthProb );
3854
+ rrProb = min( rrProb, 1.0 );
3855
+ if ( rand( 8 ) > rrProb ) {
3856
+
3857
+ break;
3858
+
3859
+ }
3860
+
3861
+ // perform sample clamping here to avoid bright pixels
3862
+ state.throughputColor *= min( 1.0 / rrProb, 20.0 );
3863
+
3864
+ #endif
3865
+
3866
+ // adjust the throughput and discard and exit if we find discard the sample if there are any NaNs
3867
+ state.throughputColor *= scatterRec.color / scatterRec.pdf;
3868
+ if ( any( isnan( state.throughputColor ) ) || any( isinf( state.throughputColor ) ) ) {
3869
+
3870
+ break;
3871
+
3872
+ }
3873
+
3874
+ //
3875
+
3876
+ // prepare for next ray
3877
+ ray.direction = scatterRec.direction;
3878
+ ray.origin = hitPoint;
3879
+
3880
+ }
3881
+
3882
+ gl_FragColor.a *= opacity;
3883
+
3884
+ #if DEBUG_MODE == 1
3885
+
3886
+ // output the number of rays checked in the path and number of
3887
+ // transmissive rays encountered.
3888
+ gl_FragColor.rgb = vec3(
3889
+ float( state.depth ),
3890
+ transmissiveBounces - state.transmissiveTraversals,
3891
+ 0.0
3892
+ );
3893
+ gl_FragColor.a = 1.0;
3894
+
3895
+ #endif
3896
+
3897
+ }
3898
+
3899
+ `}),this.setValues(e)}};function*oo(){let{_renderer:e,_fsQuad:t,_blendQuad:n,_primaryTarget:r,_blendTargets:i,_sobolTarget:a,_subframe:o,alpha:s,material:c}=this,l=new ce,u=new ce,d=n.material,[f,p]=i;for(;;){s?(d.opacity=this._opacityFactor/(this.samples+1),c.blending=0,c.opacity=1):(c.opacity=this._opacityFactor/(this.samples+1),c.blending=1);let[i,m,h,g]=o,_=r.width,v=r.height;c.resolution.set(_*h,v*g),c.sobolTexture=a.texture,c.stratifiedTexture.init(20,c.bounces+c.transmissiveBounces+5),c.stratifiedTexture.next(),c.seed++;let y=this.tiles.x||1,b=this.tiles.y||1,x=y*b,S=Math.ceil(_*h),C=Math.ceil(v*g),w=Math.floor(i*_),ee=Math.floor(m*v),te=Math.ceil(S/y),T=Math.ceil(C/b);for(let i=0;i<b;i++)for(let a=0;a<y;a++){let o=e.getRenderTarget(),c=e.autoClear,m=e.getScissorTest();e.getScissor(l),e.getViewport(u);let h=a,g=i;if(!this.stableTiles){let e=this._currentTile%(y*b);h=e%y,g=~~(e/y),this._currentTile=e+1}let _=b-g-1;r.scissor.set(w+h*te,ee+_*T,Math.min(te,S-h*te),Math.min(T,C-_*T)),r.viewport.set(w,ee,S,C),e.setRenderTarget(r),e.setScissorTest(!0),e.autoClear=!1,t.render(e),e.setViewport(u),e.setScissor(l),e.setScissorTest(m),e.setRenderTarget(o),e.autoClear=c,s&&(d.target1=f.texture,d.target2=r.texture,e.setRenderTarget(p),n.render(e),e.setRenderTarget(o)),this.samples+=1/x,a===y-1&&i===b-1&&(this.samples=Math.round(this.samples)),yield}[f,p]=[p,f]}}var so=new ze,co=class{get material(){return this._fsQuad.material}set material(e){this._fsQuad.material.removeEventListener(`recompilation`,this._compileFunction),e.addEventListener(`recompilation`,this._compileFunction),this._fsQuad.material=e}get target(){return this._alpha?this._blendTargets[1]:this._primaryTarget}set alpha(e){this._alpha!==e&&(e||(this._blendTargets[0].dispose(),this._blendTargets[1].dispose()),this._alpha=e,this.reset())}get alpha(){return this._alpha}get isCompiling(){return!!this._compilePromise}constructor(e){this.camera=null,this.tiles=new E(3,3),this.stableNoise=!1,this.stableTiles=!0,this.samples=0,this._subframe=new ce(0,0,1,1),this._opacityFactor=1,this._renderer=e,this._alpha=!1,this._fsQuad=new ae(new ao),this._blendQuad=new ae(new ki),this._task=null,this._currentTile=0,this._compilePromise=null,this._sobolTarget=new Ii().generate(e),this._primaryTarget=new De(1,1,{format:d,type:b,magFilter:S,minFilter:S}),this._blendTargets=[new De(1,1,{format:d,type:b,magFilter:S,minFilter:S}),new De(1,1,{format:d,type:b,magFilter:S,minFilter:S})],this._compileFunction=()=>{let e=this.compileMaterial(this._fsQuad._mesh);e.then(()=>{this._compilePromise===e&&(this._compilePromise=null)}),this._compilePromise=e},this.material.addEventListener(`recompilation`,this._compileFunction)}compileMaterial(){return this._renderer.compileAsync(this._fsQuad._mesh)}setCamera(e){let{material:t}=this;t.cameraWorldMatrix.copy(e.matrixWorld),t.invProjectionMatrix.copy(e.projectionMatrixInverse),t.physicalCamera.updateFrom(e);let n=0;e.projectionMatrix.elements[15]>0&&(n=1),e.isEquirectCamera&&(n=2),t.setDefine(`CAMERA_TYPE`,n),this.camera=e}setSize(e,t){e=Math.ceil(e),t=Math.ceil(t),(this._primaryTarget.width!==e||this._primaryTarget.height!==t)&&(this._primaryTarget.setSize(e,t),this._blendTargets[0].setSize(e,t),this._blendTargets[1].setSize(e,t),this.reset())}getSize(e){e.x=this._primaryTarget.width,e.y=this._primaryTarget.height}dispose(){this._primaryTarget.dispose(),this._blendTargets[0].dispose(),this._blendTargets[1].dispose(),this._sobolTarget.dispose(),this._fsQuad.dispose(),this._blendQuad.dispose(),this._task=null}reset(){let{_renderer:e,_primaryTarget:t,_blendTargets:n}=this,r=e.getRenderTarget(),i=e.getClearAlpha();e.getClearColor(so),e.setRenderTarget(t),e.setClearColor(0,0),e.clearColor(),e.setRenderTarget(n[0]),e.setClearColor(0,0),e.clearColor(),e.setRenderTarget(n[1]),e.setClearColor(0,0),e.clearColor(),e.setClearColor(so,i),e.setRenderTarget(r),this.samples=0,this._task=null,this.material.stratifiedTexture.stableNoise=this.stableNoise,this.stableNoise&&(this.material.seed=0,this.material.stratifiedTexture.reset())}update(){this.material.onBeforeRender(),!this.isCompiling&&(this._task||=oo.call(this),this._task.next())}},lo=new E,uo=new E,fo=new ge,po=new ze,mo=class extends Re{constructor(t=512,n=512){super(new Float32Array(t*n*4),t,n,d,b,303,e,T,m,m),this.generationCallback=null}update(){this.dispose(),this.needsUpdate=!0;let{data:e,width:t,height:n}=this.image;for(let r=0;r<t;r++)for(let i=0;i<n;i++){uo.set(t,n),lo.set(r/t,i/n),lo.x-=.5,lo.y=1-lo.y,fo.theta=lo.x*2*Math.PI,fo.phi=lo.y*Math.PI,fo.radius=1,this.generationCallback(fo,lo,uo,po);let a=4*(i*t+r);e[a+0]=po.r,e[a+1]=po.g,e[a+2]=po.b,e[a+3]=1}}copy(e){return super.copy(e),this.generationCallback=e.generationCallback,this}},ho=new D,go=class extends mo{constructor(e=512){super(e,e),this.topColor=new ze().set(16777215),this.bottomColor=new ze().set(0),this.exponent=2,this.generationCallback=(e,t,n,r)=>{ho.setFromSpherical(e);let i=ho.y*.5+.5;r.lerpColors(this.bottomColor,this.topColor,i**this.exponent)}}copy(e){return super.copy(e),this.topColor.copy(e.topColor),this.bottomColor.copy(e.bottomColor),this}},_o=class extends Pe{get map(){return this.uniforms.map.value}set map(e){this.uniforms.map.value=e}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e)}constructor(e){super({uniforms:{map:{value:null},opacity:{value:1}},vertexShader:`
3900
+ varying vec2 vUv;
3901
+ void main() {
3902
+
3903
+ vUv = uv;
3904
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
3905
+
3906
+ }
3907
+ `,fragmentShader:`
3908
+ uniform sampler2D map;
3909
+ uniform float opacity;
3910
+ varying vec2 vUv;
3911
+
3912
+ vec4 clampedTexelFatch( sampler2D map, ivec2 px, int lod ) {
3913
+
3914
+ vec4 res = texelFetch( map, ivec2( px.x, px.y ), 0 );
3915
+
3916
+ #if defined( TONE_MAPPING )
3917
+
3918
+ res.xyz = toneMapping( res.xyz );
3919
+
3920
+ #endif
3921
+
3922
+ return linearToOutputTexel( res );
3923
+
3924
+ }
3925
+
3926
+ void main() {
3927
+
3928
+ vec2 size = vec2( textureSize( map, 0 ) );
3929
+ vec2 pxUv = vUv * size;
3930
+ vec2 pxCurr = floor( pxUv );
3931
+ vec2 pxFrac = fract( pxUv ) - 0.5;
3932
+ vec2 pxOffset;
3933
+ pxOffset.x = pxFrac.x > 0.0 ? 1.0 : - 1.0;
3934
+ pxOffset.y = pxFrac.y > 0.0 ? 1.0 : - 1.0;
3935
+
3936
+ vec2 pxNext = clamp( pxOffset + pxCurr, vec2( 0.0 ), size - 1.0 );
3937
+ vec2 alpha = abs( pxFrac );
3938
+
3939
+ vec4 p1 = mix(
3940
+ clampedTexelFatch( map, ivec2( pxCurr.x, pxCurr.y ), 0 ),
3941
+ clampedTexelFatch( map, ivec2( pxNext.x, pxCurr.y ), 0 ),
3942
+ alpha.x
3943
+ );
3944
+
3945
+ vec4 p2 = mix(
3946
+ clampedTexelFatch( map, ivec2( pxCurr.x, pxNext.y ), 0 ),
3947
+ clampedTexelFatch( map, ivec2( pxNext.x, pxNext.y ), 0 ),
3948
+ alpha.x
3949
+ );
3950
+
3951
+ gl_FragColor = mix( p1, p2, alpha.y );
3952
+ gl_FragColor.a *= opacity;
3953
+ #include <premultiplied_alpha_fragment>
3954
+
3955
+ }
3956
+ `}),this.setValues(e)}},vo=class extends Pe{constructor(){super({uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:`
3957
+ varying vec2 vUv;
3958
+ void main() {
3959
+
3960
+ vUv = uv;
3961
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
3962
+
3963
+ }`,fragmentShader:`
3964
+ #define ENVMAP_TYPE_CUBE_UV
3965
+
3966
+ uniform samplerCube envMap;
3967
+ uniform float flipEnvMap;
3968
+ varying vec2 vUv;
3969
+
3970
+ #include <common>
3971
+ #include <cube_uv_reflection_fragment>
3972
+
3973
+ ${Wa}
3974
+
3975
+ void main() {
3976
+
3977
+ vec3 rayDirection = equirectUvToDirection( vUv );
3978
+ rayDirection.x *= flipEnvMap;
3979
+ gl_FragColor = textureCube( envMap, rayDirection );
3980
+
3981
+ }`}),this.depthWrite=!1,this.depthTest=!1}},yo=class{constructor(e){this._renderer=e,this._quad=new ae(new vo)}generate(t,n=null,i=null){if(!t.isCubeTexture)throw Error(`CubeToEquirectMaterial: Source can only be cube textures.`);let a=t.images[0],o=this._renderer,s=this._quad;n===null&&(n=4*a.height),i===null&&(i=2*a.height);let c=new De(n,i,{type:b,colorSpace:a.colorSpace}),l=a.height,u=Math.log2(l)-2,f=1/l,p=1/(3*Math.max(2**u,112));s.material.defines.CUBEUV_MAX_MIP=`${u}.0`,s.material.defines.CUBEUV_TEXEL_WIDTH=p,s.material.defines.CUBEUV_TEXEL_HEIGHT=f,s.material.uniforms.envMap.value=t,s.material.uniforms.flipEnvMap.value=t.isRenderTargetTexture?1:-1,s.material.needsUpdate=!0;let h=o.getRenderTarget(),_=o.autoClear;o.autoClear=!0,o.setRenderTarget(c),s.render(o),o.setRenderTarget(h),o.autoClear=_;let v=new Uint16Array(n*i*4),y=new Float32Array(n*i*4);o.readRenderTargetPixels(c,0,0,n,i,y),c.dispose();for(let e=0,t=y.length;e<t;e++)v[e]=r.toHalfFloat(y[e]);let x=new Re(v,n,i,d,ve);return x.minFilter=g,x.magFilter=m,x.wrapS=e,x.wrapT=e,x.mapping=303,x.needsUpdate=!0,x}dispose(){this._quad.dispose()}};function bo(e){return e.extensions.get(`EXT_float_blend`)}var xo=new E,So=class{get multipleImportanceSampling(){return!!this._pathTracer.material.defines.FEATURE_MIS}set multipleImportanceSampling(e){this._pathTracer.material.setDefine(`FEATURE_MIS`,+!!e)}get transmissiveBounces(){return this._pathTracer.material.transmissiveBounces}set transmissiveBounces(e){this._pathTracer.material.transmissiveBounces=e}get bounces(){return this._pathTracer.material.bounces}set bounces(e){this._pathTracer.material.bounces=e}get filterGlossyFactor(){return this._pathTracer.material.filterGlossyFactor}set filterGlossyFactor(e){this._pathTracer.material.filterGlossyFactor=e}get samples(){return this._pathTracer.samples}get target(){return this._pathTracer.target}get tiles(){return this._pathTracer.tiles}get stableNoise(){return this._pathTracer.stableNoise}set stableNoise(e){this._pathTracer.stableNoise=e}get isCompiling(){return!!this._pathTracer.isCompiling}constructor(e){this._renderer=e,this._generator=new Di,this._pathTracer=new co(e),this._queueReset=!1,this._clock=new Ie,this._compilePromise=null,this._lowResPathTracer=new co(e),this._lowResPathTracer.tiles.set(1,1),this._quad=new ae(new _o({map:null,transparent:!0,blending:0,premultipliedAlpha:e.getContextAttributes().premultipliedAlpha})),this._materials=null,this._previousEnvironment=null,this._previousBackground=null,this._internalBackground=null,this.renderDelay=100,this.minSamples=5,this.fadeDuration=500,this.enablePathTracing=!0,this.pausePathTracing=!1,this.dynamicLowRes=!1,this.lowResScale=.25,this.renderScale=1,this.synchronizeRenderSize=!0,this.rasterizeScene=!0,this.renderToCanvas=!0,this.textureSize=new E(1024,1024),this.rasterizeSceneCallback=(e,t)=>{this._renderer.render(e,t)},this.renderToCanvasCallback=(e,t,n)=>{let r=t.autoClear;t.autoClear=!1,n.render(t),t.autoClear=r},this.setScene(new le,new c)}setBVHWorker(e){this._generator.setBVHWorker(e)}setScene(e,t,n={}){e.updateMatrixWorld(!0),t.updateMatrixWorld();let r=this._generator;if(r.setObjects(e),this._buildAsync)return r.generateAsync(n.onProgress).then(n=>this._updateFromResults(e,t,n));{let n=r.generate();return this._updateFromResults(e,t,n)}}setSceneAsync(...e){this._buildAsync=!0;let t=this.setScene(...e);return this._buildAsync=!1,t}setCamera(e){this.camera=e,this.updateCamera()}updateCamera(){let e=this.camera;e.updateMatrixWorld(),this._pathTracer.setCamera(e),this._lowResPathTracer.setCamera(e),this.reset()}updateMaterials(){let e=this._pathTracer.material,t=this._renderer,n=this._materials,r=this.textureSize,i=ua(n);e.textures.setTextures(t,i,r.x,r.y),e.materials.updateFrom(n,i),this.reset()}updateLights(){let e=this.scene,t=this._renderer,n=this._pathTracer.material,r=da(e),i=la(r);n.lights.updateFrom(r,i),n.iesProfiles.setTextures(t,i),this.reset()}updateEnvironment(){let e=this.scene,t=this._pathTracer.material;if(this._internalBackground&&=(this._internalBackground.dispose(),null),t.backgroundBlur=e.backgroundBlurriness,t.backgroundIntensity=e.backgroundIntensity??1,t.backgroundRotation.makeRotationFromEuler(e.backgroundRotation).invert(),e.background===null)t.backgroundMap=null,t.backgroundAlpha=0;else if(e.background.isColor){this._colorBackground=this._colorBackground||new go(16);let n=this._colorBackground;n.topColor.equals(e.background)||(n.topColor.set(e.background),n.bottomColor.set(e.background),n.update()),t.backgroundMap=n,t.backgroundAlpha=1}else if(e.background.isCubeTexture){if(e.background!==this._previousBackground){let n=new yo(this._renderer).generate(e.background);this._internalBackground=n,t.backgroundMap=n,t.backgroundAlpha=1}}else t.backgroundMap=e.background,t.backgroundAlpha=1;if(t.environmentIntensity=e.environment===null?0:e.environmentIntensity??1,t.environmentRotation.makeRotationFromEuler(e.environmentRotation).invert(),this._previousEnvironment!==e.environment&&e.environment!==null){if(e.environment.isCubeTexture){let n=new yo(this._renderer).generate(e.environment);t.envMapInfo.updateFrom(n)}else t.envMapInfo.updateFrom(e.environment)}this._previousEnvironment=e.environment,this._previousBackground=e.background,this.reset()}_updateFromResults(e,t,n){let{materials:r,geometry:i,bvh:a,bvhChanged:o,needsMaterialIndexUpdate:s}=n;this._materials=r;let c=this._pathTracer.material;return o&&(c.bvh.updateFrom(a),c.attributesArray.updateFrom(i.attributes.normal,i.attributes.tangent,i.attributes.uv,i.attributes.color)),s&&c.materialIndexAttribute.updateFrom(i.attributes.materialIndex),this._previousScene=e,this.scene=e,this.camera=t,this.updateCamera(),this.updateMaterials(),this.updateEnvironment(),this.updateLights(),n}renderSample(){let e=this._lowResPathTracer,t=this._pathTracer,n=this._renderer,r=this._clock,i=this._quad;this._updateScale(),this._queueReset&&(t.reset(),e.reset(),this._queueReset=!1,i.material.opacity=0,r.start());let a=r.getDelta()*1e3,o=r.getElapsedTime()*1e3;if(!this.pausePathTracing&&this.enablePathTracing&&this.renderDelay<=o&&!this.isCompiling&&t.update(),t.alpha=t.material.backgroundAlpha!==1||!bo(n),e.alpha=t.alpha,this.renderToCanvas){let n=this._renderer,r=this.minSamples;if(o>=this.renderDelay&&this.samples>=this.minSamples&&(this.fadeDuration===0?i.material.opacity=1:i.material.opacity=Math.min(i.material.opacity+a/this.fadeDuration,1)),!this.enablePathTracing||this.samples<r||i.material.opacity<1){if(this.dynamicLowRes&&!this.isCompiling){e.samples<1&&(e.material=t.material,e.update());let r=i.material.opacity;i.material.opacity=1-i.material.opacity,i.material.map=e.target.texture,i.render(n),i.material.opacity=r}(!this.dynamicLowRes&&this.rasterizeScene||this.dynamicLowRes&&this.isCompiling)&&this.rasterizeSceneCallback(this.scene,this.camera)}this.enablePathTracing&&i.material.opacity>0&&(i.material.opacity<1&&(i.material.blending=this.dynamicLowRes?2:1),i.material.map=t.target.texture,this.renderToCanvasCallback(t.target,n,i),i.material.blending=0)}}reset(){this._queueReset=!0,this._pathTracer.samples=0}dispose(){this._quad.dispose(),this._quad.material.dispose(),this._pathTracer.dispose()}_updateScale(){if(this.synchronizeRenderSize){this._renderer.getDrawingBufferSize(xo);let e=Math.floor(this.renderScale*xo.x),t=Math.floor(this.renderScale*xo.y);if(this._pathTracer.getSize(xo),xo.x!==e||xo.y!==t){let n=this.lowResScale;this._pathTracer.setSize(e,t),this._lowResPathTracer.setSize(Math.floor(e*n),Math.floor(t*n))}}}},Co=class extends y{constructor(e){super(e),this.type=ve}parse(e){let t=function(e,t){switch(e){case 1:throw Error(`THREE.HDRLoader: Read Error: `+(t||``));case 2:throw Error(`THREE.HDRLoader: Write Error: `+(t||``));case 3:throw Error(`THREE.HDRLoader: Bad File Format: `+(t||``));default:case 4:throw Error(`THREE.HDRLoader: Memory Error: `+(t||``))}},n=function(e,t,n){t||=1024;let r=e.pos,i=-1,a=0,o=``,s=String.fromCharCode.apply(null,new Uint16Array(e.subarray(r,r+128)));for(;0>(i=s.indexOf(`
3982
+ `))&&a<t&&r<e.byteLength;)o+=s,a+=s.length,r+=128,s=String.fromCharCode.apply(null,new Uint16Array(e.subarray(r,r+128)));return-1<i&&(!1!==n&&(e.pos+=a+i+1),o+s.slice(0,i))},i=function(e){let r=/^#\?(\S+)/,i=/^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/,a=/^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/,o=/^\s*FORMAT=(\S+)\s*$/,s=/^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/,c={valid:0,string:``,comments:``,programtype:`RGBE`,format:``,gamma:1,exposure:1,width:0,height:0},l,u;for((e.pos>=e.byteLength||!(l=n(e)))&&t(1,`no header found`),(u=l.match(r))||t(3,`bad initial token`),c.valid|=1,c.programtype=u[1],c.string+=l+`
3983
+ `;l=n(e),!1!==l;){if(c.string+=l+`
3984
+ `,l.charAt(0)===`#`){c.comments+=l+`
3985
+ `;continue}if((u=l.match(i))&&(c.gamma=parseFloat(u[1])),(u=l.match(a))&&(c.exposure=parseFloat(u[1])),(u=l.match(o))&&(c.valid|=2,c.format=u[1]),(u=l.match(s))&&(c.valid|=4,c.height=parseInt(u[1],10),c.width=parseInt(u[2],10)),c.valid&2&&c.valid&4)break}return c.valid&2||t(3,`missing format specifier`),c.valid&4||t(3,`missing image size specifier`),c},a=function(e,n,r){let i=n;if(i<8||i>32767||e[0]!==2||e[1]!==2||e[2]&128)return new Uint8Array(e);i!==(e[2]<<8|e[3])&&t(3,`wrong scanline width`);let a=new Uint8Array(4*n*r);a.length||t(4,`unable to allocate buffer space`);let o=0,s=0,c=4*i,l=new Uint8Array(4),u=new Uint8Array(c),d=r;for(;d>0&&s<e.byteLength;){s+4>e.byteLength&&t(1),l[0]=e[s++],l[1]=e[s++],l[2]=e[s++],l[3]=e[s++],(l[0]!=2||l[1]!=2||(l[2]<<8|l[3])!=i)&&t(3,`bad rgbe scanline format`);let n=0,r;for(;n<c&&s<e.byteLength;){r=e[s++];let i=r>128;if(i&&(r-=128),(r===0||n+r>c)&&t(3,`bad scanline data`),i){let t=e[s++];for(let e=0;e<r;e++)u[n++]=t}else u.set(e.subarray(s,s+r),n),n+=r,s+=r}let f=i;for(let e=0;e<f;e++){let t=0;a[o]=u[e+t],t+=i,a[o+1]=u[e+t],t+=i,a[o+2]=u[e+t],t+=i,a[o+3]=u[e+t],o+=4}d--}return a},s=function(e,t,n,r){let i=2**(e[t+3]-128)/255;n[r+0]=e[t+0]*i,n[r+1]=e[t+1]*i,n[r+2]=e[t+2]*i,n[r+3]=1},c=function(e,t,n,i){let a=2**(e[t+3]-128)/255;n[i+0]=r.toHalfFloat(Math.min(e[t+0]*a,65504)),n[i+1]=r.toHalfFloat(Math.min(e[t+1]*a,65504)),n[i+2]=r.toHalfFloat(Math.min(e[t+2]*a,65504)),n[i+3]=r.toHalfFloat(1)},l=new Uint8Array(e);l.pos=0;let u=i(l),d=u.width,f=u.height,p=a(l.subarray(l.pos),d,f),h,g,_;switch(this.type){case b:_=p.length/4;let e=new Float32Array(_*4);for(let t=0;t<_;t++)s(p,t*4,e,t*4);h=e,g=b;break;case ve:_=p.length/4;let t=new Uint16Array(_*4);for(let e=0;e<_;e++)c(p,e*4,t,e*4);h=t,g=ve;break;default:throw Error(`THREE.HDRLoader: Unsupported type: `+this.type)}return{width:d,height:f,data:h,header:u.string,gamma:u.gamma,exposure:u.exposure,type:g,colorSpace:o,minFilter:m,magFilter:m,generateMipmaps:!1,flipY:!0}}setDataType(e){return this.type=e,this}},wo=`// Global detail setting
3986
+ $fn = 64;
3987
+
3988
+ // Animation Data Dictionary
3989
+ // Subdividing 360-degree rotations into 90-degree increments to adhere to glTF shortest-path interpolation rules.
3990
+ anim_data = [
3991
+ ["OrbitalMotion", [
3992
+ ["OuterRing", [
3993
+ // Rotates around the X-axis
3994
+ [0.0, [0, 0, 0]],
3995
+ [1.0, [90, 0, 0]],
3996
+ [2.0, [180, 0, 0]],
3997
+ [3.0, [270, 0, 0]],
3998
+ [4.0, [360, 0, 0]]
3999
+ ]],
4000
+ ["InnerRing", [
4001
+ // Rotates backwards around the Y-axis (relative to OuterRing)
4002
+ [0.0, [0, 0, 0]],
4003
+ [1.0, [0, -90, 0]],
4004
+ [2.0, [0, -180, 0]],
4005
+ [3.0, [0, -270, 0]],
4006
+ [4.0, [0, -360, 0]]
4007
+ ]],
4008
+ ["Gem", [
4009
+ // Rotates around the Z-axis while hovering up and down on the Z-axis
4010
+ [0.0, [0, 0, 0], [0, 0, 0]],
4011
+ [1.0, [0, 0, 90], [0, 0, 2]],
4012
+ [2.0, [0, 0, 180], [0, 0, 0]],
4013
+ [3.0, [0, 0, 270], [0, 0, -2]],
4014
+ [4.0, [0, 0, 360], [0, 0, 0]]
4015
+ ]]
4016
+ ]]
4017
+ ];
4018
+
4019
+ // Root armature wrapping all animated and static components
4020
+ armature(animations=anim_data) {
4021
+
4022
+ // --- STATIC ENVIRONMENT ---
4023
+
4024
+ // Polished dark wood base
4025
+ // Showcasing: autoSmoothAngle (smoothes cylinder sides but keeps caps sharp)
4026
+ color([0.15, 0.08, 0.03, 1.0], roughness=0.8, clearcoat=1.0, clearcoatRoughness=0.02, ior=1.5, $asa=30) {
4027
+ cylinder(h=2, r=24);
4028
+ translate([0, 0, 2]) cylinder(h=1, r1=24, r2=22);
4029
+ translate([0, 0, 3]) cylinder(h=2, r=22);
4030
+ }
4031
+
4032
+ // Glowing Neon Accent Inlay on the base
4033
+ // Showcasing: emissive, autoSmoothAngle (makes the thin torus look like a smooth tube)
4034
+ translate([0, 0, 5])
4035
+ color([0.0, 0.0, 0.0, 1.0], emissive=[0.0, 0.6, 1.0], emissiveIntensity=4.0, $asa=45) {
4036
+ rotate_extrude() translate([20, 0, 0]) circle(r=0.2);
4037
+ }
4038
+
4039
+ // Velvet display cushion
4040
+ // Showcasing: sheen, autoSmoothAngle (essential for organic/soft shapes)
4041
+ translate([0, 0, 5.5])
4042
+ color([0.7, 0.05, 0.1, 1.0], roughness=0.9, sheen=1.0, sheenColor=[1.0, 0.3, 0.3], sheenRoughness=0.6, specularColor=[0.5, 0.1, 0.1], specularIntensity=0.5, $asa=60)
4043
+ scale([1, 1, 0.3]) sphere(r=18);
4044
+
4045
+ // Bronze support pillars
4046
+ color([0.8, 0.5, 0.2, 1.0], metalness=1.0, roughness=0.4, $asa=35) {
4047
+ // Vertical stands
4048
+ translate([-17, 0, 15]) cylinder(h=22, r=1.5, center=true);
4049
+ translate([17, 0, 15]) cylinder(h=22, r=1.5, center=true);
4050
+
4051
+ // Pivot caps
4052
+ translate([-17, 0, 26]) rotate([0, 90, 0]) cylinder(h=4, r=1.2, center=true);
4053
+ translate([17, 0, 26]) rotate([0, 90, 0]) cylinder(h=4, r=1.2, center=true);
4054
+ }
4055
+
4056
+ // --- ANIMATED MECHANISM HIERARCHY ---
4057
+
4058
+ // 1. Outer Ring
4059
+ bone(name="OuterRing", t=[0,0,26]) {
4060
+
4061
+ // Polished Gold Material
4062
+ // Showcasing: metalness + autoSmoothAngle for high-quality reflections
4063
+ color([1.0, 0.84, 0.0, 1.0], metalness=1.0, roughness=0.1, $asa=45) {
4064
+ rotate_extrude() translate([14, 0, 0]) circle(r=1.0);
4065
+
4066
+ // Inner pins (sharp edges are preserved because they exceed 45 degrees)
4067
+ translate([0, 11.5, 0]) rotate([90, 0, 0]) cylinder(h=3, r=0.6, center=true);
4068
+ translate([0, -11.5, 0]) rotate([90, 0, 0]) cylinder(h=3, r=0.6, center=true);
4069
+ }
4070
+
4071
+ // 2. Inner Ring
4072
+ bone(name="InnerRing", t=[0,0,0]) {
4073
+
4074
+ // Iridescent Titanium Material
4075
+ color([0.6, 0.6, 0.65, 1.0], metalness=0.9, roughness=0.05, iridescence=1.0, iridescenceIOR=1.6, $asa=45) {
4076
+ rotate_extrude() translate([10, 0, 0]) circle(r=0.8);
4077
+ }
4078
+
4079
+ // 3. Floating Gem
4080
+ bone(name="Gem", t=[0,0,0]) {
4081
+
4082
+ // Deep Volumetric Sapphire
4083
+ // Showcasing: $asa=0.0 (Preserves faceted "cut" look of the crystal)
4084
+ color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=3.0) {
4085
+
4086
+ // Octagonal faceted crystal shape
4087
+ union() {
4088
+ cylinder(h=5, r1=4, r2=0, $fn=8);
4089
+ translate([0,0,-5]) cylinder(h=5, r1=0, r2=4, $fn=8);
4090
+ }
4091
+ }
4092
+ }
4093
+ }
4094
+ }
4095
+ }
4096
+ `,To=document.getElementById(`prompt-desc`),Eo=document.getElementById(`copy-prompt-btn`),Do=document.getElementById(`model-name-input`),z=document.getElementById(`editor`),Oo=document.getElementById(`render-btn`),ko=document.getElementById(`load-scad-btn`),Ao=document.getElementById(`download-scad-btn`),jo=document.getElementById(`export-gltf-btn`),Mo=document.getElementById(`capture-image-btn`),No=document.getElementById(`share-btn`),Po=document.getElementById(`auto-render-cb`),Fo=document.getElementById(`minify-share-cb`),B=document.getElementById(`viewer`),Io=document.getElementById(`right-panel`),Lo=document.getElementById(`backend-url`),Ro=document.getElementById(`backend-connect-btn`),zo=document.getElementById(`backend-ui`),Bo=document.getElementById(`backend-select`),Vo=document.getElementById(`backend-single-save-btn`),Ho=document.getElementById(`backend-delete-btn`),Uo=document.getElementById(`anim-controls-section`),Wo=document.getElementById(`anim-play-btn`),Go=document.getElementById(`anim-select`),Ko=document.getElementById(`anim-slider`),qo=document.getElementById(`prompt-ui-container`),Jo=document.getElementById(`show-grid-cb`),Yo=document.getElementById(`wireframe-cb`),Xo=document.getElementById(`fullscreen-btn`),Zo=``,V=null,Qo=null,$o=[],es=!1,ts=null,H=null,ns=!1,rs=!1,is=null,as=[],os=!1,ss=!1,cs={isNew:!0,options:{},content:``},U=null,ls=!0,us=!1;function ds(){return ss?z.value:z.value||`// Global detail setting
4097
+ $fn = 64;
4098
+
4099
+ // Animation Data Dictionary
4100
+ // Subdividing 360-degree rotations into 90-degree increments to adhere to glTF shortest-path interpolation rules.
4101
+ anim_data = [
4102
+ ["OrbitalMotion", [
4103
+ ["OuterRing", [
4104
+ // Rotates around the X-axis
4105
+ [0.0, [0, 0, 0]],
4106
+ [1.0, [90, 0, 0]],
4107
+ [2.0, [180, 0, 0]],
4108
+ [3.0, [270, 0, 0]],
4109
+ [4.0, [360, 0, 0]]
4110
+ ]],
4111
+ ["InnerRing", [
4112
+ // Rotates backwards around the Y-axis (relative to OuterRing)
4113
+ [0.0, [0, 0, 0]],
4114
+ [1.0, [0, -90, 0]],
4115
+ [2.0, [0, -180, 0]],
4116
+ [3.0, [0, -270, 0]],
4117
+ [4.0, [0, -360, 0]]
4118
+ ]],
4119
+ ["Gem", [
4120
+ // Rotates around the Z-axis while hovering up and down on the Z-axis
4121
+ [0.0, [0, 0, 0], [0, 0, 0]],
4122
+ [1.0, [0, 0, 90], [0, 0, 2]],
4123
+ [2.0, [0, 0, 180], [0, 0, 0]],
4124
+ [3.0, [0, 0, 270], [0, 0, -2]],
4125
+ [4.0, [0, 0, 360], [0, 0, 0]]
4126
+ ]]
4127
+ ]]
4128
+ ];
4129
+
4130
+ // Root armature wrapping all animated and static components
4131
+ armature(animations=anim_data) {
4132
+
4133
+ // --- STATIC ENVIRONMENT ---
4134
+
4135
+ // Polished dark wood base
4136
+ // Showcasing: autoSmoothAngle (smoothes cylinder sides but keeps caps sharp)
4137
+ color([0.15, 0.08, 0.03, 1.0], roughness=0.8, clearcoat=1.0, clearcoatRoughness=0.02, ior=1.5, $asa=30) {
4138
+ cylinder(h=2, r=24);
4139
+ translate([0, 0, 2]) cylinder(h=1, r1=24, r2=22);
4140
+ translate([0, 0, 3]) cylinder(h=2, r=22);
4141
+ }
4142
+
4143
+ // Glowing Neon Accent Inlay on the base
4144
+ // Showcasing: emissive, autoSmoothAngle (makes the thin torus look like a smooth tube)
4145
+ translate([0, 0, 5])
4146
+ color([0.0, 0.0, 0.0, 1.0], emissive=[0.0, 0.6, 1.0], emissiveIntensity=4.0, $asa=45) {
4147
+ rotate_extrude() translate([20, 0, 0]) circle(r=0.2);
4148
+ }
4149
+
4150
+ // Velvet display cushion
4151
+ // Showcasing: sheen, autoSmoothAngle (essential for organic/soft shapes)
4152
+ translate([0, 0, 5.5])
4153
+ color([0.7, 0.05, 0.1, 1.0], roughness=0.9, sheen=1.0, sheenColor=[1.0, 0.3, 0.3], sheenRoughness=0.6, specularColor=[0.5, 0.1, 0.1], specularIntensity=0.5, $asa=60)
4154
+ scale([1, 1, 0.3]) sphere(r=18);
4155
+
4156
+ // Bronze support pillars
4157
+ color([0.8, 0.5, 0.2, 1.0], metalness=1.0, roughness=0.4, $asa=35) {
4158
+ // Vertical stands
4159
+ translate([-17, 0, 15]) cylinder(h=22, r=1.5, center=true);
4160
+ translate([17, 0, 15]) cylinder(h=22, r=1.5, center=true);
4161
+
4162
+ // Pivot caps
4163
+ translate([-17, 0, 26]) rotate([0, 90, 0]) cylinder(h=4, r=1.2, center=true);
4164
+ translate([17, 0, 26]) rotate([0, 90, 0]) cylinder(h=4, r=1.2, center=true);
4165
+ }
4166
+
4167
+ // --- ANIMATED MECHANISM HIERARCHY ---
4168
+
4169
+ // 1. Outer Ring
4170
+ bone(name="OuterRing", t=[0,0,26]) {
4171
+
4172
+ // Polished Gold Material
4173
+ // Showcasing: metalness + autoSmoothAngle for high-quality reflections
4174
+ color([1.0, 0.84, 0.0, 1.0], metalness=1.0, roughness=0.1, $asa=45) {
4175
+ rotate_extrude() translate([14, 0, 0]) circle(r=1.0);
4176
+
4177
+ // Inner pins (sharp edges are preserved because they exceed 45 degrees)
4178
+ translate([0, 11.5, 0]) rotate([90, 0, 0]) cylinder(h=3, r=0.6, center=true);
4179
+ translate([0, -11.5, 0]) rotate([90, 0, 0]) cylinder(h=3, r=0.6, center=true);
4180
+ }
4181
+
4182
+ // 2. Inner Ring
4183
+ bone(name="InnerRing", t=[0,0,0]) {
4184
+
4185
+ // Iridescent Titanium Material
4186
+ color([0.6, 0.6, 0.65, 1.0], metalness=0.9, roughness=0.05, iridescence=1.0, iridescenceIOR=1.6, $asa=45) {
4187
+ rotate_extrude() translate([10, 0, 0]) circle(r=0.8);
4188
+ }
4189
+
4190
+ // 3. Floating Gem
4191
+ bone(name="Gem", t=[0,0,0]) {
4192
+
4193
+ // Deep Volumetric Sapphire
4194
+ // Showcasing: $asa=0.0 (Preserves faceted "cut" look of the crystal)
4195
+ color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=3.0) {
4196
+
4197
+ // Octagonal faceted crystal shape
4198
+ union() {
4199
+ cylinder(h=5, r1=4, r2=0, $fn=8);
4200
+ translate([0,0,-5]) cylinder(h=5, r1=0, r2=4, $fn=8);
4201
+ }
4202
+ }
4203
+ }
4204
+ }
4205
+ }
4206
+ }
4207
+ `}function fs(){return Do.value.trim()||`model`}Do.addEventListener(`input`,Ds),qo&&(qo.innerHTML=We,Ue(qo,`scad_editor_prompt_settings`)),Eo.onclick=async()=>{let e=To.value.trim()||`an object`,t=He(qo),n=Ve(e,t);try{await navigator.clipboard.writeText(n);let e=Eo.innerText;Eo.innerText=`✅ Copied!`,setTimeout(()=>{Eo.innerText=e},2e3)}catch(e){alert(`Failed to copy clipboard: `+e)}};async function ps(e){let t={};if(!ss)return t;let n=new Set;async function r(e){let i=/(?:include|use)\s*([<"])([^>"]+)([>"])/g,a;for(;(a=i.exec(e))!==null;){let e=a[2],i=e.split(/[/\\]/).pop();if(!n.has(e)){n.add(e);try{let n=await fetch(`${Cs}/api/scads/${encodeURIComponent(i)}`);if(n.ok){let i=await n.json();t[e]=i.content,await r(i.content)}}catch{console.warn(`Failed to load dependency: ${e}`)}}}}return await r(e),t}async function ms(e){if(typeof e!=`string`)return;if(es){ts=e;return}if(e.trim()===``){Rs();return}let t=e.match(/\/\*\s*Model Name:\s*(.*?)\s*\*\//i);if(t&&t[1]){let e=t[1].trim().replace(/\s+/g,`_`).replace(/[^a-zA-Z0-9_-]/g,``);e&&Do.value!==e&&(Do.value=e,Ds())}es=!0;try{let t=await ps(e);Qo=await xe(e,{wasmUrl:me,additionalFiles:t}),await Bs(Qo)}catch(e){console.error(e)}finally{if(es=!1,ts!==null){let e=ts;ts=null,ms(e)}}}Oo.onclick=()=>ms(ds());var hs;z.addEventListener(`input`,()=>{Ds(),clearTimeout(hs),Po.checked&&(hs=setTimeout(()=>{ms(ds())},800))}),Po.addEventListener(`change`,()=>{Po.checked&&ms(ds())}),ko.onclick=()=>{let e=document.createElement(`input`);e.type=`file`,e.accept=`.scad`,e.onchange=async e=>{if(e.target.files&&e.target.files.length>0)try{let t=await e.target.files[0].text();z.value=t,Ds(),ms(t)}catch(e){alert(`Failed to read file: `+e.message)}},e.click()};function gs(e,t){let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n)}Ao.onclick=()=>{let e=ds();gs(new Blob([e],{type:`text/plain`}),`${fs()}.scad`)},jo.onclick=()=>{Qo&&gs(new Blob([Qo],{type:`application/octet-stream`}),`${fs()}.glb`)};function _s(){Mo.innerText=rs?`🔴 Rec...`:U&&ls?`🎥 Video`:`📷 Image`}Mo.onclick=()=>{if(rs){os=!0,is&&is.state!==`inactive`&&is.stop();return}if(U&&ls){if(typeof MediaRecorder<`u`){let e=q!==void 0&&J&&J.checked;U.time=0,H&&H.update(0),G.aspect=1,G.updateProjectionMatrix(),K.setSize(512,512,!1),ks.setSize(512,512),e&&q.updateCamera();let t=K.domElement,n=document.createElement(`canvas`);n.width=512,n.height=512;let r=n.getContext(`2d`),i=n.captureStream(30),a=25e5,o=`mp4`,s={mimeType:`video/mp4;codecs=avc1`,videoBitsPerSecond:a};MediaRecorder.isTypeSupported(s.mimeType)||(s={mimeType:`video/mp4`,videoBitsPerSecond:a},MediaRecorder.isTypeSupported(s.mimeType)||(o=`webm`,s={mimeType:`video/webm;codecs=vp9`,videoBitsPerSecond:a},MediaRecorder.isTypeSupported(s.mimeType)||(s={mimeType:`video/webm;codecs=vp8`,videoBitsPerSecond:a},MediaRecorder.isTypeSupported(s.mimeType)||(s={mimeType:`video/webm`,videoBitsPerSecond:a}))));let c=null;os=!1,rs=!0,_s();let l=U.getClip().duration,u=Math.round(l*30),d=0,f=[],p=()=>{if(os||d>=u){if(f.length===0){rs=!1,G.aspect=B.clientWidth/B.clientHeight,G.updateProjectionMatrix(),K.setSize(B.clientWidth,B.clientHeight),ks.setSize(B.clientWidth,B.clientHeight),e&&q!==void 0&&q.updateCamera(),_s();return}os=!1,is=new MediaRecorder(i,s),as=[],is.ondataavailable=e=>{e.data.size>0&&as.push(e.data)},is.onstop=()=>{c&&clearTimeout(c),gs(new Blob(as,{type:s.mimeType.split(`;`)[0]}),`${fs()}.${o}`),rs=!1,G.aspect=B.clientWidth/B.clientHeight,G.updateProjectionMatrix(),K.setSize(B.clientWidth,B.clientHeight),ks.setSize(B.clientWidth,B.clientHeight),e&&q!==void 0&&q.updateCamera(),_s()},r.drawImage(f[0],0,0,512,512),is.start();let t=0,n=performance.now(),a=()=>{if(os){is.stop();return}if(t<f.length){r.drawImage(f[t],0,0,512,512),t++;let e=n+1e3/30*t,i=Math.max(0,e-performance.now());c=setTimeout(a,i)}else c=setTimeout(()=>is.stop(),50)};c=setTimeout(a,0);return}if(U.time=d/30,H&&H.update(0),e){Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G);let e=()=>{if(os){p();return}let n=0;for(;q.samples<100&&n<5;)q.renderSample(),n++;if(q.samples>=100){let e=document.createElement(`canvas`);e.width=512,e.height=512,e.getContext(`2d`).drawImage(t,0,0,512,512),f.push(e),d++,setTimeout(p,0)}else requestAnimationFrame(e)};e()}else{let e=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!0),Y!==void 0&&(Y.visible=!0),Q!==void 0&&(Q.visible=e),$!==void 0&&($.visible=e),ks.render();let n=document.createElement(`canvas`);n.width=512,n.height=512,n.getContext(`2d`).drawImage(t,0,0,512,512),f.push(n),d++,setTimeout(p,0)}};p()}else alert(`Video recording is not supported in this browser.`)}else ns=!0};function vs(e){let t=e.length%4;return t===2?e+`==`:t===3?e+`=`:e}async function ys(e){try{if(typeof CompressionStream<`u`){let t=new Blob([e]).stream().pipeThrough(new CompressionStream(`deflate-raw`)),n=await new Response(t).arrayBuffer(),r=new Uint8Array(n),i=``;for(let e=0;e<r.length;e++)i+=String.fromCharCode(r[e]);return`c`+btoa(i).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}}catch(e){console.warn(`CompressionStream failed, falling back`,e)}return`u`+btoa(unescape(encodeURIComponent(e))).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}async function bs(e){if(!e)return null;let t=e.charAt(0),n=e.substring(1);if(n=vs(n.replace(/-/g,`+`).replace(/_/g,`/`)),t===`c`)try{let e=atob(n),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);let r=new Blob([t]).stream().pipeThrough(new DecompressionStream(`deflate-raw`)),i=await new Response(r).arrayBuffer();return new TextDecoder().decode(i)}catch(e){console.warn(`DecompressionStream failed`,e)}else if(t===`u`)try{return decodeURIComponent(escape(atob(n)))}catch(e){console.warn(`Unescape failed`,e)}else try{return decodeURIComponent(escape(atob(vs(e.replace(/-/g,`+`).replace(/_/g,`/`)))))}catch{return decodeURIComponent(e)}return null}No.onclick=async()=>{let e=z.value.trim(),t=e,n=new URL(window.location.href),r=``;if(Fo&&Fo.checked){let t=``,n=!1,r=0;for(;r<e.length;)if(n)t+=e[r],e[r]===`"`&&e[r-1]!==`\\`&&(n=!1),r++;else if(e[r]===`"`)n=!0,t+=e[r],r++;else if(e[r]===`/`&&e[r+1]===`/`)for(r+=2;r<e.length&&e[r]!==`
4208
+ `;)r++;else if(e[r]===`/`&&e[r+1]===`*`){for(r+=2;r<e.length&&(e[r]!==`*`||e[r+1]!==`/`);)r++;r+=2}else/\s/.test(e[r])?(t.length>0&&!/\s/.test(t[t.length-1])&&(t+=` `),r++):(t+=e[r],r++);e=t.trim()}if(!e||!ss&&t===`// Global detail setting
4209
+ $fn = 64;
4210
+
4211
+ // Animation Data Dictionary
4212
+ // Subdividing 360-degree rotations into 90-degree increments to adhere to glTF shortest-path interpolation rules.
4213
+ anim_data = [
4214
+ ["OrbitalMotion", [
4215
+ ["OuterRing", [
4216
+ // Rotates around the X-axis
4217
+ [0.0, [0, 0, 0]],
4218
+ [1.0, [90, 0, 0]],
4219
+ [2.0, [180, 0, 0]],
4220
+ [3.0, [270, 0, 0]],
4221
+ [4.0, [360, 0, 0]]
4222
+ ]],
4223
+ ["InnerRing", [
4224
+ // Rotates backwards around the Y-axis (relative to OuterRing)
4225
+ [0.0, [0, 0, 0]],
4226
+ [1.0, [0, -90, 0]],
4227
+ [2.0, [0, -180, 0]],
4228
+ [3.0, [0, -270, 0]],
4229
+ [4.0, [0, -360, 0]]
4230
+ ]],
4231
+ ["Gem", [
4232
+ // Rotates around the Z-axis while hovering up and down on the Z-axis
4233
+ [0.0, [0, 0, 0], [0, 0, 0]],
4234
+ [1.0, [0, 0, 90], [0, 0, 2]],
4235
+ [2.0, [0, 0, 180], [0, 0, 0]],
4236
+ [3.0, [0, 0, 270], [0, 0, -2]],
4237
+ [4.0, [0, 0, 360], [0, 0, 0]]
4238
+ ]]
4239
+ ]]
4240
+ ];
4241
+
4242
+ // Root armature wrapping all animated and static components
4243
+ armature(animations=anim_data) {
4244
+
4245
+ // --- STATIC ENVIRONMENT ---
4246
+
4247
+ // Polished dark wood base
4248
+ // Showcasing: autoSmoothAngle (smoothes cylinder sides but keeps caps sharp)
4249
+ color([0.15, 0.08, 0.03, 1.0], roughness=0.8, clearcoat=1.0, clearcoatRoughness=0.02, ior=1.5, $asa=30) {
4250
+ cylinder(h=2, r=24);
4251
+ translate([0, 0, 2]) cylinder(h=1, r1=24, r2=22);
4252
+ translate([0, 0, 3]) cylinder(h=2, r=22);
4253
+ }
4254
+
4255
+ // Glowing Neon Accent Inlay on the base
4256
+ // Showcasing: emissive, autoSmoothAngle (makes the thin torus look like a smooth tube)
4257
+ translate([0, 0, 5])
4258
+ color([0.0, 0.0, 0.0, 1.0], emissive=[0.0, 0.6, 1.0], emissiveIntensity=4.0, $asa=45) {
4259
+ rotate_extrude() translate([20, 0, 0]) circle(r=0.2);
4260
+ }
4261
+
4262
+ // Velvet display cushion
4263
+ // Showcasing: sheen, autoSmoothAngle (essential for organic/soft shapes)
4264
+ translate([0, 0, 5.5])
4265
+ color([0.7, 0.05, 0.1, 1.0], roughness=0.9, sheen=1.0, sheenColor=[1.0, 0.3, 0.3], sheenRoughness=0.6, specularColor=[0.5, 0.1, 0.1], specularIntensity=0.5, $asa=60)
4266
+ scale([1, 1, 0.3]) sphere(r=18);
4267
+
4268
+ // Bronze support pillars
4269
+ color([0.8, 0.5, 0.2, 1.0], metalness=1.0, roughness=0.4, $asa=35) {
4270
+ // Vertical stands
4271
+ translate([-17, 0, 15]) cylinder(h=22, r=1.5, center=true);
4272
+ translate([17, 0, 15]) cylinder(h=22, r=1.5, center=true);
4273
+
4274
+ // Pivot caps
4275
+ translate([-17, 0, 26]) rotate([0, 90, 0]) cylinder(h=4, r=1.2, center=true);
4276
+ translate([17, 0, 26]) rotate([0, 90, 0]) cylinder(h=4, r=1.2, center=true);
4277
+ }
4278
+
4279
+ // --- ANIMATED MECHANISM HIERARCHY ---
4280
+
4281
+ // 1. Outer Ring
4282
+ bone(name="OuterRing", t=[0,0,26]) {
4283
+
4284
+ // Polished Gold Material
4285
+ // Showcasing: metalness + autoSmoothAngle for high-quality reflections
4286
+ color([1.0, 0.84, 0.0, 1.0], metalness=1.0, roughness=0.1, $asa=45) {
4287
+ rotate_extrude() translate([14, 0, 0]) circle(r=1.0);
4288
+
4289
+ // Inner pins (sharp edges are preserved because they exceed 45 degrees)
4290
+ translate([0, 11.5, 0]) rotate([90, 0, 0]) cylinder(h=3, r=0.6, center=true);
4291
+ translate([0, -11.5, 0]) rotate([90, 0, 0]) cylinder(h=3, r=0.6, center=true);
4292
+ }
4293
+
4294
+ // 2. Inner Ring
4295
+ bone(name="InnerRing", t=[0,0,0]) {
4296
+
4297
+ // Iridescent Titanium Material
4298
+ color([0.6, 0.6, 0.65, 1.0], metalness=0.9, roughness=0.05, iridescence=1.0, iridescenceIOR=1.6, $asa=45) {
4299
+ rotate_extrude() translate([10, 0, 0]) circle(r=0.8);
4300
+ }
4301
+
4302
+ // 3. Floating Gem
4303
+ bone(name="Gem", t=[0,0,0]) {
4304
+
4305
+ // Deep Volumetric Sapphire
4306
+ // Showcasing: $asa=0.0 (Preserves faceted "cut" look of the crystal)
4307
+ color([1.0, 1.0, 1.0, 1.0], transmission=1.0, roughness=0.0, thickness=8.0, ior=1.76, attenuationColor=[0.1, 0.3, 0.9], attenuationDistance=3.0) {
4308
+
4309
+ // Octagonal faceted crystal shape
4310
+ union() {
4311
+ cylinder(h=5, r1=4, r2=0, $fn=8);
4312
+ translate([0,0,-5]) cylinder(h=5, r1=0, r2=4, $fn=8);
4313
+ }
4314
+ }
4315
+ }
4316
+ }
4317
+ }
4318
+ }`)r=n.origin+n.pathname+n.search;else try{n.hash=await ys(e),r=n.toString()}catch{r=n.origin+n.pathname+n.search}window.history.replaceState(null,``,r);try{if(navigator.share){let e=Do.value.trim(),t=e?e.replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()):`Scadify`;await navigator.share({title:t,url:r});let n=No.innerText;No.innerText=`✅ Shared!`,setTimeout(()=>No.innerText=n,2e3)}else{await navigator.clipboard.writeText(r);let e=No.innerText;No.innerText=`✅ Copied Link!`,setTimeout(()=>No.innerText=e,2e3)}}catch(e){if(e.name!==`AbortError`)try{await navigator.clipboard.writeText(r),No.innerText=`✅ Copied Link!`,setTimeout(()=>No.innerText=`🔗 Share`,2e3)}catch{alert(`Failed to share or copy link.`)}}};var xs=document.getElementById(`drag-overlay`);window.addEventListener(`dragenter`,e=>{e.preventDefault(),xs.classList.add(`active`)}),window.addEventListener(`dragover`,e=>{e.preventDefault(),xs.classList.add(`active`)}),xs.addEventListener(`dragleave`,e=>{e.preventDefault(),xs.classList.remove(`active`)}),window.addEventListener(`drop`,async e=>{if(e.preventDefault(),xs.classList.remove(`active`),e.dataTransfer.files&&e.dataTransfer.files.length>0){let t=e.dataTransfer.files[0],n=t.name.toLowerCase();if(n.endsWith(`.hdr`)){let e=URL.createObjectURL(t);new Co().load(e,e=>{e.mapping=303,W.environment=e,Ps=e,J&&J.checked?(W.background=e,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),q!==void 0&&(q.setScene(W,G),q.updateCamera())},void 0,e=>{console.warn(`Error loading HDR:`,e),alert(`Failed to load HDR file.`)})}else if(n.endsWith(`.scad`)||!t.type||t.type.includes(`text`))try{let e=await t.text();z.value=e,Ds(),ms(e)}catch(e){alert(`Failed to read file: `+e.message)}else alert(`Please drop a valid .scad or .hdr file.`)}});var Ss=null,Cs=``;async function ws(e){let t=await fetch(`${e}/api/scads`);if(!t.ok)throw Error(`Failed to fetch files`);return(await t.json()).files}function Ts(){Bo.innerHTML=`<option value="">-- Create New Model --</option>`,Array.isArray(Ss)&&Ss.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e,Bo.appendChild(n)}),Bo.value=Zo,Ho&&(Ho.style.display=Zo===``?`none`:`flex`)}function Es(){let e=Do.value.trim();return e&&=e.replace(/\.scad$/i,``).split(/[/\\]/).pop()+`.scad`,{filename:e}}function Ds(){if(Ss===null)return;let e=Bo.value===``,{filename:t}=Es(),n=z.value,r=!1;e?(t&&t!==`.scad`||n.trim()!==``)&&(r=!0):n!==cs.content&&(r=!0),r?(Vo.style.display=`flex`,Vo.innerText=`Save`):Vo.style.display=`none`}async function Os(e,t=!1){if(!e)return!1;try{return Ro.innerText=`Connecting...`,Ss=await ws(e),Cs=e,ss=!0,Ro.innerText=`Connected`,zo.classList.add(`active`),z.placeholder=`Enter OpenSCAD code here...`,Zo=``,Ts(),cs={isNew:!0,content:``},Ds(),!0}catch(e){return t?console.warn(`Auto-connect failed:`,e.message):alert(`Connection failed: `+e.message),Ro.innerText=`Connect`,zo.classList.remove(`active`),ss=!1,z.placeholder=wo,!1}}Ro.onclick=async()=>{await Os(Lo.value.trim(),!1)&&ms(ds())},Bo.addEventListener(`change`,async()=>{if(Vo.style.display!==`none`&&!confirm(`You have unsaved changes. Are you sure you want to discard them and load another model?`)){Bo.value=Zo;return}let e=Bo.value;if(Zo=e,Ho&&(Ho.style.display=e===``?`none`:`flex`),e===``)Do.value=``,z.value=``,ms(ds()),cs={isNew:!0,content:``},Ds();else{let t=Ss[e];Do.value=t.replace(/\.scad$/i,``);try{let e=await fetch(`${Cs}/api/scads/${encodeURIComponent(t)}`);if(!e.ok){let t=await e.json();throw Error(t.error||`Failed to load model`)}let n=await e.json();z.value=n.content,ms(ds()),cs={isNew:!1,content:n.content},Ds()}catch(e){alert(`Error loading model: `+e.message)}}}),Vo.onclick=async()=>{let{filename:e}=Es();if(!e||e===`.scad`){let e=Vo.innerText;Vo.innerText=`⚠️ Name Required!`,Vo.style.borderColor=`#ff4444`,Do.focus(),setTimeout(()=>{Vo.innerText=e,Vo.style.borderColor=``},2e3);return}if(Bo.value===``&&Ss&&Ss.includes(e)&&!confirm(`A model named "${e}" already exists. Are you sure you want to overwrite it?`))return;let t={filename:e,content:z.value};try{Vo.innerText=`Saving...`;let n=await fetch(`${Cs}/api/scads`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok){let e=await n.json();throw Error(e.error||`Save failed`)}Ss=await ws(Cs);let r=Ss.findIndex(t=>t===e);Zo=r>=0?r.toString():``,Ts(),cs={isNew:!1,content:z.value},Ds()}catch(e){Ds(),alert(`Error: `+e.message)}},Ho.onclick=async()=>{let e=Bo.value;if(e===``)return;let t=Ss[e];if(confirm(`Are you sure you want to delete "${t}"?`))try{Ho.innerText=`Deleting...`;let e=await fetch(`${Cs}/api/scads/${encodeURIComponent(t)}`,{method:`DELETE`});if(!e.ok){let t=`Delete failed`;try{let n=await e.json();n.error&&(t=n.error)}catch{}throw Error(t)}Ss=await ws(Cs),Zo=``,Ts(),Do.value=``,z.value=``,ms(ds()),cs={isNew:!0,content:``},Ds(),Ho.innerText=`Delete`}catch(e){alert(`Error: `+e.message),Ho.innerText=`Delete`}};var W=new le;W.background=new ze(2236962);var G=new c(60,B.clientWidth/B.clientHeight,.1,2e3);G.position.set(50,50,-50);var K=new Fe({antialias:!0,preserveDrawingBuffer:!0});K.setSize(B.clientWidth,B.clientHeight),K.setPixelRatio(Math.min(window.devicePixelRatio,2)),K.toneMapping=4,K.toneMappingExposure=1,K.shadowMap.enabled=!0,K.shadowMap.type=2,B.appendChild(K.domElement);var ks=new he(K),As=new ke(W,G);ks.addPass(As);var js=new Se(new E(B.clientWidth,B.clientHeight),.1,.1,2);ks.addPass(js);var Ms=new Ne;ks.addPass(Ms);var q=new So(K);q.bounces=10,q.transmissiveBounces=10,q.multipleImportanceSampling=!0,q.filterGlossyFactor=.2;var Ns=new je(G,K.domElement);Ns.addEventListener(`change`,()=>{q!==void 0&&q.updateCamera()});var Ps=null;new Co().load(`./aristea_wreck_puresky_2k.hdr`,e=>{e.mapping=303,W.environment=e,Ps=e;let t=J&&J.checked,n=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!t),Y!==void 0&&(Y.visible=!t),Q!==void 0&&(Q.visible=!t&&n),$!==void 0&&($.visible=!t&&n),t?(W.background=e,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),q!==void 0&&(q.setScene(W,G),q.updateCamera())},void 0,e=>console.warn(`Error loading HDR:`,e));var J=document.getElementById(`path-tracing-cb`);J&&J.addEventListener(`change`,()=>{let e=J.checked,t=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!e),Y!==void 0&&(Y.visible=!e),Q!==void 0&&(Q.visible=!e&&t),$!==void 0&&($.visible=!e&&t),e?Ps&&(W.background=Ps,W.backgroundBlurriness=.5,W.backgroundIntensity=.3):(W.background=new ze(2236962),W.backgroundBlurriness=0,W.backgroundIntensity=1),e&&q!==void 0&&q.setScene(W,G)}),Ns.enableDamping=!0,Ns.dampingFactor=.1,Ns.maxDistance=2e3,W.environment=new ye(K).fromScene(new ne,.04).texture,W.environmentIntensity=.8;var Y=new h;W.add(Y);var X=new Le(16777215,1.5);X.castShadow=!0,X.shadow.mapSize.width=2048,X.shadow.mapSize.height=2048,X.shadow.bias=-5e-4,Y.add(X),Y.add(X.target);var Fs=new t(16777215,4473924,.6);Fs.position.set(0,20,0),Y.add(Fs);var Z=new v(new C(2e3,2e3),new n({color:2236962,roughness:.8,metalness:.1}));Z.rotation.x=-Math.PI/2,Z.receiveShadow=!0,W.add(Z);var Q=new i(2e3,100,5592405,3355443);Q.material.transparent=!0,Q.material.opacity=.5,W.add(Q);var $=new ue(100);W.add($),Yo&&Yo.addEventListener(`change`,()=>{let e=Yo.checked;V&&V.traverse(t=>{t.isMesh&&t.material&&(Array.isArray(t.material)?t.material.forEach(t=>{t.wireframe=e}):t.material.wireframe=e)})}),Xo&&Io&&(Xo.addEventListener(`click`,()=>{document.fullscreenElement?document.exitFullscreen():Io.requestFullscreen().catch(e=>{console.warn(`Error attempting to enable fullscreen: ${e.message}`)})}),document.addEventListener(`fullscreenchange`,()=>{setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100)}));function Is(){U&&(ls=!1,U.paused=!0,Wo.innerText=`▶ Play`,_s())}function Ls(e){if(!H||!$o[e])return;U&&U.stop();let t=$o[e];U=H.clipAction(t),U.play(),U.time=0,H.update(0),ls=!0,U.paused=!1,Wo.innerText=`⏸ Pause`,Ko.value=0,_s()}Go.addEventListener(`change`,e=>{Ls(parseInt(e.target.value))}),Wo.addEventListener(`click`,()=>{U&&(ls=!ls,U.paused=!ls,Wo.innerText=ls?`⏸ Pause`:`▶ Play`,_s())}),Ko.addEventListener(`mousedown`,()=>{us=!0,Is()}),Ko.addEventListener(`mouseup`,()=>{us=!1}),Ko.addEventListener(`touchstart`,()=>{us=!0,Is()},{passive:!0}),Ko.addEventListener(`touchend`,()=>{us=!1},{passive:!0}),Ko.addEventListener(`input`,e=>{if(Is(),U){let t=U.getClip().duration;U.time=parseFloat(e.target.value)*t,H&&(H.update(0),J&&J.checked&&q!==void 0&&(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G)))}});function Rs(){rs&&is&&is.state!==`inactive`&&(is.stop(),rs=!1),V&&=(H&&=(H.stopAllAction(),H.uncacheRoot(H.getRoot()),null),U=null,W.remove(V),V.traverse(e=>{e.isMesh&&(e.geometry.dispose(),Array.isArray(e.material)?e.material.forEach(e=>e.dispose()):e.material&&e.material.dispose())}),null),_s()}function zs(e,t){if(!e)return new Ee;e.updateMatrixWorld(!0);let n=new Ee().setFromObject(e);if(!t||t.length===0)return n;let r=new Map;e.traverse(e=>{r.set(e,{position:e.position.clone(),quaternion:e.quaternion.clone(),scale:e.scale.clone()})});let i=new se(e);for(let r of t){let t=i.clipAction(r);t.play();let a=new Set([0,r.duration]);if(r.tracks){for(let e of r.tracks)if(e.times)for(let t=0;t<e.times.length;t++)a.add(e.times[t])}if(r.duration>0)for(let e=0;e<=20;e++)a.add(e/20*r.duration);let o=Array.from(a).sort((e,t)=>e-t);if(o.length>60){let e=[],t=(o.length-1)/59;for(let n=0;n<60;n++)e.push(o[Math.round(n*t)]);o=e}for(let r of o)t.time=r,i.update(0),e.updateMatrixWorld(!0),n.expandByObject(e);t.stop()}return i.stopAllAction(),i.uncacheRoot(e),r.forEach((e,t)=>{t.position.copy(e.position),t.quaternion.copy(e.quaternion),t.scale.copy(e.scale)}),e.updateMatrixWorld(!0),n}function Bs(e){return new Promise((t,n)=>{let r=null;V&&(H&&U&&(U.time=0,H.update(0),J&&J.checked&&q!==void 0&&(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q.setScene(W,G))),r=zs(V,$o)),Rs();let i=e;e instanceof Uint8Array&&(i=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)),new we().parse(i,``,e=>{V=e.scene,$o=e.animations||[];let n=Yo?Yo.checked:!1;$o.length?(H=new se(V),Uo.style.display=`flex`,Go.innerHTML=``,$o.forEach((e,t)=>{let n=document.createElement(`option`);n.value=t,n.innerText=e.name||`Animation ${t+1}`,Go.appendChild(n)}),Ls(0)):(Uo.style.display=`none`,U=null,_s()),V.traverse(e=>{e.isMesh&&(e.castShadow=!0,e.receiveShadow=!0,e.frustumCulled=!1,e.material&&(Array.isArray(e.material)?e.material.forEach(e=>{e.wireframe=n}):e.material.wireframe=n))}),W.add(V);let i=!0;if(r&&!r.isEmpty()){let e=zs(V,$o);if(!e.isEmpty()){let t=.001;r.min.distanceTo(e.min)<t&&r.max.distanceTo(e.max)<t&&(i=!1)}}if(i)typeof Vs==`function`&&Vs();else{let e=J&&J.checked,t=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!e),Y!==void 0&&(Y.visible=!e),Q!==void 0&&(Q.visible=!e&&t),$!==void 0&&($.visible=!e&&t),q!==void 0&&q.setScene(W,G)}t()},n)})}function Vs(){if(!V)return;let e=zs(V,$o);if(e.isEmpty())return;let t=e.getCenter(new D),n=e.getSize(new D),r=Math.max(n.x,n.y,n.z)||10;Z.position.y=e.min.y-.01,Q.position.y=Z.position.y+.001,$.position.y=Z.position.y+.002;let i=G.fov*(Math.PI/180),o=r/(2*Math.tan(i/2));G.aspect<1&&(o/=G.aspect),o*=1.5,G.near=Math.max(.01,r*.01),G.far=Math.max(2e3,o*10),G.updateProjectionMatrix(),Ns.maxDistance=G.far,G.position.set(t.x+o*.8,t.y+o*.8,t.z-o*.8),G.lookAt(t),Ns.target.copy(t),Ns.update(),X.position.set(t.x+r,t.y+r*1.5,t.z-r),X.target.position.copy(t),X.target.updateMatrixWorld();let s=r*1.5;X.shadow.camera.left=-s,X.shadow.camera.right=s,X.shadow.camera.top=s,X.shadow.camera.bottom=-s,X.shadow.camera.near=.1,X.shadow.camera.far=r*5,X.shadow.camera.updateProjectionMatrix(),W.fog=new a(2236962,o*1.5,o*5);let c=J&&J.checked,l=!Jo||Jo.checked;Z!==void 0&&(Z.visible=!c),Y!==void 0&&(Y.visible=!c),Q!==void 0&&(Q.visible=!c&&l),$!==void 0&&($.visible=!c&&l),q!==void 0&&q.setScene(W,G)}var Hs=performance.now();function Us(){requestAnimationFrame(Us);let e=performance.now(),t=(e-Hs)/1e3;if(Hs=e,H&&(!(J&&J.checked)&&!rs&&H.update(t),U&&ls&&!us)){let e=U.getClip().duration;if(e>0){let t=U.time%e;t<0&&(t+=e),Ko.value=t/e}}Ns.update();let n=J&&J.checked,r=!Jo||Jo.checked;n?(Z!==void 0&&(Z.visible=!1),Y!==void 0&&(Y.visible=!1),Q!==void 0&&(Q.visible=!1),$!==void 0&&($.visible=!1),q!==void 0&&!rs&&q.renderSample()):(Z!==void 0&&(Z.visible=!0),Y!==void 0&&(Y.visible=!0),Q!==void 0&&(Q.visible=r),$!==void 0&&($.visible=r),rs||ks.render()),ns&&(ns=!1,K.domElement.toBlob(e=>{e&&gs(e,`${fs()}.png`)},`image/png`))}Us(),window.addEventListener(`resize`,()=>{if(!B||rs)return;let e=B.clientWidth,t=B.clientHeight;G.aspect=e/t,G.updateProjectionMatrix(),K.setSize(e,t),ks.setSize(e,t)}),setTimeout(()=>window.dispatchEvent(new Event(`resize`)),100);var Ws=[`localhost`,`127.0.0.1`,``].includes(window.location.hostname);z.placeholder=Ws?`Enter OpenSCAD code here...`:wo,(async function(){if(Ws&&await Os(Lo.value.trim(),!0),window.location.hash&&window.location.hash.length>1)try{let e=await bs(window.location.hash.substring(1));e&&(z.value=e)}catch(e){console.error(`Failed to decode hash`,e)}Ds(),setTimeout(()=>ms(ds()),500)})();