avbd-babylon 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -8
- package/dist/avbd.global.js +274 -188
- package/dist/avbd.js +273 -187
- package/dist/nativeScenes.d.ts +4 -2
- package/docs/BABYLON_API.md +18 -2
- package/package.json +2 -1
- package/tests/README.md +16 -2
package/dist/avbd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var S=(s=0,e=0,t=0)=>Float64Array.of(s,e,t),jt=(s=0,e=0,t=0,i=1)=>Float64Array.of(s,e,t,i),oe=()=>new Float64Array(9);var Ps=s=>s<0?-1:s>0?1:0,Bt=(s,e)=>s<e?s:e,Cu=(s,e)=>s>e?s:e,ce=(s,e,t)=>Cu(e,Bt(t,s)),V=(s,e)=>s[0]*e[0]+s[1]*e[1]+s[2]*e[2],Ne=s=>V(s,s),rr=s=>Math.sqrt(Ne(s));function lt(s,e,t,i){return s[0]=e,s[1]=t,s[2]=i,s}var Li=(s,e,t)=>lt(s,e[0]+t[0],e[1]+t[1],e[2]+t[2]),J=(s,e,t)=>lt(s,e[0]-t[0],e[1]-t[1],e[2]-t[2]),ne=(s,e,t)=>lt(s,e[0]*t,e[1]*t,e[2]*t),_t=(s,e,t)=>lt(s,e[0]/t,e[1]/t,e[2]/t),ee=(s,e)=>lt(s,-e[0],-e[1],-e[2]),vn=(s,e)=>lt(s,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),D=(s,e,t,i)=>lt(s,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),Su=(s,e)=>_t(s,e,rr(e)),Dt=(s,e,t)=>lt(s,e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]);function ir(s,e,t,i,r){return s[0]=e,s[1]=t,s[2]=i,s[3]=r,s}function Gr(s,e,t){let i=e[0],r=e[1],o=e[2],n=e[3],a=t[0],l=t[1],c=t[2],u=t[3];return ir(s,n*a+i*u+r*c-o*l,n*l-i*c+r*u+o*a,n*c+i*l-r*a+o*u,n*u-i*a-r*l-o*c)}var ku=(s,e)=>ir(s,-e[0],-e[1],-e[2],e[3]),Xa=s=>Ne(s)+s[3]*s[3];function xn(s,e){let t=Xa(e);return ir(s,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function Pu(s,e){let t=Math.sqrt(Xa(e));return ir(s,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var tr=new Float64Array(4),Pi=new Float64Array(4);function ct(s,e,t){return Gr(tr,e,xn(tr,t)),lt(s,tr[0]*2,tr[1]*2,tr[2]*2)}function si(s,e,t){return ir(Pi,t[0],t[1],t[2],0),Gr(Pi,Pi,e),Pu(s,ir(s,e[0]+Pi[0]*.5,e[1]+Pi[1]*.5,e[2]+Pi[2]*.5,e[3]+Pi[3]*.5))}function ae(s,e,t){let i=e[0],r=e[1],o=e[2],n=e[3],a=(r*t[2]-o*t[1])*2,l=(o*t[0]-i*t[2])*2,c=(i*t[1]-r*t[0])*2;return lt(s,t[0]+a*n+(r*c-o*l),t[1]+l*n+(o*a-i*c),t[2]+c*n+(i*l-r*a))}var Ja=new Float64Array(3);function Gt(s,e,t,i){return ae(Ja,t,i),Li(s,Ja,e)}function Te(s,e,t){return ae(s,ku(tr,e),t)}function Ce(s,e,t,i){return s.fill(0),s[0]=e,s[4]=t,s[8]=i,s}function An(s,e){return s[0]=0,s[1]=-e[2],s[2]=e[1],s[3]=e[2],s[4]=0,s[5]=-e[0],s[6]=-e[1],s[7]=e[0],s[8]=0,s}function _i(s,e){let t=e[1],i=e[2],r=e[5];return s[0]=e[0],s[1]=e[3],s[2]=e[6],s[3]=t,s[4]=e[4],s[5]=e[7],s[6]=i,s[7]=r,s[8]=e[8],s}function Ke(s,e,t){let i=e[0],r=e[1],o=e[2],n=e[3],a=e[4],l=e[5],c=e[6],u=e[7],f=e[8],d=t[0],p=t[1],m=t[2],h=t[3],g=t[4],b=t[5],y=t[6],A=t[7],v=t[8];return s[0]=i*d+r*h+o*y,s[1]=i*p+r*g+o*A,s[2]=i*m+r*b+o*v,s[3]=n*d+a*h+l*y,s[4]=n*p+a*g+l*A,s[5]=n*m+a*b+l*v,s[6]=c*d+u*h+f*y,s[7]=c*p+u*g+f*A,s[8]=c*m+u*b+f*v,s}function ue(s,e,t){let i=t[0],r=t[1],o=t[2];return lt(s,e[0]*i+e[1]*r+e[2]*o,e[3]*i+e[4]*r+e[5]*o,e[6]*i+e[7]*r+e[8]*o)}function qe(s,e){for(let t=0;t<9;t++)s[t]+=e[t];return s}function Ei(s,e,t){for(let i=0;i<9;i++)s[i]=e[i]*t;return s}function Ya(s,e){for(let t=0;t<9;t++)s[t]=-e[t];return s}function Ls(s,e,t){for(let i=0;i<3;i++)for(let r=0;r<3;r++)s[i*3+r]=t[r]*e[i];return s}function Qa(s,e){let t=Math.sqrt(e[0]*e[0]+e[3]*e[3]+e[6]*e[6]),i=Math.sqrt(e[1]*e[1]+e[4]*e[4]+e[7]*e[7]),r=Math.sqrt(e[2]*e[2]+e[5]*e[5]+e[8]*e[8]);return Ce(s,t,i,r)}function _s(s,e){let t=Math.abs(e[0])>Math.abs(e[2])?S(-e[1],e[0],0):S(0,-e[2],e[1]);Su(t,t);let i=Dt(S(),e,t);return s[0]=e[0],s[1]=e[1],s[2]=e[2],s.set(t,3),s.set(i,6),s}function Es(s,e,t,i,r,o,n){let a=s[0],l=s[3],c=s[4],u=s[6],f=s[7],d=s[8],p=t[0],m=t[1],h=t[2],g=e[0],b=t[3],y=t[4],A=t[5],v=e[3],x=e[4],w=t[6],B=t[7],L=t[8],M=e[6],I=e[7],E=e[8],k=l/a,P=u/a,C=p/a,_=b/a,z=w/a,R=a,G=c-k*k*R,X=(f-k*P*R)/G,W=(m-k*C*R)/G,U=(y-k*_*R)/G,H=(B-k*z*R)/G,Z=d-(P*P*R+X*X*G),he=(h-P*C*R-X*W*G)/Z,we=(A-P*_*R-X*U*G)/Z,Oe=(L-P*z*R-X*H*G)/Z,$=g-(C*C*R+W*W*G+he*he*Z),Je=(v-C*_*R-W*U*G-he*we*Z)/$,Xe=(M-C*z*R-W*H*G-he*Oe*Z)/$,Be=x-(_*_*R+U*U*G+we*we*Z+Je*Je*$),ot=(I-_*z*R-U*H*G-we*Oe*Z-Je*Xe*$)/Be,ri=E-(z*z*R+H*H*G+Oe*Oe*Z+Xe*Xe*$+ot*ot*Be),Ye=i[0],nt=i[1]-k*Ye,Qe=i[2]-P*Ye-X*nt,at=r[0]-C*Ye-W*nt-he*Qe,wt=r[1]-_*Ye-U*nt-we*Qe-Je*at,Qi=r[2]-z*Ye-H*nt-Oe*Qe-Xe*at-ot*wt,Ki=Ye/R,Zi=nt/G,er=Qe/Z,Wt=at/$,wu=wt/Be,Bu=Qi/ri;n[2]=Bu,n[1]=wu-ot*n[2],n[0]=Wt-Je*n[1]-Xe*n[2],o[2]=er-he*n[0]-we*n[1]-Oe*n[2],o[1]=Zi-X*o[2]-W*n[0]-U*n[1]-H*n[2],o[0]=Ki-k*o[1]-P*o[2]-C*n[0]-_*n[1]-z*n[2]}var Et=1,Ve=1e10,Ms=.01,Ka=1e-5,Ze=class{solver;forces=[];positionLin;positionAng=jt();initialLin=S();initialAng=jt();inertialLin=S();inertialAng=jt();velocityLin;velocityAng=S();prevVelocityLin;size;mass;moment;friction;radius;constructor(e,t,i,r,o,n=[0,0,0]){this.solver=e,this.size=S(t[0],t[1],t[2]),this.friction=r,this.positionLin=S(o[0],o[1],o[2]),this.velocityLin=S(n[0],n[1],n[2]),this.prevVelocityLin=S(n[0],n[1],n[2]);let[a,l,c]=this.size;this.mass=a*l*c*i,this.moment=S((l*l+c*c)/12*this.mass,(a*a+c*c)/12*this.mass,(a*a+l*l)/12*this.mass),this.radius=rr([a*.5,l*.5,c*.5]),e.bodies.push(this)}constrainedTo(e){for(let t of this.forces)if(t.bodyA===this&&t.bodyB===e||t.bodyA===e&&t.bodyB===this)return!0;return!1}},zs=()=>({lhsLin:oe(),lhsAng:oe(),lhsCross:oe(),rhsLin:S(),rhsAng:S()}),Ft=class{solver;bodyA;bodyB;constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}};var rl=8,Za=16,zi=1e-6,Bn=1e-5,Lu=1e-6,Cn=34028234663852886e22,Is=0,sl=1,Rs=2;function el(s){let e=s.positionAng;return{center:s.positionLin,half:ne(S(),s.size,.5),axis:[ae(S(),e,[1,0,0]),ae(S(),e,[0,1,0]),ae(S(),e,[0,0,1])]}}var Mi=(s,e)=>Math.abs(V(s,e));function Os(s,e){let t=V(e,s.axis[0])>=0?1:-1,i=V(e,s.axis[1])>=0?1:-1,r=V(e,s.axis[2])>=0?1:-1,o=D(S(),s.center,s.axis[0],s.half[0]*t);return D(o,o,s.axis[1],s.half[1]*i),D(o,o,s.axis[2],s.half[2]*r)}function ol(s,e){return e===0?{u:s.axis[1],v:s.axis[2],extentU:s.half[1],extentV:s.half[2]}:e===1?{u:s.axis[0],v:s.axis[2],extentU:s.half[0],extentV:s.half[2]}:{u:s.axis[0],v:s.axis[1],extentU:s.half[0],extentV:s.half[1]}}function _u(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]);return{normal:r,center:o,...ol(s,e)}}function Eu(s,e){let t=0,i=-Cn;for(let r=0;r<3;r++){let o=Mi(s.axis[r],e);o>i&&(i=o,t=r)}return t}function Mu(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]),{u:n,v:a,extentU:l,extentV:c}=ol(s,e),u=(f,d)=>{let p=D(S(),o,n,f*l);return D(p,p,a,d*c)};return[u(1,1),u(-1,1),u(-1,-1),u(1,-1)]}function zu(s,e,t){let i=[];if(s.length===0)return i;let r=s[s.length-1],o=V(e,r)-t;for(let n of s){let a=V(e,n)-t,l=o<=Bn,c=a<=Bn;if(l!==c){let u=0,f=o-a;Math.abs(f)>zi&&(u=ce(o/f,0,1)),i.length<Za&&i.push(D(S(),r,J(S(),n,r),u))}c&&i.length<Za&&i.push(n),r=n,o=a}return i}var Ts=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(S(),Li(S(),e,t),.5);for(let a of this.midpoints)if(Ne(J(S(),r,a))<Lu)return;if(this.contacts.length>=rl)return;let{bodyA:o,bodyB:n}=this;this.contacts.push({feature:i,rA:Te(S(),o.positionAng,J(S(),e,o.positionLin)),rB:Te(S(),n.positionAng,J(S(),t,n.positionLin))}),this.midpoints.push(r)}};function wn(s,e,t,i,r,o,n,a){let l=Ne(i);if(l<zi)return!0;let c=1/Math.sqrt(l),u=ne(S(),i,c);V(u,t)<0&&ee(u,u);let f=Math.abs(V(t,u)),d=s.half[0]*Mi(u,s.axis[0])+s.half[1]*Mi(u,s.axis[1])+s.half[2]*Mi(u,s.axis[2]),p=e.half[0]*Mi(u,e.axis[0])+e.half[1]*Mi(u,e.axis[1])+e.half[2]*Mi(u,e.axis[2]),m=f-(d+p);return m>0?!1:((!a.valid||m>a.separation)&&(a.valid=!0,a.type=r,a.indexA=o,a.indexB=n,a.separation=m,a.normalAB=u),!0)}function tl(s,e,t){let i=(e+1)%3,r=(e+2)%3,o=V(t,s.axis[i])>=0?1:-1,n=V(t,s.axis[r])>=0?1:-1,a=D(S(),s.center,s.axis[i],s.half[i]*o);D(a,a,s.axis[r],s.half[r]*n);let l=ne(S(),s.axis[e],s.half[e]);return[J(S(),a,l),Li(S(),a,l)]}function Iu(s,e,t,i){let r=J(S(),e,s),o=J(S(),i,t),n=J(S(),s,t),a=V(r,r),l=V(o,o),c=V(o,n),u=0,f=0;if(a<=zi&&l<=zi)return[s,t];if(a<=zi)f=ce(c/l,0,1);else{let d=V(r,n);if(l<=zi)u=ce(-d/a,0,1);else{let p=V(r,o),m=a*l-p*p;Math.abs(m)>zi&&(u=ce((p*c-d*l)/m,0,1)),f=(p*u+c)/l,f<0?(f=0,u=ce(-d/a,0,1)):f>1&&(f=1,u=ce((p-d)/a,0,1))}}return[D(S(),s,r,u),D(S(),t,o,f)]}function il(s,e,t,i,r,o,n){let a=r?t:i,l=r?i:t,c=r?n:ee(S(),n),u=_u(a,o,c),f=Eu(l,u.normal),d=Mu(l,f,u.normal),p=[[u.u,u.extentU],[ee(S(),u.u),u.extentU],[u.v,u.extentV],[ee(S(),u.v),u.extentV]];for(let[g,b]of p)if(d=zu(d,g,V(g,u.center)+b),!d.length)return[];let m=new Ts(s,e),h=(r?Is:sl)<<24|(o&255)<<16|(f&255)<<8;for(let g=0;g<d.length&&m.contacts.length<rl;g++){let b=d[g],y=V(J(S(),b,u.center),u.normal);if(y>Bn)continue;let A=D(S(),b,u.normal,-y),v=r?A:b,x=r?b:A;m.add(v,x,h|g&255)}return m.contacts.length||m.add(Os(t,n),Os(i,ee(S(),n)),h),m.contacts}function Ru(s,e,t,i,r,o,n){let[a,l]=tl(t,r,n),[c,u]=tl(i,o,ee(S(),n)),[f,d]=Iu(a,l,c,u),p=new Ts(s,e),m=Rs<<24|(r&255)<<8|o&255;return p.add(f,d,m),p.contacts.length||p.add(Os(t,n),Os(i,ee(S(),n)),m),p.contacts}function nl(s,e,t){let i=el(s),r=el(e),o=J(S(),r.center,i.center),n={type:Is,indexA:0,indexB:0,separation:-Cn,normalAB:S(),valid:!1},a={type:Rs,indexA:0,indexB:0,separation:-Cn,normalAB:S(),valid:!1};for(let u=0;u<3;u++)if(!wn(i,r,o,i.axis[u],Is,u,-1,n))return[];for(let u=0;u<3;u++)if(!wn(i,r,o,r.axis[u],sl,-1,u,n))return[];let l=S();for(let u=0;u<3;u++)for(let f=0;f<3;f++)if(Dt(l,i.axis[u],r.axis[f]),!wn(i,r,o,l,Rs,u,f,a))return[];if(!n.valid)return[];let c=n;return a.valid&&.95*a.separation>n.separation+.01&&(c=a),_s(t,ee(S(),c.normalAB)),c.type===Rs?Ru(s,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===Is?il(s,e,i,r,!0,c.indexA,c.normalAB):il(s,e,i,r,!1,c.indexB,c.normalAB)}var al=S(),ll=S(),cl=S(),ul=S(),fl=S(),dl=S(),js=oe(),Sn=oe(),kn=oe(),Ds=oe(),Pn=oe(),Ln=oe(),_n=oe(),Gs=oe(),Me=S(),je=S(),Ii=S(),oi=S(),hl=S(),pl=S(),sr=0,Fr=0;function ml(s,e,t){for(let i=0;i<3;i++)oi[0]=t[i*3],oi[1]=t[i*3+1],oi[2]=t[i*3+2],Dt(oi,e,oi),s[i*3]=oi[0],s[i*3+1]=oi[1],s[i*3+2]=oi[2];return s}var ni=class extends Ft{contacts=[];basis=oe();friction=0;constructor(e,t,i){super(e,t,i)}get numContacts(){return this.contacts.length}initialize(){let e=this.bodyA,t=this.bodyB,{solver:i}=this;this.friction=Math.sqrt(e.friction*t.friction);let r=this.contacts;this.contacts=nl(e,t,this.basis).map(o=>{let n=r.find(a=>a.feature===o.feature);return n?{feature:o.feature,rA:n.stick?n.rA:o.rA,rB:n.stick?n.rB:o.rB,C0:S(),penalty:n.penalty.slice(),lambda:n.lambda.slice(),stick:n.stick}:{feature:o.feature,rA:o.rA,rB:o.rB,C0:S(),penalty:S(),lambda:S(),stick:!1}});for(let o of this.contacts){Gt(hl,e.positionLin,e.positionAng,o.rA),Gt(pl,t.positionLin,t.positionAng,o.rB),ue(o.C0,this.basis,J(Ii,hl,pl)),o.C0[0]+=Ms;for(let n=0;n<3;n++)o.lambda[n]=o.lambda[n]*i.alpha*i.gamma,o.penalty[n]=ce(o.penalty[n]*i.gamma,Et,Ve)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,o=this.basis;ae(fl,i.positionAng,e.rA),ae(dl,r.positionAng,e.rB),Ya(js,o),ml(Sn,fl,o),ml(kn,dl,js),Ce(Ds,e.penalty[0],e.penalty[1],e.penalty[2]),ne(Me,e.C0,1-t),D(Me,Me,ue(Ii,o,al),1),D(Me,Me,ue(Ii,js,cl),1),D(Me,Me,ue(Ii,Sn,ll),1),D(Me,Me,ue(Ii,kn,ul),1),ue(je,Ds,Me),D(je,je,e.lambda,1),je[0]=Bt(je[0],0),Fr=Math.abs(je[0])*this.friction,sr=Math.sqrt(je[1]*je[1]+je[2]*je[2]),sr>Fr&&sr>0&&(je[1]*=Fr/sr,je[2]*=Fr/sr)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;J(al,e.positionLin,e.initialLin),ct(ll,e.positionAng,e.initialAng),J(cl,t.positionLin,t.initialLin),ct(ul,t.positionAng,t.initialAng)}updatePrimal(e,t,i){this.computeDisplacements();let r=e===this.bodyA;for(let o of this.contacts){this.evaluate(o,t);let n=r?this.basis:js,a=r?Sn:kn;_i(Pn,n),_i(Ln,a),Ke(_n,Ln,Ds),qe(i.lhsLin,Ke(Gs,Ke(Gs,Pn,Ds),n)),qe(i.lhsAng,Ke(Gs,_n,a)),qe(i.lhsCross,Ke(Gs,_n,n)),D(i.rhsLin,i.rhsLin,ue(Ii,Pn,je),1),D(i.rhsAng,i.rhsAng,ue(Ii,Ln,je),1)}}updateDual(e){let{solver:t}=this;this.computeDisplacements();for(let i of this.contacts)this.evaluate(i,e),i.lambda.set(je),je[0]<0&&(i.penalty[0]=Bt(i.penalty[0]+t.betaLin*Math.abs(Me[0]),Ve)),sr<=Fr&&(i.penalty[1]=Bt(i.penalty[1]+t.betaLin*Math.abs(Me[1]),Ve),i.penalty[2]=Bt(i.penalty[2]+t.betaLin*Math.abs(Me[2]),Ve),i.stick=Math.sqrt(Me[1]*Me[1]+Me[2]*Me[2])<Ka)}};var En=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),ai=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,En())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=S(),n=S();for(let a=this.bodies.length-1;a>=0;a--){let l=this.bodies[a];if(l.mass<=0)continue;Te(o,l.positionAng,J(o,e,l.positionLin)),Te(n,l.positionAng,t);let c=0,u=1/0,f=!0;for(let p=0;p<3&&f;p++){let m=l.size[p]*.5;if(Math.abs(n[p])<1e-6){(o[p]<-m||o[p]>m)&&(f=!1);continue}let h=1/n[p],g=(-m-o[p])*h,b=(m-o[p])*h;g>b&&([g,b]=[b,g]),c=Math.max(c,g),u=Math.min(u,b),c>u&&(f=!1)}if(!f)continue;let d=c>=0?c:u;d<0||(!r||d<r.t)&&(r={body:l,local:D(S(),o,n,d),t:d})}return r}sys=zs();dxLin=S();dxAng=S();dp=S();accel=S();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:o}=this;for(let a=i.length-1;a>=0;a--){let l=i[a];for(let c=a-1;c>=0;c--){let u=i[c];J(o,l.positionLin,u.positionLin);let f=l.radius+u.radius;V(o,o)<=f*f&&!l.constrainedTo(u)&&new ni(this,l,u)}}let n=[];for(let a of this.forces)a.initialize()?n.push(a):a.unlinkFromBodies();this.forces=n;for(let a=i.length-1;a>=0;a--){let l=i[a];D(l.inertialLin,l.positionLin,l.velocityLin,e),l.mass>0&&(l.inertialLin[2]+=t*(e*e)),si(l.inertialAng,l.positionAng,gl(l.velocityAng,e)),_t(this.accel,J(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*Ps(t),u=ce(c/Math.abs(t),0,1);Number.isFinite(u)||(u=0),l.initialLin.set(l.positionLin),l.initialAng.set(l.positionAng),l.mass>0&&(D(l.positionLin,l.positionLin,l.velocityLin,e),l.positionLin[2]+=t*(u*e*e),si(l.positionAng,l.positionAng,gl(l.velocityAng,e)))}for(let a=0;a<this.iterations;a++){for(let l=i.length-1;l>=0;l--){let c=i[l];if(c.mass<=0)continue;let u=c.mass,f=c.moment;Ce(r.lhsLin,u/(e*e),u/(e*e),u/(e*e)),Ce(r.lhsAng,f[0]/(e*e),f[1]/(e*e),f[2]/(e*e)),r.lhsCross.fill(0),ue(r.rhsLin,r.lhsLin,J(o,c.positionLin,c.inertialLin)),ue(r.rhsAng,r.lhsAng,ct(o,c.positionAng,c.inertialAng));let d=c.forces;for(let p=d.length-1;p>=0;p--)d[p].updatePrimal(c,this.alpha,r);Es(r.lhsLin,r.lhsAng,r.lhsCross,ee(r.rhsLin,r.rhsLin),ee(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),D(c.positionLin,c.positionLin,this.dxLin,1),si(c.positionAng,c.positionAng,this.dxAng)}for(let l of this.forces)l.updateDual(this.alpha)}for(let a of i)a.prevVelocityLin.set(a.velocityLin),a.mass>0&&(_t(a.velocityLin,J(a.velocityLin,a.positionLin,a.initialLin),e),_t(a.velocityAng,ct(a.velocityAng,a.positionAng,a.initialAng),e))}},Ou=S(),gl=(s,e)=>ne(Ou,s,e);var xe=(s,e)=>[s[0]-e[0],s[1]-e[1],s[2]-e[2]],Mt=(s,e)=>s[0]*e[0]+s[1]*e[1]+s[2]*e[2],Ri=(s,e)=>[s[1]*e[2]-s[2]*e[1],s[2]*e[0]-s[0]*e[2],s[0]*e[1]-s[1]*e[0]],et=s=>Math.sqrt(Mt(s,s));function Ur(s){let e=Mn(s);return!e||e.vertices.length/3<=32?e:Mn(bl(e,32))}function bl(s,e){let[t,i,r,o]=s.rotation,n=f=>{let d=[2*(i*f[2]-r*f[1]),2*(r*f[0]-t*f[2]),2*(t*f[1]-i*f[0])];return[f[0]+o*d[0]+(i*d[2]-r*d[1])+s.center[0],f[1]+o*d[1]+(r*d[0]-t*d[2])+s.center[1],f[2]+o*d[2]+(t*d[1]-i*d[0])+s.center[2]]},a=[];for(let f=0;f<s.vertices.length;f+=3)a.push(n([s.vertices[f],s.vertices[f+1],s.vertices[f+2]]));let l=0;a.forEach((f,d)=>{et(xe(f,s.center))>et(xe(a[l],s.center))&&(l=d)});let c=[l],u=a.map(f=>et(xe(f,a[l])));for(;c.length<e;){let f=0;u.forEach((d,p)=>{d>u[f]&&(f=p)}),c.push(f),a.forEach((d,p)=>{u[p]=Math.min(u[p],et(xe(d,a[f])))})}return c.flatMap(f=>a[f])}function Mn(s){let e=0;for(let v=0;v<s.length;v++)e=Math.max(e,Math.abs(s[v]));let t=Math.max(e,1e-9)*1e-6,i=new Set,r=[];for(let v=0;v+2<s.length;v+=3){let x=[s[v],s[v+1],s[v+2]],w=x.map(B=>Math.round(B/t)).join(",");i.has(w)||(i.add(w),r.push(x))}if(r.length<4)return null;let o=Math.max(e,1e-9)*1e-10,n=0;for(let v=1;v<r.length;v++)r[v][0]<r[n][0]&&(n=v);let a=-1,l=0;for(let v=0;v<r.length;v++){let x=et(xe(r[v],r[n]));x>l&&(l=x,a=v)}if(a<0||l<o)return null;let c=-1;l=0;let u=xe(r[a],r[n]);for(let v=0;v<r.length;v++){let x=et(Ri(u,xe(r[v],r[n])));x>l&&(l=x,c=v)}if(c<0||l<o*et(u))return null;let f=Ri(u,xe(r[c],r[n])),d=-1;l=0;for(let v=0;v<r.length;v++){let x=Math.abs(Mt(f,xe(r[v],r[n])));x>l&&(l=x,d=v)}if(d<0||l<o*et(f))return null;let p=[],m=(v,x,w)=>{let B=Ri(xe(r[x],r[v]),xe(r[w],r[v])),L=et(B)||1,M=[B[0]/L,B[1]/L,B[2]/L];return{v:[v,x,w],n:M,d:Mt(M,r[v])}},g=Mt(f,xe(r[d],r[n]))>0?[n,c,a]:[n,a,c];p.push(m(g[0],g[1],g[2])),p.push(m(g[0],d,g[1])),p.push(m(g[1],d,g[2])),p.push(m(g[2],d,g[0]));let b=new Set([n,a,c,d]),y=r.map((v,x)=>x).filter(v=>!b.has(v)),A=[n,a,c,d].reduce((v,x)=>[v[0]+r[x][0]/4,v[1]+r[x][1]/4,v[2]+r[x][2]/4],[0,0,0]);y.sort((v,x)=>et(xe(r[x],A))-et(xe(r[v],A)));for(let v of y){let x=p.filter(k=>Mt(k.n,r[v])-k.d>o);if(!x.length)continue;let w=new Map;for(let k of p)for(let P=0;P<3;P++)w.set(`${k.v[P]},${k.v[(P+1)%3]}`,k);let B=new Set(x),L=[...x];for(;L.length;){let k=L.pop();for(let P=0;P<3;P++){let C=w.get(`${k.v[(P+1)%3]},${k.v[P]}`);C&&!B.has(C)&&Mt(C.n,r[v])-C.d>-o&&(B.add(C),L.push(C))}}let M=[...B],I=new Set;for(let k of M)for(let P=0;P<3;P++)I.add(`${k.v[P]},${k.v[(P+1)%3]}`);let E=[];for(let k of M)for(let P=0;P<3;P++){let C=k.v[P],_=k.v[(P+1)%3];I.has(`${_},${C}`)||E.push([C,_])}for(let k of M)p.splice(p.indexOf(k),1);for(let[k,P]of E)p.push(m(k,P,v))}return yl(r.flat(),p.flatMap(v=>v.v))}function zn(s,e){let t=yl(s,e);return t&&t.vertices.length/3>32?Mn(bl(t,32)):t}function yl(s,e){let t=[];for(let C=0;C+2<s.length;C+=3)t.push([s[C],s[C+1],s[C+2]]);let i=0;for(let C of t)i=Math.max(i,Math.abs(C[0]),Math.abs(C[1]),Math.abs(C[2]));let r=Math.max(i,1e-9)*1e-5,o=[];for(let C=0;C+2<e.length;C+=3){let _=[e[C],e[C+1],e[C+2]],z=Ri(xe(t[_[1]],t[_[0]]),xe(t[_[2]],t[_[0]])),R=et(z);if(!(R>0))continue;let G=[z[0]/R,z[1]/R,z[2]/R];o.push({v:_,n:G,d:Mt(G,t[_[0]])})}let n=new Map;o.forEach((C,_)=>{for(let z=0;z<3;z++)n.set(`${C.v[z]},${C.v[(z+1)%3]}`,_)});let a=new Array(o.length).fill(-1),l=[];o.forEach((C,_)=>{if(a[_]>=0)return;let z=[_];a[_]=l.length;for(let R=0;R<z.length;R++){let G=o[z[R]];for(let X=0;X<3;X++){let W=n.get(`${G.v[(X+1)%3]},${G.v[X]}`);if(W===void 0||a[W]>=0)continue;let U=o[W];Mt(C.n,U.n)>1-1e-5&&U.v.every(H=>Math.abs(Mt(C.n,t[H])-C.d)<=r)&&(a[W]=l.length,z.push(W))}}l.push(z)});let c=[];for(let C of l){let _=new Set;for(let U of C)for(let H=0;H<3;H++)_.add(`${o[U].v[H]},${o[U].v[(H+1)%3]}`);let z=new Map;for(let U of C)for(let H=0;H<3;H++){let Z=o[U].v[H],he=o[U].v[(H+1)%3];if(!_.has(`${he},${Z}`)){if(z.has(Z))return null;z.set(Z,he)}}let R=[0,0,0];for(let U of C){let H=o[U],Z=Ri(xe(t[H.v[1]],t[H.v[0]]),xe(t[H.v[2]],t[H.v[0]]));R[0]+=Z[0],R[1]+=Z[1],R[2]+=Z[2]}let G=et(R)||1,X=z.keys().next().value,W=[X];for(let U=z.get(X);U!==X&&W.length<=z.size;U=z.get(U))W.push(U);if(W.length!==z.size)return null;c.push({n:[R[0]/G,R[1]/G,R[2]/G],verts:W})}let u=new Map,f=[];for(let C of c)for(let _ of C.verts)u.has(_)||(u.set(_,f.length),f.push(t[_]));for(let C of c)C.verts=C.verts.map(_=>u.get(_));let d=new Set,p=0;for(let C of c)C.verts.forEach((_,z)=>{d.add(`${_},${C.verts[(z+1)%C.verts.length]}`),p++});if(d.size!==p||![...d].every(C=>{let[_,z]=C.split(",");return d.has(`${z},${_}`)})||f.length-d.size/2+c.length!==2)return null;let m=0,h=[0,0,0],g=[0,0,0,0,0,0];for(let C of c){let _=f[C.verts[0]];for(let z=1;z+1<C.verts.length;z++){let R=f[C.verts[z]],G=f[C.verts[z+1]],X=Mt(_,Ri(R,G));m+=X/6;for(let U=0;U<3;U++)h[U]+=X/24*(_[U]+R[U]+G[U]);let W=(U,H)=>X/120*(_[U]*_[H]+R[U]*R[H]+G[U]*G[H]+(_[U]+R[U]+G[U])*(_[H]+R[H]+G[H]));g[0]+=W(0,0),g[1]+=W(1,1),g[2]+=W(2,2),g[3]+=W(0,1),g[4]+=W(0,2),g[5]+=W(1,2)}}if(!(m>0))return null;for(let C=0;C<3;C++)h[C]/=m;g[0]-=m*h[0]*h[0],g[1]-=m*h[1]*h[1],g[2]-=m*h[2]*h[2],g[3]-=m*h[0]*h[1],g[4]-=m*h[0]*h[2],g[5]-=m*h[1]*h[2];let b=g[0]+g[1]+g[2],y=[[b-g[0],-g[3],-g[4]],[-g[3],b-g[1],-g[5]],[-g[4],-g[5],b-g[2]]],{values:A,vectors:v}=Tu(y),x=v,w=C=>{let _=xe(C,h);return[x[0][0]*_[0]+x[1][0]*_[1]+x[2][0]*_[2],x[0][1]*_[0]+x[1][1]*_[1]+x[2][1]*_[2],x[0][2]*_[0]+x[1][2]*_[1]+x[2][2]*_[2]]},B=f.map(w),L=new Float32Array(B.flat()),M=c.map(C=>{let _=C.n,z=[x[0][0]*_[0]+x[1][0]*_[1]+x[2][0]*_[2],x[0][1]*_[0]+x[1][1]*_[1]+x[2][1]*_[2],x[0][2]*_[0]+x[1][2]*_[1]+x[2][2]*_[2]],R=-1/0;for(let G of C.verts)R=Math.max(R,Mt(z,B[G]));return{normal:z,d:R,verts:C.verts}}),I=new Map;M.forEach((C,_)=>C.verts.forEach((z,R)=>I.set(`${z},${C.verts[(R+1)%C.verts.length]}`,_)));let E=[];M.forEach((C,_)=>C.verts.forEach((z,R)=>{let G=C.verts[(R+1)%C.verts.length],X=I.get(`${G},${z}`);z<G&&X!==void 0&&E.push([z,G,_,X])}));let k=[0,0,0],P=0;for(let C of B){for(let _=0;_<3;_++)k[_]=Math.max(k[_],2*Math.abs(C[_]));P=Math.max(P,et(C))}return{vertices:L,faces:M,edges:E,volume:m,moments:[A[0],A[1],A[2]],size:k,radius:P,center:h,rotation:ju(x)}}function Tu(s){let e=s.map(a=>[...a]),t=[[1,0,0],[0,1,0],[0,0,1]];for(let a=0;a<50&&!(e[0][1]**2+e[0][2]**2+e[1][2]**2<1e-30*(e[0][0]**2+e[1][1]**2+e[2][2]**2+1e-300));a++)for(let c=0;c<3;c++)for(let u=c+1;u<3;u++){if(Math.abs(e[c][u])<1e-300)continue;let f=(e[u][u]-e[c][c])/(2*e[c][u]),d=Math.sign(f||1)/(Math.abs(f)+Math.sqrt(f*f+1)),p=1/Math.sqrt(d*d+1),m=d*p;for(let h=0;h<3;h++){let g=e[h][c],b=e[h][u];e[h][c]=p*g-m*b,e[h][u]=m*g+p*b}for(let h=0;h<3;h++){let g=e[c][h],b=e[u][h];e[c][h]=p*g-m*b,e[u][h]=m*g+p*b}for(let h=0;h<3;h++){let g=t[h][c],b=t[h][u];t[h][c]=p*g-m*b,t[h][u]=m*g+p*b}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],o=Ri(i,r),n=[[i[0],r[0],o[0]],[i[1],r[1],o[1]],[i[2],r[2],o[2]]];return{values:[e[0][0],e[1][1],e[2][2]],vectors:n}}function ju(s){let e=s[0][0]+s[1][1]+s[2][2],t,i,r,o;if(e>0){let a=.5/Math.sqrt(e+1);o=.25/a,t=(s[2][1]-s[1][2])*a,i=(s[0][2]-s[2][0])*a,r=(s[1][0]-s[0][1])*a}else if(s[0][0]>s[1][1]&&s[0][0]>s[2][2]){let a=2*Math.sqrt(1+s[0][0]-s[1][1]-s[2][2]);o=(s[2][1]-s[1][2])/a,t=.25*a,i=(s[0][1]+s[1][0])/a,r=(s[0][2]+s[2][0])/a}else if(s[1][1]>s[2][2]){let a=2*Math.sqrt(1+s[1][1]-s[0][0]-s[2][2]);o=(s[0][2]-s[2][0])/a,t=(s[0][1]+s[1][0])/a,i=.25*a,r=(s[1][2]+s[2][1])/a}else{let a=2*Math.sqrt(1+s[2][2]-s[0][0]-s[1][1]);o=(s[1][0]-s[0][1])/a,t=(s[0][2]+s[2][0])/a,i=(s[1][2]+s[2][1])/a,r=.25*a}let n=Math.hypot(t,i,r,o);return[t/n,i/n,r/n,o/n]}var xl=new WeakSet,De=s=>xl.has(s),Du=new WeakSet;var or=s=>Du.has(s);function nr(s,e,t,i,r,o=[0,0,0]){let n=new Ze(s,[2*e,2*e,2*e],t,i,r,o);n.mass=t>0?4/3*Math.PI*e*e*e*t:0;let a=.4*n.mass*e*e;return n.moment.set([a,a,a]),n.radius=e,xl.add(n),n}var Al=new WeakMap,Ct=s=>Al.get(s);function ar(s,e,t,i,r,o=[0,0,0,1],n=[0,0,0]){if(e.vertices.length/3>32||e.faces.length>255||e.edges.length>255)throw new Error(`hull: ${e.vertices.length/3} vertices, ${e.faces.length} faces, ${e.edges.length} edges (at most ${32} vertices, 255 faces and edges)`);let a=new Ze(s,e.size,t,i,r,n);return a.positionAng.set([o[0],o[1],o[2],o[3]]),a.mass=t>0?e.volume*t:0,a.moment.set(e.moments.map(l=>l*Math.max(t,0))),a.radius=e.radius,Al.set(a,e),a}var In=`
|
|
1
|
+
var S=(s=0,e=0,t=0)=>Float64Array.of(s,e,t),jt=(s=0,e=0,t=0,i=1)=>Float64Array.of(s,e,t,i),ae=()=>new Float64Array(9);var Ps=s=>s<0?-1:s>0?1:0,Bt=(s,e)=>s<e?s:e,Pu=(s,e)=>s>e?s:e,ce=(s,e,t)=>Pu(e,Bt(t,s)),V=(s,e)=>s[0]*e[0]+s[1]*e[1]+s[2]*e[2],Ne=s=>V(s,s),rr=s=>Math.sqrt(Ne(s));function lt(s,e,t,i){return s[0]=e,s[1]=t,s[2]=i,s}var Li=(s,e,t)=>lt(s,e[0]+t[0],e[1]+t[1],e[2]+t[2]),J=(s,e,t)=>lt(s,e[0]-t[0],e[1]-t[1],e[2]-t[2]),ne=(s,e,t)=>lt(s,e[0]*t,e[1]*t,e[2]*t),_t=(s,e,t)=>lt(s,e[0]/t,e[1]/t,e[2]/t),ee=(s,e)=>lt(s,-e[0],-e[1],-e[2]),ya=(s,e)=>lt(s,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),D=(s,e,t,i)=>lt(s,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),Lu=(s,e)=>_t(s,e,rr(e)),Dt=(s,e,t)=>lt(s,e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]);function ir(s,e,t,i,r){return s[0]=e,s[1]=t,s[2]=i,s[3]=r,s}function Gr(s,e,t){let i=e[0],r=e[1],o=e[2],a=e[3],n=t[0],l=t[1],c=t[2],u=t[3];return ir(s,a*n+i*u+r*c-o*l,a*l-i*c+r*u+o*n,a*c+i*l-r*n+o*u,a*u-i*n-r*l-o*c)}var _u=(s,e)=>ir(s,-e[0],-e[1],-e[2],e[3]),Xn=s=>Ne(s)+s[3]*s[3];function va(s,e){let t=Xn(e);return ir(s,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function Eu(s,e){let t=Math.sqrt(Xn(e));return ir(s,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var tr=new Float64Array(4),Pi=new Float64Array(4);function ct(s,e,t){return Gr(tr,e,va(tr,t)),lt(s,tr[0]*2,tr[1]*2,tr[2]*2)}function si(s,e,t){return ir(Pi,t[0],t[1],t[2],0),Gr(Pi,Pi,e),Eu(s,ir(s,e[0]+Pi[0]*.5,e[1]+Pi[1]*.5,e[2]+Pi[2]*.5,e[3]+Pi[3]*.5))}function le(s,e,t){let i=e[0],r=e[1],o=e[2],a=e[3],n=(r*t[2]-o*t[1])*2,l=(o*t[0]-i*t[2])*2,c=(i*t[1]-r*t[0])*2;return lt(s,t[0]+n*a+(r*c-o*l),t[1]+l*a+(o*n-i*c),t[2]+c*a+(i*l-r*n))}var Jn=new Float64Array(3);function Gt(s,e,t,i){return le(Jn,t,i),Li(s,Jn,e)}function Te(s,e,t){return le(s,_u(tr,e),t)}function Ce(s,e,t,i){return s.fill(0),s[0]=e,s[4]=t,s[8]=i,s}function xa(s,e){return s[0]=0,s[1]=-e[2],s[2]=e[1],s[3]=e[2],s[4]=0,s[5]=-e[0],s[6]=-e[1],s[7]=e[0],s[8]=0,s}function _i(s,e){let t=e[1],i=e[2],r=e[5];return s[0]=e[0],s[1]=e[3],s[2]=e[6],s[3]=t,s[4]=e[4],s[5]=e[7],s[6]=i,s[7]=r,s[8]=e[8],s}function Ke(s,e,t){let i=e[0],r=e[1],o=e[2],a=e[3],n=e[4],l=e[5],c=e[6],u=e[7],f=e[8],d=t[0],p=t[1],m=t[2],h=t[3],g=t[4],b=t[5],y=t[6],A=t[7],v=t[8];return s[0]=i*d+r*h+o*y,s[1]=i*p+r*g+o*A,s[2]=i*m+r*b+o*v,s[3]=a*d+n*h+l*y,s[4]=a*p+n*g+l*A,s[5]=a*m+n*b+l*v,s[6]=c*d+u*h+f*y,s[7]=c*p+u*g+f*A,s[8]=c*m+u*b+f*v,s}function ue(s,e,t){let i=t[0],r=t[1],o=t[2];return lt(s,e[0]*i+e[1]*r+e[2]*o,e[3]*i+e[4]*r+e[5]*o,e[6]*i+e[7]*r+e[8]*o)}function qe(s,e){for(let t=0;t<9;t++)s[t]+=e[t];return s}function Ei(s,e,t){for(let i=0;i<9;i++)s[i]=e[i]*t;return s}function Yn(s,e){for(let t=0;t<9;t++)s[t]=-e[t];return s}function Ls(s,e,t){for(let i=0;i<3;i++)for(let r=0;r<3;r++)s[i*3+r]=t[r]*e[i];return s}function Qn(s,e){let t=Math.sqrt(e[0]*e[0]+e[3]*e[3]+e[6]*e[6]),i=Math.sqrt(e[1]*e[1]+e[4]*e[4]+e[7]*e[7]),r=Math.sqrt(e[2]*e[2]+e[5]*e[5]+e[8]*e[8]);return Ce(s,t,i,r)}function _s(s,e){let t=Math.abs(e[0])>Math.abs(e[2])?S(-e[1],e[0],0):S(0,-e[2],e[1]);Lu(t,t);let i=Dt(S(),e,t);return s[0]=e[0],s[1]=e[1],s[2]=e[2],s.set(t,3),s.set(i,6),s}function Es(s,e,t,i,r,o,a){let n=s[0],l=s[3],c=s[4],u=s[6],f=s[7],d=s[8],p=t[0],m=t[1],h=t[2],g=e[0],b=t[3],y=t[4],A=t[5],v=e[3],x=e[4],w=t[6],B=t[7],L=t[8],M=e[6],I=e[7],E=e[8],k=l/n,P=u/n,C=p/n,_=b/n,z=w/n,R=n,G=c-k*k*R,X=(f-k*P*R)/G,W=(m-k*C*R)/G,U=(y-k*_*R)/G,H=(B-k*z*R)/G,Z=d-(P*P*R+X*X*G),he=(h-P*C*R-X*W*G)/Z,we=(A-P*_*R-X*U*G)/Z,Oe=(L-P*z*R-X*H*G)/Z,$=g-(C*C*R+W*W*G+he*he*Z),Je=(v-C*_*R-W*U*G-he*we*Z)/$,Xe=(M-C*z*R-W*H*G-he*Oe*Z)/$,Be=x-(_*_*R+U*U*G+we*we*Z+Je*Je*$),ot=(I-_*z*R-U*H*G-we*Oe*Z-Je*Xe*$)/Be,ri=E-(z*z*R+H*H*G+Oe*Oe*Z+Xe*Xe*$+ot*ot*Be),Ye=i[0],at=i[1]-k*Ye,Qe=i[2]-P*Ye-X*at,nt=r[0]-C*Ye-W*at-he*Qe,wt=r[1]-_*Ye-U*at-we*Qe-Je*nt,Qi=r[2]-z*Ye-H*at-Oe*Qe-Xe*nt-ot*wt,Ki=Ye/R,Zi=at/G,er=Qe/Z,Wt=nt/$,Su=wt/Be,ku=Qi/ri;a[2]=ku,a[1]=Su-ot*a[2],a[0]=Wt-Je*a[1]-Xe*a[2],o[2]=er-he*a[0]-we*a[1]-Oe*a[2],o[1]=Zi-X*o[2]-W*a[0]-U*a[1]-H*a[2],o[0]=Ki-k*o[1]-P*o[2]-C*a[0]-_*a[1]-z*a[2]}var Et=1,Ve=1e10,Ms=.01,Kn=1e-5,Ze=class{solver;forces=[];positionLin;positionAng=jt();initialLin=S();initialAng=jt();inertialLin=S();inertialAng=jt();velocityLin;velocityAng=S();prevVelocityLin;size;mass;moment;friction;radius;constructor(e,t,i,r,o,a=[0,0,0]){this.solver=e,this.size=S(t[0],t[1],t[2]),this.friction=r,this.positionLin=S(o[0],o[1],o[2]),this.velocityLin=S(a[0],a[1],a[2]),this.prevVelocityLin=S(a[0],a[1],a[2]);let[n,l,c]=this.size;this.mass=n*l*c*i,this.moment=S((l*l+c*c)/12*this.mass,(n*n+c*c)/12*this.mass,(n*n+l*l)/12*this.mass),this.radius=rr([n*.5,l*.5,c*.5]),e.bodies.push(this)}constrainedTo(e){for(let t of this.forces)if(t.bodyA===this&&t.bodyB===e||t.bodyA===e&&t.bodyB===this)return!0;return!1}},zs=()=>({lhsLin:ae(),lhsAng:ae(),lhsCross:ae(),rhsLin:S(),rhsAng:S()}),Ft=class{solver;bodyA;bodyB;constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}};var rl=8,Zn=16,zi=1e-6,wa=1e-5,Mu=1e-6,Ba=34028234663852886e22,Is=0,sl=1,Rs=2;function el(s){let e=s.positionAng;return{center:s.positionLin,half:ne(S(),s.size,.5),axis:[le(S(),e,[1,0,0]),le(S(),e,[0,1,0]),le(S(),e,[0,0,1])]}}var Mi=(s,e)=>Math.abs(V(s,e));function Os(s,e){let t=V(e,s.axis[0])>=0?1:-1,i=V(e,s.axis[1])>=0?1:-1,r=V(e,s.axis[2])>=0?1:-1,o=D(S(),s.center,s.axis[0],s.half[0]*t);return D(o,o,s.axis[1],s.half[1]*i),D(o,o,s.axis[2],s.half[2]*r)}function ol(s,e){return e===0?{u:s.axis[1],v:s.axis[2],extentU:s.half[1],extentV:s.half[2]}:e===1?{u:s.axis[0],v:s.axis[2],extentU:s.half[0],extentV:s.half[2]}:{u:s.axis[0],v:s.axis[1],extentU:s.half[0],extentV:s.half[1]}}function zu(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]);return{normal:r,center:o,...ol(s,e)}}function Iu(s,e){let t=0,i=-Ba;for(let r=0;r<3;r++){let o=Mi(s.axis[r],e);o>i&&(i=o,t=r)}return t}function Ru(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]),{u:a,v:n,extentU:l,extentV:c}=ol(s,e),u=(f,d)=>{let p=D(S(),o,a,f*l);return D(p,p,n,d*c)};return[u(1,1),u(-1,1),u(-1,-1),u(1,-1)]}function Ou(s,e,t){let i=[];if(s.length===0)return i;let r=s[s.length-1],o=V(e,r)-t;for(let a of s){let n=V(e,a)-t,l=o<=wa,c=n<=wa;if(l!==c){let u=0,f=o-n;Math.abs(f)>zi&&(u=ce(o/f,0,1)),i.length<Zn&&i.push(D(S(),r,J(S(),a,r),u))}c&&i.length<Zn&&i.push(a),r=a,o=n}return i}var Ts=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(S(),Li(S(),e,t),.5);for(let n of this.midpoints)if(Ne(J(S(),r,n))<Mu)return;if(this.contacts.length>=rl)return;let{bodyA:o,bodyB:a}=this;this.contacts.push({feature:i,rA:Te(S(),o.positionAng,J(S(),e,o.positionLin)),rB:Te(S(),a.positionAng,J(S(),t,a.positionLin))}),this.midpoints.push(r)}};function Aa(s,e,t,i,r,o,a,n){let l=Ne(i);if(l<zi)return!0;let c=1/Math.sqrt(l),u=ne(S(),i,c);V(u,t)<0&&ee(u,u);let f=Math.abs(V(t,u)),d=s.half[0]*Mi(u,s.axis[0])+s.half[1]*Mi(u,s.axis[1])+s.half[2]*Mi(u,s.axis[2]),p=e.half[0]*Mi(u,e.axis[0])+e.half[1]*Mi(u,e.axis[1])+e.half[2]*Mi(u,e.axis[2]),m=f-(d+p);return m>0?!1:((!n.valid||m>n.separation)&&(n.valid=!0,n.type=r,n.indexA=o,n.indexB=a,n.separation=m,n.normalAB=u),!0)}function tl(s,e,t){let i=(e+1)%3,r=(e+2)%3,o=V(t,s.axis[i])>=0?1:-1,a=V(t,s.axis[r])>=0?1:-1,n=D(S(),s.center,s.axis[i],s.half[i]*o);D(n,n,s.axis[r],s.half[r]*a);let l=ne(S(),s.axis[e],s.half[e]);return[J(S(),n,l),Li(S(),n,l)]}function Tu(s,e,t,i){let r=J(S(),e,s),o=J(S(),i,t),a=J(S(),s,t),n=V(r,r),l=V(o,o),c=V(o,a),u=0,f=0;if(n<=zi&&l<=zi)return[s,t];if(n<=zi)f=ce(c/l,0,1);else{let d=V(r,a);if(l<=zi)u=ce(-d/n,0,1);else{let p=V(r,o),m=n*l-p*p;Math.abs(m)>zi&&(u=ce((p*c-d*l)/m,0,1)),f=(p*u+c)/l,f<0?(f=0,u=ce(-d/n,0,1)):f>1&&(f=1,u=ce((p-d)/n,0,1))}}return[D(S(),s,r,u),D(S(),t,o,f)]}function il(s,e,t,i,r,o,a){let n=r?t:i,l=r?i:t,c=r?a:ee(S(),a),u=zu(n,o,c),f=Iu(l,u.normal),d=Ru(l,f,u.normal),p=[[u.u,u.extentU],[ee(S(),u.u),u.extentU],[u.v,u.extentV],[ee(S(),u.v),u.extentV]];for(let[g,b]of p)if(d=Ou(d,g,V(g,u.center)+b),!d.length)return[];let m=new Ts(s,e),h=(r?Is:sl)<<24|(o&255)<<16|(f&255)<<8;for(let g=0;g<d.length&&m.contacts.length<rl;g++){let b=d[g],y=V(J(S(),b,u.center),u.normal);if(y>wa)continue;let A=D(S(),b,u.normal,-y),v=r?A:b,x=r?b:A;m.add(v,x,h|g&255)}return m.contacts.length||m.add(Os(t,a),Os(i,ee(S(),a)),h),m.contacts}function ju(s,e,t,i,r,o,a){let[n,l]=tl(t,r,a),[c,u]=tl(i,o,ee(S(),a)),[f,d]=Tu(n,l,c,u),p=new Ts(s,e),m=Rs<<24|(r&255)<<8|o&255;return p.add(f,d,m),p.contacts.length||p.add(Os(t,a),Os(i,ee(S(),a)),m),p.contacts}function al(s,e,t){let i=el(s),r=el(e),o=J(S(),r.center,i.center),a={type:Is,indexA:0,indexB:0,separation:-Ba,normalAB:S(),valid:!1},n={type:Rs,indexA:0,indexB:0,separation:-Ba,normalAB:S(),valid:!1};for(let u=0;u<3;u++)if(!Aa(i,r,o,i.axis[u],Is,u,-1,a))return[];for(let u=0;u<3;u++)if(!Aa(i,r,o,r.axis[u],sl,-1,u,a))return[];let l=S();for(let u=0;u<3;u++)for(let f=0;f<3;f++)if(Dt(l,i.axis[u],r.axis[f]),!Aa(i,r,o,l,Rs,u,f,n))return[];if(!a.valid)return[];let c=a;return n.valid&&.95*n.separation>a.separation+.01&&(c=n),_s(t,ee(S(),c.normalAB)),c.type===Rs?ju(s,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===Is?il(s,e,i,r,!0,c.indexA,c.normalAB):il(s,e,i,r,!1,c.indexB,c.normalAB)}var nl=S(),ll=S(),cl=S(),ul=S(),fl=S(),dl=S(),js=ae(),Ca=ae(),Sa=ae(),Ds=ae(),ka=ae(),Pa=ae(),La=ae(),Gs=ae(),Me=S(),je=S(),Ii=S(),oi=S(),hl=S(),pl=S(),sr=0,Fr=0;function ml(s,e,t){for(let i=0;i<3;i++)oi[0]=t[i*3],oi[1]=t[i*3+1],oi[2]=t[i*3+2],Dt(oi,e,oi),s[i*3]=oi[0],s[i*3+1]=oi[1],s[i*3+2]=oi[2];return s}var ai=class extends Ft{contacts=[];basis=ae();friction=0;constructor(e,t,i){super(e,t,i)}get numContacts(){return this.contacts.length}initialize(){let e=this.bodyA,t=this.bodyB,{solver:i}=this;this.friction=Math.sqrt(e.friction*t.friction);let r=this.contacts;this.contacts=al(e,t,this.basis).map(o=>{let a=r.find(n=>n.feature===o.feature);return a?{feature:o.feature,rA:a.stick?a.rA:o.rA,rB:a.stick?a.rB:o.rB,C0:S(),penalty:a.penalty.slice(),lambda:a.lambda.slice(),stick:a.stick}:{feature:o.feature,rA:o.rA,rB:o.rB,C0:S(),penalty:S(),lambda:S(),stick:!1}});for(let o of this.contacts){Gt(hl,e.positionLin,e.positionAng,o.rA),Gt(pl,t.positionLin,t.positionAng,o.rB),ue(o.C0,this.basis,J(Ii,hl,pl)),o.C0[0]+=Ms;for(let a=0;a<3;a++)o.lambda[a]=o.lambda[a]*i.alpha*i.gamma,o.penalty[a]=ce(o.penalty[a]*i.gamma,Et,Ve)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,o=this.basis;le(fl,i.positionAng,e.rA),le(dl,r.positionAng,e.rB),Yn(js,o),ml(Ca,fl,o),ml(Sa,dl,js),Ce(Ds,e.penalty[0],e.penalty[1],e.penalty[2]),ne(Me,e.C0,1-t),D(Me,Me,ue(Ii,o,nl),1),D(Me,Me,ue(Ii,js,cl),1),D(Me,Me,ue(Ii,Ca,ll),1),D(Me,Me,ue(Ii,Sa,ul),1),ue(je,Ds,Me),D(je,je,e.lambda,1),je[0]=Bt(je[0],0),Fr=Math.abs(je[0])*this.friction,sr=Math.sqrt(je[1]*je[1]+je[2]*je[2]),sr>Fr&&sr>0&&(je[1]*=Fr/sr,je[2]*=Fr/sr)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;J(nl,e.positionLin,e.initialLin),ct(ll,e.positionAng,e.initialAng),J(cl,t.positionLin,t.initialLin),ct(ul,t.positionAng,t.initialAng)}updatePrimal(e,t,i){this.computeDisplacements();let r=e===this.bodyA;for(let o of this.contacts){this.evaluate(o,t);let a=r?this.basis:js,n=r?Ca:Sa;_i(ka,a),_i(Pa,n),Ke(La,Pa,Ds),qe(i.lhsLin,Ke(Gs,Ke(Gs,ka,Ds),a)),qe(i.lhsAng,Ke(Gs,La,n)),qe(i.lhsCross,Ke(Gs,La,a)),D(i.rhsLin,i.rhsLin,ue(Ii,ka,je),1),D(i.rhsAng,i.rhsAng,ue(Ii,Pa,je),1)}}updateDual(e){let{solver:t}=this;this.computeDisplacements();for(let i of this.contacts)this.evaluate(i,e),i.lambda.set(je),je[0]<0&&(i.penalty[0]=Bt(i.penalty[0]+t.betaLin*Math.abs(Me[0]),Ve)),sr<=Fr&&(i.penalty[1]=Bt(i.penalty[1]+t.betaLin*Math.abs(Me[1]),Ve),i.penalty[2]=Bt(i.penalty[2]+t.betaLin*Math.abs(Me[2]),Ve),i.stick=Math.sqrt(Me[1]*Me[1]+Me[2]*Me[2])<Kn)}};var _a=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),ni=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,_a())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=S(),a=S();for(let n=this.bodies.length-1;n>=0;n--){let l=this.bodies[n];if(l.mass<=0)continue;Te(o,l.positionAng,J(o,e,l.positionLin)),Te(a,l.positionAng,t);let c=0,u=1/0,f=!0;for(let p=0;p<3&&f;p++){let m=l.size[p]*.5;if(Math.abs(a[p])<1e-6){(o[p]<-m||o[p]>m)&&(f=!1);continue}let h=1/a[p],g=(-m-o[p])*h,b=(m-o[p])*h;g>b&&([g,b]=[b,g]),c=Math.max(c,g),u=Math.min(u,b),c>u&&(f=!1)}if(!f)continue;let d=c>=0?c:u;d<0||(!r||d<r.t)&&(r={body:l,local:D(S(),o,a,d),t:d})}return r}sys=zs();dxLin=S();dxAng=S();dp=S();accel=S();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:o}=this;for(let n=i.length-1;n>=0;n--){let l=i[n];for(let c=n-1;c>=0;c--){let u=i[c];J(o,l.positionLin,u.positionLin);let f=l.radius+u.radius;V(o,o)<=f*f&&!l.constrainedTo(u)&&new ai(this,l,u)}}let a=[];for(let n of this.forces)n.initialize()?a.push(n):n.unlinkFromBodies();this.forces=a;for(let n=i.length-1;n>=0;n--){let l=i[n];D(l.inertialLin,l.positionLin,l.velocityLin,e),l.mass>0&&(l.inertialLin[2]+=t*(e*e)),si(l.inertialAng,l.positionAng,gl(l.velocityAng,e)),_t(this.accel,J(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*Ps(t),u=ce(c/Math.abs(t),0,1);Number.isFinite(u)||(u=0),l.initialLin.set(l.positionLin),l.initialAng.set(l.positionAng),l.mass>0&&(D(l.positionLin,l.positionLin,l.velocityLin,e),l.positionLin[2]+=t*(u*e*e),si(l.positionAng,l.positionAng,gl(l.velocityAng,e)))}for(let n=0;n<this.iterations;n++){for(let l=i.length-1;l>=0;l--){let c=i[l];if(c.mass<=0)continue;let u=c.mass,f=c.moment;Ce(r.lhsLin,u/(e*e),u/(e*e),u/(e*e)),Ce(r.lhsAng,f[0]/(e*e),f[1]/(e*e),f[2]/(e*e)),r.lhsCross.fill(0),ue(r.rhsLin,r.lhsLin,J(o,c.positionLin,c.inertialLin)),ue(r.rhsAng,r.lhsAng,ct(o,c.positionAng,c.inertialAng));let d=c.forces;for(let p=d.length-1;p>=0;p--)d[p].updatePrimal(c,this.alpha,r);Es(r.lhsLin,r.lhsAng,r.lhsCross,ee(r.rhsLin,r.rhsLin),ee(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),D(c.positionLin,c.positionLin,this.dxLin,1),si(c.positionAng,c.positionAng,this.dxAng)}for(let l of this.forces)l.updateDual(this.alpha)}for(let n of i)n.prevVelocityLin.set(n.velocityLin),n.mass>0&&(_t(n.velocityLin,J(n.velocityLin,n.positionLin,n.initialLin),e),_t(n.velocityAng,ct(n.velocityAng,n.positionAng,n.initialAng),e))}},Du=S(),gl=(s,e)=>ne(Du,s,e);var xe=(s,e)=>[s[0]-e[0],s[1]-e[1],s[2]-e[2]],Mt=(s,e)=>s[0]*e[0]+s[1]*e[1]+s[2]*e[2],Ri=(s,e)=>[s[1]*e[2]-s[2]*e[1],s[2]*e[0]-s[0]*e[2],s[0]*e[1]-s[1]*e[0]],et=s=>Math.sqrt(Mt(s,s));function Ur(s){let e=Ea(s);return!e||e.vertices.length/3<=32?e:Ea(bl(e,32))}function bl(s,e){let[t,i,r,o]=s.rotation,a=f=>{let d=[2*(i*f[2]-r*f[1]),2*(r*f[0]-t*f[2]),2*(t*f[1]-i*f[0])];return[f[0]+o*d[0]+(i*d[2]-r*d[1])+s.center[0],f[1]+o*d[1]+(r*d[0]-t*d[2])+s.center[1],f[2]+o*d[2]+(t*d[1]-i*d[0])+s.center[2]]},n=[];for(let f=0;f<s.vertices.length;f+=3)n.push(a([s.vertices[f],s.vertices[f+1],s.vertices[f+2]]));let l=0;n.forEach((f,d)=>{et(xe(f,s.center))>et(xe(n[l],s.center))&&(l=d)});let c=[l],u=n.map(f=>et(xe(f,n[l])));for(;c.length<e;){let f=0;u.forEach((d,p)=>{d>u[f]&&(f=p)}),c.push(f),n.forEach((d,p)=>{u[p]=Math.min(u[p],et(xe(d,n[f])))})}return c.flatMap(f=>n[f])}function Ea(s){let e=0;for(let v=0;v<s.length;v++)e=Math.max(e,Math.abs(s[v]));let t=Math.max(e,1e-9)*1e-6,i=new Set,r=[];for(let v=0;v+2<s.length;v+=3){let x=[s[v],s[v+1],s[v+2]],w=x.map(B=>Math.round(B/t)).join(",");i.has(w)||(i.add(w),r.push(x))}if(r.length<4)return null;let o=Math.max(e,1e-9)*1e-10,a=0;for(let v=1;v<r.length;v++)r[v][0]<r[a][0]&&(a=v);let n=-1,l=0;for(let v=0;v<r.length;v++){let x=et(xe(r[v],r[a]));x>l&&(l=x,n=v)}if(n<0||l<o)return null;let c=-1;l=0;let u=xe(r[n],r[a]);for(let v=0;v<r.length;v++){let x=et(Ri(u,xe(r[v],r[a])));x>l&&(l=x,c=v)}if(c<0||l<o*et(u))return null;let f=Ri(u,xe(r[c],r[a])),d=-1;l=0;for(let v=0;v<r.length;v++){let x=Math.abs(Mt(f,xe(r[v],r[a])));x>l&&(l=x,d=v)}if(d<0||l<o*et(f))return null;let p=[],m=(v,x,w)=>{let B=Ri(xe(r[x],r[v]),xe(r[w],r[v])),L=et(B)||1,M=[B[0]/L,B[1]/L,B[2]/L];return{v:[v,x,w],n:M,d:Mt(M,r[v])}},g=Mt(f,xe(r[d],r[a]))>0?[a,c,n]:[a,n,c];p.push(m(g[0],g[1],g[2])),p.push(m(g[0],d,g[1])),p.push(m(g[1],d,g[2])),p.push(m(g[2],d,g[0]));let b=new Set([a,n,c,d]),y=r.map((v,x)=>x).filter(v=>!b.has(v)),A=[a,n,c,d].reduce((v,x)=>[v[0]+r[x][0]/4,v[1]+r[x][1]/4,v[2]+r[x][2]/4],[0,0,0]);y.sort((v,x)=>et(xe(r[x],A))-et(xe(r[v],A)));for(let v of y){let x=p.filter(k=>Mt(k.n,r[v])-k.d>o);if(!x.length)continue;let w=new Map;for(let k of p)for(let P=0;P<3;P++)w.set(`${k.v[P]},${k.v[(P+1)%3]}`,k);let B=new Set(x),L=[...x];for(;L.length;){let k=L.pop();for(let P=0;P<3;P++){let C=w.get(`${k.v[(P+1)%3]},${k.v[P]}`);C&&!B.has(C)&&Mt(C.n,r[v])-C.d>-o&&(B.add(C),L.push(C))}}let M=[...B],I=new Set;for(let k of M)for(let P=0;P<3;P++)I.add(`${k.v[P]},${k.v[(P+1)%3]}`);let E=[];for(let k of M)for(let P=0;P<3;P++){let C=k.v[P],_=k.v[(P+1)%3];I.has(`${_},${C}`)||E.push([C,_])}for(let k of M)p.splice(p.indexOf(k),1);for(let[k,P]of E)p.push(m(k,P,v))}return yl(r.flat(),p.flatMap(v=>v.v))}function Ma(s,e){let t=yl(s,e);return t&&t.vertices.length/3>32?Ea(bl(t,32)):t}function yl(s,e){let t=[];for(let C=0;C+2<s.length;C+=3)t.push([s[C],s[C+1],s[C+2]]);let i=0;for(let C of t)i=Math.max(i,Math.abs(C[0]),Math.abs(C[1]),Math.abs(C[2]));let r=Math.max(i,1e-9)*1e-5,o=[];for(let C=0;C+2<e.length;C+=3){let _=[e[C],e[C+1],e[C+2]],z=Ri(xe(t[_[1]],t[_[0]]),xe(t[_[2]],t[_[0]])),R=et(z);if(!(R>0))continue;let G=[z[0]/R,z[1]/R,z[2]/R];o.push({v:_,n:G,d:Mt(G,t[_[0]])})}let a=new Map;o.forEach((C,_)=>{for(let z=0;z<3;z++)a.set(`${C.v[z]},${C.v[(z+1)%3]}`,_)});let n=new Array(o.length).fill(-1),l=[];o.forEach((C,_)=>{if(n[_]>=0)return;let z=[_];n[_]=l.length;for(let R=0;R<z.length;R++){let G=o[z[R]];for(let X=0;X<3;X++){let W=a.get(`${G.v[(X+1)%3]},${G.v[X]}`);if(W===void 0||n[W]>=0)continue;let U=o[W];Mt(C.n,U.n)>1-1e-5&&U.v.every(H=>Math.abs(Mt(C.n,t[H])-C.d)<=r)&&(n[W]=l.length,z.push(W))}}l.push(z)});let c=[];for(let C of l){let _=new Set;for(let U of C)for(let H=0;H<3;H++)_.add(`${o[U].v[H]},${o[U].v[(H+1)%3]}`);let z=new Map;for(let U of C)for(let H=0;H<3;H++){let Z=o[U].v[H],he=o[U].v[(H+1)%3];if(!_.has(`${he},${Z}`)){if(z.has(Z))return null;z.set(Z,he)}}let R=[0,0,0];for(let U of C){let H=o[U],Z=Ri(xe(t[H.v[1]],t[H.v[0]]),xe(t[H.v[2]],t[H.v[0]]));R[0]+=Z[0],R[1]+=Z[1],R[2]+=Z[2]}let G=et(R)||1,X=z.keys().next().value,W=[X];for(let U=z.get(X);U!==X&&W.length<=z.size;U=z.get(U))W.push(U);if(W.length!==z.size)return null;c.push({n:[R[0]/G,R[1]/G,R[2]/G],verts:W})}let u=new Map,f=[];for(let C of c)for(let _ of C.verts)u.has(_)||(u.set(_,f.length),f.push(t[_]));for(let C of c)C.verts=C.verts.map(_=>u.get(_));let d=new Set,p=0;for(let C of c)C.verts.forEach((_,z)=>{d.add(`${_},${C.verts[(z+1)%C.verts.length]}`),p++});if(d.size!==p||![...d].every(C=>{let[_,z]=C.split(",");return d.has(`${z},${_}`)})||f.length-d.size/2+c.length!==2)return null;let m=0,h=[0,0,0],g=[0,0,0,0,0,0];for(let C of c){let _=f[C.verts[0]];for(let z=1;z+1<C.verts.length;z++){let R=f[C.verts[z]],G=f[C.verts[z+1]],X=Mt(_,Ri(R,G));m+=X/6;for(let U=0;U<3;U++)h[U]+=X/24*(_[U]+R[U]+G[U]);let W=(U,H)=>X/120*(_[U]*_[H]+R[U]*R[H]+G[U]*G[H]+(_[U]+R[U]+G[U])*(_[H]+R[H]+G[H]));g[0]+=W(0,0),g[1]+=W(1,1),g[2]+=W(2,2),g[3]+=W(0,1),g[4]+=W(0,2),g[5]+=W(1,2)}}if(!(m>0))return null;for(let C=0;C<3;C++)h[C]/=m;g[0]-=m*h[0]*h[0],g[1]-=m*h[1]*h[1],g[2]-=m*h[2]*h[2],g[3]-=m*h[0]*h[1],g[4]-=m*h[0]*h[2],g[5]-=m*h[1]*h[2];let b=g[0]+g[1]+g[2],y=[[b-g[0],-g[3],-g[4]],[-g[3],b-g[1],-g[5]],[-g[4],-g[5],b-g[2]]],{values:A,vectors:v}=Gu(y),x=v,w=C=>{let _=xe(C,h);return[x[0][0]*_[0]+x[1][0]*_[1]+x[2][0]*_[2],x[0][1]*_[0]+x[1][1]*_[1]+x[2][1]*_[2],x[0][2]*_[0]+x[1][2]*_[1]+x[2][2]*_[2]]},B=f.map(w),L=new Float32Array(B.flat()),M=c.map(C=>{let _=C.n,z=[x[0][0]*_[0]+x[1][0]*_[1]+x[2][0]*_[2],x[0][1]*_[0]+x[1][1]*_[1]+x[2][1]*_[2],x[0][2]*_[0]+x[1][2]*_[1]+x[2][2]*_[2]],R=-1/0;for(let G of C.verts)R=Math.max(R,Mt(z,B[G]));return{normal:z,d:R,verts:C.verts}}),I=new Map;M.forEach((C,_)=>C.verts.forEach((z,R)=>I.set(`${z},${C.verts[(R+1)%C.verts.length]}`,_)));let E=[];M.forEach((C,_)=>C.verts.forEach((z,R)=>{let G=C.verts[(R+1)%C.verts.length],X=I.get(`${G},${z}`);z<G&&X!==void 0&&E.push([z,G,_,X])}));let k=[0,0,0],P=0;for(let C of B){for(let _=0;_<3;_++)k[_]=Math.max(k[_],2*Math.abs(C[_]));P=Math.max(P,et(C))}return{vertices:L,faces:M,edges:E,volume:m,moments:[A[0],A[1],A[2]],size:k,radius:P,center:h,rotation:Fu(x)}}function Gu(s){let e=s.map(n=>[...n]),t=[[1,0,0],[0,1,0],[0,0,1]];for(let n=0;n<50&&!(e[0][1]**2+e[0][2]**2+e[1][2]**2<1e-30*(e[0][0]**2+e[1][1]**2+e[2][2]**2+1e-300));n++)for(let c=0;c<3;c++)for(let u=c+1;u<3;u++){if(Math.abs(e[c][u])<1e-300)continue;let f=(e[u][u]-e[c][c])/(2*e[c][u]),d=Math.sign(f||1)/(Math.abs(f)+Math.sqrt(f*f+1)),p=1/Math.sqrt(d*d+1),m=d*p;for(let h=0;h<3;h++){let g=e[h][c],b=e[h][u];e[h][c]=p*g-m*b,e[h][u]=m*g+p*b}for(let h=0;h<3;h++){let g=e[c][h],b=e[u][h];e[c][h]=p*g-m*b,e[u][h]=m*g+p*b}for(let h=0;h<3;h++){let g=t[h][c],b=t[h][u];t[h][c]=p*g-m*b,t[h][u]=m*g+p*b}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],o=Ri(i,r),a=[[i[0],r[0],o[0]],[i[1],r[1],o[1]],[i[2],r[2],o[2]]];return{values:[e[0][0],e[1][1],e[2][2]],vectors:a}}function Fu(s){let e=s[0][0]+s[1][1]+s[2][2],t,i,r,o;if(e>0){let n=.5/Math.sqrt(e+1);o=.25/n,t=(s[2][1]-s[1][2])*n,i=(s[0][2]-s[2][0])*n,r=(s[1][0]-s[0][1])*n}else if(s[0][0]>s[1][1]&&s[0][0]>s[2][2]){let n=2*Math.sqrt(1+s[0][0]-s[1][1]-s[2][2]);o=(s[2][1]-s[1][2])/n,t=.25*n,i=(s[0][1]+s[1][0])/n,r=(s[0][2]+s[2][0])/n}else if(s[1][1]>s[2][2]){let n=2*Math.sqrt(1+s[1][1]-s[0][0]-s[2][2]);o=(s[0][2]-s[2][0])/n,t=(s[0][1]+s[1][0])/n,i=.25*n,r=(s[1][2]+s[2][1])/n}else{let n=2*Math.sqrt(1+s[2][2]-s[0][0]-s[1][1]);o=(s[1][0]-s[0][1])/n,t=(s[0][2]+s[2][0])/n,i=(s[1][2]+s[2][1])/n,r=.25*n}let a=Math.hypot(t,i,r,o);return[t/a,i/a,r/a,o/a]}var xl=new WeakSet,De=s=>xl.has(s),Uu=new WeakSet;var or=s=>Uu.has(s);function ar(s,e,t,i,r,o=[0,0,0]){let a=new Ze(s,[2*e,2*e,2*e],t,i,r,o);a.mass=t>0?4/3*Math.PI*e*e*e*t:0;let n=.4*a.mass*e*e;return a.moment.set([n,n,n]),a.radius=e,xl.add(a),a}var Al=new WeakMap,Ct=s=>Al.get(s);function nr(s,e,t,i,r,o=[0,0,0,1],a=[0,0,0]){if(e.vertices.length/3>32||e.faces.length>255||e.edges.length>255)throw new Error(`hull: ${e.vertices.length/3} vertices, ${e.faces.length} faces, ${e.edges.length} edges (at most ${32} vertices, 255 faces and edges)`);let n=new Ze(s,e.size,t,i,r,a);return n.positionAng.set([o[0],o[1],o[2],o[3]]),n.mass=t>0?e.volume*t:0,n.moment.set(e.moments.map(l=>l*Math.max(t,0))),n.radius=e.radius,Al.set(n,e),n}var za=`
|
|
2
2
|
const T_NONE = 0;
|
|
3
3
|
|
|
4
4
|
const MAX_COLORS = 64u;
|
|
@@ -40,7 +40,7 @@ fn hash32(v: u32) -> u32 {
|
|
|
40
40
|
}
|
|
41
41
|
`,Ut=`
|
|
42
42
|
// Shared by every 2D module (layout.ts)
|
|
43
|
-
${
|
|
43
|
+
${za}
|
|
44
44
|
const T_JOINT = 1;
|
|
45
45
|
const T_SPRING = 2;
|
|
46
46
|
const T_MOTOR = 3;
|
|
@@ -185,7 +185,7 @@ fn argsColors(@builtin(local_invocation_id) lid: vec3u) {
|
|
|
185
185
|
let c = lid.x;
|
|
186
186
|
setArgs(IA_COLOR + 3u * c, colorThreads(color[params.colorStartOffset + c + 1u] - color[params.colorStartOffset + c]));
|
|
187
187
|
}
|
|
188
|
-
`,Fs=Nr(Ut),Se=["collision","adjacency","coloring","solve"];var
|
|
188
|
+
`,Fs=Nr(Ut),Se=["collision","adjacency","coloring","solve"];var Nu=`
|
|
189
189
|
struct ScanParams {
|
|
190
190
|
offset: u32, // first element of the range in data
|
|
191
191
|
n: u32, // elements in the range
|
|
@@ -233,7 +233,7 @@ fn addBlocks(@builtin(workgroup_id) wid: vec3u, @builtin(local_invocation_id) li
|
|
|
233
233
|
if (base < sp.n) { data[sp.offset + base] += add; }
|
|
234
234
|
if (base + 1u < sp.n) { data[sp.offset + base + 1u] += add; }
|
|
235
235
|
}
|
|
236
|
-
`,tt=class{levels=[];buffers=[];scanPipeline;addPipeline;constructor(e,t,i,r){let o=e.createShaderModule({label:"prefix scan",code:
|
|
236
|
+
`,tt=class{levels=[];buffers=[];scanPipeline;addPipeline;constructor(e,t,i,r){let o=e.createShaderModule({label:"prefix scan",code:Nu}),a=e.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}}]}),n=e.createPipelineLayout({bindGroupLayouts:[a]});this.scanPipeline=e.createComputePipeline({label:"scanBlocks",layout:n,compute:{module:o,entryPoint:"scanBlocks"}}),this.addPipeline=e.createComputePipeline({label:"addBlocks",layout:n,compute:{module:o,entryPoint:"addBlocks"}});let l=t,c=i,u=r;for(;;){let f=Math.max(1,Math.ceil(u/512)),d=e.createBuffer({label:`scan sums ${this.levels.length}`,size:Math.max(f,4)*4,usage:GPUBufferUsage.STORAGE}),p=e.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM,mappedAtCreation:!0});new Uint32Array(p.getMappedRange()).set([c,u,0,0]),p.unmap(),this.buffers.push(d,p);let m=e.createBindGroup({layout:a,entries:[{binding:0,resource:{buffer:p}},{binding:1,resource:{buffer:l}},{binding:2,resource:{buffer:d}}]});if(this.levels.push({n:u,blocks:f,group:m}),f===1)break;l=d,c=0,u=f}}encode(e){e.setPipeline(this.scanPipeline);for(let t of this.levels)e.setBindGroup(0,t.group),e.dispatchWorkgroups(t.blocks);e.setPipeline(this.addPipeline);for(let t=this.levels.length-2;t>=0;t--)e.setBindGroup(0,this.levels[t].group),e.dispatchWorkgroups(this.levels[t].blocks)}destroy(){for(let e of this.buffers)e.destroy()}};var qu=`
|
|
237
237
|
alias TopoItem = Contact;
|
|
238
238
|
|
|
239
239
|
fn topoCount() -> u32 {
|
|
@@ -532,9 +532,9 @@ fn colorScatter(@builtin(global_invocation_id) gid: vec3u, @builtin(workgroup_id
|
|
|
532
532
|
let slot = atomicLoad(&color[params.colorHistOffset + col * params.colorGroups + wid.x]) + state(params.stateBOffset, b);
|
|
533
533
|
atomicStore(&color[params.colorBodiesOffset + slot], b);
|
|
534
534
|
}
|
|
535
|
-
`,qr=Ti(Ut,
|
|
535
|
+
`,qr=Ti(Ut,qu);var Vu=jt(),zt=ae(),Us=ae(),ji=ae(),Ia=ae(),lr=ae(),Ra=ae(),ut=ae(),Ns=ae(),Vr=ae(),ye=S(),ze=S(),ve=S(),li=S(),cr=S();function Oa(s,e,t){return Ce(s,-t[e],-t[e],-t[e]),s[e]+=t[0],s[3+e]+=t[1],s[6+e]+=t[2],s}var qs=(s,e,t)=>lt(s,Bt(e[0],t),Bt(e[1],t),Bt(e[2],t)),ke=class extends Ft{rA;rB;C0Lin=S();C0Ang=S();penaltyLin=S();penaltyAng=S();lambdaLin=S();lambdaAng=S();stiffnessLin;stiffnessAng;fracture;torqueArm;broken=!1;constructor(e,t,i,r,o,a=1/0,n=0,l=1/0){super(e,t,i),this.rA=S(r[0],r[1],r[2]),this.rB=S(o[0],o[1],o[2]),this.stiffnessLin=a,this.stiffnessAng=n,this.fracture=l;let c=t?t.size:[0,0,0];this.torqueArm=Ne([c[0]+i.size[0],c[1]+i.size[1],c[2]+i.size[2]])}evaluateLin(e){let{bodyA:t,bodyB:i}=this;return t?Gt(li,t.positionLin,t.positionAng,this.rA):li.set(this.rA),J(e,li,Gt(cr,i.positionLin,i.positionAng,this.rB))}evaluateAng(e){return ct(e,this.bodyA?this.bodyA.positionAng:Vu,this.bodyB.positionAng),ne(e,e,this.torqueArm)}initialize(){let{solver:e}=this;this.evaluateLin(this.C0Lin),this.evaluateAng(this.C0Ang);for(let t=0;t<3;t++)this.lambdaLin[t]=this.lambdaLin[t]*e.alpha*e.gamma,this.lambdaAng[t]=this.lambdaAng[t]*e.alpha*e.gamma,this.penaltyLin[t]=ce(this.penaltyLin[t]*e.gamma,Et,Ve),this.penaltyAng[t]=ce(this.penaltyAng[t]*e.gamma,Et,Ve);return qs(this.penaltyLin,this.penaltyLin,this.stiffnessLin),qs(this.penaltyAng,this.penaltyAng,this.stiffnessAng),!this.broken}updatePrimal(e,t,i){let r=e===this.bodyA;if(Ne(this.penaltyLin)>0){Ce(zt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(ye),this.stiffnessLin===1/0&&D(ye,ye,this.C0Lin,-t),ue(ze,zt,ye),D(ze,ze,this.lambdaLin,1);let o=r?1:-1;Ce(Us,o,o,o),r?xa(ji,ee(ve,le(ve,this.bodyA.positionAng,this.rA))):xa(ji,le(ve,this.bodyB.positionAng,this.rB)),_i(Ia,Us),_i(lr,ji),Ke(Ra,lr,zt),qe(i.lhsLin,Ke(ut,Ke(ut,Ia,zt),Us)),qe(i.lhsAng,Ke(ut,Ra,ji)),qe(i.lhsCross,Ke(ut,Ra,Us)),r?le(ve,this.bodyA.positionAng,this.rA):ee(ve,le(ve,this.bodyB.positionAng,this.rB)),Ei(Ns,Oa(Vr,0,ve),ze[0]),qe(Ns,Ei(Vr,Oa(Vr,1,ve),ze[1])),qe(Ns,Ei(Vr,Oa(Vr,2,ve),ze[2])),qe(i.lhsAng,Qn(ut,Ns)),D(i.rhsLin,i.rhsLin,ue(ve,Ia,ze),1),D(i.rhsAng,i.rhsAng,ue(ve,lr,ze),1)}if(Ne(this.penaltyAng)>0){Ce(zt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(ye),this.stiffnessAng===1/0&&D(ye,ye,this.C0Ang,-t),ue(ze,zt,ye),D(ze,ze,this.lambdaAng,1);let o=(r?1:-1)*this.torqueArm;Ce(ji,o,o,o),_i(lr,ji),qe(i.lhsAng,Ke(ut,Ke(ut,lr,zt),ji)),D(i.rhsAng,i.rhsAng,ue(ve,lr,ze),1)}}updateDual(e){let{solver:t}=this;Ne(this.penaltyLin)>0&&(Ce(zt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(ye),this.stiffnessLin===1/0&&(D(ye,ye,this.C0Lin,-e),ue(ze,zt,ye),D(this.lambdaLin,ze,this.lambdaLin,1)),D(this.penaltyLin,this.penaltyLin,ya(ve,ye),t.betaLin),qs(this.penaltyLin,this.penaltyLin,Bt(this.stiffnessLin,Ve))),Ne(this.penaltyAng)>0&&(Ce(zt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(ye),this.stiffnessAng===1/0&&(D(ye,ye,this.C0Ang,-e),ue(ze,zt,ye),D(this.lambdaAng,ze,this.lambdaAng,1)),D(this.penaltyAng,this.penaltyAng,ya(ve,ye),t.betaAng),qs(this.penaltyAng,this.penaltyAng,Bt(this.stiffnessAng,Ve))),Ne(this.lambdaAng)>this.fracture*this.fracture&&(this.penaltyLin.fill(0),this.penaltyAng.fill(0),this.lambdaLin.fill(0),this.lambdaAng.fill(0),this.broken=!0)}},ft=class extends Ft{rA;rB;rest;stiffness;constructor(e,t,i,r,o,a,n=-1){super(e,t,i),this.rA=S(r[0],r[1],r[2]),this.rB=S(o[0],o[1],o[2]),this.stiffness=a,this.rest=n,this.rest<0&&(Gt(li,t.positionLin,t.positionAng,this.rA),Gt(cr,i.positionLin,i.positionAng,this.rB),this.rest=rr(J(ye,li,cr)))}initialize(){return!0}updatePrimal(e,t,i){let r=this.bodyA,o=this.bodyB;Gt(li,r.positionLin,r.positionAng,this.rA),Gt(cr,o.positionLin,o.positionAng,this.rB);let a=J(ye,li,cr),n=rr(a);if(n<=1e-6)return;let l=_t(ze,a,n),c=n-this.rest,u=this.stiffness*c,f=li,d=cr;e===r?(le(ve,r.positionAng,this.rA),f.set(l),Dt(d,ve,l)):(le(ve,o.positionAng,this.rB),ee(f,l),ee(d,Dt(d,ve,l))),qe(i.lhsLin,Ei(ut,Ls(ut,f,f),this.stiffness)),qe(i.lhsAng,Ei(ut,Ls(ut,d,d),this.stiffness)),qe(i.lhsCross,Ei(ut,Ls(ut,d,f),this.stiffness)),D(i.rhsLin,i.rhsLin,ne(ve,f,u),1),D(i.rhsAng,i.rhsAng,ne(ve,d,u),1)}updateDual(){}},Vs=class extends Ft{initialize(){return!0}updatePrimal(){}updateDual(){}};var We=40,ur=0,Wr=4,wl=8,Bl=12,Ta=16,ja=20,Cl=24,Sl=28,Da=32,Ga=36,pe=32,ci=0,Hr=4,fr=8,dr=12,kl=16,Pl=20,ui=24,hr=28,Nt=8;var Ws=4,Ht=16,pr=0,Hs=4,$s=8,Js=12,$r=2147483648,mr=6,Wu=7,Ll=0,Fa=1,Ua=2,Xs=3,Jr=1,gr=2,Xr=1,Yr=2,Qr=4,Kr=8,Zr=16,Hu=.1,_l=.002,El=.002,$u=.05,es=44,ge=`
|
|
536
536
|
// Shared by every 3D module (avbd3d/gpu/layout.ts)
|
|
537
|
-
${
|
|
537
|
+
${za}
|
|
538
538
|
const T_JOINT = ${Jr};
|
|
539
539
|
const T_SPRING = ${gr};
|
|
540
540
|
|
|
@@ -545,12 +545,12 @@ const PIVOT_FLOOR = 1e-5;
|
|
|
545
545
|
const COLLISION_MARGIN = 0.01;
|
|
546
546
|
const STICK_THRESH = 0.00001;
|
|
547
547
|
|
|
548
|
-
const SHAPE_SPHERE = ${
|
|
549
|
-
const SHAPE_SAIL = ${
|
|
548
|
+
const SHAPE_SPHERE = ${Fa}.0;
|
|
549
|
+
const SHAPE_SAIL = ${Ua}.0;
|
|
550
550
|
const SHAPE_HULL = ${Xs}.0;
|
|
551
551
|
const STICK_BIT = ${$r}u;
|
|
552
552
|
const C_MANIFOLDS = ${mr}u;
|
|
553
|
-
const C_PREV_MANIFOLDS = ${
|
|
553
|
+
const C_PREV_MANIFOLDS = ${Wu}u;
|
|
554
554
|
/** Feature key of every contact involving a sphere (one contact per pair). */
|
|
555
555
|
const SPHERE_FEATURE = 3u << 24u;
|
|
556
556
|
|
|
@@ -559,8 +559,8 @@ const FLAG_FACE_BIAS = ${Yr}u;
|
|
|
559
559
|
const FLAG_REUSE_CONTACTS = ${Qr}u;
|
|
560
560
|
const FLAG_START_AT_REST = ${Kr}u;
|
|
561
561
|
const FLAG_MASS_PENALTY = ${Zr}u;
|
|
562
|
-
const REST_SPEED = ${
|
|
563
|
-
const NEAREST_FRACTION = ${
|
|
562
|
+
const REST_SPEED = ${Hu};
|
|
563
|
+
const NEAREST_FRACTION = ${$u};
|
|
564
564
|
|
|
565
565
|
struct Body {
|
|
566
566
|
pos: vec4f, // xyz, w: friction
|
|
@@ -699,7 +699,7 @@ fn jointActive(j: u32) -> bool {
|
|
|
699
699
|
let k = joints[j];
|
|
700
700
|
return info[j].x != T_NONE && (k.penLin.w != 0.0 || k.penAng.w != 0.0);
|
|
701
701
|
}
|
|
702
|
-
`,
|
|
702
|
+
`,Na=`
|
|
703
703
|
fn lanesFor(bodies: u32) -> u32 {
|
|
704
704
|
let rule = params.primalLanes;
|
|
705
705
|
if (bodies >= (1u << (rule & 255u))) { return 1u; }
|
|
@@ -711,7 +711,7 @@ fn lanesFor(bodies: u32) -> u32 {
|
|
|
711
711
|
fn colorThreads(bodies: u32) -> u32 {
|
|
712
712
|
return bodies * lanesFor(bodies);
|
|
713
713
|
}
|
|
714
|
-
`,Ys={counter:"C_MANIFOLDS",prevCounter:"C_PREV_MANIFOLDS",capacity:"params.manifoldCapacity",colorThreads:
|
|
714
|
+
`,Ys={counter:"C_MANIFOLDS",prevCounter:"C_PREV_MANIFOLDS",capacity:"params.manifoldCapacity",colorThreads:Na};var Ml=`
|
|
715
715
|
const MAX_CLIP = 64u;
|
|
716
716
|
const NO_HULL = 0xffffffffu;
|
|
717
717
|
|
|
@@ -1878,7 +1878,7 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
1878
1878
|
}
|
|
1879
1879
|
`,zl=yr(!1),Il=yr(!0);var Ge=`
|
|
1880
1880
|
${ge}
|
|
1881
|
-
${
|
|
1881
|
+
${Na}
|
|
1882
1882
|
|
|
1883
1883
|
// Per-dispatch constants, selected with a dynamic uniform offset
|
|
1884
1884
|
struct PassConstants {
|
|
@@ -2418,7 +2418,7 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
2418
2418
|
bodies[i].vel.w = prevUp;
|
|
2419
2419
|
}
|
|
2420
2420
|
}
|
|
2421
|
-
`;function Ks(s,e){let t=0,i=s.length-1;for(;t<i;){let r=s[e],o=t,n=i;do{for(;s[o]<r;)o++;for(;r<s[n];)n--;if(o<=n){let a=s[o];s[o]=s[n],s[n]=a,o++,n--}}while(o<=n);n<e&&(t=o),e<o&&(i=n)}return s[e]}function Vn(s){let e=s.length,t=e>0?Ks(s,e>>1):1,i=0;for(let r=0;r<e;r++)s[r]<=2*t&&(i=Math.max(i,s[r]));return e>64&&(i=Math.max(i,Ks(s,e-64-1))),i}var $t=s=>s===1/0?3e38:s===-1/0?-3e38:s,dt=s=>Math.ceil(s/64),Ol=s=>2**Math.ceil(Math.log2(Math.max(s,2))),Wu=s=>Math.max(s,1)*We*4,Hu=8,Zs=3,$u=8,Ju=1.6,Xu=.8,Yu=2,Qu=[0,0,1],Ku=()=>({...En(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function Zu(s,e){let t=s.vertices.length/3,i=s.faces.length,r=s.faces.reduce((m,h)=>m+h.verts.length,0),o=Math.ceil(r/4),n=s.edges.length,a=new Float32Array((2+t+2*i+o+n)*4),l=new Uint32Array(a.buffer),c=e+2,u=c+t,f=u+2*i,d=f+o;l.set([c,t,u,i,d,n,f,0],0);for(let m=0;m<t;m++)a.set(s.vertices.subarray(m*3,m*3+3),(2+m)*4);let p=0;return s.faces.forEach((m,h)=>{let g=(2+t+2*h)*4;a.set(m.normal,g),a[g+3]=m.d,l[g+4]=p,l[g+5]=m.verts.length;for(let b of m.verts)l[(2+t+2*i)*4+p++]=b}),s.edges.forEach((m,h)=>l.set(m,(2+t+2*i+o+h)*4)),a}var Wn=.001;function ef(s,e){let t=s.length,i=s.map(y=>{let A=y;return[A.group??4294967295,A.collidesWith??4294967295]}),r=i.map(([y,A])=>y!==0&&A!==0),o=s.map(y=>[0,1,2].map(A=>{let v=[0,0,0];return v[A]=1,ae(S(),y.positionAng,v)})),n=s.map((y,A)=>{if(De(y))return[y.radius,y.radius,y.radius];let v=[0,0,0];for(let x=0;x<3;x++)for(let w=0;w<3;w++)v[w]+=y.size[x]*.5*Math.abs(o[A][x][w]);return v}),a=Vn(Float64Array.from(s,y=>y.radius)),l=Math.max(2*a,.001),c=(y,A)=>{let v=s[y],x=s[A];if(v.mass<=0&&x.mass<=0||!(i[y][0]&i[A][1])||!(i[A][0]&i[y][1]))return!1;let w=v.positionLin[0]-x.positionLin[0],B=v.positionLin[1]-x.positionLin[1],L=v.positionLin[2]-x.positionLin[2];if(!(Math.abs(w)<=n[y][0]+n[A][0]&&Math.abs(B)<=n[y][1]+n[A][1]&&Math.abs(L)<=n[y][2]+n[A][2]))return!1;let M=v.radius+x.radius;return w*w+B*B+L*L<=M*M},u=(y,A)=>y[0]*A[0]+y[1]*A[1]+y[2]*A[2],f=(y,A)=>{let v=s[y],x=s[A],w=[0,1,2].map(E=>x.positionLin[E]-v.positionLin[E]),[B,L]=[De(v),De(x)];if(B&&L)return Math.hypot(w[0],w[1],w[2])<=v.radius+x.radius+Wn;if(B||L){let[E,k,P]=B?[A,v.radius,-1]:[y,x.radius,1],C=0;for(let _=0;_<3;_++){let z=Math.abs(P*u(w,o[E][_]))-s[E].size[_]*.5;z>0&&(C+=z*z)}return C<=(k+Wn)**2}let M=(E,k)=>s[E].size[0]*.5*Math.abs(u(o[E][0],k))+s[E].size[1]*.5*Math.abs(u(o[E][1],k))+s[E].size[2]*.5*Math.abs(u(o[E][2],k)),I=E=>{let k=Math.hypot(E[0],E[1],E[2]);return k<1e-6?!1:Math.abs(u(w,E))-M(y,E)-M(A,E)>Wn*k};for(let E=0;E<3;E++)if(I(o[y][E])||I(o[A][E]))return!1;for(let E of o[y])for(let k of o[A])if(I([E[1]*k[2]-E[2]*k[1],E[2]*k[0]-E[0]*k[2],E[0]*k[1]-E[1]*k[0]]))return!1;return!0},d=new Set,p=new Map,m=(y,A,v)=>(y*73856093^A*19349663^v*83492791)>>>0,h=y=>[0,1,2].map(A=>Math.floor(s[y].positionLin[A]/l));for(let y=0;y<t;y++){if(!r[y])continue;if(s[y].radius>a){d.add(y);continue}let[A,v,x]=h(y),w=m(A,v,x),B=p.get(w);B?B.push(y):p.set(w,[y])}let g=0,b=0;for(let y=0;y<t;y++){if(!r[y]||d.has(y))continue;let[A,v,x]=h(y);for(let w=-1;w<=1;w++)for(let B=-1;B<=1;B++)for(let L=-1;L<=1;L++)for(let M of p.get(m(A+L,v+B,x+w))??[])M<y&&c(y,M)&&(g++,f(y,M)&&(b++,e[y]++,e[M]++))}for(let y of d)for(let A=0;A<t;A++)r[A]&&A!==y&&(!d.has(A)||A<y)&&c(y,A)&&(g++,f(y,A)&&(b++,e[y]++,e[A]++));return{pairs:g,touching:b}}function tf(s){let e=[1/0,1/0,1/0],t=[-1/0,-1/0,-1/0];for(let o of s)for(let n=0;n<3;n++)e[n]=Math.min(e[n],o.positionLin[n]),t[n]=Math.max(t[n],o.positionLin[n]);let i=o=>{let n=o&1023;return n=(n|n<<16)&50331903,n=(n|n<<8)&50393103,n=(n|n<<4)&51130563,n=(n|n<<2)&153391689,n},r=s.map(o=>{let n=[0,1,2].map(a=>Math.min(1023,Math.floor((o.positionLin[a]-e[a])/Math.max(t[a]-e[a],1e-9)*1024)));return(i(n[0])|i(n[1])<<1|i(n[2])<<2)>>>0});return s.map((o,n)=>n).sort((o,n)=>r[o]-r[n]||o-n)}var Pe=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,eo=class{device;params=Ku();bodyCount=0;bodyCapacity;bodies=[];hulls;hullBuffer;get hullStorage(){return this.hullBuffer}hullCapacity=4096;hullTop=0;hullSlots=new Map;contactShaders;hullShaders=!1;hullFree=[];refToGpu;jointCount=0;jointCapacity=0;info=new Int32Array(0);pairCapacity=0;manifoldCapacity=0;contactCapacity=0;colorCap=12;fixedColors=null;primalLanes=[2**31,2**15,2**12];splitPasses=!1;shrinkVotes=0;colorRounds;get colorHistOffset(){return 3*this.bodyCapacity+65}bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;manifoldBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;filterBuffer;get filterStorage(){return this.filterBuffer}counterBuffer;get contactStorage(){let e=1-this.parity;return{manifolds:this.manifoldBuffers[e],contacts:this.contactBuffers[e],counters:this.counterBuffer}}argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;refBuffer;stepCount=0;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;radiiDirty=!0;noCollideDirty=!0;large=[];entries=[];pendingEntries=[];releasedSlots=new Set;freeSlots=[];layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){this.device=e;let{dt:r,gravity:o,iterations:n,alpha:a,betaLin:l,betaAng:c,gamma:u}=t;if(Object.assign(this.params,{dt:r,gravity:o,iterations:n,alpha:a,betaLin:l,betaAng:c,gamma:u,up:Qu}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(We*4)):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies 3d",size:Wu(this.bodyCapacity),usage:Pe()}),this.filterBuffer=e.createBuffer({label:"collision filters",size:Math.max(this.bodyCapacity,1)*8,usage:Pe()}),e.queue.writeBuffer(this.filterBuffer,0,new Uint32Array(Math.max(this.bodyCapacity,1)*2).fill(4294967295));let f=this.bodyCapacity;this.tableSize=Ol(2*f),this.gridBuffer=e.createBuffer({label:"grid 3d",size:(2*this.tableSize+1+4*f)*4,usage:Pe()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:Pe()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:Pe()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(f/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*f+65+64*this.colorGroups+1)*4,usage:Pe()}),this.paramsBuffer=e.createBuffer({label:"params 3d",size:es*4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.refBuffer=e.createBuffer({label:"reference poses",size:f*32,usage:Pe()}),this.hulls=(i.hulls??!0)&&e.limits.maxStorageBuffersPerShaderStage>=9,this.hullBuffer=e.createBuffer({label:"hulls",size:this.hullCapacity*16,usage:Pe()}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(f).fill(255));let d=2*Se.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:d}),resolve:e.createBuffer({size:d*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:d*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(i.shaders??{}),this.gridScan=new tt(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new tt(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1);let p=i.spatialSort===!1?t.bodies.map((w,B)=>B):tf(t.bodies);this.refToGpu=new Int32Array(p.length),p.forEach((w,B)=>this.refToGpu[w]=B);let m=new Map(t.bodies.map((w,B)=>[w,this.refToGpu[B]])),h=t.forces.filter(w=>w instanceof ke||w instanceof ft);for(let w of t.forces){if(!(w instanceof Vs))continue;let B=m.get(w.bodyA),L=m.get(w.bodyB);this.pendingEntries.push([Math.max(B,L),Math.min(B,L),4294967295])}this.allocateJoints(Math.max(h.length+256,i.capacity?.joints??0));for(let w of h)this.writeJoint(this.jointCount++,w,m);let g=new Int32Array(t.bodies.length),{pairs:b,touching:y}=ef(t.bodies,g),A=new Map(t.bodies.map((w,B)=>[w,B]));for(let w of h)w.bodyA&&g[A.get(w.bodyA)]++,g[A.get(w.bodyB)]++;let v=0;t.bodies.forEach((w,B)=>{w.mass>0&&(v=Math.max(v,g[B]))}),this.colorCap=Math.min(64,Math.max($u,v+1+Zs,i.capacity?.colors??0));let x=i.capacity??{};this.allocateContacts(Math.max(8192,4*f,Hu*y,x.contacts??0),Math.max(4096,4*f,2*b,x.pairs??0),Math.max(4096,f,Ju*y,x.manifolds??0)),this.writeBodies(0,p.map(w=>t.bodies[w]))}gpuIndex(e){return e<this.refToGpu.length?this.refToGpu[e]:e}createLayouts(){let e=this.device,t=(n,a)=>e.createBindGroupLayout({label:n,entries:a.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",o="uniform";return{broad:t("broadphase 3d",[o,i,r,r,r,i,i,i]),contacts:t("contacts 3d",[o,i,i,r,i,r,i,r,r,...this.hulls?[i]:[]]),topo:t("topology 3d",[o,i,i,i,i,r,r,r]),solve:t("solve 3d",[o,r,r,i,r,i,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args 3d",[o,i,i,r]),refs:t("refs 3d",[o,r,r])}}createPipelines(e){let t=this.device,i=(o,n,a)=>{let l=t.createShaderModule({code:o}),c=t.createPipelineLayout({bindGroupLayouts:n});for(let u of a)this.pipes[u]=t.createComputePipeline({label:u,layout:c,compute:{module:l,entryPoint:u}})},r=this.layouts;i(fi,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(e.refs??Qs,[r.refs],["updateRefs"]),i(e.contacts??zl,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:o=>i(o,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(e.topology??Ti(ge,br),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??Ge,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(Nr(ge,Ys),[r.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints 3d",size:Math.max(e,1)*pe*4,usage:Pe()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:Pe()});if(this.jointBuffer){let n=t.createCommandEncoder();n.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*pe*4),n.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([n.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}let o=new Int32Array(e*4);o.set(this.info.subarray(0,Math.min(this.info.length,o.length))),this.info=o,this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t,i){let r=this.device,o=r.limits.maxStorageBufferBindingSize,n=Math.min(Math.ceil(e),Math.floor(o/(Ht*4))),a=Math.min(Math.ceil(t),Math.floor(o/8)),l=Math.min(Math.ceil(i),a,Math.floor(o/(Nt*4))),c={contacts:this.contactBuffers,manifolds:this.manifoldBuffers,contactCapacity:this.contactCapacity,manifoldCapacity:this.manifoldCapacity};for(let u of[this.pairBuffer,this.tableBuffer])u?.destroy();if(this.contactCapacity=n,this.pairCapacity=a,this.manifoldCapacity=l,this.hashSize=Ol(2*l),this.pairBuffer=r.createBuffer({label:"pairs",size:a*8,usage:Pe()}),this.tableBuffer=r.createBuffer({label:"manifold hash",size:this.hashSize*4,usage:Pe()}),this.contactBuffers=[0,1].map(u=>r.createBuffer({label:`contacts 3d ${u}`,size:n*Ht*4,usage:Pe()})),this.manifoldBuffers=[0,1].map(u=>r.createBuffer({label:`manifolds 3d ${u}`,size:l*Nt*4,usage:Pe()})),c.contacts){let u=r.createCommandEncoder(),f=Math.min(c.contactCapacity,n)*Ht*4,d=Math.min(c.manifoldCapacity,l)*Nt*4;c.contacts.forEach((p,m)=>u.copyBufferToBuffer(p,0,this.contactBuffers[m],0,f)),c.manifolds.forEach((p,m)=>u.copyBufferToBuffer(p,0,this.manifoldBuffers[m],0,d)),r.queue.submit([u.finish()]),[...c.contacts,...c.manifolds].forEach(p=>p.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.manifoldCapacity))*4,usage:Pe()}),this.adjScan?.destroy(),this.adjScan=new tt(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:Pe()});let i=(d,p)=>e.createBindGroup({layout:d,entries:p.map((m,h)=>({binding:h,resource:{buffer:m}}))}),r=this.paramsBuffer,[o,n]=this.contactBuffers,[a,l]=this.manifoldBuffers,c=this.adjBuffer,u=this.tableBuffer,f=this.counterBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,f,this.staticBuffer,this.jointBuffer,this.filterBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,o,n,a,l,u,f,...this.hulls?[this.hullBuffer]:[]]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,n,o,l,a,u,f,...this.hulls?[this.hullBuffer]:[]])],topo:[a,l].map(d=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,d,f,c,this.colorBuffer])),solve:[0,1].map(d=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,[o,n][d],c,this.colorBuffer,f,[a,l][d]])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer]),refs:i(this.layouts.refs,[r,this.bodyBuffer,this.refBuffer])}}writeBodies(e,t){let i=new Float32Array(Math.max(t.length,1)*We);t.forEach((r,o)=>{let n=this.pack(r,i,o*We,this.bodies[e+o]?.hull);this.bodies[e+o]?.radius!==n.radius&&(this.radiiDirty=!0),this.bodies[e+o]=n}),this.device.queue.writeBuffer(this.bodyBuffer,e*We*4,i,0,t.length*We)}rewriteFixed(e,t,i,r,o){if(!e.length)return;let n=new Float32Array(We),a={positionLin:[0,0,0],positionAng:[0,0,0,1],initialLin:[0,0,0],initialAng:[0,0,0,1],inertialLin:[0,0,0],inertialAng:[0,0,0,1],velocityLin:[0,0,0],prevVelocityLin:[0,0,0],velocityAng:[0,0,0],size:[i[0],i[1],i[2]],mass:0,moment:[0,0,0],friction:r,radius:Math.hypot(i[0]/2,i[1]/2,i[2]/2)},l=this.pack(a,n,0);for(let c=0;c<e.length;){let u=c+1;for(;u<e.length&&e[u]===e[u-1]+1;)u++;let f=new Float32Array((u-c)*We);for(let d=c;d<u;d++){let p=(d-c)*We;f.set(n,p);for(let h=0;h<3;h++)f[p+ur+h]=t[3*d+h];if(o)for(let h=0;h<4;h++)f[p+Wr+h]=o[4*d+h];let m=this.bodies[e[d]];m?.radius!==l.radius&&(this.radiiDirty=!0),m?.hull&&this.releaseHull(m.hull),this.bodies[e[d]]={...l,size:[...l.size]}}this.device.queue.writeBuffer(this.bodyBuffer,e[c]*We*4,f),this.device.queue.writeBuffer(this.colorBuffer,e[c]*4,new Uint32Array(u-c).fill(255)),c=u}}pack(e,t,i,r){{t.set(e.positionLin,i+ur),t[i+ur+3]=e.friction,t.set(e.positionAng,i+Wr),t.set(e.size,i+jn),t[i+jn+3]=e.mass,t.set(e.moment,i+Dn),t[i+Dn+3]=e.radius,t.set(e.initialLin,i+wl),t.set(e.initialAng,i+Bl),t.set(e.inertialLin,i+Cl),t.set(e.inertialAng,i+Sl),t.set(e.velocityLin,i+Gn);let o=this.params.up;t[i+Gn+3]=e.prevVelocityLin[0]*o[0]+e.prevVelocityLin[1]*o[1]+e.prevVelocityLin[2]*o[2],t.set(e.velocityAng,i+Fn);let n=De(e),a=this.hulls?Ct(e):void 0;r&&r!==a&&this.releaseHull(r);let l=a?r===a?this.hullSlots.get(a).offset:this.acquireHull(a):-1;return t[i+Fn+3]=n?Un:l>=0?Xs+l:or(e)?Nn:Ll,{radius:e.radius,dynamic:e.mass>0,sphere:n,size:[e.size[0],e.size[1],e.size[2]],hull:a}}}writeJoint(e,t,i){let r=new Float32Array(pe),o=t.bodyA?i.get(t.bodyA):-1,n=i.get(t.bodyB);t instanceof ke?(r.set(t.penaltyLin,ci),r[ci+3]=$t(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,Hr),r[Hr+3]=$t(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,fr),r[fr+3]=$t(t.fracture),r.set(t.lambdaAng,dr),r[dr+3]=t.torqueArm,r.set(t.C0Lin,kl),r.set(t.C0Ang,Pl),r.set(t.rA,ui),r.set(t.rB,hr)):(r[ci+3]=t.stiffness,r.set(t.rA,ui),r[ui+3]=t.rest,r.set(t.rB,hr)),this.info.set([t instanceof ke?Jr:gr,o,n,0],e*4),this.device.queue.writeBuffer(this.jointBuffer,e*pe*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info,e*4,4),this.noCollide(o,n,e)}noCollide(e,t,i){e<0||(this.pendingEntries.push([Math.max(e,t),Math.min(e,t),i]),this.noCollideDirty=!0)}uploadStatics(){if(this.radiiDirty){let r=this.bodyCount,o=new Float64Array(r);for(let c=0;c<r;c++)o[c]=this.bodies[c].radius;let n=Vn(o),a=1/0;for(let c=0;c<r;c++)o[c]>n&&(a=Math.min(a,o[c]));let l=a===1/0?n*1.5:(n+a)/2;this.maxSmallRadius=l,this.cellSize=Math.max(2*n*(1+1e-4),.001),this.large=[];for(let c=0;c<r;c++)this.bodies[c].radius>l&&this.large.push(c);this.radiiDirty=!1}if(this.noCollideDirty){let r=(l,c)=>l[0]-c[0]||l[1]-c[1],o=this.releasedSlots.size?this.entries.filter(l=>!this.releasedSlots.has(l[2])):this.entries;this.releasedSlots.clear();let n=this.pendingEntries.sort(r);this.pendingEntries=[];let a=[];for(let l=0,c=0;l<o.length||c<n.length;)c>=n.length||l<o.length&&r(o[l],n[c])<=0?a.push(o[l++]):a.push(n[c++]);this.entries=a,this.noCollideDirty=!1}let{large:e,entries:t}=this;this.largeCount=e.length,this.noCollideCount=t.length;let i=new Uint32Array(Math.max(e.length+3*t.length,4));i.set(e,0),t.forEach((r,o)=>i.set(r,e.length+3*o)),this.staticBuffer.size<i.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:i.byteLength,usage:Pe()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,i)}seedFrom(e){if(this.refToGpu.some((m,h)=>m!==h))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((m,h)=>[m,h]));this.writeBodies(0,e.bodies);let i=0;for(let m of e.forces)(m instanceof ke||m instanceof ft)&&this.writeJoint(i++,m,t);if(i!==this.jointCount)throw new Error("seedFrom: joints differ from the reference");let r=e.forces.filter(m=>m instanceof ni),o=r.reduce((m,h)=>m+h.contacts.length,0);if(o>this.contactCapacity||r.length>this.manifoldCapacity)throw new Error("seedFrom: too many contacts");let n=new ArrayBuffer(Math.max(o,1)*Ht*4),a=new Uint32Array(n),l=new Float32Array(n),c=new ArrayBuffer(Math.max(r.length,1)*Nt*4),u=new Uint32Array(c),f=new Float32Array(c),d=0;r.forEach((m,h)=>{u.set([t.get(m.bodyA),t.get(m.bodyB),d,m.contacts.length],h*Nt),f.set([m.basis[0],m.basis[1],m.basis[2],m.friction],h*Nt+Ws);for(let g of m.contacts){let b=d++*Ht;l.set(g.rA,b+pr),a[b+pr+3]=g.feature>>>0|(g.stick?$r:0),l.set([...g.rB,g.C0[0]],b+Hs),l.set([...g.penalty,g.C0[1]],b+$s),l.set([...g.lambda,g.C0[2]],b+Js)}}),this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,n),this.device.queue.writeBuffer(this.manifoldBuffers[1-this.parity],0,c);let p=new Uint32Array(16);p[1]=o,p[mr]=r.length,this.device.queue.writeBuffer(this.counterBuffer,0,p)}sequentialColors(){let e=new Uint32Array(this.bodyCount).fill(255),t=0;for(let i=this.bodyCount-1;i>=0;i--)this.bodies[i].dynamic&&(e[i]=t++);if(t>64)throw new Error(`sequentialColors: ${t} dynamic bodies, at most ${64}`);return e}acquireHull(e){!this.hullShaders&&!this.contactShaders.custom&&(this.contactShaders.make(Il),this.hullShaders=!0);let t=this.hullSlots.get(e);if(t)return t.refs++,t.offset;let i=2+e.vertices.length/3+2*e.faces.length+Math.ceil(e.faces.reduce((n,a)=>n+a.verts.length,0)/4)+e.edges.length,r=-1,o=this.hullFree.findIndex(n=>n.size>=i);if(o>=0){let n=this.hullFree[o];r=n.offset,n.size===i?this.hullFree.splice(o,1):Object.assign(n,{offset:n.offset+i,size:n.size-i})}else r=this.hullTop,this.hullTop+=i,this.hullTop>this.hullCapacity&&this.growHulls(this.hullTop);if(r+Xs>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,Zu(e,r)),this.hullSlots.set(e,{offset:r,size:i,refs:1}),r}releaseHull(e){let t=this.hullSlots.get(e);if(!t||--t.refs>0)return;this.hullSlots.delete(e),this.hullFree.push({offset:t.offset,size:t.size}),this.hullFree.sort((r,o)=>r.offset-o.offset);let i=[];for(let r of this.hullFree){let o=i[i.length-1];o&&o.offset+o.size===r.offset?o.size+=r.size:i.push({...r})}this.hullFree=i}growHulls(e){let t=this.hullCapacity;for(;t<e;)t*=2;let i=this.device.createBuffer({label:"hulls",size:t*16,usage:Pe()}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(this.hullBuffer,0,i,0,this.hullCapacity*16),this.device.queue.submit([r.finish()]),this.hullBuffer.destroy(),this.hullBuffer=i,this.hullCapacity=t,this.contactBuffers&&this.rebuildBindings()}addBody(e){return this.addBodies([e])}addBodies(e){let t=Math.min(e.length,this.bodyCapacity-this.bodyCount);if(t<=0)return-1;let i=this.bodyCount;return this.bodyCount+=t,this.writeBodies(i,e.slice(0,t)),this.device.queue.writeBuffer(this.colorBuffer,i*4,new Uint32Array(t).fill(255)),this.adjScan.destroy(),this.adjScan=new tt(this.device,this.adjBuffer,0,this.bodyCount+1),i}appendJoint(e,t,i,r,o,n){let a=this.jointCount;a>=this.jointCapacity&&this.allocateJoints(this.jointCapacity*2),this.jointCount++;let l=new Float32Array(pe);l[ci+3]=$t(o),l[Hr+3]=$t(n),l[fr+3]=3e38,l.set([i[0],i[1],i[2]],ui),l.set([r[0],r[1],r[2]],hr);let c=e>=0?this.bodies[e].size:[0,0,0],u=this.bodies[t].size;return l[dr+3]=(c[0]+u[0])**2+(c[1]+u[1])**2+(c[2]+u[2])**2,this.info.set([Jr,e,t,0],a*4),this.device.queue.writeBuffer(this.jointBuffer,a*pe*4,l),this.device.queue.writeBuffer(this.infoBuffer,a*16,this.info,a*4,4),this.noCollide(e,t,a),a}appendJoints(e,t,i=!1){return this.appendConstraints(e.length,(r,o)=>{let{a:n,b:a,rA:l,rB:c,angular:u}=e[r];o[ci+3]=3e38,o[Hr+3]=u===void 0?3e38:$t(u),o[fr+3]=i?-$t(t):$t(t),o.set([l[0],l[1],l[2]],ui),o.set([c[0],c[1],c[2]],hr);let[f,d]=[this.bodies[n].size,this.bodies[a].size];return o[dr+3]=(f[0]+d[0])**2+(f[1]+d[1])**2+(f[2]+d[2])**2,[Jr,n,a]})}appendSprings(e){return this.appendConstraints(e.length,(t,i)=>{let{a:r,b:o,rA:n,rB:a,stiffness:l,rest:c}=e[t];return i[ci+3]=$t(l),i.set([n[0],n[1],n[2],c],ui),i.set([a[0],a[1],a[2]],hr),[gr,r,o]})}appendConstraints(e,t){let i=[];if(this.freeSlots.length)for(this.freeSlots.sort((a,l)=>l-a);i.length<e&&this.freeSlots.length;)i.push(this.freeSlots.pop());let r=e-i.length,o=this.jointCapacity;for(;this.jointCount+r>o;)o*=2;o>this.jointCapacity&&this.allocateJoints(o);for(let a=0;a<r;a++)i.push(this.jointCount++);let n=new Float32Array(e*pe);for(let a=0;a<e;a++){let[l,c,u]=t(a,n.subarray(a*pe,(a+1)*pe));this.info.set([l,c,u,0],i[a]*4),this.releasedSlots.delete(i[a]),this.noCollide(c,u,i[a])}for(let a=0;a<e;){let l=a+1;for(;l<e&&i[l]===i[l-1]+1;)l++;this.device.queue.writeBuffer(this.jointBuffer,i[a]*pe*4,n,a*pe,(l-a)*pe),this.device.queue.writeBuffer(this.infoBuffer,i[a]*16,this.info,i[a]*4,(l-a)*4),a=l}return i}releaseJoints(e){let t=[...new Set(Array.from(e))].filter(r=>r<this.jointCount&&this.info[r*4]!==0).sort((r,o)=>r-o);if(!t.length)return;for(let r of t)this.info.set([0,-1,-1,0],r*4),this.releasedSlots.add(r),this.freeSlots.push(r);let i=new Float32Array(0);for(let r=0;r<t.length;){let o=r+1;for(;o<t.length&&t[o]===t[o-1]+1;)o++;i.length<(o-r)*pe&&(i=new Float32Array((o-r)*pe)),this.device.queue.writeBuffer(this.jointBuffer,t[r]*pe*4,i,0,(o-r)*pe),this.device.queue.writeBuffer(this.infoBuffer,t[r]*16,this.info,t[r]*4,(o-r)*4),r=o}this.noCollideDirty=!0}rewriteBodies(e,t){for(let i=0;i<e.length;){let r=i+1;for(;r<e.length&&e[r]===e[r-1]+1;)r++;this.writeBodies(e[i],t.slice(i,r)),this.device.queue.writeBuffer(this.colorBuffer,e[i]*4,new Uint32Array(r-i).fill(255)),i=r}}setFilters(e,t,i){for(let r=0;r<e.length;){let o=r+1;for(;o<e.length&&e[o]===e[o-1]+1;)o++;let n=new Uint32Array(2*(o-r));for(let a=r;a<o;a++)n.set([t[a]>>>0,i[a]>>>0],2*(a-r));this.device.queue.writeBuffer(this.filterBuffer,e[r]*8,n),r=o}}setWorldAnchor(e,t){this.device.queue.writeBuffer(this.jointBuffer,(e*pe+ui)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*pe+ci)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+fr)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+dr)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(es*4),r=new Float32Array(i),o=new Uint32Array(i);r[0]=e.dt,r[1]=e.gravity,r[2]=e.betaLin,r[3]=e.betaAng,r[4]=e.gamma,r[5]=e.alpha,o[6]=(e.matchNearest?Xr:0)|(e.faceBias?Yr:0)|(e.reuseContacts?Qr:0)|(e.startAtRest?Kr:0)|(e.massPenalty?Zr:0),o[7]=this.bodyCount,o[8]=this.jointCount,o[9]=this.colorCap,r[10]=this.cellSize,o[11]=this.tableSize-1,r[12]=this.maxSmallRadius,o[13]=this.largeCount,o[14]=this.noCollideCount,o[15]=this.pairCapacity,o[16]=this.contactCapacity,o[17]=this.hashSize-1,o[18]=t+1,o[19]=2*t+1,o[20]=this.tableSize+1,o[21]=2*this.tableSize+1,o[22]=2*this.tableSize+1+t,o[23]=t,o[24]=this.colorHistOffset,o[25]=2*t,o[26]=this.colorGroups,o[27]=2*t+65,o[28]=this.colorRounds,o[29]=this.manifoldCapacity,o[30]=this.stepCount;let n=a=>Math.min(31,Math.max(0,Math.round(Math.log2(a))));o[31]=n(this.primalLanes[0])|n(this.primalLanes[1])<<8|n(this.primalLanes[2])<<16,r.set([e.windSpeed*Math.cos(e.windAngle),e.windSpeed*Math.sin(e.windAngle),0,e.windPressure,e.windGust],32),r.set([e.up[0],e.up[1],e.up[2],0],40),this.device.queue.writeBuffer(this.paramsBuffer,0,i)}writePassConstants(e,t){let i=this.colorCap+1,r=Math.max(e*i,1);(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let o=new ArrayBuffer(r*256),n=new Uint32Array(o),a=new Float32Array(o);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let u=(l*i+c)*256/4;n[u]=c,a[u+1]=t}this.device.queue.writeBuffer(this.passBuffer,0,o)}step(){(this.radiiDirty||this.noCollideDirty)&&this.uploadStatics();let e=this.params;this.fixedColors&&(this.device.queue.writeBuffer(this.colorBuffer,0,this.fixedColors),this.colorCap=Math.max(1,...this.fixedColors.map(y=>y===255?0:y+1))),this.stepCount++,this.writeParams(),this.writePassConstants(e.iterations,e.alpha);let t=this.parity,i=this.bodyCapacity,r=this.bodyCount,o=this.jointCount,n=this.device.createCommandEncoder({label:"avbd3d step"});n.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),n.clearBuffer(this.tableBuffer),n.clearBuffer(this.adjBuffer,0,(2*i+1)*4);let a=this.timingCallback!==null&&this.timing!==null,l,c=0,u=a||this.splitPasses,f=()=>{l&&!u||(l?.end(),l=n.beginComputePass({label:u?Se[c]:"avbd3d step",timestampWrites:a?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*c,endOfPassWriteIndex:2*c+1}:void 0}),c++)},d=this.groups,p=(y,A,v)=>{v<=0||(l.setPipeline(this.pipes[y]),l.setBindGroup(0,A),l.dispatchWorkgroups(v))},m=(y,A,v)=>{l.setPipeline(this.pipes[y]),l.setBindGroup(0,A),l.dispatchWorkgroupsIndirect(this.argsBuffer,v*4)};if(f(),p("beginFrame",d.broad,1),e.reuseContacts&&p("updateRefs",d.refs,dt(r)),p("argsPrev",d.args,1),m("hashInsert",d.contacts[t],6),p("gridCount",d.broad,dt(r)),this.gridScan.encode(l),p("gridScatter",d.broad,dt(r)),p("findPairs",d.broad,dt(r)),p("argsPairs",d.args,1),m("narrowphase",d.contacts[t],0),p("argsContacts",d.args,1),f(),p("degreeJoints",d.topo[t],dt(o)),m("degreeContacts",d.topo[t],3),this.adjScan.encode(l),p("fillJoints",d.topo[t],dt(o)),m("fillContacts",d.topo[t],3),p("sortAdjacency",d.topo[t],dt(r)),f(),!this.fixedColors){p("colorCompact",d.topo[t],dt(r)),p("colorMark",d.topo[t],dt(r));for(let y=0;y<this.colorRounds;y++)p(y%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],dt(r))}p("colorCount",d.topo[t],this.colorGroups),this.colorHistScan.encode(l),p("colorStarts",d.topo[t],1),p("argsColors",d.args,1),p("colorScatter",d.topo[t],this.colorGroups),f();let h=this.colorCap+1,g=y=>l.setBindGroup(1,this.passGroup,[y*256]);l.setBindGroup(0,d.solve[t]),g(0),o>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups(dt(o))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups(dt(r));for(let y=0;y<e.iterations;y++){l.setPipeline(this.pipes.primal);for(let A=0;A<this.colorCap;A++)g(y*h+A),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*A)*4);g(y*h+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups(dt(r)),l.end();let b=2*Se.length;if(a){let{querySet:y,resolve:A,read:v}=this.timing;n.resolveQuerySet(y,0,b,A,0),n.copyBufferToBuffer(A,0,v,0,b*8)}if(this.device.queue.submit([n.finish()]),this.parity=1-t,a){let y=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let A=this.timing.read;A.mapAsync(GPUMapMode.READ).then(()=>{let v=new BigUint64Array(A.getMappedRange()).slice();if(A.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let x=(B,L)=>Number(v[L]-v[B])/1e6,w={total:x(0,b-1)};Se.forEach((B,L)=>w[B]=x(2*L,2*L+1)),y(w)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*We*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*pe*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],manifolds:e[mr],overflow:e[3],clashes:e[4],colors:e[5]}}async readContactList(){let e=await this.readCounters(),t=Math.min(e.manifolds,this.manifoldCapacity),i=Math.min(e.contacts,this.contactCapacity),r=1-this.parity,[o,n]=await Promise.all([this.read(this.manifoldBuffers[r],t*Nt*4),this.read(this.contactBuffers[r],i*Ht*4)]),a=new Uint32Array(o),l=new Float32Array(o),c=new Uint32Array(n),u=new Float32Array(n),f=[];for(let d=0;d<t;d++){let p=d*Nt,m=[...l.subarray(p+Ws,p+Ws+3)];for(let h=a[p+2];h<a[p+2]+(a[p+3]&15);h++){let g=h*Ht,b=c[g+pr+3];f.push({a:a[p],b:a[p+1],feature:(b&~$r)>>>0,stick:(b&$r)!==0,rA:[...u.subarray(g+pr,g+pr+3)],rB:[...u.subarray(g+Hs,g+Hs+3)],pen:[...u.subarray(g+$s,g+$s+3)],lam:[...u.subarray(g+Js,g+Js+3)],normal:m})}}return f}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}jointInfo(){return this.info.subarray(0,this.jointCount*4)}adapt(e){if(this.fixedColors)return;let t=e.colors;e.clashes>0&&(this.colorRounds=Math.min(32,this.colorRounds*2)),e.clashes>0||this.colorCap-t<2?(this.colorCap=Math.min(64,Math.max(t+Zs+2,this.colorCap+4)),this.shrinkVotes=0):t+Zs+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+Zs),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(a,l,c)=>(e.overflow&c)!==0||a>Xu*l?Math.max(Yu*a,1.25*l):l,r=i(e.contacts,this.contactCapacity,2),o=i(e.pairs,this.pairCapacity,1),n=i(e.manifolds,this.manifoldCapacity,4);this.capacityCanGrow(r,o,n)&&this.allocateContacts(r,o,n)}capacityCanGrow(e,t,i){let r=this.device.limits.maxStorageBufferBindingSize;return Math.min(e,r/(Ht*4))>this.contactCapacity||Math.min(t,r/8)>this.pairCapacity||Math.min(i,t,r/(Nt*4))>this.manifoldCapacity}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),o}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,...this.manifoldBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.refBuffer,this.hullBuffer,this.passBuffer,this.filterBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var Tl=` e.C = vec3f(
|
|
2421
|
+
`;function Ks(s,e){let t=0,i=s.length-1;for(;t<i;){let r=s[e],o=t,a=i;do{for(;s[o]<r;)o++;for(;r<s[a];)a--;if(o<=a){let n=s[o];s[o]=s[a],s[a]=n,o++,a--}}while(o<=a);a<e&&(t=o),e<o&&(i=a)}return s[e]}function qa(s){let e=s.length,t=e>0?Ks(s,e>>1):1,i=0;for(let r=0;r<e;r++)s[r]<=2*t&&(i=Math.max(i,s[r]));return e>64&&(i=Math.max(i,Ks(s,e-64-1))),i}var $t=s=>s===1/0?3e38:s===-1/0?-3e38:s,dt=s=>Math.ceil(s/64),Ol=s=>2**Math.ceil(Math.log2(Math.max(s,2))),Ju=s=>Math.max(s,1)*We*4,Xu=8,Zs=3,Yu=8,Qu=1.6,Ku=.8,Zu=2,ef=[0,0,1],tf=()=>({..._a(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function rf(s,e){let t=s.vertices.length/3,i=s.faces.length,r=s.faces.reduce((m,h)=>m+h.verts.length,0),o=Math.ceil(r/4),a=s.edges.length,n=new Float32Array((2+t+2*i+o+a)*4),l=new Uint32Array(n.buffer),c=e+2,u=c+t,f=u+2*i,d=f+o;l.set([c,t,u,i,d,a,f,0],0);for(let m=0;m<t;m++)n.set(s.vertices.subarray(m*3,m*3+3),(2+m)*4);let p=0;return s.faces.forEach((m,h)=>{let g=(2+t+2*h)*4;n.set(m.normal,g),n[g+3]=m.d,l[g+4]=p,l[g+5]=m.verts.length;for(let b of m.verts)l[(2+t+2*i)*4+p++]=b}),s.edges.forEach((m,h)=>l.set(m,(2+t+2*i+o+h)*4)),n}var Va=.001;function sf(s,e){let t=s.length,i=s.map(y=>{let A=y;return[A.group??4294967295,A.collidesWith??4294967295]}),r=i.map(([y,A])=>y!==0&&A!==0),o=s.map(y=>[0,1,2].map(A=>{let v=[0,0,0];return v[A]=1,le(S(),y.positionAng,v)})),a=s.map((y,A)=>{if(De(y))return[y.radius,y.radius,y.radius];let v=[0,0,0];for(let x=0;x<3;x++)for(let w=0;w<3;w++)v[w]+=y.size[x]*.5*Math.abs(o[A][x][w]);return v}),n=qa(Float64Array.from(s,y=>y.radius)),l=Math.max(2*n,.001),c=(y,A)=>{let v=s[y],x=s[A];if(v.mass<=0&&x.mass<=0||!(i[y][0]&i[A][1])||!(i[A][0]&i[y][1]))return!1;let w=v.positionLin[0]-x.positionLin[0],B=v.positionLin[1]-x.positionLin[1],L=v.positionLin[2]-x.positionLin[2];if(!(Math.abs(w)<=a[y][0]+a[A][0]&&Math.abs(B)<=a[y][1]+a[A][1]&&Math.abs(L)<=a[y][2]+a[A][2]))return!1;let M=v.radius+x.radius;return w*w+B*B+L*L<=M*M},u=(y,A)=>y[0]*A[0]+y[1]*A[1]+y[2]*A[2],f=(y,A)=>{let v=s[y],x=s[A],w=[0,1,2].map(E=>x.positionLin[E]-v.positionLin[E]),[B,L]=[De(v),De(x)];if(B&&L)return Math.hypot(w[0],w[1],w[2])<=v.radius+x.radius+Va;if(B||L){let[E,k,P]=B?[A,v.radius,-1]:[y,x.radius,1],C=0;for(let _=0;_<3;_++){let z=Math.abs(P*u(w,o[E][_]))-s[E].size[_]*.5;z>0&&(C+=z*z)}return C<=(k+Va)**2}let M=(E,k)=>s[E].size[0]*.5*Math.abs(u(o[E][0],k))+s[E].size[1]*.5*Math.abs(u(o[E][1],k))+s[E].size[2]*.5*Math.abs(u(o[E][2],k)),I=E=>{let k=Math.hypot(E[0],E[1],E[2]);return k<1e-6?!1:Math.abs(u(w,E))-M(y,E)-M(A,E)>Va*k};for(let E=0;E<3;E++)if(I(o[y][E])||I(o[A][E]))return!1;for(let E of o[y])for(let k of o[A])if(I([E[1]*k[2]-E[2]*k[1],E[2]*k[0]-E[0]*k[2],E[0]*k[1]-E[1]*k[0]]))return!1;return!0},d=new Set,p=new Map,m=(y,A,v)=>(y*73856093^A*19349663^v*83492791)>>>0,h=y=>[0,1,2].map(A=>Math.floor(s[y].positionLin[A]/l));for(let y=0;y<t;y++){if(!r[y])continue;if(s[y].radius>n){d.add(y);continue}let[A,v,x]=h(y),w=m(A,v,x),B=p.get(w);B?B.push(y):p.set(w,[y])}let g=0,b=0;for(let y=0;y<t;y++){if(!r[y]||d.has(y))continue;let[A,v,x]=h(y);for(let w=-1;w<=1;w++)for(let B=-1;B<=1;B++)for(let L=-1;L<=1;L++)for(let M of p.get(m(A+L,v+B,x+w))??[])M<y&&c(y,M)&&(g++,f(y,M)&&(b++,e[y]++,e[M]++))}for(let y of d)for(let A=0;A<t;A++)r[A]&&A!==y&&(!d.has(A)||A<y)&&c(y,A)&&(g++,f(y,A)&&(b++,e[y]++,e[A]++));return{pairs:g,touching:b}}function of(s){let e=[1/0,1/0,1/0],t=[-1/0,-1/0,-1/0];for(let o of s)for(let a=0;a<3;a++)e[a]=Math.min(e[a],o.positionLin[a]),t[a]=Math.max(t[a],o.positionLin[a]);let i=o=>{let a=o&1023;return a=(a|a<<16)&50331903,a=(a|a<<8)&50393103,a=(a|a<<4)&51130563,a=(a|a<<2)&153391689,a},r=s.map(o=>{let a=[0,1,2].map(n=>Math.min(1023,Math.floor((o.positionLin[n]-e[n])/Math.max(t[n]-e[n],1e-9)*1024)));return(i(a[0])|i(a[1])<<1|i(a[2])<<2)>>>0});return s.map((o,a)=>a).sort((o,a)=>r[o]-r[a]||o-a)}var Pe=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,eo=class{device;params=tf();bodyCount=0;bodyCapacity;bodies=[];hulls;hullBuffer;get hullStorage(){return this.hullBuffer}hullCapacity=4096;hullTop=0;hullSlots=new Map;contactShaders;hullShaders=!1;hullFree=[];refToGpu;jointCount=0;jointCapacity=0;info=new Int32Array(0);pairCapacity=0;manifoldCapacity=0;contactCapacity=0;colorCap=12;fixedColors=null;primalLanes=[2**31,2**15,2**12];splitPasses=!1;shrinkVotes=0;colorRounds;get colorHistOffset(){return 3*this.bodyCapacity+65}bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;manifoldBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;filterBuffer;get filterStorage(){return this.filterBuffer}counterBuffer;get contactStorage(){let e=1-this.parity;return{manifolds:this.manifoldBuffers[e],contacts:this.contactBuffers[e],counters:this.counterBuffer}}argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;refBuffer;stepCount=0;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;radiiDirty=!0;noCollideDirty=!0;large=[];entries=[];pendingEntries=[];releasedSlots=new Set;freeSlots=[];layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){this.device=e;let{dt:r,gravity:o,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:u}=t;if(Object.assign(this.params,{dt:r,gravity:o,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:u,up:ef}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(We*4)):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies 3d",size:Ju(this.bodyCapacity),usage:Pe()}),this.filterBuffer=e.createBuffer({label:"collision filters",size:Math.max(this.bodyCapacity,1)*8,usage:Pe()}),e.queue.writeBuffer(this.filterBuffer,0,new Uint32Array(Math.max(this.bodyCapacity,1)*2).fill(4294967295));let f=this.bodyCapacity;this.tableSize=Ol(2*f),this.gridBuffer=e.createBuffer({label:"grid 3d",size:(2*this.tableSize+1+4*f)*4,usage:Pe()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:Pe()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:Pe()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(f/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*f+65+64*this.colorGroups+1)*4,usage:Pe()}),this.paramsBuffer=e.createBuffer({label:"params 3d",size:es*4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.refBuffer=e.createBuffer({label:"reference poses",size:f*32,usage:Pe()}),this.hulls=(i.hulls??!0)&&e.limits.maxStorageBuffersPerShaderStage>=9,this.hullBuffer=e.createBuffer({label:"hulls",size:this.hullCapacity*16,usage:Pe()}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(f).fill(255));let d=2*Se.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:d}),resolve:e.createBuffer({size:d*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:d*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(i.shaders??{}),this.gridScan=new tt(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new tt(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1);let p=i.spatialSort===!1?t.bodies.map((w,B)=>B):of(t.bodies);this.refToGpu=new Int32Array(p.length),p.forEach((w,B)=>this.refToGpu[w]=B);let m=new Map(t.bodies.map((w,B)=>[w,this.refToGpu[B]])),h=t.forces.filter(w=>w instanceof ke||w instanceof ft);for(let w of t.forces){if(!(w instanceof Vs))continue;let B=m.get(w.bodyA),L=m.get(w.bodyB);this.pendingEntries.push([Math.max(B,L),Math.min(B,L),4294967295])}this.allocateJoints(Math.max(h.length+256,i.capacity?.joints??0));for(let w of h)this.writeJoint(this.jointCount++,w,m);let g=new Int32Array(t.bodies.length),{pairs:b,touching:y}=sf(t.bodies,g),A=new Map(t.bodies.map((w,B)=>[w,B]));for(let w of h)w.bodyA&&g[A.get(w.bodyA)]++,g[A.get(w.bodyB)]++;let v=0;t.bodies.forEach((w,B)=>{w.mass>0&&(v=Math.max(v,g[B]))}),this.colorCap=Math.min(64,Math.max(Yu,v+1+Zs,i.capacity?.colors??0));let x=i.capacity??{};this.allocateContacts(Math.max(8192,4*f,Xu*y,x.contacts??0),Math.max(4096,4*f,2*b,x.pairs??0),Math.max(4096,f,Qu*y,x.manifolds??0)),this.writeBodies(0,p.map(w=>t.bodies[w]))}gpuIndex(e){return e<this.refToGpu.length?this.refToGpu[e]:e}createLayouts(){let e=this.device,t=(a,n)=>e.createBindGroupLayout({label:a,entries:n.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",o="uniform";return{broad:t("broadphase 3d",[o,i,r,r,r,i,i,i]),contacts:t("contacts 3d",[o,i,i,r,i,r,i,r,r,...this.hulls?[i]:[]]),topo:t("topology 3d",[o,i,i,i,i,r,r,r]),solve:t("solve 3d",[o,r,r,i,r,i,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args 3d",[o,i,i,r]),refs:t("refs 3d",[o,r,r])}}createPipelines(e){let t=this.device,i=(o,a,n)=>{let l=t.createShaderModule({code:o}),c=t.createPipelineLayout({bindGroupLayouts:a});for(let u of n)this.pipes[u]=t.createComputePipeline({label:u,layout:c,compute:{module:l,entryPoint:u}})},r=this.layouts;i(fi,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(e.refs??Qs,[r.refs],["updateRefs"]),i(e.contacts??zl,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:o=>i(o,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(e.topology??Ti(ge,br),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??Ge,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(Nr(ge,Ys),[r.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints 3d",size:Math.max(e,1)*pe*4,usage:Pe()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:Pe()});if(this.jointBuffer){let a=t.createCommandEncoder();a.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*pe*4),a.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([a.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}let o=new Int32Array(e*4);o.set(this.info.subarray(0,Math.min(this.info.length,o.length))),this.info=o,this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t,i){let r=this.device,o=r.limits.maxStorageBufferBindingSize,a=Math.min(Math.ceil(e),Math.floor(o/(Ht*4))),n=Math.min(Math.ceil(t),Math.floor(o/8)),l=Math.min(Math.ceil(i),n,Math.floor(o/(Nt*4))),c={contacts:this.contactBuffers,manifolds:this.manifoldBuffers,contactCapacity:this.contactCapacity,manifoldCapacity:this.manifoldCapacity};for(let u of[this.pairBuffer,this.tableBuffer])u?.destroy();if(this.contactCapacity=a,this.pairCapacity=n,this.manifoldCapacity=l,this.hashSize=Ol(2*l),this.pairBuffer=r.createBuffer({label:"pairs",size:n*8,usage:Pe()}),this.tableBuffer=r.createBuffer({label:"manifold hash",size:this.hashSize*4,usage:Pe()}),this.contactBuffers=[0,1].map(u=>r.createBuffer({label:`contacts 3d ${u}`,size:a*Ht*4,usage:Pe()})),this.manifoldBuffers=[0,1].map(u=>r.createBuffer({label:`manifolds 3d ${u}`,size:l*Nt*4,usage:Pe()})),c.contacts){let u=r.createCommandEncoder(),f=Math.min(c.contactCapacity,a)*Ht*4,d=Math.min(c.manifoldCapacity,l)*Nt*4;c.contacts.forEach((p,m)=>u.copyBufferToBuffer(p,0,this.contactBuffers[m],0,f)),c.manifolds.forEach((p,m)=>u.copyBufferToBuffer(p,0,this.manifoldBuffers[m],0,d)),r.queue.submit([u.finish()]),[...c.contacts,...c.manifolds].forEach(p=>p.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.manifoldCapacity))*4,usage:Pe()}),this.adjScan?.destroy(),this.adjScan=new tt(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:Pe()});let i=(d,p)=>e.createBindGroup({layout:d,entries:p.map((m,h)=>({binding:h,resource:{buffer:m}}))}),r=this.paramsBuffer,[o,a]=this.contactBuffers,[n,l]=this.manifoldBuffers,c=this.adjBuffer,u=this.tableBuffer,f=this.counterBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,f,this.staticBuffer,this.jointBuffer,this.filterBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,o,a,n,l,u,f,...this.hulls?[this.hullBuffer]:[]]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,o,l,n,u,f,...this.hulls?[this.hullBuffer]:[]])],topo:[n,l].map(d=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,d,f,c,this.colorBuffer])),solve:[0,1].map(d=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,[o,a][d],c,this.colorBuffer,f,[n,l][d]])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer]),refs:i(this.layouts.refs,[r,this.bodyBuffer,this.refBuffer])}}writeBodies(e,t){let i=new Float32Array(Math.max(t.length,1)*We);t.forEach((r,o)=>{let a=this.pack(r,i,o*We,this.bodies[e+o]?.hull);this.bodies[e+o]?.radius!==a.radius&&(this.radiiDirty=!0),this.bodies[e+o]=a}),this.device.queue.writeBuffer(this.bodyBuffer,e*We*4,i,0,t.length*We)}rewriteFixed(e,t,i,r,o){if(!e.length)return;let a=new Float32Array(We),n={positionLin:[0,0,0],positionAng:[0,0,0,1],initialLin:[0,0,0],initialAng:[0,0,0,1],inertialLin:[0,0,0],inertialAng:[0,0,0,1],velocityLin:[0,0,0],prevVelocityLin:[0,0,0],velocityAng:[0,0,0],size:[i[0],i[1],i[2]],mass:0,moment:[0,0,0],friction:r,radius:Math.hypot(i[0]/2,i[1]/2,i[2]/2)},l=this.pack(n,a,0);for(let c=0;c<e.length;){let u=c+1;for(;u<e.length&&e[u]===e[u-1]+1;)u++;let f=new Float32Array((u-c)*We);for(let d=c;d<u;d++){let p=(d-c)*We;f.set(a,p);for(let h=0;h<3;h++)f[p+ur+h]=t[3*d+h];if(o)for(let h=0;h<4;h++)f[p+Wr+h]=o[4*d+h];let m=this.bodies[e[d]];m?.radius!==l.radius&&(this.radiiDirty=!0),m?.hull&&this.releaseHull(m.hull),this.bodies[e[d]]={...l,size:[...l.size]}}this.device.queue.writeBuffer(this.bodyBuffer,e[c]*We*4,f),this.device.queue.writeBuffer(this.colorBuffer,e[c]*4,new Uint32Array(u-c).fill(255)),c=u}}pack(e,t,i,r){{t.set(e.positionLin,i+ur),t[i+ur+3]=e.friction,t.set(e.positionAng,i+Wr),t.set(e.size,i+Ta),t[i+Ta+3]=e.mass,t.set(e.moment,i+ja),t[i+ja+3]=e.radius,t.set(e.initialLin,i+wl),t.set(e.initialAng,i+Bl),t.set(e.inertialLin,i+Cl),t.set(e.inertialAng,i+Sl),t.set(e.velocityLin,i+Da);let o=this.params.up;t[i+Da+3]=e.prevVelocityLin[0]*o[0]+e.prevVelocityLin[1]*o[1]+e.prevVelocityLin[2]*o[2],t.set(e.velocityAng,i+Ga);let a=De(e),n=this.hulls?Ct(e):void 0;r&&r!==n&&this.releaseHull(r);let l=n?r===n?this.hullSlots.get(n).offset:this.acquireHull(n):-1;return t[i+Ga+3]=a?Fa:l>=0?Xs+l:or(e)?Ua:Ll,{radius:e.radius,dynamic:e.mass>0,sphere:a,size:[e.size[0],e.size[1],e.size[2]],hull:n}}}writeJoint(e,t,i){let r=new Float32Array(pe),o=t.bodyA?i.get(t.bodyA):-1,a=i.get(t.bodyB);t instanceof ke?(r.set(t.penaltyLin,ci),r[ci+3]=$t(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,Hr),r[Hr+3]=$t(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,fr),r[fr+3]=$t(t.fracture),r.set(t.lambdaAng,dr),r[dr+3]=t.torqueArm,r.set(t.C0Lin,kl),r.set(t.C0Ang,Pl),r.set(t.rA,ui),r.set(t.rB,hr)):(r[ci+3]=t.stiffness,r.set(t.rA,ui),r[ui+3]=t.rest,r.set(t.rB,hr)),this.info.set([t instanceof ke?Jr:gr,o,a,0],e*4),this.device.queue.writeBuffer(this.jointBuffer,e*pe*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info,e*4,4),this.noCollide(o,a,e)}noCollide(e,t,i){e<0||(this.pendingEntries.push([Math.max(e,t),Math.min(e,t),i]),this.noCollideDirty=!0)}uploadStatics(){if(this.radiiDirty){let r=this.bodyCount,o=new Float64Array(r);for(let c=0;c<r;c++)o[c]=this.bodies[c].radius;let a=qa(o),n=1/0;for(let c=0;c<r;c++)o[c]>a&&(n=Math.min(n,o[c]));let l=n===1/0?a*1.5:(a+n)/2;this.maxSmallRadius=l,this.cellSize=Math.max(2*a*(1+1e-4),.001),this.large=[];for(let c=0;c<r;c++)this.bodies[c].radius>l&&this.large.push(c);this.radiiDirty=!1}if(this.noCollideDirty){let r=(l,c)=>l[0]-c[0]||l[1]-c[1],o=this.releasedSlots.size?this.entries.filter(l=>!this.releasedSlots.has(l[2])):this.entries;this.releasedSlots.clear();let a=this.pendingEntries.sort(r);this.pendingEntries=[];let n=[];for(let l=0,c=0;l<o.length||c<a.length;)c>=a.length||l<o.length&&r(o[l],a[c])<=0?n.push(o[l++]):n.push(a[c++]);this.entries=n,this.noCollideDirty=!1}let{large:e,entries:t}=this;this.largeCount=e.length,this.noCollideCount=t.length;let i=new Uint32Array(Math.max(e.length+3*t.length,4));i.set(e,0),t.forEach((r,o)=>i.set(r,e.length+3*o)),this.staticBuffer.size<i.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:i.byteLength,usage:Pe()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,i)}seedFrom(e){if(this.refToGpu.some((m,h)=>m!==h))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((m,h)=>[m,h]));this.writeBodies(0,e.bodies);let i=0;for(let m of e.forces)(m instanceof ke||m instanceof ft)&&this.writeJoint(i++,m,t);if(i!==this.jointCount)throw new Error("seedFrom: joints differ from the reference");let r=e.forces.filter(m=>m instanceof ai),o=r.reduce((m,h)=>m+h.contacts.length,0);if(o>this.contactCapacity||r.length>this.manifoldCapacity)throw new Error("seedFrom: too many contacts");let a=new ArrayBuffer(Math.max(o,1)*Ht*4),n=new Uint32Array(a),l=new Float32Array(a),c=new ArrayBuffer(Math.max(r.length,1)*Nt*4),u=new Uint32Array(c),f=new Float32Array(c),d=0;r.forEach((m,h)=>{u.set([t.get(m.bodyA),t.get(m.bodyB),d,m.contacts.length],h*Nt),f.set([m.basis[0],m.basis[1],m.basis[2],m.friction],h*Nt+Ws);for(let g of m.contacts){let b=d++*Ht;l.set(g.rA,b+pr),n[b+pr+3]=g.feature>>>0|(g.stick?$r:0),l.set([...g.rB,g.C0[0]],b+Hs),l.set([...g.penalty,g.C0[1]],b+$s),l.set([...g.lambda,g.C0[2]],b+Js)}}),this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,a),this.device.queue.writeBuffer(this.manifoldBuffers[1-this.parity],0,c);let p=new Uint32Array(16);p[1]=o,p[mr]=r.length,this.device.queue.writeBuffer(this.counterBuffer,0,p)}sequentialColors(){let e=new Uint32Array(this.bodyCount).fill(255),t=0;for(let i=this.bodyCount-1;i>=0;i--)this.bodies[i].dynamic&&(e[i]=t++);if(t>64)throw new Error(`sequentialColors: ${t} dynamic bodies, at most ${64}`);return e}acquireHull(e){!this.hullShaders&&!this.contactShaders.custom&&(this.contactShaders.make(Il),this.hullShaders=!0);let t=this.hullSlots.get(e);if(t)return t.refs++,t.offset;let i=2+e.vertices.length/3+2*e.faces.length+Math.ceil(e.faces.reduce((a,n)=>a+n.verts.length,0)/4)+e.edges.length,r=-1,o=this.hullFree.findIndex(a=>a.size>=i);if(o>=0){let a=this.hullFree[o];r=a.offset,a.size===i?this.hullFree.splice(o,1):Object.assign(a,{offset:a.offset+i,size:a.size-i})}else r=this.hullTop,this.hullTop+=i,this.hullTop>this.hullCapacity&&this.growHulls(this.hullTop);if(r+Xs>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,rf(e,r)),this.hullSlots.set(e,{offset:r,size:i,refs:1}),r}releaseHull(e){let t=this.hullSlots.get(e);if(!t||--t.refs>0)return;this.hullSlots.delete(e),this.hullFree.push({offset:t.offset,size:t.size}),this.hullFree.sort((r,o)=>r.offset-o.offset);let i=[];for(let r of this.hullFree){let o=i[i.length-1];o&&o.offset+o.size===r.offset?o.size+=r.size:i.push({...r})}this.hullFree=i}growHulls(e){let t=this.hullCapacity;for(;t<e;)t*=2;let i=this.device.createBuffer({label:"hulls",size:t*16,usage:Pe()}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(this.hullBuffer,0,i,0,this.hullCapacity*16),this.device.queue.submit([r.finish()]),this.hullBuffer.destroy(),this.hullBuffer=i,this.hullCapacity=t,this.contactBuffers&&this.rebuildBindings()}addBody(e){return this.addBodies([e])}addBodies(e){let t=Math.min(e.length,this.bodyCapacity-this.bodyCount);if(t<=0)return-1;let i=this.bodyCount;return this.bodyCount+=t,this.writeBodies(i,e.slice(0,t)),this.device.queue.writeBuffer(this.colorBuffer,i*4,new Uint32Array(t).fill(255)),this.adjScan.destroy(),this.adjScan=new tt(this.device,this.adjBuffer,0,this.bodyCount+1),i}appendJoint(e,t,i,r,o,a){let n=this.jointCount;n>=this.jointCapacity&&this.allocateJoints(this.jointCapacity*2),this.jointCount++;let l=new Float32Array(pe);l[ci+3]=$t(o),l[Hr+3]=$t(a),l[fr+3]=3e38,l.set([i[0],i[1],i[2]],ui),l.set([r[0],r[1],r[2]],hr);let c=e>=0?this.bodies[e].size:[0,0,0],u=this.bodies[t].size;return l[dr+3]=(c[0]+u[0])**2+(c[1]+u[1])**2+(c[2]+u[2])**2,this.info.set([Jr,e,t,0],n*4),this.device.queue.writeBuffer(this.jointBuffer,n*pe*4,l),this.device.queue.writeBuffer(this.infoBuffer,n*16,this.info,n*4,4),this.noCollide(e,t,n),n}appendJoints(e,t,i=!1){return this.appendConstraints(e.length,(r,o)=>{let{a,b:n,rA:l,rB:c,angular:u}=e[r];o[ci+3]=3e38,o[Hr+3]=u===void 0?3e38:$t(u),o[fr+3]=i?-$t(t):$t(t),o.set([l[0],l[1],l[2]],ui),o.set([c[0],c[1],c[2]],hr);let[f,d]=[this.bodies[a].size,this.bodies[n].size];return o[dr+3]=(f[0]+d[0])**2+(f[1]+d[1])**2+(f[2]+d[2])**2,[Jr,a,n]})}appendSprings(e){return this.appendConstraints(e.length,(t,i)=>{let{a:r,b:o,rA:a,rB:n,stiffness:l,rest:c}=e[t];return i[ci+3]=$t(l),i.set([a[0],a[1],a[2],c],ui),i.set([n[0],n[1],n[2]],hr),[gr,r,o]})}appendConstraints(e,t){let i=[];if(this.freeSlots.length)for(this.freeSlots.sort((n,l)=>l-n);i.length<e&&this.freeSlots.length;)i.push(this.freeSlots.pop());let r=e-i.length,o=this.jointCapacity;for(;this.jointCount+r>o;)o*=2;o>this.jointCapacity&&this.allocateJoints(o);for(let n=0;n<r;n++)i.push(this.jointCount++);let a=new Float32Array(e*pe);for(let n=0;n<e;n++){let[l,c,u]=t(n,a.subarray(n*pe,(n+1)*pe));this.info.set([l,c,u,0],i[n]*4),this.releasedSlots.delete(i[n]),this.noCollide(c,u,i[n])}for(let n=0;n<e;){let l=n+1;for(;l<e&&i[l]===i[l-1]+1;)l++;this.device.queue.writeBuffer(this.jointBuffer,i[n]*pe*4,a,n*pe,(l-n)*pe),this.device.queue.writeBuffer(this.infoBuffer,i[n]*16,this.info,i[n]*4,(l-n)*4),n=l}return i}releaseJoints(e){let t=[...new Set(Array.from(e))].filter(r=>r<this.jointCount&&this.info[r*4]!==0).sort((r,o)=>r-o);if(!t.length)return;for(let r of t)this.info.set([0,-1,-1,0],r*4),this.releasedSlots.add(r),this.freeSlots.push(r);let i=new Float32Array(0);for(let r=0;r<t.length;){let o=r+1;for(;o<t.length&&t[o]===t[o-1]+1;)o++;i.length<(o-r)*pe&&(i=new Float32Array((o-r)*pe)),this.device.queue.writeBuffer(this.jointBuffer,t[r]*pe*4,i,0,(o-r)*pe),this.device.queue.writeBuffer(this.infoBuffer,t[r]*16,this.info,t[r]*4,(o-r)*4),r=o}this.noCollideDirty=!0}rewriteBodies(e,t){for(let i=0;i<e.length;){let r=i+1;for(;r<e.length&&e[r]===e[r-1]+1;)r++;this.writeBodies(e[i],t.slice(i,r)),this.device.queue.writeBuffer(this.colorBuffer,e[i]*4,new Uint32Array(r-i).fill(255)),i=r}}setFilters(e,t,i){for(let r=0;r<e.length;){let o=r+1;for(;o<e.length&&e[o]===e[o-1]+1;)o++;let a=new Uint32Array(2*(o-r));for(let n=r;n<o;n++)a.set([t[n]>>>0,i[n]>>>0],2*(n-r));this.device.queue.writeBuffer(this.filterBuffer,e[r]*8,a),r=o}}setWorldAnchor(e,t){this.device.queue.writeBuffer(this.jointBuffer,(e*pe+ui)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*pe+ci)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+fr)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+dr)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(es*4),r=new Float32Array(i),o=new Uint32Array(i);r[0]=e.dt,r[1]=e.gravity,r[2]=e.betaLin,r[3]=e.betaAng,r[4]=e.gamma,r[5]=e.alpha,o[6]=(e.matchNearest?Xr:0)|(e.faceBias?Yr:0)|(e.reuseContacts?Qr:0)|(e.startAtRest?Kr:0)|(e.massPenalty?Zr:0),o[7]=this.bodyCount,o[8]=this.jointCount,o[9]=this.colorCap,r[10]=this.cellSize,o[11]=this.tableSize-1,r[12]=this.maxSmallRadius,o[13]=this.largeCount,o[14]=this.noCollideCount,o[15]=this.pairCapacity,o[16]=this.contactCapacity,o[17]=this.hashSize-1,o[18]=t+1,o[19]=2*t+1,o[20]=this.tableSize+1,o[21]=2*this.tableSize+1,o[22]=2*this.tableSize+1+t,o[23]=t,o[24]=this.colorHistOffset,o[25]=2*t,o[26]=this.colorGroups,o[27]=2*t+65,o[28]=this.colorRounds,o[29]=this.manifoldCapacity,o[30]=this.stepCount;let a=n=>Math.min(31,Math.max(0,Math.round(Math.log2(n))));o[31]=a(this.primalLanes[0])|a(this.primalLanes[1])<<8|a(this.primalLanes[2])<<16,r.set([e.windSpeed*Math.cos(e.windAngle),e.windSpeed*Math.sin(e.windAngle),0,e.windPressure,e.windGust],32),r.set([e.up[0],e.up[1],e.up[2],0],40),this.device.queue.writeBuffer(this.paramsBuffer,0,i)}writePassConstants(e,t){let i=this.colorCap+1,r=Math.max(e*i,1);(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let o=new ArrayBuffer(r*256),a=new Uint32Array(o),n=new Float32Array(o);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let u=(l*i+c)*256/4;a[u]=c,n[u+1]=t}this.device.queue.writeBuffer(this.passBuffer,0,o)}step(){(this.radiiDirty||this.noCollideDirty)&&this.uploadStatics();let e=this.params;this.fixedColors&&(this.device.queue.writeBuffer(this.colorBuffer,0,this.fixedColors),this.colorCap=Math.max(1,...this.fixedColors.map(y=>y===255?0:y+1))),this.stepCount++,this.writeParams(),this.writePassConstants(e.iterations,e.alpha);let t=this.parity,i=this.bodyCapacity,r=this.bodyCount,o=this.jointCount,a=this.device.createCommandEncoder({label:"avbd3d step"});a.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),a.clearBuffer(this.tableBuffer),a.clearBuffer(this.adjBuffer,0,(2*i+1)*4);let n=this.timingCallback!==null&&this.timing!==null,l,c=0,u=n||this.splitPasses,f=()=>{l&&!u||(l?.end(),l=a.beginComputePass({label:u?Se[c]:"avbd3d step",timestampWrites:n?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*c,endOfPassWriteIndex:2*c+1}:void 0}),c++)},d=this.groups,p=(y,A,v)=>{v<=0||(l.setPipeline(this.pipes[y]),l.setBindGroup(0,A),l.dispatchWorkgroups(v))},m=(y,A,v)=>{l.setPipeline(this.pipes[y]),l.setBindGroup(0,A),l.dispatchWorkgroupsIndirect(this.argsBuffer,v*4)};if(f(),p("beginFrame",d.broad,1),e.reuseContacts&&p("updateRefs",d.refs,dt(r)),p("argsPrev",d.args,1),m("hashInsert",d.contacts[t],6),p("gridCount",d.broad,dt(r)),this.gridScan.encode(l),p("gridScatter",d.broad,dt(r)),p("findPairs",d.broad,dt(r)),p("argsPairs",d.args,1),m("narrowphase",d.contacts[t],0),p("argsContacts",d.args,1),f(),p("degreeJoints",d.topo[t],dt(o)),m("degreeContacts",d.topo[t],3),this.adjScan.encode(l),p("fillJoints",d.topo[t],dt(o)),m("fillContacts",d.topo[t],3),p("sortAdjacency",d.topo[t],dt(r)),f(),!this.fixedColors){p("colorCompact",d.topo[t],dt(r)),p("colorMark",d.topo[t],dt(r));for(let y=0;y<this.colorRounds;y++)p(y%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],dt(r))}p("colorCount",d.topo[t],this.colorGroups),this.colorHistScan.encode(l),p("colorStarts",d.topo[t],1),p("argsColors",d.args,1),p("colorScatter",d.topo[t],this.colorGroups),f();let h=this.colorCap+1,g=y=>l.setBindGroup(1,this.passGroup,[y*256]);l.setBindGroup(0,d.solve[t]),g(0),o>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups(dt(o))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups(dt(r));for(let y=0;y<e.iterations;y++){l.setPipeline(this.pipes.primal);for(let A=0;A<this.colorCap;A++)g(y*h+A),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*A)*4);g(y*h+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups(dt(r)),l.end();let b=2*Se.length;if(n){let{querySet:y,resolve:A,read:v}=this.timing;a.resolveQuerySet(y,0,b,A,0),a.copyBufferToBuffer(A,0,v,0,b*8)}if(this.device.queue.submit([a.finish()]),this.parity=1-t,n){let y=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let A=this.timing.read;A.mapAsync(GPUMapMode.READ).then(()=>{let v=new BigUint64Array(A.getMappedRange()).slice();if(A.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let x=(B,L)=>Number(v[L]-v[B])/1e6,w={total:x(0,b-1)};Se.forEach((B,L)=>w[B]=x(2*L,2*L+1)),y(w)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*We*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*pe*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],manifolds:e[mr],overflow:e[3],clashes:e[4],colors:e[5]}}async readContactList(){let e=await this.readCounters(),t=Math.min(e.manifolds,this.manifoldCapacity),i=Math.min(e.contacts,this.contactCapacity),r=1-this.parity,[o,a]=await Promise.all([this.read(this.manifoldBuffers[r],t*Nt*4),this.read(this.contactBuffers[r],i*Ht*4)]),n=new Uint32Array(o),l=new Float32Array(o),c=new Uint32Array(a),u=new Float32Array(a),f=[];for(let d=0;d<t;d++){let p=d*Nt,m=[...l.subarray(p+Ws,p+Ws+3)];for(let h=n[p+2];h<n[p+2]+(n[p+3]&15);h++){let g=h*Ht,b=c[g+pr+3];f.push({a:n[p],b:n[p+1],feature:(b&~$r)>>>0,stick:(b&$r)!==0,rA:[...u.subarray(g+pr,g+pr+3)],rB:[...u.subarray(g+Hs,g+Hs+3)],pen:[...u.subarray(g+$s,g+$s+3)],lam:[...u.subarray(g+Js,g+Js+3)],normal:m})}}return f}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}jointInfo(){return this.info.subarray(0,this.jointCount*4)}adapt(e){if(this.fixedColors)return;let t=e.colors;e.clashes>0&&(this.colorRounds=Math.min(32,this.colorRounds*2)),e.clashes>0||this.colorCap-t<2?(this.colorCap=Math.min(64,Math.max(t+Zs+2,this.colorCap+4)),this.shrinkVotes=0):t+Zs+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+Zs),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(n,l,c)=>(e.overflow&c)!==0||n>Ku*l?Math.max(Zu*n,1.25*l):l,r=i(e.contacts,this.contactCapacity,2),o=i(e.pairs,this.pairCapacity,1),a=i(e.manifolds,this.manifoldCapacity,4);this.capacityCanGrow(r,o,a)&&this.allocateContacts(r,o,a)}capacityCanGrow(e,t,i){let r=this.device.limits.maxStorageBufferBindingSize;return Math.min(e,r/(Ht*4))>this.contactCapacity||Math.min(t,r/8)>this.pairCapacity||Math.min(i,t,r/(Nt*4))>this.manifoldCapacity}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),o}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,...this.manifoldBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.refBuffer,this.hullBuffer,this.passBuffer,this.filterBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var Tl=` e.C = vec3f(
|
|
2422
2422
|
k.c0x * keep + dot(n, A.dLin) - dot(n, B.dLin) + dot(cross(e.rAW, n), A.dAng) + dot(cross(e.rBW, -n), B.dAng),
|
|
2423
2423
|
k.c0y * keep + dot(t1, A.dLin) - dot(t1, B.dLin) + dot(cross(e.rAW, t1), A.dAng) + dot(cross(e.rBW, -t1), B.dAng),
|
|
2424
2424
|
k.c0z * keep + dot(t2, A.dLin) - dot(t2, B.dLin) + dot(cross(e.rAW, t2), A.dAng) + dot(cross(e.rBW, -t2), B.dAng));`;if(Ge.split(Tl).length!==2)throw Error("Pinned contact evaluation changed; review the app kernel variant");var is=Ge.replace(Tl,` let delta = A.dLin - B.dLin + cross(A.dAng, e.rAW) - cross(B.dAng, e.rBW);
|
|
@@ -2435,13 +2435,13 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
2435
2435
|
if (c >= cEnd) { continue; }
|
|
2436
2436
|
isA = i == mf.ids.x;`),s=St(s,` c = mf.ids.z;
|
|
2437
2437
|
cEnd = mf.ids.z + pairCount(mf);
|
|
2438
|
-
if (c == cEnd) { continue; }`,""),s=St(s," c++;"," c += lanes;"),s})();function
|
|
2438
|
+
if (c == cEnd) { continue; }`,""),s=St(s," c++;"," c += lanes;"),s})();function Qa(s=Ge){s=St(s,"fn manifoldCount() -> u32 {",`fn solverStepValid() -> bool {
|
|
2439
2439
|
return counters[C_OVERFLOW] == 0u && counters[C_CLASHES] == 0u;
|
|
2440
2440
|
}
|
|
2441
2441
|
|
|
2442
2442
|
|
|
2443
2443
|
fn manifoldCount() -> u32 {`);for(let[e,t]of[["warmStartJoints","global_invocation_id"],["warmStartBodies","global_invocation_id"],["dual","global_invocation_id"],["updateVelocities","global_invocation_id"]]){let i=`fn ${e}(@builtin(${t}) gid: vec3u) {`;s=St(s,i,`${i}
|
|
2444
|
-
if (!solverStepValid()) { return; }`)}return St(s," let live = k < count;"," let live = k < count && solverStepValid();")}function
|
|
2444
|
+
if (!solverStepValid()) { return; }`)}return St(s," let live = k < count;"," let live = k < count && solverStepValid();")}function Ka(s){return s=St(s," return qsub(rotA(a), bodies[b].rot) * k.lamAng.w;",` let rest = vec4f(k.c0Lin.w, k.c0Ang.w, k.rA.w, k.rB.w);
|
|
2445
2445
|
var restTarget = rotA(a);
|
|
2446
2446
|
if (dot(rest, rest) > 0.5) {
|
|
2447
2447
|
restTarget = vec4f(restTarget.w * rest.xyz + rest.w * restTarget.xyz + cross(restTarget.xyz, rest.xyz),
|
|
@@ -2472,7 +2472,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
|
|
|
2472
2472
|
joints[slot].rA.w=length(pa-pb);
|
|
2473
2473
|
}
|
|
2474
2474
|
}
|
|
2475
|
-
`;function hi(s){let e=new ArrayBuffer(s*4);return{f:new Float32Array(e),u:new Uint32Array(e),previous:new Uint32Array(s),buffer:null,uploads:0,bytes:0}}function Qt(s,e,t){let{u:i,previous:r}=t,o=0,
|
|
2475
|
+
`;function hi(s){let e=new ArrayBuffer(s*4);return{f:new Float32Array(e),u:new Uint32Array(e),previous:new Uint32Array(s),buffer:null,uploads:0,bytes:0}}function Qt(s,e,t){let{u:i,previous:r}=t,o=0,a=i.length-1;if(t.buffer===e){for(;o<=a&&i[o]===r[o];)o++;for(;a>=o&&i[a]===r[a];)a--;if(o>a)return}s.queue.writeBuffer(e,o*4,i,o,a-o+1),r.set(i),t.buffer=e,t.uploads++,t.bytes+=(a-o+1)*4}var Dl=`// Conservative GPU sleeping. Resting contacts form supports from the ground
|
|
2476
2476
|
// upward. Any impact, moving joint endpoint or explicit edit wakes the world.
|
|
2477
2477
|
// This deliberately favors reliable waking over finely partitioned islands.
|
|
2478
2478
|
struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
@@ -2487,23 +2487,25 @@ struct Params { count:u32,joints:u32,manifolds:u32,forceWake:u32,speed:f32,delay
|
|
|
2487
2487
|
@group(0) @binding(5) var<storage,read_write> globals:array<atomic<u32>>;
|
|
2488
2488
|
@group(0) @binding(6) var<uniform> params:Params;
|
|
2489
2489
|
fn speed(i:u32)->f32 {return length(bodies[i].velocity.xyz)+length(bodies[i].angular.xyz)*bodies[i].moment.w;}
|
|
2490
|
-
fn wake(i:u32) {if(states[i].mass>0.){bodies[i].size.w=states[i].mass;states[i].asleep=0u;states[i].quiet=0.;}}
|
|
2490
|
+
fn wake(i:u32) {if(states[i].mass>0.){bodies[i].size.w=states[i].mass;states[i].asleep=0u;states[i].quiet=0.;states[i].pad2=0u;}}
|
|
2491
2491
|
@compute @workgroup_size(64) fn before(@builtin(global_invocation_id) id:vec3u) {
|
|
2492
2492
|
let i=id.x;if(i>=params.count){return;}
|
|
2493
|
-
if(params.forceWake!=0u||atomicLoad(&globals[0])!=0u){wake(i);}
|
|
2493
|
+
if(params.forceWake!=0u||atomicLoad(&globals[0])!=0u||(states[i].pad2&2u)!=0u){wake(i);}
|
|
2494
2494
|
atomicStore(&states[i].support,0u);
|
|
2495
2495
|
}
|
|
2496
2496
|
@compute @workgroup_size(1) fn clear() {atomicStore(&globals[0],0u);atomicStore(&globals[1],0u);}
|
|
2497
2497
|
fn impact(a:u32,b:u32) {
|
|
2498
2498
|
if(states[a].asleep!=0u && states[b].asleep==0u && speed(b)>2.*params.speed){atomicStore(&globals[0],1u);}
|
|
2499
2499
|
}
|
|
2500
|
+
fn signalWake(i:u32){atomicStore(&globals[0],1u);}
|
|
2500
2501
|
@compute @workgroup_size(64) fn contacts(@builtin(global_invocation_id) id:vec3u) {
|
|
2501
2502
|
let i=id.x;if(i>=min(counters[6],params.manifolds)){return;}
|
|
2502
2503
|
let m=manifolds[i];if((m.ids.w&15u)==0u || (m.ids.w&0x80000000u)!=0u){return;}
|
|
2503
|
-
let a=m.ids.x;let b=m.ids.y;
|
|
2504
|
+
let a=m.ids.x;let b=m.ids.y;
|
|
2505
|
+
manifoldImpact(m);
|
|
2504
2506
|
let up=dot(m.geo.xyz,params.up.xyz);
|
|
2505
|
-
if(
|
|
2506
|
-
if(
|
|
2507
|
+
if(up>0.3){support(a,b);}
|
|
2508
|
+
if(up < -0.3){support(b,a);}
|
|
2507
2509
|
}
|
|
2508
2510
|
@compute @workgroup_size(64) fn joints(@builtin(global_invocation_id) id:vec3u) {
|
|
2509
2511
|
let i=id.x;if(i>=params.joints){return;}let j=info[i];if(j.x==0||j.y<0){return;}
|
|
@@ -2513,6 +2515,7 @@ fn impact(a:u32,b:u32) {
|
|
|
2513
2515
|
let i=id.x;if(i>=params.count||states[i].mass<=0.){return;}
|
|
2514
2516
|
if(states[i].eligible==0u){wake(i);return;}
|
|
2515
2517
|
if(atomicLoad(&globals[0])!=0u || counters[3]!=0u || counters[4]!=0u){wake(i);return;}
|
|
2518
|
+
states[i].pad2=select(0u,1u,speed(i)<=params.speed && (params.gravity==0. || atomicLoad(&states[i].support)!=0u));
|
|
2516
2519
|
if(states[i].asleep==0u){
|
|
2517
2520
|
if(speed(i)<=params.speed && (params.gravity==0. || atomicLoad(&states[i].support)!=0u)) {states[i].quiet+=params.dt;}
|
|
2518
2521
|
else {states[i].quiet=0.;}
|
|
@@ -2522,101 +2525,184 @@ fn impact(a:u32,b:u32) {
|
|
|
2522
2525
|
}
|
|
2523
2526
|
if(states[i].asleep!=0u){atomicAdd(&globals[1],1u);}
|
|
2524
2527
|
}
|
|
2525
|
-
`;var Gl=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
2526
|
-
struct State { mass:f32, quiet:f32, asleep:u32, support:atomic<u32>, eligible:u32, parent:atomic<u32>, wake:atomic<u32>, pad2:u32 }
|
|
2527
|
-
struct Manifold { ids:vec4u, geo:vec4f }
|
|
2528
|
-
struct Params { count:u32,joints:u32,manifolds:u32,forceWake:u32,speed:f32,delay:f32,dt:f32,gravity:f32,up:vec4f }
|
|
2529
|
-
@group(0) @binding(0) var<storage,read_write> bodies:array<Body
|
|
2530
|
-
@group(0) @binding(1) var<storage,read_write> states:array<State
|
|
2531
|
-
@group(0) @binding(2) var<storage,read> info:array<vec4i
|
|
2532
|
-
@group(0) @binding(3) var<storage,read> manifolds:array<Manifold
|
|
2533
|
-
@group(0) @binding(4) var<storage,read> counters:array<u32
|
|
2534
|
-
@group(0) @binding(5) var<storage,read_write> globals:array<atomic<u32
|
|
2535
|
-
@group(0) @binding(6) var<uniform> params:Params
|
|
2536
|
-
fn speed(i:u32)->f32 {return length(bodies[i].velocity.xyz)+length(bodies[i].angular.xyz)*bodies[i].moment.w;}
|
|
2537
|
-
fn wake(i:u32) {if(states[i].mass>0.){bodies[i].size.w=states[i].mass;states[i].asleep=0u;states[i].quiet=0.;}}
|
|
2538
|
-
@compute @workgroup_size(64) fn before(@builtin(global_invocation_id) id:vec3u) {
|
|
2539
|
-
let i=id.x;if(i>=params.count){return;}
|
|
2540
|
-
if(params.forceWake!=0u||atomicLoad(&globals[0])!=0u){wake(i);atomicStore(&states[i].parent,i);}
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
if(
|
|
2554
|
-
if(
|
|
2555
|
-
}
|
|
2556
|
-
@compute @workgroup_size(64) fn
|
|
2557
|
-
let i=id.x;if(i>=params.
|
|
2558
|
-
|
|
2559
|
-
}
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
if(
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
}
|
|
2572
|
-
if(
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
}
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
if(
|
|
2612
|
-
}
|
|
2613
|
-
|
|
2528
|
+
`;var Gl=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
2529
|
+
struct State { mass:f32, quiet:f32, asleep:u32, support:atomic<u32>, eligible:u32, parent:atomic<u32>, wake:atomic<u32>, pad2:u32 }
|
|
2530
|
+
struct Manifold { ids:vec4u, geo:vec4f }
|
|
2531
|
+
struct Params { count:u32,joints:u32,manifolds:u32,forceWake:u32,speed:f32,delay:f32,dt:f32,gravity:f32,up:vec4f }
|
|
2532
|
+
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
2533
|
+
@group(0) @binding(1) var<storage,read_write> states:array<State>;
|
|
2534
|
+
@group(0) @binding(2) var<storage,read> info:array<vec4i>;
|
|
2535
|
+
@group(0) @binding(3) var<storage,read> manifolds:array<Manifold>;
|
|
2536
|
+
@group(0) @binding(4) var<storage,read> counters:array<u32>;
|
|
2537
|
+
@group(0) @binding(5) var<storage,read_write> globals:array<atomic<u32>>;
|
|
2538
|
+
@group(0) @binding(6) var<uniform> params:Params;
|
|
2539
|
+
fn speed(i:u32)->f32 {return length(bodies[i].velocity.xyz)+length(bodies[i].angular.xyz)*bodies[i].moment.w;}
|
|
2540
|
+
fn wake(i:u32) {if(states[i].mass>0.){bodies[i].size.w=states[i].mass;states[i].asleep=0u;states[i].quiet=0.;states[i].pad2=0u;}}
|
|
2541
|
+
@compute @workgroup_size(64) fn before(@builtin(global_invocation_id) id:vec3u) {
|
|
2542
|
+
let i=id.x;if(i>=params.count){return;}
|
|
2543
|
+
if((params.forceWake&1u)!=0u||atomicLoad(&globals[0])!=0u){wake(i);atomicStore(&states[i].parent,i);}
|
|
2544
|
+
else if((states[i].pad2&10u)!=0u){let held=states[i].pad2&2u;wake(i);states[i].pad2|=held;}
|
|
2545
|
+
// Rebuild live connectivity after bodies wake or separate. Only sleeping
|
|
2546
|
+
// subtrees need history, since their static/static contact pairs are omitted.
|
|
2547
|
+
if(states[i].asleep==0u){atomicStore(&states[i].parent,i);}
|
|
2548
|
+
if((params.forceWake&2u)!=0u && speed(i)<=params.speed && states[i].eligible!=0u){states[i].pad2|=4u;}
|
|
2549
|
+
atomicStore(&states[i].wake,0u);
|
|
2550
|
+
atomicStore(&states[i].support,0u);
|
|
2551
|
+
}
|
|
2552
|
+
@compute @workgroup_size(1) fn clear() {atomicStore(&globals[0],0u);atomicStore(&globals[1],0u);}
|
|
2553
|
+
// Gather old roots before before() resets awake parents. Separate dispatches
|
|
2554
|
+
// prevent another workgroup from clearing a wake request before it is consumed.
|
|
2555
|
+
@compute @workgroup_size(64) fn requestWake(@builtin(global_invocation_id) id:vec3u){
|
|
2556
|
+
let i=id.x;if(i>=params.count || states[i].asleep==0u || (states[i].pad2&2u)==0u){return;}
|
|
2557
|
+
let r=root(i);if(r<params.count){atomicOr(&states[r].wake,8u);}
|
|
2558
|
+
}
|
|
2559
|
+
@compute @workgroup_size(64) fn gatherWake(@builtin(global_invocation_id) id:vec3u){
|
|
2560
|
+
let i=id.x;if(i>=params.count){return;}
|
|
2561
|
+
let r=root(i);if(r<params.count && (atomicLoad(&states[r].wake)&8u)!=0u){states[i].pad2|=8u;}
|
|
2562
|
+
}
|
|
2563
|
+
fn impact(a:u32,b:u32) {
|
|
2564
|
+
if(states[a].asleep!=0u && states[b].asleep==0u && speed(b)>2.*params.speed){signalWake(a);}
|
|
2565
|
+
}
|
|
2566
|
+
fn signalWake(i:u32){let r=root(i);if(r<params.count){atomicOr(&states[r].wake,1u);}else{atomicStore(&globals[0],1u);}}
|
|
2567
|
+
@compute @workgroup_size(64) fn contacts(@builtin(global_invocation_id) id:vec3u) {
|
|
2568
|
+
let i=id.x;if(i>=min(counters[6],params.manifolds)){return;}
|
|
2569
|
+
let m=manifolds[i];if((m.ids.w&15u)==0u || (m.ids.w&0x80000000u)!=0u){return;}
|
|
2570
|
+
let a=m.ids.x;let b=m.ids.y;
|
|
2571
|
+
manifoldImpact(m);
|
|
2572
|
+
let up=dot(m.geo.xyz,params.up.xyz);
|
|
2573
|
+
if(up>0.3){support(a,b);}
|
|
2574
|
+
if(up < -0.3){support(b,a);}
|
|
2575
|
+
if((params.forceWake&2u)!=0u){
|
|
2576
|
+
if(states[b].mass<=0. && speed(b)<=params.speed && up>0.3 && states[a].mass>0.){atomicOr(&states[root(a)].wake,2u);}
|
|
2577
|
+
if(states[a].mass<=0. && speed(a)<=params.speed && up< -0.3 && states[b].mass>0.){atomicOr(&states[root(b)].wake,2u);}
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
@compute @workgroup_size(64) fn joints(@builtin(global_invocation_id) id:vec3u) {
|
|
2581
|
+
let i=id.x;if(i>=params.joints){return;}let j=info[i];if(j.x==0||j.y<0){return;}
|
|
2582
|
+
impact(u32(j.y),u32(j.z));impact(u32(j.z),u32(j.y));
|
|
2583
|
+
}
|
|
2584
|
+
@compute @workgroup_size(64) fn rest(@builtin(global_invocation_id) id:vec3u) {
|
|
2585
|
+
let i=id.x;if(i>=params.count||states[i].mass<=0.){return;}
|
|
2586
|
+
if(states[i].eligible==0u){wake(i);return;}
|
|
2587
|
+
let r=root(i);
|
|
2588
|
+
let flags=atomicLoad(&states[min(r,params.count-1u)].wake);
|
|
2589
|
+
if(r>=params.count || (flags&1u)!=0u || atomicLoad(&globals[0])!=0u || counters[3]!=0u || counters[4]!=0u){wake(i);return;}
|
|
2590
|
+
let initiallyQuiet=(states[i].pad2&4u)!=0u;
|
|
2591
|
+
let held=(states[i].pad2&2u)!=0u;
|
|
2592
|
+
states[i].pad2=select(0u,1u,speed(i)<=params.speed && (params.gravity==0. || atomicLoad(&states[i].support)!=0u));
|
|
2593
|
+
if(states[i].asleep==0u){
|
|
2594
|
+
if(speed(i)<=params.speed && (params.gravity==0. || atomicLoad(&states[i].support)!=0u)) {states[i].quiet+=params.dt;}
|
|
2595
|
+
else {states[i].quiet=0.;}
|
|
2596
|
+
if(!held && ((states[i].quiet>=params.delay && (flags&4u)==0u) || ((params.forceWake&2u)!=0u && initiallyQuiet && (flags&6u)==2u))){
|
|
2597
|
+
states[i].asleep=1u;bodies[i].size.w=0.;bodies[i].velocity=vec4f(0.);bodies[i].angular=vec4f(vec3f(0.),bodies[i].angular.w);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
if(states[i].asleep!=0u){atomicAdd(&globals[1],1u);}
|
|
2601
|
+
}
|
|
2602
|
+
// Startup sleep is restricted to contact islands connected to stationary solid
|
|
2603
|
+
// support, with no moving or sleep-excluded member. Floating groups stay awake.
|
|
2604
|
+
@compute @workgroup_size(64) fn activity(@builtin(global_invocation_id) id:vec3u){
|
|
2605
|
+
let i=id.x;if(i>=params.count||states[i].mass<=0.){return;}
|
|
2606
|
+
var busy=false;
|
|
2607
|
+
if((params.forceWake&2u)!=0u){busy=(states[i].pad2&4u)==0u;}
|
|
2608
|
+
else if(states[i].asleep==0u){
|
|
2609
|
+
busy=speed(i)>params.speed;
|
|
2610
|
+
if(states[i].eligible!=0u && (states[i].pad2&2u)==0u){
|
|
2611
|
+
busy=busy || states[i].quiet+params.dt<params.delay || (params.gravity!=0. && atomicLoad(&states[i].support)==0u);
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
if(busy){let r=root(i);if(r<params.count){atomicOr(&states[r].wake,4u);}}
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// Dynamic connectivity is conservative: unions persist while asleep because
|
|
2618
|
+
// collision detection intentionally omits pairs of sleeping bodies. The
|
|
2619
|
+
// awake bodies rebuild their links each step. Shared static ground never joins
|
|
2620
|
+
// otherwise independent piles. No thread waits for another workgroup.
|
|
2621
|
+
fn root(index:u32)->u32 {
|
|
2622
|
+
var i=index;
|
|
2623
|
+
for(var attempt=0u;attempt<256u;attempt++) {
|
|
2624
|
+
if(i>=params.count){atomicStore(&globals[0],1u);return params.count;}
|
|
2625
|
+
let p=atomicLoad(&states[i].parent);
|
|
2626
|
+
if(p==i){return i;}
|
|
2627
|
+
if(p>i){atomicStore(&globals[0],1u);return params.count;}
|
|
2628
|
+
let gp=atomicLoad(&states[p].parent);
|
|
2629
|
+
atomicMin(&states[i].parent,gp);
|
|
2630
|
+
i=gp;
|
|
2631
|
+
}
|
|
2632
|
+
// Excessive depth is a safe wake-all fallback, never a cross-group wait.
|
|
2633
|
+
atomicStore(&globals[0],1u);return params.count;
|
|
2634
|
+
}
|
|
2635
|
+
fn connect(a:u32,b:u32) {
|
|
2636
|
+
if(a>=params.count || b>=params.count || states[a].mass<=0. || states[b].mass<=0.){return;}
|
|
2637
|
+
for(var attempt=0u;attempt<256u;attempt++) {
|
|
2638
|
+
let ra=root(a);let rb=root(b);
|
|
2639
|
+
if(ra==rb || ra>=params.count || rb>=params.count){return;}
|
|
2640
|
+
let hi=max(ra,rb);let lo=min(ra,rb);
|
|
2641
|
+
let result=atomicCompareExchangeWeak(&states[hi].parent,hi,lo);
|
|
2642
|
+
if(result.exchanged){return;}
|
|
2643
|
+
}
|
|
2644
|
+
atomicStore(&globals[0],1u);
|
|
2645
|
+
}
|
|
2646
|
+
@compute @workgroup_size(64) fn connectContacts(@builtin(global_invocation_id) id:vec3u){
|
|
2647
|
+
let i=id.x;if(i>=min(counters[6],params.manifolds)){return;}
|
|
2648
|
+
let m=manifolds[i];if((m.ids.w&15u)==0u || (m.ids.w&0x80000000u)!=0u){return;}
|
|
2649
|
+
connect(m.ids.x,m.ids.y);
|
|
2650
|
+
}
|
|
2651
|
+
@compute @workgroup_size(64) fn connectJoints(@builtin(global_invocation_id) id:vec3u){
|
|
2652
|
+
let i=id.x;if(i>=params.joints){return;}let j=info[i];
|
|
2653
|
+
if(j.x!=0 && j.y>=0 && j.z>=0){connect(u32(j.y),u32(j.z));}
|
|
2654
|
+
}
|
|
2655
|
+
`;var Fl=`struct Contact {rA:vec3f,key:u32,rB:vec3f,c0x:f32,pen:vec3f,c0y:f32,lam:vec3f,c0z:f32}
|
|
2656
|
+
@group(0) @binding(7) var<storage,read> contactPoints:array<Contact>;
|
|
2657
|
+
fn rotated(q:vec4f,r:vec3f)->vec3f {return r+2.*cross(q.xyz,cross(q.xyz,r)+q.w*r);}
|
|
2658
|
+
fn contactVelocity(i:u32,r:vec3f,previous:bool)->vec3f {
|
|
2659
|
+
// Prediction is skipped while asleep. The last inertial target can retain
|
|
2660
|
+
// gravity from the last awake step, which must not be counted as an impact.
|
|
2661
|
+
if(states[i].asleep!=0u){return vec3f(0.);}
|
|
2662
|
+
var v=bodies[i].velocity.xyz;
|
|
2663
|
+
var w=bodies[i].angular.xyz;
|
|
2664
|
+
var q=bodies[i].rot;
|
|
2665
|
+
if(previous){
|
|
2666
|
+
// The inertial target retains the incoming velocity after the solve. Remove
|
|
2667
|
+
// this step's gravity increment so resting contacts do not count as impacts.
|
|
2668
|
+
v=(bodies[i].inertialPos.xyz-bodies[i].initialPos.xyz)/params.dt;
|
|
2669
|
+
if(bodies[i].size.w>0.){v-=params.up.xyz*(params.gravity*params.dt);}
|
|
2670
|
+
let a=bodies[i].inertialRot;let b=bodies[i].initialRot;
|
|
2671
|
+
w=2.*(-a.w*b.xyz+b.w*a.xyz-cross(a.xyz,b.xyz))/(dot(b,b)*params.dt);
|
|
2672
|
+
q=b;
|
|
2673
|
+
}
|
|
2674
|
+
return v+cross(w,rotated(q,r));
|
|
2675
|
+
}
|
|
2676
|
+
fn manifoldImpact(m:Manifold){
|
|
2677
|
+
let a=m.ids.x;let b=m.ids.y;
|
|
2678
|
+
if(states[a].asleep==states[b].asleep){return;}
|
|
2679
|
+
// A rotating face can approach at one corner while its first contact recedes.
|
|
2680
|
+
for(var k=0u;k<(m.ids.w&15u);k++){
|
|
2681
|
+
let c=contactPoints[m.ids.z+k];
|
|
2682
|
+
contactImpact(a,b,m.geo.xyz,c.rA,c.rB);contactImpact(b,a,-m.geo.xyz,c.rB,c.rA);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
fn contactImpact(a:u32,b:u32,n:vec3f,rA:vec3f,rB:vec3f) {
|
|
2686
|
+
if(states[a].asleep==0u || states[b].asleep!=0u){return;}
|
|
2687
|
+
let incoming=dot(contactVelocity(b,rB,true)-contactVelocity(a,rA,true),n);
|
|
2688
|
+
let outgoing=dot(contactVelocity(b,rB,false)-contactVelocity(a,rA,false),n);
|
|
2689
|
+
if(max(incoming,outgoing)>2.*params.speed){signalWake(a);}
|
|
2690
|
+
}
|
|
2691
|
+
fn support(a:u32,b:u32) {
|
|
2692
|
+
// Grounded, quiet awake bodies can support the next course. This lets a stack
|
|
2693
|
+
// accumulate quiet time together instead of stopping one course at a time.
|
|
2694
|
+
if(states[b].mass<=0. || states[b].asleep!=0u || ((states[b].pad2&1u)!=0u && speed(b)<=params.speed)) {
|
|
2695
|
+
atomicStore(&states[a].support,1u);
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
`;var Za=class{constructor(e){this.device=e,this.targets=new Map,this.bits=new Float32Array(1),this.words=new Uint32Array(this.bits.buffer)}target(e){let t=this.targets.get(e);return t||this.targets.set(e,t={pending:new Map}),t}uint(e,t,i){this.target(e).pending.set(t,i>>>0)}float(e,t,i){this.bits[0]=i,this.uint(e,t,this.words[0])}get pending(){for(let e of this.targets.values())if(e.pending.size)return!0;return!1}flush(e){let t=this.device,i=e,r=0,o=0,a=0,n=0;for(let[l,c]of this.targets){let u=c.pending.size;if(!u)continue;(!c.data||c.data.length<u*2)&&(c.data=new Uint32Array(Math.max(u*2,(c.data?.length??0)*2,128)));let f=Array.from(c.pending.keys()),d=!0;for(let m=1;m<u;m++)if(f[m]<f[m-1]){d=!1;break}d||f.sort((m,h)=>m-h);let p=1;for(let m=1;m<u;m++)f[m]!==f[m-1]+1&&p++;if(p<=4||u<=8){for(let m=0;m<u;m++)c.data[m]=c.pending.get(f[m]);for(let m=0;m<u;){let h=m+1;for(;h<u&&f[h]===f[h-1]+1;)h++;t.queue.writeBuffer(l,f[m]*4,c.data.buffer,m*4,(h-m)*4),r++,m=h}n+=u*4}else{let m=u*8,h=Math.min(t.limits.maxBufferSize,t.limits.maxStorageBufferBindingSize);if(m>h)throw Error("Property batch exceeds GPU buffer limits");for(let A=0;A<u;A++)c.data[A*2]=f[A],c.data[A*2+1]=c.pending.get(f[A]);(!c.upload||c.upload.size<m)&&(c.upload?.destroy(),c.upload=t.createBuffer({size:Math.min(h,m*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),c.group=null),t.queue.writeBuffer(c.upload,0,c.data.buffer,0,m),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"applyEdits",module:t.createShaderModule({code:`
|
|
2614
2699
|
@group(0) @binding(0) var<storage,read_write> outputWords:array<u32>;
|
|
2615
2700
|
@group(0) @binding(1) var<storage,read> edits:array<vec2u>;
|
|
2616
2701
|
@compute @workgroup_size(64) fn applyEdits(@builtin(global_invocation_id) id:vec3u){
|
|
2617
2702
|
let i=id.x+id.y*${t.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
2618
2703
|
if(i>=arrayLength(&edits)){return;}let edit=edits[i];outputWords[edit.x]=edit.y;
|
|
2619
|
-
}`})}}),(!c.group||c.bytes!==m)&&(c.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}},{binding:1,resource:{buffer:c.upload,size:m}}]}),c.bytes=m),i??=t.createCommandEncoder({label:"AVBD property edits"});let g=i.beginComputePass();g.setPipeline(this.pipeline),g.setBindGroup(0,c.group);let b=Math.ceil(u/64),y=t.limits.maxComputeWorkgroupsPerDimension;g.dispatchWorkgroups(Math.min(b,y),Math.ceil(b/y)),g.end(),r++,o++,
|
|
2704
|
+
}`})}}),(!c.group||c.bytes!==m)&&(c.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}},{binding:1,resource:{buffer:c.upload,size:m}}]}),c.bytes=m),i??=t.createCommandEncoder({label:"AVBD property edits"});let g=i.beginComputePass();g.setPipeline(this.pipeline),g.setBindGroup(0,c.group);let b=Math.ceil(u/64),y=t.limits.maxComputeWorkgroupsPerDimension;g.dispatchWorkgroups(Math.min(b,y),Math.ceil(b/y)),g.end(),r++,o++,n+=m}a+=u,c.pending.clear()}!e&&i&&t.queue.submit([i.finish()]),a&&(this.lastBatch={words:a,writes:r,dispatches:o,uploadedBytes:n,submissions:!e&&i?1:0})}destroy(){for(let e of this.targets.values())e.upload?.destroy();this.targets.clear()}forget(e){this.targets.get(e)?.upload?.destroy(),this.targets.delete(e)}};function oe(s){return s.propertyEdits??=new Za(s.device)}var Fi=class{constructor(e,t={}){if(this.world=e,this.device=e.device,this.speed=t.speedThreshold??.03,this.delay=t.timeThreshold??.5,t.startAsleep!==void 0&&typeof t.startAsleep!="boolean")throw Error("startAsleep must be a boolean");if(t.islands!==void 0&&typeof t.islands!="boolean")throw Error("Sleep islands must be a boolean");if(this.islands=t.islands??(e.gpu.bodyCount>=4096&&!e.gpu.portableContactMath&&!e.gpu.dispatchIsolation),this.initializing=!!t.startAsleep&&this.islands,!Number.isFinite(this.speed)||this.speed<=0||!Number.isFinite(this.delay)||this.delay<=0)throw Error("Sleep speedThreshold and timeThreshold must be positive and finite");let i=this.device,r=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.state=i.createBuffer({size:e.capacity*32,usage:r}),this.globals=i.createBuffer({size:8,usage:r}),this.params=i.createBuffer({size:48,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let o=i.createShaderModule({code:`${this.islands?Gl:Dl}
|
|
2705
|
+
${Fl}`});this.layout=i.createBindGroupLayout({entries:["storage","storage","read-only-storage","read-only-storage","read-only-storage","storage","uniform","read-only-storage"].map((c,u)=>({binding:u,visibility:GPUShaderStage.COMPUTE,buffer:{type:c}}))});let a=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipelines=Object.fromEntries(["before","clear",...this.islands?["requestWake","gatherWake","connectContacts","connectJoints","activity"]:[],"contacts","joints","rest"].map(c=>[c,i.createComputePipeline({layout:a,compute:{module:o,entryPoint:c}})]));let n=new Float32Array(e.capacity*8),l=new Uint32Array(n.buffer);for(let c of e.aggregates)if(!c.disposed){let u=(c.gpuSlot??e.gpu.gpuIndex(c.index))*8;n[u]=c.rigid.mass,l[u+4]=c.allowSleep===!1||c.rigid.allowSleep===!1?0:1,this.islands&&(l[u+5]=u/8)}i.queue.writeBuffer(this.state,0,n),this.wakeRequested=!1}register(e,t,i=!0){let r=new Float32Array([t,0,0,0,0,0,0,0]);new Uint32Array(r.buffer)[4]=i?1:0,this.islands&&(new Uint32Array(r.buffer)[5]=e);let o=new Uint32Array(r.buffer),a=oe(this.world.gpu);for(let n=0;n<8;n++)a.uint(this.state,e*8+n,o[n]);this.wakeRequested=!0}bindContacts(){let e=this.world.gpu,t=e.contactStorage,i=[e.bodyBuffer,this.state,e.infoBuffer,t.manifolds,t.counters,this.globals,this.params,t.contacts],r=this.groups?.find(o=>o.buffers.every((a,n)=>a===i[n]));r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((o,a)=>({binding:a,resource:{buffer:o}}))})},this.groups=[...(this.groups??[]).slice(-1),r]),this.group=r.group}before(e){this.world.gpu.flushPropertyEdits(e);let{gpu:t}=this.world,i=this.paramData??=hi(12),{u:r,f:o}=i;r[0]=t.bodyCount,r[1]=t.jointCount,r[2]=t.manifoldCapacity,r[3]=Number(this.wakeRequested)|(this.initializing?2:0),o[4]=this.speed,o[5]=this.delay,o[6]=t.params.dt,o[7]=t.params.gravity,o[8]=t.params.up[0],o[9]=t.params.up[1],o[10]=t.params.up[2],Qt(this.device,this.params,i),this.wakeRequested=!1,this.bindContacts(),this.run([...this.islands&&this.localWakeRequested?[["requestWake",t.bodyCount],["gatherWake",t.bodyCount]]:[],["before",t.bodyCount],["clear",1]],e),this.localWakeRequested=!1}wakeBody(e){if(!Number.isInteger(e)||e<0||e>=this.world.gpu.bodyCount)throw Error("Sleeping wake requires a live GPU body index");oe(this.world.gpu).uint(this.state,e*8+7,2),this.localWakeRequested=!0}after(e){let t=this.world.gpu;this.bindContacts(),this.run([...this.islands?[["connectContacts",t.manifoldCapacity],["connectJoints",t.jointCount]]:[],["contacts",t.manifoldCapacity],["joints",t.jointCount],...this.islands?[["activity",t.bodyCount]]:[],["rest",t.bodyCount]],e),this.initializing=!1}run(e,t){let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();r.setBindGroup(0,this.group);for(let[o,a]of e)a&&(r.setPipeline(this.pipelines[o]),r.dispatchWorkgroups(o==="clear"?1:Math.ceil(a/64)));r.end(),t||this.device.queue.submit([i.finish()])}async readStats(){let e=this.device,t=e.createBuffer({size:8,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let i=e.createCommandEncoder();i.copyBufferToBuffer(this.globals,0,t,0,8),e.queue.submit([i.finish()]),await t.mapAsync(GPUMapMode.READ);let r=new Uint32Array(t.getMappedRange());return{sleeping:r[1],wakeRequested:!!r[0]}}finally{t.destroy()}}dispose(){this.world.gpu.propertyEdits?.forget(this.state),this.state.destroy(),this.globals.destroy(),this.params.destroy()}};function uf(s){s=s.replaceAll("bodies[","localBodies["),s=s.replace("var<storage, read_write> bodies: array<Body>;","var<storage, read_write> globalBodies: array<Body>;");let e=s.indexOf(`@compute @workgroup_size(64)
|
|
2620
2706
|
fn primal(`),t=s.indexOf(`
|
|
2621
2707
|
/**`,e);if(e<0||t<e)throw Error("Review small-group primal integration");let i=s.slice(e,t).replace(`@compute @workgroup_size(64)
|
|
2622
2708
|
`,"").replace("fn primal(@builtin(local_invocation_id) lid: vec3u, @builtin(workgroup_id) wid: vec3u)","fn localPrimal(lid: vec3u, wid: vec3u, col: u32)").replaceAll("pc.color","col");return s.slice(0,e)+i+s.slice(t)+`
|
|
@@ -2646,7 +2732,7 @@ fn smallGroup(@builtin(local_invocation_id) lid: vec3u) {
|
|
|
2646
2732
|
}
|
|
2647
2733
|
if(lid.x<params.bodyCount){globalBodies[lid.x]=localBodies[lid.x];}
|
|
2648
2734
|
}
|
|
2649
|
-
`}function
|
|
2735
|
+
`}function Ul(s){if(!(s.smallGroups!==!1&&s.bodyCount>0&&s.bodyCount<=32&&s.jointCount===0&&s.solverDecision.selected==="standard"&&!s.scalarPrimal&&!s.portableContactMath&&!s.dispatchIsolation)){s.smallGroupPipeline=null;return}let t=`${s.sensorsEnabled?1:0}`;if(s.smallGroupPipelines??=new Map,!s.smallGroupPipelines.has(t)){let i=uf(s.solveSource(Ge,"standard"));s.smallGroupPipelines.set(t,s.device.createComputePipeline({label:"AVBD closed small group",layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.solve,s.layouts.pass]}),compute:{module:s.device.createShaderModule({code:i}),entryPoint:"smallGroup"}}))}s.smallGroupPipeline=s.smallGroupPipelines.get(t)}var en=new WeakMap;function It(s={}){let e={};for(let t of["breakStrain","breakForce"]){let i=s[t]??1/0;if(i!==1/0&&(!Number.isFinite(i)||i<=0))throw Error(`${t} must be positive or Infinity`);e[t]=i}return e}function vr(s,e){let t=It(e);Number.isFinite(t.breakStrain)||Number.isFinite(t.breakForce)?en.set(s,t):en.delete(s)}var Ui=s=>en.get(s);function io(s){let e="fn dualJoint(j: u32) {";if(s.split(e).length!==2)throw Error("Review spring fracture kernel integration");return s.replace(e,`${e}
|
|
2650
2736
|
if (info[j].x == T_SPRING) {
|
|
2651
2737
|
var material = joints[j];
|
|
2652
2738
|
let strainLimit = material.lamLin.w;
|
|
@@ -2668,7 +2754,7 @@ fn smallGroup(@builtin(local_invocation_id) lid: vec3u) {
|
|
|
2668
2754
|
}
|
|
2669
2755
|
}
|
|
2670
2756
|
}
|
|
2671
|
-
`)}var
|
|
2757
|
+
`)}var ff=["auto","standard","optimized","points"];function xr({requested:s="auto",bodyCount:e,constraintCount:t=0,contactScheduling:i="manifolds",custom:r=!1}){if(!ff.includes(s))throw Error("solverMode must be 'auto', 'standard', 'optimized' or 'points'");if(!["manifolds","points"].includes(i))throw Error("contactScheduling must be 'manifolds' or 'points'");if(r&&s!=="auto")throw Error("A scene-specific solve shader cannot be replaced with a rigid-body solver mode");let o=r?"custom":s!=="auto"?s:t>0||e<5e4?"standard":i==="points"?"points":"optimized",a=r?"This scene supplies its own solver. Automatic keeps that implementation.":s!=="auto"?"Explicit implementation override (developer setting).":t>0?"Work layout selected for joints and springs.":e<5e4?"Work layout selected for smaller rigid scenes.":o==="points"?"Large rigid scene with a measured shared-contact-points layout.":"Work layout selected for large rigid scenes.";return{requested:s,selected:o,reason:a,bodyCount:e,constraintCount:t}}var ro=`// Portable hierarchical PLOC: Morton ordering and independent SAH treelets.
|
|
2672
2758
|
// Dispatch boundaries publish each hierarchy level; no cross-workgroup spin waits.
|
|
2673
2759
|
struct Node {
|
|
2674
2760
|
lo: vec3f,
|
|
@@ -2936,7 +3022,7 @@ fn ropes(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
2936
3022
|
}
|
|
2937
3023
|
links[build.escapeOffset + id] = escape;
|
|
2938
3024
|
}
|
|
2939
|
-
`;var
|
|
3025
|
+
`;var Nl=`// Stable 4-bit GPU radix sort; PrefixScan scans digit-major block counts.
|
|
2940
3026
|
struct SortSettings { count: u32, blocks: u32, shift: u32, _pad: u32 }
|
|
2941
3027
|
@group(0) @binding(0) var<uniform> sort: SortSettings;
|
|
2942
3028
|
@group(0) @binding(1) var<storage, read> input: array<vec2u>;
|
|
@@ -2984,10 +3070,10 @@ fn scatterDigits(
|
|
|
2984
3070
|
}
|
|
2985
3071
|
output[hist[digit * sort.blocks + wid.x] + rank] = value;
|
|
2986
3072
|
}\r
|
|
2987
|
-
`;var
|
|
2988
|
-
fn gridCount`),
|
|
2989
|
-
fn findPairs`);if(
|
|
2990
|
-
${
|
|
3073
|
+
`;var pf=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,Rt=(s,e=128)=>Math.max(1,Math.ceil(s/e)),mf=4294967295,tn=fi.indexOf(`@compute @workgroup_size(64)
|
|
3074
|
+
fn gridCount`),rn=fi.indexOf("/** Binary search"),ql=fi.indexOf(`@compute @workgroup_size(64)
|
|
3075
|
+
fn findPairs`);if(tn<0||rn<tn||ql<rn)throw Error("Pinned broadphase changed; review H-PLOC integration");var gf=ro.slice(0,ro.indexOf("@group(0)")),bf=(fi.slice(0,tn)+fi.slice(rn,ql)).replace("var<storage, read_write> grid: array<atomic<u32>>;","var<storage, read_write> tree: array<Node>;")+`
|
|
3076
|
+
${gf}
|
|
2991
3077
|
@group(1) @binding(0) var<uniform> build: BuildSettings;
|
|
2992
3078
|
@group(1) @binding(1) var<storage,read> links: array<u32>;
|
|
2993
3079
|
@compute @workgroup_size(64)
|
|
@@ -3010,10 +3096,10 @@ fn findTreePairs(@builtin(global_invocation_id) gid:vec3u) {
|
|
|
3010
3096
|
at=links[build.escapeOffset+at];
|
|
3011
3097
|
} else {at=node.left;}
|
|
3012
3098
|
}
|
|
3013
|
-
}`,so=class{constructor(e,t,{rebuildInterval:i=64}={}){if(!Number.isInteger(i)||i<1)throw Error("BVH rebuildInterval must be a positive integer");if(this.device=e,this.capacity=t.bodyCapacity,this.capacity>16*e.limits.maxComputeWorkgroupsPerDimension)throw Error("H-PLOC capacity exceeds the adapter dispatch limit; select the grid");this.bodyBuffer=t.bodyBuffer,this.rebuildInterval=i,this.nodeCapacity=2*this.capacity+16*Math.ceil(Math.log2(Math.max(2,this.capacity)))+32,this.buffers=[],this.uniforms=[],this.stats={builds:0,refits:0,nodes:0,extraBytes:0};let r=(u,f,d=
|
|
3099
|
+
}`,so=class{constructor(e,t,{rebuildInterval:i=64}={}){if(!Number.isInteger(i)||i<1)throw Error("BVH rebuildInterval must be a positive integer");if(this.device=e,this.capacity=t.bodyCapacity,this.capacity>16*e.limits.maxComputeWorkgroupsPerDimension)throw Error("H-PLOC capacity exceeds the adapter dispatch limit; select the grid");this.bodyBuffer=t.bodyBuffer,this.rebuildInterval=i,this.nodeCapacity=2*this.capacity+16*Math.ceil(Math.log2(Math.max(2,this.capacity)))+32,this.buffers=[],this.uniforms=[],this.stats={builds:0,refits:0,nodes:0,extraBytes:0};let r=(u,f,d=pf())=>{if(d&GPUBufferUsage.STORAGE&&f>e.limits.maxStorageBufferBindingSize)throw Error(`${u} exceeds the adapter storage binding limit`);let p=e.createBuffer({label:u,size:Math.max(16,f),usage:d});return this.buffers.push(p),this.stats.extraBytes+=p.size,p};this.nodes=r("H-PLOC nodes",this.nodeCapacity*32),this.links=r("H-PLOC parents and escape links",this.nodeCapacity*8),this.keys=[r("Morton keys A",this.capacity*8),r("Morton keys B",this.capacity*8)],this.clusters=r("H-PLOC level roots",this.capacity*8+128),this.bounds=r("H-PLOC scene bounds",(Rt(this.capacity)*2+256)*16),this.sortBlocks=Rt(this.capacity),this.hist=r("Morton radix counts",this.sortBlocks*16*4),this.scan=new tt(e,this.hist,0,this.sortBlocks*16);let o=(u,f)=>({binding:u,visibility:GPUShaderStage.COMPUTE,buffer:{type:f}});this.layout=e.createBindGroupLayout({entries:[o(0,"uniform"),o(1,"read-only-storage"),...Array.from({length:5},(u,f)=>o(f+2,"storage"))]}),this.extraLayout=e.createBindGroupLayout({entries:[o(0,"uniform"),o(1,"read-only-storage")]});let a=e.createShaderModule({label:"portable H-PLOC",code:ge+ro+`
|
|
3014
3100
|
@compute @workgroup_size(128)
|
|
3015
3101
|
fn initializeLinks(@builtin(global_invocation_id) gid:vec3u){if(gid.x<build.escapeOffset){links[gid.x]=0xffffffffu;}}
|
|
3016
|
-
`}),
|
|
3102
|
+
`}),n=e.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["leaves","refitLeaves","reduceBounds","mortonCodes","seedClusters","miniHploc","refitTreelets","ropes","initializeLinks"].map(u=>[u,e.createComputePipeline({layout:n,compute:{module:a,entryPoint:u}})]));let l=e.createShaderModule({label:"stable Morton radix",code:Nl});this.sortLayout=e.createBindGroupLayout({entries:[o(0,"uniform"),o(1,"read-only-storage"),o(2,"storage"),o(3,"storage")]});let c=e.createPipelineLayout({bindGroupLayouts:[this.sortLayout]});this.sortPipes=Object.fromEntries(["countDigits","scatterDigits"].map(u=>[u,e.createComputePipeline({layout:c,compute:{module:l,entryPoint:u}})])),this.traverse=e.createComputePipeline({label:"H-PLOC collision traversal",layout:e.createPipelineLayout({bindGroupLayouts:[t.layouts.broad,this.extraLayout]}),compute:{module:e.createShaderModule({code:bf}),entryPoint:"findTreePairs"}}),this.configure(t.bodyCount)}uniform(e){let t=this.device.createBuffer({size:e.length*4,usage:GPUBufferUsage.UNIFORM,mappedAtCreation:!0});return new Uint32Array(t.getMappedRange()).set(e),t.unmap(),this.uniforms.push(t),t}configure(e){if(e===this.count)return;if(e>this.capacity)throw Error("H-PLOC body capacity exceeded");for(let d of this.uniforms)d.destroy();this.uniforms=[],this.count=e,this.built=!1,this.stepsSinceBuild=0;let t=[],i=e,r=e,o=0,a=e,n=e===0?mf:0;for(;i>1;){let d=Math.ceil(i/16);t.push({inputCount:i,nodeBase:r,inputOffset:o,outputOffset:a,blocks:d}),d===1&&(n=r+i-2),r+=d*15,o=a,a+=d,i=d}this.stats.nodes=e?2*e-1:0;let l=(d={})=>{let p=this.uniform([e,d.nodeBase??0,d.inputOffset??0,d.outputOffset??0,n,this.sortBlocks,0,d.boundsOffset??0,d.inputCount??e,this.nodeCapacity,0,0]);return{group:this.device.createBindGroup({layout:this.layout,entries:[p,this.bodyBuffer,this.nodes,this.keys[0],this.bounds,this.links,this.clusters].map((h,g)=>({binding:g,resource:{buffer:h}}))}),buffer:p}};this.base=l(),this.levels=t.map(d=>({...d,...l(d)})),this.reductions=[];let c=Rt(e),u=0,f=c*2;for(;c>1;){let d=Rt(c);this.reductions.push({blocks:d,...l({inputCount:c,inputOffset:u,outputOffset:f})}),u=f,f+=d*2,c=d}this.morton=l({boundsOffset:u}),this.sortGroups=Array.from({length:8},(d,p)=>{let m=this.uniform([e,this.sortBlocks,p*4,0]);return this.device.createBindGroup({layout:this.sortLayout,entries:[m,this.keys[p%2],this.keys[1-p%2],this.hist].map((h,g)=>({binding:g,resource:{buffer:h}}))})}),this.extra=this.device.createBindGroup({layout:this.extraLayout,entries:[{binding:0,resource:{buffer:this.base.buffer}},{binding:1,resource:{buffer:this.links}}]}),this.broadBuffers=null}encodeBuild(e){let t=(r,o,a)=>{e.setPipeline(this.pipes[r]),e.setBindGroup(0,o),e.dispatchWorkgroups(a)},i=!this.built||this.stepsSinceBuild>=this.rebuildInterval;if(this.lastRebuilt=i,t(i?"leaves":"refitLeaves",this.base.group,Rt(this.count)),i){t("initializeLinks",this.base.group,Rt(this.nodeCapacity));for(let r of this.reductions)t("reduceBounds",r.group,r.blocks);t("mortonCodes",this.morton.group,Rt(this.count));for(let r of this.sortGroups)e.setPipeline(this.sortPipes.countDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks),this.scan.encode(e),e.setPipeline(this.sortPipes.scatterDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks);t("seedClusters",this.base.group,Rt(this.count));for(let r of this.levels)t("miniHploc",r.group,r.blocks);t("ropes",this.base.group,Rt(this.nodeCapacity)),this.built=!0,this.stepsSinceBuild=0,this.stats.builds++}else{for(let r of this.levels)t("refitTreelets",r.group,Rt(r.blocks,64));this.stats.refits++}this.stepsSinceBuild++}encodePairs(e,t){this.configure(t.bodyCount);let i=[t.paramsBuffer,t.bodyBuffer,this.nodes,t.pairBuffer,t.counterBuffer,t.staticBuffer,t.jointBuffer,t.filterBuffer];(!this.broadBuffers||i.some((r,o)=>r!==this.broadBuffers[o]))&&(this.broadBuffers=i,this.broad=this.device.createBindGroup({layout:t.layouts.broad,entries:i.map((r,o)=>({binding:o,resource:{buffer:r}}))})),e.setPipeline(this.traverse),e.setBindGroup(0,this.broad),e.setBindGroup(1,this.extra),e.dispatchWorkgroups(Rt(this.count,64))}destroy(){this.scan.destroy();for(let e of[...this.buffers,...this.uniforms])e.destroy()}};function Vl(s,e={}){let t,i,r=!0,o=new Map,a=l=>{if(!i&&!l)return;let c=e.timestampWrites,u=s.beginComputePass({...e,timestampWrites:c&&(r||l)?{querySet:c.querySet,...r&&c.beginningOfPassWriteIndex!==void 0?{beginningOfPassWriteIndex:c.beginningOfPassWriteIndex}:{},...l&&c.endOfPassWriteIndex!==void 0?{endOfPassWriteIndex:c.endOfPassWriteIndex}:{}}:void 0});if(i){u.setPipeline(i.pipeline);for(let[f,d]of i.bindings)u.setBindGroup(f,...d);u[i.method](...i.args)}u.end(),i=null,r=!1},n=(l,c)=>{a(!1),i={pipeline:t,bindings:new Map(o),method:l,args:c}};return{setPipeline(l){t=l},setBindGroup(l,c,u){o.set(l,u===void 0?[c]:[c,[...u]])},dispatchWorkgroups(...l){n("dispatchWorkgroups",l)},dispatchWorkgroupsIndirect(...l){n("dispatchWorkgroupsIndirect",l)},end(){a(!0)}}}var pt=s=>Math.ceil(s/64);function Wl(s){(s.radiiDirty||s.noCollideDirty)&&s.uploadStatics();let e=s.params;s.fixedColors&&(s.device.queue.writeBuffer(s.colorBuffer,0,s.fixedColors),s.colorCap=Math.max(1,...s.fixedColors.map(k=>k===255?0:k+1))),s.stepCount++,s.writeParams(),s.writePassConstants(e.iterations,e.alpha);let t=s.parity,i=s.bodyCapacity,r=s.bodyCount,o=s.jointCount,a=s.groups,n=s.device.createCommandEncoder({label:"AVBD app step"});s.encodeStepPrelude?.(n),s.bvh||n.clearBuffer(s.gridBuffer,0,(2*s.tableSize+1)*4),n.clearBuffer(s.tableBuffer),n.clearBuffer(s.adjBuffer,0,(2*i+1)*4);let l=!!s.detailCallback,c=l||s.timingCallback!==null&&s.timing!==null,u=c||s.splitPasses,f=l?s.detailTiming:s.timing,d=[],p,m,h,g=(k,P)=>{if(p&&!l&&(m===P||!u))return;p?.end(),m=P;let C=l?d.length:Se.indexOf(P);d.push({name:k,aggregate:P});let _={label:k,timestampWrites:c?{querySet:f.querySet,beginningOfPassWriteIndex:C*2,endOfPassWriteIndex:C*2+1}:void 0};p=s.dispatchIsolation?Vl(n,_):n.beginComputePass(_),h=void 0},b=k=>{h!==k&&(p.setBindGroup(0,k),h=k)},y=(k,P,C)=>{C<=0||(p.setPipeline(s.pipes[k]),b(P),p.dispatchWorkgroups(C))},A=(k,P,C)=>{p.setPipeline(s.pipes[k]),b(P),p.dispatchWorkgroupsIndirect(s.argsBuffer,C*4)};if(g("contactCache","collision"),y("beginFrame",a.broad,1),e.reuseContacts&&y("updateRefs",a.refs,pt(r)),y("argsPrev",a.args,1),A("hashInsert",a.contacts[t],6),g("broadphaseBuild","collision"),s.bvh?(s.bvh.configure(r),s.bvh.encodeBuild(p),h=void 0):(y("gridCount",a.broad,pt(r)),s.gridScan.encode(p),h=void 0,y("gridScatter",a.broad,pt(r))),g("broadphasePairs","collision"),s.bvh?(s.bvh.encodePairs(p,s),h=void 0):y("findPairs",a.broad,pt(r)),g("narrowphase","collision"),y("argsPairs",a.args,1),A("narrowphase",a.contacts[t],0),y("argsContacts",a.args,1),g("adjacency","adjacency"),y("degreeJoints",a.topo[t],pt(o)),A("degreeContacts",a.topo[t],3),s.adjScan.encode(p),h=void 0,y("fillJoints",a.topo[t],pt(o)),A("fillContacts",a.topo[t],3),y("sortAdjacency",a.topo[t],pt(r)),g("coloring","coloring"),!s.fixedColors){y("colorCompact",a.topo[t],pt(r)),y("colorMark",a.topo[t],pt(r));for(let k=0;k<s.colorRounds;k++)y(k%2===0?"colorRoundAB":"colorRoundBA",a.topo[t],pt(r))}y("colorCount",a.topo[t],s.colorGroups),s.colorHistScan.encode(p),h=void 0,y("colorStarts",a.topo[t],1),y("argsColors",a.args,1),y("colorScatter",a.topo[t],s.colorGroups);let v=s.passOffsets??=[0],x,w,B,L=k=>{w===p&&B===k||(v[0]=k*256,p.setBindGroup(1,s.passGroup,v),w=p,B=k)},M=()=>{x!==p&&(p.setBindGroup(0,a.solve[t]),x=p)};if(g("warmStart","solve"),M(),L(0),o>0&&(p.setPipeline(s.pipes.warmStartJoints),p.dispatchWorkgroups(pt(o))),p.setPipeline(s.pipes.warmStartBodies),r&&p.dispatchWorkgroups(pt(r)),s.smallGroupPipeline)g("smallGroupSolve","solve"),M(),L(0),p.setPipeline(s.smallGroupPipeline),p.dispatchWorkgroups(1);else for(let k=0;k<e.iterations;k++){g("bodySolve","solve"),M(),p.setPipeline(s.pipes.primal);for(let P=0;P<s.colorCap;P++)L(k*(s.colorCap+1)+P),p.dispatchWorkgroupsIndirect(s.argsBuffer,(12+3*P)*4);g("contactUpdate","solve"),M(),L(k*(s.colorCap+1)+s.colorCap),p.setPipeline(s.pipes.dual),p.dispatchWorkgroupsIndirect(s.argsBuffer,36)}g("velocities","solve"),M(),L(0),p.setPipeline(s.pipes.updateVelocities),r&&p.dispatchWorkgroups(pt(r)),p.end();let I=2*(l?d.length:Se.length);if(c&&(n.resolveQuerySet(f.querySet,0,I,f.resolve,0),n.copyBufferToBuffer(f.resolve,0,f.read,0,I*8)),s.parity=1-t,s.encodeStepPostlude?.(n),s.device.queue.submit([n.finish()]),!c)return;let E=l?s.detailCallback:s.timingCallback;l?s.detailCallback=null:s.timingCallback=null,s.timingBusy=!0,f.read.mapAsync(GPUMapMode.READ).then(()=>{let k=new BigUint64Array(f.read.getMappedRange()).slice();if(f.read.unmap(),s.timingBusy=!1,s.destroyed){s.releaseAppTiming(),s.releaseTiming();return}let P=(_,z)=>Number(k[z]-k[_])/1e6,C={total:P(0,I-1),broadphase:s.broadphase,bvhRebuilt:s.bvh?.lastRebuilt??!1};if(l){C.details={};for(let[_,z]of d.entries()){let R=P(_*2,_*2+1);C[z.aggregate]=(C[z.aggregate]??0)+R,C.details[z.name]=(C.details[z.name]??0)+R}}else Se.forEach((_,z)=>C[_]=P(z*2,z*2+1));E(C)},k=>{s.timingBusy=!1,s.profileErrors.push(k.message),s.destroyed&&(s.releaseAppTiming(),s.releaseTiming())})}function oo(s){let e=s.indexOf(`@compute @workgroup_size(256)
|
|
3017
3103
|
fn colorCount(`),t=s.indexOf(`
|
|
3018
3104
|
// 5.`,e);if(e<0||t<e)throw Error("Pinned color buckets changed; review the bitset rank kernel");return s.slice(0,e).replace(`var<workgroup> hist: array<atomic<u32>, 64>;
|
|
3019
3105
|
var<workgroup> chunkColors: array<u32, 256>;
|
|
@@ -3054,8 +3140,8 @@ fn colorCount(@builtin(global_invocation_id) gid:vec3u,
|
|
|
3054
3140
|
if(gid.x==0u){atomicStore(&color[params.colorHistOffset+MAX_COLORS*params.colorGroups],0u);}
|
|
3055
3141
|
}
|
|
3056
3142
|
|
|
3057
|
-
`+s.slice(t)}function
|
|
3058
|
-
for (var i = lo + 1u; i < hi; i++) {`,
|
|
3143
|
+
`+s.slice(t)}function ao(s,e){let t=s.bodyCount>=1024;if(s.bitsetColorBuckets===t)return;let i=s.colorBucketPipelines??={scan:s.pipes.colorCount};t&&!i.bitset&&(i.bitset=s.device.createComputePipeline({label:"Stable bitset color buckets",layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.topo]}),compute:{module:s.device.createShaderModule({code:e}),entryPoint:"colorCount"}})),s.pipes.colorCount=t?i.bitset:i.scan,s.bitsetColorBuckets=t}var Hl=` let list = params.adjListOffset;
|
|
3144
|
+
for (var i = lo + 1u; i < hi; i++) {`,sn=Ti(ge,br),$l=oo(sn);if(sn.split(Hl).length!==2)throw Error("Pinned adjacency sort changed; review the cached-key variant");var Jl=sn.replace(Hl,`
|
|
3059
3145
|
let list = params.adjListOffset;
|
|
3060
3146
|
let count = hi - lo;
|
|
3061
3147
|
if (count <= 16u) {
|
|
@@ -3084,13 +3170,13 @@ fn colorCount(@builtin(global_invocation_id) gid:vec3u,
|
|
|
3084
3170
|
}
|
|
3085
3171
|
return;
|
|
3086
3172
|
}
|
|
3087
|
-
for (var i = lo + 1u; i < hi; i++) {`);function
|
|
3088
|
-
if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function
|
|
3173
|
+
for (var i = lo + 1u; i < hi; i++) {`);function on(s){return s.replace("return m.ids.w & 15u;","return select(m.ids.w & 15u, 0u, (m.ids.w & 0x80000000u) != 0u);").replace("bodies[i].initialPos = pos;","bodies[i].initialPos = vec4f(pos.xyz, bodies[i].initialPos.w);")}function no(s){return s.replace("let generated = prev.ids.w >> 4u;","let generated = (prev.ids.w >> 4u) & 0x07ffffffu;").replace("reuseContacts(a, b))","bodies[a].initialPos.w == 0.0 && bodies[b].initialPos.w == 0.0 && reuseContacts(a, b))").replace("count | (params.step << 4u)","count | ((params.step & 0x07ffffffu) << 4u) | select(0u, 0x80000000u, bodies[a].initialPos.w != 0.0 || bodies[b].initialPos.w != 0.0)")}function an(s=Ti(ge,br)){return s.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
|
|
3174
|
+
if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function nn(s){let e=s.device,t=e.createShaderModule({code:`
|
|
3089
3175
|
@group(0) @binding(0) var<storage,read_write> records:array<vec4f>;
|
|
3090
3176
|
@compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){
|
|
3091
3177
|
if(id.x*10u+2u>=arrayLength(&records)){return;}
|
|
3092
3178
|
records[id.x*10u+2u].w=0.;
|
|
3093
|
-
}`}),i=e.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"clear"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:s.bodyBuffer}}]}),o=e.createCommandEncoder(),
|
|
3179
|
+
}`}),i=e.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"clear"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:s.bodyBuffer}}]}),o=e.createCommandEncoder(),a=o.beginComputePass();a.setPipeline(i),a.setBindGroup(0,r),a.dispatchWorkgroups(Math.ceil(s.bodyCount/64)),a.end(),e.queue.submit([o.finish()])}var yf=`
|
|
3094
3180
|
fn contactGeneration(value: f32) -> u32 {
|
|
3095
3181
|
return select(0u, bitcast<u32>(value) ^ 0x3f800000u, value != 0.0);
|
|
3096
3182
|
}
|
|
@@ -3098,9 +3184,9 @@ fn contactMovedAfter(value: f32, generated: u32) -> bool {
|
|
|
3098
3184
|
let age = (contactGeneration(value) - generated) & 0x07ffffffu;
|
|
3099
3185
|
return age != 0u && age < 0x04000000u;
|
|
3100
3186
|
}
|
|
3101
|
-
`;function
|
|
3102
|
-
if (count == 0u || generated < moved) { return false; }`;if(!s.includes(e))throw Error("Contact cache layout changed");return
|
|
3103
|
-
if (count == 0u || contactMovedAfter(bodies[a].inertialPos.w, generation) || contactMovedAfter(bodies[b].inertialPos.w, generation)) { return false; }`)}var
|
|
3187
|
+
`;function Xl(s){let e="bodies[i].inertialPos.w = bitcast<f32>(params.step);";if(!s.includes(e))throw Error("Contact reference layout changed");return s.replace(e,"bodies[i].inertialPos.w = bitcast<f32>((params.step & 0x07ffffffu) ^ 0x3f800000u);")}function Yl(s){let e=` let moved = max(bitcast<u32>(bodies[a].inertialPos.w), bitcast<u32>(bodies[b].inertialPos.w));
|
|
3188
|
+
if (count == 0u || generated < moved) { return false; }`;if(!s.includes(e))throw Error("Contact cache layout changed");return yf+s.replace(e,` let generation = generated & 0x07ffffffu;
|
|
3189
|
+
if (count == 0u || contactMovedAfter(bodies[a].inertialPos.w, generation) || contactMovedAfter(bodies[b].inertialPos.w, generation)) { return false; }`)}var vf=["captureIncoming"],xf=["captureSolved","initialize",...Array.from({length:12},()=>["pairs","apply"]).flat(),"writeBack"],Af=`${ge}
|
|
3104
3190
|
struct Config { count:u32, joints:u32, capacity:u32, list:u32, threshold:f32, dt:f32, pad0:u32, pad1:u32 }
|
|
3105
3191
|
struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
|
|
3106
3192
|
struct Bounce { impulse:f32, delta:f32, reboundSpeed:f32, inverseMass:f32, rA:vec4f, rB:vec4f }
|
|
@@ -3176,7 +3262,7 @@ fn pointVelocity(i:u32,r:vec3f,incoming:bool)->vec3f {
|
|
|
3176
3262
|
let i=id.x;if(i>=cfg.count || bodies[i].size.w<=0.){return;}
|
|
3177
3263
|
bodies[i].vel=velocities[i].linear;bodies[i].angVel=velocities[i].angular;
|
|
3178
3264
|
}
|
|
3179
|
-
`;var Ar=class{constructor(e,{code:t=
|
|
3265
|
+
`;var Ar=class{constructor(e,{code:t=Af,dimension:i=3}={}){this.solver=e,this.dimension=i,this.device=e.device,this.active=new Set;let r=this.device,o=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST;this.materials=r.createBuffer({size:e.bodyCapacity*4,usage:o}),this.velocities=r.createBuffer({size:e.bodyCapacity*64,usage:o}),this.params=r.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.layout=r.createBindGroupLayout({entries:["uniform","storage","storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage"].map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))});let a=r.createShaderModule({code:t}),n=r.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["captureIncoming","captureSolved","initialize","pairs","apply","writeBack"].map(l=>[l,r.createComputePipeline({layout:n,compute:{module:a,entryPoint:l}})]))}set(e,t){if(!Number.isFinite(t)||t<0||t>1)throw Error("restitution must be between 0 and 1");t>0?this.active.add(e):this.active.delete(e),oe(this.solver).float(this.materials,e,t)}bind(){let e=this.solver,t=this.device,i=this.dimension===3?e.contactStorage:{manifolds:e.contactBuffers[1-e.parity],contacts:e.contactBuffers[1-e.parity],counters:e.counterBuffer},r=this.dimension===3?e.manifoldCapacity:e.contactCapacity;this.capacity!==r&&(this.bounce?.destroy(),this.capacity=r,this.bounce=t.createBuffer({size:Math.max(1,this.capacity)*48,usage:GPUBufferUsage.STORAGE}));let o=[this.params,e.bodyBuffer,this.velocities,this.materials,i.manifolds,i.contacts,e.adjBuffer,i.counters,this.bounce];(!this.buffers||o.some((c,u)=>c!==this.buffers[u]))&&(this.buffers=o,this.group=t.createBindGroup({layout:this.layout,entries:o.map((c,u)=>({binding:u,resource:{buffer:c}}))}));let a=this.paramData??=hi(8),{u:n,f:l}=a;n[0]=e.bodyCount,n[1]=e.jointCount,n[2]=r,n[3]=2*e.bodyCapacity+1,l[4]=1,l[5]=e.params.dt,Qt(t,this.params,a)}run(e,t){this.bind();let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();r.setBindGroup(0,this.group);for(let o of e)r.setPipeline(this.pipes[o]),r.dispatchWorkgroups(Math.max(1,Math.ceil((o==="initialize"||o==="pairs"?this.capacity:this.solver.bodyCount)/64)));r.end(),t||this.device.queue.submit([i.finish()])}before(e){this.active.size&&this.run(vf,e)}after(e){this.active.size&&this.run(xf,e)}destroy(){for(let e of["materials","velocities","params","bounce"])this[e]?.destroy()}};var Ql=new WeakMap,rs=s=>Ql.get(s);function wr(s,e){Ql.set(s,e)}var ln=(s,e,t)=>{if(s.split(e).length!==2)throw Error("Pinned angular solver layout changed");return s.replace(e,t)};function cn(s){return s=ln(s,"fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {",`
|
|
3180
3266
|
fn angularFrame(k:Joint,a:i32,b:i32)->mat3x3f {
|
|
3181
3267
|
let qa=rotA(a);let axis=qrotate(qa,k.rA.xyz);
|
|
3182
3268
|
let ta=qrotate(qa,k.c0Lin.xyz);let tb=qrotate(bodies[b].rot,k.c0Ang.xyz);
|
|
@@ -3227,10 +3313,10 @@ fn dualAngular(j:u32){
|
|
|
3227
3313
|
}
|
|
3228
3314
|
`+`
|
|
3229
3315
|
fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {
|
|
3230
|
-
if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),s=
|
|
3316
|
+
if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),s=ln(s," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if(j>=params.jointCount){return;}
|
|
3231
3317
|
if(info[j].x==3 || info[j].x==4){warmAngular(j);return;}
|
|
3232
|
-
if(info[j].x!=T_JOINT){return;}`),
|
|
3233
|
-
if(info[j].x==3 || info[j].x==4){dualAngular(j);return;}`)}function kt({type:s,axisA:e=[0,1,0],axisB:t=[0,1,0],speed:i=1,maxTorque:r=100,minAngle:o=-Math.PI,maxAngle:
|
|
3318
|
+
if(info[j].x!=T_JOINT){return;}`),ln(s,"fn dualJoint(j: u32) {",`fn dualJoint(j: u32) {
|
|
3319
|
+
if(info[j].x==3 || info[j].x==4){dualAngular(j);return;}`)}function kt({type:s,axisA:e=[0,1,0],axisB:t=[0,1,0],speed:i=1,maxTorque:r=100,minAngle:o=-Math.PI,maxAngle:a=Math.PI}={}){let n=l=>{let c=l&&typeof l.x=="number"?[l.x,l.y,l.z]:Array.from(l),u=Math.hypot(...c);if(c.length!==3||!c.every(Number.isFinite)||u<1e-8)throw Error("Angular axis requires three finite, nonzero components");return c.map(f=>f/u)};if(s!=="motor"&&s!=="limit")throw Error("Invalid angular constraint type");if(!Number.isFinite(i)||!Number.isFinite(r)||r<0)throw Error("Motor speed must be finite and maxTorque nonnegative");if(!Number.isFinite(o)||!Number.isFinite(a)||o<-Math.PI||a>Math.PI||o>a)throw Error("Hinge angles must be ordered within -PI..PI radians");return{type:s,axisA:n(e),axisB:n(t),speed:i,maxTorque:r,minAngle:o,maxAngle:a}}var Kl=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
3234
3320
|
struct Joint {penLin:vec4f,penAng:vec4f,lamLin:vec4f,lamAng:vec4f,c0Lin:vec4f,c0Ang:vec4f,rA:vec4f,rB:vec4f}
|
|
3235
3321
|
@group(0) @binding(0) var<storage,read> bodies:array<Body>;
|
|
3236
3322
|
@group(0) @binding(1) var<storage,read_write> joints:array<Joint>;
|
|
@@ -3248,7 +3334,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f{let t=2.*cross(q.xyz,v);return v+q.w*t+cross(q
|
|
|
3248
3334
|
if(length(tangentB)<1.e-6){tangentB=select(vec3f(0.,-axisB.z,axisB.y),vec3f(-axisB.y,axisB.x,0.),abs(axisB.x)>abs(axisB.z));}
|
|
3249
3335
|
joints[j].c0Lin=vec4f(tangent,0.);joints[j].c0Ang=vec4f(normalize(tangentB),0.);
|
|
3250
3336
|
}
|
|
3251
|
-
`;var
|
|
3337
|
+
`;var Zl=new WeakSet,Fe=s=>Zl.has(s);function Br(s,e,t,i=1,r=.6,o=[0,0,0]){if(!Number.isFinite(e)||e<=0||!Number.isFinite(t)||t<2*e)throw Error("Capsule height must be at least twice its positive radius");if(!Number.isFinite(i)||i<0)throw Error("Capsule density must be nonnegative");let a=new Ze(s,[2*e,t,2*e],i,r,o),n=t-2*e,l=Math.PI*e*e*n*i,c=4/3*Math.PI*e**3*i;a.mass=l+c;let u=l*e**2/2+c*2*e**2/5,f=l*(n**2/12+e**2/4)+c*(2*e**2/5+n**2/4+3*n*e/8);return a.moment.set([f,u,f]),a.radius=t/2,Zl.add(a),a}var ec=`// Exact rounded capsule geometry. Shape -1: radius=size.x/2, segment along Y,
|
|
3252
3338
|
// half-length=size.y/2-radius. No tessellation, endpoint-only collision test,
|
|
3253
3339
|
// shape storage buffer or iterative support search for capsule/sphere/box.
|
|
3254
3340
|
struct Capsule {a:vec3f,b:vec3f,r:f32}
|
|
@@ -3332,16 +3418,16 @@ fn capsuleBox(A:Capsule,B:Box,q:vec4f,sat:ptr<function,Sat>)->Found{
|
|
|
3332
3418
|
if(ends.count==2u){return ends;}
|
|
3333
3419
|
addFound(&found,B.c+qrotate(q,center)+n*A.r,B.c+qrotate(q,onBox),4u<<24u);return found;
|
|
3334
3420
|
}
|
|
3335
|
-
`;function
|
|
3336
|
-
fn narrowphase`;if(!s.includes(e))throw Error("Pinned capsule narrowphase layout changed");let t=s.includes("struct Poly");t&&(s=s.replace(e,
|
|
3337
|
-
`+e)),s=s.replace(e,
|
|
3421
|
+
`;function un(s){if(s.includes("fn capsuleOf"))throw Error("Capsule collision kernel installed twice");let e=`@compute @workgroup_size(64)
|
|
3422
|
+
fn narrowphase`;if(!s.includes(e))throw Error("Pinned capsule narrowphase layout changed");let t=s.includes("struct Poly");t&&(s=s.replace(e,Cf+`
|
|
3423
|
+
`+e)),s=s.replace(e,ec+`
|
|
3338
3424
|
`+e);let i=t?" else if(isHull(b)){found=capsulePoly(capsuleOf(a),polyOf(b),&sat);}":"",r=t?" else if(isHull(a)){found=capsulePoly(capsuleOf(b),polyOf(a),&sat);}":"";return s.replace("let anySphere = sphereA || sphereB;","let anySphere = sphereA || sphereB || capsuleA || capsuleB;").replace(" if (sphereA && sphereB) {",` let capsuleA=bodies[a].angVel.w == -1.;let capsuleB=bodies[b].angVel.w == -1.;
|
|
3339
3425
|
if(capsuleA || capsuleB){
|
|
3340
3426
|
if(capsuleA && capsuleB){found=capsuleCapsule(capsuleOf(a),capsuleOf(b),&sat);}
|
|
3341
3427
|
else if(capsuleA){if(sphereB){found=capsuleSphere(capsuleOf(a),B.c,B.h.x,&sat);}${i} else{found=capsuleBox(capsuleOf(a),B,bodies[b].rot,&sat);}}
|
|
3342
3428
|
else{if(sphereA){found=capsuleSphere(capsuleOf(b),A.c,A.h.x,&sat);}${r} else{found=capsuleBox(capsuleOf(b),A,bodies[a].rot,&sat);}
|
|
3343
3429
|
sat.n=-sat.n;for(var c=0u;c<found.count;c++){let p=found.xA[c];found.xA[c]=found.xB[c];found.xB[c]=p;}}
|
|
3344
|
-
} else if (sphereA && sphereB) {`)}var
|
|
3430
|
+
} else if (sphereA && sphereB) {`)}var Cf=`
|
|
3345
3431
|
fn capPolyAxis(A:Capsule,P:Poly,axis:vec3f,best:ptr<function,f32>,normal:ptr<function,vec3f>)->bool{
|
|
3346
3432
|
let l=length(axis);if(l<1.e-8){return true;}let n=axis/l;
|
|
3347
3433
|
let lo=min(dot(A.a,n),dot(A.b,n))-A.r;let hi=max(dot(A.a,n),dot(A.b,n))+A.r;
|
|
@@ -3373,7 +3459,7 @@ fn capsulePoly(A:Capsule,P:Poly,sat:ptr<function,Sat>)->Found{
|
|
|
3373
3459
|
}
|
|
3374
3460
|
(*sat).n=n;addFound(&found,points.a+n*A.r,points.b,4u<<24u);return found;
|
|
3375
3461
|
}
|
|
3376
|
-
`;var
|
|
3462
|
+
`;var fn=`// GPU queries against current poses. Rounded box/hull sweeps test offset faces
|
|
3377
3463
|
// and rounded edges, so expanding a bounding box cannot create corner hits.
|
|
3378
3464
|
struct Query { origin:vec4f, direction:vec4f }
|
|
3379
3465
|
struct Params { head:vec4u, flags:vec4u, ignored:array<vec4u,4> }
|
|
@@ -3527,24 +3613,24 @@ fn nearest(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.x||igno
|
|
|
3527
3613
|
fn owner(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.x||ignored(id.x)){return;}let h=trace(id.x,queries[id.y]);if(h.t>=0.&&bitcast<u32>(max(0.,h.t))==atomicLoad(&best[id.y*2u])){atomicMin(&best[id.y*2u+1u],id.x);}}
|
|
3528
3614
|
@compute @workgroup_size(64)
|
|
3529
3615
|
fn finish(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.y){return;}let index=atomicLoad(&best[id.x*2u+1u]);if(index==0xffffffffu){hits[id.x*2u]=vec4f(0.,0.,0.,-1.);return;}let h=trace(index,queries[id.x]);hits[id.x*2u]=vec4f(h.n,max(0.,h.t));hits[id.x*2u+1u]=vec4f(bitcast<f32>(index),0.,0.,0.);}
|
|
3530
|
-
`;var pi=class{constructor(e,t=3){this.solver=e,this.dimension=t,this.jobs=new Set,this.idle=[],this.destroyed=!1;let i=e.device,r=i.createShaderModule({label:`AVBD ${t}D queries`,code:t===2?
|
|
3616
|
+
`;var pi=class{constructor(e,t=3){this.solver=e,this.dimension=t,this.jobs=new Set,this.idle=[],this.destroyed=!1;let i=e.device,r=i.createShaderModule({label:`AVBD ${t}D queries`,code:t===2?fn.replace("const DIM=3u;","const DIM=2u;").replace("const STRIDE=10u;","const STRIDE=6u;"):fn});this.layout=i.createBindGroupLayout({entries:["read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform","read-only-storage"].map((a,n)=>({binding:n,visibility:GPUShaderStage.COMPUTE,buffer:{type:a}}))});let o=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["nearest","owner","finish"].map(a=>[a,i.createComputePipeline({layout:o,compute:{module:r,entryPoint:a}})])),this.dummy=i.createBuffer({size:16,usage:GPUBufferUsage.STORAGE})}acquire(e){if(this.destroyed)throw Error("GPU queries have been destroyed");let t=this.idle.findIndex(r=>r.capacity>=e),i=t>=0?this.idle.splice(t,1)[0]:this.idle.pop()??{};if(!i.capacity||i.capacity<e){let r=Math.min(65535,Math.max(e,(i.capacity??0)*2,16));for(let[o,a,n]of[["queryBuffer",r*32,GPUBufferUsage.STORAGE],["best",r*8,GPUBufferUsage.STORAGE],["hits",r*32,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC],["uniform",96,GPUBufferUsage.UNIFORM],["read",r*32,GPUBufferUsage.MAP_READ]])i[o]?.destroy(),i[o]=this.solver.device.createBuffer({size:a,usage:n|GPUBufferUsage.COPY_DST});i.capacity=r,i.buffers=null,i.data=new Float32Array(r*8),i.initial=new Uint32Array(r*2),i.params=new Uint32Array(24)}return this.jobs.add(i),i}release(e){if(e.read.unmap(),!this.destroyed&&e.capacity*104<=2*1024*1024&&this.idle.length<2)this.idle.push(e);else{for(let t of["queryBuffer","best","hits","uniform","read"])e[t].destroy();this.jobs.delete(e)}}async cast(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0,maxDistance:o}={}){if(this.destroyed)throw Error("GPU queries have been destroyed");let a=this.solver,n=a.device,l=this.dimension;if(!Number.isInteger(i)||i<0||i>4294967295)throw Error("Query mask must be an unsigned 32-bit integer");if(typeof r!="boolean")throw Error("includeTriggers must be boolean");if(t.length>16||t.some(d=>!Number.isInteger(d)||d<0||d>=a.bodyCount))throw Error("Queries can ignore up to 16 live body indices");if(e.length>Math.min(65535,n.limits.maxComputeWorkgroupsPerDimension))throw Error("Too many queries in one batch");let c=e.map(d=>{let p=Array.from(d.origin),m=Array.from(d.direction),h=d.radius??0,g=d.maxDistance??o??3e38;if(p.length!==l||m.length!==l||![...p,...m].every(Number.isFinite)||Math.hypot(...m)===0)throw Error(`A query requires ${l} finite components and a nonzero direction`);if(!Number.isFinite(h)||h<0||!Number.isFinite(g)||g<0)throw Error("Query radius and maximum distance must be finite and nonnegative");let b=Math.hypot(...m);return{origin:p,direction:m.map(y=>y/b),radius:h,maxDistance:g}});if(!c.length)return[];let u=c.length,f=this.acquire(u);try{let d=f.data;c.forEach((I,E)=>{d.set([...I.origin,...Array(3-l).fill(0),I.maxDistance,...I.direction,...Array(3-l).fill(0),I.radius],E*8)});let p=f.params;p.fill(0),p.set([a.bodyCount,u,t.length,i,r?0:1,l===3||a.filters?1:0,a.sensorsEnabled?1:0,l===2&&a.shapeBuffer?1:0]),p.set(t,8);let m=f.initial;for(let I=0;I<u;I++)m.set([2139095040,4294967295],I*2);let{queryBuffer:h,best:g,hits:b,uniform:y,read:A}=f;n.queue.writeBuffer(h,0,d.buffer,0,u*32),n.queue.writeBuffer(g,0,m.buffer,0,u*8),n.queue.writeBuffer(y,0,p);let v=[a.bodyBuffer,l===3?a.hullStorage:a.shapeBuffer??this.dummy,h,g,b,y,l===3?a.filterStorage:a.filters??this.dummy];(!f.buffers||v.some((I,E)=>I!==f.buffers[E]))&&(f.group=n.createBindGroup({layout:this.layout,entries:v.map((I,E)=>({binding:E,resource:{buffer:I}}))}),f.buffers=v);let x=n.createCommandEncoder(),w=x.beginComputePass();w.setBindGroup(0,f.group);for(let I of["nearest","owner"])w.setPipeline(this.pipes[I]),w.dispatchWorkgroups(Math.ceil(a.bodyCount/64),u);w.setPipeline(this.pipes.finish),w.dispatchWorkgroups(Math.ceil(u/64)),w.end(),x.copyBufferToBuffer(b,0,A,0,u*32),n.queue.submit([x.finish()]),await A.mapAsync(GPUMapMode.READ,0,u*32);let B=A.getMappedRange(0,u*32),L=new Float32Array(B),M=new Uint32Array(B);return c.map((I,E)=>{let k=L[E*8+3];if(k<0)return null;let P=Array.from(L.slice(E*8,E*8+l)),C=I.origin.map((_,z)=>_+k*I.direction[z]);return{index:M[E*8+4],distance:k,normal:P,center:C,point:C.map((_,z)=>_-I.radius*P[z])}})}finally{this.release(f)}}destroy(){this.destroyed=!0;for(let e of this.jobs)for(let t of["queryBuffer","best","hits","uniform","read"])e[t].destroy();this.jobs.clear(),this.idle.length=0,this.dummy.destroy()}};function lo(s,e){let t=this.passConstantCache??={alphas:[]},i=t.iterations!==s||t.colors!==this.colorCap||t.buffer!==this.passBuffer||!this.passGroup;for(let n=0;n<s;n++){let l=typeof e=="function"?e(n):e;t.alphas[n]!==l&&(i=!0),t.alphas[n]=l}if(!i)return;let r=this.colorCap+1,o=Math.max(s*r,1),a=o*256;(o>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"AVBD pass constants",size:a,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=o,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]})),(!t.data||t.data.byteLength<a)&&(t.data=new ArrayBuffer(Math.max(a,(t.data?.byteLength??0)*2)),t.u32=new Uint32Array(t.data),t.f32=new Float32Array(t.data)),t.u32[2]=s;for(let n=0;n<s;n++)for(let l=0;l<=this.colorCap;l++){let c=(n*r+l)*256/4;t.u32[c]=l,t.u32[c+2]=s,t.f32[c+1]=t.alphas[n]}this.device.queue.writeBuffer(this.passBuffer,0,t.data,0,a),t.iterations=s,t.colors=this.colorCap,t.buffer=this.passBuffer,t.uploads=(t.uploads??0)+1}function oc(s,e){return s.stepParamsCache??=hi(e)}function ac(s,e,t){let i=s.bodyCapacity;t[7]=s.bodyCount,t[8]=s.jointCount,t[9]=s.colorCap,e[10]=s.cellSize,t[11]=s.tableSize-1,e[12]=s.maxSmallRadius,t[13]=s.largeCount,t[14]=s.noCollideCount,t[15]=s.pairCapacity,t[16]=s.contactCapacity,t[17]=s.hashSize-1,t[18]=i+1,t[19]=2*i+1,t[20]=s.tableSize+1,t[21]=2*s.tableSize+1,t[22]=2*s.tableSize+1+i,t[23]=i,t[24]=s.colorHistOffset,t[25]=2*i,t[26]=s.colorGroups,t[27]=2*i+65,t[28]=s.colorRounds}function nc(s,e){let t=oc(this,32),{f:i,u:r}=t,o=this.params;i[0]=o.dt,i[1]=o.gravity,i[2]=o.beta,i[3]=o.gamma,i[4]=s,i[5]=o.vbdStiffness,r[6]=(o.vbd?1:0)|(o.stiffnessRescale?2:0)|(e?4:0)|(o.matchNearest?8:0),ac(this,i,r),Qt(this.device,this.paramsBuffer,t)}function lc(){let s=oc(this,es),{f:e,u:t}=s,i=this.params;e[0]=i.dt,e[1]=i.gravity,e[2]=i.betaLin,e[3]=i.betaAng,e[4]=i.gamma,e[5]=i.alpha,t[6]=(i.matchNearest?Xr:0)|(i.faceBias?Yr:0)|(i.reuseContacts?Qr:0)|(i.startAtRest?Kr:0)|(i.massPenalty?Zr:0),ac(this,e,t),t[29]=this.manifoldCapacity,t[30]=this.stepCount;let r=o=>Math.min(31,Math.max(0,Math.round(Math.log2(o))));t[31]=r(this.primalLanes[0])|r(this.primalLanes[1])<<8|r(this.primalLanes[2])<<16,e[32]=i.windSpeed*Math.cos(i.windAngle),e[33]=i.windSpeed*Math.sin(i.windAngle),e[34]=0,e[35]=i.windPressure,e[36]=i.windGust,e[40]=i.up[0],e[41]=i.up[1],e[42]=i.up[2],e[43]=0,Qt(this.device,this.paramsBuffer,s)}function cc(s){let e=(i,r)=>{if(s.split(i).length!==2)throw Error("Review packed Newton accumulator integration");s=s.replace(i,r)};e(` lin: mat3x3f,
|
|
3531
3617
|
ang: mat3x3f,`,` lin: mat2x3f,
|
|
3532
3618
|
ang: mat2x3f,`),e("fn addRow(acc:",`fn symmetricOuter(v: vec3f) -> mat2x3f {
|
|
3533
3619
|
return mat2x3f(v * v, vec3f(v.y * v.x, v.z * v.x, v.z * v.y));
|
|
3534
3620
|
}
|
|
3535
3621
|
|
|
3536
|
-
fn addRow(acc:`),e("outer(l, l) * k","symmetricOuter(l) * k"),e("outer(a, a) * k","symmetricOuter(a) * k"),e("mat3x3f(vec3f(g.x, 0.0, 0.0), vec3f(0.0, g.y, 0.0), vec3f(0.0, 0.0, g.z))","mat2x3f(g, vec3f(0.0))"),e("mat3x3f(vec3f(m, 0.0, 0.0), vec3f(0.0, m, 0.0), vec3f(0.0, 0.0, m))","mat2x3f(vec3f(m), vec3f(0.0))"),e("mat3x3f(vec3f(I.x, 0.0, 0.0), vec3f(0.0, I.y, 0.0), vec3f(0.0, 0.0, I.z))","mat2x3f(I, vec3f(0.0))");let t={"0][0":"0][0","0][1":"1][0","0][2":"1][1","1][1":"0][1","1][2":"1][2","2][2":"0][2"};return s=s.replace(/acc\.(lin|ang)\[(\d)\]\[(\d)\]/g,(i,r,o,
|
|
3622
|
+
fn addRow(acc:`),e("outer(l, l) * k","symmetricOuter(l) * k"),e("outer(a, a) * k","symmetricOuter(a) * k"),e("mat3x3f(vec3f(g.x, 0.0, 0.0), vec3f(0.0, g.y, 0.0), vec3f(0.0, 0.0, g.z))","mat2x3f(g, vec3f(0.0))"),e("mat3x3f(vec3f(m, 0.0, 0.0), vec3f(0.0, m, 0.0), vec3f(0.0, 0.0, m))","mat2x3f(vec3f(m), vec3f(0.0))"),e("mat3x3f(vec3f(I.x, 0.0, 0.0), vec3f(0.0, I.y, 0.0), vec3f(0.0, 0.0, I.z))","mat2x3f(I, vec3f(0.0))");let t={"0][0":"0][0","0][1":"1][0","0][2":"1][1","1][1":"0][1","1][2":"1][2","2][2":"0][2"};return s=s.replace(/acc\.(lin|ang)\[(\d)\]\[(\d)\]/g,(i,r,o,a)=>{let n=t[`${o}][${a}`];if(!n)throw Error("Unexpected Newton accumulator entry");return`acc.${r}[${n}]`}),s}var hn=["pos","rot","initialPos","initialRot","size","moment","inertialPos","inertialRot","vel","angVel"],kf=/bodies\[([^\]]+)\]\.([A-Za-z]+)/g;function Pf(s){let e=new Map;for(let t of s.matchAll(/\bfn (\w+)\(/g)){let i=t.index,r=s.indexOf("{",i),o=1,a=r+1;for(;o&&a<s.length;a++)s[a]==="{"&&o++,s[a]==="}"&&o--;if(o)throw Error("Review compact body function boundaries");e.set(t[1],{start:i,brace:r,end:a,text:s.slice(i,a),body:s.slice(r+1,a-1)})}return e}function pn(s){let e=Pf(s),t=m=>{let h=e.get(m);if(!h)throw Error(`Review compact body integration: ${m}`);return h},i=(m,h)=>m.replace(kf,(g,b,y)=>{let A=hn.indexOf(y);if(A<0)throw Error(`Review compact body field: ${y}`);return h?`bodies[compactBodySlot(u32(${b}), ${A}u)]`:`bodies[u32(${b}) * 10u + ${A}u]`}),r=m=>[...m.matchAll(/\b(\w+)\s*\(/g)].map(h=>h[1]).filter(h=>e.has(h)),o=new Set([...e].filter(([,m])=>m.body.includes("bodies[")).map(([m])=>m)),a;do{a=!1;for(let[m,h]of e)!o.has(m)&&r(h.body).some(g=>o.has(g))&&(o.add(m),a=!0)}while(a);let n=new Set,l=m=>{for(let h of r(t(m).body))!o.has(h)||n.has(h)||(n.add(h),l(h))};l("warmStartJoints"),l("warmStartBodies");let c=m=>m.replace(/\b(\w+)(?=\s*\()/g,h=>n.has(h)?`${h}BodyRecords`:h),u=[...n].map(m=>c(i(t(m).text,!1))).join(`
|
|
3537
3623
|
|
|
3538
3624
|
`),f=(m,h)=>{let g=t(m).body;for(let y of[" if (!solverStepValid()) { return; }"," let i = gid.x;"," if (i >= params.bodyCount) { return; }"]){if(g.split(y).length!==2)throw Error(`Review compact body entry: ${m}`);g=g.replace(y,"")}g=g.replace(/bodies\[i\]\.([A-Za-z]+)/g,"value.$1"),h||(g=c(g));let b=(y,A)=>A?`compactBodySlot(i, ${y}u)`:`i * 10u + ${y}u`;return`fn ${m}(@builtin(global_invocation_id) gid: vec3u) {
|
|
3539
3625
|
let i = gid.x;
|
|
3540
3626
|
let allocated = i < params.stateBOffset;
|
|
3541
3627
|
var value: Body;
|
|
3542
|
-
if (allocated) { value = Body(${
|
|
3628
|
+
if (allocated) { value = Body(${hn.map((y,A)=>`bodies[${b(A,h)}]`).join(", ")}); }
|
|
3543
3629
|
if (i < params.bodyCount && solverStepValid()) { ${g} }
|
|
3544
3630
|
// All threads finish reading their tile before any thread overwrites it.
|
|
3545
3631
|
// Inactive and unused records participate, including a partial final tile.
|
|
3546
3632
|
storageBarrier();
|
|
3547
|
-
if (allocated) { ${
|
|
3633
|
+
if (allocated) { ${hn.map((y,A)=>`bodies[${b(A,!h)}] = value.${y};`).join(`
|
|
3548
3634
|
`)} }
|
|
3549
3635
|
}`},d=[[t("warmStartJoints"),c(i(t("warmStartJoints").text,!1))],[t("warmStartBodies"),f("warmStartBodies",!1)],[t("updateVelocities"),f("updateVelocities",!0)]].sort(([m],[h])=>h.start-m.start);for(let[m,h]of d)s=s.slice(0,m.start)+h+s.slice(m.end);let p="var<storage, read_write> bodies: array<Body>;";if(s.split(p).length!==2)throw Error("Review compact body storage binding");return s=s.replace(p,"var<storage, read_write> bodies: array<vec4f>;"),i(s,!0)+`
|
|
3550
3636
|
${u}
|
|
@@ -3554,15 +3640,15 @@ fn compactBodySlot(i: u32, field: u32) -> u32 {
|
|
|
3554
3640
|
if (field < 4u) { return tile * 10u + (i % 64u) * 4u + field; }
|
|
3555
3641
|
return tile * 10u + width * 4u + (i % 64u) * 6u + field - 4u;
|
|
3556
3642
|
}
|
|
3557
|
-
`}var co=256;function
|
|
3643
|
+
`}var co=256;function uc(s){let e=(t,i)=>{if(s.split(t).length!==2)throw Error("Review parallel dual integration");s=s.replace(t,i)};return e("fn dualManifold(m: u32) {","fn dualManifold(m: u32, lane: u32) {"),e("for (var c = mf.ids.z; c < end; c++) {","for (var c = mf.ids.z + lane; c < end; c += 4u) {"),e(` let id = gid.x;
|
|
3558
3644
|
if (id < params.jointCount) { dualJoint(id); }
|
|
3559
3645
|
else if (id - params.jointCount < manifoldCount()) { dualManifold(id - params.jointCount); }`,` let id = (gid.x + gid.y * ${co*64}u) / 4u;
|
|
3560
3646
|
let lane = gid.x % 4u;
|
|
3561
3647
|
if (id < params.jointCount) { if (lane == 0u) { dualJoint(id); } }
|
|
3562
|
-
else if (id - params.jointCount < manifoldCount()) { dualManifold(id - params.jointCount, lane); }`),s}function
|
|
3648
|
+
else if (id - params.jointCount < manifoldCount()) { dualManifold(id - params.jointCount, lane); }`),s}function mn(s,e){let t=s.dualArgPipelines??={serial:s.pipes.argsContacts};if(e&&!t.parallel){let i=Nr(ge,Ys),r="setArgs(IA_CONSTRAINTS, params.jointCount + items);";if(i.split(r).length!==2)throw Error("Review parallel contact dispatch sizing");let o=i.replace(r,`let blocks = groupsFor((params.jointCount + items) * 4u);
|
|
3563
3649
|
args[IA_CONSTRAINTS] = min(blocks, ${co}u);
|
|
3564
3650
|
args[IA_CONSTRAINTS + 1u] = max(1u, (blocks + ${co-1}u) / ${co}u);
|
|
3565
|
-
args[IA_CONSTRAINTS + 2u] = 1u;`);t.parallel=s.device.createComputePipeline({layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.args]}),compute:{module:s.device.createShaderModule({code:o}),entryPoint:"argsContacts"}})}s.pipes.argsContacts=e?t.parallel:t.serial,s.parallelDual=e}var
|
|
3651
|
+
args[IA_CONSTRAINTS + 2u] = 1u;`);t.parallel=s.device.createComputePipeline({layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.args]}),compute:{module:s.device.createShaderModule({code:o}),entryPoint:"argsContacts"}})}s.pipes.argsContacts=e?t.parallel:t.serial,s.parallelDual=e}var gn=new WeakMap;function uo(s){return gn.get(s)}function fc(s,e){e?gn.set(s,Object.freeze({...e})):gn.delete(s)}function bn(s){let e=s.indexOf("/** A partial system per body slot handed between lanes (primal). */"),t=s.indexOf("fn accumulate(",e),i=s.lastIndexOf("/**",t);if(e<0||t<0||i<=e)throw Error("3D primal layout changed; review the compatibility kernel");return s.slice(0,e)+`
|
|
3566
3652
|
@compute @workgroup_size(64)
|
|
3567
3653
|
fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
3568
3654
|
let start = color[params.colorStartOffset + pc.color];
|
|
@@ -3572,7 +3658,7 @@ fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3572
3658
|
finishBody(i, accumulate(i, 0u, 1u));
|
|
3573
3659
|
}
|
|
3574
3660
|
|
|
3575
|
-
`+s.slice(i)}function mi(s,e,t){if(s.split(e).length!==2)throw Error("3D contact layout changed; review the compatibility kernel");return s.replace(e,t)}function fo(s,e,t){let i=new RegExp(`struct ${e} \\{[^}]*\\}`,"g");if([...s.matchAll(i)].length!==1)throw Error(`Expected one 3D ${e} layout`);return s.replace(i,t)}function
|
|
3661
|
+
`+s.slice(i)}function mi(s,e,t){if(s.split(e).length!==2)throw Error("3D contact layout changed; review the compatibility kernel");return s.replace(e,t)}function fo(s,e,t){let i=new RegExp(`struct ${e} \\{[^}]*\\}`,"g");if([...s.matchAll(i)].length!==1)throw Error(`Expected one 3D ${e} layout`);return s.replace(i,t)}function dc(s,e,t){let i=`fn ${e}(`;if(s.split(i).length!==2)throw Error(`Expected one 3D ${e} function`);let r=s.indexOf(i),a=s.indexOf("{",r)+1,n=1;for(;a<s.length&&n;a++)s[a]==="{"&&n++,s[a]==="}"&&n--;if(n)throw Error(`Incomplete 3D ${e} function`);return s.slice(0,r)+t(s.slice(r,a))+s.slice(a)}function yn(s){s=fo(s,"PairBody",`struct PairBody {
|
|
3576
3662
|
ids: vec4u,
|
|
3577
3663
|
rot: vec4f,
|
|
3578
3664
|
dLin: vec4f,
|
|
@@ -3582,7 +3668,7 @@ fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3582
3668
|
anchorB: vec4f, // xyz: anchor, w: normal error
|
|
3583
3669
|
penalty: vec4f, // xyz: penalties, w: first tangent error
|
|
3584
3670
|
force: vec4f, // xyz: forces, w: second tangent error
|
|
3585
|
-
}`);let e={rA:"bitcast<vec3f>(k.anchorA.xyz)",rB:"k.anchorB.xyz",key:"k.anchorA.w",c0x:"k.anchorB.w",c0y:"k.penalty.w",c0z:"k.force.w",pen:"k.penalty.xyz",lam:"k.force.xyz"};for(let t of["evalContact","accumulate","dualManifold"])s=
|
|
3671
|
+
}`);let e={rA:"bitcast<vec3f>(k.anchorA.xyz)",rB:"k.anchorB.xyz",key:"k.anchorA.w",c0x:"k.anchorB.w",c0y:"k.penalty.w",c0z:"k.force.w",pen:"k.penalty.xyz",lam:"k.force.xyz"};for(let t of["evalContact","accumulate","dualManifold"])s=dc(s,t,i=>i.replace(/\bk\.(rA|rB|key|c0x|c0y|c0z|pen|lam)\b/g,(r,o)=>e[o]));return s=mi(s,"contacts[c].lam = e.F;","contacts[c].force = vec4f(e.F, k.force.w);"),s=mi(s,"contacts[c].key =","contacts[c].anchorA.w ="),s=mi(s,"contacts[c].pen = pen;","contacts[c].penalty = vec4f(pen, k.penalty.w);"),s=dc(s,"evalContact",t=>{t=mi(t,` var e: ContactEval;
|
|
3586
3672
|
`,""),t=mi(t," e.rAW ="," let rAW ="),t=mi(t," e.rBW ="," let rBW ="),t=t.replaceAll("e.rAW","rAW").replaceAll("e.rBW","rBW"),t=mi(t," e.C ="," let C =");let i=t.indexOf(" var F ="),r=t.indexOf(" return e;",i);if(i<0||r<0)throw Error("Expected 3D contact force projection");return t.slice(0,i)+` let raw = k.penalty.xyz * C + k.force.xyz;
|
|
3587
3673
|
let normalForce = select(0.0, raw.x, raw.x < 0.0);
|
|
3588
3674
|
let bounds = abs(normalForce) * friction;
|
|
@@ -3600,7 +3686,7 @@ fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3600
3686
|
C: vec4f,
|
|
3601
3687
|
F: vec4f,
|
|
3602
3688
|
limits: vec4f, // x: tangent force length, y: Coulomb bound
|
|
3603
|
-
}`),s=s.replace(/\b(ev|e)\.(rAW|rBW|C|F)\b/g,"$1.$2.xyz").replace(/\be\.frictionScale\b/g,"e.limits.x").replace(/\be\.bounds\b/g,"e.limits.y"),s=fo(s,"PassConstants","struct PassConstants { data: vec4u, }"),s.replaceAll("pc.color","pc.data.x").replaceAll("pc.alpha","bitcast<f32>(pc.data.y)")}function
|
|
3689
|
+
}`),s=s.replace(/\b(ev|e)\.(rAW|rBW|C|F)\b/g,"$1.$2.xyz").replace(/\be\.frictionScale\b/g,"e.limits.x").replace(/\be\.bounds\b/g,"e.limits.y"),s=fo(s,"PassConstants","struct PassConstants { data: vec4u, }"),s.replaceAll("pc.color","pc.data.x").replaceAll("pc.alpha","bitcast<f32>(pc.data.y)")}function hc(s){if(!s.includes("struct Poly"))return s;let e=`@compute @workgroup_size(64)
|
|
3604
3690
|
fn narrowphase(@builtin(global_invocation_id) gid: vec3u)`;if(s.split(e).length!==2)throw Error("3D hull narrowphase changed; review the compatibility kernel");return s.replace(e,"fn narrowphasePair(gid: vec3u)")+`
|
|
3605
3691
|
@compute @workgroup_size(32)
|
|
3606
3692
|
fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
@@ -3608,9 +3694,9 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3608
3694
|
narrowphasePair(vec3u(gid.x * 2u + offset, 0u, 0u));
|
|
3609
3695
|
}
|
|
3610
3696
|
}
|
|
3611
|
-
`}function
|
|
3612
|
-
${
|
|
3613
|
-
${jl.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}})),(!this.frameData||this.frameData.length<e.length*4)&&(this.frameData=new Uint32Array(Math.max(e.length*4,(this.frameData?.length??0)*2,64)));for(let t=0;t<e.length;t++)this.frameData[t*4]=e[t].slot,this.frameData[t*4+1]=e[t].spring?1:0;this.runCapture("frame",this.framePipeline,this.frameData.subarray(0,e.length*4),e.length)}}runCapture(e,t,i,r){this.flushEdits?.();let o=this.device,n=(this.captureUploads??={})[e]??={};(!n.buffer||n.buffer.size<i.byteLength)&&(n.buffer?.destroy(),n.buffer=o.createBuffer({size:i.byteLength*2,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),n.buffers=null),o.queue.writeBuffer(n.buffer,0,i);let a=[this.bodyBuffer,this.jointBuffer,this.infoBuffer,n.buffer];(!n.buffers||a.some((d,p)=>d!==n.buffers[p])||n.bytes!==i.byteLength)&&(n.group=o.createBindGroup({layout:t.getBindGroupLayout(0),entries:a.map((d,p)=>({binding:p,resource:{buffer:d,...p===3?{size:i.byteLength}:{}}}))}),n.buffers=a,n.bytes=i.byteLength);let l=o.createCommandEncoder(),c=l.beginComputePass();c.setPipeline(t),c.setBindGroup(0,n.group);let u=Math.ceil(r/64),f=o.limits.maxComputeWorkgroupsPerDimension;c.dispatchWorkgroups(Math.min(u,f),Math.ceil(u/f)),c.end(),o.queue.submit([l.finish()])}setSpringMaterial(e,t={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4]!==gr)throw Error("Spring material requires an active spring slot");let i=It(t);!this.springFracture&&(Number.isFinite(i.breakStrain)||Number.isFinite(i.breakForce))&&(this.springFracture=!0,this.rebuildSolvePipelines());for(let[r,o]of[["breakStrain",11],["breakForce",15]])le(this).float(this.jointBuffer,e*32+o,i[r]===1/0?0:i[r]);this.wakeAll()}appendConstraints(...e){return this.flushPropertyEdits(),super.appendConstraints(...e)}appendJoint(e,t,i,r,o,n){let a=c=>c===1/0?3e38:c,[l]=this.appendConstraints(1,(c,u)=>{u[3]=a(o),u[7]=a(n),u[11]=3e38,u.set(i,24),u.set(r,28);let f=e>=0?this.bodies[e].size:[0,0,0],d=this.bodies[t].size;return u[15]=f.reduce((p,m,h)=>p+(m+d[h])**2,0),[1,e,t]});return this.wakeAll(),l}appendJoints(...e){let t=super.appendJoints(...e);return this.wakeAll(),t}appendSprings(...e){let t=super.appendSprings(...e);return this.wakeAll(),t}enableSleeping(e,t={}){this.sleeping||(this.sleeping=new Fi({device:this.device,gpu:this,capacity:this.bodyCapacity,aggregates:e.bodies.map((i,r)=>({rigid:i,index:r}))},t))}wakeAll(){this.sleeping&&(this.sleeping.wakeRequested=!0)}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}addBodies(e){this.flushPropertyEdits();let t=super.addBodies(e);if(t>=0)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)Fe(e[i])&&this.installCapsule(t+i);if(t>=0&&this.sleeping)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)this.sleeping.register(t+i,e[i].mass,e[i].allowSleep!==!1);return t}rewriteBodies(e,t){this.flushPropertyEdits(),super.rewriteBodies(e,t);for(let i=0;i<e.length;i++)Fe(t[i])&&this.installCapsule(e[i]);if(this.sleeping)for(let i=0;i<e.length;i++)this.sleeping.register(e[i],t[i].mass,t[i].allowSleep!==!1)}releaseJoints(e){this.flushPropertyEdits(),super.releaseJoints(e),this.wakeAll()}setWorldAnchor(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4+1]!==-1||t?.length!==3||!Array.from(t).every(Number.isFinite))throw Error("A world joint and finite xyz anchor are required");for(let i=0;i<3;i++)le(this).float(this.jointBuffer,e*32+24+i,t[i]);this.wakeAll()}disableConstraint(e){this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll()}setFilters(e,t,i){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");for(let o=0;o<e.length;o++){if(!Number.isInteger(e[o])||e[o]<0||e[o]>=this.bodyCount)throw Error("Collision filters require live body indices");for(let n of[t[o],i[o]])if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers")}let r=le(this);for(let o=0;o<e.length;o++)r.uint(this.filterBuffer,e[o]*2,t[o]),r.uint(this.filterBuffer,e[o]*2+1,i[o]);this.wakeAll()}profileNextStep(e){return this.detailCallback?!1:super.profileNextStep(t=>e({...t,broadphase:this.broadphase,bvhRebuilt:this.bvh?.lastRebuilt??!1}))}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}releaseAppTiming(){if(this.detailTiming){for(let e of["querySet","resolve","read"])this.detailTiming[e].destroy();this.detailTiming=null}}destroy(){this.passConstantCache=null;for(let e of Object.values(this.captureUploads??{}))e.buffer.destroy();this.captureUploads=null,this.frameData=null,this.propertyEdits?.destroy(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.sleeping?.dispose(),this.bvh?.destroy(),this.bvh=null,this.timingBusy||this.releaseAppTiming(),super.destroy(),this.solvePipelineCache.clear(),this.adjacencyPipelines={}}adapt(e){if(super.adapt(e),this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.adaptiveScheduling){let t=this.bodyCount>=25e4&&this.jointCount-this.freeSlots.length===0&&e.manifolds>=1.75*this.bodyCount&&!e.overflow&&!e.clashes;this.primalLanes=t?[2**31,2**31,2**31]:[...this.defaultPrimalLanes],this.schedulingDecision={bodyCount:this.bodyCount,manifolds:e.manifolds,dense:t,cachedAdjacency:this.adjacencyCached,reason:t?"Eight threads share each dense body solve":"Threads per body follow solve-group size"}}}selectAdjacencyPipeline(){let e=this.cacheAdjacencyKeys??this.bodyCount>=25e4;this.adjacencyCached!==e&&(e&&!this.adjacencyPipelines.cached&&(this.adjacencyPipelines.cached=this.device.createComputePipeline({label:"Cached adjacency keys",layout:this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]}),compute:{module:this.device.createShaderModule({code:$l}),entryPoint:"sortAdjacency"}})),this.pipes.sortAdjacency=e?this.adjacencyPipelines.cached:this.adjacencyPipelines.original,this.adjacencyCached=e)}};var va=new WeakMap,pc=[{name:"batched-grid",broadphase:"grid",dispatchIsolation:!1},{name:"isolated-grid",broadphase:"grid",dispatchIsolation:!0},{name:"batched-hploc",broadphase:"hploc",dispatchIsolation:!1},{name:"isolated-hploc",broadphase:"hploc",dispatchIsolation:!0},{name:"scalar-grid",broadphase:"grid",dispatchIsolation:!1,scalarPrimal:!0},{name:"scalar-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0},{name:"portable-contact-grid",broadphase:"grid",dispatchIsolation:!1,portableContactMath:!0},{name:"portable-contact-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0,portableContactMath:!0}];function mc(s){return va.has(s)||va.set(s,Cf(s)),va.get(s)}async function Cf(s,e){let t=[];for(let i of pc){let r=await _f(s,i,e);if(t.push(r),!!r.passed)return uc(s,{dispatchIsolation:i.dispatchIsolation,scalarPrimal:i.scalarPrimal??!1,portableContactMath:i.portableContactMath??!1,broadphase:i.broadphase==="hploc"?"hploc":void 0}),{...r,selected:i.name,compatibility:i.name!==pc[0].name,attempts:t}}return{...t[0],selected:null,compatibility:!1,attempts:t}}function Sf(s){let e=new ai;return e.iterations=5,s||(e.gravity=0),new Ze(e,[16,.25,16],0,.6,[0,-.125,0]),new Ze(e,[1,1,1],1,.6,[-2,s?2:.45,0]),nr(e,.5,1,.6,[0,s?2.3:.45,0]),Br(e,.25,1.6,1,.6,[2,s?2.5:.75,0]),e}async function bi(s,e,t=e.size){let i=s.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST});try{let r=s.createCommandEncoder();return r.copyBufferToBuffer(e,0,i,0,t),s.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ),i.getMappedRange().slice(0)}finally{i.destroy()}}async function kf(s){s.step();let e=await s.readCounters(),t=new Uint32Array(await bi(s.device,s.argsBuffer)),i=await Lf(s),r=new Uint32Array(i),o=new Float32Array(i),n=new Uint32Array(await bi(s.device,s.adjBuffer)),a=new Uint32Array(await bi(s.device,s.colorBuffer)),{manifolds:l,contacts:c}=s.contactStorage,u=await bi(s.device,l,Math.max(32,e.manifolds*32)),f=new Uint32Array(u),d=new Float32Array(u),p=new Float32Array(await bi(s.device,c,Math.max(64,e.contacts*64))),m=await s.readBodies(),h=e.manifolds>=3?await Pf(s):[],g=h.some(({contactEvaluation:b})=>b.returnedForce===0&&b.independentForce<-1e-5);return{expectedFloorPairs:3,counters:e,pairs:e.pairs?Array.from(await s.readPairs()):[],narrowphaseWorkgroups:Array.from(t.subarray(0,3)),solver:{scalarPrimal:s.scalarPrimal,portableContactMath:s.portableContactMath,lanes:[...s.primalLanes],contactWorkgroups:Array.from(t.subarray(3,6)),dualWorkgroups:Array.from(t.subarray(9,12)),colorWorkgroups:Array.from(t.subarray(12,12+3*s.colorCap)),adjacencyStarts:Array.from(n.subarray(0,5)),adjacencyEntries:Array.from(n.subarray(2*s.bodyCapacity+1,2*s.bodyCapacity+1+n[4])),colorStarts:Array.from(a.subarray(2*s.bodyCapacity,2*s.bodyCapacity+s.colorCap+1)),colorBodies:Array.from(a.subarray(2*s.bodyCapacity+65,2*s.bodyCapacity+68)),positionsAfterOverlap:[1,2,3].map(b=>Array.from(m.subarray(b*40,b*40+3))),manifolds:Array.from({length:e.manifolds},(b,y)=>({ids:Array.from(f.subarray(y*8,y*8+4)),normalAndFriction:Array.from(d.subarray(y*8+4,y*8+8))})),contacts:Array.from({length:e.contacts},(b,y)=>({anchorA:Array.from(p.subarray(y*16,y*16+3)),anchorB:Array.from(p.subarray(y*16+4,y*16+7)),gap:p[y*16+7],penalty:Array.from(p.subarray(y*16+8,y*16+11)),force:Array.from(p.subarray(y*16+12,y*16+15))})),serialSystems:h,failure:g?"contact-evaluation":null},filters:Array.from(new Uint32Array(await bi(s.device,s.filterBuffer))),params:{bodyCount:r[0],largeCount:r[1],noCollideCount:r[2],pairCapacity:r[3],contactCapacity:r[4],dt:o[5],cellSize:o[6],maxSmallRadius:o[7],up:Array.from(o.subarray(8,11)),alpha:o[11]},stage:e.pairs<3?"collision-pairs":e.manifolds<3?"contact-generation":"contacts-created"}}async function Pf(s){let e=s.portableContactMath?"bitcast<f32>(pc.data.y)":"pc.alpha",t=s.portableContactMath?"k.anchorB.w":"k.c0x",i=s.portableContactMath?"k.penalty.x":"k.pen.x",r=s.portableContactMath?"k.force.x":"k.lam.x",o=s.device.createShaderModule({code:s.solveSource(Ge)+`
|
|
3697
|
+
`}function pc(s,e){return e==="points"&&(s=cc(s)),(e==="points"||e==="optimized")&&(s=uc(s)),s}var gi=class extends eo{writeParams(){lc.call(this)}writePassConstants(e,t){lo.call(this,e,t)}constructor(e,t,i={}){if(i.smallGroups!==void 0&&typeof i.smallGroups!="boolean")throw Error("smallGroups must be a boolean");let r=uo(e),o=i.scalarPrimal??r?.scalarPrimal??!1,a=i.portableContactMath??r?.portableContactMath??!1,n=t.forces.some(v=>rs(v)),l=t.forces.some(v=>{let x=Ui(v);return x&&(Number.isFinite(x.breakStrain)||Number.isFinite(x.breakForce))}),c={requested:i.solverMode??"auto",contactScheduling:i.contactScheduling??"manifolds",custom:!!i.shaders?.solve},u=xr({...c,bodyCount:t.bodies.length,constraintCount:t.forces.filter(v=>v instanceof ke||v instanceof ft).length}),f=u.selected==="custom"?i.shaders.solve:u.selected==="optimized"?is:u.selected==="points"?to:void 0,d=r?.broadphase??i.broadphase??"auto";if(!["grid","hploc","auto"].includes(d))throw Error("broadphase must be 'grid', 'hploc' or 'auto'");let p=i.bvh?.rebuildInterval??64;if(!Number.isInteger(p)||p<1)throw Error("BVH rebuildInterval must be a positive integer");for(let[v,x]of[["minimumColors",64],["minimumColorRounds",32]]){let w=i[v]??0;if(!Number.isInteger(w)||w<0||w>x)throw Error(`${v} must be an integer between 0 and ${x}`);if(v==="minimumColorRounds"&&w%2)throw Error("minimumColorRounds must be even")}let m=pc(f??Ge,u.selected),h=Qa(Ka(n?cn(l?io(m):m):l?io(m):m));o&&(h=bn(h)),a&&(h=yn(h)),u.selected==="points"&&!o&&!a&&!r?.dispatchIsolation&&(h=pn(h));let g=!i.shaders?.contacts&&i.hulls!==!1&&e.limits.maxStorageBuffersPerShaderStage>=9&&t.bodies.some(v=>Ct(v)),b=!i.shaders?.contacts&&t.bodies.some(v=>Fe(v)),y=!i.shaders?.contacts&&t.bodies.some(v=>v.isTrigger);y&&(h=on(h));let A=v=>(b&&(v=un(v)),y&&(v=no(v)),v=Yl(v),a?hc(v):v);if(super(e,t,{...i,shaders:{...i.shaders,contacts:i.shaders?.contacts??A(yr(g)),solve:h,refs:i.shaders?.refs??(i.shaders?.contacts?void 0:Xl(Qs)),topology:i.shaders?.topology??(y?an():void 0)}}),this.solverPolicy=c,this.scalarPrimal=o,this.portableContactMath=a,y&&(this.sensorsEnabled=!0,nn(this)),o&&(this.primalLanes=[1,1,1]),this.dispatchIsolation=r?.dispatchIsolation??!1,!i.shaders?.contacts){this.contactShaders.custom=!1,this.hullShaders=g,this.capsulesEnabled=b;let v=this.contactShaders.make;this.contactShaders.make=x=>v(A(x))}this.springFracture=l,this.angularConstraints=n,this.customSolveSource=i.shaders?.solve,t.forces.forEach((v,x)=>{let w=Ui(v);w&&this.setSpringMaterial(x,w)}),this.adaptiveScheduling=o?!1:i.adaptiveScheduling??!0,this.defaultPrimalLanes=[...this.primalLanes],this.cacheAdjacencyKeys=i.cacheAdjacencyKeys,this.smallGroups=i.smallGroups,this.adjacencyPipelines={original:this.pipes.sortAdjacency},this.selectAdjacencyPipeline(),this.solverDecision=u,this.solvePipelineCache=new Map([[u.selected,Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(v=>[v,this.pipes[v]]))]]),mn(this,u.selected==="points"||u.selected==="optimized"),this.minimumColors=i.minimumColors??0,this.minimumColorRounds=i.minimumColorRounds??4,this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.broadphaseRequested=d,this.broadphase=d==="hploc"?"hploc":"grid",this.bvhOptions=i.bvh??{},this.bvh=null,this.detailCallback=null,this.detailTiming=null,this.profileErrors=[],this.selectBroadphase(),t.forces.forEach((v,x)=>{let w=rs(v);w&&this.setAngularConstraint(x,w)}),t.bodies.forEach((v,x)=>{Fe(v)&&this.installCapsule(this.gpuIndex(x))})}installCapsule(e){if(!this.capsulesEnabled){if(this.contactShaders.custom)throw Error("Analytic capsules require the built-in contact kernel");this.capsulesEnabled=!0;let t=this.contactShaders.make;this.contactShaders.make=i=>t(un(i)),this.contactShaders.make(yr(this.hullShaders??!1))}this.device.queue.writeBuffer(this.bodyBuffer,e*160+156,new Float32Array([-1]))}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new pi(this),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async sphereCast(e,t,i,r={}){let[o]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}sphereCastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new pi(this),this.shapeQueries.cast(e,t)}selectBroadphase(){if(this.broadphaseRequested!=="auto"){this.broadphaseDecision={requested:this.broadphaseRequested,selected:this.broadphase,reason:"Explicit selection"};return}let e=new Float64Array(this.bodyCount),t=0,i=0,r=0;for(let d=0;d<this.bodyCount;d++){let p=this.bodies[d];if(p.radius>0&&(e[t++]=p.radius),p.dynamic){i++;let[m,h,g]=p.size,b=Math.min(m,h,g),y=Math.max(m,h,g),A=m+h+g-b-y;!p.sphere&&!p.hull&&b<=.4*A&&A>=.5*y&&r++}}let o=t?Ks(e.subarray(0,t),t>>1):1,a=this.cellSize/(2*o),n=this.jointCount-this.freeSlots.length,l=i?r/i:0,c=this.bodyCapacity<=16*this.device.limits.maxComputeWorkgroupsPerDimension&&(2*this.bodyCapacity+16*Math.ceil(Math.log2(Math.max(2,this.bodyCapacity)))+32)*32<=this.device.limits.maxStorageBufferBindingSize,u=n>=this.bodyCount&&l>=.25,f=c&&this.bodyCount>=1e4&&(a>=4||u);this.broadphase=f?"hploc":"grid",this.broadphaseDecision={requested:"auto",selected:this.broadphase,bodyCount:this.bodyCount,cellRatio:a,constraintCount:n,thinPlateFraction:l,reason:c?f?u?"Many linked thin plates; the tree avoids empty grid-cell space":"Large grid cells contain many smaller objects":"Grid retained for this body count and size distribution":"The GPU tree exceeds this adapter's limits; the grid is used"}}step(){ao(this,$l),this.selectAdjacencyPipeline(),this.selectSolver(),Ul(this),this.radiiDirty&&(this.uploadStatics(),this.selectBroadphase()),this.broadphaseRequested==="auto"&&this.broadphaseDecision.constraintCount!==this.jointCount-this.freeSlots.length&&this.selectBroadphase(),this.broadphase==="grid"&&this.bvh&&(this.bvh.destroy(),this.bvh=null),this.broadphase==="hploc"&&(!this.bvh||this.bvh.bodyBuffer!==this.bodyBuffer)&&(this.bvh?.destroy(),this.bvh=new so(this.device,this,this.bvhOptions)),this.dispatchIsolation||this.smallGroupPipeline||this.bvh||this.detailCallback||this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep?Wl(this):super.step()}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.externalBeforeStep?.(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e),this.externalAfterStep?.(e)}setRestitution(e,t){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a value between 0 and 1");t>0&&!this.restitution&&(this.restitution=new Ar(this)),this.restitution?.set(e,t),this.wakeAll()}setSolverMode(e){return xr({...this.solverPolicy,requested:e,bodyCount:this.bodyCount}),this.solverPolicy.requested=e,this.selectSolver(),this.solverDecision}solveSource(e,t=this.solverDecision.selected){return e=pc(e,t),this.springFracture&&(e=io(e)),this.angularConstraints&&(e=cn(e)),this.sensorsEnabled&&(e=on(e)),e=Qa(Ka(e)),this.scalarPrimal&&(e=bn(e)),this.portableContactMath&&(e=yn(e)),t==="points"&&!this.scalarPrimal&&!this.portableContactMath&&!this.dispatchIsolation&&(e=pn(e)),e}setAngularConstraint(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount)throw Error("Angular constraint needs a live slot");let i=kt(t);this.flushPropertyEdits(),this.angularConstraints||(this.angularConstraints=!0,this.rebuildSolvePipelines());let r=new Float32Array(32);r[0]=r[1]=1e3,r[3]=3e38,r.set(i.axisA,24),r.set(i.axisB,28),r[27]=i.type==="motor"?i.speed:i.minAngle,r[31]=i.type==="motor"?i.maxTorque:i.maxAngle,this.device.queue.writeBuffer(this.jointBuffer,e*128,r),this.info[e*4]=i.type==="motor"?3:4,this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info.subarray(e*4,e*4+4)),this.captureAngularFrames([e]),this.wakeAll()}captureAngularFrames(e){if(!e.length)return;this.flushPropertyEdits();let t=this.device;this.angularFramePipeline??=t.createComputePipeline({layout:"auto",compute:{module:t.createShaderModule({code:`const DISPATCH_STRIDE = ${t.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3698
|
+
${Kl.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}),this.runCapture("angular",this.angularFramePipeline,Uint32Array.from(e),e.length)}setMotor(e,{speed:t,maxTorque:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(this.info[e*4]!==3)throw Error("Motor needs an active motor slot");if(t!==void 0&&!Number.isFinite(t))throw Error("Motor speed must be finite");if(i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("maxTorque must be nonnegative");t!==void 0&&oe(this).float(this.jointBuffer,e*32+27,t),i!==void 0&&oe(this).float(this.jointBuffer,e*32+31,i),this.wakeAll()}rebuildSolvePipelines(){this.solvePipelineCache.clear(),this.smallGroupPipelines?.clear(),this.smallGroupPipeline=null;let e=this.solverDecision.selected,t=e==="custom"?this.customSolveSource:e==="optimized"?is:e==="points"?to:Ge,i=this.device.createShaderModule({code:this.solveSource(t)}),r=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.solve,this.layouts.pass]}),o=Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(a=>[a,this.device.createComputePipeline({layout:r,compute:{module:i,entryPoint:a}})]));Object.assign(this.pipes,o),this.solvePipelineCache.set(e,o)}setSensor(e,t){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount||typeof t!="boolean")throw Error("Sensor requires a live body index and boolean flag");if(t&&!this.sensorsEnabled){this.sensorsEnabled=!0,nn(this),this.rebuildSolvePipelines(),this.contactShaders.make(no(yr(this.hullShaders??!1)));let i=this.contactShaders.make;this.contactShaders.make=a=>i(no(a));let r=this.device.createShaderModule({code:an()}),o=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]});for(let a of["degreeContacts","fillContacts"])this.pipes[a]=this.device.createComputePipeline({layout:o,compute:{module:r,entryPoint:a}})}this.sensorsEnabled&&oe(this).float(this.bodyBuffer,e*40+11,t?1:0),this.wakeAll()}selectSolver(){let e=this.jointCount-this.freeSlots.length;this.adaptiveScheduling&&this.schedulingDecision?.dense&&(this.bodyCount<25e4||e>0)&&(this.primalLanes=[...this.defaultPrimalLanes],this.schedulingDecision={...this.schedulingDecision,dense:!1,reason:"Threads per body follow solve-group size"});let t=this.solverDecision;if(t.requested===this.solverPolicy.requested&&t.bodyCount===this.bodyCount&&t.constraintCount===e)return;let i=xr({...this.solverPolicy,bodyCount:this.bodyCount,constraintCount:e});if(i.selected!==t.selected){let r=this.solvePipelineCache.get(i.selected);if(!r){let o=i.selected==="optimized"?is:i.selected==="points"?to:void 0,a=this.device.createShaderModule({code:this.solveSource(o??Ge,i.selected)}),n=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.solve,this.layouts.pass]});r=Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(l=>[l,this.device.createComputePipeline({label:`${i.selected} ${l}`,layout:n,compute:{module:a,entryPoint:l}})])),this.solvePipelineCache.set(i.selected,r)}Object.assign(this.pipes,r)}this.solverDecision=i,mn(this,i.selected==="points"||i.selected==="optimized")}profileDetailedNextStep(e){if(!this.device.features.has("timestamp-query")||this.timingBusy||this.timingCallback||this.detailCallback)return!1;let t=2*(9+2*this.params.iterations);return(!this.detailTiming||this.detailTiming.count<t)&&(this.releaseAppTiming(),this.detailTiming={count:t,querySet:this.device.createQuerySet({type:"timestamp",count:t}),resolve:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ})}),this.detailCallback=e,!0}captureConstraintFrames(e){if(e.length){this.flushPropertyEdits(),this.framePipeline||(this.framePipeline=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3699
|
+
${jl.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}})),(!this.frameData||this.frameData.length<e.length*4)&&(this.frameData=new Uint32Array(Math.max(e.length*4,(this.frameData?.length??0)*2,64)));for(let t=0;t<e.length;t++)this.frameData[t*4]=e[t].slot,this.frameData[t*4+1]=e[t].spring?1:0;this.runCapture("frame",this.framePipeline,this.frameData.subarray(0,e.length*4),e.length)}}runCapture(e,t,i,r){this.flushEdits?.();let o=this.device,a=(this.captureUploads??={})[e]??={};(!a.buffer||a.buffer.size<i.byteLength)&&(a.buffer?.destroy(),a.buffer=o.createBuffer({size:i.byteLength*2,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),a.buffers=null),o.queue.writeBuffer(a.buffer,0,i);let n=[this.bodyBuffer,this.jointBuffer,this.infoBuffer,a.buffer];(!a.buffers||n.some((d,p)=>d!==a.buffers[p])||a.bytes!==i.byteLength)&&(a.group=o.createBindGroup({layout:t.getBindGroupLayout(0),entries:n.map((d,p)=>({binding:p,resource:{buffer:d,...p===3?{size:i.byteLength}:{}}}))}),a.buffers=n,a.bytes=i.byteLength);let l=o.createCommandEncoder(),c=l.beginComputePass();c.setPipeline(t),c.setBindGroup(0,a.group);let u=Math.ceil(r/64),f=o.limits.maxComputeWorkgroupsPerDimension;c.dispatchWorkgroups(Math.min(u,f),Math.ceil(u/f)),c.end(),o.queue.submit([l.finish()])}setSpringMaterial(e,t={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4]!==gr)throw Error("Spring material requires an active spring slot");let i=It(t);!this.springFracture&&(Number.isFinite(i.breakStrain)||Number.isFinite(i.breakForce))&&(this.springFracture=!0,this.rebuildSolvePipelines());for(let[r,o]of[["breakStrain",11],["breakForce",15]])oe(this).float(this.jointBuffer,e*32+o,i[r]===1/0?0:i[r]);this.wakeAll()}appendConstraints(...e){return this.flushPropertyEdits(),super.appendConstraints(...e)}appendJoint(e,t,i,r,o,a){let n=c=>c===1/0?3e38:c,[l]=this.appendConstraints(1,(c,u)=>{u[3]=n(o),u[7]=n(a),u[11]=3e38,u.set(i,24),u.set(r,28);let f=e>=0?this.bodies[e].size:[0,0,0],d=this.bodies[t].size;return u[15]=f.reduce((p,m,h)=>p+(m+d[h])**2,0),[1,e,t]});return this.wakeConstraint(l),l}appendJoints(...e){let t=super.appendJoints(...e);for(let i of t)this.wakeConstraint(i);return t}appendSprings(...e){let t=super.appendSprings(...e);for(let i of t)this.wakeConstraint(i);return t}enableSleeping(e,t={}){this.sleeping||(this.sleeping=new Fi({device:this.device,gpu:this,capacity:this.bodyCapacity,aggregates:e.bodies.map((i,r)=>({rigid:i,index:r}))},t),this.sleeping.initializing&&this.step())}wakeAll(){this.sleeping&&(this.sleeping.wakeRequested=!0)}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}addBodies(e){this.flushPropertyEdits();let t=super.addBodies(e);if(t>=0)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)Fe(e[i])&&this.installCapsule(t+i);if(t>=0&&this.sleeping)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)this.sleeping.register(t+i,e[i].mass,e[i].allowSleep!==!1);return t}rewriteBodies(e,t){this.flushPropertyEdits(),super.rewriteBodies(e,t);for(let i=0;i<e.length;i++)Fe(t[i])&&this.installCapsule(e[i]);if(this.sleeping)for(let i=0;i<e.length;i++)this.sleeping.register(e[i],t[i].mass,t[i].allowSleep!==!1)}releaseJoints(e){this.flushPropertyEdits();let t=Array.from(e);for(let i of t)this.wakeConstraint(i);super.releaseJoints(t)}setWorldAnchor(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4+1]!==-1||t?.length!==3||!Array.from(t).every(Number.isFinite))throw Error("A world joint and finite xyz anchor are required");for(let i=0;i<3;i++)oe(this).float(this.jointBuffer,e*32+24+i,t[i]);this.sleeping?.wakeBody(this.info[e*4+2])}disableConstraint(e){this.flushPropertyEdits(),this.wakeConstraint(e),super.disableConstraint(e)}wakeConstraint(e){if(!(!this.sleeping||e<0||e>=this.jointCount||!this.info[e*4]))for(let t of this.info.subarray(e*4+1,e*4+3))t>=0&&this.sleeping.wakeBody(t)}setFilters(e,t,i){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");for(let o=0;o<e.length;o++){if(!Number.isInteger(e[o])||e[o]<0||e[o]>=this.bodyCount)throw Error("Collision filters require live body indices");for(let a of[t[o],i[o]])if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers")}let r=oe(this);for(let o=0;o<e.length;o++)r.uint(this.filterBuffer,e[o]*2,t[o]),r.uint(this.filterBuffer,e[o]*2+1,i[o]);this.wakeAll()}profileNextStep(e){return this.detailCallback?!1:super.profileNextStep(t=>e({...t,broadphase:this.broadphase,bvhRebuilt:this.bvh?.lastRebuilt??!1}))}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}releaseAppTiming(){if(this.detailTiming){for(let e of["querySet","resolve","read"])this.detailTiming[e].destroy();this.detailTiming=null}}destroy(){this.passConstantCache=null;for(let e of Object.values(this.captureUploads??{}))e.buffer.destroy();this.captureUploads=null,this.frameData=null,this.propertyEdits?.destroy(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.sleeping?.dispose(),this.bvh?.destroy(),this.bvh=null,this.timingBusy||this.releaseAppTiming(),super.destroy(),this.solvePipelineCache.clear(),this.adjacencyPipelines={}}adapt(e){if(super.adapt(e),this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.adaptiveScheduling){let t=this.bodyCount>=25e4&&this.jointCount-this.freeSlots.length===0&&e.manifolds>=1.75*this.bodyCount&&!e.overflow&&!e.clashes;this.primalLanes=t?[2**31,2**31,2**31]:[...this.defaultPrimalLanes],this.schedulingDecision={bodyCount:this.bodyCount,manifolds:e.manifolds,dense:t,cachedAdjacency:this.adjacencyCached,reason:t?"Eight threads share each dense body solve":"Threads per body follow solve-group size"}}}selectAdjacencyPipeline(){let e=this.cacheAdjacencyKeys??this.bodyCount>=25e4;this.adjacencyCached!==e&&(e&&!this.adjacencyPipelines.cached&&(this.adjacencyPipelines.cached=this.device.createComputePipeline({label:"Cached adjacency keys",layout:this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]}),compute:{module:this.device.createShaderModule({code:Jl}),entryPoint:"sortAdjacency"}})),this.pipes.sortAdjacency=e?this.adjacencyPipelines.cached:this.adjacencyPipelines.original,this.adjacencyCached=e)}};var vn=new WeakMap,mc=[{name:"batched-grid",broadphase:"grid",dispatchIsolation:!1},{name:"isolated-grid",broadphase:"grid",dispatchIsolation:!0},{name:"batched-hploc",broadphase:"hploc",dispatchIsolation:!1},{name:"isolated-hploc",broadphase:"hploc",dispatchIsolation:!0},{name:"scalar-grid",broadphase:"grid",dispatchIsolation:!1,scalarPrimal:!0},{name:"scalar-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0},{name:"portable-contact-grid",broadphase:"grid",dispatchIsolation:!1,portableContactMath:!0},{name:"portable-contact-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0,portableContactMath:!0}];function yc(s){return vn.has(s)||vn.set(s,Lf(s,void 0,{diagnostics:!1})),vn.get(s)}async function Lf(s,e,{diagnostics:t=!0}={}){let i=[];for(let r of mc){let o=await bc(s,r,e,t);if(!t&&!o.passed&&(o=await bc(s,r,e,!0)),i.push(o),!!o.passed)return fc(s,{dispatchIsolation:r.dispatchIsolation,scalarPrimal:r.scalarPrimal??!1,portableContactMath:r.portableContactMath??!1,broadphase:r.broadphase==="hploc"?"hploc":void 0}),{...o,selected:r.name,compatibility:r.name!==mc[0].name,attempts:i}}return{...i[0],selected:null,compatibility:!1,attempts:i}}function gc(s){let e=new ni;return e.iterations=5,s||(e.gravity=0),new Ze(e,[16,.25,16],0,.6,[0,-.125,0]),new Ze(e,[1,1,1],1,.6,[-2,s?2:.45,0]),ar(e,.5,1,.6,[0,s?2.3:.45,0]),Br(e,.25,1.6,1,.6,[2,s?2.5:.75,0]),e}async function bi(s,e,t=e.size){let i=s.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST});try{let r=s.createCommandEncoder();return r.copyBufferToBuffer(e,0,i,0,t),s.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ),i.getMappedRange().slice(0)}finally{i.destroy()}}async function _f(s){s.step();let e=await s.readCounters(),t=new Uint32Array(await bi(s.device,s.argsBuffer)),i=await Mf(s),r=new Uint32Array(i),o=new Float32Array(i),a=new Uint32Array(await bi(s.device,s.adjBuffer)),n=new Uint32Array(await bi(s.device,s.colorBuffer)),{manifolds:l,contacts:c}=s.contactStorage,u=await bi(s.device,l,Math.max(32,e.manifolds*32)),f=new Uint32Array(u),d=new Float32Array(u),p=new Float32Array(await bi(s.device,c,Math.max(64,e.contacts*64))),m=await s.readBodies(),h=e.manifolds>=3?await Ef(s):[],g=h.some(({contactEvaluation:b})=>b.returnedForce===0&&b.independentForce<-1e-5);return{expectedFloorPairs:3,counters:e,pairs:e.pairs?Array.from(await s.readPairs()):[],narrowphaseWorkgroups:Array.from(t.subarray(0,3)),solver:{scalarPrimal:s.scalarPrimal,portableContactMath:s.portableContactMath,lanes:[...s.primalLanes],contactWorkgroups:Array.from(t.subarray(3,6)),dualWorkgroups:Array.from(t.subarray(9,12)),colorWorkgroups:Array.from(t.subarray(12,12+3*s.colorCap)),adjacencyStarts:Array.from(a.subarray(0,5)),adjacencyEntries:Array.from(a.subarray(2*s.bodyCapacity+1,2*s.bodyCapacity+1+a[4])),colorStarts:Array.from(n.subarray(2*s.bodyCapacity,2*s.bodyCapacity+s.colorCap+1)),colorBodies:Array.from(n.subarray(2*s.bodyCapacity+65,2*s.bodyCapacity+68)),positionsAfterOverlap:[1,2,3].map(b=>Array.from(m.subarray(b*40,b*40+3))),manifolds:Array.from({length:e.manifolds},(b,y)=>({ids:Array.from(f.subarray(y*8,y*8+4)),normalAndFriction:Array.from(d.subarray(y*8+4,y*8+8))})),contacts:Array.from({length:e.contacts},(b,y)=>({anchorA:Array.from(p.subarray(y*16,y*16+3)),anchorB:Array.from(p.subarray(y*16+4,y*16+7)),gap:p[y*16+7],penalty:Array.from(p.subarray(y*16+8,y*16+11)),force:Array.from(p.subarray(y*16+12,y*16+15))})),serialSystems:h,failure:g?"contact-evaluation":null},filters:Array.from(new Uint32Array(await bi(s.device,s.filterBuffer))),params:{bodyCount:r[0],largeCount:r[1],noCollideCount:r[2],pairCapacity:r[3],contactCapacity:r[4],dt:o[5],cellSize:o[6],maxSmallRadius:o[7],up:Array.from(o.subarray(8,11)),alpha:o[11]},stage:e.pairs<3?"collision-pairs":e.manifolds<3?"contact-generation":"contacts-created"}}async function Ef(s){let e=s.portableContactMath?"bitcast<f32>(pc.data.y)":"pc.alpha",t=s.portableContactMath?"k.anchorB.w":"k.c0x",i=s.portableContactMath?"k.penalty.x":"k.pen.x",r=s.portableContactMath?"k.force.x":"k.lam.x",o=s.device.createShaderModule({code:s.solveSource(Ge)+`
|
|
3614
3700
|
@compute @workgroup_size(1) fn inspectSystems(@builtin(global_invocation_id) gid: vec3u) {
|
|
3615
3701
|
let acc = accumulate(gid.x + 1u, 0u, 1u);
|
|
3616
3702
|
joints[gid.x].penLin = vec4f(acc.lin[0][0], acc.lin[1][1], acc.lin[2][2], 0.0);
|
|
@@ -3634,7 +3720,7 @@ ${jl.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}
|
|
|
3634
3720
|
let raw = ${i} * expectedC + ${r};
|
|
3635
3721
|
joints[gid.x].rA = vec4f(${t}, ${e}, result.C.x, result.F.x);
|
|
3636
3722
|
joints[gid.x].rB = vec4f(${i}, ${r}, expectedC, min(raw, 0.0));
|
|
3637
|
-
}`}),
|
|
3723
|
+
}`}),a=s.device.createComputePipeline({layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.solve,s.layouts.pass]}),compute:{module:o,entryPoint:"inspectSystems"}}),n=s.device.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(a),l.setBindGroup(0,s.groups.solve[1-s.parity]),l.setBindGroup(1,s.passGroup,[0]),l.dispatchWorkgroups(3),l.end(),s.device.queue.submit([n.finish()]);let c=new Float32Array(await bi(s.device,s.jointBuffer,384));return[0,1,2].map(u=>({linearDiagonal:Array.from(c.subarray(u*32,u*32+3)),angularDiagonal:Array.from(c.subarray(u*32+4,u*32+7)),linearForce:Array.from(c.subarray(u*32+16,u*32+19)),angularForce:Array.from(c.subarray(u*32+20,u*32+23)),contactEvaluation:{inputGap:c[u*32+24],passAlpha:c[u*32+25],returnedError:c[u*32+26],returnedForce:c[u*32+27],inputPenalty:c[u*32+28],inputForce:c[u*32+29],independentError:c[u*32+30],independentForce:c[u*32+31]}}))}async function Mf(s){let{device:e}=s,t=e.createBuffer({size:48,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC});try{let i=e.createComputePipeline({layout:"auto",compute:{module:e.createShaderModule({code:ge+`
|
|
3638
3724
|
@group(0) @binding(0) var<uniform> p: Params;
|
|
3639
3725
|
@group(0) @binding(1) var<storage,read_write> out: array<u32>;
|
|
3640
3726
|
@compute @workgroup_size(1) fn inspectParams() {
|
|
@@ -3643,7 +3729,7 @@ ${jl.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}
|
|
|
3643
3729
|
out[5]=bitcast<u32>(p.dt); out[6]=bitcast<u32>(p.cellSize); out[7]=bitcast<u32>(p.maxSmallRadius);
|
|
3644
3730
|
out[8]=bitcast<u32>(p.up.x); out[9]=bitcast<u32>(p.up.y); out[10]=bitcast<u32>(p.up.z);
|
|
3645
3731
|
out[11]=bitcast<u32>(p.alpha);
|
|
3646
|
-
}`}),entryPoint:"inspectParams"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:s.paramsBuffer}},{binding:1,resource:{buffer:t}}]}),o=e.createCommandEncoder(),
|
|
3732
|
+
}`}),entryPoint:"inspectParams"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:s.paramsBuffer}},{binding:1,resource:{buffer:t}}]}),o=e.createCommandEncoder(),a=o.beginComputePass();return a.setPipeline(i),a.setBindGroup(0,r),a.dispatchWorkgroups(1),a.end(),e.queue.submit([o.finish()]),await bi(e,t)}finally{t.destroy()}}async function bc(s,e,t,i){let r={mode:e.name,passed:!1,steps:i?180:90,cases:[],errors:[],contactCache:"normal-float-generations-v1",diagnosticVersion:5,detailed:i,limits:{storageBindings:s.limits.maxStorageBuffersPerShaderStage,workgroupStorageBytes:s.limits.maxComputeWorkgroupStorageSize,workgroupInvocations:s.limits.maxComputeInvocationsPerWorkgroup,storageBindingBytes:s.limits.maxStorageBufferBindingSize}},o,a=n=>{let l=new gi(s,gc(n),{spatialSort:!1,bodyCapacity:4,smallGroups:!1,broadphase:e.broadphase,scalarPrimal:e.scalarPrimal??!1,portableContactMath:e.portableContactMath??!1,capacity:{pairs:32,manifolds:32,contacts:256,colors:8}});return l.params.up=[0,1,0],l.dispatchIsolation=e.dispatchIsolation,t?.(l,e,n),l};s.pushErrorScope("validation");try{if(o=a(!1),i)r.pipeline=await _f(o);else{o.step();let n=await o.readCounters();r.pipeline={expectedFloorPairs:3,counters:n,solver:{scalarPrimal:o.scalarPrimal,portableContactMath:o.portableContactMath,lanes:[...o.primalLanes]},stage:n.pairs<3?"collision-pairs":n.manifolds<3?"contact-generation":"contacts-created"}}if(r.pipeline.stage!=="contacts-created")r.reason=r.pipeline.stage==="collision-pairs"?"The GPU did not find the three known overlapping floor pairs.":"The GPU found overlapping pairs but did not create all floor contacts.";else{if(i)o.destroy(),o=a(!0);else{let c=gc(!0);o.rewriteBodies([0,1,2,3],c.bodies),o.params.gravity=c.gravity}let n=0;r.samples=[];for(let c=0;c<r.steps;c++){if(o.step(),(c+1)%30&&c!==39)continue;let u=await o.readCounters();if(r.samples.push({step:c+1,...u}),u.manifolds&&n++,u.overflow||u.clashes)throw Error("Incomplete GPU contact graph")}let l=await o.readBodies();for(let c=1;c<4;c++){let u=c*40,[f,d,p,m]=l.subarray(u+4,u+8),h=c===1?.5*(Math.abs(2*(f*d+m*p))+Math.abs(1-2*(f*f+p*p))+Math.abs(2*(d*p-m*f))):c===2?.5:.25+.55*Math.abs(1-2*(f*f+p*p)),g=l[u+1]-h,b=l.subarray(u,u+40).every(Number.isFinite);r.cases.push({shape:["floor","box","sphere","capsule"][c],passed:b&&g>=-.06&&g<.12,floorGap:g,position:Array.from(l.subarray(u,u+3))})}r.contactSteps=n,r.passed=n>0&&r.cases.every(c=>c.passed),r.passed||(r.reason=r.pipeline.solver?.failure==="contact-evaluation"?"The GPU created floor contacts but returned zero force where a push was required.":"The GPU did not keep all three falling shapes on the floor.")}}catch(n){r.errors.push(n.message)}finally{try{let n=await s.popErrorScope();n&&r.errors.push(n.message)}catch(n){r.errors.push(n.message)}o?.destroy()}return r.passed=r.passed&&!r.errors.length,r}async function ss(s={}){if(!globalThis.navigator?.gpu)throw Error("WebGPU needs a supported browser on localhost or HTTPS");let e=await navigator.gpu.requestAdapter({powerPreference:s.powerPreference??"high-performance"});if(!e)throw Error("No WebGPU adapter is available");let t={};for(let[r,o]of Object.entries(s.preferredLimits??{})){if(!r.startsWith("max")||e.limits[r]===void 0||!Number.isSafeInteger(o)||o<0)throw Error(`Invalid preferred GPU limit: ${r}`);t[r]=Math.min(o,e.limits[r])}for(let[r,o]of Object.entries(s.requiredLimits??{})){if(e.limits[r]===void 0||!Number.isSafeInteger(o)||o<0)throw Error(`Invalid required GPU limit: ${r}`);let a=e.limits[r];if(r.startsWith("min")&&o<a||!r.startsWith("min")&&o>a)throw Error(`GPU limit ${r} requires ${o}; this adapter supports ${a}`);t[r]=o}let i=await e.requestDevice({requiredLimits:t,requiredFeatures:e.features.has("timestamp-query")?["timestamp-query"]:[]});if(s.validate3D)try{await Cr(i)}catch(r){throw i.destroy(),r}return{adapter:e,device:i}}async function Cr(s){if(uo(s))return s;let e=await yc(s);if(!e.passed)throw Error(`GPU 3D contact check failed: ${e.reason??e.errors.join("; ")}`);return s}var vc=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
3647
3733
|
struct Command { index:u32,mask:u32,pad0:u32,pad1:u32,velocity:vec4f,angular:vec4f,impulse:vec4f,torque:vec4f,position:vec4f,rotation:vec4f,pointImpulse:vec4f }
|
|
3648
3734
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
3649
3735
|
@group(0) @binding(1) var<storage,read> commands:array<Command>;
|
|
@@ -3671,18 +3757,18 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
|
|
|
3671
3757
|
if((c.mask&16u)!=0u){b.size.w=0.;b.moment=vec4f(vec3f(0),b.moment.w);b.velocity=vec4f(0);b.angular=vec4f(vec3f(0),b.angular.w);}
|
|
3672
3758
|
bodies[c.index]=b;
|
|
3673
3759
|
}
|
|
3674
|
-
`;var Kt=class{constructor(e,t,i="linear"){this.solver=e,this.dimension=t,this.components=i==="angular"&&t===2?1:t;let r=e.device,o=t===3?10:6,
|
|
3760
|
+
`;var Kt=class{constructor(e,t,i="linear"){this.solver=e,this.dimension=t,this.components=i==="angular"&&t===2?1:t;let r=e.device,o=t===3?10:6,a=t===3?i==="angular"?9:8:3;this.pipeline=r.createComputePipeline({layout:"auto",compute:{entryPoint:"setVelocities",module:r.createShaderModule({code:`
|
|
3675
3761
|
@group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
|
|
3676
3762
|
@group(0) @binding(1) var<storage,read> indices:array<u32>;
|
|
3677
3763
|
@group(0) @binding(2) var<storage,read> velocities:array<f32>;
|
|
3678
3764
|
@compute @workgroup_size(64) fn setVelocities(@builtin(global_invocation_id) id:vec3u) {
|
|
3679
3765
|
let i=id.x+id.y*${r.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3680
3766
|
if(i>=arrayLength(&indices)){return;}
|
|
3681
|
-
let base=indices[i]*${o}u+${
|
|
3767
|
+
let base=indices[i]*${o}u+${a}u;
|
|
3682
3768
|
var v=bodies[base];
|
|
3683
3769
|
${this.components===1?"v.z=velocities[i];":`v.x=velocities[i*${t}u];v.y=velocities[i*${t}u+1u];${t===3?"v.z=velocities[i*3u+2u];":""}`}
|
|
3684
3770
|
bodies[base]=v;
|
|
3685
|
-
}`})}})}set(e,t){let i=this.solver,r=i.device,o=e instanceof Uint32Array?e:Uint32Array.from(e),
|
|
3771
|
+
}`})}})}set(e,t){let i=this.solver,r=i.device,o=e instanceof Uint32Array?e:Uint32Array.from(e),a=t instanceof Float32Array?t:Float32Array.from(t);if(a.length!==o.length*this.components)throw Error(`Provide ${this.components} velocity components per body`);let n=-1;for(let c=0;c<e.length;c++){if(i.liveIndex(e[c]),e[c]<=n)throw Error("Velocity batch indices must be unique and ascending GPU slots");n=e[c]}for(let c of a)if(!Number.isFinite(c))throw Error("Velocities must be finite Float32 values");let l=Math.min(r.limits.maxStorageBufferBindingSize,r.limits.maxBufferSize);if(o.byteLength>l||a.byteLength>l)throw Error("Velocity batch exceeds GPU buffer limits");o.length&&(i.wakeAll(),i.flushEdits(),this.submitValidated(o,a))}submitValidated(e,t,i){if(!e.length)return;let r=this.solver,o=r.device,a=Math.min(o.limits.maxStorageBufferBindingSize,o.limits.maxBufferSize);if(e.byteLength>a||t.byteLength>a)throw Error("Velocity batch exceeds GPU buffer limits");for(let[f,d]of[["indices",e],["values",t]])(!this[f]||this[f].size<d.byteLength)&&(this[f]?.destroy(),this[f]=o.createBuffer({size:Math.min(a,d.byteLength*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),o.queue.writeBuffer(this[f],0,d);let n=i??o.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(this.pipeline),(!this.binding||this.boundBody!==r.bodyBuffer||this.boundIndices!==this.indices||this.boundValues!==this.values||this.boundBytes!==t.byteLength)&&(this.binding=o.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:r.bodyBuffer}},{binding:1,resource:{buffer:this.indices,size:e.byteLength}},{binding:2,resource:{buffer:this.values,size:t.byteLength}}]}),this.boundBody=r.bodyBuffer,this.boundIndices=this.indices,this.boundValues=this.values,this.boundBytes=t.byteLength),l.setBindGroup(0,this.binding);let c=Math.ceil(e.length/64),u=o.limits.maxComputeWorkgroupsPerDimension;l.dispatchWorkgroups(Math.min(c,u),Math.ceil(c/u)),l.end(),i||o.queue.submit([n.finish()]),this.lastBatch={bodies:e.length,uploadedBytes:e.byteLength+t.byteLength}}destroy(){this.indices?.destroy(),this.values?.destroy()}},yi=class{constructor(e,t){this.solver=e,this.dimension=t,this.wordsPerBody=t===3?8:5}set(e,{linear:t,angular:i}={}){let r=this.solver,o=this.dimension,a=o===3?3:1,n=e instanceof Uint32Array?e:Uint32Array.from(e),l=t===void 0?null:t instanceof Float32Array?t:Float32Array.from(t),c=i===void 0?null:i instanceof Float32Array?i:Float32Array.from(i);if(!l&&!c)throw Error("Provide linear and/or angular velocities");if(l&&l.length!==n.length*o)throw Error(`Provide ${o} linear components per body`);if(c&&c.length!==n.length*a)throw Error(`Provide ${a} angular components per body`);let u=-1;for(let p=0;p<e.length;p++){if(r.liveIndex(e[p]),e[p]<=u)throw Error("Velocity batch indices must be unique and ascending GPU slots");u=e[p]}for(let p of[l,c])if(p){for(let m of p)if(!Number.isFinite(m))throw Error("Velocities must be finite Float32 values")}let f=n.length*this.wordsPerBody,d=Math.min(r.device.limits.maxStorageBufferBindingSize,r.device.limits.maxBufferSize);if(f*4>d)throw Error("Motion batch exceeds GPU buffer limits");if(f){(!this.data||this.data.length<f)&&(this.data=new Uint32Array(Math.max(f,(this.data?.length??0)*2)),this.floats=new Float32Array(this.data.buffer));for(let p=0;p<n.length;p++){let m=p*this.wordsPerBody;if(this.data[m]=n[p],this.data[m+1]=(l?1:0)|(c?2:0),l)for(let h=0;h<o;h++)this.floats[m+2+h]=l[p*o+h];if(c)for(let h=0;h<a;h++)this.floats[m+2+o+h]=c[p*a+h]}r.wakeAll(),r.flushEdits(),this.submitValidated(this.data.subarray(0,f))}}submitValidated(e,t){if(!e.length)return;let i=this.solver,r=i.device,o=this.wordsPerBody,a=Math.min(r.limits.maxStorageBufferBindingSize,r.limits.maxBufferSize);if(e.byteLength>a)throw Error("Motion batch exceeds GPU buffer limits");(!this.buffer||this.buffer.size<e.byteLength)&&(this.buffer?.destroy(),this.buffer=r.createBuffer({size:Math.min(a,e.byteLength*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),r.queue.writeBuffer(this.buffer,0,e),this.pipeline??=r.createComputePipeline({layout:"auto",compute:{entryPoint:"edit",module:r.createShaderModule({code:`
|
|
3686
3772
|
@group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
|
|
3687
3773
|
@group(0) @binding(1) var<storage,read> commands:array<u32>;
|
|
3688
3774
|
@compute @workgroup_size(64) fn edit(@builtin(global_invocation_id) id:vec3u) {
|
|
@@ -3694,14 +3780,14 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
|
|
|
3694
3780
|
if((mask&1u)!=0u){v.x=bitcast<f32>(commands[o+2u]);v.y=bitcast<f32>(commands[o+3u]);${this.dimension===3?"v.z=bitcast<f32>(commands[o+4u]);":""}}
|
|
3695
3781
|
${this.dimension===2?"if((mask&2u)!=0u){v.z=bitcast<f32>(commands[o+4u]);}":"if((mask&2u)!=0u){var a=bodies[b+9u];a.xyz=vec3f(bitcast<f32>(commands[o+5u]),bitcast<f32>(commands[o+6u]),bitcast<f32>(commands[o+7u]));bodies[b+9u]=a;}"}
|
|
3696
3782
|
bodies[b+${this.dimension===3?8:3}u]=v;
|
|
3697
|
-
}`})}}),(!this.binding||this.boundBody!==i.bodyBuffer||this.boundBuffer!==this.buffer||this.boundBytes!==e.byteLength)&&(this.binding=r.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.buffer,size:e.byteLength}}]}),this.boundBody=i.bodyBuffer,this.boundBuffer=this.buffer,this.boundBytes=e.byteLength);let
|
|
3783
|
+
}`})}}),(!this.binding||this.boundBody!==i.bodyBuffer||this.boundBuffer!==this.buffer||this.boundBytes!==e.byteLength)&&(this.binding=r.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.buffer,size:e.byteLength}}]}),this.boundBody=i.bodyBuffer,this.boundBuffer=this.buffer,this.boundBytes=e.byteLength);let n=t??r.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(this.pipeline),l.setBindGroup(0,this.binding);let c=Math.ceil(e.length/o/64),u=r.limits.maxComputeWorkgroupsPerDimension;l.dispatchWorkgroups(Math.min(c,u),Math.ceil(c/u)),l.end(),t||r.queue.submit([n.finish()]),this.lastBatch={bodies:e.length/o,uploadedBytes:e.byteLength}}destroy(){this.buffer?.destroy(),this.binding=null,this.data=this.floats=null}};function ho(s,e={}){return xr({...e,requested:e.solverMode??"auto",bodyCount:s.bodies.length,custom:!!e.shaders?.solve}),{...e,solverMode:e.solverMode??"auto"}}function Sr(s,e){let t=s&&typeof s.x=="number"?[s.x,s.y,s.z]:Array.from(s);if(t.length!==3||!t.every(Number.isFinite))throw Error(`${e} requires three finite components`);return t}function po(s,e){if(s!==1/0&&(!Number.isFinite(s)||s<0))throw Error(`${e} must be nonnegative or Infinity`);return s}function xc(s,e){let t=Sr(e,"anchor");if(!s)return t;let i=s.aggregate,r=i.mesh.position.constructor;return new r(...t).subtract(i.localCenter).applyRotationQuaternion(i.localRotation.conjugate()).asArray()}var os=class{constructor(e,t,i,r={}){e.assertAlive();for(let a of[t,i])if(a&&(a.assertAlive(),a.aggregate.world!==e))throw Error("Constraint bodies must belong to this world");if(!i||t===i)throw Error("A constraint needs a body B and distinct endpoints");let o=r.type??"ball";if(!["ball","fixed","spring","motor","limit"].includes(o))throw Error("Constraint type must be ball, fixed, spring, motor or limit");if(o==="spring"&&!t)throw Error("A spring requires two bodies; use a static aggregate for a world anchor");if(this.world=e,this.bodyA=t,this.bodyB=i,this.type=o,o==="motor"||o==="limit"){let a=(n,l)=>n?new n.aggregate.mesh.position.constructor(...Sr(l,"axis")).applyRotationQuaternion(n.aggregate.localRotation.conjugate()).asArray():Sr(l,"axis");this.angularMaterial=kt({...r,type:o,axisA:a(t,r.axisA??[0,1,0]),axisB:a(i,r.axisB??[0,1,0])})}if(this.anchorA=xc(t,r.anchorA??[0,0,0]),this.anchorB=xc(i,r.anchorB??[0,0,0]),this.linear=po(o==="spring"?r.stiffness??1e3:r.stiffness??1/0,"stiffness"),this.angular=o==="fixed"?po(r.angularStiffness??1/0,"angularStiffness"):0,this.breakForce=po(r.breakForce??1/0,"breakForce"),this.breakForce===0)throw Error("breakForce must be positive");if(this.material=It({breakForce:this.breakForce,breakStrain:r.breakStrain}),o!=="spring"&&r.breakStrain!==void 0)throw Error("breakStrain applies to springs only");if(this.breakOnPull=r.breakOnPull??!1,this.rest=r.rest,this.rest!==void 0&&(!Number.isFinite(this.rest)||this.rest<0))throw Error("Spring rest length must be nonnegative and finite");if(o==="spring"&&!Number.isFinite(this.linear))throw Error("Spring stiffness must be finite");e.gpu?(e.flushCommands(),this.attach()):this.force=o==="spring"?new ft(e.ref,t.aggregate.rigid,i.aggregate.rigid,this.anchorA,this.anchorB,this.linear,this.rest??-1):new ke(e.ref,t?.aggregate.rigid??null,i.aggregate.rigid,this.anchorA,this.anchorB,this.linear,this.angular,this.breakForce),this.force&&o==="spring"&&vr(this.force,this.material),this.force&&this.angularMaterial&&wr(this.force,this.angularMaterial),e.constraints.add(this),this.wakeEndpoints()}wakeEndpoints(){let e=this.world.sleepManager;if(e)for(let t of[this.bodyA,this.bodyB])t&&e.wakeBody(t.gpuIndex)}attach(e,t=!0){let i=this.world.gpu;if(e!==void 0?this.slot=e:this.type==="spring"?[this.slot]=i.appendSprings([{a:this.bodyA.gpuIndex,b:this.bodyB.gpuIndex,rA:this.anchorA,rB:this.anchorB,stiffness:this.linear,rest:this.rest??0}]):this.slot=i.appendJoint(this.bodyA?.gpuIndex??-1,this.bodyB.gpuIndex,this.anchorA,this.anchorB,this.linear,this.angular),this.angularMaterial&&i.setAngularConstraint(this.slot,this.angularMaterial),this.type==="spring"&&(Number.isFinite(this.material.breakStrain)||Number.isFinite(this.material.breakForce))&&i.setSpringMaterial(this.slot,this.material),this.type!=="spring"&&Number.isFinite(this.breakForce)){let r=Number.isFinite(this.breakForce)?this.breakForce:1e30;i.device.queue.writeBuffer(i.jointBuffer,(this.slot*32+11)*4,new Float32Array([this.breakOnPull?-r:r]))}t&&(this.type==="fixed"||this.type==="spring"&&this.rest===void 0)&&i.captureConstraintFrames([{slot:this.slot,spring:this.type==="spring"}])}assertAlive(){if(this.world.assertAlive(),this.disposed)throw Error("Constraint is disposed")}setWorldAnchor(e){if(this.assertAlive(),this.bodyA||this.type==="spring")throw Error("Only world-anchored joints have a movable world anchor");return this.anchorA=Sr(e,"world anchor"),this.world.gpu?this.world.gpu.setWorldAnchor(this.slot,this.anchorA):this.force.rA.set(this.anchorA),this.world.sleepManager?.wakeBody(this.bodyB.gpuIndex),this}async readState(){this.assertAlive(),this.world.initialize();let e=this.world.gpu,t=this.world.device,i=t.createBuffer({size:128,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let r=t.createCommandEncoder();r.copyBufferToBuffer(e.jointBuffer,this.slot*128,i,0,128),t.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=new Float32Array(i.getMappedRange().slice(0));return{broken:o[3]===0&&o[7]===0,linearForce:Array.from(o.subarray(8,11)),angularForce:Array.from(o.subarray(12,15))}}finally{i.destroy()}}setMotor(e){if(this.assertAlive(),this.type!=="motor")throw Error("setMotor requires a motor constraint");return this.angularMaterial=kt({...this.angularMaterial,...e}),this.world.gpu?this.world.gpu.setMotor(this.slot,e):wr(this.force,this.angularMaterial),this.world.wakeAll(),this}dispose(){this.disposed||(this.world.disposed||(this.world.gpu?this.world.gpu.releaseJoints([this.slot]):this.force.destroy(),this.wakeEndpoints()),this.world.constraints.delete(this),this.disposed=!0)}},as=class{constructor(e,t,i,r={}){let o=kt({...r,type:"limit"}),a=r.span??1;if(!Number.isFinite(a)||a<=0)throw Error("Hinge span must be positive");let n=Sr(r.anchorA??[0,0,0],"anchorA"),l=Sr(r.anchorB??[0,0,0],"anchorB");r.motor&&kt({...r.motor,type:"motor",axisA:o.axisA,axisB:o.axisB}),this.world=e,this.bodyA=t,this.bodyB=i,this.disposed=!1,this.joints=[-1,1].map(c=>e.addJoint(t,i,{anchorA:n.map((u,f)=>u+c*a*.5*o.axisA[f]),anchorB:l.map((u,f)=>u+c*a*.5*o.axisB[f]),stiffness:r.stiffness??1/0})),(r.minAngle!==void 0||r.maxAngle!==void 0)&&(this.limit=e.addConstraint(t,i,{...o,type:"limit"})),r.motor&&(this.motor=e.addMotor(t,i,{axisA:o.axisA,axisB:o.axisB,...r.motor}))}dispose(){if(!this.disposed){for(let e of[...this.joints,this.limit,this.motor])e?.dispose();this.disposed=!0}}};var vi=class{constructor(e){this.device=e,this.pipelines=new Map,this.idle=[],this.jobs=new Set,this.destroyed=!1}acquire(e,t){if(this.destroyed)throw Error("Pose readback has been destroyed");let i=this.device;if(e>i.limits.maxStorageBufferBindingSize||t>Math.min(i.limits.maxStorageBufferBindingSize,i.limits.maxBufferSize))throw Error("Pose selection exceeds GPU buffer limits");let r=this.idle.findIndex(a=>a.selection.size>=e&&a.selected.size>=t),o=r>=0?this.idle.splice(r,1)[0]:this.idle.pop()??{};for(let[a,n,l]of[["selection",e,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST],["selected",t,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC],["staging",t,GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST]])(!o[a]||o[a].size<n)&&(o[a]?.destroy(),o[a]=i.createBuffer({size:n,usage:l}));return this.jobs.add(o),o}release(e){if(e.staging.unmap(),!this.destroyed&&e.selected.size<=8*1024*1024&&this.idle.length<2)this.idle.push(e);else{for(let t of["selection","selected","staging"])e[t].destroy();this.jobs.delete(e)}}destroy(){this.destroyed=!0;for(let e of this.jobs)for(let t of["selection","selected","staging"])e[t].destroy();this.jobs.clear(),this.idle.length=0}async read(e,t,i=!1,r=10){if(!t.length)return new Float32Array;let o=i?r===6?1:2:r,a=this.device,n=a.limits.maxComputeWorkgroupsPerDimension,l=`${r}:${o}`,c=this.pipelines.get(l);c||(c=a.createComputePipeline({layout:"auto",compute:{entryPoint:"gather",module:a.createShaderModule({code:`
|
|
3698
3784
|
@group(0) @binding(0) var<storage,read> bodies:array<vec4f>;
|
|
3699
3785
|
@group(0) @binding(1) var<storage,read> indices:array<u32>;
|
|
3700
3786
|
@group(0) @binding(2) var<storage,read_write> selected:array<vec4f>;
|
|
3701
3787
|
@compute @workgroup_size(64) fn gather(@builtin(global_invocation_id) id:vec3u) {
|
|
3702
|
-
let i=id.x+id.y*${
|
|
3788
|
+
let i=id.x+id.y*${n*64}u; if(i>=arrayLength(&selected)){return;}
|
|
3703
3789
|
selected[i]=bodies[indices[i/${o}u]*${r}u+i%${o}u];
|
|
3704
|
-
}`})}}),this.pipelines.set(l,c));let u=t.length*o*16,f=this.acquire(t.length*4,u),{selection:d,selected:p,staging:m}=f;try{
|
|
3790
|
+
}`})}}),this.pipelines.set(l,c));let u=t.length*o*16,f=this.acquire(t.length*4,u),{selection:d,selected:p,staging:m}=f;try{a.queue.writeBuffer(d,0,t instanceof Uint32Array?t:Uint32Array.from(t)),(!f.group||f.body!==e||f.pipeline!==c||f.boundSelection!==d||f.boundSelected!==p||f.indexBytes!==t.length*4||f.bytes!==u)&&(f.group=a.createBindGroup({layout:c.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e}},{binding:1,resource:{buffer:d,size:t.length*4}},{binding:2,resource:{buffer:p,size:u}}]}),f.body=e,f.pipeline=c,f.boundSelection=d,f.boundSelected=p,f.indexBytes=t.length*4,f.bytes=u);let h=a.createCommandEncoder(),g=h.beginComputePass();g.setPipeline(c),g.setBindGroup(0,f.group);let b=Math.ceil(t.length*o/64);return g.dispatchWorkgroups(Math.min(b,n),Math.ceil(b/n)),g.end(),h.copyBufferToBuffer(p,0,m,0,u),a.queue.submit([h.finish()]),await m.mapAsync(GPUMapMode.READ,0,u),new Float32Array(m.getMappedRange(0,u).slice(0))}finally{this.release(f)}}};var xn=4,kr=3,Ai=1,If=2,ns=`
|
|
3705
3791
|
struct Manifold { ids: vec4u, geo: vec4f } // a, b (a > b), first contact, count (low 4 bits); normal (b to a)
|
|
3706
3792
|
struct Contact { rA: vec3f, key: u32, rB: vec3f, c0x: f32, pen: vec3f, c0y: f32, lam: vec3f, c0z: f32 }
|
|
3707
3793
|
struct Params { prev: u32, now: u32, mask: u32, capacity: u32, pairs: u32, step: u32, dt: f32, pad: u32 }
|
|
@@ -3730,7 +3816,7 @@ fn turn(q: vec4f, v: vec3f) -> vec3f {
|
|
|
3730
3816
|
}
|
|
3731
3817
|
|
|
3732
3818
|
fn slotOf(base: u32, h: u32) -> u32 {
|
|
3733
|
-
return ${
|
|
3819
|
+
return ${xn}u + 2u * (base + h);
|
|
3734
3820
|
}
|
|
3735
3821
|
|
|
3736
3822
|
fn insert(base: u32, a: u32, b: u32) {
|
|
@@ -3801,10 +3887,10 @@ fn ended(@builtin(global_invocation_id) id: vec3u) {
|
|
|
3801
3887
|
let k = atomicLoad(&table[s]);
|
|
3802
3888
|
if (k == 0u) { return; }
|
|
3803
3889
|
let b = atomicLoad(&table[s + 1u]);
|
|
3804
|
-
if (!has(params.now, k - 1u, b)) { emit(${
|
|
3890
|
+
if (!has(params.now, k - 1u, b)) { emit(${If}u, k - 1u, b, vec3f(0.0), 0.0, vec3f(0.0)); }
|
|
3805
3891
|
}
|
|
3806
|
-
`,
|
|
3807
|
-
fn touching`),
|
|
3892
|
+
`,Rf=ns.replace("struct Manifold { ids: vec4u, geo: vec4f }","struct Manifold { ids:vec4u, pl:vec4f, anchors:vec4f, geo:vec4f, misc:vec4f }").replace(`const STRIDE = ${We/4}u;`,"const STRIDE = 6u;").replace(ns.slice(ns.indexOf(`@compute @workgroup_size(64)
|
|
3893
|
+
fn touching`),ns.indexOf("/** Per slot of last step")),`
|
|
3808
3894
|
@compute @workgroup_size(64) fn touching(@builtin(global_invocation_id) id:vec3u){
|
|
3809
3895
|
let m=id.x;let count=min(counters[1u],params.pairs);if(m>=count){return;}
|
|
3810
3896
|
let c=manifolds[m];let a=c.ids.x;let b=c.ids.y;
|
|
@@ -3816,8 +3902,8 @@ fn touching`),as.indexOf("/** Per slot of last step")),`
|
|
|
3816
3902
|
emit(${Ai}u,a,b,vec3f(point,0.),abs(c.pl.z)*params.dt,vec3f(c.geo.zw,0.));
|
|
3817
3903
|
}
|
|
3818
3904
|
|
|
3819
|
-
`),xi=class{device;pipelines;layout;watch;table;events;params;setSlots;capacity;group=null;bound=[];flip=0;bodies;constructor(e,t,i,r,o,n=3){this.device=e,this.bodies=t,this.capacity=o,this.setSlots=2**Math.ceil(Math.log2(Math.max(64,2*r)));let a=e.createShaderModule({label:"contact events",code:n===2?zf:as}),l=["read-only-storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform"];this.layout=e.createBindGroupLayout({label:"contact events",entries:l.map((d,p)=>({binding:p,visibility:GPUShaderStage.COMPUTE,buffer:{type:d}}))});let c=e.createPipelineLayout({bindGroupLayouts:[this.layout]}),u=d=>e.createComputePipeline({label:`contact events ${d}`,layout:c,compute:{module:a,entryPoint:d}});this.pipelines={touching:u("touching"),ended:u("ended")};let f=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.watch=e.createBuffer({label:"contact watch",size:Math.max(16,i*4),usage:f}),this.table=e.createBuffer({label:"contact sets",size:(xa+4*this.setSlots)*4,usage:f}),this.events=e.createBuffer({label:"contact events",size:o*kr*16,usage:f}),this.params=e.createBuffer({label:"contact params",size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST})}setWatched(e,t){let i=e.map((r,o)=>[r,t[o]?1:0]).sort((r,o)=>r[0]-o[0]);for(let r=0;r<i.length;){let o=r+1;for(;o<i.length&&i[o][0]===i[o-1][0]+1;)o++;this.device.queue.writeBuffer(this.watch,i[r][0]*4,Uint32Array.from(i.slice(r,o),n=>n[1])),r=o}}run(e,t,i,r){let o=this.device,n=[this.bodies,e.manifolds,e.contacts,e.counters,this.watch,this.table,this.events,this.params];(!this.group||n.some((d,p)=>d!==this.bound[p]))&&(this.bound=n,this.group=o.createBindGroup({layout:this.layout,entries:n.map((d,p)=>({binding:p,resource:{buffer:d}}))})),this.flip=1-this.flip;let[a,l]=this.flip?[0,this.setSlots]:[this.setSlots,0],c=new Uint32Array([a,l,this.setSlots-1,this.capacity,t,i,0,0]);new Float32Array(c.buffer)[6]=r,o.queue.writeBuffer(this.params,0,c);let u=o.createCommandEncoder({label:"contact events"});u.clearBuffer(this.table,(xa+2*l)*4,2*this.setSlots*4);let f=u.beginComputePass({label:"contact events"});f.setBindGroup(0,this.group),f.setPipeline(this.pipelines.touching),f.dispatchWorkgroups(Math.max(1,Math.ceil(t/64))),f.setPipeline(this.pipelines.ended),f.dispatchWorkgroups(Math.ceil(this.setSlots/64)),f.end(),o.queue.submit([u.finish()])}async read(){let e=this.device,t=16+this.capacity*kr*16,i=e.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=e.createCommandEncoder({label:"read contact events"});r.copyBufferToBuffer(this.table,0,i,0,16),r.copyBufferToBuffer(this.events,0,i,16,t-16),r.clearBuffer(this.table,0,16),e.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);i.unmap(),i.destroy();let n=new Uint32Array(o),a=new Float32Array(o),l=Math.min(n[0],this.capacity),c=[];for(let u=0;u<l;u++){let f=4+u*kr*4;c.push({kind:n[f],a:n[f+1],b:n[f+2],step:n[f+3],point:[a[f+4],a[f+5],a[f+6]],impulse:a[f+7],normal:[a[f+8],a[f+9],a[f+10]]})}return c.sort((u,f)=>u.step-f.step),{events:c,dropped:n[0]-l,full:n[1]!==0}}destroy(){for(let e of[this.watch,this.table,this.events,this.params])e.destroy()}};var yc=[0,1,2,3,5,4,0,3,4,0,4,1,1,4,5,1,5,2,2,5,3,2,3,0],Pr=yc.slice();for(let s=0;s<Pr.length;s+=3)[Pr[s+1],Pr[s+2]]=[Pr[s+2],Pr[s+1]];var mo=(s,e,t)=>{let i=Array.from(s??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite values`);return i},vc=(s,e)=>{if(!Number.isFinite(s)||s<=0)throw Error(`${e} must be positive and finite`);return s};function xc(s){let e=[0,1,2].map(u=>s.reduce((f,d)=>f+d[u],0)/s.length),[t,i,r,o]=s,n=i.map((u,f)=>u-t[f]),a=r.map((u,f)=>u-t[f]),l=(n[1]*a[2]-n[2]*a[1])*(t[0]-o[0])+(n[2]*a[0]-n[0]*a[2])*(t[1]-o[1])+(n[0]*a[1]-n[1]*a[0])*(t[2]-o[2]),c=zn(s.flatMap(u=>u.map((f,d)=>f-e[d])),l>0?yc:Pr);if(!c)throw Error("Terrain contains a degenerate collision piece");return{shape:c,position:c.center.map((u,f)=>u+e[f]),rotation:c.rotation}}function ls({heights:s,columns:e,rows:t,spacing:i=1,origin:r=[0,0,0],upAxis:o=1,baseHeight:n,holes:a}={}){for(let[g,b]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(b)||b<2)throw Error(`${g} must be an integer >= 2`);let l=mo(s,e*t,"heights"),c=mo(r,3,"origin");if(![0,1,2].includes(o))throw Error("upAxis must be 0, 1 or 2");let u=typeof i=="number"?[i,i]:mo(i,2,"spacing");u.forEach(g=>vc(g,"spacing"));let f=n??l.reduce((g,b)=>Math.min(g,b),1/0)-2;if(!Number.isFinite(f)||l.some(g=>g<=f))throw Error("baseHeight must be below every terrain height");let d=(e-1)*(t-1);if(a&&a.length!==d)throw Error("holes requires one value per terrain cell");let p=[0,1,2].filter(g=>g!==o),m=[],h=(g,b)=>{let y=[...c];return y[p[0]]+=g*u[0],y[p[1]]+=b*u[1],y[o]+=l[b*e+g],y};for(let g=0;g<t-1;g++)for(let b=0;b<e-1;b++){if(a?.[g*(e-1)+b])continue;let y=h(b,g),A=h(b+1,g),v=h(b,g+1),x=h(b+1,g+1);for(let w of[[y,A,v],[A,x,v]])m.push(xc([...w,...w.map(B=>{let L=[...B];return L[o]=c[o]+f,L})]))}return{kind:"heightfield",pieces:m,triangleCount:m.length}}function cs({positions:s,indices:e,thickness:t=.1}={}){if(!s||s.length%3)throw Error("positions requires xyz triples");let i=mo(s,s.length,"positions"),r=i.length/3;if(e===void 0&&r%3)throw Error("Nonindexed positions require three vertices per triangle");let o=e===void 0?Array.from({length:r},(a,l)=>l):Array.from(e);if(!o.length||o.length%3||!o.every(a=>Number.isInteger(a)&&a>=0&&a<r))throw Error("indices requires valid triangle vertex indices");vc(t,"thickness");let n=[];for(let a=0;a<o.length;a+=3){let l=o.slice(a,a+3).map(g=>i.slice(g*3,g*3+3)),[c,u,f]=l,d=u.map((g,b)=>g-c[b]),p=f.map((g,b)=>g-c[b]),m=[d[1]*p[2]-d[2]*p[1],d[2]*p[0]-d[0]*p[2],d[0]*p[1]-d[1]*p[0]],h=Math.hypot(...m);h!==0&&n.push(xc([...l,...l.map(g=>g.map((b,y)=>b-m[y]*t/h))]))}if(!n.length)throw Error("Static mesh requires at least one nondegenerate triangle");return{kind:"static-mesh",pieces:n,triangleCount:n.length}}function go(s,e,t={}){if(t.mass!==void 0&&t.mass!==0)throw Error("Terrain is static and requires zero mass");let i=t.friction??.6,r=t.restitution??0;if(!Number.isFinite(i)||i<0)throw Error("friction must be nonnegative and finite");if(!Number.isFinite(r)||r<0||r>1)throw Error("restitution must be between 0 and 1");for(let o of["group","collidesWith"]){let n=t[o]??4294967295;if(!Number.isInteger(n)||n<0||n>4294967295)throw Error(`${o} must be an unsigned 32-bit integer`)}if(!e?.pieces?.length)throw Error("Terrain requires collision pieces");return e.pieces.map(o=>{let n=ar(s,o.shape,0,i,o.position,o.rotation);return Object.assign(n,{allowSleep:!1,isTrigger:!1,restitution:r,group:t.group??4294967295,collidesWith:t.collidesWith??4294967295}),n})}var mt=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),Le=s=>s&&typeof s.x=="number"?[s.x,s.y,s.z,...typeof s.w=="number"?[s.w]:[]]:Array.from(s),us=new WeakMap,Lr=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s},bo=(s,e,t=!1)=>{if(!Number.isFinite(s)||(t?s<0:s<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return s},yo=class s{static async create({device:e,...t}={}){if(t.scene&&us.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await ss({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return await Cr(e),r=new s(e,t),r.ownsDevice=i,r.scene&&r.autoAttach&&r.attachToScene(),r}catch(o){throw r?r.dispose():i&&e.destroy(),o}}constructor(e,{scene:t,gravity:i=[0,-10,0],timeStep:r=1/60,iterations:o=10,capacity:n=4096,syncMeshes:a=!0,maxSyncedBodies:l=2048,broadphase:c="auto",solverMode:u="auto",smallGroups:f,bvh:d={},autoAttach:p=!0,maxContactPairs:m=8192,maxContactEvents:h=4096,sleeping:g=!1}={}){if(this.device=e,this.scene=t,this.autoAttach=p,this.syncEnabled=a,this.maxSyncedBodies=l,!Number.isInteger(n)||n<1||!Number.isInteger(l)||l<0)throw Error("capacity and maxSyncedBodies must be valid integer counts");if(this.capacity=n,this.collisionOptions={broadphase:c,bvh:d,solverMode:u,smallGroups:f},this.ref=new ai,this.ref.dt=bo(r,"timeStep"),!Number.isInteger(o)||o<1)throw Error("iterations must be a positive integer");this.ref.iterations=o,this.aggregates=[],this.constraints=new Set,this.contactListeners=new Set;for(let[y,A]of Object.entries({maxContactPairs:m,maxContactEvents:h}))if(!Number.isInteger(A)||A<1)throw Error(`${y} must be a positive integer`);if(this.contactOptions={maxContactPairs:m,maxContactEvents:h},this.bodyReadback=new vi(e),this.sleepOptions=g,g!==!1&&g!==!0&&(typeof g!="object"||g===null))throw Error("sleeping must be a boolean or options object");this.commands=new Map,this.steps=0,this.errors=[],this.deviceLost=null;let b=new WeakRef(this);e.lost.then(y=>{let A=b.deref();!A||A.disposed||(A.deviceLost={reason:y.reason,message:y.message},A.errors.push(`WebGPU device lost: ${y.message||y.reason}`),A.dispose())}),this.setGravity(i),this.errorListener=y=>this.errors.push(y.error.message),e.addEventListener("uncapturederror",this.errorListener),t&&us.set(t,this)}setGravity(e){let t=Le(e);if(t.length!==3||!t.every(Number.isFinite))throw Error("gravity requires three finite components");let i=Math.hypot(...t);this.gravity=-i,this.up=i?t.map(r=>-r/i):[0,1,0],this.gpu&&(this.gpu.params.gravity=this.gravity,this.gpu.params.up=this.up),this.wakeAll()}get solverDecision(){return this.initialize(),{...this.gpu.solverDecision}}setSolverMode(e){this.initialize();let t=this.gpu.setSolverMode(e);return this.collisionOptions.solverMode=e,{...t}}initialize(){if(this.assertAlive(),this.gpu)return;if(this.syncEnabled&&this.aggregates.filter(r=>r.sync).length>this.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(this.aggregates.length>this.capacity)throw Error(`Body capacity ${this.capacity} exceeded; size the world before initialization`);this.gpu=new gi(this.device,this.ref,ho(this.ref,{bodyCapacity:Math.max(1,this.capacity),spatialSort:!0,...this.collisionOptions})),Object.assign(this.gpu.params,{gravity:this.gravity,up:this.up}),this.gpu.externalBeforeStep=r=>{this.sleepManager?.before(r),this.flushCommands(r)},this.gpu.externalAfterStep=r=>this.sleepManager?.after(r);let e=new Map([...this.constraints].map(r=>[r.force,r])),t=[],i=0;for(let r of this.ref.forces){let o=e.get(r);o&&(o.attach(i++,!1),(o.type==="fixed"||o.type==="spring"&&o.rest===void 0)&&t.push({slot:o.slot,spring:o.type==="spring"}))}this.gpu.captureConstraintFrames(t),this.byGpuIndex=new Map(this.aggregates.map(r=>[this.gpu.gpuIndex(r.index),r.body]));for(let r of this.aggregates)(r.disposed||r.group!==4294967295||r.collidesWith!==4294967295)&&this.gpu.setFilters([this.gpu.gpuIndex(r.index)],[r.disposed?0:r.group],[r.disposed?0:r.collidesWith]);for(let r of this.aggregates)r.isTrigger&&this.gpu.setSensor(r.body.gpuIndex,!0);for(let r of this.aggregates)r.restitution>0&&this.gpu.setRestitution(r.body.gpuIndex,r.restitution);this.contactListeners.size&&this.initializeContacts(),this.sleepOptions&&(this.sleepManager=new Fi(this,this.sleepOptions===!0?{}:this.sleepOptions))}addAggregate(e,t,i={}){return new vo(e,t,i,this)}addAggregates(e){return e.map(({mesh:t,type:i,options:r})=>this.addAggregate(t,i,r))}addHeightfield(e){return this.addTerrain(ls(e),e)}addStaticMesh(e,t={}){e.computeWorldMatrix(!0);let i=e.getVerticesData("position"),r=e.getWorldMatrix().m;if(!i)throw Error("Static mesh requires vertex positions");let o=new Float64Array(i.length);for(let u=0;u<i.length;u+=3){let f=i[u],d=i[u+1],p=i[u+2];for(let m=0;m<3;m++)o[u+m]=r[m]*f+r[4+m]*d+r[8+m]*p+r[12+m]}let n=e.getIndices(),a=n?.length?Uint32Array.from(n):Uint32Array.from({length:i.length/3},(u,f)=>f),l=e.getVerticesData("normal"),c=e.getWorldMatrix().determinant()<0;for(let u=0;u<a.length;u+=3){let f=a[u]*3,d=a[u+1]*3,p=a[u+2]*3,m=[0,1,2].map(b=>i[d+b]-i[f+b]),h=[0,1,2].map(b=>i[p+b]-i[f+b]),g=!1;l?.length===i.length&&(g=[m[1]*h[2]-m[2]*h[1],m[2]*h[0]-m[0]*h[2],m[0]*h[1]-m[1]*h[0]].reduce((y,A,v)=>y+A*(l[f+v]+l[d+v]+l[p+v]),0)<0),g!==c&&([a[u+1],a[u+2]]=[a[u+2],a[u+1]])}return this.addTerrain(cs({positions:o,indices:a,thickness:t.thickness}),t)}addTerrain(e,t={}){if(this.assertAlive(),this.gpu)throw Error("Add static terrain before initializing the physics world");if(this.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Terrain collisions require nine storage buffer bindings");if(this.aggregates.length+(e?.pieces?.length??0)>this.capacity)throw Error("AVBD terrain exceeds body capacity; reserve space for its collision pieces");let r=go(this.ref,e,t).map(n=>new Aa(this,n)),o=!1;return{bodies:r.map(n=>n.body),triangleCount:e.triangleCount,kind:e.kind,dispose:()=>{if(o)return;o=!0;let n=new Set(r.map(a=>a.body));for(let a of[...this.constraints])(n.has(a.bodyA)||n.has(a.bodyB))&&a.dispose();if(this.gpu&&!this.disposed){let a=r.map(c=>c.body.gpuIndex),l=new Uint32Array(a.length);this.gpu.setFilters(a,l,l)}for(let a of r)a.disposed=a.body.disposed=!0,a.group=a.collidesWith=0,this.commands.delete(a.body);this.wakeAll()}}}addConstraint(e,t,i={}){return new os(this,e,t,i)}addJoint(e,t,i={}){return this.addConstraint(e,t,{...i,type:i.type??"ball"})}addWeld(e,t,i={}){return this.addConstraint(e,t,{...i,type:"fixed"})}addSpring(e,t,i={}){return this.addConstraint(e,t,{...i,type:"spring"})}enableSleeping(e={}){return this.assertAlive(),this.sleepOptions=e,this.gpu&&!this.sleepManager&&(this.sleepManager=new Fi(this,e)),this}disableSleeping(){return this.assertAlive(),this.sleepOptions=!1,this.gpu?(this.flushCommands(),this.sleepManager&&(this.sleepManager.wakeRequested=!0,this.sleepManager.before(),this.sleepManager.dispose(),this.sleepManager=null),this):this}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new ns(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new xi(this.device,this.gpu.bodyBuffer,this.capacity,e,t);let i=this.aggregates.filter(r=>!r.disposed);this.contactWatch.setWatched(i.map(r=>r.body.gpuIndex),i.map(()=>!0))}onContact(e){if(this.assertAlive(),typeof e!="function")throw Error("A contact callback is required");return this.contactListeners.add(e),this.gpu&&this.initializeContacts(),()=>this.contactListeners.delete(e)}async readContactEvents(){return this.initialize(),this.initializeContacts(),this.contactPending?this.contactPending:(this.contactPending=this.contactWatch.read().then(e=>({...e,events:e.events.map(t=>({...t,type:t.kind===Ai?"begin":"end",isTrigger:!!(this.byGpuIndex.get(t.a)?.aggregate.isTrigger||this.byGpuIndex.get(t.b)?.aggregate.isTrigger),a:this.byGpuIndex.get(t.a),b:this.byGpuIndex.get(t.b)}))})).finally(()=>this.contactPending=null),this.contactPending)}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}async sphereCast(e,t,i,r={}){let[o]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}async sphereCastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){this.assertAlive(),Lr(i),this.initialize(),this.flushCommands();for(let n of t)if(n.aggregate.world!==this||n.disposed)throw Error("Ignored cast bodies must belong to this world and be alive");return(await this.gpu.sphereCastAll(e.map(n=>({...n,origin:Le(n.origin),direction:Le(n.direction)})),{ignore:t.map(n=>n.gpuIndex),collidesWith:i,includeTriggers:r})).map(n=>n?{...n,body:this.byGpuIndex.get(n.index)??null}:null)}async raycastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){Lr(i),this.initialize(),this.flushCommands();let o=e.map(a=>{let l=Le(a.origin),c=Le(a.direction);if(l.length!==3||c.length!==3||![...l,...c].every(Number.isFinite)||!Math.hypot(...c))throw Error("A ray requires a finite origin and nonzero direction");return a.maxDistance!==void 0&&bo(a.maxDistance,"maxDistance",!0),{...a,origin:l,direction:c}});for(let a of t)if(a.aggregate.world!==this)throw Error("Ignored ray bodies must belong to this world");return(await this.gpu.raycastAll(o,{ignore:t.map(a=>a.gpuIndex),collidesWith:i,includeTriggers:r})).map(a=>a?{...a,body:this.byGpuIndex.get(a.index)??null}:null)}getRenderBinding(e=this.device){if(this.initialize(),e!==this.device)throw Error("Direct GPU rendering must share the physics GPUDevice");return{device:e,buffer:this.bodyBuffer,stride:160,positionOffset:0,rotationOffset:16,sizeOffset:64,count:this.gpu.bodyCount}}wakeAll(){this.sleepManager&&(this.sleepManager.wakeRequested=!0)}async readSleepStats(){return this.initialize(),this.sleepManager?this.sleepManager.readStats():{sleeping:0,wakeRequested:!1}}assertAlive(){if(this.disposed)throw Error("AVBD world is disposed")}queue(e,t,i,r){this.assertAlive(),e.assertAlive();let o=Le(r);if(o.length!==(i==="rotation"?4:3)||!o.every(Number.isFinite))throw Error(`${i} requires ${i==="rotation"?4:3} finite components`);let n=this.commands.get(e);return n||this.commands.set(e,n={mask:0}),n.mask|=t,n[i]=o,this.wakeAll(),n}flushCommands(e){if(this.initialize(),this.gpu.flushPropertyEdits(e),!this.commands.size)return;this.sleepManager?.wakeRequested&&this.sleepManager.before(e);let t=this.commands.size,i=0;for(let h of this.commands.values())i|=h.mask;if(i===1||i===2){if(!this.velocityIndices||this.velocityIndices.length<t){let y=Math.max(t,(this.velocityIndices?.length??0)*2,64);this.velocityIndices=new Uint32Array(y),this.velocityValues=new Float32Array(y*3)}let h=0;for(let[y,A]of this.commands)this.velocityIndices[h]=y.aggregate.gpuSlot??this.gpu.gpuIndex(y.aggregate.index),this.velocityValues.set(i===2?A.angular:A.velocity,h++*3);let g=i===2?"angularVelocityBatch":"velocityBatch",b=this[g]??=new Kt(this.gpu,3,i===2?"angular":"linear");b.solver=this.gpu,b.submitValidated(this.velocityIndices.subarray(0,t),this.velocityValues.subarray(0,t*3),e),this.lastCommandBatch={bodies:t,path:i===2?"packed-angular-velocities":"packed-velocities",uploadedBytes:t*16},this.commands.clear();return}let r=!0;for(let h of this.commands.values())if((h.mask&-4)!==0){r=!1;break}if(r){(!this.motionData||this.motionData.length<t*8)&&(this.motionData=new Uint32Array(Math.max(t*8,(this.motionData?.length??0)*2,512)),this.motionFloats=new Float32Array(this.motionData.buffer));let h=0;for(let[g,b]of this.commands){let y=h++*8;this.motionData[y]=g.aggregate.gpuSlot??this.gpu.gpuIndex(g.aggregate.index),this.motionData[y+1]=b.mask,b.velocity&&this.motionFloats.set(b.velocity,y+2),b.angular&&this.motionFloats.set(b.angular,y+5)}this.motionBatch??=new yi(this.gpu,3),this.motionBatch.submitValidated(this.motionData.subarray(0,t*8),e),this.lastCommandBatch={bodies:t,path:"packed-motion",uploadedBytes:t*32},this.commands.clear();return}let o=t*128,n=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize);if(o>n)throw Error("Body command batch exceeds GPU buffer limits");(!this.commandData||this.commandData.byteLength<o)&&(this.commandData=new ArrayBuffer(Math.min(n,Math.max(o,(this.commandData?.byteLength??0)*2,128))));let a=this.commandData,l=new Float32Array(a),c=new Uint32Array(a);l.fill(0,0,t*32);let u=0;for(let[h,g]of this.commands){let b=u++*32;c[b]=h.aggregate.gpuSlot??this.gpu.gpuIndex(h.aggregate.index),c[b+1]=g.mask;for(let[y,A]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])g[y]&&l.set(g[y],b+A)}this.commands.clear(),(!this.commandBuffer||this.commandBuffer.size<o)&&(this.commandBuffer?.destroy(),this.commandBuffer=this.device.createBuffer({size:Math.min(n,Math.max(o*2,128)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),this.device.queue.writeBuffer(this.commandBuffer,0,a,0,o),this.commandPipeline??=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3820
|
-
${gc}`,label:"AVBD body commands"}),entryPoint:"applyCommands"}}),(!this.commandBinding||this.boundCommandBody!==this.gpu.bodyBuffer||this.boundCommandBuffer!==this.commandBuffer||this.boundCommandBytes!==o)&&(this.commandBinding=this.device.createBindGroup({layout:this.commandPipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.gpu.bodyBuffer}},{binding:1,resource:{buffer:this.commandBuffer,size:o}}]}),this.boundCommandBody=this.gpu.bodyBuffer,this.boundCommandBuffer=this.commandBuffer,this.boundCommandBytes=o);let f=e??this.device.createCommandEncoder(),d=f.beginComputePass();d.setPipeline(this.commandPipeline),d.setBindGroup(0,this.commandBinding);let p=Math.ceil(u/64),m=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(p,m),Math.ceil(p/m)),d.end(),e||this.device.queue.submit([f.finish()]),this.lastCommandBatch={bodies:t,path:"aggregate-commands",uploadedBytes:o}}step(){this.initialize(),this.gpu.step(),this.steps++,this.contactWatch&&(this.contactWatch.run(this.gpu.contactStorage,this.gpu.manifoldCapacity,this.steps,this.ref.dt),this.contactListeners.size&&!this.contactPending&&this.readContactEvents().then(({events:e,dropped:t,full:i})=>{if(!this.disposed){(t||i)&&this.errors.push(`Contact event storage full: ${t} events dropped; increase maxContactEvents/maxContactPairs`);for(let r of e)for(let o of this.contactListeners)o(r)}}).catch(e=>{this.disposed||this.errors.push(e.message)})),this.syncEnabled&&!this.syncPending&&this.syncMeshes().catch(e=>this.errors.push(e.message)),this.steps%20===0&&!this.counterPending&&(this.counterPending=!0,this.gpu.readCounters().then(e=>{this.disposed||(this.counters=e,this.gpu.adapt(e),(e.overflow||e.clashes)&&this.errors.push(`Collision capacity/color conflict: ${JSON.stringify(e)}`))}).catch(e=>{this.disposed||this.errors.push(e.message)}).finally(()=>this.counterPending=!1))}async syncMeshes(){if(this.initialize(),this.flushCommands(),this.syncPending)return this.syncPending;let e=this.aggregates.filter(t=>t.sync&&!t.disposed);return this.syncPending=this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.body.gpuIndex),!0).then(t=>{if(!this.disposed){for(let i=0;i<e.length;i++)e[i].disposed||e[i].syncPose(t,i*8);this.lastSyncBytes=t.byteLength}}).finally(()=>this.syncPending=null),this.syncPending}get bodyBuffer(){return this.initialize(),this.gpu.bodyBuffer}async readBodies(e){if(this.initialize(),this.flushCommands(),e){for(let t of e)if(t.aggregate.world!==this)throw Error("Selected bodies must belong to this world");return this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.gpuIndex))}return this.gpu.readBodies()}dispose(){if(!this.disposed){this.disposed=!0,this.observer&&this.scene?.onBeforeRenderObservable.remove(this.observer),this.disposeObserver&&this.scene?.onDisposeObservable.remove(this.disposeObserver),this.scene&&us.get(this.scene)===this&&us.delete(this.scene),this.device.removeEventListener("uncapturederror",this.errorListener),this.gpu?.destroy(),this.contactWatch?.destroy(),this.sleepManager?.dispose(),this.commandBuffer?.destroy(),this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback.destroy(),this.commandData=this.velocityIndices=this.velocityValues=null,this.commandBinding=null;for(let e of this.aggregates)e.disposed=!0,e.body.disposed=!0;this.commands.clear(),this.contactListeners.clear();for(let e of this.constraints)e.disposed=!0;this.constraints.clear(),this.ownsDevice&&this.device.destroy()}}attachToScene(e=this.scene){if(this.assertAlive(),!e)throw Error("A Babylon scene is required");if(this.observer&&e===this.scene)return this;if(this.observer)throw Error("AVBD world is already attached to another scene");this.scene=e,this.disposeObserver=e.onDisposeObservable.addOnce(()=>this.dispose());let t=0;return this.observer=e.onBeforeRenderObservable.add(()=>{t+=Math.min(.05,e.getEngine().getDeltaTime()/1e3);for(let i=0;t>=this.ref.dt&&i<3;i++)this.step(),t-=this.ref.dt}),this}},vo=class{constructor(e,t,{mass:i=1,friction:r=.6,restitution:o=0,allowSleep:n,isTrigger:a=!1,sync:l=!0,group:c=4294967295,collidesWith:u=4294967295}={},f){if(f=f instanceof yo?f:us.get(f??e.getScene()),!f)throw Error("Create an AVBD world for this scene before adding aggregates");if(f.assertAlive(),typeof t=="number"&&(t=[mt.SPHERE,mt.CAPSULE,mt.CYLINDER,mt.BOX,mt.CONVEX_HULL][t]),bo(i,"mass",!0),bo(r,"friction",!0),!Number.isFinite(o)||o<0||o>1)throw Error("restitution must be between 0 and 1");if(e.parent)throw Error("AVBD aggregate meshes must have no parent; bake the parent transform first");if(!Object.values(mt).includes(t))throw Error(`Unsupported AVBD shape: ${t}`);if(f.aggregates.length>=f.capacity)throw Error("AVBD body capacity exceeded");if(this.world=f,this.mesh=e,this.sync=l,this.type=t,n!==void 0&&typeof n!="boolean")throw Error("allowSleep must be a boolean");if(this.allowSleep=n??!0,typeof a!="boolean")throw Error("isTrigger must be a boolean");if(this.isTrigger=a,this.restitution=o,n===!0&&!f.sleepOptions&&f.enableSleeping(),this.group=Lr(c),this.collidesWith=Lr(u),f.gpu&&f.syncEnabled&&l&&f.aggregates.filter(v=>v.sync&&!v.disposed).length>=f.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(![mt.BOX,mt.SPHERE,mt.CAPSULE].includes(t)&&f.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Convex hull collisions require nine storage buffer bindings");e.computeWorldMatrix(!0),e.refreshBoundingInfo();let d=e.getBoundingInfo().boundingBox,p=e.scaling;if([p.x,p.y,p.z].some(v=>v<=0))throw Error("Bake negative or zero mesh scaling before creating an aggregate");let m=d.maximum.subtract(d.minimum).multiply(p),h=d.center.multiply(p),g=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=h,this.localRotation=g.constructor.Identity();let b;if(t===mt.BOX)b=new Ze(f.ref,Le(m),1,r,[0,0,0]);else if(t===mt.SPHERE){if(Math.max(m.x,m.y,m.z)-Math.min(m.x,m.y,m.z)>1e-4)throw Error("Sphere aggregate requires uniform dimensions");b=nr(f.ref,m.x/2,1,r,[0,0,0])}else if(t===mt.CAPSULE){if(Math.abs(m.x-m.z)>1e-4)throw Error("Capsule aggregate requires equal X and Z diameters; bake an upright Y-axis capsule mesh");b=Br(f.ref,m.x/2,m.y,1,r,[0,0,0])}else{let v=e.getVerticesData("position");if(!v)throw Error("Hull aggregate requires mesh positions");let x=Float32Array.from(v,(B,L)=>B*[p.x,p.y,p.z][L%3]),w=Ur(x);if(!w)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...w.center),this.localRotation=g.constructor.FromArray(w.rotation),b=ar(f.ref,w,1,r,[0,0,0])}let y=i/b.mass;b.mass=i,b.moment.forEach((v,x)=>b.moment[x]=v*y);let A=e.position.add(this.localCenter.applyRotationQuaternion(g));if(b.positionLin.set(Le(A)),b.positionAng.set(g.multiply(this.localRotation).asArray()),this.rigid=b,this.index=f.aggregates.length,this.body=new fs(this),f.aggregates.push(this),e.rotationQuaternion=g,f.gpu){if(this.gpuSlot=f.gpu.addBody(b),this.gpuSlot<0)throw Error("AVBD body capacity exceeded");f.byGpuIndex.set(this.gpuSlot,this.body),f.gpu.setFilters([this.gpuSlot],[this.group],[this.collidesWith]),(this.isTrigger||f.gpu.sensorsEnabled)&&f.gpu.setSensor(this.gpuSlot,this.isTrigger),this.restitution>0&&f.gpu.setRestitution(this.gpuSlot,this.restitution),f.contactWatch?.setWatched([this.gpuSlot],[!0]),f.sleepManager?.register(this.gpuSlot,b.mass,this.allowSleep),f.wakeAll?.()}}syncPose(e,t){let i=t??this.body.gpuIndex*40,r=this.localRotation.constructor.FromArray(e,i+4).multiply(this.localRotation.conjugate());this.mesh.rotationQuaternion.copyFrom(r),this.mesh.position.copyFrom(new this.mesh.position.constructor(...e.subarray(i,i+3)).subtract(this.localCenter.applyRotationQuaternion(r)))}dispose(){Ac(this)}},Aa=class{constructor(e,t){Object.assign(this,{world:e,rigid:t,index:e.aggregates.length,sync:!1,allowSleep:!1,isTrigger:!1,restitution:t.restitution,group:t.group,collidesWith:t.collidesWith,staticTerrain:!0}),this.body=new fs(this),e.aggregates.push(this)}dispose(){Ac(this)}};function Ac(s){if(s.disposed)return;let e=s.world;for(let t of[...e.constraints])(t.bodyA===s.body||t.bodyB===s.body)&&t.dispose();if(e.gpu){let t=s.body.gpuIndex;e.queue(s.body,16,"velocity",[0,0,0]),e.gpu.setFilters([t],[0],[0]),e.gpu.setRestitution(t,0)}s.rigid.mass=0,s.rigid.moment.fill(0),s.sync=!1,s.disposed=s.body.disposed=!0,e.sleepManager?.register(s.gpuSlot??e.gpu?.gpuIndex(s.index)??s.index,0),e.wakeAll?.()}var fs=class{constructor(e){this.aggregate=e}assertAlive(){if(this.disposed||this.aggregate.disposed)throw Error("AVBD body is disposed")}get gpuIndex(){this.assertAlive();let e=this.aggregate.world;return e.initialize(),this.aggregate.gpuSlot??e.gpu.gpuIndex(this.aggregate.index)}setLinearVelocity(e){return this.aggregate.world.queue(this,1,"velocity",e),this}setAngularVelocity(e){return this.aggregate.world.queue(this,2,"angular",e),this}applyImpulse(e,t){let i=t?Le(t):null;if(i&&(i.length!==3||!i.every(Number.isFinite)))throw Error("Impulse point requires three finite components");let r=this.aggregate.world,o=Le(e),n=r.commands.get(this),a=o.map((c,u)=>c+(n?.impulse?.[u]||0)),l=r.queue(this,4,"impulse",a);if(t){let c=[i[1]*o[2]-i[2]*o[1],i[2]*o[0]-i[0]*o[2],i[0]*o[1]-i[1]*o[0]],u=l.torque||[0,0,0];l.torque=Le(c).map((f,d)=>f+u[d]),l.pointImpulse=o.map((f,d)=>f+(l.pointImpulse?.[d]||0))}return this}applyAngularImpulse(e){let t=this.aggregate.world,i=t.commands.get(this)?.torque||[0,0,0];return t.queue(this,4,"torque",Le(e).map((r,o)=>r+i[o])),this}applyForce(e,t){return this.applyImpulse(Le(e).map(i=>i*this.aggregate.world.ref.dt),t)}applyTorque(e){return this.applyAngularImpulse(Le(e).map(t=>t*this.aggregate.world.ref.dt))}setCollisionGroups(e,t=4294967295){this.assertAlive();let i=this.aggregate,r=Lr(e),o=Lr(t);return i.group=r,i.collidesWith=o,i.world.gpu&&i.world.gpu.setFilters([this.gpuIndex],[i.group],[i.collidesWith]),i.world.wakeAll?.(),this}setTrigger(e){if(this.assertAlive(),typeof e!="boolean")throw Error("isTrigger must be a boolean");let t=this.aggregate;return t.isTrigger=e,t.world.gpu&&t.world.gpu.setSensor(this.gpuIndex,e),t.world.wakeAll(),this}setRestitution(e){if(this.assertAlive(),!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");let t=this.aggregate;return t.restitution=e,t.world.gpu&&t.world.gpu.setRestitution(this.gpuIndex,e),t.world.wakeAll(),this}async readState(){let e=await this.aggregate.world.readBodies([this]),t=this.aggregate.rigid.mass;return{position:Array.from(e.subarray(0,3)),rotation:Array.from(e.subarray(4,8)),linearVelocity:Array.from(e.subarray(32,35)),angularVelocity:Array.from(e.subarray(36,39)),mass:t,effectiveMass:e[19],sleeping:t>0&&e[19]===0}}wakeUp(){return this.assertAlive(),this.aggregate.world.wakeAll(),this}setSleepEnabled(e){if(this.assertAlive(),typeof e!="boolean")throw Error("Sleep eligibility must be a boolean");let t=this.aggregate,i=t.world;return t.allowSleep=e,e&&!i.sleepOptions&&i.enableSleeping(),i.gpu&&i.sleepManager?.register(this.gpuIndex,t.rigid.mass,e),i.wakeAll(),this}teleport(e,t){if(this.assertAlive(),this.aggregate.staticTerrain)throw Error("Terrain is static; set its geometry transform before registration");let i=this.aggregate;if(t===void 0){let n=i.world.commands.get(this);if(n&&(n.mask&8)!==0&&(n.mask&32)===0){let a=i.mesh.rotationQuaternion.clone();a.copyFromFloats(...n.rotation);let l=i.localCenter.applyRotationQuaternion(i.localRotation.conjugate());i.world.queue(this,8,"position",Le(e.add(l.applyRotationQuaternion(a))))}else{let a=i.localCenter.applyRotationQuaternion(i.localRotation.conjugate());i.world.queue(this,40,"position",Le(e)),i.world.queue(this,40,"rotation",[...Le(a),0])}return this}let r=t.clone().normalize();i.world.queue(this,8,"position",Le(e.add(i.localCenter.applyRotationQuaternion(r))));let o=i.world.queue(this,8,"rotation",r.multiply(i.localRotation).asArray());return o.mask&=-33,this}};var Ni=class{solver;bodyA;bodyB;J=new Float64Array(12);H=new Float64Array(36);C=new Float64Array(4);fmin=new Float64Array(4).fill(-1/0);fmax=new Float64Array(4).fill(1/0);stiffness=new Float64Array(4).fill(1/0);fracture=new Float64Array(4).fill(1/0);penalty=new Float64Array(4);lambda=new Float64Array(4);constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}disable(){this.stiffness.fill(0),this.penalty.fill(0),this.lambda.fill(0)}};var wc=s=>s<0?-1:s>0?1:0,_r=(s,e)=>s<e?s:e,If=(s,e)=>s>e?s:e,Er=(s,e,t)=>If(e,_r(t,s));function ds(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function qi(s,e,t){let i=Math.cos(s[2]),r=Math.sin(s[2]);return[i*e-r*t+s[0],r*e+i*t+s[1]]}function Zt(s,e,t,i,r){s[e*3]=t,s[e*3+1]=i,s[e*3+2]=r}var xo=class extends Ni{rA;rB;C0=new Float64Array(3);torqueArm;restAngle;constructor(e,t,i,r,o,n=[1/0,1/0,1/0],a=1/0){super(e,t,i),this.rA=[r[0],r[1]],this.rB=[o[0],o[1]],this.stiffness[0]=n[0],this.stiffness[1]=n[1],this.stiffness[2]=n[2],this.fmax[2]=a,this.fmin[2]=-a,this.fracture[2]=a,this.restAngle=(t?t.position[2]:0)-i.position[2];let l=(t?t.size[0]:0)+i.size[0],c=(t?t.size[1]:0)+i.size[1];this.torqueArm=l*l+c*c}rows(){return 3}evaluate(e){let t=this.bodyA?qi(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=qi(this.bodyB.position,this.rB[0],this.rB[1]);e[0]=t[0]-i[0],e[1]=t[1]-i[1],e[2]=((this.bodyA?this.bodyA.position[2]:0)-this.bodyB.position[2]-this.restAngle)*this.torqueArm}initialize(){return this.evaluate(this.C0),this.stiffness[0]!==0||this.stiffness[1]!==0||this.stiffness[2]!==0}Cn=new Float64Array(3);computeConstraint(e){this.evaluate(this.Cn);for(let t=0;t<3;t++)this.C[t]=this.stiffness[t]===1/0?this.Cn[t]-this.C0[t]*e:this.Cn[t]}computeDerivatives(e){let{J:t,H:i}=this;if(i.fill(0,0,27),e===this.bodyA){let r=ds(e.position[2],this.rA[0],this.rA[1]);Zt(t,0,1,0,-r[1]),Zt(t,1,0,1,r[0]),Zt(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=ds(e.position[2],this.rB[0],this.rB[1]);Zt(t,0,-1,0,r[1]),Zt(t,1,0,-1,-r[0]),Zt(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},Ao=class extends Ni{rA;rB;rest;constructor(e,t,i,r,o,n,a=-1){if(super(e,t,i),this.rA=[r[0],r[1]],this.rB=[o[0],o[1]],this.stiffness[0]=n,this.rest=a,this.rest<0){let l=qi(t.position,r[0],r[1]),c=qi(i.position,o[0],o[1]);this.rest=Math.hypot(l[0]-c[0],l[1]-c[1])}}rows(){return 1}initialize(){return!0}delta(){let e=qi(this.bodyA.position,this.rA[0],this.rA[1]),t=qi(this.bodyB.position,this.rB[0],this.rB[1]);return[e[0]-t[0],e[1]-t[1]]}computeConstraint(){let e=this.delta();this.C[0]=Math.hypot(e[0],e[1])-this.rest}computeDerivatives(e){let t=this.delta(),i=t[0]*t[0]+t[1]*t[1];if(i===0)return;let r=Math.sqrt(i),o=t[0]/r,n=t[1]/r,a=(1-o*o)/r,l=(0-o*n)/r,c=(0-n*o)/r,u=(1-n*n)/r,f=e===this.bodyA,d=f?this.rA:this.rB,p=ds(e.position[2],-d[1],d[0]),m=ds(e.position[2],d[0],d[1]),h=a*p[0]+l*p[1],g=c*p[0]+u*p[1],b=o*m[0]+n*m[1],y=o*p[0]+n*p[1];f?Zt(this.J,0,o,n,y):Zt(this.J,0,-o,-n,-y);let A=f?-b-b:b+b,v=this.H;v[0]=a,v[1]=l,v[2]=h,v[3]=c,v[4]=u,v[5]=g,v[6]=h,v[7]=g,v[8]=A}},wo=class extends Ni{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},Bo=class extends Ni{speed;constructor(e,t,i,r,o){super(e,t,i),this.speed=r,this.fmax[0]=o,this.fmin[0]=-o}rows(){return 1}initialize(){return!0}computeConstraint(){let e=this.bodyA?this.bodyA.position[2]-this.bodyA.initial[2]:0,t=this.bodyB.position[2]-this.bodyB.initial[2];this.C[0]=e-t-this.speed*this.solver.dt}computeDerivatives(e){Zt(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var Co=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),So=()=>({...Co(),postStabilize:!1,alpha:.95,matchNearest:!0});var Mr=(s,e)=>s>e?s*2097152+e:e*2097152+s;function Rf(s,e,t){let i=0,r=e;for(;i<r;){let o=i+r>>>1;s[o]<t?i=o+1:r=o}return i}function wa(s,e,t){let i=Rf(s,e,t);return i<e&&s[i]===t}var ko=class{cellSize=1;pairs=new Float64Array(1024);pairCount=0;configuredFor=-1;large=new Int32Array(0);isLarge=new Uint8Array(0);cellX=new Int32Array(0);cellY=new Int32Array(0);tableSize=0;bucketStart=new Int32Array(0);sorted=new Int32Array(0);configure(e,t){this.configuredFor=e;let i=Float64Array.from({length:e},(a,l)=>t[l*4+1]).sort(),r=e>0?i[e>>1]:1,o=0;for(let a=0;a<e;a++)i[a]<=4*r&&(o=Math.max(o,i[a]));this.cellSize=Math.max(2*o,.001),this.isLarge=new Uint8Array(e);let n=[];for(let a=0;a<e;a++)t[a*4+1]>o&&(this.isLarge[a]=1,n.push(a));for(this.large=Int32Array.from(n),this.cellX=new Int32Array(e),this.cellY=new Int32Array(e),this.tableSize=1;this.tableSize<2*e;)this.tableSize<<=1;this.bucketStart=new Int32Array(this.tableSize+1),this.sorted=new Int32Array(e)}hash(e,t){return(Math.imul(e,73856093)^Math.imul(t,19349663))&this.tableSize-1}findPairs(e,t,i,r,o,n){e!==this.configuredFor&&this.configure(e,i),this.pairCount=0;let{cellX:a,cellY:l,isLarge:c,bucketStart:u,sorted:f}=this,d=1/this.cellSize;u.fill(0);for(let h=0;h<e;h++)c[h]||(a[h]=Math.floor(t[h*4]*d),l[h]=Math.floor(t[h*4+1]*d),u[this.hash(a[h],l[h])+1]++);for(let h=0;h<this.tableSize;h++)u[h+1]+=u[h];let p=u.slice(0,this.tableSize);for(let h=0;h<e;h++)c[h]||(f[p[this.hash(a[h],l[h])]++]=h);let m=(h,g)=>{if(!r[h]&&!r[g])return;let b=t[h*4]-t[g*4],y=t[h*4+1]-t[g*4+1],A=i[h*4+1]+i[g*4+1];if(b*b+y*y>A*A)return;let v=Mr(h,g);if(!wa(o,n,v)){if(this.pairCount===this.pairs.length){let x=new Float64Array(this.pairs.length*2);x.set(this.pairs),this.pairs=x}this.pairs[this.pairCount++]=v}};for(let h=0;h<e;h++){if(c[h])continue;let g=a[h],b=l[h];for(let y=-1;y<=1;y++)for(let A=-1;A<=1;A++){let v=this.hash(g+A,b+y);for(let x=u[v];x<u[v+1];x++){let w=f[x];w>=h||a[w]!==g+A||l[w]!==b+y||m(h,w)}}}for(let h of this.large)for(let g=0;g<e;g++)g===h||c[g]&&g>h||m(h,g);this.pairs.subarray(0,this.pairCount).sort()}};var gt=new Float64Array(12),Bc=new Float64Array(12),Vi=new Float64Array(12);function qt(s,e,t,i,r,o,n,a){let l=e*6;s[l]=t,s[l+1]=i,s[l+2]=r,s[l+3]=o,s[l+4]=n,s[l+5]=a}function Cc(s,e,t,i,r,o){let n=0,a=t*e[0]+i*e[1]-r,l=t*e[6]+i*e[7]-r;if(a<=0){for(let c=0;c<6;c++)s[n*6+c]=e[c];n++}if(l<=0){for(let c=0;c<6;c++)s[n*6+c]=e[6+c];n++}if(a*l<0){let c=a/(a-l),u=e[0]+(e[6]-e[0])*c,f=e[1]+(e[7]-e[1])*c;a>0?qt(s,n,u,f,o,e[3],0,e[5]):qt(s,n,u,f,e[8],o,e[10],0),n++}return n}function Po(s,e,t,i,r,o,n,a){let l=-(r*n+o*a),c=-(-o*n+r*a);Math.abs(l)>Math.abs(c)?l>0?(qt(gt,0,s,-e,0,0,3,4),qt(gt,1,s,e,0,0,4,1)):(qt(gt,0,-s,e,0,0,1,2),qt(gt,1,-s,-e,0,0,2,3)):c>0?(qt(gt,0,s,e,0,0,4,1),qt(gt,1,-s,e,0,0,1,2)):(qt(gt,0,-s,-e,0,0,2,3),qt(gt,1,s,-e,0,0,3,4));for(let u=0;u<2;u++){let f=u*6,d=gt[f],p=gt[f+1];gt[f]=t+r*d-o*p,gt[f+1]=i+o*d+r*p}}function Lo(s,e,t,i,r,o,n,a,l,c,u){let f=Math.cos(t),d=Math.sin(t),p=Math.cos(a),m=Math.sin(a),h=o-s,g=n-e,b=f*h+d*g,y=-d*h+f*g,A=p*h+m*g,v=-m*h+p*g,x=Math.abs(f*p+d*m),w=Math.abs(f*-m+d*p),B=Math.abs(-d*p+f*m),L=Math.abs(-d*-m+f*p),M=Math.abs(b)-i-(x*l+w*c),I=Math.abs(y)-r-(B*l+L*c);if(M>0||I>0)return 0;let E=Math.abs(A)-(x*i+B*r)-l,k=Math.abs(v)-(w*i+L*r)-c;if(E>0||k>0)return 0;let P=0,C=M,_=b>0?f:-f,z=b>0?d:-d;I>.95*C+.01*r&&(P=1,C=I,_=y>0?-d:d,z=y>0?f:-f),E>.95*C+.01*l&&(P=2,C=E,_=A>0?p:-p,z=A>0?m:-m),k>.95*C+.01*c&&(P=3,C=k,_=v>0?-m:m,z=v>0?p:-p);let R,G,X,W,U,H,Z,he,we;if(P===0){R=_,G=z,X=s*R+e*G+i,W=-d,U=f;let $=s*W+e*U;H=-$+r,Z=$+r,he=3,we=1,Po(l,c,o,n,p,m,R,G)}else if(P===1){R=_,G=z,X=s*R+e*G+r,W=f,U=d;let $=s*W+e*U;H=-$+i,Z=$+i,he=2,we=4,Po(l,c,o,n,p,m,R,G)}else if(P===2){R=-_,G=-z,X=o*R+n*G+l,W=-m,U=p;let $=o*W+n*U;H=-$+c,Z=$+c,he=3,we=1,Po(i,r,s,e,f,d,R,G)}else{R=-_,G=-z,X=o*R+n*G+c,W=p,U=m;let $=o*W+n*U;H=-$+l,Z=$+l,he=2,we=4,Po(i,r,s,e,f,d,R,G)}if(Cc(Bc,gt,-W,-U,H,he)<2||Cc(Vi,Bc,W,U,Z,we)<2)return 0;let Oe=0;for(let $=0;$<2;$++){let Je=$*6,Xe=Vi[Je],Be=Vi[Je+1],ot=R*Xe+G*Be-X;if(ot>0)continue;let ri=Xe-R*ot,Ye=Be-G*ot,nt=Vi[Je+2],Qe=Vi[Je+3],at=Vi[Je+4],wt=Vi[Je+5],Qi,Ki,Zi,er;P>=2?([nt,Qe,at,wt]=[at,wt,nt,Qe],Qi=Xe-s,Ki=Be-e,Zi=ri-o,er=Ye-n):(Qi=ri-s,Ki=Ye-e,Zi=Xe-o,er=Be-n);let Wt=Oe*7;u[Wt+0]=nt&255|(Qe&255)<<8|(at&255)<<16|(wt&255)<<24,u[Wt+1]=f*Qi+d*Ki,u[Wt+1+1]=-d*Qi+f*Ki,u[Wt+3]=p*Zi+m*er,u[Wt+3+1]=-m*Zi+p*er,u[Wt+5]=-_,u[Wt+5+1]=-z,Oe++}return Oe}var Sc=s=>31-Math.clz32(s&-s);function Of(s){let e=s+2654435769;return e^=e>>>16,e=Math.imul(e,2146121005),e^=e>>>15,e=Math.imul(e,2221713035),e^=e>>>16,e>>>0}var Ba=(s,e,t,i)=>s>t||s===t&&e>i,_o=class{colors=new Int32Array(0);colorStart=new Int32Array(65);colorBodies=new Int32Array(0);next=new Int32Array(0);pending=new Uint8Array(0);prio=new Uint32Array(0);resize(e){if(this.colors.length===e)return;let t=new Int32Array(e).fill(-1);t.set(this.colors.subarray(0,Math.min(e,this.colors.length))),this.colors=t,this.next=new Int32Array(e),this.pending=new Uint8Array(e),this.colorBodies=new Int32Array(e),this.prio=Uint32Array.from({length:e},(i,r)=>Of(r))}run(e,t,i,r,o,n){this.resize(e);let{colors:a,next:l,pending:c,prio:u}=this,f=(v,x)=>{let w=o[v*4+1],B=w===x?o[v*4+2]:w;return B>=0&&t[B]?B:-1},d=(v,x)=>{let w=0,B=0;for(let L=i[v];L<i[v+1];L++){let M=f(r[L],v);if(M<0||x&&c[M])continue;let I=a[M];I<0||(I<32?w|=1<<I:B|=1<<I-32)}return~w!==0?Sc(~w):~B!==0?32+Sc(~B):63};for(let v=0;v<e;v++){if(l[v]=a[v],!t[v]||a[v]<0)continue;let x=!0;for(let w=i[v];w<i[v+1]&&x;w++){let B=f(r[w],v);B>=0&&!Ba(u[v],v,u[B],B)&&(x=!1)}x&&(l[v]=Math.min(a[v],d(v,!1)))}a.set(l);let p=()=>{let v=0;for(let x=0;x<e;x++)if(c[x]=0,!!t[x]){if(a[x]<0)c[x]=1;else for(let w=i[x];w<i[x+1];w++){let B=f(r[w],x);if(B>=0&&a[B]===a[x]&&Ba(u[B],B,u[x],x)){c[x]=1;break}}v+=c[x]}return v},m=p(),h=0;for(;m>0&&h<n;){h++;for(let v=0;v<e;v++){if(l[v]=-2,!c[v])continue;let x=!0;for(let w=i[v];w<i[v+1]&&x;w++){let B=f(r[w],v);B>=0&&c[B]&&!Ba(u[v],v,u[B],B)&&(x=!1)}x&&(l[v]=d(v,!0))}for(let v=0;v<e;v++)l[v]!==-2&&(a[v]=l[v],c[v]=0,m--)}let g=0;if(m>0){for(let v=0;v<e;v++)c[v]&&a[v]<0&&(a[v]=0);g=p()}let b=this.colorStart;b.fill(0);let y=0;for(let v=0;v<e;v++)t[v]&&(b[a[v]+1]++,y=Math.max(y,a[v]+1));for(let v=0;v<64;v++)b[v+1]+=b[v];let A=b.slice(0,64);for(let v=0;v<e;v++)t[v]&&(this.colorBodies[A[a[v]]++]=v);return{numColors:y,conflicts:g,rounds:h}}};var kc=0,Bi=1,Pc=2,Io=3,hs=4,Lc=[0,3,1,1,2],Sa=.05,j=4,O=32,se=0,te=3,Q=6,_e=9,Ae=12,Pt=15,fe=18,N=21,Y=23,re=25,be=26,Ue=27,Wi=28,zr=class{params=Co();options;bodyCount=0;pose;initial;inertial;velocity;prevVelocity;shape;props;dynamic=new Uint8Array(0);jointCount=0;contactCount=0;info=new Int32Array(0);data;handles=[];prevCount=0;prevKeys=new Float64Array(0);prevInfo=new Int32Array(0);prevData=new Float64Array(0);ignorePairs=[];noCollide=new Float64Array(0);noCollideCount=0;noCollideDirty=!0;adjStart=new Int32Array(1);adjList=new Int32Array(0);broadphase=new ko;coloring=new _o;numColors=0;colorConflicts=0;colorRounds=0;profiling=!1;profile={};phaseStart=0;contactOut=new Float64Array(14);dxBuf=new Float64Array(0);constructor(e={}){this.options={precision:"f32",order:"colored",colorRounds:32,...e},this.pose=this.real(0),this.initial=this.real(0),this.inertial=this.real(0),this.velocity=this.real(0),this.prevVelocity=this.real(0),this.shape=this.real(0),this.props=this.real(0),this.data=this.real(0)}real(e){return this.options.precision==="f32"?new Float32Array(e):new Float64Array(e)}grow(e,t){if(e.length>=t)return e;let i=new e.constructor(Math.max(t,e.length*2,16));return i.set(e),i}clear(){this.bodyCount=0,this.jointCount=0,this.contactCount=0,this.prevCount=0,this.handles=[],this.ignorePairs=[],this.noCollideDirty=!0,this.coloring.resize(0)}addBody(e,t,i,r,o=[0,0,0]){let n=this.bodyCount++,a=this.bodyCount*4;this.pose=this.grow(this.pose,a),this.initial=this.grow(this.initial,a),this.inertial=this.grow(this.inertial,a),this.velocity=this.grow(this.velocity,a),this.prevVelocity=this.grow(this.prevVelocity,a),this.shape=this.grow(this.shape,a),this.props=this.grow(this.props,a),this.dynamic=this.grow(this.dynamic,this.bodyCount);let l=e[0]*e[1]*t;return this.pose.set([r[0],r[1],r[2]??0,0],n*4),this.velocity.set([o[0],o[1],o[2]??0,0],n*4),this.prevVelocity.set(this.velocity.subarray(n*4,n*4+4),n*4),this.shape.set([e[0],e[1],l,l*(e[0]*e[0]+e[1]*e[1])/12],n*4),this.props.set([i,Math.hypot(e[0]*.5,e[1]*.5),0,0],n*4),this.dynamic[n]=l>0?1:0,n}allocConstraint(){let e=this.jointCount++,t=this.jointCount+this.contactCount;this.info=this.grow(this.info,t*j),this.data=this.grow(this.data,t*O),this.contactCount>0&&(this.info.copyWithin((e+1)*j,e*j,(t-1)*j),this.data.copyWithin((e+1)*O,e*O,(t-1)*O)),this.data.fill(0,e*O,e*O+O);for(let i=0;i<3;i++)this.data[e*O+Q+i]=1/0,this.data[e*O+_e+i]=-1/0,this.data[e*O+Ae+i]=1/0,this.data[e*O+Pt+i]=1/0;return this.handles[e]=null,this.noCollideDirty=!0,e}setInfo(e,t,i,r){this.info.set([t,i,r,0],e*j)}addJoint(e,t,i,r,o=[1/0,1/0,1/0],n=1/0){let a=this.allocConstraint();this.setInfo(a,Bi,e,t);let l=this.data,c=a*O;l.set(o,c+Q),l[c+Ae+2]=n,l[c+_e+2]=-n,l[c+Pt+2]=n,l.set(i,c+N),l.set(r,c+Y),l[c+re]=(e>=0?this.pose[e*4+2]:0)-this.pose[t*4+2];let u=(e>=0?this.shape[e*4]:0)+this.shape[t*4],f=(e>=0?this.shape[e*4+1]:0)+this.shape[t*4+1];l[c+be]=u*u+f*f;let d={slot:a,alive:!0};return this.handles[a]=d,d}setJointWorldAnchor(e,t,i){e.alive&&(this.data[e.slot*O+N]=t,this.data[e.slot*O+N+1]=i)}removeJoint(e){e.alive&&(this.disable(e.slot),e.alive=!1)}addSpring(e,t,i,r,o,n){let a=this.allocConstraint();this.setInfo(a,Pc,e,t);let l=a*O;this.data[l+Q]=o,this.data.set(i,l+N),this.data.set(r,l+Y),this.data[l+re]=n}addMotor(e,t,i,r){let o=this.allocConstraint();this.setInfo(o,Io,e,t);let n=o*O;this.data[n+Ae]=r,this.data[n+_e]=-r,this.data[n+re]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(Mr(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(Co());Object.assign(this.params,Object.fromEntries(t.map(r=>[r,e[r]])));let i=new Map(e.bodies.map((r,o)=>[r,o]));for(let r of e.bodies){let o=this.addBody(r.size,0,r.friction,r.position,r.velocity);this.shape[o*4+2]=r.mass,this.shape[o*4+3]=r.moment,this.dynamic[o]=r.mass>0?1:0}for(let r of e.forces){let o=r.bodyA?i.get(r.bodyA):-1,n=i.get(r.bodyB);if(r instanceof xo){let a=this.addJoint(o,n,r.rA,r.rB,[r.stiffness[0],r.stiffness[1],r.stiffness[2]],r.fracture[2]);this.data[a.slot*O+re]=r.restAngle,this.data[a.slot*O+be]=r.torqueArm}else r instanceof Ao?this.addSpring(o,n,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof Bo?this.addMotor(o,n,r.speed,r.fmax[0]):r instanceof wo&&this.addIgnoreCollision(o,n)}}step(){let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha;this.phaseStart=this.profiling?performance.now():0,this.saveContacts(),this.findPairs(),this.mark("broadphase"),this.initJoints(),this.narrowphase(),this.mark("narrowphase"),this.warmStartConstraints(t,i),this.buildAdjacency(),this.mark("adjacency"),this.colorBodies(),this.mark("coloring"),this.warmStartBodies();let r=e.iterations+(t?1:0);for(let o=0;o<r;o++){let n=t?o<e.iterations?1:0:i;this.primal(n),this.mark("primal"),o<e.iterations&&this.dual(n),o===e.iterations-1&&this.updateVelocities(),this.mark("dual")}t&&this.refreshStick()}mark(e){if(!this.profiling)return;let t=performance.now();this.profile[e]=(this.profile[e]??0)+t-this.phaseStart,this.phaseStart=t}resetProfile(){for(let e of Object.keys(this.profile))delete this.profile[e]}saveContacts(){let e=this.contactCount,t=this.jointCount;this.prevKeys.length<e&&(this.prevKeys=new Float64Array(e*2),this.prevInfo=new Int32Array(e*2*j),this.prevData=new Float64Array(e*2*O));for(let i=0;i<e;i++){let r=t+i;this.prevKeys[i]=Mr(this.info[r*j+1],this.info[r*j+2])}this.prevInfo.set(this.info.subarray(t*j,(t+e)*j)),this.prevData.set(this.data.subarray(t*O,(t+e)*O)),this.prevCount=e,this.contactCount=0}findPairs(){if(this.noCollideDirty){let e=[...this.ignorePairs];for(let t=0;t<this.jointCount;t++){let i=this.info[t*j+1],r=this.info[t*j+2];this.info[t*j]!==kc&&i>=0&&e.push(Mr(i,r))}this.noCollide=Float64Array.from(e).sort(),this.noCollideCount=e.length,this.noCollideDirty=!1}this.broadphase.findPairs(this.bodyCount,this.pose,this.props,this.dynamic,this.noCollide,this.noCollideCount)}initJoints(){let{info:e,data:t,pose:i}=this,r=0;for(let o=0;o<this.jointCount;o++){let n=o*O;if(e[o*j]===Bi){if(t[n+Q]===0&&t[n+Q+1]===0&&t[n+Q+2]===0){let u=this.handles[o];u&&(u.alive=!1);continue}let l=e[o*j+1],c=e[o*j+2];this.jointC(l,c,n,i,t,n+fe)}r!==o&&(e.copyWithin(r*j,o*j,(o+1)*j),t.copyWithin(r*O,n,n+O),this.handles[r]=this.handles[o],this.handles[r]&&(this.handles[r].slot=r)),r++}if(r!==this.jointCount){for(let o=r;o<this.jointCount;o++)this.handles[o]=null;this.jointCount=r,this.noCollideDirty=!0}}jointC(e,t,i,r,o,n,a=o){let l,c,u;if(e>=0){let h=Math.cos(r[e*4+2]),g=Math.sin(r[e*4+2]),b=o[i+N],y=o[i+N+1];l=h*b-g*y+r[e*4],c=g*b+h*y+r[e*4+1],u=r[e*4+2]}else l=o[i+N],c=o[i+N+1],u=0;let f=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),p=o[i+Y],m=o[i+Y+1];a[n]=l-(f*p-d*m+r[t*4]),a[n+1]=c-(d*p+f*m+r[t*4+1]),a[n+2]=(u-r[t*4+2]-o[i+re])*o[i+be]}narrowphase(){let{pose:e,shape:t,props:i}=this,r=this.broadphase.pairs,o=this.contactOut;for(let n=0;n<this.broadphase.pairCount;n++){let a=r[n],l=Math.floor(a/2097152),c=a-l*2097152,u=Lo(e[l*4],e[l*4+1],e[l*4+2],t[l*4]*.5,t[l*4+1]*.5,e[c*4],e[c*4+1],e[c*4+2],t[c*4]*.5,t[c*4+1]*.5,o);if(u===0)continue;let f=0,d=this.prevCount;for(;f<d;){let h=f+d>>>1;this.prevKeys[h]<a?f=h+1:d=h}let p=f,m=Math.sqrt(i[l*4]*i[c*4]);for(let h=0;h<u;h++){let g=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(g+1)*j),this.data=this.grow(this.data,(g+1)*O);let b=this.data,y=g*O,A=h*7,v=o[A+0];this.info.set([hs,l,c,v],g*j),b.fill(0,y,y+O),b[y+Q]=b[y+Q+1]=1/0,b[y+_e]=-1/0,b[y+Ae]=0,b[y+Pt]=b[y+Pt+1]=1/0,b[y+N]=o[A+1],b[y+N+1]=o[A+1+1],b[y+Y]=o[A+3],b[y+Y+1]=o[A+3+1],b[y+re]=m,b[y+be]=o[A+5],b[y+Ue]=o[A+5+1];let x=-1;for(let C=p;C<this.prevCount&&this.prevKeys[C]===a;C++)this.prevInfo[C*j+3]===v&&(x=C);if(x<0&&this.params.matchNearest){let C=Sa*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let _=p;_<this.prevCount&&this.prevKeys[_]===a;_++){let z=Math.hypot(this.prevData[_*O+N]-b[y+N],this.prevData[_*O+N+1]-b[y+N+1]);z<=C&&(C=z,x=_)}}if(x>=0){let C=x*O;b[y+se]=this.prevData[C+se],b[y+se+1]=this.prevData[C+se+1],b[y+te]=this.prevData[C+te],b[y+te+1]=this.prevData[C+te+1],this.prevData[C+Wi]&&(b[y+N]=this.prevData[C+N],b[y+N+1]=this.prevData[C+N+1],b[y+Y]=this.prevData[C+Y],b[y+Y+1]=this.prevData[C+Y+1])}let[w,B]=wi(e[l*4+2],b[y+N],b[y+N+1]),[L,M]=wi(e[c*4+2],b[y+Y],b[y+Y+1]),I=b[y+be],E=b[y+Ue],k=e[l*4]+w-e[c*4]-L,P=e[l*4+1]+B-e[c*4+1]-M;b[y+fe]=I*k+E*P+5e-4,b[y+fe+1]=E*k-I*P}}}warmStartConstraints(e,t){let i=this.params,r=this.data,o=this.jointCount+this.contactCount;for(let n=0;n<o;n++){let a=Lc[this.info[n*j]],l=n*O;for(let c=0;c<a;c++){if(i.vbd){r[l+se+c]=_r(r[l+Q+c],i.vbdStiffness);continue}e||(r[l+te+c]=r[l+te+c]*t*i.gamma),r[l+se+c]=_r(Er(r[l+se+c]*i.gamma,1,1e9),r[l+Q+c])}}}prepareTopology(){this.buildAdjacency(),this.colorBodies()}buildAdjacency(){let e=this.bodyCount,t=this.jointCount+this.contactCount;this.adjStart=this.grow(this.adjStart,e+1),this.adjList=this.grow(this.adjList,t*2);let i=this.adjStart;i.fill(0,0,e+1);for(let o=0;o<t;o++)for(let n=1;n<=2;n++){let a=this.info[o*j+n];a>=0&&this.dynamic[a]&&i[a+1]++}for(let o=0;o<e;o++)i[o+1]+=i[o];let r=i.slice(0,e);for(let o=0;o<t;o++)for(let n=1;n<=2;n++){let a=this.info[o*j+n];a>=0&&this.dynamic[a]&&(this.adjList[r[a]++]=o)}}colorBodies(){if(this.options.order!=="colored")return;let e=this.coloring.run(this.bodyCount,this.dynamic,this.adjStart,this.adjList,this.info,this.options.colorRounds);this.numColors=e.numColors,this.colorConflicts=e.conflicts,this.colorRounds=e.rounds}warmStartBodies(){let{dt:e,gravity:t}=this.params,{pose:i,velocity:r,initial:o,inertial:n,prevVelocity:a}=this;for(let l=0;l<this.bodyCount;l++){let c=l*4;r[c+2]=Er(r[c+2],-50,50),n[c]=i[c]+r[c]*e,n[c+1]=i[c+1]+r[c+1]*e,n[c+2]=i[c+2]+r[c+2]*e,this.dynamic[l]&&(n[c+1]+=t*e*e);let u=(r[c+1]-a[c+1])/e*wc(t),f=Er(u/Math.abs(t),0,1);Number.isFinite(f)||(f=0),o[c]=i[c],o[c+1]=i[c+1],o[c+2]=i[c+2],i[c]=i[c]+r[c]*e,i[c+1]=i[c+1]+r[c+1]*e+t*f*e*e,i[c+2]=i[c+2]+r[c+2]*e}}eC=new Float64Array(3);eMin=new Float64Array(3);eMax=new Float64Array(3);eJ=new Float64Array(9);eG=new Float64Array(9);evalConstraint(e,t,i){let{info:r,data:o,pose:n,initial:a}=this,l=r[e*j],c=r[e*j+1],u=r[e*j+2],f=e*O,{eC:d,eMin:p,eMax:m,eJ:h,eG:g}=this,b=Lc[l];for(let A=0;A<b;A++)p[A]=o[f+_e+A],m[A]=o[f+Ae+A];let y=i===c;if(g.fill(0),l===Bi){this.jointC(c,u,f,n,o,0,d);for(let A=0;A<3;A++)o[f+Q+A]===1/0&&(d[A]-=o[f+fe+A]*t);if(i>=0){let A=n[i*4+2],[v,x]=wi(A,o[f+(y?N:Y)],o[f+(y?N:Y)+1]),w=y?1:-1;h[0]=w,h[1]=0,h[2]=-w*x,h[3]=0,h[4]=w,h[5]=w*v,h[6]=0,h[7]=0,h[8]=w*o[f+be],g[2]=Math.abs(v),g[5]=Math.abs(x)}}else if(l===Pc){let[A,v]=_c(n,c,o[f+N],o[f+N+1]),[x,w]=_c(n,u,o[f+Y],o[f+Y+1]),B=A-x,L=v-w,M=B*B+L*L,I=Math.sqrt(M);if(d[0]=I-o[f+re],i>=0&&M!==0){let E=B/I,k=L/I,P=(1-E*E)/I,C=-E*k/I,_=(1-k*k)/I,z=n[i*4+2],R=y?N:Y,[G,X]=wi(z,-o[f+R+1],o[f+R]),[W,U]=wi(z,o[f+R],o[f+R+1]),H=P*G+C*X,Z=C*G+_*X,he=E*W+k*U,we=E*G+k*X,Oe=y?1:-1;h[0]=Oe*E,h[1]=Oe*k,h[2]=Oe*we;let $=y?-he-he:he+he;g[0]=Math.hypot(P,C,H),g[1]=Math.hypot(C,_,Z),g[2]=Math.hypot(H,Z,$)}else i>=0&&h.fill(0,0,3)}else if(l===Io){let A=c>=0?n[c*4+2]-a[c*4+2]:0,v=n[u*4+2]-a[u*4+2];d[0]=A-v-o[f+re]*this.params.dt,i>=0&&(h[0]=0,h[1]=0,h[2]=y?1:-1)}else if(l===hs){let A=o[f+be],v=o[f+Ue],x=v,w=-A,[B,L]=wi(a[c*4+2],o[f+N],o[f+N+1]),[M,I]=wi(a[u*4+2],o[f+Y],o[f+Y+1]),E=B*v-L*A,k=-(M*v-I*A),P=B*w-L*x,C=-(M*w-I*x),_=n[c*4]-a[c*4],z=n[c*4+1]-a[c*4+1],R=n[c*4+2]-a[c*4+2],G=n[u*4]-a[u*4],X=n[u*4+1]-a[u*4+1],W=n[u*4+2]-a[u*4+2];d[0]=o[f+fe]*(1-t)+A*_+v*z+E*R+-A*G+-v*X+k*W,d[1]=o[f+fe+1]*(1-t)+x*_+w*z+P*R+-x*G+-w*X+C*W;let U=Math.abs(o[f+te])*o[f+re];m[1]=U,p[1]=-U,i>=0&&(y?(h[0]=A,h[1]=v,h[2]=E,h[3]=x,h[4]=w,h[5]=P):(h[0]=-A,h[1]=-v,h[2]=k,h[3]=-x,h[4]=-w,h[5]=C))}return b}solveBody(e,t){let i=this.params,{shape:r,pose:o,inertial:n,data:a}=this,l=i.dt*i.dt,c=r[e*4+2]/l,u=r[e*4+3]/l,f=c,d=0,p=c,m=0,h=0,g=u,b=c*(o[e*4]-n[e*4]),y=c*(o[e*4+1]-n[e*4+1]),A=u*(o[e*4+2]-n[e*4+2]),{eC:v,eMin:x,eMax:w,eJ:B,eG:L}=this,M=this.adjStart,I=this.adjList;for(let H=M[e+1]-1;H>=M[e];H--){let Z=I[H],he=this.evalConstraint(Z,t,e),we=Z*O,Oe=!i.vbd;for(let $=0;$<he;$++){let Je=a[we+Q+$],Xe=Oe&&Je===1/0?a[we+te+$]:0,Be=a[we+se+$],ot=v[$],ri=Be*ot+Xe,Ye=Er(ri,x[$],w[$]),nt=Math.abs(Ye);i.stiffnessRescale&&ot!==0&&(ri<x[$]?Be=Math.abs((x[$]-Xe)/ot):ri>w[$]&&(Be=Math.abs((w[$]-Xe)/ot)));let Qe=B[$*3],at=B[$*3+1],wt=B[$*3+2];b+=Qe*Ye,y+=at*Ye,A+=wt*Ye,f+=Qe*Qe*Be+L[$*3]*nt,d+=at*Qe*Be,p+=at*at*Be+L[$*3+1]*nt,m+=wt*Qe*Be,h+=wt*at*Be,g+=wt*wt*Be+L[$*3+2]*nt}}let E=f,k=d/f,P=m/f,C=p-k*k*E,_=(h-k*P*E)/C,z=g-(P*P*E+_*_*C),R=y-k*b,X=(A-P*b-_*R)/z,W=R/C-_*X,U=b/E-k*W-P*X;this.dxBuf[e*3]=U,this.dxBuf[e*3+1]=W,this.dxBuf[e*3+2]=X}applyDx(e){this.pose[e*4]-=this.dxBuf[e*3],this.pose[e*4+1]-=this.dxBuf[e*3+1],this.pose[e*4+2]-=this.dxBuf[e*3+2]}primal(e){if(this.dxBuf.length<this.bodyCount*3&&(this.dxBuf=new Float64Array(this.bodyCount*3)),this.options.order==="sequential"){for(let r=this.bodyCount-1;r>=0;r--)this.dynamic[r]&&(this.solveBody(r,e),this.applyDx(r));return}let{colorStart:t,colorBodies:i}=this.coloring;for(let r=0;r<this.numColors;r++){for(let o=t[r];o<t[r+1];o++)this.solveBody(i[o],e);for(let o=t[r];o<t[r+1];o++)this.applyDx(i[o])}}dual(e){let t=this.params,i=this.data,{eC:r,eMin:o,eMax:n}=this,a=this.jointCount+this.contactCount;for(let l=0;l<a;l++){let c=this.info[l*j];if(c===kc)continue;let u=this.evalConstraint(l,e,-1),f=l*O;c===hs&&(i[f+Wi]=Math.abs(i[f+te+1])<n[1]&&Math.abs(i[f+fe+1])<.01?1:0);for(let d=0;d<u;d++){let p=i[f+Q+d],m=!t.vbd&&p===1/0?i[f+te+d]:0,h=Er(i[f+se+d]*r[d]+m,o[d],n[d]);i[f+te+d]=h,Math.abs(h)>=i[f+Pt+d]&&this.disable(l),!t.vbd&&h>o[d]&&h<n[d]&&(i[f+se+d]=_r(i[f+se+d]+t.beta*Math.abs(r[d]),_r(1e9,i[f+Q+d])))}}}refreshStick(){let e=this.data;for(let t=0;t<this.contactCount;t++){let i=(this.jointCount+t)*O,r=Math.abs(e[i+te])*e[i+re];e[i+Wi]=Math.abs(e[i+te+1])<r&&Math.abs(e[i+fe+1])<.01?1:0}}disable(e){let t=e*O;this.data.fill(0,t+se,t+se+3),this.data.fill(0,t+te,t+te+3),this.data.fill(0,t+Q,t+Q+3)}updateVelocities(){let{dt:e}=this.params,{pose:t,initial:i,velocity:r,prevVelocity:o}=this;for(let n=0;n<this.bodyCount;n++){let a=n*4;o[a]=r[a],o[a+1]=r[a+1],o[a+2]=r[a+2],this.dynamic[n]&&(r[a]=(t[a]-i[a])/e,r[a+1]=(t[a+1]-i[a+1])/e,r[a+2]=(t[a+2]-i[a+2])/e)}}pick(e,t){for(let i=this.bodyCount-1;i>=0;i--){let[r,o]=wi(-this.pose[i*4+2],e-this.pose[i*4],t-this.pose[i*4+1]);if(Math.abs(r)<=this.shape[i*4]*.5&&Math.abs(o)<=this.shape[i*4+1]*.5)return{body:i,local:[r,o]}}return null}kineticEnergy(){let e=0;for(let t=0;t<this.bodyCount;t++){if(!this.dynamic[t])continue;let i=t*4,r=this.velocity;e+=.5*this.shape[i+2]*(r[i]*r[i]+r[i+1]*r[i+1])+.5*this.shape[i+3]*r[i+2]*r[i+2]}return e}maxJointError(){let e=0,t=new Float64Array(3);for(let i=0;i<this.jointCount;i++){if(this.info[i*j]!==Bi)continue;let r=i*O;this.jointC(this.info[i*j+1],this.info[i*j+2],r,this.pose,this.data,0,t);for(let o=0;o<2;o++)this.data[r+Q+o]===1/0&&(e=Math.max(e,Math.abs(t[o])))}return e}isContactPairIgnored(e,t){return wa(this.noCollide,this.noCollideCount,Mr(e,t))}};function wi(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function _c(s,e,t,i){let r=Math.cos(s[e*4+2]),o=Math.sin(s[e*4+2]);return[r*t-o*i+s[e*4],o*t+r*i+s[e*4+1]]}var ps=`
|
|
3905
|
+
`),xi=class{device;pipelines;layout;watch;table;events;params;setSlots;capacity;group=null;bound=[];flip=0;bodies;constructor(e,t,i,r,o,a=3){this.device=e,this.bodies=t,this.capacity=o,this.setSlots=2**Math.ceil(Math.log2(Math.max(64,2*r)));let n=e.createShaderModule({label:"contact events",code:a===2?Rf:ns}),l=["read-only-storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform"];this.layout=e.createBindGroupLayout({label:"contact events",entries:l.map((d,p)=>({binding:p,visibility:GPUShaderStage.COMPUTE,buffer:{type:d}}))});let c=e.createPipelineLayout({bindGroupLayouts:[this.layout]}),u=d=>e.createComputePipeline({label:`contact events ${d}`,layout:c,compute:{module:n,entryPoint:d}});this.pipelines={touching:u("touching"),ended:u("ended")};let f=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.watch=e.createBuffer({label:"contact watch",size:Math.max(16,i*4),usage:f}),this.table=e.createBuffer({label:"contact sets",size:(xn+4*this.setSlots)*4,usage:f}),this.events=e.createBuffer({label:"contact events",size:o*kr*16,usage:f}),this.params=e.createBuffer({label:"contact params",size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST})}setWatched(e,t){let i=e.map((r,o)=>[r,t[o]?1:0]).sort((r,o)=>r[0]-o[0]);for(let r=0;r<i.length;){let o=r+1;for(;o<i.length&&i[o][0]===i[o-1][0]+1;)o++;this.device.queue.writeBuffer(this.watch,i[r][0]*4,Uint32Array.from(i.slice(r,o),a=>a[1])),r=o}}run(e,t,i,r){let o=this.device,a=[this.bodies,e.manifolds,e.contacts,e.counters,this.watch,this.table,this.events,this.params];(!this.group||a.some((d,p)=>d!==this.bound[p]))&&(this.bound=a,this.group=o.createBindGroup({layout:this.layout,entries:a.map((d,p)=>({binding:p,resource:{buffer:d}}))})),this.flip=1-this.flip;let[n,l]=this.flip?[0,this.setSlots]:[this.setSlots,0],c=new Uint32Array([n,l,this.setSlots-1,this.capacity,t,i,0,0]);new Float32Array(c.buffer)[6]=r,o.queue.writeBuffer(this.params,0,c);let u=o.createCommandEncoder({label:"contact events"});u.clearBuffer(this.table,(xn+2*l)*4,2*this.setSlots*4);let f=u.beginComputePass({label:"contact events"});f.setBindGroup(0,this.group),f.setPipeline(this.pipelines.touching),f.dispatchWorkgroups(Math.max(1,Math.ceil(t/64))),f.setPipeline(this.pipelines.ended),f.dispatchWorkgroups(Math.ceil(this.setSlots/64)),f.end(),o.queue.submit([u.finish()])}async read(){let e=this.device,t=16+this.capacity*kr*16,i=e.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=e.createCommandEncoder({label:"read contact events"});r.copyBufferToBuffer(this.table,0,i,0,16),r.copyBufferToBuffer(this.events,0,i,16,t-16),r.clearBuffer(this.table,0,16),e.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);i.unmap(),i.destroy();let a=new Uint32Array(o),n=new Float32Array(o),l=Math.min(a[0],this.capacity),c=[];for(let u=0;u<l;u++){let f=4+u*kr*4;c.push({kind:a[f],a:a[f+1],b:a[f+2],step:a[f+3],point:[n[f+4],n[f+5],n[f+6]],impulse:n[f+7],normal:[n[f+8],n[f+9],n[f+10]]})}return c.sort((u,f)=>u.step-f.step),{events:c,dropped:a[0]-l,full:a[1]!==0}}destroy(){for(let e of[this.watch,this.table,this.events,this.params])e.destroy()}};var Ac=[0,1,2,3,5,4,0,3,4,0,4,1,1,4,5,1,5,2,2,5,3,2,3,0],Pr=Ac.slice();for(let s=0;s<Pr.length;s+=3)[Pr[s+1],Pr[s+2]]=[Pr[s+2],Pr[s+1]];var mo=(s,e,t)=>{let i=Array.from(s??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite values`);return i},wc=(s,e)=>{if(!Number.isFinite(s)||s<=0)throw Error(`${e} must be positive and finite`);return s};function Bc(s){let e=[0,1,2].map(u=>s.reduce((f,d)=>f+d[u],0)/s.length),[t,i,r,o]=s,a=i.map((u,f)=>u-t[f]),n=r.map((u,f)=>u-t[f]),l=(a[1]*n[2]-a[2]*n[1])*(t[0]-o[0])+(a[2]*n[0]-a[0]*n[2])*(t[1]-o[1])+(a[0]*n[1]-a[1]*n[0])*(t[2]-o[2]),c=Ma(s.flatMap(u=>u.map((f,d)=>f-e[d])),l>0?Ac:Pr);if(!c)throw Error("Terrain contains a degenerate collision piece");return{shape:c,position:c.center.map((u,f)=>u+e[f]),rotation:c.rotation}}function ls({heights:s,columns:e,rows:t,spacing:i=1,origin:r=[0,0,0],upAxis:o=1,baseHeight:a,holes:n}={}){for(let[g,b]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(b)||b<2)throw Error(`${g} must be an integer >= 2`);let l=mo(s,e*t,"heights"),c=mo(r,3,"origin");if(![0,1,2].includes(o))throw Error("upAxis must be 0, 1 or 2");let u=typeof i=="number"?[i,i]:mo(i,2,"spacing");u.forEach(g=>wc(g,"spacing"));let f=a??l.reduce((g,b)=>Math.min(g,b),1/0)-2;if(!Number.isFinite(f)||l.some(g=>g<=f))throw Error("baseHeight must be below every terrain height");let d=(e-1)*(t-1);if(n&&n.length!==d)throw Error("holes requires one value per terrain cell");let p=[0,1,2].filter(g=>g!==o),m=[],h=(g,b)=>{let y=[...c];return y[p[0]]+=g*u[0],y[p[1]]+=b*u[1],y[o]+=l[b*e+g],y};for(let g=0;g<t-1;g++)for(let b=0;b<e-1;b++){if(n?.[g*(e-1)+b])continue;let y=h(b,g),A=h(b+1,g),v=h(b,g+1),x=h(b+1,g+1);for(let w of[[y,A,v],[A,x,v]])m.push(Bc([...w,...w.map(B=>{let L=[...B];return L[o]=c[o]+f,L})]))}return{kind:"heightfield",pieces:m,triangleCount:m.length}}function cs({positions:s,indices:e,thickness:t=.1}={}){if(!s||s.length%3)throw Error("positions requires xyz triples");let i=mo(s,s.length,"positions"),r=i.length/3;if(e===void 0&&r%3)throw Error("Nonindexed positions require three vertices per triangle");let o=e===void 0?Array.from({length:r},(n,l)=>l):Array.from(e);if(!o.length||o.length%3||!o.every(n=>Number.isInteger(n)&&n>=0&&n<r))throw Error("indices requires valid triangle vertex indices");wc(t,"thickness");let a=[];for(let n=0;n<o.length;n+=3){let l=o.slice(n,n+3).map(g=>i.slice(g*3,g*3+3)),[c,u,f]=l,d=u.map((g,b)=>g-c[b]),p=f.map((g,b)=>g-c[b]),m=[d[1]*p[2]-d[2]*p[1],d[2]*p[0]-d[0]*p[2],d[0]*p[1]-d[1]*p[0]],h=Math.hypot(...m);h!==0&&a.push(Bc([...l,...l.map(g=>g.map((b,y)=>b-m[y]*t/h))]))}if(!a.length)throw Error("Static mesh requires at least one nondegenerate triangle");return{kind:"static-mesh",pieces:a,triangleCount:a.length}}function go(s,e,t={}){if(t.mass!==void 0&&t.mass!==0)throw Error("Terrain is static and requires zero mass");let i=t.friction??.6,r=t.restitution??0;if(!Number.isFinite(i)||i<0)throw Error("friction must be nonnegative and finite");if(!Number.isFinite(r)||r<0||r>1)throw Error("restitution must be between 0 and 1");for(let o of["group","collidesWith"]){let a=t[o]??4294967295;if(!Number.isInteger(a)||a<0||a>4294967295)throw Error(`${o} must be an unsigned 32-bit integer`)}if(!e?.pieces?.length)throw Error("Terrain requires collision pieces");return e.pieces.map(o=>{let a=nr(s,o.shape,0,i,o.position,o.rotation);return Object.assign(a,{allowSleep:!1,isTrigger:!1,restitution:r,group:t.group??4294967295,collidesWith:t.collidesWith??4294967295}),a})}var mt=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),Le=s=>s&&typeof s.x=="number"?[s.x,s.y,s.z,...typeof s.w=="number"?[s.w]:[]]:Array.from(s),us=new WeakMap,Lr=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s},bo=(s,e,t=!1)=>{if(!Number.isFinite(s)||(t?s<0:s<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return s},yo=class s{static async create({device:e,...t}={}){if(t.scene&&us.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await ss({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return await Cr(e),r=new s(e,t),r.ownsDevice=i,r.scene&&r.autoAttach&&r.attachToScene(),r}catch(o){throw r?r.dispose():i&&e.destroy(),o}}constructor(e,{scene:t,gravity:i=[0,-10,0],timeStep:r=1/60,iterations:o=10,capacity:a=4096,syncMeshes:n=!0,maxSyncedBodies:l=2048,broadphase:c="auto",solverMode:u="auto",smallGroups:f,bvh:d={},autoAttach:p=!0,maxContactPairs:m=8192,maxContactEvents:h=4096,sleeping:g=!1}={}){if(this.device=e,this.scene=t,this.autoAttach=p,this.syncEnabled=n,this.maxSyncedBodies=l,!Number.isInteger(a)||a<1||!Number.isInteger(l)||l<0)throw Error("capacity and maxSyncedBodies must be valid integer counts");if(this.capacity=a,this.collisionOptions={broadphase:c,bvh:d,solverMode:u,smallGroups:f},this.ref=new ni,this.ref.dt=bo(r,"timeStep"),!Number.isInteger(o)||o<1)throw Error("iterations must be a positive integer");this.ref.iterations=o,this.aggregates=[],this.constraints=new Set,this.contactListeners=new Set;for(let[y,A]of Object.entries({maxContactPairs:m,maxContactEvents:h}))if(!Number.isInteger(A)||A<1)throw Error(`${y} must be a positive integer`);if(this.contactOptions={maxContactPairs:m,maxContactEvents:h},this.bodyReadback=new vi(e),this.sleepOptions=g,g!==!1&&g!==!0&&(typeof g!="object"||g===null))throw Error("sleeping must be a boolean or options object");this.commands=new Map,this.steps=0,this.errors=[],this.deviceLost=null;let b=new WeakRef(this);e.lost.then(y=>{let A=b.deref();!A||A.disposed||(A.deviceLost={reason:y.reason,message:y.message},A.errors.push(`WebGPU device lost: ${y.message||y.reason}`),A.dispose())}),this.setGravity(i),this.errorListener=y=>this.errors.push(y.error.message),e.addEventListener("uncapturederror",this.errorListener),t&&us.set(t,this)}setGravity(e){let t=Le(e);if(t.length!==3||!t.every(Number.isFinite))throw Error("gravity requires three finite components");let i=Math.hypot(...t);this.gravity=-i,this.up=i?t.map(r=>-r/i):[0,1,0],this.gpu&&(this.gpu.params.gravity=this.gravity,this.gpu.params.up=this.up),this.wakeAll()}get solverDecision(){return this.initialize(),{...this.gpu.solverDecision}}setSolverMode(e){this.initialize();let t=this.gpu.setSolverMode(e);return this.collisionOptions.solverMode=e,{...t}}initialize(){if(this.assertAlive(),this.gpu)return;if(this.syncEnabled&&this.aggregates.filter(r=>r.sync).length>this.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(this.aggregates.length>this.capacity)throw Error(`Body capacity ${this.capacity} exceeded; size the world before initialization`);this.gpu=new gi(this.device,this.ref,ho(this.ref,{bodyCapacity:Math.max(1,this.capacity),spatialSort:!0,...this.collisionOptions})),Object.assign(this.gpu.params,{gravity:this.gravity,up:this.up}),this.gpu.externalBeforeStep=r=>{this.sleepManager?.before(r),this.flushCommands(r)},this.gpu.externalAfterStep=r=>this.sleepManager?.after(r);let e=new Map([...this.constraints].map(r=>[r.force,r])),t=[],i=0;for(let r of this.ref.forces){let o=e.get(r);o&&(o.attach(i++,!1),(o.type==="fixed"||o.type==="spring"&&o.rest===void 0)&&t.push({slot:o.slot,spring:o.type==="spring"}))}this.gpu.captureConstraintFrames(t),this.byGpuIndex=new Map(this.aggregates.map(r=>[this.gpu.gpuIndex(r.index),r.body]));for(let r of this.aggregates)(r.disposed||r.group!==4294967295||r.collidesWith!==4294967295)&&this.gpu.setFilters([this.gpu.gpuIndex(r.index)],[r.disposed?0:r.group],[r.disposed?0:r.collidesWith]);for(let r of this.aggregates)r.isTrigger&&this.gpu.setSensor(r.body.gpuIndex,!0);for(let r of this.aggregates)r.restitution>0&&this.gpu.setRestitution(r.body.gpuIndex,r.restitution);this.contactListeners.size&&this.initializeContacts(),this.sleepOptions&&(this.sleepManager=new Fi(this,this.sleepOptions===!0?{}:this.sleepOptions))}addAggregate(e,t,i={}){return new vo(e,t,i,this)}addAggregates(e){return e.map(({mesh:t,type:i,options:r})=>this.addAggregate(t,i,r))}addHeightfield(e){return this.addTerrain(ls(e),e)}addStaticMesh(e,t={}){e.computeWorldMatrix(!0);let i=e.getVerticesData("position"),r=e.getWorldMatrix().m;if(!i)throw Error("Static mesh requires vertex positions");let o=new Float64Array(i.length);for(let u=0;u<i.length;u+=3){let f=i[u],d=i[u+1],p=i[u+2];for(let m=0;m<3;m++)o[u+m]=r[m]*f+r[4+m]*d+r[8+m]*p+r[12+m]}let a=e.getIndices(),n=a?.length?Uint32Array.from(a):Uint32Array.from({length:i.length/3},(u,f)=>f),l=e.getVerticesData("normal"),c=e.getWorldMatrix().determinant()<0;for(let u=0;u<n.length;u+=3){let f=n[u]*3,d=n[u+1]*3,p=n[u+2]*3,m=[0,1,2].map(b=>i[d+b]-i[f+b]),h=[0,1,2].map(b=>i[p+b]-i[f+b]),g=!1;l?.length===i.length&&(g=[m[1]*h[2]-m[2]*h[1],m[2]*h[0]-m[0]*h[2],m[0]*h[1]-m[1]*h[0]].reduce((y,A,v)=>y+A*(l[f+v]+l[d+v]+l[p+v]),0)<0),g!==c&&([n[u+1],n[u+2]]=[n[u+2],n[u+1]])}return this.addTerrain(cs({positions:o,indices:n,thickness:t.thickness}),t)}addTerrain(e,t={}){if(this.assertAlive(),this.gpu)throw Error("Add static terrain before initializing the physics world");if(this.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Terrain collisions require nine storage buffer bindings");if(this.aggregates.length+(e?.pieces?.length??0)>this.capacity)throw Error("AVBD terrain exceeds body capacity; reserve space for its collision pieces");let r=go(this.ref,e,t).map(a=>new An(this,a)),o=!1;return{bodies:r.map(a=>a.body),triangleCount:e.triangleCount,kind:e.kind,dispose:()=>{if(o)return;o=!0;let a=new Set(r.map(n=>n.body));for(let n of[...this.constraints])(a.has(n.bodyA)||a.has(n.bodyB))&&n.dispose();if(this.gpu&&!this.disposed){let n=r.map(c=>c.body.gpuIndex),l=new Uint32Array(n.length);this.gpu.setFilters(n,l,l)}for(let n of r)n.disposed=n.body.disposed=!0,n.group=n.collidesWith=0,this.commands.delete(n.body);this.wakeAll()}}}addConstraint(e,t,i={}){return new os(this,e,t,i)}addJoint(e,t,i={}){return this.addConstraint(e,t,{...i,type:i.type??"ball"})}addWeld(e,t,i={}){return this.addConstraint(e,t,{...i,type:"fixed"})}addSpring(e,t,i={}){return this.addConstraint(e,t,{...i,type:"spring"})}enableSleeping(e={}){return this.assertAlive(),this.sleepOptions=e,this.gpu&&!this.sleepManager&&(this.sleepManager=new Fi(this,e)),this}disableSleeping(){return this.assertAlive(),this.sleepOptions=!1,this.gpu?(this.flushCommands(),this.sleepManager&&(this.sleepManager.wakeRequested=!0,this.sleepManager.before(),this.sleepManager.dispose(),this.sleepManager=null),this):this}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new as(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new xi(this.device,this.gpu.bodyBuffer,this.capacity,e,t);let i=this.aggregates.filter(r=>!r.disposed);this.contactWatch.setWatched(i.map(r=>r.body.gpuIndex),i.map(()=>!0))}onContact(e){if(this.assertAlive(),typeof e!="function")throw Error("A contact callback is required");return this.contactListeners.add(e),this.gpu&&this.initializeContacts(),()=>this.contactListeners.delete(e)}async readContactEvents(){return this.initialize(),this.initializeContacts(),this.contactPending?this.contactPending:(this.contactPending=this.contactWatch.read().then(e=>({...e,events:e.events.map(t=>({...t,type:t.kind===Ai?"begin":"end",isTrigger:!!(this.byGpuIndex.get(t.a)?.aggregate.isTrigger||this.byGpuIndex.get(t.b)?.aggregate.isTrigger),a:this.byGpuIndex.get(t.a),b:this.byGpuIndex.get(t.b)}))})).finally(()=>this.contactPending=null),this.contactPending)}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}async sphereCast(e,t,i,r={}){let[o]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}async sphereCastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){this.assertAlive(),Lr(i),this.initialize(),this.flushCommands();for(let a of t)if(a.aggregate.world!==this||a.disposed)throw Error("Ignored cast bodies must belong to this world and be alive");return(await this.gpu.sphereCastAll(e.map(a=>({...a,origin:Le(a.origin),direction:Le(a.direction)})),{ignore:t.map(a=>a.gpuIndex),collidesWith:i,includeTriggers:r})).map(a=>a?{...a,body:this.byGpuIndex.get(a.index)??null}:null)}async raycastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){Lr(i),this.initialize(),this.flushCommands();let o=e.map(n=>{let l=Le(n.origin),c=Le(n.direction);if(l.length!==3||c.length!==3||![...l,...c].every(Number.isFinite)||!Math.hypot(...c))throw Error("A ray requires a finite origin and nonzero direction");return n.maxDistance!==void 0&&bo(n.maxDistance,"maxDistance",!0),{...n,origin:l,direction:c}});for(let n of t)if(n.aggregate.world!==this)throw Error("Ignored ray bodies must belong to this world");return(await this.gpu.raycastAll(o,{ignore:t.map(n=>n.gpuIndex),collidesWith:i,includeTriggers:r})).map(n=>n?{...n,body:this.byGpuIndex.get(n.index)??null}:null)}getRenderBinding(e=this.device){if(this.initialize(),e!==this.device)throw Error("Direct GPU rendering must share the physics GPUDevice");return{device:e,buffer:this.bodyBuffer,stride:160,positionOffset:0,rotationOffset:16,sizeOffset:64,count:this.gpu.bodyCount}}wakeAll(){this.sleepManager&&(this.sleepManager.wakeRequested=!0)}async readSleepStats(){return this.initialize(),this.sleepManager?this.sleepManager.readStats():{sleeping:0,wakeRequested:!1}}assertAlive(){if(this.disposed)throw Error("AVBD world is disposed")}queue(e,t,i,r){this.assertAlive(),e.assertAlive();let o=Le(r);if(o.length!==(i==="rotation"?4:3)||!o.every(Number.isFinite))throw Error(`${i} requires ${i==="rotation"?4:3} finite components`);let a=this.commands.get(e);return a||this.commands.set(e,a={mask:0}),a.mask|=t,a[i]=o,this.wakeAll(),a}flushCommands(e){if(this.initialize(),this.gpu.flushPropertyEdits(e),!this.commands.size)return;this.sleepManager?.wakeRequested&&this.sleepManager.before(e);let t=this.commands.size,i=0;for(let h of this.commands.values())i|=h.mask;if(i===1||i===2){if(!this.velocityIndices||this.velocityIndices.length<t){let y=Math.max(t,(this.velocityIndices?.length??0)*2,64);this.velocityIndices=new Uint32Array(y),this.velocityValues=new Float32Array(y*3)}let h=0;for(let[y,A]of this.commands)this.velocityIndices[h]=y.aggregate.gpuSlot??this.gpu.gpuIndex(y.aggregate.index),this.velocityValues.set(i===2?A.angular:A.velocity,h++*3);let g=i===2?"angularVelocityBatch":"velocityBatch",b=this[g]??=new Kt(this.gpu,3,i===2?"angular":"linear");b.solver=this.gpu,b.submitValidated(this.velocityIndices.subarray(0,t),this.velocityValues.subarray(0,t*3),e),this.lastCommandBatch={bodies:t,path:i===2?"packed-angular-velocities":"packed-velocities",uploadedBytes:t*16},this.commands.clear();return}let r=!0;for(let h of this.commands.values())if((h.mask&-4)!==0){r=!1;break}if(r){(!this.motionData||this.motionData.length<t*8)&&(this.motionData=new Uint32Array(Math.max(t*8,(this.motionData?.length??0)*2,512)),this.motionFloats=new Float32Array(this.motionData.buffer));let h=0;for(let[g,b]of this.commands){let y=h++*8;this.motionData[y]=g.aggregate.gpuSlot??this.gpu.gpuIndex(g.aggregate.index),this.motionData[y+1]=b.mask,b.velocity&&this.motionFloats.set(b.velocity,y+2),b.angular&&this.motionFloats.set(b.angular,y+5)}this.motionBatch??=new yi(this.gpu,3),this.motionBatch.submitValidated(this.motionData.subarray(0,t*8),e),this.lastCommandBatch={bodies:t,path:"packed-motion",uploadedBytes:t*32},this.commands.clear();return}let o=t*128,a=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize);if(o>a)throw Error("Body command batch exceeds GPU buffer limits");(!this.commandData||this.commandData.byteLength<o)&&(this.commandData=new ArrayBuffer(Math.min(a,Math.max(o,(this.commandData?.byteLength??0)*2,128))));let n=this.commandData,l=new Float32Array(n),c=new Uint32Array(n);l.fill(0,0,t*32);let u=0;for(let[h,g]of this.commands){let b=u++*32;c[b]=h.aggregate.gpuSlot??this.gpu.gpuIndex(h.aggregate.index),c[b+1]=g.mask;for(let[y,A]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])g[y]&&l.set(g[y],b+A)}this.commands.clear(),(!this.commandBuffer||this.commandBuffer.size<o)&&(this.commandBuffer?.destroy(),this.commandBuffer=this.device.createBuffer({size:Math.min(a,Math.max(o*2,128)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),this.device.queue.writeBuffer(this.commandBuffer,0,n,0,o),this.commandPipeline??=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3906
|
+
${vc}`,label:"AVBD body commands"}),entryPoint:"applyCommands"}}),(!this.commandBinding||this.boundCommandBody!==this.gpu.bodyBuffer||this.boundCommandBuffer!==this.commandBuffer||this.boundCommandBytes!==o)&&(this.commandBinding=this.device.createBindGroup({layout:this.commandPipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.gpu.bodyBuffer}},{binding:1,resource:{buffer:this.commandBuffer,size:o}}]}),this.boundCommandBody=this.gpu.bodyBuffer,this.boundCommandBuffer=this.commandBuffer,this.boundCommandBytes=o);let f=e??this.device.createCommandEncoder(),d=f.beginComputePass();d.setPipeline(this.commandPipeline),d.setBindGroup(0,this.commandBinding);let p=Math.ceil(u/64),m=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(p,m),Math.ceil(p/m)),d.end(),e||this.device.queue.submit([f.finish()]),this.lastCommandBatch={bodies:t,path:"aggregate-commands",uploadedBytes:o}}step(){this.initialize(),this.gpu.step(),this.steps++,this.contactWatch&&(this.contactWatch.run(this.gpu.contactStorage,this.gpu.manifoldCapacity,this.steps,this.ref.dt),this.contactListeners.size&&!this.contactPending&&this.readContactEvents().then(({events:e,dropped:t,full:i})=>{if(!this.disposed){(t||i)&&this.errors.push(`Contact event storage full: ${t} events dropped; increase maxContactEvents/maxContactPairs`);for(let r of e)for(let o of this.contactListeners)o(r)}}).catch(e=>{this.disposed||this.errors.push(e.message)})),this.syncEnabled&&!this.syncPending&&this.syncMeshes().catch(e=>this.errors.push(e.message)),this.steps%20===0&&!this.counterPending&&(this.counterPending=!0,this.gpu.readCounters().then(e=>{this.disposed||(this.counters=e,this.gpu.adapt(e),(e.overflow||e.clashes)&&this.errors.push(`Collision capacity/color conflict: ${JSON.stringify(e)}`))}).catch(e=>{this.disposed||this.errors.push(e.message)}).finally(()=>this.counterPending=!1))}async syncMeshes(){if(this.initialize(),this.flushCommands(),this.syncPending)return this.syncPending;let e=this.aggregates.filter(t=>t.sync&&!t.disposed);return this.syncPending=this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.body.gpuIndex),!0).then(t=>{if(!this.disposed){for(let i=0;i<e.length;i++)e[i].disposed||e[i].syncPose(t,i*8);this.lastSyncBytes=t.byteLength}}).finally(()=>this.syncPending=null),this.syncPending}get bodyBuffer(){return this.initialize(),this.gpu.bodyBuffer}async readBodies(e){if(this.initialize(),this.flushCommands(),e){for(let t of e)if(t.aggregate.world!==this)throw Error("Selected bodies must belong to this world");return this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.gpuIndex))}return this.gpu.readBodies()}dispose(){if(!this.disposed){this.disposed=!0,this.observer&&this.scene?.onBeforeRenderObservable.remove(this.observer),this.disposeObserver&&this.scene?.onDisposeObservable.remove(this.disposeObserver),this.scene&&us.get(this.scene)===this&&us.delete(this.scene),this.device.removeEventListener("uncapturederror",this.errorListener),this.gpu?.destroy(),this.contactWatch?.destroy(),this.sleepManager?.dispose(),this.commandBuffer?.destroy(),this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback.destroy(),this.commandData=this.velocityIndices=this.velocityValues=null,this.commandBinding=null;for(let e of this.aggregates)e.disposed=!0,e.body.disposed=!0;this.commands.clear(),this.contactListeners.clear();for(let e of this.constraints)e.disposed=!0;this.constraints.clear(),this.ownsDevice&&this.device.destroy()}}attachToScene(e=this.scene){if(this.assertAlive(),!e)throw Error("A Babylon scene is required");if(this.observer&&e===this.scene)return this;if(this.observer)throw Error("AVBD world is already attached to another scene");this.scene=e,this.disposeObserver=e.onDisposeObservable.addOnce(()=>this.dispose());let t=0;return this.observer=e.onBeforeRenderObservable.add(()=>{t+=Math.min(.05,e.getEngine().getDeltaTime()/1e3);for(let i=0;t>=this.ref.dt&&i<3;i++)this.step(),t-=this.ref.dt}),this}},vo=class{constructor(e,t,{mass:i=1,friction:r=.6,restitution:o=0,allowSleep:a,isTrigger:n=!1,sync:l=!0,group:c=4294967295,collidesWith:u=4294967295}={},f){if(f=f instanceof yo?f:us.get(f??e.getScene()),!f)throw Error("Create an AVBD world for this scene before adding aggregates");if(f.assertAlive(),typeof t=="number"&&(t=[mt.SPHERE,mt.CAPSULE,mt.CYLINDER,mt.BOX,mt.CONVEX_HULL][t]),bo(i,"mass",!0),bo(r,"friction",!0),!Number.isFinite(o)||o<0||o>1)throw Error("restitution must be between 0 and 1");if(e.parent)throw Error("AVBD aggregate meshes must have no parent; bake the parent transform first");if(!Object.values(mt).includes(t))throw Error(`Unsupported AVBD shape: ${t}`);if(f.aggregates.length>=f.capacity)throw Error("AVBD body capacity exceeded");if(this.world=f,this.mesh=e,this.sync=l,this.type=t,a!==void 0&&typeof a!="boolean")throw Error("allowSleep must be a boolean");if(this.allowSleep=a??!0,typeof n!="boolean")throw Error("isTrigger must be a boolean");if(this.isTrigger=n,this.restitution=o,a===!0&&!f.sleepOptions&&f.enableSleeping(),this.group=Lr(c),this.collidesWith=Lr(u),f.gpu&&f.syncEnabled&&l&&f.aggregates.filter(v=>v.sync&&!v.disposed).length>=f.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(![mt.BOX,mt.SPHERE,mt.CAPSULE].includes(t)&&f.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Convex hull collisions require nine storage buffer bindings");e.computeWorldMatrix(!0),e.refreshBoundingInfo();let d=e.getBoundingInfo().boundingBox,p=e.scaling;if([p.x,p.y,p.z].some(v=>v<=0))throw Error("Bake negative or zero mesh scaling before creating an aggregate");let m=d.maximum.subtract(d.minimum).multiply(p),h=d.center.multiply(p),g=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=h,this.localRotation=g.constructor.Identity();let b;if(t===mt.BOX)b=new Ze(f.ref,Le(m),1,r,[0,0,0]);else if(t===mt.SPHERE){if(Math.max(m.x,m.y,m.z)-Math.min(m.x,m.y,m.z)>1e-4)throw Error("Sphere aggregate requires uniform dimensions");b=ar(f.ref,m.x/2,1,r,[0,0,0])}else if(t===mt.CAPSULE){if(Math.abs(m.x-m.z)>1e-4)throw Error("Capsule aggregate requires equal X and Z diameters; bake an upright Y-axis capsule mesh");b=Br(f.ref,m.x/2,m.y,1,r,[0,0,0])}else{let v=e.getVerticesData("position");if(!v)throw Error("Hull aggregate requires mesh positions");let x=Float32Array.from(v,(B,L)=>B*[p.x,p.y,p.z][L%3]),w=Ur(x);if(!w)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...w.center),this.localRotation=g.constructor.FromArray(w.rotation),b=nr(f.ref,w,1,r,[0,0,0])}let y=i/b.mass;b.mass=i,b.moment.forEach((v,x)=>b.moment[x]=v*y);let A=e.position.add(this.localCenter.applyRotationQuaternion(g));if(b.positionLin.set(Le(A)),b.positionAng.set(g.multiply(this.localRotation).asArray()),this.rigid=b,this.index=f.aggregates.length,this.body=new fs(this),f.aggregates.push(this),e.rotationQuaternion=g,f.gpu){if(this.gpuSlot=f.gpu.addBody(b),this.gpuSlot<0)throw Error("AVBD body capacity exceeded");f.byGpuIndex.set(this.gpuSlot,this.body),f.gpu.setFilters([this.gpuSlot],[this.group],[this.collidesWith]),(this.isTrigger||f.gpu.sensorsEnabled)&&f.gpu.setSensor(this.gpuSlot,this.isTrigger),this.restitution>0&&f.gpu.setRestitution(this.gpuSlot,this.restitution),f.contactWatch?.setWatched([this.gpuSlot],[!0]),f.sleepManager?.register(this.gpuSlot,b.mass,this.allowSleep),f.wakeAll?.()}}syncPose(e,t){let i=t??this.body.gpuIndex*40,r=this.localRotation.constructor.FromArray(e,i+4).multiply(this.localRotation.conjugate());this.mesh.rotationQuaternion.copyFrom(r),this.mesh.position.copyFrom(new this.mesh.position.constructor(...e.subarray(i,i+3)).subtract(this.localCenter.applyRotationQuaternion(r)))}dispose(){Cc(this)}},An=class{constructor(e,t){Object.assign(this,{world:e,rigid:t,index:e.aggregates.length,sync:!1,allowSleep:!1,isTrigger:!1,restitution:t.restitution,group:t.group,collidesWith:t.collidesWith,staticTerrain:!0}),this.body=new fs(this),e.aggregates.push(this)}dispose(){Cc(this)}};function Cc(s){if(s.disposed)return;let e=s.world;for(let t of[...e.constraints])(t.bodyA===s.body||t.bodyB===s.body)&&t.dispose();if(e.gpu){let t=s.body.gpuIndex;e.queue(s.body,16,"velocity",[0,0,0]),e.gpu.setFilters([t],[0],[0]),e.gpu.setRestitution(t,0)}s.rigid.mass=0,s.rigid.moment.fill(0),s.sync=!1,s.disposed=s.body.disposed=!0,e.sleepManager?.register(s.gpuSlot??e.gpu?.gpuIndex(s.index)??s.index,0),e.wakeAll?.()}var fs=class{constructor(e){this.aggregate=e}assertAlive(){if(this.disposed||this.aggregate.disposed)throw Error("AVBD body is disposed")}get gpuIndex(){this.assertAlive();let e=this.aggregate.world;return e.initialize(),this.aggregate.gpuSlot??e.gpu.gpuIndex(this.aggregate.index)}setLinearVelocity(e){return this.aggregate.world.queue(this,1,"velocity",e),this}setAngularVelocity(e){return this.aggregate.world.queue(this,2,"angular",e),this}applyImpulse(e,t){let i=t?Le(t):null;if(i&&(i.length!==3||!i.every(Number.isFinite)))throw Error("Impulse point requires three finite components");let r=this.aggregate.world,o=Le(e),a=r.commands.get(this),n=o.map((c,u)=>c+(a?.impulse?.[u]||0)),l=r.queue(this,4,"impulse",n);if(t){let c=[i[1]*o[2]-i[2]*o[1],i[2]*o[0]-i[0]*o[2],i[0]*o[1]-i[1]*o[0]],u=l.torque||[0,0,0];l.torque=Le(c).map((f,d)=>f+u[d]),l.pointImpulse=o.map((f,d)=>f+(l.pointImpulse?.[d]||0))}return this}applyAngularImpulse(e){let t=this.aggregate.world,i=t.commands.get(this)?.torque||[0,0,0];return t.queue(this,4,"torque",Le(e).map((r,o)=>r+i[o])),this}applyForce(e,t){return this.applyImpulse(Le(e).map(i=>i*this.aggregate.world.ref.dt),t)}applyTorque(e){return this.applyAngularImpulse(Le(e).map(t=>t*this.aggregate.world.ref.dt))}setCollisionGroups(e,t=4294967295){this.assertAlive();let i=this.aggregate,r=Lr(e),o=Lr(t);return i.group=r,i.collidesWith=o,i.world.gpu&&i.world.gpu.setFilters([this.gpuIndex],[i.group],[i.collidesWith]),i.world.wakeAll?.(),this}setTrigger(e){if(this.assertAlive(),typeof e!="boolean")throw Error("isTrigger must be a boolean");let t=this.aggregate;return t.isTrigger=e,t.world.gpu&&t.world.gpu.setSensor(this.gpuIndex,e),t.world.wakeAll(),this}setRestitution(e){if(this.assertAlive(),!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");let t=this.aggregate;return t.restitution=e,t.world.gpu&&t.world.gpu.setRestitution(this.gpuIndex,e),t.world.wakeAll(),this}async readState(){let e=await this.aggregate.world.readBodies([this]),t=this.aggregate.rigid.mass;return{position:Array.from(e.subarray(0,3)),rotation:Array.from(e.subarray(4,8)),linearVelocity:Array.from(e.subarray(32,35)),angularVelocity:Array.from(e.subarray(36,39)),mass:t,effectiveMass:e[19],sleeping:t>0&&e[19]===0}}wakeUp(){return this.assertAlive(),this.aggregate.world.wakeAll(),this}setSleepEnabled(e){if(this.assertAlive(),typeof e!="boolean")throw Error("Sleep eligibility must be a boolean");let t=this.aggregate,i=t.world;return t.allowSleep=e,e&&!i.sleepOptions&&i.enableSleeping(),i.gpu&&i.sleepManager?.register(this.gpuIndex,t.rigid.mass,e),i.wakeAll(),this}teleport(e,t){if(this.assertAlive(),this.aggregate.staticTerrain)throw Error("Terrain is static; set its geometry transform before registration");let i=this.aggregate;if(t===void 0){let a=i.world.commands.get(this);if(a&&(a.mask&8)!==0&&(a.mask&32)===0){let n=i.mesh.rotationQuaternion.clone();n.copyFromFloats(...a.rotation);let l=i.localCenter.applyRotationQuaternion(i.localRotation.conjugate());i.world.queue(this,8,"position",Le(e.add(l.applyRotationQuaternion(n))))}else{let n=i.localCenter.applyRotationQuaternion(i.localRotation.conjugate());i.world.queue(this,40,"position",Le(e)),i.world.queue(this,40,"rotation",[...Le(n),0])}return this}let r=t.clone().normalize();i.world.queue(this,8,"position",Le(e.add(i.localCenter.applyRotationQuaternion(r))));let o=i.world.queue(this,8,"rotation",r.multiply(i.localRotation).asArray());return o.mask&=-33,this}};var Ni=class{solver;bodyA;bodyB;J=new Float64Array(12);H=new Float64Array(36);C=new Float64Array(4);fmin=new Float64Array(4).fill(-1/0);fmax=new Float64Array(4).fill(1/0);stiffness=new Float64Array(4).fill(1/0);fracture=new Float64Array(4).fill(1/0);penalty=new Float64Array(4);lambda=new Float64Array(4);constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}disable(){this.stiffness.fill(0),this.penalty.fill(0),this.lambda.fill(0)}};var Sc=s=>s<0?-1:s>0?1:0,_r=(s,e)=>s<e?s:e,Of=(s,e)=>s>e?s:e,Er=(s,e,t)=>Of(e,_r(t,s));function ds(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function qi(s,e,t){let i=Math.cos(s[2]),r=Math.sin(s[2]);return[i*e-r*t+s[0],r*e+i*t+s[1]]}function Zt(s,e,t,i,r){s[e*3]=t,s[e*3+1]=i,s[e*3+2]=r}var xo=class extends Ni{rA;rB;C0=new Float64Array(3);torqueArm;restAngle;constructor(e,t,i,r,o,a=[1/0,1/0,1/0],n=1/0){super(e,t,i),this.rA=[r[0],r[1]],this.rB=[o[0],o[1]],this.stiffness[0]=a[0],this.stiffness[1]=a[1],this.stiffness[2]=a[2],this.fmax[2]=n,this.fmin[2]=-n,this.fracture[2]=n,this.restAngle=(t?t.position[2]:0)-i.position[2];let l=(t?t.size[0]:0)+i.size[0],c=(t?t.size[1]:0)+i.size[1];this.torqueArm=l*l+c*c}rows(){return 3}evaluate(e){let t=this.bodyA?qi(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=qi(this.bodyB.position,this.rB[0],this.rB[1]);e[0]=t[0]-i[0],e[1]=t[1]-i[1],e[2]=((this.bodyA?this.bodyA.position[2]:0)-this.bodyB.position[2]-this.restAngle)*this.torqueArm}initialize(){return this.evaluate(this.C0),this.stiffness[0]!==0||this.stiffness[1]!==0||this.stiffness[2]!==0}Cn=new Float64Array(3);computeConstraint(e){this.evaluate(this.Cn);for(let t=0;t<3;t++)this.C[t]=this.stiffness[t]===1/0?this.Cn[t]-this.C0[t]*e:this.Cn[t]}computeDerivatives(e){let{J:t,H:i}=this;if(i.fill(0,0,27),e===this.bodyA){let r=ds(e.position[2],this.rA[0],this.rA[1]);Zt(t,0,1,0,-r[1]),Zt(t,1,0,1,r[0]),Zt(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=ds(e.position[2],this.rB[0],this.rB[1]);Zt(t,0,-1,0,r[1]),Zt(t,1,0,-1,-r[0]),Zt(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},Ao=class extends Ni{rA;rB;rest;constructor(e,t,i,r,o,a,n=-1){if(super(e,t,i),this.rA=[r[0],r[1]],this.rB=[o[0],o[1]],this.stiffness[0]=a,this.rest=n,this.rest<0){let l=qi(t.position,r[0],r[1]),c=qi(i.position,o[0],o[1]);this.rest=Math.hypot(l[0]-c[0],l[1]-c[1])}}rows(){return 1}initialize(){return!0}delta(){let e=qi(this.bodyA.position,this.rA[0],this.rA[1]),t=qi(this.bodyB.position,this.rB[0],this.rB[1]);return[e[0]-t[0],e[1]-t[1]]}computeConstraint(){let e=this.delta();this.C[0]=Math.hypot(e[0],e[1])-this.rest}computeDerivatives(e){let t=this.delta(),i=t[0]*t[0]+t[1]*t[1];if(i===0)return;let r=Math.sqrt(i),o=t[0]/r,a=t[1]/r,n=(1-o*o)/r,l=(0-o*a)/r,c=(0-a*o)/r,u=(1-a*a)/r,f=e===this.bodyA,d=f?this.rA:this.rB,p=ds(e.position[2],-d[1],d[0]),m=ds(e.position[2],d[0],d[1]),h=n*p[0]+l*p[1],g=c*p[0]+u*p[1],b=o*m[0]+a*m[1],y=o*p[0]+a*p[1];f?Zt(this.J,0,o,a,y):Zt(this.J,0,-o,-a,-y);let A=f?-b-b:b+b,v=this.H;v[0]=n,v[1]=l,v[2]=h,v[3]=c,v[4]=u,v[5]=g,v[6]=h,v[7]=g,v[8]=A}},wo=class extends Ni{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},Bo=class extends Ni{speed;constructor(e,t,i,r,o){super(e,t,i),this.speed=r,this.fmax[0]=o,this.fmin[0]=-o}rows(){return 1}initialize(){return!0}computeConstraint(){let e=this.bodyA?this.bodyA.position[2]-this.bodyA.initial[2]:0,t=this.bodyB.position[2]-this.bodyB.initial[2];this.C[0]=e-t-this.speed*this.solver.dt}computeDerivatives(e){Zt(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var Co=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),So=()=>({...Co(),postStabilize:!1,alpha:.95,matchNearest:!0});var Mr=(s,e)=>s>e?s*2097152+e:e*2097152+s;function Tf(s,e,t){let i=0,r=e;for(;i<r;){let o=i+r>>>1;s[o]<t?i=o+1:r=o}return i}function wn(s,e,t){let i=Tf(s,e,t);return i<e&&s[i]===t}var ko=class{cellSize=1;pairs=new Float64Array(1024);pairCount=0;configuredFor=-1;large=new Int32Array(0);isLarge=new Uint8Array(0);cellX=new Int32Array(0);cellY=new Int32Array(0);tableSize=0;bucketStart=new Int32Array(0);sorted=new Int32Array(0);configure(e,t){this.configuredFor=e;let i=Float64Array.from({length:e},(n,l)=>t[l*4+1]).sort(),r=e>0?i[e>>1]:1,o=0;for(let n=0;n<e;n++)i[n]<=4*r&&(o=Math.max(o,i[n]));this.cellSize=Math.max(2*o,.001),this.isLarge=new Uint8Array(e);let a=[];for(let n=0;n<e;n++)t[n*4+1]>o&&(this.isLarge[n]=1,a.push(n));for(this.large=Int32Array.from(a),this.cellX=new Int32Array(e),this.cellY=new Int32Array(e),this.tableSize=1;this.tableSize<2*e;)this.tableSize<<=1;this.bucketStart=new Int32Array(this.tableSize+1),this.sorted=new Int32Array(e)}hash(e,t){return(Math.imul(e,73856093)^Math.imul(t,19349663))&this.tableSize-1}findPairs(e,t,i,r,o,a){e!==this.configuredFor&&this.configure(e,i),this.pairCount=0;let{cellX:n,cellY:l,isLarge:c,bucketStart:u,sorted:f}=this,d=1/this.cellSize;u.fill(0);for(let h=0;h<e;h++)c[h]||(n[h]=Math.floor(t[h*4]*d),l[h]=Math.floor(t[h*4+1]*d),u[this.hash(n[h],l[h])+1]++);for(let h=0;h<this.tableSize;h++)u[h+1]+=u[h];let p=u.slice(0,this.tableSize);for(let h=0;h<e;h++)c[h]||(f[p[this.hash(n[h],l[h])]++]=h);let m=(h,g)=>{if(!r[h]&&!r[g])return;let b=t[h*4]-t[g*4],y=t[h*4+1]-t[g*4+1],A=i[h*4+1]+i[g*4+1];if(b*b+y*y>A*A)return;let v=Mr(h,g);if(!wn(o,a,v)){if(this.pairCount===this.pairs.length){let x=new Float64Array(this.pairs.length*2);x.set(this.pairs),this.pairs=x}this.pairs[this.pairCount++]=v}};for(let h=0;h<e;h++){if(c[h])continue;let g=n[h],b=l[h];for(let y=-1;y<=1;y++)for(let A=-1;A<=1;A++){let v=this.hash(g+A,b+y);for(let x=u[v];x<u[v+1];x++){let w=f[x];w>=h||n[w]!==g+A||l[w]!==b+y||m(h,w)}}}for(let h of this.large)for(let g=0;g<e;g++)g===h||c[g]&&g>h||m(h,g);this.pairs.subarray(0,this.pairCount).sort()}};var gt=new Float64Array(12),kc=new Float64Array(12),Vi=new Float64Array(12);function qt(s,e,t,i,r,o,a,n){let l=e*6;s[l]=t,s[l+1]=i,s[l+2]=r,s[l+3]=o,s[l+4]=a,s[l+5]=n}function Pc(s,e,t,i,r,o){let a=0,n=t*e[0]+i*e[1]-r,l=t*e[6]+i*e[7]-r;if(n<=0){for(let c=0;c<6;c++)s[a*6+c]=e[c];a++}if(l<=0){for(let c=0;c<6;c++)s[a*6+c]=e[6+c];a++}if(n*l<0){let c=n/(n-l),u=e[0]+(e[6]-e[0])*c,f=e[1]+(e[7]-e[1])*c;n>0?qt(s,a,u,f,o,e[3],0,e[5]):qt(s,a,u,f,e[8],o,e[10],0),a++}return a}function Po(s,e,t,i,r,o,a,n){let l=-(r*a+o*n),c=-(-o*a+r*n);Math.abs(l)>Math.abs(c)?l>0?(qt(gt,0,s,-e,0,0,3,4),qt(gt,1,s,e,0,0,4,1)):(qt(gt,0,-s,e,0,0,1,2),qt(gt,1,-s,-e,0,0,2,3)):c>0?(qt(gt,0,s,e,0,0,4,1),qt(gt,1,-s,e,0,0,1,2)):(qt(gt,0,-s,-e,0,0,2,3),qt(gt,1,s,-e,0,0,3,4));for(let u=0;u<2;u++){let f=u*6,d=gt[f],p=gt[f+1];gt[f]=t+r*d-o*p,gt[f+1]=i+o*d+r*p}}function Lo(s,e,t,i,r,o,a,n,l,c,u){let f=Math.cos(t),d=Math.sin(t),p=Math.cos(n),m=Math.sin(n),h=o-s,g=a-e,b=f*h+d*g,y=-d*h+f*g,A=p*h+m*g,v=-m*h+p*g,x=Math.abs(f*p+d*m),w=Math.abs(f*-m+d*p),B=Math.abs(-d*p+f*m),L=Math.abs(-d*-m+f*p),M=Math.abs(b)-i-(x*l+w*c),I=Math.abs(y)-r-(B*l+L*c);if(M>0||I>0)return 0;let E=Math.abs(A)-(x*i+B*r)-l,k=Math.abs(v)-(w*i+L*r)-c;if(E>0||k>0)return 0;let P=0,C=M,_=b>0?f:-f,z=b>0?d:-d;I>.95*C+.01*r&&(P=1,C=I,_=y>0?-d:d,z=y>0?f:-f),E>.95*C+.01*l&&(P=2,C=E,_=A>0?p:-p,z=A>0?m:-m),k>.95*C+.01*c&&(P=3,C=k,_=v>0?-m:m,z=v>0?p:-p);let R,G,X,W,U,H,Z,he,we;if(P===0){R=_,G=z,X=s*R+e*G+i,W=-d,U=f;let $=s*W+e*U;H=-$+r,Z=$+r,he=3,we=1,Po(l,c,o,a,p,m,R,G)}else if(P===1){R=_,G=z,X=s*R+e*G+r,W=f,U=d;let $=s*W+e*U;H=-$+i,Z=$+i,he=2,we=4,Po(l,c,o,a,p,m,R,G)}else if(P===2){R=-_,G=-z,X=o*R+a*G+l,W=-m,U=p;let $=o*W+a*U;H=-$+c,Z=$+c,he=3,we=1,Po(i,r,s,e,f,d,R,G)}else{R=-_,G=-z,X=o*R+a*G+c,W=p,U=m;let $=o*W+a*U;H=-$+l,Z=$+l,he=2,we=4,Po(i,r,s,e,f,d,R,G)}if(Pc(kc,gt,-W,-U,H,he)<2||Pc(Vi,kc,W,U,Z,we)<2)return 0;let Oe=0;for(let $=0;$<2;$++){let Je=$*6,Xe=Vi[Je],Be=Vi[Je+1],ot=R*Xe+G*Be-X;if(ot>0)continue;let ri=Xe-R*ot,Ye=Be-G*ot,at=Vi[Je+2],Qe=Vi[Je+3],nt=Vi[Je+4],wt=Vi[Je+5],Qi,Ki,Zi,er;P>=2?([at,Qe,nt,wt]=[nt,wt,at,Qe],Qi=Xe-s,Ki=Be-e,Zi=ri-o,er=Ye-a):(Qi=ri-s,Ki=Ye-e,Zi=Xe-o,er=Be-a);let Wt=Oe*7;u[Wt+0]=at&255|(Qe&255)<<8|(nt&255)<<16|(wt&255)<<24,u[Wt+1]=f*Qi+d*Ki,u[Wt+1+1]=-d*Qi+f*Ki,u[Wt+3]=p*Zi+m*er,u[Wt+3+1]=-m*Zi+p*er,u[Wt+5]=-_,u[Wt+5+1]=-z,Oe++}return Oe}var Lc=s=>31-Math.clz32(s&-s);function jf(s){let e=s+2654435769;return e^=e>>>16,e=Math.imul(e,2146121005),e^=e>>>15,e=Math.imul(e,2221713035),e^=e>>>16,e>>>0}var Bn=(s,e,t,i)=>s>t||s===t&&e>i,_o=class{colors=new Int32Array(0);colorStart=new Int32Array(65);colorBodies=new Int32Array(0);next=new Int32Array(0);pending=new Uint8Array(0);prio=new Uint32Array(0);resize(e){if(this.colors.length===e)return;let t=new Int32Array(e).fill(-1);t.set(this.colors.subarray(0,Math.min(e,this.colors.length))),this.colors=t,this.next=new Int32Array(e),this.pending=new Uint8Array(e),this.colorBodies=new Int32Array(e),this.prio=Uint32Array.from({length:e},(i,r)=>jf(r))}run(e,t,i,r,o,a){this.resize(e);let{colors:n,next:l,pending:c,prio:u}=this,f=(v,x)=>{let w=o[v*4+1],B=w===x?o[v*4+2]:w;return B>=0&&t[B]?B:-1},d=(v,x)=>{let w=0,B=0;for(let L=i[v];L<i[v+1];L++){let M=f(r[L],v);if(M<0||x&&c[M])continue;let I=n[M];I<0||(I<32?w|=1<<I:B|=1<<I-32)}return~w!==0?Lc(~w):~B!==0?32+Lc(~B):63};for(let v=0;v<e;v++){if(l[v]=n[v],!t[v]||n[v]<0)continue;let x=!0;for(let w=i[v];w<i[v+1]&&x;w++){let B=f(r[w],v);B>=0&&!Bn(u[v],v,u[B],B)&&(x=!1)}x&&(l[v]=Math.min(n[v],d(v,!1)))}n.set(l);let p=()=>{let v=0;for(let x=0;x<e;x++)if(c[x]=0,!!t[x]){if(n[x]<0)c[x]=1;else for(let w=i[x];w<i[x+1];w++){let B=f(r[w],x);if(B>=0&&n[B]===n[x]&&Bn(u[B],B,u[x],x)){c[x]=1;break}}v+=c[x]}return v},m=p(),h=0;for(;m>0&&h<a;){h++;for(let v=0;v<e;v++){if(l[v]=-2,!c[v])continue;let x=!0;for(let w=i[v];w<i[v+1]&&x;w++){let B=f(r[w],v);B>=0&&c[B]&&!Bn(u[v],v,u[B],B)&&(x=!1)}x&&(l[v]=d(v,!0))}for(let v=0;v<e;v++)l[v]!==-2&&(n[v]=l[v],c[v]=0,m--)}let g=0;if(m>0){for(let v=0;v<e;v++)c[v]&&n[v]<0&&(n[v]=0);g=p()}let b=this.colorStart;b.fill(0);let y=0;for(let v=0;v<e;v++)t[v]&&(b[n[v]+1]++,y=Math.max(y,n[v]+1));for(let v=0;v<64;v++)b[v+1]+=b[v];let A=b.slice(0,64);for(let v=0;v<e;v++)t[v]&&(this.colorBodies[A[n[v]]++]=v);return{numColors:y,conflicts:g,rounds:h}}};var _c=0,Bi=1,Ec=2,Io=3,hs=4,Mc=[0,3,1,1,2],Sn=.05,j=4,O=32,se=0,te=3,Q=6,_e=9,Ae=12,Pt=15,fe=18,N=21,Y=23,re=25,be=26,Ue=27,Wi=28,zr=class{params=Co();options;bodyCount=0;pose;initial;inertial;velocity;prevVelocity;shape;props;dynamic=new Uint8Array(0);jointCount=0;contactCount=0;info=new Int32Array(0);data;handles=[];prevCount=0;prevKeys=new Float64Array(0);prevInfo=new Int32Array(0);prevData=new Float64Array(0);ignorePairs=[];noCollide=new Float64Array(0);noCollideCount=0;noCollideDirty=!0;adjStart=new Int32Array(1);adjList=new Int32Array(0);broadphase=new ko;coloring=new _o;numColors=0;colorConflicts=0;colorRounds=0;profiling=!1;profile={};phaseStart=0;contactOut=new Float64Array(14);dxBuf=new Float64Array(0);constructor(e={}){this.options={precision:"f32",order:"colored",colorRounds:32,...e},this.pose=this.real(0),this.initial=this.real(0),this.inertial=this.real(0),this.velocity=this.real(0),this.prevVelocity=this.real(0),this.shape=this.real(0),this.props=this.real(0),this.data=this.real(0)}real(e){return this.options.precision==="f32"?new Float32Array(e):new Float64Array(e)}grow(e,t){if(e.length>=t)return e;let i=new e.constructor(Math.max(t,e.length*2,16));return i.set(e),i}clear(){this.bodyCount=0,this.jointCount=0,this.contactCount=0,this.prevCount=0,this.handles=[],this.ignorePairs=[],this.noCollideDirty=!0,this.coloring.resize(0)}addBody(e,t,i,r,o=[0,0,0]){let a=this.bodyCount++,n=this.bodyCount*4;this.pose=this.grow(this.pose,n),this.initial=this.grow(this.initial,n),this.inertial=this.grow(this.inertial,n),this.velocity=this.grow(this.velocity,n),this.prevVelocity=this.grow(this.prevVelocity,n),this.shape=this.grow(this.shape,n),this.props=this.grow(this.props,n),this.dynamic=this.grow(this.dynamic,this.bodyCount);let l=e[0]*e[1]*t;return this.pose.set([r[0],r[1],r[2]??0,0],a*4),this.velocity.set([o[0],o[1],o[2]??0,0],a*4),this.prevVelocity.set(this.velocity.subarray(a*4,a*4+4),a*4),this.shape.set([e[0],e[1],l,l*(e[0]*e[0]+e[1]*e[1])/12],a*4),this.props.set([i,Math.hypot(e[0]*.5,e[1]*.5),0,0],a*4),this.dynamic[a]=l>0?1:0,a}allocConstraint(){let e=this.jointCount++,t=this.jointCount+this.contactCount;this.info=this.grow(this.info,t*j),this.data=this.grow(this.data,t*O),this.contactCount>0&&(this.info.copyWithin((e+1)*j,e*j,(t-1)*j),this.data.copyWithin((e+1)*O,e*O,(t-1)*O)),this.data.fill(0,e*O,e*O+O);for(let i=0;i<3;i++)this.data[e*O+Q+i]=1/0,this.data[e*O+_e+i]=-1/0,this.data[e*O+Ae+i]=1/0,this.data[e*O+Pt+i]=1/0;return this.handles[e]=null,this.noCollideDirty=!0,e}setInfo(e,t,i,r){this.info.set([t,i,r,0],e*j)}addJoint(e,t,i,r,o=[1/0,1/0,1/0],a=1/0){let n=this.allocConstraint();this.setInfo(n,Bi,e,t);let l=this.data,c=n*O;l.set(o,c+Q),l[c+Ae+2]=a,l[c+_e+2]=-a,l[c+Pt+2]=a,l.set(i,c+N),l.set(r,c+Y),l[c+re]=(e>=0?this.pose[e*4+2]:0)-this.pose[t*4+2];let u=(e>=0?this.shape[e*4]:0)+this.shape[t*4],f=(e>=0?this.shape[e*4+1]:0)+this.shape[t*4+1];l[c+be]=u*u+f*f;let d={slot:n,alive:!0};return this.handles[n]=d,d}setJointWorldAnchor(e,t,i){e.alive&&(this.data[e.slot*O+N]=t,this.data[e.slot*O+N+1]=i)}removeJoint(e){e.alive&&(this.disable(e.slot),e.alive=!1)}addSpring(e,t,i,r,o,a){let n=this.allocConstraint();this.setInfo(n,Ec,e,t);let l=n*O;this.data[l+Q]=o,this.data.set(i,l+N),this.data.set(r,l+Y),this.data[l+re]=a}addMotor(e,t,i,r){let o=this.allocConstraint();this.setInfo(o,Io,e,t);let a=o*O;this.data[a+Ae]=r,this.data[a+_e]=-r,this.data[a+re]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(Mr(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(Co());Object.assign(this.params,Object.fromEntries(t.map(r=>[r,e[r]])));let i=new Map(e.bodies.map((r,o)=>[r,o]));for(let r of e.bodies){let o=this.addBody(r.size,0,r.friction,r.position,r.velocity);this.shape[o*4+2]=r.mass,this.shape[o*4+3]=r.moment,this.dynamic[o]=r.mass>0?1:0}for(let r of e.forces){let o=r.bodyA?i.get(r.bodyA):-1,a=i.get(r.bodyB);if(r instanceof xo){let n=this.addJoint(o,a,r.rA,r.rB,[r.stiffness[0],r.stiffness[1],r.stiffness[2]],r.fracture[2]);this.data[n.slot*O+re]=r.restAngle,this.data[n.slot*O+be]=r.torqueArm}else r instanceof Ao?this.addSpring(o,a,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof Bo?this.addMotor(o,a,r.speed,r.fmax[0]):r instanceof wo&&this.addIgnoreCollision(o,a)}}step(){let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha;this.phaseStart=this.profiling?performance.now():0,this.saveContacts(),this.findPairs(),this.mark("broadphase"),this.initJoints(),this.narrowphase(),this.mark("narrowphase"),this.warmStartConstraints(t,i),this.buildAdjacency(),this.mark("adjacency"),this.colorBodies(),this.mark("coloring"),this.warmStartBodies();let r=e.iterations+(t?1:0);for(let o=0;o<r;o++){let a=t?o<e.iterations?1:0:i;this.primal(a),this.mark("primal"),o<e.iterations&&this.dual(a),o===e.iterations-1&&this.updateVelocities(),this.mark("dual")}t&&this.refreshStick()}mark(e){if(!this.profiling)return;let t=performance.now();this.profile[e]=(this.profile[e]??0)+t-this.phaseStart,this.phaseStart=t}resetProfile(){for(let e of Object.keys(this.profile))delete this.profile[e]}saveContacts(){let e=this.contactCount,t=this.jointCount;this.prevKeys.length<e&&(this.prevKeys=new Float64Array(e*2),this.prevInfo=new Int32Array(e*2*j),this.prevData=new Float64Array(e*2*O));for(let i=0;i<e;i++){let r=t+i;this.prevKeys[i]=Mr(this.info[r*j+1],this.info[r*j+2])}this.prevInfo.set(this.info.subarray(t*j,(t+e)*j)),this.prevData.set(this.data.subarray(t*O,(t+e)*O)),this.prevCount=e,this.contactCount=0}findPairs(){if(this.noCollideDirty){let e=[...this.ignorePairs];for(let t=0;t<this.jointCount;t++){let i=this.info[t*j+1],r=this.info[t*j+2];this.info[t*j]!==_c&&i>=0&&e.push(Mr(i,r))}this.noCollide=Float64Array.from(e).sort(),this.noCollideCount=e.length,this.noCollideDirty=!1}this.broadphase.findPairs(this.bodyCount,this.pose,this.props,this.dynamic,this.noCollide,this.noCollideCount)}initJoints(){let{info:e,data:t,pose:i}=this,r=0;for(let o=0;o<this.jointCount;o++){let a=o*O;if(e[o*j]===Bi){if(t[a+Q]===0&&t[a+Q+1]===0&&t[a+Q+2]===0){let u=this.handles[o];u&&(u.alive=!1);continue}let l=e[o*j+1],c=e[o*j+2];this.jointC(l,c,a,i,t,a+fe)}r!==o&&(e.copyWithin(r*j,o*j,(o+1)*j),t.copyWithin(r*O,a,a+O),this.handles[r]=this.handles[o],this.handles[r]&&(this.handles[r].slot=r)),r++}if(r!==this.jointCount){for(let o=r;o<this.jointCount;o++)this.handles[o]=null;this.jointCount=r,this.noCollideDirty=!0}}jointC(e,t,i,r,o,a,n=o){let l,c,u;if(e>=0){let h=Math.cos(r[e*4+2]),g=Math.sin(r[e*4+2]),b=o[i+N],y=o[i+N+1];l=h*b-g*y+r[e*4],c=g*b+h*y+r[e*4+1],u=r[e*4+2]}else l=o[i+N],c=o[i+N+1],u=0;let f=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),p=o[i+Y],m=o[i+Y+1];n[a]=l-(f*p-d*m+r[t*4]),n[a+1]=c-(d*p+f*m+r[t*4+1]),n[a+2]=(u-r[t*4+2]-o[i+re])*o[i+be]}narrowphase(){let{pose:e,shape:t,props:i}=this,r=this.broadphase.pairs,o=this.contactOut;for(let a=0;a<this.broadphase.pairCount;a++){let n=r[a],l=Math.floor(n/2097152),c=n-l*2097152,u=Lo(e[l*4],e[l*4+1],e[l*4+2],t[l*4]*.5,t[l*4+1]*.5,e[c*4],e[c*4+1],e[c*4+2],t[c*4]*.5,t[c*4+1]*.5,o);if(u===0)continue;let f=0,d=this.prevCount;for(;f<d;){let h=f+d>>>1;this.prevKeys[h]<n?f=h+1:d=h}let p=f,m=Math.sqrt(i[l*4]*i[c*4]);for(let h=0;h<u;h++){let g=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(g+1)*j),this.data=this.grow(this.data,(g+1)*O);let b=this.data,y=g*O,A=h*7,v=o[A+0];this.info.set([hs,l,c,v],g*j),b.fill(0,y,y+O),b[y+Q]=b[y+Q+1]=1/0,b[y+_e]=-1/0,b[y+Ae]=0,b[y+Pt]=b[y+Pt+1]=1/0,b[y+N]=o[A+1],b[y+N+1]=o[A+1+1],b[y+Y]=o[A+3],b[y+Y+1]=o[A+3+1],b[y+re]=m,b[y+be]=o[A+5],b[y+Ue]=o[A+5+1];let x=-1;for(let C=p;C<this.prevCount&&this.prevKeys[C]===n;C++)this.prevInfo[C*j+3]===v&&(x=C);if(x<0&&this.params.matchNearest){let C=Sn*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let _=p;_<this.prevCount&&this.prevKeys[_]===n;_++){let z=Math.hypot(this.prevData[_*O+N]-b[y+N],this.prevData[_*O+N+1]-b[y+N+1]);z<=C&&(C=z,x=_)}}if(x>=0){let C=x*O;b[y+se]=this.prevData[C+se],b[y+se+1]=this.prevData[C+se+1],b[y+te]=this.prevData[C+te],b[y+te+1]=this.prevData[C+te+1],this.prevData[C+Wi]&&(b[y+N]=this.prevData[C+N],b[y+N+1]=this.prevData[C+N+1],b[y+Y]=this.prevData[C+Y],b[y+Y+1]=this.prevData[C+Y+1])}let[w,B]=wi(e[l*4+2],b[y+N],b[y+N+1]),[L,M]=wi(e[c*4+2],b[y+Y],b[y+Y+1]),I=b[y+be],E=b[y+Ue],k=e[l*4]+w-e[c*4]-L,P=e[l*4+1]+B-e[c*4+1]-M;b[y+fe]=I*k+E*P+5e-4,b[y+fe+1]=E*k-I*P}}}warmStartConstraints(e,t){let i=this.params,r=this.data,o=this.jointCount+this.contactCount;for(let a=0;a<o;a++){let n=Mc[this.info[a*j]],l=a*O;for(let c=0;c<n;c++){if(i.vbd){r[l+se+c]=_r(r[l+Q+c],i.vbdStiffness);continue}e||(r[l+te+c]=r[l+te+c]*t*i.gamma),r[l+se+c]=_r(Er(r[l+se+c]*i.gamma,1,1e9),r[l+Q+c])}}}prepareTopology(){this.buildAdjacency(),this.colorBodies()}buildAdjacency(){let e=this.bodyCount,t=this.jointCount+this.contactCount;this.adjStart=this.grow(this.adjStart,e+1),this.adjList=this.grow(this.adjList,t*2);let i=this.adjStart;i.fill(0,0,e+1);for(let o=0;o<t;o++)for(let a=1;a<=2;a++){let n=this.info[o*j+a];n>=0&&this.dynamic[n]&&i[n+1]++}for(let o=0;o<e;o++)i[o+1]+=i[o];let r=i.slice(0,e);for(let o=0;o<t;o++)for(let a=1;a<=2;a++){let n=this.info[o*j+a];n>=0&&this.dynamic[n]&&(this.adjList[r[n]++]=o)}}colorBodies(){if(this.options.order!=="colored")return;let e=this.coloring.run(this.bodyCount,this.dynamic,this.adjStart,this.adjList,this.info,this.options.colorRounds);this.numColors=e.numColors,this.colorConflicts=e.conflicts,this.colorRounds=e.rounds}warmStartBodies(){let{dt:e,gravity:t}=this.params,{pose:i,velocity:r,initial:o,inertial:a,prevVelocity:n}=this;for(let l=0;l<this.bodyCount;l++){let c=l*4;r[c+2]=Er(r[c+2],-50,50),a[c]=i[c]+r[c]*e,a[c+1]=i[c+1]+r[c+1]*e,a[c+2]=i[c+2]+r[c+2]*e,this.dynamic[l]&&(a[c+1]+=t*e*e);let u=(r[c+1]-n[c+1])/e*Sc(t),f=Er(u/Math.abs(t),0,1);Number.isFinite(f)||(f=0),o[c]=i[c],o[c+1]=i[c+1],o[c+2]=i[c+2],i[c]=i[c]+r[c]*e,i[c+1]=i[c+1]+r[c+1]*e+t*f*e*e,i[c+2]=i[c+2]+r[c+2]*e}}eC=new Float64Array(3);eMin=new Float64Array(3);eMax=new Float64Array(3);eJ=new Float64Array(9);eG=new Float64Array(9);evalConstraint(e,t,i){let{info:r,data:o,pose:a,initial:n}=this,l=r[e*j],c=r[e*j+1],u=r[e*j+2],f=e*O,{eC:d,eMin:p,eMax:m,eJ:h,eG:g}=this,b=Mc[l];for(let A=0;A<b;A++)p[A]=o[f+_e+A],m[A]=o[f+Ae+A];let y=i===c;if(g.fill(0),l===Bi){this.jointC(c,u,f,a,o,0,d);for(let A=0;A<3;A++)o[f+Q+A]===1/0&&(d[A]-=o[f+fe+A]*t);if(i>=0){let A=a[i*4+2],[v,x]=wi(A,o[f+(y?N:Y)],o[f+(y?N:Y)+1]),w=y?1:-1;h[0]=w,h[1]=0,h[2]=-w*x,h[3]=0,h[4]=w,h[5]=w*v,h[6]=0,h[7]=0,h[8]=w*o[f+be],g[2]=Math.abs(v),g[5]=Math.abs(x)}}else if(l===Ec){let[A,v]=zc(a,c,o[f+N],o[f+N+1]),[x,w]=zc(a,u,o[f+Y],o[f+Y+1]),B=A-x,L=v-w,M=B*B+L*L,I=Math.sqrt(M);if(d[0]=I-o[f+re],i>=0&&M!==0){let E=B/I,k=L/I,P=(1-E*E)/I,C=-E*k/I,_=(1-k*k)/I,z=a[i*4+2],R=y?N:Y,[G,X]=wi(z,-o[f+R+1],o[f+R]),[W,U]=wi(z,o[f+R],o[f+R+1]),H=P*G+C*X,Z=C*G+_*X,he=E*W+k*U,we=E*G+k*X,Oe=y?1:-1;h[0]=Oe*E,h[1]=Oe*k,h[2]=Oe*we;let $=y?-he-he:he+he;g[0]=Math.hypot(P,C,H),g[1]=Math.hypot(C,_,Z),g[2]=Math.hypot(H,Z,$)}else i>=0&&h.fill(0,0,3)}else if(l===Io){let A=c>=0?a[c*4+2]-n[c*4+2]:0,v=a[u*4+2]-n[u*4+2];d[0]=A-v-o[f+re]*this.params.dt,i>=0&&(h[0]=0,h[1]=0,h[2]=y?1:-1)}else if(l===hs){let A=o[f+be],v=o[f+Ue],x=v,w=-A,[B,L]=wi(n[c*4+2],o[f+N],o[f+N+1]),[M,I]=wi(n[u*4+2],o[f+Y],o[f+Y+1]),E=B*v-L*A,k=-(M*v-I*A),P=B*w-L*x,C=-(M*w-I*x),_=a[c*4]-n[c*4],z=a[c*4+1]-n[c*4+1],R=a[c*4+2]-n[c*4+2],G=a[u*4]-n[u*4],X=a[u*4+1]-n[u*4+1],W=a[u*4+2]-n[u*4+2];d[0]=o[f+fe]*(1-t)+A*_+v*z+E*R+-A*G+-v*X+k*W,d[1]=o[f+fe+1]*(1-t)+x*_+w*z+P*R+-x*G+-w*X+C*W;let U=Math.abs(o[f+te])*o[f+re];m[1]=U,p[1]=-U,i>=0&&(y?(h[0]=A,h[1]=v,h[2]=E,h[3]=x,h[4]=w,h[5]=P):(h[0]=-A,h[1]=-v,h[2]=k,h[3]=-x,h[4]=-w,h[5]=C))}return b}solveBody(e,t){let i=this.params,{shape:r,pose:o,inertial:a,data:n}=this,l=i.dt*i.dt,c=r[e*4+2]/l,u=r[e*4+3]/l,f=c,d=0,p=c,m=0,h=0,g=u,b=c*(o[e*4]-a[e*4]),y=c*(o[e*4+1]-a[e*4+1]),A=u*(o[e*4+2]-a[e*4+2]),{eC:v,eMin:x,eMax:w,eJ:B,eG:L}=this,M=this.adjStart,I=this.adjList;for(let H=M[e+1]-1;H>=M[e];H--){let Z=I[H],he=this.evalConstraint(Z,t,e),we=Z*O,Oe=!i.vbd;for(let $=0;$<he;$++){let Je=n[we+Q+$],Xe=Oe&&Je===1/0?n[we+te+$]:0,Be=n[we+se+$],ot=v[$],ri=Be*ot+Xe,Ye=Er(ri,x[$],w[$]),at=Math.abs(Ye);i.stiffnessRescale&&ot!==0&&(ri<x[$]?Be=Math.abs((x[$]-Xe)/ot):ri>w[$]&&(Be=Math.abs((w[$]-Xe)/ot)));let Qe=B[$*3],nt=B[$*3+1],wt=B[$*3+2];b+=Qe*Ye,y+=nt*Ye,A+=wt*Ye,f+=Qe*Qe*Be+L[$*3]*at,d+=nt*Qe*Be,p+=nt*nt*Be+L[$*3+1]*at,m+=wt*Qe*Be,h+=wt*nt*Be,g+=wt*wt*Be+L[$*3+2]*at}}let E=f,k=d/f,P=m/f,C=p-k*k*E,_=(h-k*P*E)/C,z=g-(P*P*E+_*_*C),R=y-k*b,X=(A-P*b-_*R)/z,W=R/C-_*X,U=b/E-k*W-P*X;this.dxBuf[e*3]=U,this.dxBuf[e*3+1]=W,this.dxBuf[e*3+2]=X}applyDx(e){this.pose[e*4]-=this.dxBuf[e*3],this.pose[e*4+1]-=this.dxBuf[e*3+1],this.pose[e*4+2]-=this.dxBuf[e*3+2]}primal(e){if(this.dxBuf.length<this.bodyCount*3&&(this.dxBuf=new Float64Array(this.bodyCount*3)),this.options.order==="sequential"){for(let r=this.bodyCount-1;r>=0;r--)this.dynamic[r]&&(this.solveBody(r,e),this.applyDx(r));return}let{colorStart:t,colorBodies:i}=this.coloring;for(let r=0;r<this.numColors;r++){for(let o=t[r];o<t[r+1];o++)this.solveBody(i[o],e);for(let o=t[r];o<t[r+1];o++)this.applyDx(i[o])}}dual(e){let t=this.params,i=this.data,{eC:r,eMin:o,eMax:a}=this,n=this.jointCount+this.contactCount;for(let l=0;l<n;l++){let c=this.info[l*j];if(c===_c)continue;let u=this.evalConstraint(l,e,-1),f=l*O;c===hs&&(i[f+Wi]=Math.abs(i[f+te+1])<a[1]&&Math.abs(i[f+fe+1])<.01?1:0);for(let d=0;d<u;d++){let p=i[f+Q+d],m=!t.vbd&&p===1/0?i[f+te+d]:0,h=Er(i[f+se+d]*r[d]+m,o[d],a[d]);i[f+te+d]=h,Math.abs(h)>=i[f+Pt+d]&&this.disable(l),!t.vbd&&h>o[d]&&h<a[d]&&(i[f+se+d]=_r(i[f+se+d]+t.beta*Math.abs(r[d]),_r(1e9,i[f+Q+d])))}}}refreshStick(){let e=this.data;for(let t=0;t<this.contactCount;t++){let i=(this.jointCount+t)*O,r=Math.abs(e[i+te])*e[i+re];e[i+Wi]=Math.abs(e[i+te+1])<r&&Math.abs(e[i+fe+1])<.01?1:0}}disable(e){let t=e*O;this.data.fill(0,t+se,t+se+3),this.data.fill(0,t+te,t+te+3),this.data.fill(0,t+Q,t+Q+3)}updateVelocities(){let{dt:e}=this.params,{pose:t,initial:i,velocity:r,prevVelocity:o}=this;for(let a=0;a<this.bodyCount;a++){let n=a*4;o[n]=r[n],o[n+1]=r[n+1],o[n+2]=r[n+2],this.dynamic[a]&&(r[n]=(t[n]-i[n])/e,r[n+1]=(t[n+1]-i[n+1])/e,r[n+2]=(t[n+2]-i[n+2])/e)}}pick(e,t){for(let i=this.bodyCount-1;i>=0;i--){let[r,o]=wi(-this.pose[i*4+2],e-this.pose[i*4],t-this.pose[i*4+1]);if(Math.abs(r)<=this.shape[i*4]*.5&&Math.abs(o)<=this.shape[i*4+1]*.5)return{body:i,local:[r,o]}}return null}kineticEnergy(){let e=0;for(let t=0;t<this.bodyCount;t++){if(!this.dynamic[t])continue;let i=t*4,r=this.velocity;e+=.5*this.shape[i+2]*(r[i]*r[i]+r[i+1]*r[i+1])+.5*this.shape[i+3]*r[i+2]*r[i+2]}return e}maxJointError(){let e=0,t=new Float64Array(3);for(let i=0;i<this.jointCount;i++){if(this.info[i*j]!==Bi)continue;let r=i*O;this.jointC(this.info[i*j+1],this.info[i*j+2],r,this.pose,this.data,0,t);for(let o=0;o<2;o++)this.data[r+Q+o]===1/0&&(e=Math.max(e,Math.abs(t[o])))}return e}isContactPairIgnored(e,t){return wn(this.noCollide,this.noCollideCount,Mr(e,t))}};function wi(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function zc(s,e,t,i){let r=Math.cos(s[e*4+2]),o=Math.sin(s[e*4+2]);return[r*t-o*i+s[e*4],o*t+r*i+s[e*4+1]]}var ps=`
|
|
3821
3907
|
${Ut}
|
|
3822
3908
|
|
|
3823
3909
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
@@ -4657,7 +4743,7 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
4657
4743
|
if (b.shape.z > 0.0) { b.vel = vec4f((b.pose.xyz - b.initial.xyz) / params.dt, 0.0); }
|
|
4658
4744
|
bodies[i] = b;
|
|
4659
4745
|
}
|
|
4660
|
-
`;var Ro=s=>s===1/0?3e38:s===-1/0?-3e38:s,bt=s=>Math.ceil(s/64),zc=s=>2**Math.ceil(Math.log2(Math.max(s,2))),Nf=s=>Math.max(s,1)*24*4,qf=4,yt=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,Oo=class{device;params=So();topology;bodyCount=0;bodyCapacity;jointCount=0;jointCapacity=0;pairCapacity=0;contactCapacity=0;colorCap=12;splitPasses=!1;primalMode="bucket";shrinkVotes=0;get colorHistOffset(){return 3*this.bodyCapacity+65}colorRounds;bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;counterBuffer;argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;staticsDirty=!0;layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){if(this.device=e,this.topology=t,Object.assign(this.params,t.params),this.bodyCount=t.bodyCount,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/96):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies",size:Nf(this.bodyCapacity),usage:yt()});let r=this.bodyCapacity;this.tableSize=zc(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:yt()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:yt()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:yt()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:yt()}),this.paramsBuffer=e.createBuffer({label:"params",size:128,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(r).fill(255));let o=2*Se.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:o}),resolve:e.createBuffer({size:o*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:o*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(),this.gridScan=new tt(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new tt(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1),this.jointCount=t.jointCount,this.allocateJoints(t.jointCount+256),this.uploadJoints(0,t.jointCount),this.allocateContacts(Math.max(4096,4*r)),this.uploadBodies()}createLayouts(){let e=this.device,t=(n,a)=>e.createBindGroupLayout({label:n,entries:a.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",o="uniform";return{broad:t("broadphase",[o,i,r,r,r,i,i]),contacts:t("contacts",[o,i,i,r,i,r,r]),topo:t("topology",[o,i,i,i,i,r,r,r]),solve:t("solve",[o,r,r,i,r,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args",[o,i,i,r])}}createPipelines(){let e=this.device,t=(r,o,n)=>{let a=e.createShaderModule({code:r}),l=e.createPipelineLayout({bindGroupLayouts:o});for(let c of n)this.pipes[c]=e.createComputePipeline({label:c,layout:l,compute:{module:a,entryPoint:c}})},i=this.layouts;t(ps,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(ms,[i.contacts],["hashInsert","narrowphase"]),t(qr,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(gs,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(Fs,[i.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints",size:Math.max(e,1)*36*4,usage:yt()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:yt()});if(this.jointBuffer){let o=t.createCommandEncoder();o.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*36*4),o.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([o.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t=2*e){let i=this.device,r=i.limits.maxStorageBufferBindingSize,o=Math.min(e,Math.floor(r/80),Math.floor(r/16)),n=Math.min(t,Math.floor(r/8)),a=this.contactBuffers,l=this.contactCapacity;for(let c of[this.pairBuffer,this.tableBuffer])c?.destroy();if(this.contactCapacity=o,this.pairCapacity=n,this.hashSize=zc(4*o),this.pairBuffer=i.createBuffer({label:"pairs",size:n*8,usage:yt()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:yt()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:o*20*4,usage:yt()})),a){let c=i.createCommandEncoder(),u=Math.min(l,o)*20*4;a.forEach((f,d)=>c.copyBufferToBuffer(f,0,this.contactBuffers[d],0,u)),i.queue.submit([c.finish()]),a.forEach(f=>f.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.contactCapacity))*4,usage:yt()}),this.adjScan?.destroy(),this.adjScan=new tt(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:yt()});let i=(l,c)=>e.createBindGroup({layout:l,entries:c.map((u,f)=>({binding:f,resource:{buffer:u}}))}),r=this.paramsBuffer,[o,n]=this.contactBuffers,a=this.adjBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,o,n,this.tableBuffer,this.counterBuffer]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,n,o,this.tableBuffer,this.counterBuffer])],topo:[o,n].map(l=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,this.counterBuffer,a,this.colorBuffer])),solve:[o,n].map(l=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,a,this.colorBuffer,this.counterBuffer])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer])}}uploadBodies(e=0,t=this.topology.bodyCount-e){let i=this.topology,r=new Float32Array(Math.max(t,1)*24);for(let o=0;o<t;o++){let n=e+o,a=o*24;r.set(i.pose.subarray(n*4,n*4+3),a),r[a+3]=i.props[n*4],r.set(i.initial.subarray(n*4,n*4+4),a+4),r.set(i.inertial.subarray(n*4,n*4+4),a+8),r.set(i.velocity.subarray(n*4,n*4+4),a+12),r.set(i.prevVelocity.subarray(n*4,n*4+4),a+16),r.set(i.shape.subarray(n*4,n*4+4),a+20)}this.device.queue.writeBuffer(this.bodyBuffer,e*24*4,r),this.staticsDirty=!0}uploadJoints(e,t){if(t===0)return;let i=this.topology,r=new Float32Array(t*36);for(let o=0;o<t;o++){let n=(e+o)*O,a=o*36;for(let l=0;l<3;l++)r[a+0+l]=i.data[n+se+l],r[a+4+l]=i.data[n+te+l],r[a+8+l]=Ro(i.data[n+Q+l]),r[a+12+l]=Ro(i.data[n+_e+l]),r[a+16+l]=Ro(i.data[n+Ae+l]),r[a+20+l]=Ro(i.data[n+Pt+l]),r[a+24+l]=i.data[n+fe+l];r[a+28]=i.data[n+N],r[a+28+1]=i.data[n+N+1],r[a+28+2]=i.data[n+Y],r[a+28+3]=i.data[n+Y+1],r[a+32]=i.data[n+re],r[a+32+1]=i.data[n+be]}this.device.queue.writeBuffer(this.jointBuffer,e*36*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,i.info,e*j,t*j)}uploadStatics(){let e=this.topology,t=this.bodyCount,i=Float64Array.from({length:t},(f,d)=>e.props[d*4+1]).sort(),r=t>0?i[t>>1]:1,o=0;for(let f=0;f<t;f++)i[f]<=qf*r&&(o=Math.max(o,i[f]));let n=1/0;for(let f=0;f<t;f++)i[f]>o&&(n=Math.min(n,i[f]));let a=n===1/0?o*1.5:(o+n)/2;this.maxSmallRadius=a,this.cellSize=Math.max(2*o*(1+1e-4),.001);let l=[];for(let f=0;f<t;f++)e.props[f*4+1]>a&&l.push(f);let c=[];for(let f=0;f<e.jointCount;f++){let d=e.info[f*j+1],p=e.info[f*j+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),f])}for(let f of e.ignoredPairs()){let d=Math.floor(f/2097152);c.push([d,f-d*2097152,4294967295])}c.sort((f,d)=>f[0]-d[0]||f[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let u=new Uint32Array(Math.max(l.length+3*c.length,4));u.set(l,0),c.forEach((f,d)=>u.set(f,l.length+3*d)),this.staticBuffer.size<u.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:u.byteLength,usage:yt()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,u),this.staticsDirty=!1}seedContactsFrom(e){let t=e.contactCount;if(t>this.contactCapacity)throw new Error("seedContactsFrom: too many contacts");let i=new ArrayBuffer(Math.max(t,1)*20*4),r=new Uint32Array(i),o=new Float32Array(i);for(let l=0;l<t;l++){let c=e.jointCount+l,u=c*O,f=l*20;r[f]=e.info[c*j+1],r[f+1]=e.info[c*j+2],r[f+2]=e.info[c*j+3],r[f+3]=e.data[u+Wi]?1:0,o.set([e.data[u+se],e.data[u+se+1],e.data[u+te],e.data[u+te+1]],f+4),o.set([e.data[u+N],e.data[u+N+1],e.data[u+Y],e.data[u+Y+1]],f+8),o.set([e.data[u+fe],e.data[u+fe+1],e.data[u+be],e.data[u+Ue]],f+12),o[f+16]=e.data[u+re]}this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,i);let n=new Uint32Array(16);n[1]=t,this.device.queue.writeBuffer(this.counterBuffer,0,n);let a=Uint32Array.from({length:this.bodyCount},(l,c)=>(e.coloring.colors[c]??-1)<0?255:e.coloring.colors[c]);this.device.queue.writeBuffer(this.colorBuffer,0,a)}addBody(e,t,i,r,o){if(this.bodyCount>=this.bodyCapacity)return-1;let n=this.topology.addBody(e,t,i,r,o);return this.bodyCount=this.topology.bodyCount,this.uploadBodies(n,1),this.device.queue.writeBuffer(this.colorBuffer,n*4,new Uint32Array([255])),this.adjScan.destroy(),this.adjScan=new tt(this.device,this.adjBuffer,0,this.bodyCount+1),n}appendJoint(e,t,i,r,o,n=1/0){let a=this.topology;if(a.contactCount>0)throw new Error("appendJoint: the CPU mirror must not hold contacts");let l=a.addJoint(e,t,i,r,o,n).slot;return l>=this.jointCapacity&&this.allocateJoints(Math.max(l+1,this.jointCapacity*2)),this.jointCount=l+1,this.uploadJoints(l,1),e>=0&&(this.staticsDirty=!0),l}setWorldAnchor(e,t,i){this.device.queue.writeBuffer(this.jointBuffer,(e*36+28)*4,new Float32Array([t,i]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*36+0)*4,new Float32Array(12)),this.topology.data.fill(0,e*O+Q,e*O+Q+3)}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(e,t){let i=this.params,r=this.bodyCapacity,o=new ArrayBuffer(128),n=new Float32Array(o),a=new Uint32Array(o);n[0]=i.dt,n[1]=i.gravity,n[2]=i.beta,n[3]=i.gamma,n[4]=e,n[5]=i.vbdStiffness,a[6]=(i.vbd?1:0)|(i.stiffnessRescale?2:0)|(t?4:0)|(i.matchNearest?8:0),a[7]=this.bodyCount,a[8]=this.jointCount,a[9]=this.colorCap,n[10]=this.cellSize,a[11]=this.tableSize-1,n[12]=this.maxSmallRadius,a[13]=this.largeCount,a[14]=this.noCollideCount,a[15]=this.pairCapacity,a[16]=this.contactCapacity,a[17]=this.hashSize-1,a[18]=r+1,a[19]=2*r+1,a[20]=this.tableSize+1,a[21]=2*this.tableSize+1,a[22]=2*this.tableSize+1+r,a[23]=r,a[24]=this.colorHistOffset,a[25]=2*r,a[26]=this.colorGroups,a[27]=2*r+65,a[28]=this.colorRounds,this.device.queue.writeBuffer(this.paramsBuffer,0,o)}writePassConstants(e,t){let i=this.colorCap+1,r=e*i;(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let o=new ArrayBuffer(r*256),n=new Uint32Array(o),a=new Float32Array(o);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let u=(l*i+c)*256/4;n[u]=c,a[u+1]=t(l)}this.device.queue.writeBuffer(this.passBuffer,0,o)}step(){this.staticsDirty&&this.uploadStatics();let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha,r=e.iterations+(t?1:0);this.writeParams(i,t),this.writePassConstants(r,x=>t?x<e.iterations?1:0:i);let o=this.parity,n=this.bodyCapacity,a=this.bodyCount,l=this.jointCount,c=this.device.createCommandEncoder({label:"avbd2d step"});c.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),c.clearBuffer(this.tableBuffer),c.clearBuffer(this.adjBuffer,0,(2*n+1)*4);let u=this.timingCallback!==null&&this.timing!==null,f,d=0,p=u||this.splitPasses,m=()=>{f&&!p||(f?.end(),f=c.beginComputePass({label:p?Se[d]:"avbd2d step",timestampWrites:u?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),d++)},h=this.groups,g=(x,w,B)=>{B<=0||(f.setPipeline(this.pipes[x]),f.setBindGroup(0,w),f.dispatchWorkgroups(B))},b=(x,w,B)=>{f.setPipeline(this.pipes[x]),f.setBindGroup(0,w),f.dispatchWorkgroupsIndirect(this.argsBuffer,B*4)};m(),g("beginFrame",h.broad,1),g("argsPrev",h.args,1),b("hashInsert",h.contacts[o],6),g("gridCount",h.broad,bt(a)),this.gridScan.encode(f),g("gridScatter",h.broad,bt(a)),g("findPairs",h.broad,bt(a)),g("argsPairs",h.args,1),b("narrowphase",h.contacts[o],0),g("argsContacts",h.args,1),m(),g("degreeJoints",h.topo[o],bt(l)),b("degreeContacts",h.topo[o],3),this.adjScan.encode(f),g("fillJoints",h.topo[o],bt(l)),b("fillContacts",h.topo[o],3),g("sortAdjacency",h.topo[o],bt(a)),m(),g("colorCompact",h.topo[o],bt(a)),g("colorMark",h.topo[o],bt(a));for(let x=0;x<this.colorRounds;x++)g(x%2===0?"colorRoundAB":"colorRoundBA",h.topo[o],bt(a));g("colorCount",h.topo[o],this.colorGroups),this.colorHistScan.encode(f),g("colorStarts",h.topo[o],1),g("argsColors",h.args,1),g("colorScatter",h.topo[o],this.colorGroups),m();let y=this.colorCap+1,A=x=>f.setBindGroup(1,this.passGroup,[x*256]);f.setBindGroup(0,h.solve[o]),A(0),l>0&&(f.setPipeline(this.pipes.warmStartJoints),f.dispatchWorkgroups(bt(l))),f.setPipeline(this.pipes.warmStartBodies),f.dispatchWorkgroups(bt(a));for(let x=0;x<r;x++){let w=this.primalMode==="scan";f.setPipeline(w?this.pipes.primalScan:this.pipes.primal);for(let B=0;B<this.colorCap;B++)A(x*y+B),w?f.dispatchWorkgroups(bt(a)):f.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);x<e.iterations&&(A(x*y+this.colorCap),f.setPipeline(this.pipes.dual),f.dispatchWorkgroupsIndirect(this.argsBuffer,36)),x===e.iterations-1&&(f.setPipeline(this.pipes.updateVelocities),f.dispatchWorkgroups(bt(a)))}t&&(f.setPipeline(this.pipes.refreshStick),f.dispatchWorkgroupsIndirect(this.argsBuffer,12)),f.end();let v=2*Se.length;if(u){let{querySet:x,resolve:w,read:B}=this.timing;c.resolveQuerySet(x,0,v,w,0),c.copyBufferToBuffer(w,0,B,0,v*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-o,u){let x=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let w=this.timing.read;w.mapAsync(GPUMapMode.READ).then(()=>{let B=new BigUint64Array(w.getMappedRange()).slice();if(w.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let L=(I,E)=>Number(B[E]-B[I])/1e6,M={total:L(0,v-1)};Se.forEach((I,E)=>M[I]=L(2*E,2*E+1)),x(M)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*24*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*36*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],overflow:e[3],clashes:e[4],colors:e[5]}}async readContacts(){let{contacts:e}=await this.readCounters(),t=Math.min(e,this.contactCapacity);return this.read(this.contactBuffers[1-this.parity],t*20*4)}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}async readColors(){return new Uint32Array(await this.read(this.colorBuffer,this.bodyCount*4))}async readAdjacency(){let e=new Uint32Array(await this.read(this.adjBuffer,this.adjBuffer.size)),t=e.slice(0,this.bodyCount+1),i=2*this.bodyCapacity+1;return{start:t,list:e.slice(i,i+t[this.bodyCount])}}adapt(e){let t=e.colors;e.clashes>0||t>=this.colorCap-1?(this.colorCap=Math.min(64,Math.max(t+4,this.colorCap+4)),this.shrinkVotes=0):t+2<this.colorCap?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,Math.max(4,t+2)),this.shrinkVotes=0):this.shrinkVotes=0;let i=(e.overflow&2)!==0||e.contacts>.8*this.contactCapacity,r=(e.overflow&1)!==0||e.pairs>.8*this.pairCapacity,o=this.contactCapacity*(i?2:1),n=this.pairCapacity*(r?2:1);(i||r)&&this.capacityCanGrow(o,n)&&this.allocateContacts(o,n)}capacityCanGrow(e,t){let i=this.device.limits.maxStorageBufferBindingSize,r=Math.min(Math.floor(i/80),Math.floor(i/16));return Math.min(e,r)>this.contactCapacity||Math.min(t,Math.floor(i/8))>this.pairCapacity}async readStats(e){let[t,i]=await Promise.all([e??this.readBodies(),this.readJoints()]),r=0;for(let c=0;c<this.bodyCount;c++){let u=c*24,f=t[u+22],d=t[u+12],p=t[u+13],m=t[u+14];f>0&&(r+=.5*f*(d*d+p*p)+.5*t[u+23]*m*m)}let o=0,n=0,a=this.topology.info,l=(c,u,f)=>{let d=c*24,p=Math.cos(t[d+2]),m=Math.sin(t[d+2]);return[p*u-m*f+t[d],m*u+p*f+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(a[c*j]!==Bi)continue;let u=c*36;if(i[u+8]===0&&i[u+8+1]===0&&i[u+8+2]===0)continue;n++;let f=a[c*j+1],d=a[c*j+2],p=f>=0?l(f,i[u+28],i[u+28+1]):[i[u+28],i[u+28+1]],m=l(d,i[u+28+2],i[u+28+3]);for(let h=0;h<2;h++)i[u+8+h]>=1e30&&(o=Math.max(o,Math.abs(p[h]-m[h])))}return{kineticEnergy:r,maxJointError:o,joints:n}}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),o}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.passBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var Vf=`${Ut}
|
|
4746
|
+
`;var Ro=s=>s===1/0?3e38:s===-1/0?-3e38:s,bt=s=>Math.ceil(s/64),Oc=s=>2**Math.ceil(Math.log2(Math.max(s,2))),Vf=s=>Math.max(s,1)*24*4,Wf=4,yt=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,Oo=class{device;params=So();topology;bodyCount=0;bodyCapacity;jointCount=0;jointCapacity=0;pairCapacity=0;contactCapacity=0;colorCap=12;splitPasses=!1;primalMode="bucket";shrinkVotes=0;get colorHistOffset(){return 3*this.bodyCapacity+65}colorRounds;bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;counterBuffer;argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;staticsDirty=!0;layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){if(this.device=e,this.topology=t,Object.assign(this.params,t.params),this.bodyCount=t.bodyCount,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/96):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies",size:Vf(this.bodyCapacity),usage:yt()});let r=this.bodyCapacity;this.tableSize=Oc(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:yt()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:yt()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:yt()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:yt()}),this.paramsBuffer=e.createBuffer({label:"params",size:128,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(r).fill(255));let o=2*Se.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:o}),resolve:e.createBuffer({size:o*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:o*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(),this.gridScan=new tt(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new tt(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1),this.jointCount=t.jointCount,this.allocateJoints(t.jointCount+256),this.uploadJoints(0,t.jointCount),this.allocateContacts(Math.max(4096,4*r)),this.uploadBodies()}createLayouts(){let e=this.device,t=(a,n)=>e.createBindGroupLayout({label:a,entries:n.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",o="uniform";return{broad:t("broadphase",[o,i,r,r,r,i,i]),contacts:t("contacts",[o,i,i,r,i,r,r]),topo:t("topology",[o,i,i,i,i,r,r,r]),solve:t("solve",[o,r,r,i,r,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args",[o,i,i,r])}}createPipelines(){let e=this.device,t=(r,o,a)=>{let n=e.createShaderModule({code:r}),l=e.createPipelineLayout({bindGroupLayouts:o});for(let c of a)this.pipes[c]=e.createComputePipeline({label:c,layout:l,compute:{module:n,entryPoint:c}})},i=this.layouts;t(ps,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(ms,[i.contacts],["hashInsert","narrowphase"]),t(qr,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(gs,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(Fs,[i.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints",size:Math.max(e,1)*36*4,usage:yt()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:yt()});if(this.jointBuffer){let o=t.createCommandEncoder();o.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*36*4),o.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([o.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t=2*e){let i=this.device,r=i.limits.maxStorageBufferBindingSize,o=Math.min(e,Math.floor(r/80),Math.floor(r/16)),a=Math.min(t,Math.floor(r/8)),n=this.contactBuffers,l=this.contactCapacity;for(let c of[this.pairBuffer,this.tableBuffer])c?.destroy();if(this.contactCapacity=o,this.pairCapacity=a,this.hashSize=Oc(4*o),this.pairBuffer=i.createBuffer({label:"pairs",size:a*8,usage:yt()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:yt()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:o*20*4,usage:yt()})),n){let c=i.createCommandEncoder(),u=Math.min(l,o)*20*4;n.forEach((f,d)=>c.copyBufferToBuffer(f,0,this.contactBuffers[d],0,u)),i.queue.submit([c.finish()]),n.forEach(f=>f.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.contactCapacity))*4,usage:yt()}),this.adjScan?.destroy(),this.adjScan=new tt(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:yt()});let i=(l,c)=>e.createBindGroup({layout:l,entries:c.map((u,f)=>({binding:f,resource:{buffer:u}}))}),r=this.paramsBuffer,[o,a]=this.contactBuffers,n=this.adjBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,o,a,this.tableBuffer,this.counterBuffer]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,o,this.tableBuffer,this.counterBuffer])],topo:[o,a].map(l=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,this.counterBuffer,n,this.colorBuffer])),solve:[o,a].map(l=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,n,this.colorBuffer,this.counterBuffer])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer])}}uploadBodies(e=0,t=this.topology.bodyCount-e){let i=this.topology,r=new Float32Array(Math.max(t,1)*24);for(let o=0;o<t;o++){let a=e+o,n=o*24;r.set(i.pose.subarray(a*4,a*4+3),n),r[n+3]=i.props[a*4],r.set(i.initial.subarray(a*4,a*4+4),n+4),r.set(i.inertial.subarray(a*4,a*4+4),n+8),r.set(i.velocity.subarray(a*4,a*4+4),n+12),r.set(i.prevVelocity.subarray(a*4,a*4+4),n+16),r.set(i.shape.subarray(a*4,a*4+4),n+20)}this.device.queue.writeBuffer(this.bodyBuffer,e*24*4,r),this.staticsDirty=!0}uploadJoints(e,t){if(t===0)return;let i=this.topology,r=new Float32Array(t*36);for(let o=0;o<t;o++){let a=(e+o)*O,n=o*36;for(let l=0;l<3;l++)r[n+0+l]=i.data[a+se+l],r[n+4+l]=i.data[a+te+l],r[n+8+l]=Ro(i.data[a+Q+l]),r[n+12+l]=Ro(i.data[a+_e+l]),r[n+16+l]=Ro(i.data[a+Ae+l]),r[n+20+l]=Ro(i.data[a+Pt+l]),r[n+24+l]=i.data[a+fe+l];r[n+28]=i.data[a+N],r[n+28+1]=i.data[a+N+1],r[n+28+2]=i.data[a+Y],r[n+28+3]=i.data[a+Y+1],r[n+32]=i.data[a+re],r[n+32+1]=i.data[a+be]}this.device.queue.writeBuffer(this.jointBuffer,e*36*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,i.info,e*j,t*j)}uploadStatics(){let e=this.topology,t=this.bodyCount,i=Float64Array.from({length:t},(f,d)=>e.props[d*4+1]).sort(),r=t>0?i[t>>1]:1,o=0;for(let f=0;f<t;f++)i[f]<=Wf*r&&(o=Math.max(o,i[f]));let a=1/0;for(let f=0;f<t;f++)i[f]>o&&(a=Math.min(a,i[f]));let n=a===1/0?o*1.5:(o+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*o*(1+1e-4),.001);let l=[];for(let f=0;f<t;f++)e.props[f*4+1]>n&&l.push(f);let c=[];for(let f=0;f<e.jointCount;f++){let d=e.info[f*j+1],p=e.info[f*j+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),f])}for(let f of e.ignoredPairs()){let d=Math.floor(f/2097152);c.push([d,f-d*2097152,4294967295])}c.sort((f,d)=>f[0]-d[0]||f[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let u=new Uint32Array(Math.max(l.length+3*c.length,4));u.set(l,0),c.forEach((f,d)=>u.set(f,l.length+3*d)),this.staticBuffer.size<u.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:u.byteLength,usage:yt()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,u),this.staticsDirty=!1}seedContactsFrom(e){let t=e.contactCount;if(t>this.contactCapacity)throw new Error("seedContactsFrom: too many contacts");let i=new ArrayBuffer(Math.max(t,1)*20*4),r=new Uint32Array(i),o=new Float32Array(i);for(let l=0;l<t;l++){let c=e.jointCount+l,u=c*O,f=l*20;r[f]=e.info[c*j+1],r[f+1]=e.info[c*j+2],r[f+2]=e.info[c*j+3],r[f+3]=e.data[u+Wi]?1:0,o.set([e.data[u+se],e.data[u+se+1],e.data[u+te],e.data[u+te+1]],f+4),o.set([e.data[u+N],e.data[u+N+1],e.data[u+Y],e.data[u+Y+1]],f+8),o.set([e.data[u+fe],e.data[u+fe+1],e.data[u+be],e.data[u+Ue]],f+12),o[f+16]=e.data[u+re]}this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,i);let a=new Uint32Array(16);a[1]=t,this.device.queue.writeBuffer(this.counterBuffer,0,a);let n=Uint32Array.from({length:this.bodyCount},(l,c)=>(e.coloring.colors[c]??-1)<0?255:e.coloring.colors[c]);this.device.queue.writeBuffer(this.colorBuffer,0,n)}addBody(e,t,i,r,o){if(this.bodyCount>=this.bodyCapacity)return-1;let a=this.topology.addBody(e,t,i,r,o);return this.bodyCount=this.topology.bodyCount,this.uploadBodies(a,1),this.device.queue.writeBuffer(this.colorBuffer,a*4,new Uint32Array([255])),this.adjScan.destroy(),this.adjScan=new tt(this.device,this.adjBuffer,0,this.bodyCount+1),a}appendJoint(e,t,i,r,o,a=1/0){let n=this.topology;if(n.contactCount>0)throw new Error("appendJoint: the CPU mirror must not hold contacts");let l=n.addJoint(e,t,i,r,o,a).slot;return l>=this.jointCapacity&&this.allocateJoints(Math.max(l+1,this.jointCapacity*2)),this.jointCount=l+1,this.uploadJoints(l,1),e>=0&&(this.staticsDirty=!0),l}setWorldAnchor(e,t,i){this.device.queue.writeBuffer(this.jointBuffer,(e*36+28)*4,new Float32Array([t,i]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*36+0)*4,new Float32Array(12)),this.topology.data.fill(0,e*O+Q,e*O+Q+3)}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(e,t){let i=this.params,r=this.bodyCapacity,o=new ArrayBuffer(128),a=new Float32Array(o),n=new Uint32Array(o);a[0]=i.dt,a[1]=i.gravity,a[2]=i.beta,a[3]=i.gamma,a[4]=e,a[5]=i.vbdStiffness,n[6]=(i.vbd?1:0)|(i.stiffnessRescale?2:0)|(t?4:0)|(i.matchNearest?8:0),n[7]=this.bodyCount,n[8]=this.jointCount,n[9]=this.colorCap,a[10]=this.cellSize,n[11]=this.tableSize-1,a[12]=this.maxSmallRadius,n[13]=this.largeCount,n[14]=this.noCollideCount,n[15]=this.pairCapacity,n[16]=this.contactCapacity,n[17]=this.hashSize-1,n[18]=r+1,n[19]=2*r+1,n[20]=this.tableSize+1,n[21]=2*this.tableSize+1,n[22]=2*this.tableSize+1+r,n[23]=r,n[24]=this.colorHistOffset,n[25]=2*r,n[26]=this.colorGroups,n[27]=2*r+65,n[28]=this.colorRounds,this.device.queue.writeBuffer(this.paramsBuffer,0,o)}writePassConstants(e,t){let i=this.colorCap+1,r=e*i;(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let o=new ArrayBuffer(r*256),a=new Uint32Array(o),n=new Float32Array(o);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let u=(l*i+c)*256/4;a[u]=c,n[u+1]=t(l)}this.device.queue.writeBuffer(this.passBuffer,0,o)}step(){this.staticsDirty&&this.uploadStatics();let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha,r=e.iterations+(t?1:0);this.writeParams(i,t),this.writePassConstants(r,x=>t?x<e.iterations?1:0:i);let o=this.parity,a=this.bodyCapacity,n=this.bodyCount,l=this.jointCount,c=this.device.createCommandEncoder({label:"avbd2d step"});c.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),c.clearBuffer(this.tableBuffer),c.clearBuffer(this.adjBuffer,0,(2*a+1)*4);let u=this.timingCallback!==null&&this.timing!==null,f,d=0,p=u||this.splitPasses,m=()=>{f&&!p||(f?.end(),f=c.beginComputePass({label:p?Se[d]:"avbd2d step",timestampWrites:u?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),d++)},h=this.groups,g=(x,w,B)=>{B<=0||(f.setPipeline(this.pipes[x]),f.setBindGroup(0,w),f.dispatchWorkgroups(B))},b=(x,w,B)=>{f.setPipeline(this.pipes[x]),f.setBindGroup(0,w),f.dispatchWorkgroupsIndirect(this.argsBuffer,B*4)};m(),g("beginFrame",h.broad,1),g("argsPrev",h.args,1),b("hashInsert",h.contacts[o],6),g("gridCount",h.broad,bt(n)),this.gridScan.encode(f),g("gridScatter",h.broad,bt(n)),g("findPairs",h.broad,bt(n)),g("argsPairs",h.args,1),b("narrowphase",h.contacts[o],0),g("argsContacts",h.args,1),m(),g("degreeJoints",h.topo[o],bt(l)),b("degreeContacts",h.topo[o],3),this.adjScan.encode(f),g("fillJoints",h.topo[o],bt(l)),b("fillContacts",h.topo[o],3),g("sortAdjacency",h.topo[o],bt(n)),m(),g("colorCompact",h.topo[o],bt(n)),g("colorMark",h.topo[o],bt(n));for(let x=0;x<this.colorRounds;x++)g(x%2===0?"colorRoundAB":"colorRoundBA",h.topo[o],bt(n));g("colorCount",h.topo[o],this.colorGroups),this.colorHistScan.encode(f),g("colorStarts",h.topo[o],1),g("argsColors",h.args,1),g("colorScatter",h.topo[o],this.colorGroups),m();let y=this.colorCap+1,A=x=>f.setBindGroup(1,this.passGroup,[x*256]);f.setBindGroup(0,h.solve[o]),A(0),l>0&&(f.setPipeline(this.pipes.warmStartJoints),f.dispatchWorkgroups(bt(l))),f.setPipeline(this.pipes.warmStartBodies),f.dispatchWorkgroups(bt(n));for(let x=0;x<r;x++){let w=this.primalMode==="scan";f.setPipeline(w?this.pipes.primalScan:this.pipes.primal);for(let B=0;B<this.colorCap;B++)A(x*y+B),w?f.dispatchWorkgroups(bt(n)):f.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);x<e.iterations&&(A(x*y+this.colorCap),f.setPipeline(this.pipes.dual),f.dispatchWorkgroupsIndirect(this.argsBuffer,36)),x===e.iterations-1&&(f.setPipeline(this.pipes.updateVelocities),f.dispatchWorkgroups(bt(n)))}t&&(f.setPipeline(this.pipes.refreshStick),f.dispatchWorkgroupsIndirect(this.argsBuffer,12)),f.end();let v=2*Se.length;if(u){let{querySet:x,resolve:w,read:B}=this.timing;c.resolveQuerySet(x,0,v,w,0),c.copyBufferToBuffer(w,0,B,0,v*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-o,u){let x=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let w=this.timing.read;w.mapAsync(GPUMapMode.READ).then(()=>{let B=new BigUint64Array(w.getMappedRange()).slice();if(w.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let L=(I,E)=>Number(B[E]-B[I])/1e6,M={total:L(0,v-1)};Se.forEach((I,E)=>M[I]=L(2*E,2*E+1)),x(M)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*24*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*36*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],overflow:e[3],clashes:e[4],colors:e[5]}}async readContacts(){let{contacts:e}=await this.readCounters(),t=Math.min(e,this.contactCapacity);return this.read(this.contactBuffers[1-this.parity],t*20*4)}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}async readColors(){return new Uint32Array(await this.read(this.colorBuffer,this.bodyCount*4))}async readAdjacency(){let e=new Uint32Array(await this.read(this.adjBuffer,this.adjBuffer.size)),t=e.slice(0,this.bodyCount+1),i=2*this.bodyCapacity+1;return{start:t,list:e.slice(i,i+t[this.bodyCount])}}adapt(e){let t=e.colors;e.clashes>0||t>=this.colorCap-1?(this.colorCap=Math.min(64,Math.max(t+4,this.colorCap+4)),this.shrinkVotes=0):t+2<this.colorCap?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,Math.max(4,t+2)),this.shrinkVotes=0):this.shrinkVotes=0;let i=(e.overflow&2)!==0||e.contacts>.8*this.contactCapacity,r=(e.overflow&1)!==0||e.pairs>.8*this.pairCapacity,o=this.contactCapacity*(i?2:1),a=this.pairCapacity*(r?2:1);(i||r)&&this.capacityCanGrow(o,a)&&this.allocateContacts(o,a)}capacityCanGrow(e,t){let i=this.device.limits.maxStorageBufferBindingSize,r=Math.min(Math.floor(i/80),Math.floor(i/16));return Math.min(e,r)>this.contactCapacity||Math.min(t,Math.floor(i/8))>this.pairCapacity}async readStats(e){let[t,i]=await Promise.all([e??this.readBodies(),this.readJoints()]),r=0;for(let c=0;c<this.bodyCount;c++){let u=c*24,f=t[u+22],d=t[u+12],p=t[u+13],m=t[u+14];f>0&&(r+=.5*f*(d*d+p*p)+.5*t[u+23]*m*m)}let o=0,a=0,n=this.topology.info,l=(c,u,f)=>{let d=c*24,p=Math.cos(t[d+2]),m=Math.sin(t[d+2]);return[p*u-m*f+t[d],m*u+p*f+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(n[c*j]!==Bi)continue;let u=c*36;if(i[u+8]===0&&i[u+8+1]===0&&i[u+8+2]===0)continue;a++;let f=n[c*j+1],d=n[c*j+2],p=f>=0?l(f,i[u+28],i[u+28+1]):[i[u+28],i[u+28+1]],m=l(d,i[u+28+2],i[u+28+3]);for(let h=0;h<2;h++)i[u+8+h]>=1e30&&(o=Math.max(o,Math.abs(p[h]-m[h])))}return{kineticEnergy:r,maxJointError:o,joints:a}}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),o}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.passBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var Hf=`${Ut}
|
|
4661
4747
|
struct Config { count:u32,joints:u32,capacity:u32,list:u32,threshold:f32,dt:f32,pad0:u32,pad1:u32 }
|
|
4662
4748
|
struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
|
|
4663
4749
|
struct Bounce { impulse:f32,delta:f32,reboundSpeed:f32,inverseMass:f32,rA:vec4f,rB:vec4f }
|
|
@@ -4703,9 +4789,9 @@ fn pointVelocity(i:u32,r:vec2f,incoming:bool)->vec2f{let v=select(velocities[i].
|
|
|
4703
4789
|
velocities[i].linear+=vec4f(dv,0.);
|
|
4704
4790
|
}
|
|
4705
4791
|
@compute @workgroup_size(64) fn writeBack(@builtin(global_invocation_id) id:vec3u){let i=id.x;if(i>=cfg.count||bodies[i].shape.z<=0.){return;}bodies[i].vel=velocities[i].linear;}
|
|
4706
|
-
`;var To=class extends Ar{constructor(e){super(e,{code:
|
|
4707
|
-
if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return s}function
|
|
4708
|
-
if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var
|
|
4792
|
+
`;var To=class extends Ar{constructor(e){super(e,{code:Hf,dimension:2})}};function Tc(s){for(let e of["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]){let t=`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {`;if(s.split(t).length!==2)throw Error(`Pinned 2D ${e} signature changed; review the step guard`);s=s.replace(t,`${t}
|
|
4793
|
+
if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return s}function Ln(s){let e="if (col == NO_COLOR) { col = params.colorCap - 1u; }";if(s.split(e).length!==2)throw Error("Pinned 2D color-count layout changed; review the capacity guard");return s.replace(e,`${e}
|
|
4794
|
+
if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var jc=`// Sleeping is scheduling metadata in velocity.w; physical mass and inertia stay intact.
|
|
4709
4795
|
struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,vel:vec4f,prev:vec4f,shape:vec4f}
|
|
4710
4796
|
struct Joint {pen:vec4f,lam:vec4f,stiff:vec4f,fmin:vec4f,fmax:vec4f,frac:vec4f,c0:vec4f,anchors:vec4f,param:vec4f}
|
|
4711
4797
|
struct Contact {ids:vec4u,pl:vec4f,anchors:vec4f,geo:vec4f,misc:vec4f}
|
|
@@ -4776,9 +4862,9 @@ fn rotate(angle:f32,p:vec2f)->vec2f{let c=cos(angle);let s=sin(angle);return vec
|
|
|
4776
4862
|
}
|
|
4777
4863
|
if(asleep(i)){atomicAdd(&globals[1],1u);}
|
|
4778
4864
|
}
|
|
4779
|
-
`;function Do(s={}){if(s!==!0&&(typeof s!="object"||s===null))throw Error("sleeping must be a boolean or options object");let e=s.speedThreshold??.03,t=s.timeThreshold??.5;if(![e,t].every(i=>Number.isFinite(i)&&i>0))throw Error("Sleep thresholds must be positive and finite");return{speedThreshold:e,timeThreshold:t}}function
|
|
4865
|
+
`;function Do(s={}){if(s!==!0&&(typeof s!="object"||s===null))throw Error("sleeping must be a boolean or options object");let e=s.speedThreshold??.03,t=s.timeThreshold??.5;if(![e,t].every(i=>Number.isFinite(i)&&i>0))throw Error("Sleep thresholds must be positive and finite");return{speedThreshold:e,timeThreshold:t}}function Dc(s){for(let e of["warmStartBodies","updateVelocities"])s=s.replace(`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {`,`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {
|
|
4780
4866
|
if(gid.x<params.bodyCount && bodies[gid.x].vel.w!=0.){return;}`);return s.replace("fn solveBody(i: u32) {",`fn solveBody(i: u32) {
|
|
4781
|
-
if(bodies[i].vel.w!=0.){return;}`)}function
|
|
4867
|
+
if(bodies[i].vel.w!=0.){return;}`)}function Gc(s){return s.replace("return i >= 0 && bodies[i].shape.z > 0.0;","return i >= 0 && bodies[i].shape.z > 0.0 && bodies[i].vel.w==0.;")}var jo=class{constructor(e,t){this.gpu=e,this.device=e.device,this.options=Do(t);let i=this.device,r=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.state=i.createBuffer({size:e.bodyCapacity*16,usage:r}),this.globals=i.createBuffer({size:8,usage:r}),this.params=i.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let o=i.createShaderModule({code:jc});this.layout=i.createBindGroupLayout({entries:["storage","storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage","uniform"].map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))});let a=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["clear","before","contactsWake","jointsWake","decide","applyWake","rest"].map(l=>[l,i.createComputePipeline({layout:a,compute:{module:o,entryPoint:l}})]));let n=new Uint32Array(e.bodyCapacity*4);for(let l=0;l<e.bodyCount;l++)n[l*4+1]=e.sleepEligible?.get(l)===!1?0:1;i.queue.writeBuffer(this.state,0,n),this.wakeRequested=!1}register(e,t=!0){let i=oe(this.gpu);for(let r=0;r<4;r++)i.uint(this.state,e*4+r,r===1?Number(t):0);this.wakeRequested=!0}group(e){let t=this.gpu,i=[t.bodyBuffer,this.state,t.infoBuffer,t.jointBuffer,t.contactBuffers[e],t.counterBuffer,this.globals,this.params],r=this.groups?.find(o=>o.buffers.every((a,n)=>a===i[n]));return r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((o,a)=>({binding:a,resource:{buffer:o}}))})},this.groups=[...(this.groups??[]).slice(-1),r]),r.group}dispatch(e,t,i){e.setBindGroup(0,this.group(t));for(let[r,o]of i)o&&(e.setPipeline(this.pipes[r]),e.dispatchWorkgroups(Math.ceil(o/64)))}run(e,t){let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();this.dispatch(r,1-this.gpu.parity,e),r.end(),t||this.device.queue.submit([i.finish()])}before(e){this.gpu.flushPropertyEdits(e);let t=this.gpu,i=this.paramData??=hi(8),{u:r,f:o}=i;r[0]=t.bodyCount,r[1]=t.jointCount,r[2]=t.contactCapacity,r[3]=Number(this.wakeRequested),o[4]=this.options.speedThreshold,o[5]=this.options.timeThreshold,o[6]=t.params.dt,o[7]=t.params.gravity,Qt(this.device,this.params,i),this.wakeRequested=!1,this.run([["clear",1],["before",t.bodyCount]],e)}encodeWake(e,t){let i=this.gpu;this.dispatch(e,t,[["contactsWake",i.contactCapacity],["jointsWake",i.jointCount],["decide",i.bodyCount],["applyWake",i.bodyCount]])}after(e){this.run([["rest",this.gpu.bodyCount]],e)}async readStats(){let e=new Uint32Array(await this.gpu.read(this.globals,8));return{sleeping:e[1],wakeRequested:this.wakeRequested||!!e[0]}}destroy(){this.gpu.propertyEdits?.forget(this.state),this.state.destroy(),this.globals.destroy(),this.params.destroy()}};var vt=s=>Math.ceil(s/64);function Fc(){this.staticsDirty&&this.uploadStatics();let s=this.params,e=s.postStabilize&&!s.vbd,t=s.vbd?0:s.alpha,i=s.iterations+(e?1:0);this.writeParams(t,e),this.writePassConstants(i,L=>e?L<s.iterations?1:0:t);let r=this.parity,o=this.bodyCapacity,a=this.bodyCount,n=this.jointCount,l=this.device.createCommandEncoder({label:"avbd2d step"});this.encodeStepPrelude?.(l),l.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),l.clearBuffer(this.tableBuffer),l.clearBuffer(this.adjBuffer,0,(2*o+1)*4);let c=this.timingCallback!==null&&this.timing!==null,u,f,d=0,p=c||this.splitPasses,m=()=>{u&&!p||(u?.end(),u=l.beginComputePass({label:p?Se[d]:"avbd2d step",timestampWrites:c?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),f=void 0,d++)},h=this.groups,g=L=>{f!==L&&(u.setBindGroup(0,L),f=L)},b=(L,M,I)=>{I<=0||(u.setPipeline(this.pipes[L]),g(M),u.dispatchWorkgroups(I))},y=(L,M,I)=>{u.setPipeline(this.pipes[L]),g(M),u.dispatchWorkgroupsIndirect(this.argsBuffer,I*4)};m(),b("beginFrame",h.broad,1),b("argsPrev",h.args,1),y("hashInsert",h.contacts[r],6),b("gridCount",h.broad,vt(a)),this.gridScan.encode(u),f=void 0,b("gridScatter",h.broad,vt(a)),b("findPairs",h.broad,vt(a)),b("argsPairs",h.args,1),y("narrowphase",h.contacts[r],0),b("argsContacts",h.args,1),this.sleeping?.encodeWake(u,r),f=void 0,m(),b("degreeJoints",h.topo[r],vt(n)),y("degreeContacts",h.topo[r],3),this.adjScan.encode(u),f=void 0,b("fillJoints",h.topo[r],vt(n)),y("fillContacts",h.topo[r],3),b("sortAdjacency",h.topo[r],vt(a)),m(),b("colorCompact",h.topo[r],vt(a)),b("colorMark",h.topo[r],vt(a));for(let L=0;L<this.colorRounds;L++)b(L%2===0?"colorRoundAB":"colorRoundBA",h.topo[r],vt(a));b("colorCount",h.topo[r],this.colorGroups),this.colorHistScan.encode(u),f=void 0,b("colorStarts",h.topo[r],1),b("argsColors",h.args,1),b("colorScatter",h.topo[r],this.colorGroups),m();let A=this.colorCap+1,v=this.passOffsets??=[0],x=-1,w=L=>{L!==x&&(v[0]=L*256,u.setBindGroup(1,this.passGroup,v),x=L)};u.setBindGroup(0,h.solve[r]),w(0),n>0&&(u.setPipeline(this.pipes.warmStartJoints),u.dispatchWorkgroups(vt(n))),u.setPipeline(this.pipes.warmStartBodies),u.dispatchWorkgroups(vt(a));for(let L=0;L<i;L++){let M=this.primalMode==="scan";u.setPipeline(M?this.pipes.primalScan:this.pipes.primal);for(let I=0;I<this.colorCap;I++)w(L*A+I),M?u.dispatchWorkgroups(vt(a)):u.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*I)*4);L<s.iterations&&(w(L*A+this.colorCap),u.setPipeline(this.pipes.dual),u.dispatchWorkgroupsIndirect(this.argsBuffer,36)),L===s.iterations-1&&(u.setPipeline(this.pipes.updateVelocities),u.dispatchWorkgroups(vt(a)))}e&&(u.setPipeline(this.pipes.refreshStick),u.dispatchWorkgroupsIndirect(this.argsBuffer,12)),u.end();let B=2*Se.length;if(c){let{querySet:L,resolve:M,read:I}=this.timing;l.resolveQuerySet(L,0,B,M,0),l.copyBufferToBuffer(M,0,I,0,B*8)}if(this.parity=1-r,this.encodeStepPostlude?.(l),this.device.queue.submit([l.finish()]),c){let L=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let M=this.timing.read;M.mapAsync(GPUMapMode.READ).then(()=>{let I=new BigUint64Array(M.getMappedRange()).slice();if(M.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let E=(P,C)=>Number(I[C]-I[P])/1e6,k={total:E(0,B-1)};Se.forEach((P,C)=>k[P]=E(2*C,2*C+1)),L(k)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}var Go=5;function ki({minAngle:s=-Math.PI,maxAngle:e=Math.PI}={}){if(!Number.isFinite(s)||!Number.isFinite(e)||s>e)throw Error("Angle limits require finite minAngle <= maxAngle, in radians");return{minAngle:s,maxAngle:e}}function vs(s,e,t){let i=e*O;s.info[e*j]=Go,s.data.set([1/0,1/0,0],i+Q),s.data.set([-1/0,0,0],i+_e),s.data.set([0,1/0,0],i+Ae),s.data[i+be]=t.minAngle,s.data[i+Ue]=t.maxAngle}function Uc(s){let e=s.topology;for(let t=0;t<e.jointCount;t++)e.info[t*j]===Go&&s.device.queue.writeBuffer(s.jointBuffer,(t*36+32)*4,new Float32Array([e.data[t*O+re],e.data[t*O+be],e.data[t*O+Ue],0]))}var ys=(s,e,t)=>{if(s.split(e).length!==2)throw Error("Pinned 2D angular layout changed");return s.replace(e,t)};function Nc(s){return s=ys(s,"case T_JOINT: { return 3u; }",`case T_JOINT: { return 3u; }
|
|
4782
4868
|
case 5: { return 2u; }`),s=ys(s,"fn jointRowsC(j: u32, alpha: f32) -> vec3f {",`
|
|
4783
4869
|
fn limitAngle2D(k:Joint,a:i32,b:i32)->f32 {
|
|
4784
4870
|
var angleA=0.;if(a>=0){angleA=bodies[a].pose.z;}
|
|
@@ -4801,7 +4887,7 @@ fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),s=ys(s,` if (t == T_JOINT) {
|
|
|
4801
4887
|
if(F.y!=0.){addRow(acc,vec3f(0.,0.,sg),vec3f(0.),C.y,k.pen.y,k.lam.y,k.stiff.y,k.fmin.y,k.fmax.y);}
|
|
4802
4888
|
return;
|
|
4803
4889
|
}`),s=ys(s," if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }",` if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }
|
|
4804
|
-
if(t==5){let angle=limitAngle2D(k,info[j].y,info[j].z);k.c0=vec4f(min(0.,angle-k.param.y),max(0.,angle-k.param.z),0.,0.);}`),s}var
|
|
4890
|
+
if(t==5){let angle=limitAngle2D(k,info[j].y,info[j].z);k.c0=vec4f(min(0.,angle-k.param.y),max(0.,angle-k.param.z),0.,0.);}`),s}var qc=`// Analytic circles and capsules, finite two-sided segments and solid half-space planes.
|
|
4805
4891
|
// Kind 4 stores bounding radius, vertex-record offset and vertex count.
|
|
4806
4892
|
@group(0) @binding(7) var<storage,read> geometry:array<vec4f>;
|
|
4807
4893
|
struct Face2D { a:vec2f, b:vec2f, feature:u32 }
|
|
@@ -4954,7 +5040,7 @@ fn collideShapes2D(a:u32,b:u32)->Collision {
|
|
|
4954
5040
|
}
|
|
4955
5041
|
out.count=count;return out;
|
|
4956
5042
|
}
|
|
4957
|
-
`;var
|
|
5043
|
+
`;var _n=new WeakMap,Ir=(s,e,t=!1)=>{if(!Number.isFinite(s)||(t?s<0:s<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return s},Fo=(s,e)=>{if(!s||s.length!==2||!Array.from(s).every(Number.isFinite))throw Error(`${e} requires two finite components`);return Array.from(s)},ei=s=>_n.get(s);function Uo(s){let e=Array.from(s??[]),t=typeof e[0]=="number"?e:e.flatMap(w=>Fo(w,"Hull point"));if(t.length<6||t.length%2||!t.every(Number.isFinite))throw Error("A 2D hull requires finite xy pairs spanning an area");let i=[];for(let w=0;w<t.length;w+=2)i.push([t[w],t[w+1]]);i.sort((w,B)=>w[0]-B[0]||w[1]-B[1]);let r=i.filter((w,B)=>!B||w[0]!==i[B-1][0]||w[1]!==i[B-1][1]),o=(w,B,L)=>(B[0]-w[0])*(L[1]-w[1])-(B[1]-w[1])*(L[0]-w[0]),a=w=>{let B=[];for(let L of w){for(;B.length>1&&o(B.at(-2),B.at(-1),L)<=0;)B.pop();B.push(L)}return B.slice(0,-1)},n=[...a(r),...a([...r].reverse())];if(n.length<3||n.length>32)throw Error("A 2D hull needs 3\u201332 boundary vertices spanning an area");let l=n[0],c=n.map(w=>[w[0]-l[0],w[1]-l[1]]),u=0,f=0,d=0;for(let w=0;w<c.length;w++){let B=c[w],L=c[(w+1)%c.length],M=B[0]*L[1]-L[0]*B[1];u+=M,f+=(B[0]+L[0])*M,d+=(B[1]+L[1])*M}if(!(u>0)||!Number.isFinite(u))throw Error("Hull area must be positive and finite");f/=3*u,d/=3*u;let p=c.map(w=>[w[0]-f,w[1]-d]),m=0,h=0,g=0,b=0,y=[];for(let w=0;w<p.length;w++){let B=p[w],L=p[(w+1)%p.length],M=B[0]*L[1]-L[0]*B[1];m+=M*(B[0]*B[0]+B[0]*L[0]+L[0]*L[0]+B[1]*B[1]+B[1]*L[1]+L[1]*L[1])/12;let I=L[0]-B[0],E=L[1]-B[1],k=Math.hypot(I,E);y.push(B[0],B[1],E/k,-I/k),h=Math.max(h,Math.hypot(...B)),g=Math.max(g,Math.abs(B[0])),b=Math.max(b,Math.abs(B[1]))}let A=u/2,v=m/A;if(![A,v,h,g,b,...y].every(w=>Number.isFinite(Math.fround(w)))||Math.fround(A)<=0||Math.fround(v)<=0)throw Error("Hull geometry exceeds GPU floating-point range");let x=[4,h,0,p.length];return x.vertices=y,{geometry:x,size:[2*g,2*b],area:A,momentPerMass:v,radius:h}}function No(s){let e=Ir(s,"radius");return{geometry:[1,e,0,0],size:[2*e,2*e],area:Math.PI*e*e,momentPerMass:e*e/2,radius:e}}function qo(s,e){let t=Ir(s,"radius");if(Ir(e,"length"),e<2*t)throw Error("Capsule length must be at least twice its radius");return Vc((e-2*t)/2,t)}function Vc(s,e){let t=2*s,i=2*e*t,r=Math.PI*e*e,o=i+r,a=i*(t*t+4*e*e)/12+r*(t*t/4+e*e/2+4*t*e/(3*Math.PI));return{geometry:[2,e,s,0],size:[t+2*e,Math.max(2*e,1e-6)],area:o,momentPerMass:o?a/o:0,radius:s+e}}function Vo(s,e,t={}){let i=Fo(s,"start"),r=Fo(e,"end"),o=r[0]-i[0],a=r[1]-i[1],n=Math.hypot(o,a),l=Ir(t.radius??0,"radius",!0);if(Ir(n,"segment length"),t.position!==void 0||t.angle!==void 0)throw Error("Segment position and angle come from its endpoints");if(l===0&&(t.density??0)!==0)throw Error("A zero-thickness segment must be static; give it a radius for a dynamic capsule");return{...Vc(n/2,l),options:{...t,density:t.density??0,position:[(i[0]+r[0])/2,(i[1]+r[1])/2],angle:Math.atan2(a,o)}}}function Wo(s,e=0,t={}){let i=Fo(s,"normal"),r=Ir(Math.hypot(...i),"normal length");if(!Number.isFinite(e))throw Error("Plane offset must be finite");if((t.density??0)!==0)throw Error("An infinite plane must be static");if(t.position!==void 0||t.angle!==void 0)throw Error("Plane position and angle come from its normal and offset");let o=i[0]/r,a=i[1]/r;return{geometry:[3,0,0,0],size:[1,1],area:0,momentPerMass:0,radius:0,options:{...t,density:0,position:[o*e,a*e],angle:Math.atan2(-o,a)}}}function Rr(s,e,t,i){let r=_n.get(s);r||_n.set(s,r=new Map),r.set(e,t.geometry);let o=t.area*i,a=e*4;s.shape[a+2]=o,s.shape[a+3]=o*t.momentPerMass,s.props[a+1]=t.radius,s.dynamic[e]=+(o>0)}var Wc=`struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,velocity:vec4f,previous:vec4f,shape:vec4f}
|
|
4958
5044
|
struct Command { header:vec4u, value:vec4f, point:vec4f }
|
|
4959
5045
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
4960
5046
|
@group(0) @binding(1) var<storage,read> commands:array<Command>;
|
|
@@ -4990,9 +5076,9 @@ fn edit(@builtin(global_invocation_id) id:vec3u) {
|
|
|
4990
5076
|
bodies[index]=b;
|
|
4991
5077
|
}
|
|
4992
5078
|
\r
|
|
4993
|
-
`;function Vt(s,e){let t=s&&typeof s.x=="number"?[s.x,s.y]:Array.isArray(s)||ArrayBuffer.isView(s)&&!(s instanceof DataView)?s:Array.from(s??[]);if(t.length!==2||!t.every(Number.isFinite))throw Error(`${e} requires two finite components`);return t}var ti=class{constructor(){this.count=0,this.capacity=0}reserve(e){if(e<=this.capacity)return;let t=Math.max(e,this.capacity*2,64),i=new ArrayBuffer(t*48),r=new Float32Array(i),o=new Uint32Array(i);this.u32&&o.set(this.u32.subarray(0,this.count*12)),this.words=r,this.u32=o,this.capacity=t}append(e,t,i,r,o=!1){this.reserve(this.count+1),
|
|
4994
|
-
`+this.shader})}}),(!this.group||this.boundBody!==this.solver.bodyBuffer||this.boundCommands!==this.commands||this.boundRanges!==this.ranges||this.boundRangeBytes!==
|
|
4995
|
-
fn narrowphase`,
|
|
5079
|
+
`;function Vt(s,e){let t=s&&typeof s.x=="number"?[s.x,s.y]:Array.isArray(s)||ArrayBuffer.isView(s)&&!(s instanceof DataView)?s:Array.from(s??[]);if(t.length!==2||!t.every(Number.isFinite))throw Error(`${e} requires two finite components`);return t}var ti=class{constructor(){this.count=0,this.capacity=0}reserve(e){if(e<=this.capacity)return;let t=Math.max(e,this.capacity*2,64),i=new ArrayBuffer(t*48),r=new Float32Array(i),o=new Uint32Array(i);this.u32&&o.set(this.u32.subarray(0,this.count*12)),this.words=r,this.u32=o,this.capacity=t}append(e,t,i,r,o=!1){this.reserve(this.count+1),Hc(this.words,this.u32,this.count++*12,e,t,i,r,o)}};function Hc(s,e,t,i,r,o,a,n){e[t]=i,e[t+1]=r,e[t+2]=Number(n),e[t+3]=0;for(let l=0;l<4;l++)s[t+4+l]=o[l]??0,s[t+8+l]=a?.[l]??0}var Or=class{constructor(e,t=Wc,i=2){this.solver=e,this.shader=t,this.dimension=i,this.pending=new Map,this.queue=new ti,this.requested=0,this.linearOnly=!0,this.angularOnly=!0,this.hasTeleports=!1,this.motionOnly=!0,this.freeRanges=[]}slot(e,t){this.requested++,this.linearOnly&&=t===0,this.angularOnly&&=t===1,this.motionOnly&&=t===0||t===1,this.hasTeleports||=t===4;let i=this.pending.get(e);if(i&&(t===0||t===1)&&this.queue.u32[i.tail*12+1]===t)return i.tail;if(this.queue.reserve(this.queue.count+1),!this.next||this.next.length<this.queue.capacity){let o=new Int32Array(this.queue.capacity);this.next&&o.set(this.next),this.next=o}let r=this.queue.count++;return this.next[r]=-1,i?(this.next[i.tail]=r,i.tail=r,i.length++):(i=this.freeRanges.pop()??{},i.head=i.tail=r,i.length=1,this.pending.set(e,i)),r}enqueue(e,t,i,r,o=!1){let a=this.slot(e,t);Hc(this.queue.words,this.queue.u32,a*12,e,t,i,r,o)}enqueueValidated(e){for(let t=0;t<e.count;t++){let i=t*12,r=this.slot(e.u32[i],e.u32[i+1])*12;for(let o=0;o<12;o++)this.queue.u32[r+o]=e.u32[i+o]}}flush(e){if(!this.pending.size)return;let t=this.solver.device,i=this.pending.size;if(this.linearOnly||this.angularOnly){let r=this.angularOnly&&this.dimension===2?1:this.dimension;if(!this.linearIndices||this.linearIndices.length<i){let l=Math.max(i,(this.linearIndices?.length??0)*2,64);this.linearIndices=new Uint32Array(l),this.linearValues=new Float32Array(l*this.dimension)}let o=0;for(let[l,c]of this.pending){this.linearIndices[o]=l;for(let u=0;u<r;u++)this.linearValues[o*r+u]=this.queue.words[c.tail*12+4+u];o++}let a=this.angularOnly?"angularVelocityBatch":"velocityBatch",n=this.solver[a]??=new Kt(this.solver,this.dimension,this.angularOnly?"angular":"linear");n.submitValidated(this.linearIndices.subarray(0,i),this.linearValues.subarray(0,i*r),e),this.lastBatch={bodies:i,commands:this.queue.count,requestedCommands:this.requested,uploadedBytes:n.lastBatch.uploadedBytes,path:this.angularOnly?"packed-angular-velocities":"packed-velocities",submissions:e?0:1}}else if(this.motionOnly){let r=this.motionBatch??=new yi(this.solver,this.dimension),o=r.wordsPerBody;(!this.motionData||this.motionData.length<i*o)&&(this.motionData=new Uint32Array(Math.max(i*o,(this.motionData?.length??0)*2,64*o)),this.motionFloats=new Float32Array(this.motionData.buffer));let a=0;for(let[n,l]of this.pending){let c=a++*o;this.motionData[c]=n;let u=0;for(let f=l.head;f!==-1;f=this.next[f]){let d=this.queue.u32[f*12+1];u|=1<<d;let p=c+(d===0?2:2+this.dimension),m=d===0||this.dimension===3?this.dimension:1;for(let h=0;h<m;h++)this.motionFloats[p+h]=this.queue.words[f*12+4+h]}this.motionData[c+1]=u}r.submitValidated(this.motionData.subarray(0,i*o),e),this.lastBatch={bodies:i,commands:this.queue.count,requestedCommands:this.requested,uploadedBytes:r.lastBatch.uploadedBytes,path:"packed-motion",submissions:e?0:1}}else{this.upload??=new ti,this.upload.count=0,this.upload.reserve(this.queue.count),(!this.rangeData||this.rangeData.length<i*2)&&(this.rangeData=new Uint32Array(Math.max(i*2,(this.rangeData?.length??0)*2,128)));let r=0,o=0;for(let p of this.pending.values()){this.rangeData[o++]=r,this.rangeData[o++]=p.length;for(let m=p.head;m!==-1;m=this.next[m]){for(let h=0;h<12;h++)this.upload.u32[r*12+h]=this.queue.u32[m*12+h];r++}}let a=r*48,n=i*8,l=Math.min(t.limits.maxStorageBufferBindingSize,t.limits.maxBufferSize);if(a>l||n>l)throw Error("Body command batch exceeds GPU buffer limits");for(let[p,m]of[["commands",a],["ranges",n]])(!this[p]||this[p].size<m)&&(this[p]?.destroy(),this[p]=t.createBuffer({size:Math.min(l,Math.max(m*2,16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}));t.queue.writeBuffer(this.commands,0,this.upload.words.buffer,0,a),t.queue.writeBuffer(this.ranges,0,this.rangeData.buffer,0,n),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"edit",module:t.createShaderModule({code:"const DISPATCH_STRIDE = "+t.limits.maxComputeWorkgroupsPerDimension*64+`u;
|
|
5080
|
+
`+this.shader})}}),(!this.group||this.boundBody!==this.solver.bodyBuffer||this.boundCommands!==this.commands||this.boundRanges!==this.ranges||this.boundRangeBytes!==n)&&(this.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.solver.bodyBuffer}},{binding:1,resource:{buffer:this.commands}},{binding:2,resource:{buffer:this.ranges,size:n}}]}),this.boundBody=this.solver.bodyBuffer,this.boundCommands=this.commands,this.boundRanges=this.ranges,this.boundRangeBytes=n);let c=e??t.createCommandEncoder(),u=c.beginComputePass();u.setPipeline(this.pipeline),u.setBindGroup(0,this.group);let f=Math.ceil(i/64),d=t.limits.maxComputeWorkgroupsPerDimension;u.dispatchWorkgroups(Math.min(f,d),Math.ceil(f/d)),u.end(),this.encodeAfterEdits(c,i),e||t.queue.submit([c.finish()]),this.lastBatch={bodies:i,commands:r,requestedCommands:this.requested,uploadedBytes:a+n,path:"ordered-commands",submissions:e?0:1}}for(let r of this.pending.values())this.freeRanges.push(r);this.pending.clear(),this.queue.count=0,this.requested=0,this.linearOnly=!0,this.angularOnly=!0,this.motionOnly=!0,this.hasTeleports=!1}encodeAfterEdits(){}destroy(){this.commands?.destroy(),this.ranges?.destroy(),this.solver.velocityBatch?.destroy(),this.solver.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.freeRanges.length=0,this.pending.clear(),this.queue=this.upload=this.next=this.linearIndices=this.linearValues=this.rangeData=this.motionData=this.motionFloats=null,this.group=null}};var Ho=class extends Oo{writeParams(e,t){nc.call(this,e,t)}writePassConstants(e,t){lo.call(this,e,t)}createPipelines(){let e=this.layouts;this.compile(ps,[e.broad],["beginFrame","gridCount","gridScatter","findPairs"]),this.compile(ms,[e.contacts],["hashInsert","narrowphase"]),this.compile(qr,[e.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),this.compile(gs,[e.solve,e.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),this.compile(Fs,[e.args],["argsPrev","argsPairs","argsContacts","argsColors"])}constructor(e,t,i={}){if(i.minimumColors!==void 0&&(!Number.isInteger(i.minimumColors)||i.minimumColors<4||i.minimumColors>64))throw Error("minimumColors must be an integer from 4 to 64");i.sleeping!==void 0&&i.sleeping!==!1&&Do(i.sleeping),super(e,t,i),this.minimumColors=i.minimumColors??4,this.colorCap=Math.max(this.colorCap,this.minimumColors),this.sleepEligible=new Map(t.avbdSleepEligible??[]),Uc(this),ei(t)?.size&&this.enableShapes(),(i.sleeping||[...this.sleepEligible.values()].some(r=>r===!0))&&this.enableSleeping(i.sleeping||{})}enableShapes(){this.shapeBuffer||(this.syncShapeGeometry(),this.shapeContactLayout=this.makeLayout(["uniform","read-only-storage","read-only-storage","storage","read-only-storage","storage","storage","read-only-storage"]),this.compileBroadphase(),this.compileContacts(),this.bindContacts(),this.staticsDirty=!0)}geometryBytes(e=0){let t=(this.geometryRecords??this.bodyCapacity)*4+e;if(this.geometryRecords===void 0)for(let r of ei(this.topology)?.values()??[])t+=r.vertices?.length??0;let i=t*4;if(i>Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize,16777216*16))throw Error("2D hull geometry exceeds this GPU's storage capacity");return i}syncShapeGeometry(){let e=this.geometryBytes(),t=new Float32Array(e/4);this.packedShapes=new Map;let i=this.bodyCapacity;for(let[r,o]of ei(this.topology)??[]){let a=Array.from(o);o.vertices&&(a[2]=i,t.set(o.vertices,i*4),i+=o.vertices.length/4),t.set(a,r*4),this.packedShapes.set(r,a)}this.geometryRecords=i,this.shapeBuffer=this.createShapeBuffer(e),this.device.queue.writeBuffer(this.shapeBuffer,0,t)}createShapeBuffer(e){let t=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize,268435456);return this.device.createBuffer({label:"2D collider geometry",size:Math.min(t,Math.max(e,Math.ceil(e*1.5/16)*16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC})}appendShapeGeometry(e,t){let i=this.geometryBytes(t.vertices?.length??0);if(this.shapeBuffer.size<i){let o=this.shapeBuffer;this.shapeBuffer=this.createShapeBuffer(i);let a=this.device.createCommandEncoder();a.copyBufferToBuffer(o,0,this.shapeBuffer,0,this.geometryRecords*16),this.device.queue.submit([a.finish()]),o.destroy(),this.bindFilters(),this.bindContacts()}let r=Array.from(t);t.vertices&&(r[2]=this.geometryRecords,this.device.queue.writeBuffer(this.shapeBuffer,this.geometryRecords*16,new Float32Array(t.vertices)),this.geometryRecords+=t.vertices.length/4),this.packedShapes.set(e,r),this.device.queue.writeBuffer(this.shapeBuffer,e*16,new Float32Array(r))}makeLayout(e){return this.device.createBindGroupLayout({entries:e.map((t,i)=>({binding:i,visibility:GPUShaderStage.COMPUTE,buffer:{type:t}}))})}contactSource(){let e=ms;return this.shapeBuffer&&(e=e.replace(`@compute @workgroup_size(64)
|
|
5081
|
+
fn narrowphase`,qc+`
|
|
4996
5082
|
@compute @workgroup_size(64)
|
|
4997
5083
|
fn narrowphase`).replace("let col = collideBoxes(A.pose.xyz, A.shape.xy * 0.5, B.pose.xyz, B.shape.xy * 0.5);","let col = collideShapes2D(a,b);")),this.sensorsEnabled&&(e=e.replace("rec.ids = vec4u(a, b, o.feature, 0u);","rec.ids = vec4u(a,b,o.feature,select(0u,0x80000000u,A.initial.w!=0.||B.initial.w!=0.));").replace("if (j >= 0) {","if (j >= 0 && rec.ids.w==0u && (prevContacts[j].ids.w&0x80000000u)==0u) {")),this.bodyCommands&&(e=e.replace("if (j >= 0","if (A.prevVel.w==0. && B.prevVel.w==0. && j >= 0")),e}compileContacts(){this.compile(this.contactSource(),[this.shapeContactLayout??this.layouts.contacts],["hashInsert","narrowphase"])}bindContacts(){this.shapeBuffer&&(this.groups.contacts=this.contactBuffers.map((e,t)=>this.device.createBindGroup({layout:this.shapeContactLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.pairBuffer,e,this.contactBuffers[1-t],this.tableBuffer,this.counterBuffer,this.shapeBuffer].map((i,r)=>({binding:r,resource:{buffer:i}}))})))}compileBroadphase(){let e=["uniform","read-only-storage","storage","storage","storage","read-only-storage","read-only-storage"],t=ps;if(this.shapeBuffer&&(e.push("read-only-storage"),t=t.replace(`fn radius(i: u32) -> f32 {
|
|
4998
5084
|
return 0.5 * length(bodies[i].shape.xy);
|
|
@@ -5002,13 +5088,13 @@ let plane=select(b,a,geometry[a].x==3.);let other=select(a,b,geometry[a].x==3.);
|
|
|
5002
5088
|
if(dot(bodies[other].pose.xy-bodies[plane].pose.xy,normal)>radius(other)){return;}
|
|
5003
5089
|
} else if(dot(d,d)>r*r){return;}`)),this.filters){let i=e.length;e.push("read-only-storage"),t=t.replace("fn ignored(hi: u32, lo: u32) -> bool {",`@group(0) @binding(${i}) var<storage,read> filters:array<vec2u>;
|
|
5004
5090
|
fn ignored(hi:u32,lo:u32)->bool{
|
|
5005
|
-
if((filters[hi].x&filters[lo].y)==0u || (filters[lo].x&filters[hi].y)==0u){return true;}`)}this.filterLayout=this.makeLayout(e),this.compile(t,[this.filterLayout],["beginFrame","gridCount","gridScatter","findPairs"]),this.bindFilters()}uploadBodies(e=0,t=this.topology.bodyCount-e){this.flushPropertyEdits(),super.uploadBodies(e,t);for(let o=e;o<e+t;o++)this.sleeping?.register(o,this.sleepEligible?.get(o)!==!1);if(!this.shapeBuffer&&!this.triggers?.size)return;let i=this.shapeBuffer&&t>0?new Float32Array(t*4):null,r=this.packedShapes;for(let o=e;o<e+t;o++){let n=r?.get(o);i&&n&&i.set(n,(o-e)*4),this.triggers?.has(o)&&this.device.queue.writeBuffer(this.bodyBuffer,o*96+28,new Float32Array([1]))}i&&this.device.queue.writeBuffer(this.shapeBuffer,e*16,i)}uploadStatics(){if(!this.shapeBuffer)return super.uploadStatics();let e=this.topology,t=ei(e),i=[];for(let f=0;f<this.bodyCount;f++)t?.get(f)?.[0]!==3&&i.push(e.props[f*4+1]);i.sort((f,d)=>f-d);let r=i[i.length>>1]??1,o=0,n=1/0;for(let f of i)f<=4*r&&(o=Math.max(o,f));for(let f of i)f>o&&(n=Math.min(n,f));let a=n===1/0?o*1.5:(o+n)/2;this.maxSmallRadius=a,this.cellSize=Math.max(2*o*(1+1e-4),.001);let l=[],c=[];for(let f=0;f<this.bodyCount;f++)(t?.get(f)?.[0]===3||e.props[f*4+1]>a)&&l.push(f);for(let f=0;f<e.jointCount;f++){let d=e.info[f*4+1],p=e.info[f*4+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),f])}for(let f of e.ignoredPairs()){let d=Math.floor(f/2097152);c.push([d,f-d*2097152,4294967295])}c.sort((f,d)=>f[0]-d[0]||f[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let u=new Uint32Array(Math.max(l.length+3*c.length,4));u.set(l),c.forEach((f,d)=>u.set(f,l.length+3*d)),this.staticBuffer.size<u.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"2D boundaries",size:u.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,u),this.staticsDirty=!1}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new pi(this,2),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async circleCast(e,t,i,r={}){let[o]=await this.circleCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}circleCastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new pi(this,2),this.shapeQueries.cast(e,t)}liveIndex(e){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 2D body index is required")}queueBodyCommand(e,t,i,r,o=!1){return this.liveIndex(e),this.prepareBodyCommands().enqueue(e,t,i,r,o),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands||(this.bodyCommands=new Or(this),this.compileContacts()),this.bodyCommands}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,Vt(t,"velocity"))}setAngularVelocity(e,t){if(!Number.isFinite(t))throw Error("Angular velocity must be finite");return this.queueBodyCommand(e,1,[t,0,0,0])}applyImpulse(e,t,i){return this.queueBodyCommand(e,2,Vt(t,"impulse"),i===void 0?void 0:Vt(i,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...Vt(t,"force"),0,this.params.dt],i===void 0?void 0:Vt(i,"point"),i!==void 0)}applyAngularImpulse(e,t){if(!Number.isFinite(t))throw Error("Angular impulse must be finite");return this.queueBodyCommand(e,5,[t,0,0,0])}applyTorque(e,t){if(!Number.isFinite(t))throw Error("Torque must be finite");return this.queueBodyCommand(e,6,[t,0,0,this.params.dt])}teleport(e,t,i){if(i!==void 0&&!Number.isFinite(i))throw Error("Angle must be finite");return this.queueBodyCommand(e,4,[...Vt(t,"position"),i??0,0],void 0,i!==void 0)}readBodies(){return this.flushEdits(),super.readBodies()}async readBodyState(e){this.liveIndex(e),this.flushEdits();let t=this.device,i=t.createBuffer({size:96,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let r=t.createCommandEncoder();r.copyBufferToBuffer(this.bodyBuffer,e*96,i,0,96),t.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=new Float32Array(i.getMappedRange());return{position:[o[0],o[1]],angle:o[2],linearVelocity:[o[12],o[13]],angularVelocity:o[14],mass:o[22],moment:o[23],sleeping:o[15]!==0}}finally{i.destroy()}}addBox(e,t={}){return this.addShape({size:Array.from(e)},t)}addCircle(e,t={}){return this.addShape(No(e),t)}addHull(e,t={}){return this.addShape(Uo(e),t)}addCapsule(e,t,i={}){return this.addShape(qo(e,t),i)}addSegment(e,t,i={}){return this.addShape(Vo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Wo(e,t,i))}addShape(e,t={}){t=e.options??t;let{density:i=1,friction:r=.6,position:o=[0,0],angle:n=0,velocity:a=[0,0,0],restitution:l=0,isTrigger:c=!1,group:u=4294967295,collidesWith:f=4294967295,allowSleep:d}=t;if(this.destroyed)throw Error("The 2D solver has been destroyed");if(d!==void 0&&typeof d!="boolean")throw Error("allowSleep must be boolean");if(this.bodyCount>=this.bodyCapacity)throw Error("2D body capacity reached; reserve bodyCapacity when creating the solver");if(e.size.length!==2||!e.size.every(h=>Number.isFinite(h)&&h>0)||!Number.isFinite(i)||i<0||!Number.isFinite(r)||r<0||!Number.isFinite(n))throw Error("Body dimensions must be positive, density and friction nonnegative, and angle finite");if(a.length!==3||!Array.from(a).every(Number.isFinite))throw Error("Velocity requires [x, y, angular] finite components");if(typeof c!="boolean"||!Number.isFinite(l)||l<0||l>1)throw Error("isTrigger must be boolean and restitution between zero and one");if([u,f].some(h=>!Number.isInteger(h)||h<0||h>4294967295))throw Error("Collision masks must be unsigned 32-bit integers");let p=Vt(o,"position");e.geometry&&this.geometryBytes(e.geometry.vertices?.length??0);let m=super.addBody(e.size,i,r,[...p,n],Array.from(a));return e.geometry&&(Rr(this.topology,m,e,i),this.shapeBuffer?this.appendShapeGeometry(m,e.geometry):this.enableShapes(),this.uploadBodies(m,1)),l&&this.setRestitution(m,l),c&&this.setSensor(m,!0),(u!==4294967295||f!==4294967295)&&this.setFilters([m],[u],[f]),this.contactWatch?.setWatched([m],[!this.watchedIndices||this.watchedIndices.has(m)]),this.sleepEligible.set(m,d??!0),d===!0&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(m,d!==!1),this.wakeAll(),m}setRestitution(e,t){if(this.liveIndex(e),!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a coefficient in 0..1");t>0&&!this.restitution&&(this.restitution=new To(this)),this.restitution?.set(e,t),this.wakeAll()}constraintSlot(e,t){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.topology.handles[e]?.disposed)throw Error("A live 2D constraint slot is required");if(t!==void 0&&this.topology.info[e*j]!==t)throw Error("The constraint has a different type");return e}setMotor(e,{speed:t,maxTorque:i}={}){if(this.constraintSlot(e,Io),t!==void 0&&!Number.isFinite(t)||i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("Motor speed must be finite and maximum torque nonnegative");let r=e*O,o=e*36;t!==void 0&&(this.topology.data[r+re]=t,le(this).float(this.jointBuffer,o+32,t)),i!==void 0&&(this.topology.data[r+_e]=-i,this.topology.data[r+Ae]=i,le(this).float(this.jointBuffer,o+12,-i),le(this).float(this.jointBuffer,o+16,i)),this.wakeAll()}setAngleLimits(e,t){this.constraintSlot(e,Go);let i=ki(t);this.topology.data[e*O+be]=i.minAngle,this.topology.data[e*O+Ue]=i.maxAngle,le(this).float(this.jointBuffer,e*36+32+1,i.minAngle),le(this).float(this.jointBuffer,e*36+32+2,i.maxAngle);for(let r=4;r<8;r++)le(this).float(this.jointBuffer,e*36+r,0);this.wakeAll()}setWorldAnchor(e,t,i){if(this.constraintSlot(e,Bi),this.topology.info[e*j+1]!==-1||!Number.isFinite(t)||!Number.isFinite(i))throw Error("A world joint and finite xy anchor are required");le(this).float(this.jointBuffer,e*36+28,t),le(this).float(this.jointBuffer,e*36+28+1,i),this.wakeAll()}disableConstraint(e){if(this.topology.handles[e]?.disposed)return;this.constraintSlot(e),this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll();let t=this.topology.handles[e];t&&(t.alive=!1,t.disposed=!0)}appendJoint(e,t,i,r,o,n=1/0){if(e!==-1&&this.liveIndex(e),this.liveIndex(t),e===t)throw Error("Constraint endpoints must differ");let a=Vt(i,"anchorA"),l=Vt(r,"anchorB");if(!o||o.length!==3||Array.from(o).some(u=>u!==1/0&&(!Number.isFinite(u)||u<0))||n!==1/0&&(!Number.isFinite(n)||n<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");this.flushPropertyEdits();let c=super.appendJoint(e,t,a,l,Array.from(o),n);return this.wakeAll(),c}setFilters(e,t,i){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");let r=a=>{if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return a},o=Array.from(e,(a,l)=>(this.liveIndex(a),[a,r(t[l]),r(i[l])]));if(this.wakeAll(),!this.filters){let a=this.device;this.filters=a.createBuffer({size:this.bodyCapacity*8,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),a.queue.writeBuffer(this.filters,0,new Uint32Array(this.bodyCapacity*2).fill(4294967295)),this.compileBroadphase()}let n=le(this);for(let[a,l,c]of o)n.uint(this.filters,a*2,l),n.uint(this.filters,a*2+1,c)}bindFilters(){this.filterLayout&&(this.groups.broad=this.device.createBindGroup({layout:this.filterLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer,...this.shapeBuffer?[this.shapeBuffer]:[],...this.filters?[this.filters]:[]].map((e,t)=>({binding:t,resource:{buffer:e}}))}))}rebuildBindings(){super.rebuildBindings(),this.bindFilters(),this.bindContacts()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sensor flag must be boolean");if(this.wakeAll(),t&&!this.sensorsEnabled){this.sensorsEnabled=!0;let i=this.device,r=i.createComputePipeline({layout:"auto",compute:{module:i.createShaderModule({code:"@group(0) @binding(0) var<storage,read_write> data:array<vec4f>; @compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){if(id.x*6u+1u<arrayLength(&data)){data[id.x*6u+1u].w=0.;}}"}),entryPoint:"clear"}}),o=i.createBindGroup({layout:r.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.bodyBuffer}}]}),n=i.createCommandEncoder(),a=n.beginComputePass();a.setPipeline(r),a.setBindGroup(0,o),a.dispatchWorkgroups(Math.ceil(this.bodyCount/64)),a.end(),i.queue.submit([n.finish()]),this.compileContacts();let l=gs.replace("b.initial = b.pose;","b.initial = vec4f(b.pose.xyz,b.initial.w);").replace("fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {",`fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {
|
|
5091
|
+
if((filters[hi].x&filters[lo].y)==0u || (filters[lo].x&filters[hi].y)==0u){return true;}`)}this.filterLayout=this.makeLayout(e),this.compile(t,[this.filterLayout],["beginFrame","gridCount","gridScatter","findPairs"]),this.bindFilters()}uploadBodies(e=0,t=this.topology.bodyCount-e){this.flushPropertyEdits(),super.uploadBodies(e,t);for(let o=e;o<e+t;o++)this.sleeping?.register(o,this.sleepEligible?.get(o)!==!1);if(!this.shapeBuffer&&!this.triggers?.size)return;let i=this.shapeBuffer&&t>0?new Float32Array(t*4):null,r=this.packedShapes;for(let o=e;o<e+t;o++){let a=r?.get(o);i&&a&&i.set(a,(o-e)*4),this.triggers?.has(o)&&this.device.queue.writeBuffer(this.bodyBuffer,o*96+28,new Float32Array([1]))}i&&this.device.queue.writeBuffer(this.shapeBuffer,e*16,i)}uploadStatics(){if(!this.shapeBuffer)return super.uploadStatics();let e=this.topology,t=ei(e),i=[];for(let f=0;f<this.bodyCount;f++)t?.get(f)?.[0]!==3&&i.push(e.props[f*4+1]);i.sort((f,d)=>f-d);let r=i[i.length>>1]??1,o=0,a=1/0;for(let f of i)f<=4*r&&(o=Math.max(o,f));for(let f of i)f>o&&(a=Math.min(a,f));let n=a===1/0?o*1.5:(o+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*o*(1+1e-4),.001);let l=[],c=[];for(let f=0;f<this.bodyCount;f++)(t?.get(f)?.[0]===3||e.props[f*4+1]>n)&&l.push(f);for(let f=0;f<e.jointCount;f++){let d=e.info[f*4+1],p=e.info[f*4+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),f])}for(let f of e.ignoredPairs()){let d=Math.floor(f/2097152);c.push([d,f-d*2097152,4294967295])}c.sort((f,d)=>f[0]-d[0]||f[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let u=new Uint32Array(Math.max(l.length+3*c.length,4));u.set(l),c.forEach((f,d)=>u.set(f,l.length+3*d)),this.staticBuffer.size<u.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"2D boundaries",size:u.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,u),this.staticsDirty=!1}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new pi(this,2),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async circleCast(e,t,i,r={}){let[o]=await this.circleCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}circleCastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new pi(this,2),this.shapeQueries.cast(e,t)}liveIndex(e){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 2D body index is required")}queueBodyCommand(e,t,i,r,o=!1){return this.liveIndex(e),this.prepareBodyCommands().enqueue(e,t,i,r,o),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands||(this.bodyCommands=new Or(this),this.compileContacts()),this.bodyCommands}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,Vt(t,"velocity"))}setAngularVelocity(e,t){if(!Number.isFinite(t))throw Error("Angular velocity must be finite");return this.queueBodyCommand(e,1,[t,0,0,0])}applyImpulse(e,t,i){return this.queueBodyCommand(e,2,Vt(t,"impulse"),i===void 0?void 0:Vt(i,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...Vt(t,"force"),0,this.params.dt],i===void 0?void 0:Vt(i,"point"),i!==void 0)}applyAngularImpulse(e,t){if(!Number.isFinite(t))throw Error("Angular impulse must be finite");return this.queueBodyCommand(e,5,[t,0,0,0])}applyTorque(e,t){if(!Number.isFinite(t))throw Error("Torque must be finite");return this.queueBodyCommand(e,6,[t,0,0,this.params.dt])}teleport(e,t,i){if(i!==void 0&&!Number.isFinite(i))throw Error("Angle must be finite");return this.queueBodyCommand(e,4,[...Vt(t,"position"),i??0,0],void 0,i!==void 0)}readBodies(){return this.flushEdits(),super.readBodies()}async readBodyState(e){this.liveIndex(e),this.flushEdits();let t=this.device,i=t.createBuffer({size:96,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let r=t.createCommandEncoder();r.copyBufferToBuffer(this.bodyBuffer,e*96,i,0,96),t.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=new Float32Array(i.getMappedRange());return{position:[o[0],o[1]],angle:o[2],linearVelocity:[o[12],o[13]],angularVelocity:o[14],mass:o[22],moment:o[23],sleeping:o[15]!==0}}finally{i.destroy()}}addBox(e,t={}){return this.addShape({size:Array.from(e)},t)}addCircle(e,t={}){return this.addShape(No(e),t)}addHull(e,t={}){return this.addShape(Uo(e),t)}addCapsule(e,t,i={}){return this.addShape(qo(e,t),i)}addSegment(e,t,i={}){return this.addShape(Vo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Wo(e,t,i))}addShape(e,t={}){t=e.options??t;let{density:i=1,friction:r=.6,position:o=[0,0],angle:a=0,velocity:n=[0,0,0],restitution:l=0,isTrigger:c=!1,group:u=4294967295,collidesWith:f=4294967295,allowSleep:d}=t;if(this.destroyed)throw Error("The 2D solver has been destroyed");if(d!==void 0&&typeof d!="boolean")throw Error("allowSleep must be boolean");if(this.bodyCount>=this.bodyCapacity)throw Error("2D body capacity reached; reserve bodyCapacity when creating the solver");if(e.size.length!==2||!e.size.every(h=>Number.isFinite(h)&&h>0)||!Number.isFinite(i)||i<0||!Number.isFinite(r)||r<0||!Number.isFinite(a))throw Error("Body dimensions must be positive, density and friction nonnegative, and angle finite");if(n.length!==3||!Array.from(n).every(Number.isFinite))throw Error("Velocity requires [x, y, angular] finite components");if(typeof c!="boolean"||!Number.isFinite(l)||l<0||l>1)throw Error("isTrigger must be boolean and restitution between zero and one");if([u,f].some(h=>!Number.isInteger(h)||h<0||h>4294967295))throw Error("Collision masks must be unsigned 32-bit integers");let p=Vt(o,"position");e.geometry&&this.geometryBytes(e.geometry.vertices?.length??0);let m=super.addBody(e.size,i,r,[...p,a],Array.from(n));return e.geometry&&(Rr(this.topology,m,e,i),this.shapeBuffer?this.appendShapeGeometry(m,e.geometry):this.enableShapes(),this.uploadBodies(m,1)),l&&this.setRestitution(m,l),c&&this.setSensor(m,!0),(u!==4294967295||f!==4294967295)&&this.setFilters([m],[u],[f]),this.contactWatch?.setWatched([m],[!this.watchedIndices||this.watchedIndices.has(m)]),this.sleepEligible.set(m,d??!0),d===!0&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(m,d!==!1),this.wakeAll(),m}setRestitution(e,t){if(this.liveIndex(e),!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a coefficient in 0..1");t>0&&!this.restitution&&(this.restitution=new To(this)),this.restitution?.set(e,t),this.wakeAll()}constraintSlot(e,t){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.topology.handles[e]?.disposed)throw Error("A live 2D constraint slot is required");if(t!==void 0&&this.topology.info[e*j]!==t)throw Error("The constraint has a different type");return e}setMotor(e,{speed:t,maxTorque:i}={}){if(this.constraintSlot(e,Io),t!==void 0&&!Number.isFinite(t)||i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("Motor speed must be finite and maximum torque nonnegative");let r=e*O,o=e*36;t!==void 0&&(this.topology.data[r+re]=t,oe(this).float(this.jointBuffer,o+32,t)),i!==void 0&&(this.topology.data[r+_e]=-i,this.topology.data[r+Ae]=i,oe(this).float(this.jointBuffer,o+12,-i),oe(this).float(this.jointBuffer,o+16,i)),this.wakeAll()}setAngleLimits(e,t){this.constraintSlot(e,Go);let i=ki(t);this.topology.data[e*O+be]=i.minAngle,this.topology.data[e*O+Ue]=i.maxAngle,oe(this).float(this.jointBuffer,e*36+32+1,i.minAngle),oe(this).float(this.jointBuffer,e*36+32+2,i.maxAngle);for(let r=4;r<8;r++)oe(this).float(this.jointBuffer,e*36+r,0);this.wakeAll()}setWorldAnchor(e,t,i){if(this.constraintSlot(e,Bi),this.topology.info[e*j+1]!==-1||!Number.isFinite(t)||!Number.isFinite(i))throw Error("A world joint and finite xy anchor are required");oe(this).float(this.jointBuffer,e*36+28,t),oe(this).float(this.jointBuffer,e*36+28+1,i),this.wakeAll()}disableConstraint(e){if(this.topology.handles[e]?.disposed)return;this.constraintSlot(e),this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll();let t=this.topology.handles[e];t&&(t.alive=!1,t.disposed=!0)}appendJoint(e,t,i,r,o,a=1/0){if(e!==-1&&this.liveIndex(e),this.liveIndex(t),e===t)throw Error("Constraint endpoints must differ");let n=Vt(i,"anchorA"),l=Vt(r,"anchorB");if(!o||o.length!==3||Array.from(o).some(u=>u!==1/0&&(!Number.isFinite(u)||u<0))||a!==1/0&&(!Number.isFinite(a)||a<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");this.flushPropertyEdits();let c=super.appendJoint(e,t,n,l,Array.from(o),a);return this.wakeAll(),c}setFilters(e,t,i){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");let r=n=>{if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return n},o=Array.from(e,(n,l)=>(this.liveIndex(n),[n,r(t[l]),r(i[l])]));if(this.wakeAll(),!this.filters){let n=this.device;this.filters=n.createBuffer({size:this.bodyCapacity*8,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),n.queue.writeBuffer(this.filters,0,new Uint32Array(this.bodyCapacity*2).fill(4294967295)),this.compileBroadphase()}let a=oe(this);for(let[n,l,c]of o)a.uint(this.filters,n*2,l),a.uint(this.filters,n*2+1,c)}bindFilters(){this.filterLayout&&(this.groups.broad=this.device.createBindGroup({layout:this.filterLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer,...this.shapeBuffer?[this.shapeBuffer]:[],...this.filters?[this.filters]:[]].map((e,t)=>({binding:t,resource:{buffer:e}}))}))}rebuildBindings(){super.rebuildBindings(),this.bindFilters(),this.bindContacts()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sensor flag must be boolean");if(this.wakeAll(),t&&!this.sensorsEnabled){this.sensorsEnabled=!0;let i=this.device,r=i.createComputePipeline({layout:"auto",compute:{module:i.createShaderModule({code:"@group(0) @binding(0) var<storage,read_write> data:array<vec4f>; @compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){if(id.x*6u+1u<arrayLength(&data)){data[id.x*6u+1u].w=0.;}}"}),entryPoint:"clear"}}),o=i.createBindGroup({layout:r.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.bodyBuffer}}]}),a=i.createCommandEncoder(),n=a.beginComputePass();n.setPipeline(r),n.setBindGroup(0,o),n.dispatchWorkgroups(Math.ceil(this.bodyCount/64)),n.end(),i.queue.submit([a.finish()]),this.compileContacts();let l=gs.replace("b.initial = b.pose;","b.initial = vec4f(b.pose.xyz,b.initial.w);").replace("fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {",`fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {
|
|
5006
5092
|
if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace("fn dualContact(c: u32) {",`fn dualContact(c: u32) {
|
|
5007
5093
|
if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace(` let k = contacts[c];
|
|
5008
5094
|
let bound`,` let k = contacts[c];
|
|
5009
5095
|
if((k.ids.w&0x80000000u)!=0u){return;}
|
|
5010
5096
|
let bound`);this.compile(l,[this.layouts.solve,this.layouts.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]);let c=qr.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
|
|
5011
|
-
if((contacts[gid.x].ids.w&0x80000000u)!=0u){return;}`);this.compile(c,[this.layouts.topo],["degreeContacts","fillContacts"])}this.sensorsEnabled&&le(this).float(this.bodyBuffer,e*24+7,t?1:0),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}compile(e,t,i){e.includes("fn warmStartBodies(")&&(e=Ic(Oc(Gc(e)))),e.includes("fn dynamicBody(")&&(e=Tc(e));let r=e.includes("fn colorCount(");r&&(this.colorBucketSource=La(oo(e)),e=La(e));let o=this.device,n=o.createShaderModule({code:e}),a=o.createPipelineLayout({bindGroupLayouts:t});for(let l of i)this.pipes[l]=o.createComputePipeline({layout:a,compute:{module:n,entryPoint:l}});r&&i.includes("colorCount")&&(this.colorBucketPipelines={scan:this.pipes.colorCount},this.bitsetColorBuckets=void 0)}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array of live body indices");for(let r of i)this.liveIndex(r)}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");return this.contactWatch||(this.contactWatch=new xi(this.device,this.bodyBuffer,this.bodyCapacity,e,t,2),i===void 0&&this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,o)=>o),Array(this.bodyCount).fill(!0))),i!==void 0&&(this.watchedIndices=new Set(i),this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,o)=>o),Array.from({length:this.bodyCount},(r,o)=>this.watchedIndices.has(o)))),this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Ai?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}step(){if(this.destroyed)throw Error("The 2D solver has been destroyed");no(this,this.colorBucketSource),this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep||this.externalAfterStep?jc.call(this):super.step(),this.contactWatch&&this.contactWatch.run({manifolds:this.contactBuffers[1-this.parity],contacts:this.contactBuffers[1-this.parity],counters:this.counterBuffer},this.contactCapacity,this.eventStep=(this.eventStep??0)+1,this.params.dt)}adapt(e){super.adapt(e),this.colorCap=Math.max(this.colorCap,this.minimumColors)}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.restitution?.before(e),this.externalBeforeStep?.(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e),this.externalAfterStep?.(e)}destroy(){this.passConstantCache=null,this.propertyEdits?.destroy(),!this.destroyed&&(this.detachFromScene(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.filters?.destroy(),this.shapeBuffer?.destroy(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),this.sleeping?.destroy(),super.destroy())}enableSleeping(e={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping?this.sleeping.options=Do(e):this.sleeping=new jo(this,e),this}disableSleeping(){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.destroy(),this.sleeping=null),this}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");return this.sleepEligible.set(e,t),t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,t),this.wakeAll(),this}wakeAll(){return this.sleeping&&(this.sleeping.wakeRequested=!0),this}wakeUp(e){return this.liveIndex(e),this.wakeAll()}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}flushEdits(){this.flushPropertyEdits(),this.sleeping?.wakeRequested&&this.sleeping.before(),this.bodyCommands?.flush()}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}attachToScene(e,{maxSubSteps:t=6,maxFrameTime:i=.05,afterStep:r}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!e?.onBeforeRenderObservable?.add||!e?.onDisposeObservable?.addOnce||!e?.getEngine)throw Error("A Babylon scene is required");if(!Number.isInteger(t)||t<1||!Number.isFinite(i)||i<=0||r!==void 0&&typeof r!="function")throw Error("Scene stepping requires positive frame limits and an optional afterStep function");if(this.sceneBinding){if(this.sceneBinding.scene===e)return this;throw Error("The 2D solver is already attached to another scene")}let o=0,n=e.onBeforeRenderObservable.add(()=>{let l=this.params.dt;if(!(l>0)||!Number.isFinite(l))throw Error("Simulation timestep must be positive and finite");let c=e.getEngine().getDeltaTime()/1e3;o=Math.min(t*l,o+Math.min(i,Math.max(0,c)));for(let u=0;o+1e-12>=l&&u<t&&(this.step(),o=Math.max(0,o-l),r?.(this),!(!this.sceneBinding||this.destroyed));u++);}),a=e.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:e,before:n,dispose:a},this}detachFromScene(){let e=this.sceneBinding;return e&&(e.scene.onBeforeRenderObservable.remove(e.before),e.scene.onDisposeObservable.remove(e.dispose),this.sceneBinding=null),this}};var $o=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s};function me(s,e,t){let i=s&&typeof s.x=="number"?[s.x,s.y,...e>=3?[s.z]:[],...e===4?[s.w]:[]]:Array.isArray(s)||ArrayBuffer.isView(s)&&!(s instanceof DataView)?s:Array.from(s??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i}var Vc=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function Jf(s,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new ti:this.editStaging??=new ti;this.editInProgress=!0;try{return Xf.call(this,s,e,i)}finally{this.editInProgress=t}}function Xf(s,e,t){t.count=0;let i,r=(o,n)=>{if(e===3)return me(o,3,n);if(!Number.isFinite(o))throw Error(`${n} must be finite`);return[o,0,0]};for(let o of s){let n=this.bodyIndex(o.body),{isTrigger:a,restitution:l,group:c,collidesWith:u,allowSleep:f}=o;if(a!==void 0||l!==void 0||c!==void 0||u!==void 0||f!==void 0){if(a!==void 0&&typeof a!="boolean")throw Error("isTrigger must be a boolean");if(f!==void 0&&typeof f!="boolean")throw Error("allowSleep must be a boolean");if(l!==void 0&&(!Number.isFinite(l)||l<0||l>1))throw Error("restitution must be between 0 and 1");if(u!==void 0&&c===void 0)throw Error("A collision-mask edit requires group as well");c!==void 0&&$o(c),u!==void 0&&$o(u),(i??=[]).push({index:n,isTrigger:a,restitution:l,group:c,collidesWith:u??4294967295,allowSleep:f})}let d=o.worldPoint===void 0?void 0:me(o.worldPoint,e,"worldPoint");if(o.position!==void 0){let p=me(o.position,e,"position"),m;if(o.rotation!==void 0){if(m=e===3?me(o.rotation,4,"rotation"):[o.rotation],e===3){let h=Math.hypot(...m);if(!h)throw Error("rotation must be nonzero");m=m.map(g=>g/h)}else if(!Number.isFinite(o.rotation))throw Error("angle must be finite")}t.append(n,4,p,e===3?m:void 0,m!==void 0),e===2&&(t.words[(t.count-1)*12+6]=m?.[0]??0)}else if(o.rotation!==void 0)throw Error("A rotation edit requires position; use teleport for a full pose");for(let p=0;p<Vc.length;p++){if(p===4)continue;let m=Vc[p];if(o[m]===void 0)continue;let g=p===1||p>=5?r(o[m],m):me(o[m],e,m);t.append(n,p,g,p===2||p===3?d:void 0,(p===2||p===3)&&d!==void 0),(p===3||p===6)&&(t.words[(t.count-1)*12+7]=this.params.dt)}}if(this.destroyed)throw Error("The solver was destroyed while reading body edits");t.count&&this.prepareBodyCommands().enqueueValidated(t);for(let o of i??[])o.isTrigger!==void 0&&this.setSensor(o.index,o.isTrigger),o.restitution!==void 0&&this.setRestitution(o.index,o.restitution),o.group!==void 0&&this.setFilters([o.index],[o.group],[o.collidesWith]),o.allowSleep!==void 0&&this.setSleepEnabled(o.index,o.allowSleep);return this}var Jo=class{constructor(e,t){this.solver=e,this.index=t}get gpuIndex(){return this.solver.liveIndex(this.index),this.index}setLinearVelocity(e){return this.solver.setLinearVelocity(this.gpuIndex,e),this}setAngularVelocity(e){return this.solver.setAngularVelocity(this.gpuIndex,e),this}applyImpulse(e,t){return this.solver.applyImpulse(this.gpuIndex,e,t),this}applyForce(e,t){return this.solver.applyForce(this.gpuIndex,e,t),this}applyAngularImpulse(e){return this.solver.applyAngularImpulse(this.gpuIndex,e),this}applyTorque(e){return this.solver.applyTorque(this.gpuIndex,e),this}teleport(e,t){return this.solver.teleport(this.gpuIndex,e,t),this}setTrigger(e){return this.solver.setSensor(this.gpuIndex,e),this.solver.wakeAll(),this}setRestitution(e){return this.solver.setRestitution(this.gpuIndex,e),this.solver.wakeAll(),this}setCollisionGroups(e,t=4294967295){return this.solver.setFilters([this.gpuIndex],[$o(e)],[$o(t)]),this.solver.wakeAll(),this}setSleepEnabled(e){return this.solver.setSleepEnabled(this.gpuIndex,e),this}wakeUp(){return this.solver.liveIndex(this.index),this.solver.wakeAll(),this}readState(){return this.solver.readBodyState(this.gpuIndex)}};function Wc(s,{maxSubSteps:e=6,maxFrameTime:t=.05}={}){if(!Number.isInteger(e)||e<1||!Number.isFinite(t)||t<=0)throw Error("Stepping limits must be positive");let i=s.params.dt;if(!Number.isFinite(i)||i<=0)throw Error("Simulation timestep must be positive and finite");return{dt:i,maxSubSteps:e,maxFrameTime:t}}function Hc(s,e,t,i){if(s.destroyed)throw Error("The solver has been destroyed");if(!Number.isFinite(e)||e<0)throw Error("elapsed must be finite nonnegative seconds");let{dt:r,maxSubSteps:o,maxFrameTime:n}=Wc(s,t);s.timeAccumulator=Math.min(o*r,(s.timeAccumulator??0)+Math.min(n,e));let a=0;for(;s.timeAccumulator+1e-12>=r&&a<o;)s.step(),s.timeAccumulator=Math.max(0,s.timeAccumulator-r),a++,i?.(s);return a}function Yf(s,e){return Hc(this,s,e)}function Xo(s,{afterStep:e,...t}={}){if(this.destroyed)throw Error("The solver has been destroyed");if(!s?.onBeforeRenderObservable?.add||!s?.onDisposeObservable?.addOnce||!s?.getEngine)throw Error("A Babylon scene is required");if(e!==void 0&&typeof e!="function")throw Error("afterStep must be a function");if(Wc(this,t),this.sceneBinding){if(this.sceneBinding.scene===s)return this;throw Error("The solver is already attached to another scene")}let i=s.onBeforeRenderObservable.add(()=>{Hc(this,Math.max(0,s.getEngine().getDeltaTime()/1e3),t,e)}),r=s.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:s,before:i,dispose:r},this}function Yo(){let s=this.sceneBinding;return s&&(s.scene.onBeforeRenderObservable.remove(s.before),s.scene.onDisposeObservable.remove(s.dispose),this.sceneBinding=null,this.timeAccumulator=0),this}function Tr(s,e,t,i={}){s.advance=Yf;let r,o;s.bodyIndex=function(a){if(this.destroyed)throw Error("The solver has been destroyed");let l=a;if(a instanceof Jo){if(a.solver!==this)throw Error("Body belongs to another solver");return a.gpuIndex}return typeof l!="number"&&t===3&&(o||(o=new WeakMap,e.bodies.forEach((c,u)=>o.set(c,this.gpuIndex(u)))),l=o.get(a)),this.liveIndex(l),l},s.body=function(a){let l=this.bodyIndex(a);return r??=new Map,r.has(l)||r.set(l,new Jo(this,l)),r.get(l)},s.editBodies=function(a){return Jf.call(this,a,t)},s.setLinearVelocities=function(a,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,a,{linear:l}):(this.velocityBatch??=new Kt(this,t),this.velocityBatch.set(a,l),this)},s.setVelocities=function(a,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,a,l):l?.angular===void 0&&l?.linear!==void 0?this.setLinearVelocities(a,l.linear):l?.linear===void 0&&l?.angular!==void 0?this.setAngularVelocities(a,l.angular):(this.motionBatch??=new yi(this,t),this.motionBatch.set(a,l),this)},s.setAngularVelocities=function(a,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,a,{angular:l}):(this.angularVelocityBatch??=new Kt(this,t,"angular"),this.angularVelocityBatch.set(a,l),this)};let n=s.destroy;return s.destroy=function(){this.destroyed||(this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback?.destroy(),this.editStaging=null,n.call(this),r?.clear(),o=null,e=null)},i.readSelectedBodies?s.readSelectedBodies=i.readSelectedBodies:t===2&&(s.readSelectedBodies=function(a,{posesOnly:l=!1}={}){let c=a instanceof Uint32Array?a:Array.from(a);return c.forEach(u=>this.liveIndex(u)),this.flushEdits(),this.bodyReadback??=new vi(this.device),this.bodyReadback.read(this.bodyBuffer,c,l,6)}),s}var Qo=class extends Ho{constructor(e,t,i){super(e,t,i),Tr(this,null,2)}};var Ea=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
|
|
5097
|
+
if((contacts[gid.x].ids.w&0x80000000u)!=0u){return;}`);this.compile(c,[this.layouts.topo],["degreeContacts","fillContacts"])}this.sensorsEnabled&&oe(this).float(this.bodyBuffer,e*24+7,t?1:0),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}compile(e,t,i){e.includes("fn warmStartBodies(")&&(e=Tc(Dc(Nc(e)))),e.includes("fn dynamicBody(")&&(e=Gc(e));let r=e.includes("fn colorCount(");r&&(this.colorBucketSource=Ln(oo(e)),e=Ln(e));let o=this.device,a=o.createShaderModule({code:e}),n=o.createPipelineLayout({bindGroupLayouts:t});for(let l of i)this.pipes[l]=o.createComputePipeline({layout:n,compute:{module:a,entryPoint:l}});r&&i.includes("colorCount")&&(this.colorBucketPipelines={scan:this.pipes.colorCount},this.bitsetColorBuckets=void 0)}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array of live body indices");for(let r of i)this.liveIndex(r)}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");return this.contactWatch||(this.contactWatch=new xi(this.device,this.bodyBuffer,this.bodyCapacity,e,t,2),i===void 0&&this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,o)=>o),Array(this.bodyCount).fill(!0))),i!==void 0&&(this.watchedIndices=new Set(i),this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,o)=>o),Array.from({length:this.bodyCount},(r,o)=>this.watchedIndices.has(o)))),this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Ai?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}step(){if(this.destroyed)throw Error("The 2D solver has been destroyed");ao(this,this.colorBucketSource),this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep||this.externalAfterStep?Fc.call(this):super.step(),this.contactWatch&&this.contactWatch.run({manifolds:this.contactBuffers[1-this.parity],contacts:this.contactBuffers[1-this.parity],counters:this.counterBuffer},this.contactCapacity,this.eventStep=(this.eventStep??0)+1,this.params.dt)}adapt(e){super.adapt(e),this.colorCap=Math.max(this.colorCap,this.minimumColors)}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.restitution?.before(e),this.externalBeforeStep?.(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e),this.externalAfterStep?.(e)}destroy(){this.passConstantCache=null,this.propertyEdits?.destroy(),!this.destroyed&&(this.detachFromScene(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.filters?.destroy(),this.shapeBuffer?.destroy(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),this.sleeping?.destroy(),super.destroy())}enableSleeping(e={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping?this.sleeping.options=Do(e):this.sleeping=new jo(this,e),this}disableSleeping(){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.destroy(),this.sleeping=null),this}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");return this.sleepEligible.set(e,t),t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,t),this.wakeAll(),this}wakeAll(){return this.sleeping&&(this.sleeping.wakeRequested=!0),this}wakeUp(e){return this.liveIndex(e),this.wakeAll()}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}flushEdits(){this.flushPropertyEdits(),this.sleeping?.wakeRequested&&this.sleeping.before(),this.bodyCommands?.flush()}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}attachToScene(e,{maxSubSteps:t=6,maxFrameTime:i=.05,afterStep:r}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!e?.onBeforeRenderObservable?.add||!e?.onDisposeObservable?.addOnce||!e?.getEngine)throw Error("A Babylon scene is required");if(!Number.isInteger(t)||t<1||!Number.isFinite(i)||i<=0||r!==void 0&&typeof r!="function")throw Error("Scene stepping requires positive frame limits and an optional afterStep function");if(this.sceneBinding){if(this.sceneBinding.scene===e)return this;throw Error("The 2D solver is already attached to another scene")}let o=0,a=e.onBeforeRenderObservable.add(()=>{let l=this.params.dt;if(!(l>0)||!Number.isFinite(l))throw Error("Simulation timestep must be positive and finite");let c=e.getEngine().getDeltaTime()/1e3;o=Math.min(t*l,o+Math.min(i,Math.max(0,c)));for(let u=0;o+1e-12>=l&&u<t&&(this.step(),o=Math.max(0,o-l),r?.(this),!(!this.sceneBinding||this.destroyed));u++);}),n=e.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:e,before:a,dispose:n},this}detachFromScene(){let e=this.sceneBinding;return e&&(e.scene.onBeforeRenderObservable.remove(e.before),e.scene.onDisposeObservable.remove(e.dispose),this.sceneBinding=null),this}};var $o=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s};function me(s,e,t){let i=s&&typeof s.x=="number"?[s.x,s.y,...e>=3?[s.z]:[],...e===4?[s.w]:[]]:Array.isArray(s)||ArrayBuffer.isView(s)&&!(s instanceof DataView)?s:Array.from(s??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i}var $c=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function Yf(s,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new ti:this.editStaging??=new ti;this.editInProgress=!0;try{return Qf.call(this,s,e,i)}finally{this.editInProgress=t}}function Qf(s,e,t){t.count=0;let i,r=(o,a)=>{if(e===3)return me(o,3,a);if(!Number.isFinite(o))throw Error(`${a} must be finite`);return[o,0,0]};for(let o of s){let a=this.bodyIndex(o.body),{isTrigger:n,restitution:l,group:c,collidesWith:u,allowSleep:f}=o;if(n!==void 0||l!==void 0||c!==void 0||u!==void 0||f!==void 0){if(n!==void 0&&typeof n!="boolean")throw Error("isTrigger must be a boolean");if(f!==void 0&&typeof f!="boolean")throw Error("allowSleep must be a boolean");if(l!==void 0&&(!Number.isFinite(l)||l<0||l>1))throw Error("restitution must be between 0 and 1");if(u!==void 0&&c===void 0)throw Error("A collision-mask edit requires group as well");c!==void 0&&$o(c),u!==void 0&&$o(u),(i??=[]).push({index:a,isTrigger:n,restitution:l,group:c,collidesWith:u??4294967295,allowSleep:f})}let d=o.worldPoint===void 0?void 0:me(o.worldPoint,e,"worldPoint");if(o.position!==void 0){let p=me(o.position,e,"position"),m;if(o.rotation!==void 0){if(m=e===3?me(o.rotation,4,"rotation"):[o.rotation],e===3){let h=Math.hypot(...m);if(!h)throw Error("rotation must be nonzero");m=m.map(g=>g/h)}else if(!Number.isFinite(o.rotation))throw Error("angle must be finite")}t.append(a,4,p,e===3?m:void 0,m!==void 0),e===2&&(t.words[(t.count-1)*12+6]=m?.[0]??0)}else if(o.rotation!==void 0)throw Error("A rotation edit requires position; use teleport for a full pose");for(let p=0;p<$c.length;p++){if(p===4)continue;let m=$c[p];if(o[m]===void 0)continue;let g=p===1||p>=5?r(o[m],m):me(o[m],e,m);t.append(a,p,g,p===2||p===3?d:void 0,(p===2||p===3)&&d!==void 0),(p===3||p===6)&&(t.words[(t.count-1)*12+7]=this.params.dt)}}if(this.destroyed)throw Error("The solver was destroyed while reading body edits");t.count&&this.prepareBodyCommands().enqueueValidated(t);for(let o of i??[])o.isTrigger!==void 0&&this.setSensor(o.index,o.isTrigger),o.restitution!==void 0&&this.setRestitution(o.index,o.restitution),o.group!==void 0&&this.setFilters([o.index],[o.group],[o.collidesWith]),o.allowSleep!==void 0&&this.setSleepEnabled(o.index,o.allowSleep);return this}var Jo=class{constructor(e,t){this.solver=e,this.index=t}get gpuIndex(){return this.solver.liveIndex(this.index),this.index}setLinearVelocity(e){return this.solver.setLinearVelocity(this.gpuIndex,e),this}setAngularVelocity(e){return this.solver.setAngularVelocity(this.gpuIndex,e),this}applyImpulse(e,t){return this.solver.applyImpulse(this.gpuIndex,e,t),this}applyForce(e,t){return this.solver.applyForce(this.gpuIndex,e,t),this}applyAngularImpulse(e){return this.solver.applyAngularImpulse(this.gpuIndex,e),this}applyTorque(e){return this.solver.applyTorque(this.gpuIndex,e),this}teleport(e,t){return this.solver.teleport(this.gpuIndex,e,t),this}setTrigger(e){return this.solver.setSensor(this.gpuIndex,e),this.solver.wakeAll(),this}setRestitution(e){return this.solver.setRestitution(this.gpuIndex,e),this.solver.wakeAll(),this}setCollisionGroups(e,t=4294967295){return this.solver.setFilters([this.gpuIndex],[$o(e)],[$o(t)]),this.solver.wakeAll(),this}setSleepEnabled(e){return this.solver.setSleepEnabled(this.gpuIndex,e),this}wakeUp(){return this.solver.liveIndex(this.index),this.solver.wakeAll(),this}readState(){return this.solver.readBodyState(this.gpuIndex)}};function Jc(s,{maxSubSteps:e=6,maxFrameTime:t=.05}={}){if(!Number.isInteger(e)||e<1||!Number.isFinite(t)||t<=0)throw Error("Stepping limits must be positive");let i=s.params.dt;if(!Number.isFinite(i)||i<=0)throw Error("Simulation timestep must be positive and finite");return{dt:i,maxSubSteps:e,maxFrameTime:t}}function Xc(s,e,t,i){if(s.destroyed)throw Error("The solver has been destroyed");if(!Number.isFinite(e)||e<0)throw Error("elapsed must be finite nonnegative seconds");let{dt:r,maxSubSteps:o,maxFrameTime:a}=Jc(s,t);s.timeAccumulator=Math.min(o*r,(s.timeAccumulator??0)+Math.min(a,e));let n=0;for(;s.timeAccumulator+1e-12>=r&&n<o;)s.step(),s.timeAccumulator=Math.max(0,s.timeAccumulator-r),n++,i?.(s);return n}function Kf(s,e){return Xc(this,s,e)}function Xo(s,{afterStep:e,...t}={}){if(this.destroyed)throw Error("The solver has been destroyed");if(!s?.onBeforeRenderObservable?.add||!s?.onDisposeObservable?.addOnce||!s?.getEngine)throw Error("A Babylon scene is required");if(e!==void 0&&typeof e!="function")throw Error("afterStep must be a function");if(Jc(this,t),this.sceneBinding){if(this.sceneBinding.scene===s)return this;throw Error("The solver is already attached to another scene")}let i=s.onBeforeRenderObservable.add(()=>{Xc(this,Math.max(0,s.getEngine().getDeltaTime()/1e3),t,e)}),r=s.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:s,before:i,dispose:r},this}function Yo(){let s=this.sceneBinding;return s&&(s.scene.onBeforeRenderObservable.remove(s.before),s.scene.onDisposeObservable.remove(s.dispose),this.sceneBinding=null,this.timeAccumulator=0),this}function Tr(s,e,t,i={}){s.advance=Kf;let r,o;s.bodyIndex=function(n){if(this.destroyed)throw Error("The solver has been destroyed");let l=n;if(n instanceof Jo){if(n.solver!==this)throw Error("Body belongs to another solver");return n.gpuIndex}return typeof l!="number"&&t===3&&(o||(o=new WeakMap,e.bodies.forEach((c,u)=>o.set(c,this.gpuIndex(u)))),l=o.get(n)),this.liveIndex(l),l},s.body=function(n){let l=this.bodyIndex(n);return r??=new Map,r.has(l)||r.set(l,new Jo(this,l)),r.get(l)},s.editBodies=function(n){return Yf.call(this,n,t)},s.setLinearVelocities=function(n,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,n,{linear:l}):(this.velocityBatch??=new Kt(this,t),this.velocityBatch.set(n,l),this)},s.setVelocities=function(n,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,n,l):l?.angular===void 0&&l?.linear!==void 0?this.setLinearVelocities(n,l.linear):l?.linear===void 0&&l?.angular!==void 0?this.setAngularVelocities(n,l.angular):(this.motionBatch??=new yi(this,t),this.motionBatch.set(n,l),this)},s.setAngularVelocities=function(n,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,n,{angular:l}):(this.angularVelocityBatch??=new Kt(this,t,"angular"),this.angularVelocityBatch.set(n,l),this)};let a=s.destroy;return s.destroy=function(){this.destroyed||(this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback?.destroy(),this.editStaging=null,a.call(this),r?.clear(),o=null,e=null)},i.readSelectedBodies?s.readSelectedBodies=i.readSelectedBodies:t===2&&(s.readSelectedBodies=function(n,{posesOnly:l=!1}={}){let c=n instanceof Uint32Array?n:Array.from(n);return c.forEach(u=>this.liveIndex(u)),this.flushEdits(),this.bodyReadback??=new vi(this.device),this.bodyReadback.read(this.bodyBuffer,c,l,6)}),s}var Qo=class extends Ho{constructor(e,t,i){super(e,t,i),Tr(this,null,2)}};var En=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
|
|
5012
5098
|
struct Command { header:vec4u, value:vec4f, point:vec4f }
|
|
5013
5099
|
struct Manifold { ids:vec4u, geo:vec4f }
|
|
5014
5100
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
@@ -5061,8 +5147,8 @@ fn angularImpulse(b:Body,impulse:vec3f)->vec3f {
|
|
|
5061
5147
|
let index=commands[ranges[i].x].header.x;
|
|
5062
5148
|
if(bodies[index].initialPos.w<0.){bodies[index].initialPos.w=-bodies[index].initialPos.w-1.;}
|
|
5063
5149
|
}
|
|
5064
|
-
`;var
|
|
5065
|
-
${
|
|
5150
|
+
`;var Mn=class extends Or{constructor(e){super(e,En,3)}encodeAfterEdits(e,t){if(!this.hasTeleports)return;let i=this.solver,r=i.device;if(!this.invalidate){let c=r.createShaderModule({code:`const DISPATCH_STRIDE = ${r.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
5151
|
+
${En}`});this.invalidate=r.createComputePipeline({layout:"auto",compute:{module:c,entryPoint:"invalidate"}}),this.clearMarkers=r.createComputePipeline({layout:"auto",compute:{module:c,entryPoint:"clearMarkers"}})}(this.invalidateBody!==i.bodyBuffer||this.invalidateManifolds!==i.contactStorage.manifolds)&&(this.invalidateGroup=r.createBindGroup({layout:this.invalidate.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:3,resource:{buffer:i.contactStorage.manifolds}}]}),this.invalidateBody=i.bodyBuffer,this.invalidateManifolds=i.contactStorage.manifolds);let o=e.beginComputePass();o.setPipeline(this.invalidate),o.setBindGroup(0,this.invalidateGroup);let a=Math.ceil(i.manifoldCapacity/64),n=r.limits.maxComputeWorkgroupsPerDimension;o.dispatchWorkgroups(Math.min(a,n),Math.ceil(a/n)),o.end(),(this.markerBody!==i.bodyBuffer||this.markerCommands!==this.commands||this.markerRanges!==this.ranges||this.markerBytes!==t*8)&&(this.markerGroup=r.createBindGroup({layout:this.clearMarkers.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.commands}},{binding:2,resource:{buffer:this.ranges,size:t*8}}]}),this.markerBody=i.bodyBuffer,this.markerCommands=this.commands,this.markerRanges=this.ranges,this.markerBytes=t*8),o=e.beginComputePass(),o.setPipeline(this.clearMarkers),o.setBindGroup(0,this.markerGroup);let l=Math.ceil(t/64);o.dispatchWorkgroups(Math.min(l,n),Math.ceil(l/n)),o.end()}},Ko=class extends gi{constructor(e,t,i){super(e,t,i),this.nativeRef=t,Tr(this,{bodies:t.bodies},3)}rigidAt(e){return this.nativeBodies||(this.nativeBodies=[],this.nativeRef.bodies.forEach((t,i)=>this.nativeBodies[this.gpuIndex(i)]=t)),this.nativeBodies[e]}rewriteBodies(e,t){if(super.rewriteBodies(e,t),this.nativeBodies)for(let i=0;i<e.length;i++)this.nativeBodies[e[i]]=t[i]}liveIndex(e){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 3D GPU body index is required")}queueBodyCommand(e,t,i,r,o=!1){return this.liveIndex(e),this.prepareBodyCommands(),this.bodyCommands.enqueue(e,t,i,r,o),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands??=new Mn(this)}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,me(t,3,"velocity"))}setAngularVelocity(e,t){return this.queueBodyCommand(e,1,me(t,3,"angular velocity"))}applyImpulse(e,t,i){return this.queueBodyCommand(e,2,me(t,3,"impulse"),i===void 0?void 0:me(i,3,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...me(t,3,"force"),this.params.dt],i===void 0?void 0:me(i,3,"point"),i!==void 0)}applyAngularImpulse(e,t){return this.queueBodyCommand(e,5,me(t,3,"angular impulse"))}applyTorque(e,t){return this.queueBodyCommand(e,6,[...me(t,3,"torque"),this.params.dt])}teleport(e,t,i){let r=me(t,3,"position"),o;if(i!==void 0){o=me(i,4,"rotation");let a=Math.hypot(...o);if(!a)throw Error("rotation must be nonzero");o=o.map(n=>n/a)}return this.queueBodyCommand(e,4,r,o,o!==void 0)}flushEdits(){if(this.destroyed)throw Error("The 3D solver has been destroyed");this.flushPropertyEdits(),this.sleeping?.wakeRequested&&this.sleeping.before(),this.bodyCommands?.flush()}readBodies(){return this.flushEdits(),super.readBodies()}readSelectedBodies(e,{posesOnly:t=!1}={}){let i=e instanceof Uint32Array?e:Array.from(e);return i.forEach(r=>this.liveIndex(r)),this.flushEdits(),this.bodyReadback??=new vi(this.device),this.bodyReadback.read(this.bodyBuffer,i,t)}async readBodyState(e){let t=await this.readSelectedBodies([e]),i=this.rigidAt(e)?.mass??t[19];return{position:Array.from(t.subarray(0,3)),rotation:Array.from(t.subarray(4,8)),linearVelocity:Array.from(t.subarray(32,35)),angularVelocity:Array.from(t.subarray(36,39)),mass:i,effectiveMass:t[19],sleeping:i>0&&t[19]===0}}raycastAll(...e){return this.flushEdits(),super.raycastAll(...e)}sphereCastAll(...e){return this.flushEdits(),super.sphereCastAll(...e)}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array");i.forEach(r=>this.liveIndex(r))}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");if(this.contactWatch||(this.contactWatch=new xi(this.device,this.bodyBuffer,this.bodyCapacity,e,t),i===void 0&&(i=Array.from({length:this.bodyCount},(r,o)=>o))),i!==void 0){let r=new Set(i);this.contactWatch.setWatched(Array.from({length:this.bodyCount},(o,a)=>a),Array.from({length:this.bodyCount},(o,a)=>r.has(a)))}return this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Ai?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}setSensor(e,t){super.setSensor(e,t),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");let i=this.rigidAt(e);if(!i)throw Error("Sleep eligibility requires a scene body");return i.allowSleep=t,t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,i.mass,t),this.wakeAll(),this}enableSleeping(e={},t={}){let i=e.bodies?e:this.nativeRef;return super.enableSleeping(i,e.bodies?t:e),this}disableSleeping(){return this.flushEdits(),this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.dispose(),this.sleeping=null),this}attachToScene(e,t){return Xo.call(this,e,t)}detachFromScene(){return Yo.call(this)}step(){if(this.destroyed)throw Error("The 3D solver has been destroyed");super.step(),this.contactWatch?.run(this.contactStorage,this.manifoldCapacity,this.stepCount,this.params.dt)}destroy(){this.destroyed||(this.detachFromScene(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),super.destroy(),this.destroyed=!0)}};function xs(s,e,t){if(s.split(e).length!==2)throw Error("Pinned solver changed; review the cloth kernel integration");return s.replace(e,t)}var ii=xs(Ge," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if (j >= params.jointCount) { return; }
|
|
5066
5152
|
if (info[j].x == T_SPRING) {
|
|
5067
5153
|
// Finite material stiffness: Eq. 16, with no hard-constraint multiplier.
|
|
5068
5154
|
joints[j].penLin.x = min(joints[j].penLin.w, max(PENALTY_MIN, joints[j].penLin.x * params.gamma));
|
|
@@ -5098,7 +5184,7 @@ ${Ea}`});this.invalidate=r.createComputePipeline({layout:"auto",compute:{module:
|
|
|
5098
5184
|
let v1 = y1/d1 - l21*v2 - l31*v3;
|
|
5099
5185
|
bodies[i].pos = vec4f(bodies[i].pos.xyz - vec3f(v1,v2,v3), bodies[i].pos.w);
|
|
5100
5186
|
return;
|
|
5101
|
-
}`);var $c=ii;var Kc=8,Jc=16,Xi=1e-6,Ia=1e-5,Kf=1e-6,Ra=34028234663852886e22,Zo=0,Zc=1,en=2;function Xc(s){let e=s.positionAng;return{center:s.positionLin,half:ne(S(),s.size,.5),axis:[ae(S(),e,[1,0,0]),ae(S(),e,[0,1,0]),ae(S(),e,[0,0,1])]}}var Ji=(s,e)=>Math.abs(V(s,e));function tn(s,e){let t=V(e,s.axis[0])>=0?1:-1,i=V(e,s.axis[1])>=0?1:-1,r=V(e,s.axis[2])>=0?1:-1,o=D(S(),s.center,s.axis[0],s.half[0]*t);return D(o,o,s.axis[1],s.half[1]*i),D(o,o,s.axis[2],s.half[2]*r)}function eu(s,e){return e===0?{u:s.axis[1],v:s.axis[2],extentU:s.half[1],extentV:s.half[2]}:e===1?{u:s.axis[0],v:s.axis[2],extentU:s.half[0],extentV:s.half[2]}:{u:s.axis[0],v:s.axis[1],extentU:s.half[0],extentV:s.half[1]}}function Zf(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]);return{normal:r,center:o,...eu(s,e)}}function ed(s,e){let t=0,i=-Ra;for(let r=0;r<3;r++){let o=Ji(s.axis[r],e);o>i&&(i=o,t=r)}return t}function td(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]),{u:n,v:a,extentU:l,extentV:c}=eu(s,e),u=(f,d)=>{let p=D(S(),o,n,f*l);return D(p,p,a,d*c)};return[u(1,1),u(-1,1),u(-1,-1),u(1,-1)]}function id(s,e,t){let i=[];if(s.length===0)return i;let r=s[s.length-1],o=V(e,r)-t;for(let n of s){let a=V(e,n)-t,l=o<=Ia,c=a<=Ia;if(l!==c){let u=0,f=o-a;Math.abs(f)>Xi&&(u=ce(o/f,0,1)),i.length<Jc&&i.push(D(S(),r,J(S(),n,r),u))}c&&i.length<Jc&&i.push(n),r=n,o=a}return i}var rn=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(S(),Li(S(),e,t),.5);for(let a of this.midpoints)if(Ne(J(S(),r,a))<Kf)return;if(this.contacts.length>=Kc)return;let{bodyA:o,bodyB:n}=this;this.contacts.push({feature:i,rA:Te(S(),o.positionAng,J(S(),e,o.positionLin)),rB:Te(S(),n.positionAng,J(S(),t,n.positionLin))}),this.midpoints.push(r)}};function za(s,e,t,i,r,o,n,a){let l=Ne(i);if(l<Xi)return!0;let c=1/Math.sqrt(l),u=ne(S(),i,c);V(u,t)<0&&ee(u,u);let f=Math.abs(V(t,u)),d=s.half[0]*Ji(u,s.axis[0])+s.half[1]*Ji(u,s.axis[1])+s.half[2]*Ji(u,s.axis[2]),p=e.half[0]*Ji(u,e.axis[0])+e.half[1]*Ji(u,e.axis[1])+e.half[2]*Ji(u,e.axis[2]),m=f-(d+p);return m>0?!1:((!a.valid||m>a.separation)&&(a.valid=!0,a.type=r,a.indexA=o,a.indexB=n,a.separation=m,a.normalAB=u),!0)}function Yc(s,e,t){let i=(e+1)%3,r=(e+2)%3,o=V(t,s.axis[i])>=0?1:-1,n=V(t,s.axis[r])>=0?1:-1,a=D(S(),s.center,s.axis[i],s.half[i]*o);D(a,a,s.axis[r],s.half[r]*n);let l=ne(S(),s.axis[e],s.half[e]);return[J(S(),a,l),Li(S(),a,l)]}function rd(s,e,t,i){let r=J(S(),e,s),o=J(S(),i,t),n=J(S(),s,t),a=V(r,r),l=V(o,o),c=V(o,n),u=0,f=0;if(a<=Xi&&l<=Xi)return[s,t];if(a<=Xi)f=ce(c/l,0,1);else{let d=V(r,n);if(l<=Xi)u=ce(-d/a,0,1);else{let p=V(r,o),m=a*l-p*p;Math.abs(m)>Xi&&(u=ce((p*c-d*l)/m,0,1)),f=(p*u+c)/l,f<0?(f=0,u=ce(-d/a,0,1)):f>1&&(f=1,u=ce((p-d)/a,0,1))}}return[D(S(),s,r,u),D(S(),t,o,f)]}function Qc(s,e,t,i,r,o,n){let a=r?t:i,l=r?i:t,c=r?n:ee(S(),n),u=Zf(a,o,c),f=ed(l,u.normal),d=td(l,f,u.normal),p=[[u.u,u.extentU],[ee(S(),u.u),u.extentU],[u.v,u.extentV],[ee(S(),u.v),u.extentV]];for(let[g,b]of p)if(d=id(d,g,V(g,u.center)+b),!d.length)return[];let m=new rn(s,e),h=(r?Zo:Zc)<<24|(o&255)<<16|(f&255)<<8;for(let g=0;g<d.length&&m.contacts.length<Kc;g++){let b=d[g],y=V(J(S(),b,u.center),u.normal);if(y>Ia)continue;let A=D(S(),b,u.normal,-y),v=r?A:b,x=r?b:A;m.add(v,x,h|g&255)}return m.contacts.length||m.add(tn(t,n),tn(i,ee(S(),n)),h),m.contacts}function sd(s,e,t,i,r,o,n){let[a,l]=Yc(t,r,n),[c,u]=Yc(i,o,ee(S(),n)),[f,d]=rd(a,l,c,u),p=new rn(s,e),m=en<<24|(r&255)<<8|o&255;return p.add(f,d,m),p.contacts.length||p.add(tn(t,n),tn(i,ee(S(),n)),m),p.contacts}function tu(s,e,t,i=!0){let r=Xc(s),o=Xc(e),n=J(S(),o.center,r.center),a={type:Zo,indexA:0,indexB:0,separation:-Ra,normalAB:S(),valid:!1},l={type:en,indexA:0,indexB:0,separation:-Ra,normalAB:S(),valid:!1};for(let f=0;f<3;f++)if(!za(r,o,n,r.axis[f],Zo,f,-1,a))return[];for(let f=0;f<3;f++)if(!za(r,o,n,o.axis[f],Zc,-1,f,a))return[];let c=S();for(let f=0;f<3;f++)for(let d=0;d<3;d++)if(Dt(c,r.axis[f],o.axis[d]),!za(r,o,n,c,en,f,d,l))return[];if(!a.valid)return[];let u=a;return l.valid&&(i?l.separation>.95*a.separation+.01:.95*l.separation>a.separation+.01)&&(u=l),_s(t,ee(S(),u.normalAB)),u.type===en?sd(s,e,r,o,u.indexA,u.indexB,u.normalAB):u.type===Zo?Qc(s,e,r,o,!0,u.indexA,u.normalAB):Qc(s,e,r,o,!1,u.indexB,u.normalAB)}var de=(s,e)=>s.map((t,i)=>t+e[i]),T=(s,e)=>s.map((t,i)=>t-e[i]),q=(s,e)=>s.map(t=>t*e),F=(s,e)=>s.reduce((t,i,r)=>t+i*e[r],0),xt=(s,e)=>[s[1]*e[2]-s[2]*e[1],s[2]*e[0]-s[0]*e[2],s[0]*e[1]-s[1]*e[0]],Tt=s=>q(s,1/(Math.hypot(...s)||1)),Ee=(s,e)=>Array.from(ae(S(),s,e)),iu=(s,e)=>Te(S(),s.positionAng,T(e,s.positionLin)),Ot=(s,e)=>de(s.positionLin,Ee(s.positionAng,e)),od=[[1,3,7,5],[0,4,6,2],[2,6,7,3],[0,1,5,4],[4,5,7,6],[0,2,3,1]],nd=[[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]];function jr(s){if(De(s)||Fe(s)){let n=(De(s),s.size[0]/2),a=Fe(s)?Math.max(0,s.size[1]/2-n):0;return{body:s,radius:n,spine:[Ot(s,[0,-a,0]),Ot(s,[0,a,0])]}}let e=Ct(s),t=e?Array.from({length:e.vertices.length/3},(n,a)=>Ot(s,e.vertices.slice(a*3,a*3+3))):Array.from({length:8},(n,a)=>Ot(s,s.size.map((l,c)=>l/2*(a>>c&1?1:-1)))),i=e?e.faces.map(n=>({vertices:n.verts.map(a=>t[a]),normal:Ee(s.positionAng,n.normal)})):od.map((n,a)=>({vertices:n.map(l=>t[l]),normal:Ee(s.positionAng,nd[a])})),r=[],o=new Set;for(let n of i)for(let a=0;a<n.vertices.length;a++){let l=n.vertices[a],c=n.vertices[(a+1)%n.vertices.length],u=t.indexOf(l),f=t.indexOf(c),d=`${Math.min(u,f)},${Math.max(u,f)}`;o.has(d)||(o.add(d),r.push([l,c]))}return{body:s,vertices:t,faces:i,edges:r}}function As(s,e){let t=s.spine??s.vertices,i=t[0];for(let r of t)F(r,e)>F(i,e)&&(i=r);return s.spine?de(i,q(e,s.radius)):i}function ad(s,e,t){let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,F(T(t,s),i)/(F(i,i)||1)))))}function sn([s,e],[t,i]){let r=T(e,s),o=T(i,t),n=T(s,t),a=F(r,r),l=F(o,o),c=F(o,n),u=0,f=0,d=p=>Math.max(0,Math.min(1,p));if(a<1e-16)f=l>1e-16?d(c/l):0;else{let p=F(r,n);if(l<1e-16)u=d(-p/a);else{let m=F(r,o),h=a*l-m*m;u=h>1e-12*a*l?d((m*c-p*l)/h):0,f=(m*u+c)/l,f<0?(f=0,u=d(-p/a)):f>1&&(f=1,u=d((m-p)/a))}}return[de(s,q(r,u)),de(t,q(o,f))]}function ld(s,e,t){let i=[];for(let r=0;r<s.length;r++){let o=s[r],n=s[(r+1)%s.length],a=F(o,e)-t,l=F(n,e)-t;a<=1e-8&&i.push(o),a<0!=l<0&&i.push(de(o,q(T(n,o),a/(a-l))))}return i}function cd(s,e,t,i){let r=T(t,e),o=T(i,e),n=T(s,e),a=F(r,n),l=F(o,n);if(a<=0&&l<=0)return e;let c=T(s,t),u=F(r,c),f=F(o,c);if(u>=0&&f<=u)return t;let d=a*f-u*l;if(d<=0&&a>=0&&u<=0)return de(e,q(r,a/(a-u)));let p=T(s,i),m=F(r,p),h=F(o,p);if(h>=0&&m<=h)return i;let g=m*l-a*h;if(g<=0&&l>=0&&h<=0)return de(e,q(o,l/(l-h)));let b=u*h-m*f;if(b<=0&&f-u>=0&&m-h>=0)return de(t,q(T(i,t),(f-u)/(f-u+m-h)));let y=1/(b+g+d);return de(e,de(q(r,g*y),q(o,d*y)))}function ud(s,e){let t=null,i=1/0;for(let r of s.faces)for(let o=1;o<r.vertices.length-1;o++){let n=cd(e,r.vertices[0],r.vertices[o],r.vertices[o+1]),a=F(T(e,n),T(e,n));a<i&&(t=n,i=a)}return t}function ru(s,e,t,i,r){if(!De(s)&&!De(e)&&!Fe(s)&&!Fe(e)&&!Ct(s)&&!Ct(e))return tu(s,e,t,s.solver.faceBias!==!1);i??=jr(s),r??=jr(e);let o=-1/0,n=[0,0,1],a=null,l=(f,d=null)=>{if(Math.hypot(...f)<1e-9)return;let p=Tt(f),m=F(T(As(i,q(p,-1)),As(r,p)),p),h=F(T(As(r,q(p,-1)),As(i,p)),p);h>m&&(m=h,p=q(p,-1)),m>o+1e-7&&(o=m,n=p,a=d)};for(let f of[i,r])for(let d of f.faces??[])l(d.normal);if(i.edges&&r.edges)for(let f of i.edges)for(let d of r.edges)l(xt(T(f[1],f[0]),T(d[1],d[0])),[f,d]);if(i.spine&&r.spine){let f=sn(i.spine,r.spine);l(T(...f))}for(let[f,d]of[[i,r],[r,i]])if(f.spine&&d.faces){for(let p of d.vertices)l(T(p,ad(...f.spine,p)));for(let p of d.edges){let m=sn(f.spine,p);l(T(...m))}}if(o===-1/0&&l(T(s.positionLin,e.positionLin)),o>0)return[];o===-1/0&&(n=[0,0,1],o=-(i.radius+r.radius));let c=Tt(xt(n,Math.abs(n[0])<.7?[1,0,0]:[0,1,0]));t.set([...n,...c,...xt(n,c)]);let u=[];if(i.spine&&r.spine){let[f,d]=sn(i.spine,r.spine);u=[[de(f,q(n,-i.radius)),de(d,q(n,r.radius))]]}else if(i.spine||r.spine){let f=i.spine?i:r,d=i.spine?r:i,p=i.spine?q(n,-1):n,m=As(f,p),h=ud(d,m);u=[i.spine?[m,h]:[h,m]]}else if(a)u=[sn(...a)];else{let f=(A,v)=>A.faces.reduce((x,w)=>F(w.normal,v)>F(x.normal,v)?w:x,A.faces[0]),d=f(i,q(n,-1)),p=f(r,n),m=F(d.normal,q(n,-1))>=F(p.normal,n),h=m?d:p,b=f(m?r:i,q(h.normal,-1)).vertices,y=q(h.vertices.reduce((A,v)=>de(A,v),[0,0,0]),1/h.vertices.length);for(let A=0;A<h.vertices.length&&b.length;A++){let v=h.vertices[A],x=h.vertices[(A+1)%h.vertices.length],w=Tt(xt(T(x,v),h.normal));F(T(y,v),w)>0&&(w=q(w,-1)),b=ld(b,w,F(v,w))}for(let A of b){let v=F(T(A,h.vertices[0]),h.normal);if(v<=1e-6){let x=T(A,q(h.normal,v));u.push(m?[x,A]:[A,x])}}}return u.slice(0,8).map(([f,d],p)=>({feature:p+1,rA:iu(s,f),rB:iu(e,d)}))}var on=(s,e,t)=>Math.max(e,Math.min(t,s)),fd=(s,e)=>[F(s.slice(0,3),e),F(s.slice(3,6),e),F(s.slice(6,9),e)];function ja(s,e=!1){if(e){s.rest=Math.hypot(...T(Ot(s.bodyA,s.rA),Ot(s.bodyB,s.rB)));return}let t=s.bodyA?.positionAng??[0,0,0,1];s.cpuRest=Gr(jt(),xn(jt(),t),s.bodyB.positionAng),s.evaluateAng=i=>{let r=Gr(jt(),s.bodyA?.positionAng??[0,0,0,1],s.cpuRest);ct(i,r,s.bodyB.positionAng);for(let o=0;o<3;o++)i[o]*=s.torqueArm;return i}}var Oa=class extends ni{initialize(){let e=this.bodyA,t=this.bodyB;if(!((e.group??4294967295)&(t.collidesWith??4294967295))||!((t.group??4294967295)&(e.collidesWith??4294967295)))return!1;let i=this.contacts,r=De(e)||De(t)||Fe(e)||Fe(t);this.friction=Math.sqrt(e.friction*t.friction),this.contacts=ru(e,t,this.basis,this.solver.cpuGeometry.get(e),this.solver.cpuGeometry.get(t)).map(o=>{let n=i.find(a=>a.feature===o.feature);return{feature:o.feature,rA:n?.stick&&!r?n.rA:o.rA,rB:n?.stick&&!r?n.rB:o.rB,C0:S(),penalty:n?n.penalty.slice():S(),lambda:n?n.lambda.slice():S(),stick:n?.stick??!1}});for(let o of this.contacts){o.C0.set(fd(this.basis,T(Ot(e,o.rA),Ot(t,o.rB)))),o.C0[0]+=Ms;for(let n=0;n<3;n++)o.lambda[n]*=this.solver.alpha*this.solver.gamma,o.penalty[n]=on(o.penalty[n]*this.solver.gamma,Et,Ve);if(this.solver.massPenalty!==!1){let n=Math.min(e.mass>0?e.mass:1/0,t.mass>0?t.mass:1/0);o.penalty[0]=Math.max(o.penalty[0],on(n/this.solver.dt**2,Et,Ve))}}return this.contacts.length>0}updatePrimal(...e){!this.bodyA.isTrigger&&!this.bodyB.isTrigger&&super.updatePrimal(...e)}updateDual(...e){!this.bodyA.isTrigger&&!this.bodyB.isTrigger&&super.updateDual(...e)}},Ta=class extends Ft{constructor(e,t,i){super(e,t.bodyA,t.bodyB),this.options={...i},this.pen=[Et,Et],this.lam=[0,0],this.source=t;let r=i.axisA;this.ta=Tt(Math.abs(r[0])>Math.abs(r[2])?[-r[1],r[0],0]:[0,-r[2],r[1]]);let o=this.bodyA?Ee(this.bodyA.positionAng,this.ta):this.ta,n=Array.from(Te(S(),this.bodyB.positionAng,o));n=T(n,q(i.axisB,F(n,i.axisB))),this.tb=Tt(n)}frame(){let e=this.bodyA?Ee(this.bodyA.positionAng,this.options.axisA):this.options.axisA,t=this.bodyA?Ee(this.bodyA.positionAng,this.ta):this.ta,i=Ee(this.bodyB.positionAng,this.tb);return{axis:e,angle:Math.atan2(F(e,xt(t,i)),F(t,i))}}initialize(){let e=this.frame().angle;this.c0=this.options.type==="motor"?[e,0]:[Math.min(0,e-this.options.minAngle),Math.max(0,e-this.options.maxAngle)];for(let t=0;t<2;t++)this.lam[t]*=this.solver.alpha*this.solver.gamma,this.pen[t]=on(this.pen[t]*this.solver.gamma,Et,Ve);return!this.source.broken}evaluate(e){let{angle:t}=this.frame(),i=this.options,r=i.type==="motor"?[Math.atan2(Math.sin(t-this.c0[0]),Math.cos(t-this.c0[0]))-i.speed*this.solver.dt,0]:[t-i.minAngle-e*this.c0[0],t-i.maxAngle-e*this.c0[1]],o=r.map((n,a)=>this.pen[a]*n+this.lam[a]);return{c:r,f:i.type==="motor"?[on(o[0],-i.maxTorque,i.maxTorque),0]:[Math.min(0,o[0]),Math.max(0,o[1])]}}updatePrimal(e,t,i){let{f:r}=this.evaluate(t),o=this.options;if(o.type==="motor"&&o.maxTorque===0)return;let n=q(this.frame().axis,e===this.bodyA?-1:1);for(let a=0;a<(o.type==="motor"?1:2);a++)if(!(o.type==="limit"&&r[a]===0))for(let l=0;l<3;l++){i.rhsAng[l]+=n[l]*r[a];for(let c=0;c<3;c++)i.lhsAng[l*3+c]+=n[l]*n[c]*this.pen[a]}}updateDual(e){let{c:t,f:i}=this.evaluate(e);this.lam=i;for(let r=0;r<2;r++)this.pen[r]=Math.min(Ve,this.pen[r]+this.solver.betaAng*(this.options.type==="motor"?Math.abs(t[r]):r===0?Math.max(0,-t[r]):Math.max(0,t[r])))}};function nn(s){if(!s.cpuPrepared){s.cpuPrepared=!0;for(let e of[...s.forces]){let t=rs(e);if(t){e.unlinkFromBodies(),s.forces.splice(s.forces.indexOf(e),1),e.cpuAngular=new Ta(s,e,t);continue}if(e instanceof ke&&e.stiffnessAng>0&&ja(e),e instanceof ft){let i=e.initialize.bind(e);e.initialize=()=>{if(e.broken)return!1;let r=Ui(e);if(r){let o=Math.max(0,Math.hypot(...T(Ot(e.bodyA,e.rA),Ot(e.bodyB,e.rB)))-e.rest);if(o/Math.max(e.rest,1e-6)>r.breakStrain||o*e.stiffness>r.breakForce)return e.broken=!0,!1}return i()}}}}}function su(s){let e=s.bodies;s.cpuGeometry=new Map;for(let l of e)(De(l)||Fe(l)||Ct(l))&&s.cpuGeometry.set(l,jr(l));let t=e.filter(l=>l.mass>0).slice(0,1024).map(l=>l.radius).sort((l,c)=>l-c),i=Math.max(.1,(t[Math.floor(t.length/2)]??1)*2),r=new Map,o=[],n=new Set,a=(l,c)=>{if(l===c||e[l].mass<=0&&e[c].mass<=0)return;let u=Math.max(l,c),f=Math.min(l,c),d=u*e.length+f;if(n.has(d))return;n.add(d);let p=e[u],m=e[f];if(!((p.group??4294967295)&(m.collidesWith??4294967295))||!((m.group??4294967295)&(p.collidesWith??4294967295)))return;let h=p.radius+m.radius;F(T(p.positionLin,m.positionLin),T(p.positionLin,m.positionLin))<=h*h&&!p.constrainedTo(m)&&new Oa(s,p,m)};for(let l=0;l<e.length;l++){let c=e[l],u=c.positionLin.map(d=>Math.floor((d-c.radius)/i)),f=c.positionLin.map(d=>Math.floor((d+c.radius)/i));if((f[0]-u[0]+1)*(f[1]-u[1]+1)*(f[2]-u[2]+1)>128){o.push(l);continue}for(let d=u[0];d<=f[0];d++)for(let p=u[1];p<=f[1];p++)for(let m=u[2];m<=f[2];m++){let h=`${d},${p},${m}`,g=r.get(h)??[];for(let b of g)a(l,b);g.push(l),r.set(h,g)}}for(let l of o)for(let c=0;c<e.length;c++)a(l,c)}function an(s,e){if(!(e===3?s.bodies.some(r=>r.restitution>0):[...s.restitution.values()].some(r=>r>0)))return null;if(e===2)return Float64Array.from(s.velocity);let i=new Float64Array(s.bodies.length*6);return s.bodies.forEach((r,o)=>{i.set(r.velocityLin,o*6),i.set(r.velocityAng,o*6+3)}),i}function ln(s,e,t){if(!t)return;let i=e===3?s.bodies.length:s.bodyCount,r=e===3?6:4,o=e===3?new Float64Array(i*r):s.velocity;e===3&&s.bodies.forEach((h,g)=>{o.set(h.velocityLin,g*r),o.set(h.velocityAng,g*r+3)});let n=h=>e===3?s.bodies[h].mass>0&&!s.bodies[h].sleeping?1/s.bodies[h].mass:0:s.dynamic[h]?1/s.shape[h*4+2]:0,a=(h,g)=>{if(!n(h))return e===3?[0,0,0]:0;if(e===2)return s.shape[h*4+3]>0?g/s.shape[h*4+3]:0;let b=s.bodies[h],y=b.positionAng,A=Ee([-y[0],-y[1],-y[2],y[3]],g);return Ee(y,A.map((v,x)=>b.moment[x]>0?v/b.moment[x]:0))},l=(h,g,b)=>{let y=g*r;return e===3?T(h.subarray(y,y+3),xt(b,h.subarray(y+3,y+6))):[h[y]-h[y+2]*b[1],h[y+1]+h[y+2]*b[0]]},c=(h,g)=>e===3?xt(h,g):h[0]*g[1]-h[1]*g[0],u=(h,g)=>e===3?F(h,g):h*g,f;if(e===3){let h=new Map(s.bodies.map((g,b)=>[g,b]));f=s.forces.filter(g=>g.contacts?.length&&!g.bodyA.isTrigger&&!g.bodyB.isTrigger).map(g=>{let b=(y,A)=>{let v=[0,0,0];for(let x of g.contacts){let w=Ee(y.positionAng,x[A]);for(let B=0;B<3;B++)v[B]+=w[B]/g.contacts.length}return v};return{a:h.get(g.bodyA),b:h.get(g.bodyB),n:g.basis.slice(0,3),ra:b(g.bodyA,"rA"),rb:b(g.bodyB,"rB"),e:Math.max(g.bodyA.restitution??0,g.bodyB.restitution??0)}})}else{let h=(g,b)=>{let y=Math.cos(s.pose[g*4+2]),A=Math.sin(s.pose[g*4+2]);return[y*b[0]-A*b[1],A*b[0]+y*b[1]]};f=s.contactPairs.filter(g=>!s.sensors.has(g.a)&&!s.sensors.has(g.b)).map(g=>({...g,ra:h(g.a,g.ra),rb:h(g.b,g.rb),e:Math.max(s.restitution.get(g.a)??0,s.restitution.get(g.b)??0)}))}let d=new Uint32Array(i);if(e===3)s.bodies.forEach((h,g)=>{d[g]=h.forces.length});else for(let h=0;h<i;h++)d[h]=s.adjStart[h+1]-s.adjStart[h];let p=[];for(let h of f){if(h.e<=0)continue;let g=F(h.n,T(l(t,h.a,h.ra),l(t,h.b,h.rb)));if(g>=-1)continue;let b=c(h.ra,h.n),y=c(h.rb,h.n),A=a(h.a,b),v=a(h.b,y),x=n(h.a)+n(h.b)+u(b,A)+u(y,v);x>0&&p.push({...h,inertiaA:A,inertiaB:v,k:x,target:-h.e*g,impulse:0})}let m=new Float64Array(o.length);for(let h=0;h<12&&p.length;h++){m.fill(0);for(let g of p){let b=F(g.n,T(l(o,g.a,g.ra),l(o,g.b,g.rb))),y=Math.max(0,g.impulse+(g.target-b)/(g.k*Math.max(1,d[g.a],d[g.b]))),A=y-g.impulse;g.impulse=y;for(let[v,x,w]of[[g.a,1,g.inertiaA],[g.b,-1,g.inertiaB]]){let B=v*r,L=n(v);for(let M=0;M<e;M++)m[B+M]+=x*A*L*g.n[M];if(e===3)for(let M=0;M<3;M++)m[B+3+M]+=x*A*w[M];else m[B+2]+=x*A*w}}for(let g=0;g<o.length;g++)o[g]+=m[g]}e===3&&s.bodies.forEach((h,g)=>{h.velocityLin.set(o.subarray(g*r,g*r+3)),h.velocityAng.set(o.subarray(g*r+3,g*r+6))})}function ou(s,e,t){let i=e.windSpeed??0;if(!or(s)||s.mass<=0||i===0)return null;let r=e.windAngle??0,o=[Math.cos(r),Math.sin(r),0],n=[-o[1],o[0],0],a=s.positionLin,l=t*e.dt,c=F(a,o),u=F(a,n),f=.5*Math.sin(1.1*l-.35*c+.2*u)+.3*Math.sin(2.9*l-.8*c+.6*a[2])+.2*Math.sin(6.1*l-1.7*c+1.3*a[2]+.9*u),d=.6*Math.sin(1.7*l-.6*c+.4*a[2])+.4*Math.sin(4.3*l-1.3*c-.7*a[2]),p=Math.sin(2.3*l-.9*c+1.1*u),m=e.windGust??.4,h=o.map((x,w)=>i*x*(1+m*f)+m*i*(n[w]*.25*d+(w===2?.1*p:0))-s.velocityLin[w]),g=Ee(s.positionAng,[0,0,1]),b=(e.windPressure??.72)*s.size[0]*s.size[1]/s.mass,y=F(h,g),A=h.map((x,w)=>x-g[w]*y),v=b*y*Math.abs(y)/(1+2*e.dt*b*Math.abs(y));return g.map((x,w)=>x*v+A[w]*.06*b*Math.hypot(...A))}var dd=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),cn=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;massPenalty=!0;faceBias=!0;up=[0,0,1];cpuStep=0;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,dd())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=S(),n=S();for(let a=this.bodies.length-1;a>=0;a--){let l=this.bodies[a];if(l.mass<=0)continue;Te(o,l.positionAng,J(o,e,l.positionLin)),Te(n,l.positionAng,t);let c=0,u=1/0,f=!0;for(let p=0;p<3&&f;p++){let m=l.size[p]*.5;if(Math.abs(n[p])<1e-6){(o[p]<-m||o[p]>m)&&(f=!1);continue}let h=1/n[p],g=(-m-o[p])*h,b=(m-o[p])*h;g>b&&([g,b]=[b,g]),c=Math.max(c,g),u=Math.min(u,b),c>u&&(f=!1)}if(!f)continue;let d=c>=0?c:u;d<0||(!r||d<r.t)&&(r={body:l,local:D(S(),o,n,d),t:d})}return r}sys=zs();dxLin=S();dxAng=S();dp=S();accel=S();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:o}=this,n=an(this,3);nn(this),su(this);let a=[];for(let l of this.forces)l.initialize()?a.push(l):l.unlinkFromBodies();this.forces=a;for(let l=i.length-1;l>=0;l--){let c=i[l];D(c.inertialLin,c.positionLin,c.velocityLin,e),c.mass>0&&D(c.inertialLin,c.inertialLin,this.up,t*e*e);let u=ou(c,this,this.cpuStep);u&&D(c.inertialLin,c.inertialLin,u,e*e),si(c.inertialAng,c.positionAng,nu(c.velocityAng,e)),_t(this.accel,J(this.accel,c.velocityLin,c.prevVelocityLin),e);let f=V(this.accel,this.up)*Ps(t),d=ce(f/Math.abs(t),0,1);Number.isFinite(d)||(d=0),c.initialLin.set(c.positionLin),c.initialAng.set(c.positionAng),c.mass>0&&!c.sleeping&&(D(c.positionLin,c.positionLin,c.velocityLin,e),D(c.positionLin,c.positionLin,this.up,t*d*e*e),si(c.positionAng,c.positionAng,nu(c.velocityAng,e)))}for(let l=0;l<this.iterations;l++){for(let c=i.length-1;c>=0;c--){let u=i[c];if(u.mass<=0||u.sleeping)continue;let f=u.mass,d=u.moment;Ce(r.lhsLin,f/(e*e),f/(e*e),f/(e*e)),Ce(r.lhsAng,d[0]/(e*e),d[1]/(e*e),d[2]/(e*e)),r.lhsCross.fill(0),ue(r.rhsLin,r.lhsLin,J(o,u.positionLin,u.inertialLin)),ue(r.rhsAng,r.lhsAng,ct(o,u.positionAng,u.inertialAng));let p=u.forces;for(let h=p.length-1;h>=0;h--)p[h].updatePrimal(u,this.alpha,r);let m=u.moment.every(h=>h===0);m&&(Ce(r.lhsAng,1,1,1),r.lhsCross.fill(0),r.rhsAng.fill(0)),Es(r.lhsLin,r.lhsAng,r.lhsCross,ee(r.rhsLin,r.rhsLin),ee(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),D(u.positionLin,u.positionLin,this.dxLin,1),m||si(u.positionAng,u.positionAng,this.dxAng)}for(let c of this.forces)c.updateDual(this.alpha)}for(let l of i)l.prevVelocityLin.set(l.velocityLin),l.mass>0&&!l.sleeping&&(_t(l.velocityLin,J(l.velocityLin,l.positionLin,l.initialLin),e),_t(l.velocityAng,ct(l.velocityAng,l.positionAng,l.initialAng),e));ln(this,3,n),this.cpuStep++}},hd=S(),nu=(s,e)=>ne(hd,s,e);var Bs=(s,e)=>s.map((t,i)=>t+e[i]),Re=(s,e)=>s.map((t,i)=>t-e[i]),Lt=(s,e)=>s.map(t=>t*e),st=(s,e)=>s[0]*e[0]+s[1]*e[1],ws=(s,e)=>[Math.cos(s)*e[0]-Math.sin(s)*e[1],Math.sin(s)*e[0]+Math.cos(s)*e[1]],lu=s=>Math.max(0,Math.min(1,s)),au=(s,e,t)=>Bs(s,Lt(Re(e,s),lu(st(Re(t,s),Re(e,s))/(st(Re(e,s),Re(e,s))||1))));function un(s,e){let t=ei(s)?.get(e)??[0,0,0,0],i=Array.from(s.pose.subarray(e*4,e*4+2)),r=s.pose[e*4+2],o=l=>Bs(i,ws(r,l)),n=[s.shape[e*4]/2,s.shape[e*4+1]/2],a=t[0]===4?Array.from({length:t.vertices.length/4},(l,c)=>o(t.vertices.slice(c*4,c*4+2))):t[0]===0?[[-1,-1],[1,-1],[1,1],[-1,1]].map(l=>o([l[0]*n[0],l[1]*n[1]])):null;return{kind:t[0],p:i,angle:r,vertices:a,radius:t[1],spine:[o([-t[2]||0,0]),o([t[2]||0,0])]}}function Yi(s,e){if(s.vertices){let r=-1/0,o=[];for(let n of s.vertices){let a=st(n,e);a>r+1e-7?(r=a,o=[n]):Math.abs(a-r)<1e-7&&o.push(n)}return[o[0],o.at(-1)]}let t=Bs(s.spine[0],Lt(e,s.radius)),i=Bs(s.spine[1],Lt(e,s.radius));return Math.abs(st(t,e)-st(i,e))<1e-7?[t,i]:st(t,e)>st(i,e)?[t,t]:[i,i]}function cu(s,e,t,i){let r=un(s,e),o=un(s,t),n=s.pose,a=s.shape;if(r.kind===0&&o.kind===0)return Lo(...n.subarray(e*4,e*4+3),a[e*4]/2,a[e*4+1]/2,...n.subarray(t*4,t*4+3),a[t*4]/2,a[t*4+1]/2,i);let l=[];if(r.kind===3||o.kind===3){let u=r.kind===3,f=u?r:o,d=u?o:r;if(d.kind===3)return 0;let p=ws(f.angle,[0,1]),m=Yi(d,Lt(p,-1));for(let h of m){let g=st(Re(h,f.p),p);if(g<=0){let b=Re(h,Lt(p,g));l.push({a:u?b:h,b:u?h:b,n:u?Lt(p,-1):p})}}}else{let u=-1/0,f=[0,1],d=x=>{let w=Math.hypot(...x);if(w<1e-9)return;let B=Lt(x,1/w),L=st(Re(Yi(r,Lt(B,-1))[0],Yi(o,B)[0]),B),M=st(Re(Yi(o,Lt(B,-1))[0],Yi(r,B)[0]),B);Math.max(L,M)>u+1e-7&&(u=Math.max(L,M),f=L>=M?B:Lt(B,-1))};for(let[x,w]of[[r,o],[o,r]])if(x.vertices)for(let B=0;B<x.vertices.length;B++){let L=Re(x.vertices[(B+1)%x.vertices.length],x.vertices[B]);d([L[1],-L[0]]),w.vertices||d(Re(x.vertices[B],au(...w.spine,x.vertices[B])))}else if(d(ws(x.angle,[0,1])),!w.vertices)for(let B of x.spine)d(Re(B,au(...w.spine,B)));if(u===-1/0&&d([0,1]),u>0)return 0;let p=Yi(r,Lt(f,-1)),m=Yi(o,f),h=[-f[1],f[0]],g=p.map(x=>st(x,h)),b=m.map(x=>st(x,h)),y=Math.max(Math.min(...g),Math.min(...b)),A=Math.min(Math.max(...g),Math.max(...b)),v=(x,w)=>{let B=st(Re(x[1],x[0]),h);return Math.abs(B)<1e-9?x[0]:Bs(x[0],Lt(Re(x[1],x[0]),lu((w-st(x[0],h))/B)))};for(let x of A-y>1e-6?[y,A]:[(y+A)/2])l.push({a:v(p,x),b:v(m,x),n:f})}let c=0;for(let u of l){if(c&&Math.hypot(...Re(u.a,l[0].a))<1e-7)continue;let f=ws(-r.angle,Re(u.a,r.p)),d=ws(-o.angle,Re(u.b,o.p));if(i.set([268435456+c,...f,...d,...u.n],c*7),c++,c===2)break}return c}var uu=(s,e,t)=>[Math.cos(s)*e-Math.sin(s)*t,Math.sin(s)*e+Math.cos(s)*t],fu=function(){let{pose:s,shape:e,props:t}=this,i=this.broadphase.pairs,r=this.contactOut;for(let o=0;o<this.broadphase.pairCount;o++){let n=i[o],a=Math.floor(n/2097152),l=n-a*2097152,c=this.collideBodies(a,l,r);if(c===0)continue;let u=0,f=this.prevCount;for(;u<f;){let m=u+f>>>1;this.prevKeys[m]<n?u=m+1:f=m}let d=u,p=Math.sqrt(t[a*4]*t[l*4]);for(let m=0;m<c;m++){let h=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(h+1)*j),this.data=this.grow(this.data,(h+1)*O);let g=this.data,b=h*O,y=m*7,A=r[y+0];this.info.set([hs,a,l,A],h*j),g.fill(0,b,b+O),g[b+Q]=g[b+Q+1]=1/0,g[b+_e]=-1/0,g[b+Ae]=0,g[b+Pt]=g[b+Pt+1]=1/0,g[b+N]=r[y+1],g[b+N+1]=r[y+1+1],g[b+Y]=r[y+3],g[b+Y+1]=r[y+3+1],g[b+re]=p,g[b+be]=r[y+5],g[b+Ue]=r[y+5+1];let v=-1;for(let P=d;P<this.prevCount&&this.prevKeys[P]===n;P++)this.prevInfo[P*j+3]===A&&(v=P);if(v<0&&this.params.matchNearest){let P=Sa*Math.min(e[a*4],e[a*4+1],e[l*4],e[l*4+1]);for(let C=d;C<this.prevCount&&this.prevKeys[C]===n;C++){let _=Math.hypot(this.prevData[C*O+N]-g[b+N],this.prevData[C*O+N+1]-g[b+N+1]);_<=P&&(P=_,v=C)}}if(v>=0){let P=v*O;g[b+se]=this.prevData[P+se],g[b+se+1]=this.prevData[P+se+1],g[b+te]=this.prevData[P+te],g[b+te+1]=this.prevData[P+te+1],this.prevData[P+Wi]&&(g[b+N]=this.prevData[P+N],g[b+N+1]=this.prevData[P+N+1],g[b+Y]=this.prevData[P+Y],g[b+Y+1]=this.prevData[P+Y+1])}let[x,w]=uu(s[a*4+2],g[b+N],g[b+N+1]),[B,L]=uu(s[l*4+2],g[b+Y],g[b+Y+1]),M=g[b+be],I=g[b+Ue],E=s[a*4]+x-s[l*4]-B,k=s[a*4+1]+w-s[l*4+1]-L;g[b+fe]=M*E+I*k+5e-4,g[b+fe+1]=I*E-M*k}}};var fn=class extends zr{constructor(e,t,i){if(super({precision:i==="soa-colored"?"f32":"f64",order:i==="soa-colored"?"colored":"sequential"}),Object.assign(this,e),this.options={...this.options,precision:i==="soa-colored"?"f32":"f64",order:i==="soa-colored"?"colored":"sequential"},this.options.precision==="f64")for(let[r,o]of Object.entries(this))o instanceof Float32Array&&(this[r]=Float64Array.from(o));for(let[r,o]of ei(e)??[]){let n=this.shape[r*4+2];Rr(this,r,{geometry:o,area:n,momentPerMass:n>0?this.shape[r*4+3]/n:0,radius:this.props[r*4+1]},1)}this.filters=new Map(t?.filters??[]),this.sensors=new Set(t?.sensors??[]),this.restitution=new Map(t?.restitution??[]),this.contactPairs=[],t&&(t.topology=this)}narrowphase(){this.contactPairs=[],fu.call(this)}collideBodies(e,t,i){let r=this.filters.get(e)??[4294967295,4294967295],o=this.filters.get(t)??[4294967295,4294967295];if(!(r[0]&o[1])||!(o[0]&r[1]))return 0;let n=cu(this,e,t,i);if(n){let a=[i[5],i[6]],l=[0,0],c=[0,0];for(let p=0;p<n;p++)for(let m=0;m<2;m++)l[m]+=i[p*7+1+m]/n,c[m]+=i[p*7+3+m]/n;let u=this.pose[e*4+2],f=Math.cos(u),d=Math.sin(u);this.contactPairs.push({a:e,b:t,n:a,ra:l,rb:c,point:[this.pose[e*4]+f*l[0]-d*l[1],this.pose[e*4+1]+d*l[0]+f*l[1],0],impulse:0})}return this.sensors.has(e)||this.sensors.has(t)?0:n}initJoints(){for(let e=0;e<this.jointCount;e++){let t=e*O,i=this.info[e*j];if(this.data[t+Q]===0&&this.data[t+Q+1]===0&&this.data[t+Q+2]===0){i!==0&&(this.info[e*j]=0,this.noCollideDirty=!0,this.handles[e]&&(this.handles[e].alive=!1));continue}i===1&&this.jointC(this.info[e*j+1],this.info[e*j+2],t,this.pose,this.data,t+fe)}for(let e=0;e<this.jointCount;e++)if(this.info[e*j]===5){let t=e*O,i=this.info[e*j+1],r=this.info[e*j+2],o=(i<0?0:this.pose[i*4+2])-this.pose[r*4+2]-this.data[t+re];this.data[t+fe]=Math.min(0,o-this.data[t+be]),this.data[t+fe+1]=Math.max(0,o-this.data[t+Ue])}}warmStartConstraints(e,t){let i=[];for(let r=0;r<this.jointCount;r++)this.info[r*j]===5&&(i.push(r),this.info[r*j]=4);super.warmStartConstraints(e,t);for(let r of i)this.info[r*j]=5}evalConstraint(e,t,i){if(this.info[e*j]!==5)return super.evalConstraint(e,t,i);let r=e*O,o=this.info[e*j+1],n=this.info[e*j+2],a=this.data,l=(o<0?0:this.pose[o*4+2])-this.pose[n*4+2]-a[r+re];this.eC[0]=l-a[r+be]-t*a[r+fe],this.eC[1]=l-a[r+Ue]-t*a[r+fe+1],this.eG.fill(0),this.eJ.fill(0);for(let c=0;c<2;c++){this.eMin[c]=a[r+_e+c],this.eMax[c]=a[r+Ae+c];let u=Math.max(this.eMin[c],Math.min(this.eMax[c],a[r+se+c]*this.eC[c]+a[r+te+c]));i>=0&&u!==0&&(this.eJ[c*3+2]=i===o?1:-1)}return 2}step(){let e=an(this,2);super.step(),ln(this,2,e);let t=new Map(this.contactPairs.map(i=>[`${i.a},${i.b}`,i]));for(let i=this.jointCount;i<this.jointCount+this.contactCount;i++){let r=t.get(`${this.info[i*j+1]},${this.info[i*j+2]}`);r&&(r.impulse+=Math.abs(this.data[i*O+te])*this.params.dt)}}};var hn=(s,e,t)=>{let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,F(T(t,s),i)/(F(i,i)||1)))))};function dn(s,e,t,i){let r=T(s,t),o=F(r,e),n=o*o-F(r,r)+i*i;if(n<0)return null;let a=-o-Math.sqrt(n);return a<0?null:{t:a,n:Tt(T(de(s,q(e,a)),t))}}function Dr(s,e){return e&&(!s||e.t<s.t)?e:s}function Da(s,e,t,i,r){let o=T(i,t),n=T(s,t),a=F(o,o);if(a<1e-16)return dn(s,e,t,r);let l=F(o,e),c=F(o,n),u=F(n,e),f=a-l*l,d=a*u-c*l,p=a*F(n,n)-c*c-r*r*a,m=d*d-f*p,h=null;if(f>1e-12*a&&m>=0){let g=(-d-Math.sqrt(m))/f,b=c+g*l;g>=0&&b>=0&&b<=a&&(h={t:g,n:Tt(T(de(s,q(e,g)),de(t,q(o,b/a))))})}return Dr(Dr(h,dn(s,e,t,r)),dn(s,e,i,r))}function du(s,e){let t=q(e.vertices.reduce((i,r)=>de(i,r),[0,0,0]),1/e.vertices.length);for(let i=0;i<e.vertices.length;i++){let r=e.vertices[i],o=e.vertices[(i+1)%e.vertices.length],n=xt(T(o,r),e.normal);if(F(n,T(s,r))*(F(n,T(t,r))>=0?1:-1)<-1e-6)return!1}return!0}function pd(s,e,t,i){if(i===0){let a=-1/0,l=1/0,c=q(t,-1);for(let u of s.faces){let f=F(u.normal,t),d=F(u.normal,T(u.vertices[0],e));if(Math.abs(f)<1e-12){if(d<0)return null}else{let p=d/f;f<0?p>a&&(a=p,c=u.normal):l=Math.min(l,p)}if(a>l)return null}return l<0?null:{t:Math.max(0,a),n:a<=0?q(t,-1):c}}let r=!0,o=1/0,n=null;for(let a of s.faces){let l=F(a.normal,T(e,a.vertices[0]));l>0&&(r=!1),du(T(e,q(a.normal,l)),a)&&(o=Math.min(o,l*l));let c=F(a.normal,t);if(c<-1e-12){let u=(i-l)/c;u>=0&&du(T(de(e,q(t,u)),q(a.normal,i)),a)&&(n=Dr(n,{t:u,n:a.normal}))}}for(let[a,l]of s.edges){let c=T(e,hn(a,l,e));o=Math.min(o,F(c,c)),n=Dr(n,Da(e,t,a,l,i))}return r||o<=i*i?{t:0,n:q(t,-1)}:n}var At=(s,e)=>s[0]*e[0]+s[1]*e[1];function md(s,e,t,i){let r=!0,o=1/0,n=null;for(let a=0;a<s.vertices.length;a++){let l=s.vertices[a],c=s.vertices[(a+1)%s.vertices.length],u=T(c,l),f=Tt([u[1],-u[0]]),d=At(T(e,l),f);d>0&&(r=!1);let p=T(e,hn(l,c,e));o=Math.min(o,At(p,p));let m=At(t,f);if(m<-1e-12){let h=(i-d)/m,g=T(de(e,q(t,h)),q(f,i)),b=At(T(g,l),u),y=1e-6*At(u,u);h>=0&&b>=-y&&b<=At(u,u)+y&&(n=Dr(n,{t:h,n:f}))}i>0&&(n=Dr(n,dn(e,t,l,i)))}return r||o<=i*i?{t:0,n:q(t,-1)}:n}function gd(s,e,t,i){if(s.vertices)return md(s,e,t,i);if(s.kind===3){let p=[-Math.sin(s.angle),Math.cos(s.angle)],m=At(T(e,s.p),p)-i,h=At(t,p);return m<=0?{t:0,n:q(t,-1)}:h<-1e-12?{t:-m/h,n:p}:null}let r=s.kind===1?s.p:s.spine[0],o=s.kind===1?s.p:s.spine[1],n=i+s.radius,a=T(e,hn(r,o,e));if(At(a,a)<=n*n)return{t:0,n:q(t,-1)};if(n>0)return Da(e,t,r,o,n);let l=T(o,r),c=t[0]*l[1]-t[1]*l[0];if(Math.abs(c)<1e-12){let p=T(e,r);if(Math.abs(p[0]*l[1]-p[1]*l[0])/Math.max(Math.hypot(...l),1e-20)>1e-6)return null;let h=Math.min(At(T(r,e),t),At(T(o,e),t));return Math.max(At(T(r,e),t),At(T(o,e),t))<0?null:{t:Math.max(0,h),n:q(t,-1)}}let u=T(r,e),f=(u[0]*l[1]-u[1]*l[0])/c,d=(u[0]*t[1]-u[1]*t[0])/c;return f>=0&&d>=0&&d<=1?{t:f,n:q(Tt([l[1],-l[0]]),c>0?-1:1)}:null}function hu(s,e,t,i,{maxDistance:r=1e30,ignore:o=[],collidesWith:n=4294967295,includeTriggers:a=!0}={}){let l=s.dimension,c=me(e,l,"origin"),u=me(i,l,"direction"),f=Math.hypot(...u);if(!f||!Number.isFinite(t)||t<0||!Number.isFinite(r)||r<0)throw Error("Provide a nonzero direction, finite nonnegative radius and distance");if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let d=new Set(o);for(let h of d)s.liveIndex(h);let p=u.map(h=>h/f),m=null;for(let h=0;h<s.bodyCount;h++){if(d.has(h))continue;let g=l===3?s.solver.bodies[h]:null,b=g?g.group??4294967295:s.solver.filters.get(h)?.[0]??4294967295,y=g?g.isTrigger:s.solver.sensors.has(h);if(!(b&n)||!a&&y)continue;let A;if(l===2)A=gd(un(s.solver,h),c,p,t);else{let x=jr(g);if(x.spine){let w=x.radius+t,B=T(c,hn(...x.spine,c));A=F(B,B)<=w*w?{t:0,n:q(p,-1)}:Da(c,p,...x.spine,w)}else A=pd(x,c,p,t)}if(!A||A.t>r||m&&A.t>=m.distance)continue;let v=de(c,q(p,A.t));m={index:h,distance:A.t,normal:A.n,center:v,point:T(v,q(A.n,t))}}return m}var pn=class{constructor(e,{speedThreshold:t=.08,timeThreshold:i=.6}={}){if(!Number.isFinite(t)||t<=0||!Number.isFinite(i)||i<=0)throw Error("Sleep thresholds must be positive and finite");this.world=e,this.speedThreshold=t,this.timeThreshold=i,this.time=new Float64Array(e.bodyCount),this.asleep=new Set}wake(){let e=this.world;for(let t of this.asleep)e.dimension===3?e.solver.bodies[t].sleeping=!1:e.solver.dynamic[t]=+(e.solver.shape[t*4+2]>0);this.asleep.clear(),this.time.fill(0)}after(){let e=this.world,t=e.solver,i=e.bodyCount,r=Int32Array.from({length:i},(d,p)=>p),o=new Set,n=[],a=d=>e.dimension===3?t.bodies[d].mass:t.shape[d*4+2],l=d=>{for(;r[d]!==d;)r[d]=r[r[d]],d=r[d];return d},c=(d,p)=>{if(d<0){o.add(p);return}a(d)<=0?o.add(p):a(p)<=0?o.add(d):r[l(d)]=l(p),n.push([d,p])};if(e.dimension===3){let d=new Map(t.bodies.map((p,m)=>[p,m]));for(let p of t.forces)!p.broken&&!p.bodyA?.isTrigger&&!p.bodyB?.isTrigger&&c(p.bodyA?d.get(p.bodyA):-1,d.get(p.bodyB))}else{for(let d of t.contactPairs)!t.sensors.has(d.a)&&!t.sensors.has(d.b)&&c(d.a,d.b);for(let d=0;d<t.jointCount;d++)(t.data[d*32+6]||t.data[d*32+7]||t.data[d*32+8])&&c(t.info[d*4+1],t.info[d*4+2])}let u=new Set([...o].map(l)),f=new Map;for(let d=0;d<i;d++)if(a(d)>0){let p=l(d),m=e.dimension===3?t.bodies[d]:null,h=m?m.allowSleep!==!1:t.avbdSleepEligible?.get(d)!==!1,g=m?Math.hypot(...m.velocityLin,...m.velocityAng):Math.hypot(...t.velocity.subarray(d*4,d*4+3));f.has(p)||f.set(p,u.has(p)),f.set(p,f.get(p)&&h&&g<=this.speedThreshold)}for(let d=0;d<i;d++)a(d)>0&&(this.time[d]=f.get(l(d))?this.time[d]+e.params.dt:0,this.time[d]>=this.timeThreshold&&(this.asleep.add(d),e.dimension===3?(t.bodies[d].sleeping=!0,t.bodies[d].velocityLin.fill(0),t.bodies[d].velocityAng.fill(0)):(t.dynamic[d]=0,t.velocity.fill(0,d*4,d*4+4))));n.some(([d,p])=>d>=0&&a(d)>0&&a(p)>0&&this.asleep.has(d)!==this.asleep.has(p))&&this.wake()}};var mn=class{constructor(e,t,i={}){if(this.backend="cpu",this.dimension=t,this.scene=e,this.stepCount=0,this.pending=new ti,this.events=[],this.previous=new Map,this.watched=null,t===3){this.solver=Object.assign(new cn,e.ref),this.solver.up=[...e.up??[0,0,1]],this.nativeRef=e.ref,this.constraints=this.solver.forces.slice();for(let r of this.solver.bodies)r.solver=this.solver;for(let r of this.constraints)r.solver=this.solver;this.constraints.forEach((r,o)=>{let n=e.handles?.get(r);n&&(n.slot=o)}),nn(this.solver)}else this.solver=new fn(e.topology,e,i.order==="colored"?"soa-colored":"soa-seq"),this.topology=this.solver,this.builder=Object.assign(Object.create(Object.getPrototypeOf(e)),{topology:this.solver,filters:this.solver.filters,sensors:this.solver.sensors,restitution:this.solver.restitution});this.params=t===3?this.solver:this.solver.params,Tr(this,t===3?{bodies:this.solver.bodies}:null,t,{setVelocities:this.setPackedVelocities,readSelectedBodies:this.readSelectedBodies}),(i.sleeping||e.sleepOptIn||[...e.topology?.avbdSleepEligible?.values()??[]].some(Boolean))&&this.enableSleeping(i.sleeping===!0?{}:i.sleeping)}get bodyCount(){return this.dimension===3?this.solver.bodies.length:this.solver.bodyCount}get jointCount(){return this.dimension===3?this.constraints.length:this.solver.jointCount}gpuIndex(e){return this.liveIndex(e),e}liveIndex(e){if(this.assertLive(),!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live body index is required")}assertLive(){if(this.destroyed)throw Error("The solver has been destroyed")}prepareBodyCommands(){return this.wakeAll(),{enqueueValidated:e=>{this.pending.reserve(this.pending.count+e.count),this.pending.u32.set(e.u32.subarray(0,e.count*12),this.pending.count*12),this.pending.count+=e.count}}}setLinearVelocity(e,t){return this.editBodies([{body:e,linearVelocity:t}])}setAngularVelocity(e,t){return this.editBodies([{body:e,angularVelocity:t}])}applyImpulse(e,t,i){return this.editBodies([{body:e,impulse:t,worldPoint:i}])}applyForce(e,t,i){return this.editBodies([{body:e,force:t,worldPoint:i}])}applyAngularImpulse(e,t){return this.editBodies([{body:e,angularImpulse:t}])}applyTorque(e,t){return this.editBodies([{body:e,torque:t}])}teleport(e,t,i){return this.editBodies([{body:e,position:t,rotation:i}])}setPackedVelocities(e,t){let i=Array.from(e),r=t?.linear,o=t?.angular,n=this.dimension,a=n===3?3:1;if(r===void 0&&o===void 0)throw Error("Provide linear or angular velocities");let l=-1;i.forEach(d=>{if(this.liveIndex(d),d<=l)throw Error("Velocity batch indices must be unique and ascending GPU slots");l=d});for(let[d,p]of[[r,n],[o,a]])if(d!==void 0&&(d.length!==i.length*p||!Array.from(d).every(m=>Number.isFinite(Math.fround(m)))))throw Error("Provide finite packed velocity components per body");let c=r===void 0?null:Array.from(r),u=o===void 0?null:Array.from(o),f=i.map((d,p)=>({body:d,...c===null?{}:{linearVelocity:c.slice(p*n,(p+1)*n)},...u===null?{}:{angularVelocity:n===3?u.slice(p*a,(p+1)*a):u[p]}}));return this.editBodies(f),this.flushEdits(),this}flushEdits(){if(this.destroyed)throw Error("The solver has been destroyed");let{words:e,u32:t,count:i}=this.pending,r=this.solver,o=this.dimension;for(let n=0;n<i;n++){let a=n*12,l=t[a],c=t[a+1],u=t[a+2]!==0,f=o===3?r.bodies[l]:null,d=f?.velocityLin??r.velocity.subarray(l*4,l*4+2),p=f?.mass??r.shape[l*4+2],m=f?.positionLin??r.pose.subarray(l*4,l*4+2),h=g=>{if(!(p<=0))if(o===2)r.velocity[l*4+2]+=g[0]/Math.max(1e-10,r.shape[l*4+3]);else{let b=f.positionAng,y=Ee([-b[0],-b[1],-b[2],b[3]],g),A=Ee(b,y.map((v,x)=>v/Math.max(1e-10,f.moment[x])));for(let v=0;v<3;v++)f.velocityAng[v]+=A[v]}};if(c===0)d.set(e.subarray(a+4,a+4+o));else if(c===1)f?f.velocityAng.set(e.subarray(a+4,a+7)):r.velocity[l*4+2]=e[a+4];else if(c===4)if(m.set(e.subarray(a+4,a+4+o)),f){u&&f.positionAng.set(e.subarray(a+8,a+12)),f.initialLin.set(m),f.inertialLin.set(m),f.initialAng.set(f.positionAng),f.inertialAng.set(f.positionAng);for(let g of[...f.forces])g.contacts&&g.destroy()}else u&&(r.pose[l*4+2]=e[a+6]),r.initial.set(r.pose.subarray(l*4,l*4+4),l*4),r.inertial.set(r.pose.subarray(l*4,l*4+4),l*4),r.prevCount=0;else if(c===2||c===3){if(p<=0)continue;let g=Array.from(e.subarray(a+4,a+4+o),b=>b*(c===3?e[a+7]:1));for(let b=0;b<o;b++)d[b]+=g[b]/p;if(u){let b=Array.from(e.subarray(a+8,a+8+o),(y,A)=>y-m[A]);h(o===3?xt(b,g):[b[0]*g[1]-b[1]*g[0]])}}else(c===5||c===6)&&h(Array.from(e.subarray(a+4,a+4+(o===3?3:1)),g=>g*(c===6?e[a+7]:1)))}this.pending.count=0}step(){this.flushEdits(),this.solver.step(),this.sleeping?.after(),this.stepCount++,this.captureEvents()}attachToScene(e,t){return Xo.call(this,e,t)}detachFromScene(){return Yo.call(this)}setFilters(e,t,i){let r=Array.from(e),o=Array.from(t),n=Array.from(i);if(o.length!==r.length||n.length!==r.length)throw Error("Provide one group and mask per body");r.forEach(a=>this.liveIndex(a));for(let a of[...o,...n])if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");r.forEach((a,l)=>{this.dimension===3?Object.assign(this.solver.bodies[a],{group:o[l],collidesWith:n[l]}):this.solver.filters.set(a,[o[l],n[l]])}),this.wakeAll()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Trigger flag must be boolean");this.dimension===3?this.solver.bodies[e].isTrigger=t:t?this.solver.sensors.add(e):this.solver.sensors.delete(e),this.wakeAll()}setRestitution(e,t){if(this.liveIndex(e),!Number.isFinite(t)||t<0||t>1)throw Error("restitution must be between 0 and 1");this.dimension===3?this.solver.bodies[e].restitution=t:this.solver.restitution.set(e,t),this.wakeAll()}setMotor(e,t){if(this.assertLive(),this.dimension===3){let i=this.constraints[e];if(!i?.cpuAngular)throw Error("A motor constraint is required");Object.assign(i.cpuAngular.options,kt({...i.cpuAngular.options,...t}))}else{let i=this.solver,r=e*O;if(!Number.isInteger(e)||e<0||e>=i.jointCount||i.info[e*4]!==3)throw Error("A motor constraint is required");let o=t.speed??i.data[r+re],n=t.maxTorque??i.data[r+Ae];if(!Number.isFinite(o)||!Number.isFinite(n)||n<0)throw Error("Provide finite motor speed and nonnegative torque");i.data[r+re]=o,i.data[r+_e]=-n,i.data[r+Ae]=n}this.wakeAll()}setAngleLimits(e,t){if(this.assertLive(),this.dimension!==2)throw Error("Use a 3D hinge constraint");if(this.solver.info[e*4]!==5)throw Error("An angular limit is required");vs(this.solver,e,ki(t)),this.wakeAll()}releaseJoints(e){for(let t of e)this.disableConstraint(t)}disableConstraint(e){if(this.assertLive(),!Number.isInteger(e)||e<0||e>=this.jointCount)throw Error("A live constraint slot is required");if(this.dimension===3){let t=this.constraints[e];t.broken=!0,(t.cpuAngular??t).destroy()}else this.solver.disable(e),this.solver.info[e*4]=0,this.solver.noCollideDirty=!0;this.wakeAll()}setWorldAnchor(e,t,i){if(this.assertLive(),this.dimension===2){let r=this.solver;if(!Number.isInteger(e)||e<0||e>=r.jointCount||r.info[e*4+1]!==-1)throw Error("A world-anchor constraint is required");r.data.set(me([t,i],2,"point"),e*O+21)}else{let r=this.constraints[e];if(!r||r.bodyA)throw Error("A world-anchor constraint is required");r.rA.set(me(t,3,"point"))}this.wakeAll()}addBox(...e){return this.addDynamic("addBox",e)}addCircle(...e){return this.addDynamic("addCircle",e)}addCapsule(...e){return this.addDynamic("addCapsule",e)}addHull(...e){return this.addDynamic("addHull",e)}addSegment(...e){return this.addDynamic("addSegment",e)}addPlane(...e){return this.addDynamic("addPlane",e)}addDynamic(e,t){if(this.destroyed)throw Error("The solver has been destroyed");if(this.dimension!==2)throw Error("Build 3D bodies before creating the solver");this.wakeAll();let i=this.sleeping?{speedThreshold:this.sleeping.speedThreshold,timeThreshold:this.sleeping.timeThreshold}:null,r=this.builder[e](...t);return i&&this.enableSleeping(i),r}appendJoint(e,t,i,r,o,n=1/0){if(this.assertLive(),this.dimension!==2)throw Error("Build 3D constraints before creating the solver");return this.wakeAll(),this.builder.addJoint(e,t,i,r,{stiffness:o,breakForce:n}).slot}captureConstraintFrames(e){if(this.assertLive(),this.dimension!==3)throw Error("Constraint frame capture requires a 3D world");let t=Array.from(e);for(let{slot:i,spring:r}of t){let o=this.constraints[i];if(!Number.isInteger(i)||!o||o.broken||o.cpuAngular||(r?o.rest===void 0:o.stiffnessAng===void 0))throw Error("A live spring or joint slot is required")}this.flushEdits();for(let{slot:i,spring:r}of t)ja(this.constraints[i],r);t.length&&this.wakeAll()}setSpringMaterial(e,t={}){this.assertLive();let i=this.constraints[e];if(!i||i.rest===void 0)throw Error("A spring constraint is required");vr(i,It(t)),this.wakeAll()}packBodyRecords(e,t=!1){let i=this.solver,r=this.dimension,o=t?r===3?8:4:r===3?40:24,n=e?.length??this.bodyCount,a=new Float32Array(n*o);for(let l=0;l<n;l++){let c=e?.[l]??l,u=l*o;if(t){if(r===3){let f=i.bodies[c];a.set(f.positionLin,u),a[u+3]=f.friction,a.set(f.positionAng,u+4)}else a.set(i.pose.subarray(c*4,c*4+4),u),a[u+3]=i.props[c*4];continue}if(r===3){let f=i.bodies[c];a.set(f.positionLin,u),a[u+3]=f.friction,a.set(f.positionAng,u+4),a.set(f.initialLin,u+8),a[u+11]=+!!f.isTrigger,a.set(f.initialAng,u+12),a.set(f.size,u+16),a[u+19]=f.sleeping?0:f.mass,a.set(f.moment,u+20),a[u+23]=f.radius,a.set(f.inertialLin,u+24),a.set(f.inertialAng,u+28),a.set(f.velocityLin,u+32),a[u+35]=f.prevVelocityLin.reduce((d,p,m)=>d+p*i.up[m],0),a.set(f.velocityAng,u+36),a[u+39]=De(f)?1:Fe(f)?-1:Ct(f)?3:or(f)?2:0}else a.set(i.pose.subarray(c*4,c*4+4),u),a[u+3]=i.props[c*4],a.set(i.initial.subarray(c*4,c*4+4),u+4),a.set(i.inertial.subarray(c*4,c*4+4),u+8),a.set(i.velocity.subarray(c*4,c*4+4),u+12),a.set(i.prevVelocity.subarray(c*4,c*4+4),u+16),a.set(i.shape.subarray(c*4,c*4+4),u+20)}return a}async readBodies(){return this.flushEdits(),this.packBodyRecords()}async readSelectedBodies(e,{posesOnly:t=!1}={}){let i=Array.from(e);return i.forEach(r=>this.liveIndex(r)),this.flushEdits(),this.packBodyRecords(i,t)}async readBodyState(e){this.liveIndex(e),this.flushEdits();let t=this.solver;if(this.dimension===3){let i=t.bodies[e],r=this.sleeping?.asleep.has(e)??!1;return{position:Array.from(i.positionLin),rotation:Array.from(i.positionAng),linearVelocity:Array.from(i.velocityLin),angularVelocity:Array.from(i.velocityAng),mass:i.mass,effectiveMass:r?0:i.mass,sleeping:r}}return{position:Array.from(t.pose.subarray(e*4,e*4+2)),angle:t.pose[e*4+2],linearVelocity:Array.from(t.velocity.subarray(e*4,e*4+2)),angularVelocity:t.velocity[e*4+2],mass:t.shape[e*4+2],moment:t.shape[e*4+3],sleeping:this.sleeping?.asleep.has(e)??!1}}async readJoints(){return this.flushEdits(),this.packJointRecords()}packJointRecords(){let e=this.dimension===3?32:36,t=new Float32Array(this.jointCount*e),i=r=>r===1/0?3e38:r===-1/0?-3e38:r;if(this.dimension===2){let r=this.solver.data;for(let o=0;o<this.jointCount;o++){let n=o*36,a=o*O;[se,te,Q,_e,Ae,Pt,fe].forEach((l,c)=>{for(let u=0;u<3;u++)t[n+c*4+u]=i(r[a+l+u])}),t.set([r[a+N],r[a+N+1],r[a+Y],r[a+Y+1]],n+28),t.set([r[a+re],r[a+re+1],r[a+re+2],0],n+32)}}else this.constraints.forEach((r,o)=>{let n=o*32;if(r.cpuAngular){let a=r.cpuAngular,l=a.options;t.set(a.pen,n),t[n+3]=r.broken?0:1,t.set(a.lam,n+8),t.set(a.ta,n+16),t.set(a.tb,n+20),t.set(l.axisA,n+24),t.set(l.axisB,n+28),t[n+19]=a.c0?.[0]??0,t[n+23]=a.c0?.[1]??0,t[n+27]=l.type==="motor"?l.speed:l.minAngle,t[n+31]=l.type==="motor"?l.maxTorque:l.maxAngle;return}for(let[a,l]of[["penaltyLin",0],["penaltyAng",4],["lambdaLin",8],["lambdaAng",12],["C0Lin",16],["C0Ang",20],["rA",24],["rB",28]])r[a]&&t.set(r[a],n+l);if(t[n+3]=r.broken?0:i(r.stiffnessLin??r.stiffness??0),t[n+7]=r.broken?0:i(r.stiffnessAng??0),t[n+11]=i(r.fracture??0),t[n+15]=r.torqueArm??0,t[n+27]=r.rest??0,r.cpuRest&&(t[n+19]=r.cpuRest[0],t[n+23]=r.cpuRest[1],t[n+27]=r.cpuRest[2],t[n+31]=r.cpuRest[3]),r.rest!==void 0){let a=Ui(r);t[n+11]=Number.isFinite(a?.breakStrain)?a.breakStrain:0,t[n+15]=Number.isFinite(a?.breakForce)?a.breakForce:0}});return t}async readCounters(){this.assertLive();let e=this.dimension===2?this.solver.contactCount:this.solver.forces.reduce((i,r)=>i+(r.contacts?.length??0),0),t=this.dimension===2?this.solver.contactPairs.length:this.solver.forces.filter(i=>i.contacts?.length).length;return{pairs:t,contacts:e,manifolds:t,joints:this.jointCount,overflow:0,clashes:0,colors:0,sleeping:this.sleeping?.asleep.size??0}}adapt(){}watchContacts({indices:e,maxPairs:t=this.maxPairs??8192,maxEvents:i=this.maxEvents??4096}={}){if(this.assertLive(),!Number.isInteger(i)||i<1||!Number.isInteger(t)||t<1)throw Error("Contact capacities must be positive integers");return e!==void 0?(e.forEach(r=>this.liveIndex(r)),this.watched=new Set(e)):this.watched??=new Set(Array.from({length:this.bodyCount},(r,o)=>o)),this.maxEvents=i,this.maxPairs=t,this}captureEvents(){if(!this.watched)return;let e=this.dimension===3?new Map(this.solver.bodies.map((i,r)=>[i,r])):null,t=this.dimension===2?this.solver.contactPairs:this.solver.forces.filter(i=>i.contacts?.length).map(i=>{let r=Array.from(i.bodyA.positionLin);for(let o of i.contacts){let n=Ee(i.bodyA.positionAng,o.rA);for(let a=0;a<3;a++)r[a]+=n[a]/i.contacts.length}return{a:e.get(i.bodyA),b:e.get(i.bodyB),point:r,normal:Array.from(i.basis.slice(0,3)),impulse:i.bodyA.isTrigger||i.bodyB.isTrigger?0:i.contacts.reduce((o,n)=>o+Math.abs(n.lambda[0])*this.params.dt,0)}});this.trackContactPairs(t)}trackContactPairs(e){let t=new Map,i=(n,a)=>{this.events.length<(this.maxEvents??4096)?this.events.push({...n,normal:n.normal??[...n.n,0],type:a,kind:a==="begin"?1:2,step:this.stepCount,isTrigger:n.isTrigger??(this.dimension===3?!!(this.solver.bodies[n.a].isTrigger||this.solver.bodies[n.b].isTrigger):this.solver.sensors.has(n.a)||this.solver.sensors.has(n.b))}):this.dropped=(this.dropped??0)+1},r=new Set,o=[];for(let n of e){if(!this.watched.has(n.a)&&!this.watched.has(n.b))continue;let a=`${Math.max(n.a,n.b)},${Math.min(n.a,n.b)}`;r.add(a),this.previous.has(a)&&t.size<this.maxPairs?t.set(a,n):o.push([a,n])}for(let[n,a]of o)if(!t.has(n)){if(t.size>=this.maxPairs){this.pairFull=!0;continue}t.set(n,a),this.previous.has(n)||i(a,"begin")}for(let[n,a]of this.previous)r.has(n)||i({...a,impulse:0},"end");this.previous=t}async readContactEvents(){this.watchContacts();let e={events:this.events,dropped:this.dropped??0,full:!!this.dropped||!!this.pairFull};return this.events=[],this.dropped=0,this.pairFull=!1,e}async raycast(e,t,i){return this.sphereCast(e,0,t,i)}async circleCast(e,t,i,r){return this.sphereCast(e,t,i,r)}async sphereCast(e,t,i,r){return this.flushEdits(),hu(this,e,t,i,r)}async raycastAll(e,t){return Promise.all(e.map(i=>this.raycast(i.origin,i.direction,{...t,maxDistance:i.maxDistance??t?.maxDistance})))}async sphereCastAll(e,t){return Promise.all(e.map(i=>this.sphereCast(i.origin,i.radius,i.direction,{...t,maxDistance:i.maxDistance??t?.maxDistance})))}async circleCastAll(e,t){return this.sphereCastAll(e,t)}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");return this.dimension===3?this.solver.bodies[e].allowSleep=t:(this.solver.avbdSleepEligible??=new Map,this.solver.avbdSleepEligible.set(e,t)),t&&!this.sleeping&&this.enableSleeping(),this.wakeAll(),this}enableSleeping(e={}){return this.assertLive(),this.disableSleeping(),this.sleeping=new pn(this,e),this}disableSleeping(){return this.sleeping?.wake(),this.sleeping=null,this}async readSleepStats(){return this.assertLive(),{sleeping:this.sleeping?.asleep.size??0,wakeRequested:!1}}wakeUp(e){return this.liveIndex(e),this.wakeAll()}wakeAll(){return this.sleeping?.wake(),this}destroy(){this.destroyed||(this.detachFromScene(),this.disableSleeping(),this.pending.count=0,this.events=[],this.previous.clear(),this.destroyed=!0)}};var pu=new WeakSet,gn=s=>s!==null&&typeof s=="object"&&pu.has(s),mu=s=>pu.add(s);async function bd({backend:s="webgpu",device:e,gl:t,validate3D:i=!0,...r}={}){if(!["webgpu","cpu"].includes(s))throw Error(`Unsupported solver backend: ${s}`);if(s!=="webgpu"&&e!==void 0)throw Error("Only WebGPU initialization accepts a GPU device");if(t!==void 0)throw Error("Solver pipelines do not accept a WebGL context");let o,n=!1,a=!1;s==="webgpu"&&(e?i&&await Cr(e):({device:e,adapter:o}=await ss({...r,preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024,...r.preferredLimits},validate3D:i}),n=!0));let l=new Set,c={backend:s,device:e,adapter:o,createSolver(u,f={}){if(a)throw Error("The solver pipeline has been destroyed");if(!u||typeof u.assertEditable!="function"||!u.ref&&!u.topology)throw Error("An AVBD scene builder is required");u.assertEditable();let d;if(s==="webgpu")d=u.createSolver(e,f);else{let m=u.ref?3:u.topology?2:0;if(f.bodyBuffer!==void 0)throw Error("A GPU body buffer requires the WebGPU backend");d=new mn(u,m,f),u.gpu=d,u.created=!0}Object.defineProperty(d,"backend",{value:s,configurable:!0}),l.add(d);let p=d.destroy;return d.destroy=function(){p.call(this),l.delete(this)},d},destroy(){if(!a){for(let u of l)u.destroy();l.clear(),n&&e.destroy(),a=!0}}};return mu(c),c}var ie=(s,e,t)=>{let i=Array.from(s);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i},K=(s,e,t=!1)=>{if(!Number.isFinite(s)||(t?s<0:s<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return s},gu=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s};function bn(s){for(let t of["allowSleep","isTrigger"])if(s[t]!==void 0&&typeof s[t]!="boolean")throw Error(`${t} must be a boolean`);let e=s.restitution??0;if(!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");if(s.mass!==void 0&&(K(s.mass,"mass",!0),s.mass>0&&s.density===0))throw Error("A positive mass needs positive density");for(let t of["velocity","angularVelocity"])s[t]!==void 0&&ie(s[t],3,t);if(s.rotation!==void 0&&!Math.hypot(...ie(s.rotation,4,"rotation")))throw Error("rotation must be nonzero");gu(s.group??4294967295),gu(s.collidesWith??4294967295)}function bu(s,e){if(e.timeStep!==void 0&&(s.dt=K(e.timeStep,"timeStep")),e.iterations!==void 0){if(K(e.iterations,"iterations"),!Number.isInteger(e.iterations))throw Error("iterations must be an integer");s.iterations=e.iterations}if(e.gravity!==void 0){if(!Number.isFinite(e.gravity))throw Error("gravity must be finite");s.gravity=e.gravity}}var Ga=class{constructor(e={}){if(this.ref=new ai,bu(this.ref,e),this.up=ie(e.up??[0,1,0],3,"up"),Math.abs(Math.hypot(...this.up)-1)>1e-5)throw Error("up must be a unit vector");this.points=!1,this.handles=new Map,this.bodySet=new Set}get bodies(){return this.ref.bodies}get constraints(){return[...this.handles.values()]}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use the solver's GPU edit methods afterwards")}addBox(e,t={}){this.assertEditable(),bn(t);let i=ie(e,3,"size");i.forEach(o=>K(o,"size"));let r=new Ze(this.ref,i,K(t.density??1,"density",!0),K(t.friction??.6,"friction",!0),ie(t.position??[0,0,0],3,"position"));return this.configure(r,t)}addSphere(e,t={}){return this.assertEditable(),bn(t),this.configure(nr(this.ref,K(e,"radius"),K(t.density??1,"density",!0),K(t.friction??.6,"friction",!0),ie(t.position??[0,0,0],3,"position")),t)}addHull(e,t={}){if(this.assertEditable(),bn(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=Ur(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(ar(this.ref,i,K(t.density??1,"density",!0),K(t.friction??.6,"friction",!0),ie(t.position??[0,0,0],3,"position")),t)}addCapsule(e,t,i={}){return this.assertEditable(),bn(i),this.configure(Br(this.ref,e,t,K(i.density??1,"density",!0),K(i.friction??.6,"friction",!0),ie(i.position??[0,0,0],3,"position")),i)}addHeightfield(e){return this.addTerrain(ls(e),e)}addStaticMesh(e){return this.addTerrain(cs(e),e)}addTerrain(e,t={}){this.assertEditable();let i=this.ref.bodies.length,r=go(this.ref,e,t);r.forEach(n=>this.bodySet.add(n)),this.hulls=!0;let o=!1;return{bodies:r,triangleCount:e.triangleCount,kind:e.kind,dispose:()=>{if(!o&&(o=!0,r.forEach(n=>{n.group=0,n.collidesWith=0}),this.gpu&&!this.gpu.destroyed)){let n=r.map((a,l)=>this.gpu.gpuIndex(i+l));this.gpu.setFilters(n,n.map(()=>0),n.map(()=>0)),this.gpu.wakeAll?.()}}}}configure(e,t){if(t.allowSleep!==void 0&&typeof t.allowSleep!="boolean")throw Error("allowSleep must be a boolean");e.allowSleep=t.allowSleep??!0,this.sleepOptIn||=t.allowSleep===!0;let i=t.restitution??0;if(!Number.isFinite(i)||i<0||i>1)throw Error("restitution must be between 0 and 1");if(e.restitution=i,t.isTrigger!==void 0&&typeof t.isTrigger!="boolean")throw Error("isTrigger must be a boolean");if(e.isTrigger=t.isTrigger??!1,e.group=t.group??4294967295,e.collidesWith=t.collidesWith??4294967295,t.mass!==void 0){let r=K(t.mass,"mass",!0);if(e.mass===0&&r>0)throw Error("A positive mass needs positive density");let o=e.mass>0?r/e.mass:0;e.mass=r,e.moment.forEach((n,a)=>e.moment[a]=n*o)}if(t.rotation){let r=ie(t.rotation,4,"rotation"),o=Math.hypot(...r);if(!o)throw Error("rotation must be nonzero");e.positionAng.set(r.map(n=>n/o))}return t.velocity&&e.velocityLin.set(ie(t.velocity,3,"velocity")),t.angularVelocity&&e.velocityAng.set(ie(t.angularVelocity,3,"angularVelocity")),this.bodySet.add(e),e}addPoint(e,{mass:t=.02,radius:i=.05,friction:r=.5}={}){let o=this.addSphere(i,{position:e,mass:t,friction:r});return o.moment.fill(0),this.points=!0,o}addFabric({columns:e=16,rows:t=16,spacing:i=.2,origin:r=[0,3,0],mass:o=.02,stiffness:n=1e3,anchorStiffness:a=n,materialAt:l,breakStrain:c=1/0,breakForce:u=1/0,bendingStiffness:f=Number.isFinite(c)||Number.isFinite(u)?0:n/50,pinCorners:d=!1,pinEdges:p=!d}={}){for(let[E,k]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(k)||k<2)throw Error(`${E} must be an integer >= 2`);if(K(i,"spacing"),K(o,"mass"),K(n,"stiffness"),K(a,"anchorStiffness"),l!==void 0&&typeof l!="function")throw Error("materialAt must be a function");if(K(f,"bendingStiffness",!0),typeof d!="boolean"||typeof p!="boolean")throw Error("Fabric pin options must be boolean");let m=It({breakStrain:c,breakForce:u}),h=ie(r,3,"origin"),g=(E,k)=>`${Math.min(E,k)},${Math.max(E,k)}`;function*b(){for(let E=0;E<t;E++)for(let k=0;k<e;k++){let P=E*e+k;k&&(yield[P,P-1,n]),E&&(yield[P,P-e,n]),k&&E&&(yield[P,P-e-1,n/2],yield[P-1,P-e,n/2]),k>1&&f&&(yield[P,P-2,f]),E>1&&f&&(yield[P,P-2*e,f])}}let y=new Map;if(l)for(let[E,k]of b()){let P=l((E%e+k%e)/(2*(e-1)),(Math.floor(E/e)+Math.floor(k/e))/(2*(t-1)))??{};if(typeof P!="object"||Array.isArray(P))throw Error("materialAt must return a material object");y.set(g(E,k),{scale:K(P.stiffnessScale??1,"materialAt.stiffnessScale"),material:It({...m,...P})})}let A=Array.from({length:t},(E,k)=>Array.from({length:e},(P,C)=>this.addPoint([h[0]+(C-(e-1)/2)*i,h[1],h[2]+(k-(t-1)/2)*i],{mass:p&&(C===0||k===0||C===e-1||k===t-1)||d&&(C===0||C===e-1)&&(k===0||k===t-1)?0:o,radius:i/Math.SQRT2}))),v=A.flat(),x=new Map(v.map((E,k)=>[E,k])),w=[],B=new Map,L=(E,k,P)=>{(E.mass===0||k.mass===0)&&(P*=a/n);let C=x.get(E),_=x.get(k),z=y.get(g(C,_)),R=this.addSpring(E,k,[0,0,0],[0,0,0],{stiffness:P*(z?.scale??1),...z?.material??m});return B.set(g(x.get(E),x.get(k)),w.length),w.push(R),R};for(let[E,k,P]of b())L(v[E],v[k],P);let M=[];for(let E=0;E<t;E++)for(let k=0;k<e;k++)if(k&&E){let P=(E-1)*e+k-1,C=P+1,_=E*e+k-1,z=_+1;M.push(P,_,C,C,_,z)}let I=[];for(let E=0;E<M.length;E+=3){let[k,P,C]=M.slice(E,E+3);I.push(B.get(g(k,P)),B.get(g(P,C)),B.get(g(C,k)))}return{points:v,grid:A,indices:Uint32Array.from(M),connections:w,triangleConnections:Uint32Array.from(I)}}addRope({segments:e=12,length:t=12,origin:i=[0,12,0],width:r=.15,endMass:o=20}={}){if(!Number.isInteger(e)||e<1)throw Error("segments must be a positive integer");K(t,"length"),K(r,"width"),K(o,"endMass",!0);let n=ie(i,3,"origin"),a=t/e,l=this.addBox([r,r,r],{mass:0,position:n}),c=[];for(let u=0;u<e;u++){let f=this.addBox([r,a,r],{mass:1,position:[n[0],n[1]-(u+.5)*a,n[2]]});this.addJoint(u?c[u-1]:l,f,[0,u?-a/2:0,0],[0,a/2,0]),c.push(f)}if(o){let u=this.addBox([.6,.6,.6],{mass:o,position:[n[0],n[1]-t-.3,n[2]]});this.addJoint(c.at(-1),u,[0,-a/2,0],[0,.3,0]),c.push(u)}return{anchor:l,links:c}}addRagdoll({origin:e=[0,2,0],mass:t=10}={}){let i=ie(e,3,"origin");K(t,"mass");let r=(l,c)=>this.addBox(l,{mass:t/10,position:c.map((u,f)=>u+i[f])}),o=r([.7,1.2,.35],[0,2,0]),n=this.addSphere(.25,{mass:t/10,position:[i[0],i[1]+2.9,i[2]]}),a=[o,n];this.addJoint(o,n,[0,.6,0],[0,-.3,0]);for(let l of[-1,1]){let c=r([.3,.8,.3],[l*.5,1.95,0]),u=r([.26,.7,.26],[l*.5,1.2,0]);this.addJoint(o,c,[l*.35,.35,0],[-l*.15,.4,0]),this.addJoint(c,u,[0,-.4,0],[0,.35,0]);let f=r([.28,.8,.3],[l*.2,1,0]),d=r([.26,.75,.28],[l*.2,.225,0]);this.addJoint(o,f,[l*.2,-.6,0],[0,.4,0]),this.addJoint(f,d,[0,-.4,0],[0,.375,0]),a.push(c,u,f,d)}return{torso:o,head:n,bodies:a}}addJoint(e,t,i=[0,0,0],r=[0,0,0],{linearStiffness:o=1/0,angularStiffness:n=0,breakForce:a=1/0,breakOnPull:l=!1}={}){this.assertEditable(),this.checkBodies(e,t);for(let c of[o,n,a])c!==1/0&&K(c,"joint stiffness/breakForce",!0);return this.handle(new ke(this.ref,e,t,ie(i,3,"anchorA"),ie(r,3,"anchorB"),o,n,l&&a!==1/0?-a:a))}addSpring(e,t,i=[0,0,0],r=[0,0,0],{stiffness:o=1e3,rest:n=-1,breakStrain:a=1/0,breakForce:l=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!e)throw Error("A spring requires two bodies");K(o,"stiffness",!0),n!==-1&&K(n,"rest",!0);let c=It({breakStrain:a,breakForce:l}),u=new ft(this.ref,e,t,ie(i,3,"anchorA"),ie(r,3,"anchorB"),o,n);return vr(u,c),this.handle(u)}checkBodies(e,t){if(!t||e===t||!this.bodySet.has(t)||e&&!this.bodySet.has(e))throw Error("Constraint endpoints must be distinct bodies in this scene")}addMotor(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=kt({...i,type:"motor"}),o=new ke(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);return wr(o,r),this.handle(o)}addHinge(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=kt({...i,type:"limit"}),o=K(i.span??1,"span"),n=ie(i.anchorA??[0,0,0],3,"anchorA"),a=ie(i.anchorB??[0,0,0],3,"anchorB"),l=[-1,1].map(f=>this.addJoint(e,t,n.map((d,p)=>d+f*o*.5*r.axisA[p]),a.map((d,p)=>d+f*o*.5*r.axisB[p]))),c,u;if(i.minAngle!==void 0||i.maxAngle!==void 0){let f=new ke(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);wr(f,r),c=this.handle(f)}return i.motor&&(u=this.addMotor(e,t,{axisA:r.axisA,axisB:r.axisB,...i.motor})),{joints:l,limit:c,motor:u,dispose(){for(let f of[...l,c,u])f?.dispose()}}}handle(e){let t={slot:-1,disposed:!1,dispose:()=>{t.disposed||(this.gpu?this.gpu.destroyed||this.gpu.releaseJoints([t.slot]):e.destroy(),this.handles.delete(e),t.disposed=!0)}};return t.destroy=t.dispose,this.handles.set(e,t),t}createSolver(e,t={}){if(gn(e))return e.createSolver(this,t);if(this.assertEditable(),this.hulls&&e.limits.maxStorageBuffersPerShaderStage<9)throw Error("Hull collisions require nine storage buffer bindings");let i=new Ko(e,this.ref,ho(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:$c}}:{}})),r=[];this.ref.forces.forEach((l,c)=>{this.handles.get(l).slot=c,l instanceof ke&&l.stiffnessAng>0&&r.push({slot:c})}),i.captureConstraintFrames(r),i.params.up=[...this.up];let o=[],n=[],a=[];for(let[l,c]of this.ref.bodies.entries())(c.group!==4294967295||c.collidesWith!==4294967295)&&(o.push(i.gpuIndex(l)),n.push(c.group),a.push(c.collidesWith)),c.isTrigger&&i.setSensor(i.gpuIndex(l),!0),c.restitution>0&&i.setRestitution(i.gpuIndex(l),c.restitution);return o.length&&i.setFilters(o,n,a),(t.sleeping||this.sleepOptIn)&&i.enableSleeping(this.ref,t.sleeping===!0?{}:t.sleeping),this.gpu=i,this.created=!0,i}},Fa=class{constructor(e={}){if(this.topology=new zr,Object.assign(this.topology.params,So()),bu(this.topology.params,e),e.alpha!==void 0){if(!Number.isFinite(e.alpha)||e.alpha<0||e.alpha>1)throw Error("alpha must be between zero and one");this.topology.params.alpha=e.alpha}for(let t of["postStabilize","matchNearest"])if(e[t]!==void 0){if(typeof e[t]!="boolean")throw Error(`${t} must be boolean`);this.topology.params[t]=e[t]}}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use GPU edit methods afterwards")}get bodyCount(){return this.topology.bodyCount}addShape(e,t={}){let i=e.options??t,r=this.addBox(e.size,i);return Rr(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(No(e),t)}addHull(e,t={}){return this.addShape(Uo(e),t)}addCapsule(e,t,i={}){return this.addShape(qo(e,t),i)}addSegment(e,t,i={}){return this.addShape(Vo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Wo(e,t,i))}addBox(e,{density:t=1,friction:i=.6,position:r=[0,0],angle:o=0,velocity:n=[0,0,0],restitution:a=0,isTrigger:l=!1,group:c=4294967295,collidesWith:u=4294967295,allowSleep:f}={}){if(this.assertEditable(),f!==void 0&&typeof f!="boolean")throw Error("allowSleep must be boolean");let d=ie(e,2,"size");d.forEach(h=>K(h,"size"));let p=ie(r,2,"position");if(!Number.isFinite(o))throw Error("angle must be finite");if(!Number.isFinite(a)||a<0||a>1)throw Error("restitution must be between 0 and 1");if(typeof l!="boolean")throw Error("isTrigger must be boolean");for(let h of[c,u])if(!Number.isInteger(h)||h<0||h>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let m=this.topology.addBody(d,K(t,"density",!0),K(i,"friction",!0),[...p,o],ie(n,3,"velocity"));return this.restitution??=new Map,a>0&&this.restitution.set(m,a),this.sensors??=new Set,l&&this.sensors.add(m),this.filters??=new Map,(c!==4294967295||u!==4294967295)&&this.filters.set(m,[c,u]),this.topology.avbdSleepEligible??=new Map,f!==void 0&&this.topology.avbdSleepEligible.set(m,f),m}checkBodies(e,t){if(!Number.isInteger(e)||!Number.isInteger(t)||e<-1||e>=this.bodyCount||t<0||t>=this.bodyCount||e===t)throw Error("Invalid 2D constraint endpoints")}addJoint(e,t,i=[0,0],r=[0,0],{stiffness:o=[1/0,1/0,0],breakForce:n=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!o||o.length!==3||Array.from(o).some(l=>l!==1/0&&(!Number.isFinite(l)||l<0))||n!==1/0&&(!Number.isFinite(n)||n<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");let a=this.topology.addJoint(e,t,ie(i,2,"anchorA"),ie(r,2,"anchorB"),o,n);return this.constraintHandle(a.slot,a)}constraintHandle(e,t={}){return Object.assign(t,{slot:e,alive:!0,disposed:!1}),t.dispose=()=>{t.disposed||(this.gpu&&!this.gpu.destroyed?this.gpu.disableConstraint(e):this.gpu||this.topology.data.fill(0,e*O+Q,e*O+Q+3),t.alive=!1,t.disposed=!0)},t.destroy=t.dispose,this.topology.handles[e]=t,t}addSpring(e,t,i=[0,0],r=[0,0],{stiffness:o=1e3,rest:n=1}={}){if(this.assertEditable(),this.checkBodies(e,t),e<0)throw Error("A spring requires two bodies");let a=this.topology.jointCount;return this.topology.addSpring(e,t,ie(i,2,"anchorA"),ie(r,2,"anchorB"),K(o,"stiffness",!0),K(n,"rest",!0)),this.constraintHandle(a)}addMotor(e,t,{speed:i=1,maxTorque:r=100}={}){if(this.assertEditable(),this.checkBodies(e,t),!Number.isFinite(i))throw Error("speed must be finite");let o=this.topology.jointCount;return this.topology.addMotor(e,t,i,K(r,"maxTorque",!0)),this.constraintHandle(o)}addAngularLimit(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=ki(i),o=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);vs(this.topology,o.slot,r);let n=this.constraintHandle(o.slot,o);return n.setLimits=a=>{if(n.disposed)throw Error("The angular limit has been disposed");return this.gpu?this.gpu.setAngleLimits(n.slot,a):vs(this.topology,n.slot,ki(a)),n},n}addHinge(e,t,i=[0,0],r=[0,0],o={}){this.assertEditable(),this.checkBodies(e,t);let n=ie(i,2,"anchorA"),a=ie(r,2,"anchorB"),l=o.minAngle!==void 0||o.maxAngle!==void 0?ki(o):null,c=o.motor;if(c){if(!Number.isFinite(c.speed??1))throw Error("speed must be finite");K(c.maxTorque??100,"maxTorque",!0)}let u=this.addJoint(e,t,n,a),f=l?this.addAngularLimit(e,t,l):null,d=c?this.addMotor(e,t,c):null;return{joint:u,limits:f,motor:d,disposed:!1,dispose(){this.disposed||(this.joint.dispose(),this.limits?.dispose(),this.motor?.dispose(),this.disposed=!0)},destroy(){this.dispose()}}}createSolver(e,t={}){if(gn(e))return e.createSolver(this,t);this.assertEditable();let i=new Qo(e,this.topology,t);for(let[r,o]of this.restitution??[])i.setRestitution(r,o);for(let r of this.sensors??[])i.setSensor(r,!0);for(let[r,o]of this.filters??[])i.setFilters([r],[o[0]],[o[1]]);return this.gpu=i,this.created=!0,i}};var Cs=class{constructor(e){if(this.device=e,this.latestMs=null,this.samples=0,this.slots=[],!!e.features.has("timestamp-query"))for(let t=0;t<3;t++)this.slots.push({query:e.createQuerySet({type:"timestamp",count:2}),resolve:e.createBuffer({size:256,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:16,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),busy:!1})}begin(){let e=this.slots.find(t=>!t.busy);return!e||this.disposed?null:(e.busy=!0,e)}descriptor(e){return e?{timestampWrites:{querySet:e.query,beginningOfPassWriteIndex:0,endOfPassWriteIndex:1}}:{}}encode(e,t){t&&(e.resolveQuerySet(t.query,0,2,t.resolve,0),e.copyBufferToBuffer(t.resolve,0,t.read,0,16))}resolve(e){if(e)return e.pending=e.read.mapAsync(GPUMapMode.READ).then(()=>{let t=new BigUint64Array(e.read.getMappedRange());this.latestMs=Number(t[1]-t[0])/1e6,this.samples++,e.read.unmap()}).catch(()=>{}).finally(()=>{e.busy=!1,e.pending=null}),e.pending}settled(){return Promise.all(this.slots.map(e=>e.pending))}destroy(){this.disposed=!0;for(let e of this.slots)e.query.destroy(),e.resolve.destroy(),e.read.destroy()}};var yu=(s,e)=>Math.ceil(s/e)*e,vu=s=>String(s>>>0),Ss=class{constructor(e,t={}){this.device=e,this.maxFreeBytes=t.maxFreeBytes??201326592,this.freeBytes=0,this.free=new Map,this.all=new Set}acquire(e,t,i="recycled-buffer"){let r=yu(Math.max(4,e),4),o=vu(t),n=this.free.get(o);if(n){let l=-1,c=1/0;for(let u=0;u<n.length;u+=1){let f=n[u];f.size>=r&&f.size<c&&(l=u,c=f.size)}if(l>=0){let[u]=n.splice(l,1);return this.freeBytes-=u.size,u.buffer}}let a=this.device.createBuffer({label:i,size:r,usage:t,mappedAtCreation:!1});return this.all.add(a),a}release(e,t,i){if(!e)return;let r=yu(Math.max(4,t),4);if(this.freeBytes+r>this.maxFreeBytes){e.destroy(),this.all.delete(e);return}let o=vu(i),n=this.free.get(o);n||(n=[],this.free.set(o,n)),n.push({buffer:e,size:r,usage:i}),this.freeBytes+=r}trim(e=this.maxFreeBytes){this.maxFreeBytes=e;for(let[t,i]of this.free.entries()){for(;this.freeBytes>this.maxFreeBytes&&i.length>0;){let r=i.pop();r.buffer.destroy(),this.all.delete(r.buffer),this.freeBytes-=r.size}i.length===0&&this.free.delete(t)}}destroy(){for(let e of this.all)e.destroy();this.free.clear(),this.all.clear(),this.freeBytes=0}};var xu=`const EPS : f32 = 1.0e-7;
|
|
5187
|
+
}`);var Yc=ii;var tu=8,Qc=16,Xi=1e-6,In=1e-5,ed=1e-6,Rn=34028234663852886e22,Zo=0,iu=1,ea=2;function Kc(s){let e=s.positionAng;return{center:s.positionLin,half:ne(S(),s.size,.5),axis:[le(S(),e,[1,0,0]),le(S(),e,[0,1,0]),le(S(),e,[0,0,1])]}}var Ji=(s,e)=>Math.abs(V(s,e));function ta(s,e){let t=V(e,s.axis[0])>=0?1:-1,i=V(e,s.axis[1])>=0?1:-1,r=V(e,s.axis[2])>=0?1:-1,o=D(S(),s.center,s.axis[0],s.half[0]*t);return D(o,o,s.axis[1],s.half[1]*i),D(o,o,s.axis[2],s.half[2]*r)}function ru(s,e){return e===0?{u:s.axis[1],v:s.axis[2],extentU:s.half[1],extentV:s.half[2]}:e===1?{u:s.axis[0],v:s.axis[2],extentU:s.half[0],extentV:s.half[2]}:{u:s.axis[0],v:s.axis[1],extentU:s.half[0],extentV:s.half[1]}}function td(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]);return{normal:r,center:o,...ru(s,e)}}function id(s,e){let t=0,i=-Rn;for(let r=0;r<3;r++){let o=Ji(s.axis[r],e);o>i&&(i=o,t=r)}return t}function rd(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]),{u:a,v:n,extentU:l,extentV:c}=ru(s,e),u=(f,d)=>{let p=D(S(),o,a,f*l);return D(p,p,n,d*c)};return[u(1,1),u(-1,1),u(-1,-1),u(1,-1)]}function sd(s,e,t){let i=[];if(s.length===0)return i;let r=s[s.length-1],o=V(e,r)-t;for(let a of s){let n=V(e,a)-t,l=o<=In,c=n<=In;if(l!==c){let u=0,f=o-n;Math.abs(f)>Xi&&(u=ce(o/f,0,1)),i.length<Qc&&i.push(D(S(),r,J(S(),a,r),u))}c&&i.length<Qc&&i.push(a),r=a,o=n}return i}var ia=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(S(),Li(S(),e,t),.5);for(let n of this.midpoints)if(Ne(J(S(),r,n))<ed)return;if(this.contacts.length>=tu)return;let{bodyA:o,bodyB:a}=this;this.contacts.push({feature:i,rA:Te(S(),o.positionAng,J(S(),e,o.positionLin)),rB:Te(S(),a.positionAng,J(S(),t,a.positionLin))}),this.midpoints.push(r)}};function zn(s,e,t,i,r,o,a,n){let l=Ne(i);if(l<Xi)return!0;let c=1/Math.sqrt(l),u=ne(S(),i,c);V(u,t)<0&&ee(u,u);let f=Math.abs(V(t,u)),d=s.half[0]*Ji(u,s.axis[0])+s.half[1]*Ji(u,s.axis[1])+s.half[2]*Ji(u,s.axis[2]),p=e.half[0]*Ji(u,e.axis[0])+e.half[1]*Ji(u,e.axis[1])+e.half[2]*Ji(u,e.axis[2]),m=f-(d+p);return m>0?!1:((!n.valid||m>n.separation)&&(n.valid=!0,n.type=r,n.indexA=o,n.indexB=a,n.separation=m,n.normalAB=u),!0)}function Zc(s,e,t){let i=(e+1)%3,r=(e+2)%3,o=V(t,s.axis[i])>=0?1:-1,a=V(t,s.axis[r])>=0?1:-1,n=D(S(),s.center,s.axis[i],s.half[i]*o);D(n,n,s.axis[r],s.half[r]*a);let l=ne(S(),s.axis[e],s.half[e]);return[J(S(),n,l),Li(S(),n,l)]}function od(s,e,t,i){let r=J(S(),e,s),o=J(S(),i,t),a=J(S(),s,t),n=V(r,r),l=V(o,o),c=V(o,a),u=0,f=0;if(n<=Xi&&l<=Xi)return[s,t];if(n<=Xi)f=ce(c/l,0,1);else{let d=V(r,a);if(l<=Xi)u=ce(-d/n,0,1);else{let p=V(r,o),m=n*l-p*p;Math.abs(m)>Xi&&(u=ce((p*c-d*l)/m,0,1)),f=(p*u+c)/l,f<0?(f=0,u=ce(-d/n,0,1)):f>1&&(f=1,u=ce((p-d)/n,0,1))}}return[D(S(),s,r,u),D(S(),t,o,f)]}function eu(s,e,t,i,r,o,a){let n=r?t:i,l=r?i:t,c=r?a:ee(S(),a),u=td(n,o,c),f=id(l,u.normal),d=rd(l,f,u.normal),p=[[u.u,u.extentU],[ee(S(),u.u),u.extentU],[u.v,u.extentV],[ee(S(),u.v),u.extentV]];for(let[g,b]of p)if(d=sd(d,g,V(g,u.center)+b),!d.length)return[];let m=new ia(s,e),h=(r?Zo:iu)<<24|(o&255)<<16|(f&255)<<8;for(let g=0;g<d.length&&m.contacts.length<tu;g++){let b=d[g],y=V(J(S(),b,u.center),u.normal);if(y>In)continue;let A=D(S(),b,u.normal,-y),v=r?A:b,x=r?b:A;m.add(v,x,h|g&255)}return m.contacts.length||m.add(ta(t,a),ta(i,ee(S(),a)),h),m.contacts}function ad(s,e,t,i,r,o,a){let[n,l]=Zc(t,r,a),[c,u]=Zc(i,o,ee(S(),a)),[f,d]=od(n,l,c,u),p=new ia(s,e),m=ea<<24|(r&255)<<8|o&255;return p.add(f,d,m),p.contacts.length||p.add(ta(t,a),ta(i,ee(S(),a)),m),p.contacts}function su(s,e,t,i=!0){let r=Kc(s),o=Kc(e),a=J(S(),o.center,r.center),n={type:Zo,indexA:0,indexB:0,separation:-Rn,normalAB:S(),valid:!1},l={type:ea,indexA:0,indexB:0,separation:-Rn,normalAB:S(),valid:!1};for(let f=0;f<3;f++)if(!zn(r,o,a,r.axis[f],Zo,f,-1,n))return[];for(let f=0;f<3;f++)if(!zn(r,o,a,o.axis[f],iu,-1,f,n))return[];let c=S();for(let f=0;f<3;f++)for(let d=0;d<3;d++)if(Dt(c,r.axis[f],o.axis[d]),!zn(r,o,a,c,ea,f,d,l))return[];if(!n.valid)return[];let u=n;return l.valid&&(i?l.separation>.95*n.separation+.01:.95*l.separation>n.separation+.01)&&(u=l),_s(t,ee(S(),u.normalAB)),u.type===ea?ad(s,e,r,o,u.indexA,u.indexB,u.normalAB):u.type===Zo?eu(s,e,r,o,!0,u.indexA,u.normalAB):eu(s,e,r,o,!1,u.indexB,u.normalAB)}var de=(s,e)=>s.map((t,i)=>t+e[i]),T=(s,e)=>s.map((t,i)=>t-e[i]),q=(s,e)=>s.map(t=>t*e),F=(s,e)=>s.reduce((t,i,r)=>t+i*e[r],0),xt=(s,e)=>[s[1]*e[2]-s[2]*e[1],s[2]*e[0]-s[0]*e[2],s[0]*e[1]-s[1]*e[0]],Tt=s=>q(s,1/(Math.hypot(...s)||1)),Ee=(s,e)=>Array.from(le(S(),s,e)),ou=(s,e)=>Te(S(),s.positionAng,T(e,s.positionLin)),Ot=(s,e)=>de(s.positionLin,Ee(s.positionAng,e)),nd=[[1,3,7,5],[0,4,6,2],[2,6,7,3],[0,1,5,4],[4,5,7,6],[0,2,3,1]],ld=[[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]];function jr(s){if(De(s)||Fe(s)){let a=(De(s),s.size[0]/2),n=Fe(s)?Math.max(0,s.size[1]/2-a):0;return{body:s,radius:a,spine:[Ot(s,[0,-n,0]),Ot(s,[0,n,0])]}}let e=Ct(s),t=e?Array.from({length:e.vertices.length/3},(a,n)=>Ot(s,e.vertices.slice(n*3,n*3+3))):Array.from({length:8},(a,n)=>Ot(s,s.size.map((l,c)=>l/2*(n>>c&1?1:-1)))),i=e?e.faces.map(a=>({vertices:a.verts.map(n=>t[n]),normal:Ee(s.positionAng,a.normal)})):nd.map((a,n)=>({vertices:a.map(l=>t[l]),normal:Ee(s.positionAng,ld[n])})),r=[],o=new Set;for(let a of i)for(let n=0;n<a.vertices.length;n++){let l=a.vertices[n],c=a.vertices[(n+1)%a.vertices.length],u=t.indexOf(l),f=t.indexOf(c),d=`${Math.min(u,f)},${Math.max(u,f)}`;o.has(d)||(o.add(d),r.push([l,c]))}return{body:s,vertices:t,faces:i,edges:r}}function As(s,e){let t=s.spine??s.vertices,i=t[0];for(let r of t)F(r,e)>F(i,e)&&(i=r);return s.spine?de(i,q(e,s.radius)):i}function cd(s,e,t){let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,F(T(t,s),i)/(F(i,i)||1)))))}function ra([s,e],[t,i]){let r=T(e,s),o=T(i,t),a=T(s,t),n=F(r,r),l=F(o,o),c=F(o,a),u=0,f=0,d=p=>Math.max(0,Math.min(1,p));if(n<1e-16)f=l>1e-16?d(c/l):0;else{let p=F(r,a);if(l<1e-16)u=d(-p/n);else{let m=F(r,o),h=n*l-m*m;u=h>1e-12*n*l?d((m*c-p*l)/h):0,f=(m*u+c)/l,f<0?(f=0,u=d(-p/n)):f>1&&(f=1,u=d((m-p)/n))}}return[de(s,q(r,u)),de(t,q(o,f))]}function ud(s,e,t){let i=[];for(let r=0;r<s.length;r++){let o=s[r],a=s[(r+1)%s.length],n=F(o,e)-t,l=F(a,e)-t;n<=1e-8&&i.push(o),n<0!=l<0&&i.push(de(o,q(T(a,o),n/(n-l))))}return i}function fd(s,e,t,i){let r=T(t,e),o=T(i,e),a=T(s,e),n=F(r,a),l=F(o,a);if(n<=0&&l<=0)return e;let c=T(s,t),u=F(r,c),f=F(o,c);if(u>=0&&f<=u)return t;let d=n*f-u*l;if(d<=0&&n>=0&&u<=0)return de(e,q(r,n/(n-u)));let p=T(s,i),m=F(r,p),h=F(o,p);if(h>=0&&m<=h)return i;let g=m*l-n*h;if(g<=0&&l>=0&&h<=0)return de(e,q(o,l/(l-h)));let b=u*h-m*f;if(b<=0&&f-u>=0&&m-h>=0)return de(t,q(T(i,t),(f-u)/(f-u+m-h)));let y=1/(b+g+d);return de(e,de(q(r,g*y),q(o,d*y)))}function dd(s,e){let t=null,i=1/0;for(let r of s.faces)for(let o=1;o<r.vertices.length-1;o++){let a=fd(e,r.vertices[0],r.vertices[o],r.vertices[o+1]),n=F(T(e,a),T(e,a));n<i&&(t=a,i=n)}return t}function au(s,e,t,i,r){if(!De(s)&&!De(e)&&!Fe(s)&&!Fe(e)&&!Ct(s)&&!Ct(e))return su(s,e,t,s.solver.faceBias!==!1);i??=jr(s),r??=jr(e);let o=-1/0,a=[0,0,1],n=null,l=(f,d=null)=>{if(Math.hypot(...f)<1e-9)return;let p=Tt(f),m=F(T(As(i,q(p,-1)),As(r,p)),p),h=F(T(As(r,q(p,-1)),As(i,p)),p);h>m&&(m=h,p=q(p,-1)),m>o+1e-7&&(o=m,a=p,n=d)};for(let f of[i,r])for(let d of f.faces??[])l(d.normal);if(i.edges&&r.edges)for(let f of i.edges)for(let d of r.edges)l(xt(T(f[1],f[0]),T(d[1],d[0])),[f,d]);if(i.spine&&r.spine){let f=ra(i.spine,r.spine);l(T(...f))}for(let[f,d]of[[i,r],[r,i]])if(f.spine&&d.faces){for(let p of d.vertices)l(T(p,cd(...f.spine,p)));for(let p of d.edges){let m=ra(f.spine,p);l(T(...m))}}if(o===-1/0&&l(T(s.positionLin,e.positionLin)),o>0)return[];o===-1/0&&(a=[0,0,1],o=-(i.radius+r.radius));let c=Tt(xt(a,Math.abs(a[0])<.7?[1,0,0]:[0,1,0]));t.set([...a,...c,...xt(a,c)]);let u=[];if(i.spine&&r.spine){let[f,d]=ra(i.spine,r.spine);u=[[de(f,q(a,-i.radius)),de(d,q(a,r.radius))]]}else if(i.spine||r.spine){let f=i.spine?i:r,d=i.spine?r:i,p=i.spine?q(a,-1):a,m=As(f,p),h=dd(d,m);u=[i.spine?[m,h]:[h,m]]}else if(n)u=[ra(...n)];else{let f=(A,v)=>A.faces.reduce((x,w)=>F(w.normal,v)>F(x.normal,v)?w:x,A.faces[0]),d=f(i,q(a,-1)),p=f(r,a),m=F(d.normal,q(a,-1))>=F(p.normal,a),h=m?d:p,b=f(m?r:i,q(h.normal,-1)).vertices,y=q(h.vertices.reduce((A,v)=>de(A,v),[0,0,0]),1/h.vertices.length);for(let A=0;A<h.vertices.length&&b.length;A++){let v=h.vertices[A],x=h.vertices[(A+1)%h.vertices.length],w=Tt(xt(T(x,v),h.normal));F(T(y,v),w)>0&&(w=q(w,-1)),b=ud(b,w,F(v,w))}for(let A of b){let v=F(T(A,h.vertices[0]),h.normal);if(v<=1e-6){let x=T(A,q(h.normal,v));u.push(m?[x,A]:[A,x])}}}return u.slice(0,8).map(([f,d],p)=>({feature:p+1,rA:ou(s,f),rB:ou(e,d)}))}var sa=(s,e,t)=>Math.max(e,Math.min(t,s)),hd=(s,e)=>[F(s.slice(0,3),e),F(s.slice(3,6),e),F(s.slice(6,9),e)];function jn(s,e=!1){if(e){s.rest=Math.hypot(...T(Ot(s.bodyA,s.rA),Ot(s.bodyB,s.rB)));return}let t=s.bodyA?.positionAng??[0,0,0,1];s.cpuRest=Gr(jt(),va(jt(),t),s.bodyB.positionAng),s.evaluateAng=i=>{let r=Gr(jt(),s.bodyA?.positionAng??[0,0,0,1],s.cpuRest);ct(i,r,s.bodyB.positionAng);for(let o=0;o<3;o++)i[o]*=s.torqueArm;return i}}var On=class extends ai{initialize(){let e=this.bodyA,t=this.bodyB;if(!((e.group??4294967295)&(t.collidesWith??4294967295))||!((t.group??4294967295)&(e.collidesWith??4294967295)))return!1;let i=this.contacts,r=De(e)||De(t)||Fe(e)||Fe(t);this.friction=Math.sqrt(e.friction*t.friction),this.contacts=au(e,t,this.basis,this.solver.cpuGeometry.get(e),this.solver.cpuGeometry.get(t)).map(o=>{let a=i.find(n=>n.feature===o.feature);return{feature:o.feature,rA:a?.stick&&!r?a.rA:o.rA,rB:a?.stick&&!r?a.rB:o.rB,C0:S(),penalty:a?a.penalty.slice():S(),lambda:a?a.lambda.slice():S(),stick:a?.stick??!1}});for(let o of this.contacts){o.C0.set(hd(this.basis,T(Ot(e,o.rA),Ot(t,o.rB)))),o.C0[0]+=Ms;for(let a=0;a<3;a++)o.lambda[a]*=this.solver.alpha*this.solver.gamma,o.penalty[a]=sa(o.penalty[a]*this.solver.gamma,Et,Ve);if(this.solver.massPenalty!==!1){let a=Math.min(e.mass>0?e.mass:1/0,t.mass>0?t.mass:1/0);o.penalty[0]=Math.max(o.penalty[0],sa(a/this.solver.dt**2,Et,Ve))}}return this.contacts.length>0}updatePrimal(...e){!this.bodyA.isTrigger&&!this.bodyB.isTrigger&&super.updatePrimal(...e)}updateDual(...e){!this.bodyA.isTrigger&&!this.bodyB.isTrigger&&super.updateDual(...e)}},Tn=class extends Ft{constructor(e,t,i){super(e,t.bodyA,t.bodyB),this.options={...i},this.pen=[Et,Et],this.lam=[0,0],this.source=t;let r=i.axisA;this.ta=Tt(Math.abs(r[0])>Math.abs(r[2])?[-r[1],r[0],0]:[0,-r[2],r[1]]);let o=this.bodyA?Ee(this.bodyA.positionAng,this.ta):this.ta,a=Array.from(Te(S(),this.bodyB.positionAng,o));a=T(a,q(i.axisB,F(a,i.axisB))),this.tb=Tt(a)}frame(){let e=this.bodyA?Ee(this.bodyA.positionAng,this.options.axisA):this.options.axisA,t=this.bodyA?Ee(this.bodyA.positionAng,this.ta):this.ta,i=Ee(this.bodyB.positionAng,this.tb);return{axis:e,angle:Math.atan2(F(e,xt(t,i)),F(t,i))}}initialize(){let e=this.frame().angle;this.c0=this.options.type==="motor"?[e,0]:[Math.min(0,e-this.options.minAngle),Math.max(0,e-this.options.maxAngle)];for(let t=0;t<2;t++)this.lam[t]*=this.solver.alpha*this.solver.gamma,this.pen[t]=sa(this.pen[t]*this.solver.gamma,Et,Ve);return!this.source.broken}evaluate(e){let{angle:t}=this.frame(),i=this.options,r=i.type==="motor"?[Math.atan2(Math.sin(t-this.c0[0]),Math.cos(t-this.c0[0]))-i.speed*this.solver.dt,0]:[t-i.minAngle-e*this.c0[0],t-i.maxAngle-e*this.c0[1]],o=r.map((a,n)=>this.pen[n]*a+this.lam[n]);return{c:r,f:i.type==="motor"?[sa(o[0],-i.maxTorque,i.maxTorque),0]:[Math.min(0,o[0]),Math.max(0,o[1])]}}updatePrimal(e,t,i){let{f:r}=this.evaluate(t),o=this.options;if(o.type==="motor"&&o.maxTorque===0)return;let a=q(this.frame().axis,e===this.bodyA?-1:1);for(let n=0;n<(o.type==="motor"?1:2);n++)if(!(o.type==="limit"&&r[n]===0))for(let l=0;l<3;l++){i.rhsAng[l]+=a[l]*r[n];for(let c=0;c<3;c++)i.lhsAng[l*3+c]+=a[l]*a[c]*this.pen[n]}}updateDual(e){let{c:t,f:i}=this.evaluate(e);this.lam=i;for(let r=0;r<2;r++)this.pen[r]=Math.min(Ve,this.pen[r]+this.solver.betaAng*(this.options.type==="motor"?Math.abs(t[r]):r===0?Math.max(0,-t[r]):Math.max(0,t[r])))}};function oa(s){if(!s.cpuPrepared){s.cpuPrepared=!0;for(let e of[...s.forces]){let t=rs(e);if(t){e.unlinkFromBodies(),s.forces.splice(s.forces.indexOf(e),1),e.cpuAngular=new Tn(s,e,t);continue}if(e instanceof ke&&e.stiffnessAng>0&&jn(e),e instanceof ft){let i=e.initialize.bind(e);e.initialize=()=>{if(e.broken)return!1;let r=Ui(e);if(r){let o=Math.max(0,Math.hypot(...T(Ot(e.bodyA,e.rA),Ot(e.bodyB,e.rB)))-e.rest);if(o/Math.max(e.rest,1e-6)>r.breakStrain||o*e.stiffness>r.breakForce)return e.broken=!0,!1}return i()}}}}}function nu(s){let e=s.bodies;s.cpuGeometry=new Map;for(let l of e)(De(l)||Fe(l)||Ct(l))&&s.cpuGeometry.set(l,jr(l));let t=e.filter(l=>l.mass>0).slice(0,1024).map(l=>l.radius).sort((l,c)=>l-c),i=Math.max(.1,(t[Math.floor(t.length/2)]??1)*2),r=new Map,o=[],a=new Set,n=(l,c)=>{if(l===c||e[l].mass<=0&&e[c].mass<=0)return;let u=Math.max(l,c),f=Math.min(l,c),d=u*e.length+f;if(a.has(d))return;a.add(d);let p=e[u],m=e[f];if(!((p.group??4294967295)&(m.collidesWith??4294967295))||!((m.group??4294967295)&(p.collidesWith??4294967295)))return;let h=p.radius+m.radius;F(T(p.positionLin,m.positionLin),T(p.positionLin,m.positionLin))<=h*h&&!p.constrainedTo(m)&&new On(s,p,m)};for(let l=0;l<e.length;l++){let c=e[l],u=c.positionLin.map(d=>Math.floor((d-c.radius)/i)),f=c.positionLin.map(d=>Math.floor((d+c.radius)/i));if((f[0]-u[0]+1)*(f[1]-u[1]+1)*(f[2]-u[2]+1)>128){o.push(l);continue}for(let d=u[0];d<=f[0];d++)for(let p=u[1];p<=f[1];p++)for(let m=u[2];m<=f[2];m++){let h=`${d},${p},${m}`,g=r.get(h)??[];for(let b of g)n(l,b);g.push(l),r.set(h,g)}}for(let l of o)for(let c=0;c<e.length;c++)n(l,c)}function aa(s,e){if(!(e===3?s.bodies.some(r=>r.restitution>0):[...s.restitution.values()].some(r=>r>0)))return null;if(e===2)return Float64Array.from(s.velocity);let i=new Float64Array(s.bodies.length*6);return s.bodies.forEach((r,o)=>{i.set(r.velocityLin,o*6),i.set(r.velocityAng,o*6+3)}),i}function na(s,e,t){if(!t)return;let i=e===3?s.bodies.length:s.bodyCount,r=e===3?6:4,o=e===3?new Float64Array(i*r):s.velocity;e===3&&s.bodies.forEach((h,g)=>{o.set(h.velocityLin,g*r),o.set(h.velocityAng,g*r+3)});let a=h=>e===3?s.bodies[h].mass>0&&!s.bodies[h].sleeping?1/s.bodies[h].mass:0:s.dynamic[h]?1/s.shape[h*4+2]:0,n=(h,g)=>{if(!a(h))return e===3?[0,0,0]:0;if(e===2)return s.shape[h*4+3]>0?g/s.shape[h*4+3]:0;let b=s.bodies[h],y=b.positionAng,A=Ee([-y[0],-y[1],-y[2],y[3]],g);return Ee(y,A.map((v,x)=>b.moment[x]>0?v/b.moment[x]:0))},l=(h,g,b)=>{let y=g*r;return e===3?T(h.subarray(y,y+3),xt(b,h.subarray(y+3,y+6))):[h[y]-h[y+2]*b[1],h[y+1]+h[y+2]*b[0]]},c=(h,g)=>e===3?xt(h,g):h[0]*g[1]-h[1]*g[0],u=(h,g)=>e===3?F(h,g):h*g,f;if(e===3){let h=new Map(s.bodies.map((g,b)=>[g,b]));f=s.forces.filter(g=>g.contacts?.length&&!g.bodyA.isTrigger&&!g.bodyB.isTrigger).map(g=>{let b=(y,A)=>{let v=[0,0,0];for(let x of g.contacts){let w=Ee(y.positionAng,x[A]);for(let B=0;B<3;B++)v[B]+=w[B]/g.contacts.length}return v};return{a:h.get(g.bodyA),b:h.get(g.bodyB),n:g.basis.slice(0,3),ra:b(g.bodyA,"rA"),rb:b(g.bodyB,"rB"),e:Math.max(g.bodyA.restitution??0,g.bodyB.restitution??0)}})}else{let h=(g,b)=>{let y=Math.cos(s.pose[g*4+2]),A=Math.sin(s.pose[g*4+2]);return[y*b[0]-A*b[1],A*b[0]+y*b[1]]};f=s.contactPairs.filter(g=>!s.sensors.has(g.a)&&!s.sensors.has(g.b)).map(g=>({...g,ra:h(g.a,g.ra),rb:h(g.b,g.rb),e:Math.max(s.restitution.get(g.a)??0,s.restitution.get(g.b)??0)}))}let d=new Uint32Array(i);if(e===3)s.bodies.forEach((h,g)=>{d[g]=h.forces.length});else for(let h=0;h<i;h++)d[h]=s.adjStart[h+1]-s.adjStart[h];let p=[];for(let h of f){if(h.e<=0)continue;let g=F(h.n,T(l(t,h.a,h.ra),l(t,h.b,h.rb)));if(g>=-1)continue;let b=c(h.ra,h.n),y=c(h.rb,h.n),A=n(h.a,b),v=n(h.b,y),x=a(h.a)+a(h.b)+u(b,A)+u(y,v);x>0&&p.push({...h,inertiaA:A,inertiaB:v,k:x,target:-h.e*g,impulse:0})}let m=new Float64Array(o.length);for(let h=0;h<12&&p.length;h++){m.fill(0);for(let g of p){let b=F(g.n,T(l(o,g.a,g.ra),l(o,g.b,g.rb))),y=Math.max(0,g.impulse+(g.target-b)/(g.k*Math.max(1,d[g.a],d[g.b]))),A=y-g.impulse;g.impulse=y;for(let[v,x,w]of[[g.a,1,g.inertiaA],[g.b,-1,g.inertiaB]]){let B=v*r,L=a(v);for(let M=0;M<e;M++)m[B+M]+=x*A*L*g.n[M];if(e===3)for(let M=0;M<3;M++)m[B+3+M]+=x*A*w[M];else m[B+2]+=x*A*w}}for(let g=0;g<o.length;g++)o[g]+=m[g]}e===3&&s.bodies.forEach((h,g)=>{h.velocityLin.set(o.subarray(g*r,g*r+3)),h.velocityAng.set(o.subarray(g*r+3,g*r+6))})}function lu(s,e,t){let i=e.windSpeed??0;if(!or(s)||s.mass<=0||i===0)return null;let r=e.windAngle??0,o=[Math.cos(r),Math.sin(r),0],a=[-o[1],o[0],0],n=s.positionLin,l=t*e.dt,c=F(n,o),u=F(n,a),f=.5*Math.sin(1.1*l-.35*c+.2*u)+.3*Math.sin(2.9*l-.8*c+.6*n[2])+.2*Math.sin(6.1*l-1.7*c+1.3*n[2]+.9*u),d=.6*Math.sin(1.7*l-.6*c+.4*n[2])+.4*Math.sin(4.3*l-1.3*c-.7*n[2]),p=Math.sin(2.3*l-.9*c+1.1*u),m=e.windGust??.4,h=o.map((x,w)=>i*x*(1+m*f)+m*i*(a[w]*.25*d+(w===2?.1*p:0))-s.velocityLin[w]),g=Ee(s.positionAng,[0,0,1]),b=(e.windPressure??.72)*s.size[0]*s.size[1]/s.mass,y=F(h,g),A=h.map((x,w)=>x-g[w]*y),v=b*y*Math.abs(y)/(1+2*e.dt*b*Math.abs(y));return g.map((x,w)=>x*v+A[w]*.06*b*Math.hypot(...A))}var pd=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),la=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;massPenalty=!0;faceBias=!0;up=[0,0,1];cpuStep=0;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,pd())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=S(),a=S();for(let n=this.bodies.length-1;n>=0;n--){let l=this.bodies[n];if(l.mass<=0)continue;Te(o,l.positionAng,J(o,e,l.positionLin)),Te(a,l.positionAng,t);let c=0,u=1/0,f=!0;for(let p=0;p<3&&f;p++){let m=l.size[p]*.5;if(Math.abs(a[p])<1e-6){(o[p]<-m||o[p]>m)&&(f=!1);continue}let h=1/a[p],g=(-m-o[p])*h,b=(m-o[p])*h;g>b&&([g,b]=[b,g]),c=Math.max(c,g),u=Math.min(u,b),c>u&&(f=!1)}if(!f)continue;let d=c>=0?c:u;d<0||(!r||d<r.t)&&(r={body:l,local:D(S(),o,a,d),t:d})}return r}sys=zs();dxLin=S();dxAng=S();dp=S();accel=S();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:o}=this,a=aa(this,3);oa(this),nu(this);let n=[];for(let l of this.forces)l.initialize()?n.push(l):l.unlinkFromBodies();this.forces=n;for(let l=i.length-1;l>=0;l--){let c=i[l];D(c.inertialLin,c.positionLin,c.velocityLin,e),c.mass>0&&D(c.inertialLin,c.inertialLin,this.up,t*e*e);let u=lu(c,this,this.cpuStep);u&&D(c.inertialLin,c.inertialLin,u,e*e),si(c.inertialAng,c.positionAng,cu(c.velocityAng,e)),_t(this.accel,J(this.accel,c.velocityLin,c.prevVelocityLin),e);let f=V(this.accel,this.up)*Ps(t),d=ce(f/Math.abs(t),0,1);Number.isFinite(d)||(d=0),c.initialLin.set(c.positionLin),c.initialAng.set(c.positionAng),c.mass>0&&!c.sleeping&&(D(c.positionLin,c.positionLin,c.velocityLin,e),D(c.positionLin,c.positionLin,this.up,t*d*e*e),si(c.positionAng,c.positionAng,cu(c.velocityAng,e)))}for(let l=0;l<this.iterations;l++){for(let c=i.length-1;c>=0;c--){let u=i[c];if(u.mass<=0||u.sleeping)continue;let f=u.mass,d=u.moment;Ce(r.lhsLin,f/(e*e),f/(e*e),f/(e*e)),Ce(r.lhsAng,d[0]/(e*e),d[1]/(e*e),d[2]/(e*e)),r.lhsCross.fill(0),ue(r.rhsLin,r.lhsLin,J(o,u.positionLin,u.inertialLin)),ue(r.rhsAng,r.lhsAng,ct(o,u.positionAng,u.inertialAng));let p=u.forces;for(let h=p.length-1;h>=0;h--)p[h].updatePrimal(u,this.alpha,r);let m=u.moment.every(h=>h===0);m&&(Ce(r.lhsAng,1,1,1),r.lhsCross.fill(0),r.rhsAng.fill(0)),Es(r.lhsLin,r.lhsAng,r.lhsCross,ee(r.rhsLin,r.rhsLin),ee(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),D(u.positionLin,u.positionLin,this.dxLin,1),m||si(u.positionAng,u.positionAng,this.dxAng)}for(let c of this.forces)c.updateDual(this.alpha)}for(let l of i)l.prevVelocityLin.set(l.velocityLin),l.mass>0&&!l.sleeping&&(_t(l.velocityLin,J(l.velocityLin,l.positionLin,l.initialLin),e),_t(l.velocityAng,ct(l.velocityAng,l.positionAng,l.initialAng),e));na(this,3,a),this.cpuStep++}},md=S(),cu=(s,e)=>ne(md,s,e);var Bs=(s,e)=>s.map((t,i)=>t+e[i]),Re=(s,e)=>s.map((t,i)=>t-e[i]),Lt=(s,e)=>s.map(t=>t*e),st=(s,e)=>s[0]*e[0]+s[1]*e[1],ws=(s,e)=>[Math.cos(s)*e[0]-Math.sin(s)*e[1],Math.sin(s)*e[0]+Math.cos(s)*e[1]],fu=s=>Math.max(0,Math.min(1,s)),uu=(s,e,t)=>Bs(s,Lt(Re(e,s),fu(st(Re(t,s),Re(e,s))/(st(Re(e,s),Re(e,s))||1))));function ca(s,e){let t=ei(s)?.get(e)??[0,0,0,0],i=Array.from(s.pose.subarray(e*4,e*4+2)),r=s.pose[e*4+2],o=l=>Bs(i,ws(r,l)),a=[s.shape[e*4]/2,s.shape[e*4+1]/2],n=t[0]===4?Array.from({length:t.vertices.length/4},(l,c)=>o(t.vertices.slice(c*4,c*4+2))):t[0]===0?[[-1,-1],[1,-1],[1,1],[-1,1]].map(l=>o([l[0]*a[0],l[1]*a[1]])):null;return{kind:t[0],p:i,angle:r,vertices:n,radius:t[1],spine:[o([-t[2]||0,0]),o([t[2]||0,0])]}}function Yi(s,e){if(s.vertices){let r=-1/0,o=[];for(let a of s.vertices){let n=st(a,e);n>r+1e-7?(r=n,o=[a]):Math.abs(n-r)<1e-7&&o.push(a)}return[o[0],o.at(-1)]}let t=Bs(s.spine[0],Lt(e,s.radius)),i=Bs(s.spine[1],Lt(e,s.radius));return Math.abs(st(t,e)-st(i,e))<1e-7?[t,i]:st(t,e)>st(i,e)?[t,t]:[i,i]}function du(s,e,t,i){let r=ca(s,e),o=ca(s,t),a=s.pose,n=s.shape;if(r.kind===0&&o.kind===0)return Lo(...a.subarray(e*4,e*4+3),n[e*4]/2,n[e*4+1]/2,...a.subarray(t*4,t*4+3),n[t*4]/2,n[t*4+1]/2,i);let l=[];if(r.kind===3||o.kind===3){let u=r.kind===3,f=u?r:o,d=u?o:r;if(d.kind===3)return 0;let p=ws(f.angle,[0,1]),m=Yi(d,Lt(p,-1));for(let h of m){let g=st(Re(h,f.p),p);if(g<=0){let b=Re(h,Lt(p,g));l.push({a:u?b:h,b:u?h:b,n:u?Lt(p,-1):p})}}}else{let u=-1/0,f=[0,1],d=x=>{let w=Math.hypot(...x);if(w<1e-9)return;let B=Lt(x,1/w),L=st(Re(Yi(r,Lt(B,-1))[0],Yi(o,B)[0]),B),M=st(Re(Yi(o,Lt(B,-1))[0],Yi(r,B)[0]),B);Math.max(L,M)>u+1e-7&&(u=Math.max(L,M),f=L>=M?B:Lt(B,-1))};for(let[x,w]of[[r,o],[o,r]])if(x.vertices)for(let B=0;B<x.vertices.length;B++){let L=Re(x.vertices[(B+1)%x.vertices.length],x.vertices[B]);d([L[1],-L[0]]),w.vertices||d(Re(x.vertices[B],uu(...w.spine,x.vertices[B])))}else if(d(ws(x.angle,[0,1])),!w.vertices)for(let B of x.spine)d(Re(B,uu(...w.spine,B)));if(u===-1/0&&d([0,1]),u>0)return 0;let p=Yi(r,Lt(f,-1)),m=Yi(o,f),h=[-f[1],f[0]],g=p.map(x=>st(x,h)),b=m.map(x=>st(x,h)),y=Math.max(Math.min(...g),Math.min(...b)),A=Math.min(Math.max(...g),Math.max(...b)),v=(x,w)=>{let B=st(Re(x[1],x[0]),h);return Math.abs(B)<1e-9?x[0]:Bs(x[0],Lt(Re(x[1],x[0]),fu((w-st(x[0],h))/B)))};for(let x of A-y>1e-6?[y,A]:[(y+A)/2])l.push({a:v(p,x),b:v(m,x),n:f})}let c=0;for(let u of l){if(c&&Math.hypot(...Re(u.a,l[0].a))<1e-7)continue;let f=ws(-r.angle,Re(u.a,r.p)),d=ws(-o.angle,Re(u.b,o.p));if(i.set([268435456+c,...f,...d,...u.n],c*7),c++,c===2)break}return c}var hu=(s,e,t)=>[Math.cos(s)*e-Math.sin(s)*t,Math.sin(s)*e+Math.cos(s)*t],pu=function(){let{pose:s,shape:e,props:t}=this,i=this.broadphase.pairs,r=this.contactOut;for(let o=0;o<this.broadphase.pairCount;o++){let a=i[o],n=Math.floor(a/2097152),l=a-n*2097152,c=this.collideBodies(n,l,r);if(c===0)continue;let u=0,f=this.prevCount;for(;u<f;){let m=u+f>>>1;this.prevKeys[m]<a?u=m+1:f=m}let d=u,p=Math.sqrt(t[n*4]*t[l*4]);for(let m=0;m<c;m++){let h=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(h+1)*j),this.data=this.grow(this.data,(h+1)*O);let g=this.data,b=h*O,y=m*7,A=r[y+0];this.info.set([hs,n,l,A],h*j),g.fill(0,b,b+O),g[b+Q]=g[b+Q+1]=1/0,g[b+_e]=-1/0,g[b+Ae]=0,g[b+Pt]=g[b+Pt+1]=1/0,g[b+N]=r[y+1],g[b+N+1]=r[y+1+1],g[b+Y]=r[y+3],g[b+Y+1]=r[y+3+1],g[b+re]=p,g[b+be]=r[y+5],g[b+Ue]=r[y+5+1];let v=-1;for(let P=d;P<this.prevCount&&this.prevKeys[P]===a;P++)this.prevInfo[P*j+3]===A&&(v=P);if(v<0&&this.params.matchNearest){let P=Sn*Math.min(e[n*4],e[n*4+1],e[l*4],e[l*4+1]);for(let C=d;C<this.prevCount&&this.prevKeys[C]===a;C++){let _=Math.hypot(this.prevData[C*O+N]-g[b+N],this.prevData[C*O+N+1]-g[b+N+1]);_<=P&&(P=_,v=C)}}if(v>=0){let P=v*O;g[b+se]=this.prevData[P+se],g[b+se+1]=this.prevData[P+se+1],g[b+te]=this.prevData[P+te],g[b+te+1]=this.prevData[P+te+1],this.prevData[P+Wi]&&(g[b+N]=this.prevData[P+N],g[b+N+1]=this.prevData[P+N+1],g[b+Y]=this.prevData[P+Y],g[b+Y+1]=this.prevData[P+Y+1])}let[x,w]=hu(s[n*4+2],g[b+N],g[b+N+1]),[B,L]=hu(s[l*4+2],g[b+Y],g[b+Y+1]),M=g[b+be],I=g[b+Ue],E=s[n*4]+x-s[l*4]-B,k=s[n*4+1]+w-s[l*4+1]-L;g[b+fe]=M*E+I*k+5e-4,g[b+fe+1]=I*E-M*k}}};var ua=class extends zr{constructor(e,t,i){if(super({precision:i==="soa-colored"?"f32":"f64",order:i==="soa-colored"?"colored":"sequential"}),Object.assign(this,e),this.options={...this.options,precision:i==="soa-colored"?"f32":"f64",order:i==="soa-colored"?"colored":"sequential"},this.options.precision==="f64")for(let[r,o]of Object.entries(this))o instanceof Float32Array&&(this[r]=Float64Array.from(o));for(let[r,o]of ei(e)??[]){let a=this.shape[r*4+2];Rr(this,r,{geometry:o,area:a,momentPerMass:a>0?this.shape[r*4+3]/a:0,radius:this.props[r*4+1]},1)}this.filters=new Map(t?.filters??[]),this.sensors=new Set(t?.sensors??[]),this.restitution=new Map(t?.restitution??[]),this.contactPairs=[],t&&(t.topology=this)}narrowphase(){this.contactPairs=[],pu.call(this)}collideBodies(e,t,i){let r=this.filters.get(e)??[4294967295,4294967295],o=this.filters.get(t)??[4294967295,4294967295];if(!(r[0]&o[1])||!(o[0]&r[1]))return 0;let a=du(this,e,t,i);if(a){let n=[i[5],i[6]],l=[0,0],c=[0,0];for(let p=0;p<a;p++)for(let m=0;m<2;m++)l[m]+=i[p*7+1+m]/a,c[m]+=i[p*7+3+m]/a;let u=this.pose[e*4+2],f=Math.cos(u),d=Math.sin(u);this.contactPairs.push({a:e,b:t,n,ra:l,rb:c,point:[this.pose[e*4]+f*l[0]-d*l[1],this.pose[e*4+1]+d*l[0]+f*l[1],0],impulse:0})}return this.sensors.has(e)||this.sensors.has(t)?0:a}initJoints(){for(let e=0;e<this.jointCount;e++){let t=e*O,i=this.info[e*j];if(this.data[t+Q]===0&&this.data[t+Q+1]===0&&this.data[t+Q+2]===0){i!==0&&(this.info[e*j]=0,this.noCollideDirty=!0,this.handles[e]&&(this.handles[e].alive=!1));continue}i===1&&this.jointC(this.info[e*j+1],this.info[e*j+2],t,this.pose,this.data,t+fe)}for(let e=0;e<this.jointCount;e++)if(this.info[e*j]===5){let t=e*O,i=this.info[e*j+1],r=this.info[e*j+2],o=(i<0?0:this.pose[i*4+2])-this.pose[r*4+2]-this.data[t+re];this.data[t+fe]=Math.min(0,o-this.data[t+be]),this.data[t+fe+1]=Math.max(0,o-this.data[t+Ue])}}warmStartConstraints(e,t){let i=[];for(let r=0;r<this.jointCount;r++)this.info[r*j]===5&&(i.push(r),this.info[r*j]=4);super.warmStartConstraints(e,t);for(let r of i)this.info[r*j]=5}evalConstraint(e,t,i){if(this.info[e*j]!==5)return super.evalConstraint(e,t,i);let r=e*O,o=this.info[e*j+1],a=this.info[e*j+2],n=this.data,l=(o<0?0:this.pose[o*4+2])-this.pose[a*4+2]-n[r+re];this.eC[0]=l-n[r+be]-t*n[r+fe],this.eC[1]=l-n[r+Ue]-t*n[r+fe+1],this.eG.fill(0),this.eJ.fill(0);for(let c=0;c<2;c++){this.eMin[c]=n[r+_e+c],this.eMax[c]=n[r+Ae+c];let u=Math.max(this.eMin[c],Math.min(this.eMax[c],n[r+se+c]*this.eC[c]+n[r+te+c]));i>=0&&u!==0&&(this.eJ[c*3+2]=i===o?1:-1)}return 2}step(){let e=aa(this,2);super.step(),na(this,2,e);let t=new Map(this.contactPairs.map(i=>[`${i.a},${i.b}`,i]));for(let i=this.jointCount;i<this.jointCount+this.contactCount;i++){let r=t.get(`${this.info[i*j+1]},${this.info[i*j+2]}`);r&&(r.impulse+=Math.abs(this.data[i*O+te])*this.params.dt)}}};var da=(s,e,t)=>{let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,F(T(t,s),i)/(F(i,i)||1)))))};function fa(s,e,t,i){let r=T(s,t),o=F(r,e),a=o*o-F(r,r)+i*i;if(a<0)return null;let n=-o-Math.sqrt(a);return n<0?null:{t:n,n:Tt(T(de(s,q(e,n)),t))}}function Dr(s,e){return e&&(!s||e.t<s.t)?e:s}function Dn(s,e,t,i,r){let o=T(i,t),a=T(s,t),n=F(o,o);if(n<1e-16)return fa(s,e,t,r);let l=F(o,e),c=F(o,a),u=F(a,e),f=n-l*l,d=n*u-c*l,p=n*F(a,a)-c*c-r*r*n,m=d*d-f*p,h=null;if(f>1e-12*n&&m>=0){let g=(-d-Math.sqrt(m))/f,b=c+g*l;g>=0&&b>=0&&b<=n&&(h={t:g,n:Tt(T(de(s,q(e,g)),de(t,q(o,b/n))))})}return Dr(Dr(h,fa(s,e,t,r)),fa(s,e,i,r))}function mu(s,e){let t=q(e.vertices.reduce((i,r)=>de(i,r),[0,0,0]),1/e.vertices.length);for(let i=0;i<e.vertices.length;i++){let r=e.vertices[i],o=e.vertices[(i+1)%e.vertices.length],a=xt(T(o,r),e.normal);if(F(a,T(s,r))*(F(a,T(t,r))>=0?1:-1)<-1e-6)return!1}return!0}function gd(s,e,t,i){if(i===0){let n=-1/0,l=1/0,c=q(t,-1);for(let u of s.faces){let f=F(u.normal,t),d=F(u.normal,T(u.vertices[0],e));if(Math.abs(f)<1e-12){if(d<0)return null}else{let p=d/f;f<0?p>n&&(n=p,c=u.normal):l=Math.min(l,p)}if(n>l)return null}return l<0?null:{t:Math.max(0,n),n:n<=0?q(t,-1):c}}let r=!0,o=1/0,a=null;for(let n of s.faces){let l=F(n.normal,T(e,n.vertices[0]));l>0&&(r=!1),mu(T(e,q(n.normal,l)),n)&&(o=Math.min(o,l*l));let c=F(n.normal,t);if(c<-1e-12){let u=(i-l)/c;u>=0&&mu(T(de(e,q(t,u)),q(n.normal,i)),n)&&(a=Dr(a,{t:u,n:n.normal}))}}for(let[n,l]of s.edges){let c=T(e,da(n,l,e));o=Math.min(o,F(c,c)),a=Dr(a,Dn(e,t,n,l,i))}return r||o<=i*i?{t:0,n:q(t,-1)}:a}var At=(s,e)=>s[0]*e[0]+s[1]*e[1];function bd(s,e,t,i){let r=!0,o=1/0,a=null;for(let n=0;n<s.vertices.length;n++){let l=s.vertices[n],c=s.vertices[(n+1)%s.vertices.length],u=T(c,l),f=Tt([u[1],-u[0]]),d=At(T(e,l),f);d>0&&(r=!1);let p=T(e,da(l,c,e));o=Math.min(o,At(p,p));let m=At(t,f);if(m<-1e-12){let h=(i-d)/m,g=T(de(e,q(t,h)),q(f,i)),b=At(T(g,l),u),y=1e-6*At(u,u);h>=0&&b>=-y&&b<=At(u,u)+y&&(a=Dr(a,{t:h,n:f}))}i>0&&(a=Dr(a,fa(e,t,l,i)))}return r||o<=i*i?{t:0,n:q(t,-1)}:a}function yd(s,e,t,i){if(s.vertices)return bd(s,e,t,i);if(s.kind===3){let p=[-Math.sin(s.angle),Math.cos(s.angle)],m=At(T(e,s.p),p)-i,h=At(t,p);return m<=0?{t:0,n:q(t,-1)}:h<-1e-12?{t:-m/h,n:p}:null}let r=s.kind===1?s.p:s.spine[0],o=s.kind===1?s.p:s.spine[1],a=i+s.radius,n=T(e,da(r,o,e));if(At(n,n)<=a*a)return{t:0,n:q(t,-1)};if(a>0)return Dn(e,t,r,o,a);let l=T(o,r),c=t[0]*l[1]-t[1]*l[0];if(Math.abs(c)<1e-12){let p=T(e,r);if(Math.abs(p[0]*l[1]-p[1]*l[0])/Math.max(Math.hypot(...l),1e-20)>1e-6)return null;let h=Math.min(At(T(r,e),t),At(T(o,e),t));return Math.max(At(T(r,e),t),At(T(o,e),t))<0?null:{t:Math.max(0,h),n:q(t,-1)}}let u=T(r,e),f=(u[0]*l[1]-u[1]*l[0])/c,d=(u[0]*t[1]-u[1]*t[0])/c;return f>=0&&d>=0&&d<=1?{t:f,n:q(Tt([l[1],-l[0]]),c>0?-1:1)}:null}function gu(s,e,t,i,{maxDistance:r=1e30,ignore:o=[],collidesWith:a=4294967295,includeTriggers:n=!0}={}){let l=s.dimension,c=me(e,l,"origin"),u=me(i,l,"direction"),f=Math.hypot(...u);if(!f||!Number.isFinite(t)||t<0||!Number.isFinite(r)||r<0)throw Error("Provide a nonzero direction, finite nonnegative radius and distance");if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let d=new Set(o);for(let h of d)s.liveIndex(h);let p=u.map(h=>h/f),m=null;for(let h=0;h<s.bodyCount;h++){if(d.has(h))continue;let g=l===3?s.solver.bodies[h]:null,b=g?g.group??4294967295:s.solver.filters.get(h)?.[0]??4294967295,y=g?g.isTrigger:s.solver.sensors.has(h);if(!(b&a)||!n&&y)continue;let A;if(l===2)A=yd(ca(s.solver,h),c,p,t);else{let x=jr(g);if(x.spine){let w=x.radius+t,B=T(c,da(...x.spine,c));A=F(B,B)<=w*w?{t:0,n:q(p,-1)}:Dn(c,p,...x.spine,w)}else A=gd(x,c,p,t)}if(!A||A.t>r||m&&A.t>=m.distance)continue;let v=de(c,q(p,A.t));m={index:h,distance:A.t,normal:A.n,center:v,point:T(v,q(A.n,t))}}return m}var ha=class{constructor(e,{speedThreshold:t=.08,timeThreshold:i=.6}={}){if(!Number.isFinite(t)||t<=0||!Number.isFinite(i)||i<=0)throw Error("Sleep thresholds must be positive and finite");this.world=e,this.speedThreshold=t,this.timeThreshold=i,this.time=new Float64Array(e.bodyCount),this.asleep=new Set}wake(){let e=this.world;for(let t of this.asleep)e.dimension===3?e.solver.bodies[t].sleeping=!1:e.solver.dynamic[t]=+(e.solver.shape[t*4+2]>0);this.asleep.clear(),this.time.fill(0)}after(){let e=this.world,t=e.solver,i=e.bodyCount,r=Int32Array.from({length:i},(d,p)=>p),o=new Set,a=[],n=d=>e.dimension===3?t.bodies[d].mass:t.shape[d*4+2],l=d=>{for(;r[d]!==d;)r[d]=r[r[d]],d=r[d];return d},c=(d,p)=>{if(d<0){o.add(p);return}n(d)<=0?o.add(p):n(p)<=0?o.add(d):r[l(d)]=l(p),a.push([d,p])};if(e.dimension===3){let d=new Map(t.bodies.map((p,m)=>[p,m]));for(let p of t.forces)!p.broken&&!p.bodyA?.isTrigger&&!p.bodyB?.isTrigger&&c(p.bodyA?d.get(p.bodyA):-1,d.get(p.bodyB))}else{for(let d of t.contactPairs)!t.sensors.has(d.a)&&!t.sensors.has(d.b)&&c(d.a,d.b);for(let d=0;d<t.jointCount;d++)(t.data[d*32+6]||t.data[d*32+7]||t.data[d*32+8])&&c(t.info[d*4+1],t.info[d*4+2])}let u=new Set([...o].map(l)),f=new Map;for(let d=0;d<i;d++)if(n(d)>0){let p=l(d),m=e.dimension===3?t.bodies[d]:null,h=m?m.allowSleep!==!1:t.avbdSleepEligible?.get(d)!==!1,g=m?Math.hypot(...m.velocityLin,...m.velocityAng):Math.hypot(...t.velocity.subarray(d*4,d*4+3));f.has(p)||f.set(p,u.has(p)),f.set(p,f.get(p)&&h&&g<=this.speedThreshold)}for(let d=0;d<i;d++)n(d)>0&&(this.time[d]=f.get(l(d))?this.time[d]+e.params.dt:0,this.time[d]>=this.timeThreshold&&(this.asleep.add(d),e.dimension===3?(t.bodies[d].sleeping=!0,t.bodies[d].velocityLin.fill(0),t.bodies[d].velocityAng.fill(0)):(t.dynamic[d]=0,t.velocity.fill(0,d*4,d*4+4))));a.some(([d,p])=>d>=0&&n(d)>0&&n(p)>0&&this.asleep.has(d)!==this.asleep.has(p))&&this.wake()}};var pa=class{constructor(e,t,i={}){if(this.backend="cpu",this.dimension=t,this.scene=e,this.stepCount=0,this.pending=new ti,this.events=[],this.previous=new Map,this.watched=null,t===3){this.solver=Object.assign(new la,e.ref),this.solver.up=[...e.up??[0,0,1]],this.nativeRef=e.ref,this.constraints=this.solver.forces.slice();for(let r of this.solver.bodies)r.solver=this.solver;for(let r of this.constraints)r.solver=this.solver;this.constraints.forEach((r,o)=>{let a=e.handles?.get(r);a&&(a.slot=o)}),oa(this.solver)}else this.solver=new ua(e.topology,e,i.order==="colored"?"soa-colored":"soa-seq"),this.topology=this.solver,this.builder=Object.assign(Object.create(Object.getPrototypeOf(e)),{topology:this.solver,filters:this.solver.filters,sensors:this.solver.sensors,restitution:this.solver.restitution});this.params=t===3?this.solver:this.solver.params,Tr(this,t===3?{bodies:this.solver.bodies}:null,t,{setVelocities:this.setPackedVelocities,readSelectedBodies:this.readSelectedBodies}),(i.sleeping||e.sleepOptIn||[...e.topology?.avbdSleepEligible?.values()??[]].some(Boolean))&&this.enableSleeping(i.sleeping===!0?{}:i.sleeping)}get bodyCount(){return this.dimension===3?this.solver.bodies.length:this.solver.bodyCount}get jointCount(){return this.dimension===3?this.constraints.length:this.solver.jointCount}gpuIndex(e){return this.liveIndex(e),e}liveIndex(e){if(this.assertLive(),!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live body index is required")}assertLive(){if(this.destroyed)throw Error("The solver has been destroyed")}prepareBodyCommands(){return this.wakeAll(),{enqueueValidated:e=>{this.pending.reserve(this.pending.count+e.count),this.pending.u32.set(e.u32.subarray(0,e.count*12),this.pending.count*12),this.pending.count+=e.count}}}setLinearVelocity(e,t){return this.editBodies([{body:e,linearVelocity:t}])}setAngularVelocity(e,t){return this.editBodies([{body:e,angularVelocity:t}])}applyImpulse(e,t,i){return this.editBodies([{body:e,impulse:t,worldPoint:i}])}applyForce(e,t,i){return this.editBodies([{body:e,force:t,worldPoint:i}])}applyAngularImpulse(e,t){return this.editBodies([{body:e,angularImpulse:t}])}applyTorque(e,t){return this.editBodies([{body:e,torque:t}])}teleport(e,t,i){return this.editBodies([{body:e,position:t,rotation:i}])}setPackedVelocities(e,t){let i=Array.from(e),r=t?.linear,o=t?.angular,a=this.dimension,n=a===3?3:1;if(r===void 0&&o===void 0)throw Error("Provide linear or angular velocities");let l=-1;i.forEach(d=>{if(this.liveIndex(d),d<=l)throw Error("Velocity batch indices must be unique and ascending GPU slots");l=d});for(let[d,p]of[[r,a],[o,n]])if(d!==void 0&&(d.length!==i.length*p||!Array.from(d).every(m=>Number.isFinite(Math.fround(m)))))throw Error("Provide finite packed velocity components per body");let c=r===void 0?null:Array.from(r),u=o===void 0?null:Array.from(o),f=i.map((d,p)=>({body:d,...c===null?{}:{linearVelocity:c.slice(p*a,(p+1)*a)},...u===null?{}:{angularVelocity:a===3?u.slice(p*n,(p+1)*n):u[p]}}));return this.editBodies(f),this.flushEdits(),this}flushEdits(){if(this.destroyed)throw Error("The solver has been destroyed");let{words:e,u32:t,count:i}=this.pending,r=this.solver,o=this.dimension;for(let a=0;a<i;a++){let n=a*12,l=t[n],c=t[n+1],u=t[n+2]!==0,f=o===3?r.bodies[l]:null,d=f?.velocityLin??r.velocity.subarray(l*4,l*4+2),p=f?.mass??r.shape[l*4+2],m=f?.positionLin??r.pose.subarray(l*4,l*4+2),h=g=>{if(!(p<=0))if(o===2)r.velocity[l*4+2]+=g[0]/Math.max(1e-10,r.shape[l*4+3]);else{let b=f.positionAng,y=Ee([-b[0],-b[1],-b[2],b[3]],g),A=Ee(b,y.map((v,x)=>v/Math.max(1e-10,f.moment[x])));for(let v=0;v<3;v++)f.velocityAng[v]+=A[v]}};if(c===0)d.set(e.subarray(n+4,n+4+o));else if(c===1)f?f.velocityAng.set(e.subarray(n+4,n+7)):r.velocity[l*4+2]=e[n+4];else if(c===4)if(m.set(e.subarray(n+4,n+4+o)),f){u&&f.positionAng.set(e.subarray(n+8,n+12)),f.initialLin.set(m),f.inertialLin.set(m),f.initialAng.set(f.positionAng),f.inertialAng.set(f.positionAng);for(let g of[...f.forces])g.contacts&&g.destroy()}else u&&(r.pose[l*4+2]=e[n+6]),r.initial.set(r.pose.subarray(l*4,l*4+4),l*4),r.inertial.set(r.pose.subarray(l*4,l*4+4),l*4),r.prevCount=0;else if(c===2||c===3){if(p<=0)continue;let g=Array.from(e.subarray(n+4,n+4+o),b=>b*(c===3?e[n+7]:1));for(let b=0;b<o;b++)d[b]+=g[b]/p;if(u){let b=Array.from(e.subarray(n+8,n+8+o),(y,A)=>y-m[A]);h(o===3?xt(b,g):[b[0]*g[1]-b[1]*g[0]])}}else(c===5||c===6)&&h(Array.from(e.subarray(n+4,n+4+(o===3?3:1)),g=>g*(c===6?e[n+7]:1)))}this.pending.count=0}step(){this.flushEdits(),this.solver.step(),this.sleeping?.after(),this.stepCount++,this.captureEvents()}attachToScene(e,t){return Xo.call(this,e,t)}detachFromScene(){return Yo.call(this)}setFilters(e,t,i){let r=Array.from(e),o=Array.from(t),a=Array.from(i);if(o.length!==r.length||a.length!==r.length)throw Error("Provide one group and mask per body");r.forEach(n=>this.liveIndex(n));for(let n of[...o,...a])if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");r.forEach((n,l)=>{this.dimension===3?Object.assign(this.solver.bodies[n],{group:o[l],collidesWith:a[l]}):this.solver.filters.set(n,[o[l],a[l]])}),this.wakeAll()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Trigger flag must be boolean");this.dimension===3?this.solver.bodies[e].isTrigger=t:t?this.solver.sensors.add(e):this.solver.sensors.delete(e),this.wakeAll()}setRestitution(e,t){if(this.liveIndex(e),!Number.isFinite(t)||t<0||t>1)throw Error("restitution must be between 0 and 1");this.dimension===3?this.solver.bodies[e].restitution=t:this.solver.restitution.set(e,t),this.wakeAll()}setMotor(e,t){if(this.assertLive(),this.dimension===3){let i=this.constraints[e];if(!i?.cpuAngular)throw Error("A motor constraint is required");Object.assign(i.cpuAngular.options,kt({...i.cpuAngular.options,...t}))}else{let i=this.solver,r=e*O;if(!Number.isInteger(e)||e<0||e>=i.jointCount||i.info[e*4]!==3)throw Error("A motor constraint is required");let o=t.speed??i.data[r+re],a=t.maxTorque??i.data[r+Ae];if(!Number.isFinite(o)||!Number.isFinite(a)||a<0)throw Error("Provide finite motor speed and nonnegative torque");i.data[r+re]=o,i.data[r+_e]=-a,i.data[r+Ae]=a}this.wakeAll()}setAngleLimits(e,t){if(this.assertLive(),this.dimension!==2)throw Error("Use a 3D hinge constraint");if(this.solver.info[e*4]!==5)throw Error("An angular limit is required");vs(this.solver,e,ki(t)),this.wakeAll()}releaseJoints(e){for(let t of e)this.disableConstraint(t)}disableConstraint(e){if(this.assertLive(),!Number.isInteger(e)||e<0||e>=this.jointCount)throw Error("A live constraint slot is required");if(this.dimension===3){let t=this.constraints[e];t.broken=!0,(t.cpuAngular??t).destroy()}else this.solver.disable(e),this.solver.info[e*4]=0,this.solver.noCollideDirty=!0;this.wakeAll()}setWorldAnchor(e,t,i){if(this.assertLive(),this.dimension===2){let r=this.solver;if(!Number.isInteger(e)||e<0||e>=r.jointCount||r.info[e*4+1]!==-1)throw Error("A world-anchor constraint is required");r.data.set(me([t,i],2,"point"),e*O+21)}else{let r=this.constraints[e];if(!r||r.bodyA)throw Error("A world-anchor constraint is required");r.rA.set(me(t,3,"point"))}this.wakeAll()}addBox(...e){return this.addDynamic("addBox",e)}addCircle(...e){return this.addDynamic("addCircle",e)}addCapsule(...e){return this.addDynamic("addCapsule",e)}addHull(...e){return this.addDynamic("addHull",e)}addSegment(...e){return this.addDynamic("addSegment",e)}addPlane(...e){return this.addDynamic("addPlane",e)}addDynamic(e,t){if(this.destroyed)throw Error("The solver has been destroyed");if(this.dimension!==2)throw Error("Build 3D bodies before creating the solver");this.wakeAll();let i=this.sleeping?{speedThreshold:this.sleeping.speedThreshold,timeThreshold:this.sleeping.timeThreshold}:null,r=this.builder[e](...t);return i&&this.enableSleeping(i),r}appendJoint(e,t,i,r,o,a=1/0){if(this.assertLive(),this.dimension!==2)throw Error("Build 3D constraints before creating the solver");return this.wakeAll(),this.builder.addJoint(e,t,i,r,{stiffness:o,breakForce:a}).slot}captureConstraintFrames(e){if(this.assertLive(),this.dimension!==3)throw Error("Constraint frame capture requires a 3D world");let t=Array.from(e);for(let{slot:i,spring:r}of t){let o=this.constraints[i];if(!Number.isInteger(i)||!o||o.broken||o.cpuAngular||(r?o.rest===void 0:o.stiffnessAng===void 0))throw Error("A live spring or joint slot is required")}this.flushEdits();for(let{slot:i,spring:r}of t)jn(this.constraints[i],r);t.length&&this.wakeAll()}setSpringMaterial(e,t={}){this.assertLive();let i=this.constraints[e];if(!i||i.rest===void 0)throw Error("A spring constraint is required");vr(i,It(t)),this.wakeAll()}packBodyRecords(e,t=!1){let i=this.solver,r=this.dimension,o=t?r===3?8:4:r===3?40:24,a=e?.length??this.bodyCount,n=new Float32Array(a*o);for(let l=0;l<a;l++){let c=e?.[l]??l,u=l*o;if(t){if(r===3){let f=i.bodies[c];n.set(f.positionLin,u),n[u+3]=f.friction,n.set(f.positionAng,u+4)}else n.set(i.pose.subarray(c*4,c*4+4),u),n[u+3]=i.props[c*4];continue}if(r===3){let f=i.bodies[c];n.set(f.positionLin,u),n[u+3]=f.friction,n.set(f.positionAng,u+4),n.set(f.initialLin,u+8),n[u+11]=+!!f.isTrigger,n.set(f.initialAng,u+12),n.set(f.size,u+16),n[u+19]=f.sleeping?0:f.mass,n.set(f.moment,u+20),n[u+23]=f.radius,n.set(f.inertialLin,u+24),n.set(f.inertialAng,u+28),n.set(f.velocityLin,u+32),n[u+35]=f.prevVelocityLin.reduce((d,p,m)=>d+p*i.up[m],0),n.set(f.velocityAng,u+36),n[u+39]=De(f)?1:Fe(f)?-1:Ct(f)?3:or(f)?2:0}else n.set(i.pose.subarray(c*4,c*4+4),u),n[u+3]=i.props[c*4],n.set(i.initial.subarray(c*4,c*4+4),u+4),n.set(i.inertial.subarray(c*4,c*4+4),u+8),n.set(i.velocity.subarray(c*4,c*4+4),u+12),n.set(i.prevVelocity.subarray(c*4,c*4+4),u+16),n.set(i.shape.subarray(c*4,c*4+4),u+20)}return n}async readBodies(){return this.flushEdits(),this.packBodyRecords()}async readSelectedBodies(e,{posesOnly:t=!1}={}){let i=Array.from(e);return i.forEach(r=>this.liveIndex(r)),this.flushEdits(),this.packBodyRecords(i,t)}async readBodyState(e){this.liveIndex(e),this.flushEdits();let t=this.solver;if(this.dimension===3){let i=t.bodies[e],r=this.sleeping?.asleep.has(e)??!1;return{position:Array.from(i.positionLin),rotation:Array.from(i.positionAng),linearVelocity:Array.from(i.velocityLin),angularVelocity:Array.from(i.velocityAng),mass:i.mass,effectiveMass:r?0:i.mass,sleeping:r}}return{position:Array.from(t.pose.subarray(e*4,e*4+2)),angle:t.pose[e*4+2],linearVelocity:Array.from(t.velocity.subarray(e*4,e*4+2)),angularVelocity:t.velocity[e*4+2],mass:t.shape[e*4+2],moment:t.shape[e*4+3],sleeping:this.sleeping?.asleep.has(e)??!1}}async readJoints(){return this.flushEdits(),this.packJointRecords()}packJointRecords(){let e=this.dimension===3?32:36,t=new Float32Array(this.jointCount*e),i=r=>r===1/0?3e38:r===-1/0?-3e38:r;if(this.dimension===2){let r=this.solver.data;for(let o=0;o<this.jointCount;o++){let a=o*36,n=o*O;[se,te,Q,_e,Ae,Pt,fe].forEach((l,c)=>{for(let u=0;u<3;u++)t[a+c*4+u]=i(r[n+l+u])}),t.set([r[n+N],r[n+N+1],r[n+Y],r[n+Y+1]],a+28),t.set([r[n+re],r[n+re+1],r[n+re+2],0],a+32)}}else this.constraints.forEach((r,o)=>{let a=o*32;if(r.cpuAngular){let n=r.cpuAngular,l=n.options;t.set(n.pen,a),t[a+3]=r.broken?0:1,t.set(n.lam,a+8),t.set(n.ta,a+16),t.set(n.tb,a+20),t.set(l.axisA,a+24),t.set(l.axisB,a+28),t[a+19]=n.c0?.[0]??0,t[a+23]=n.c0?.[1]??0,t[a+27]=l.type==="motor"?l.speed:l.minAngle,t[a+31]=l.type==="motor"?l.maxTorque:l.maxAngle;return}for(let[n,l]of[["penaltyLin",0],["penaltyAng",4],["lambdaLin",8],["lambdaAng",12],["C0Lin",16],["C0Ang",20],["rA",24],["rB",28]])r[n]&&t.set(r[n],a+l);if(t[a+3]=r.broken?0:i(r.stiffnessLin??r.stiffness??0),t[a+7]=r.broken?0:i(r.stiffnessAng??0),t[a+11]=i(r.fracture??0),t[a+15]=r.torqueArm??0,t[a+27]=r.rest??0,r.cpuRest&&(t[a+19]=r.cpuRest[0],t[a+23]=r.cpuRest[1],t[a+27]=r.cpuRest[2],t[a+31]=r.cpuRest[3]),r.rest!==void 0){let n=Ui(r);t[a+11]=Number.isFinite(n?.breakStrain)?n.breakStrain:0,t[a+15]=Number.isFinite(n?.breakForce)?n.breakForce:0}});return t}async readCounters(){this.assertLive();let e=this.dimension===2?this.solver.contactCount:this.solver.forces.reduce((i,r)=>i+(r.contacts?.length??0),0),t=this.dimension===2?this.solver.contactPairs.length:this.solver.forces.filter(i=>i.contacts?.length).length;return{pairs:t,contacts:e,manifolds:t,joints:this.jointCount,overflow:0,clashes:0,colors:0,sleeping:this.sleeping?.asleep.size??0}}adapt(){}watchContacts({indices:e,maxPairs:t=this.maxPairs??8192,maxEvents:i=this.maxEvents??4096}={}){if(this.assertLive(),!Number.isInteger(i)||i<1||!Number.isInteger(t)||t<1)throw Error("Contact capacities must be positive integers");return e!==void 0?(e.forEach(r=>this.liveIndex(r)),this.watched=new Set(e)):this.watched??=new Set(Array.from({length:this.bodyCount},(r,o)=>o)),this.maxEvents=i,this.maxPairs=t,this}captureEvents(){if(!this.watched)return;let e=this.dimension===3?new Map(this.solver.bodies.map((i,r)=>[i,r])):null,t=this.dimension===2?this.solver.contactPairs:this.solver.forces.filter(i=>i.contacts?.length).map(i=>{let r=Array.from(i.bodyA.positionLin);for(let o of i.contacts){let a=Ee(i.bodyA.positionAng,o.rA);for(let n=0;n<3;n++)r[n]+=a[n]/i.contacts.length}return{a:e.get(i.bodyA),b:e.get(i.bodyB),point:r,normal:Array.from(i.basis.slice(0,3)),impulse:i.bodyA.isTrigger||i.bodyB.isTrigger?0:i.contacts.reduce((o,a)=>o+Math.abs(a.lambda[0])*this.params.dt,0)}});this.trackContactPairs(t)}trackContactPairs(e){let t=new Map,i=(a,n)=>{this.events.length<(this.maxEvents??4096)?this.events.push({...a,normal:a.normal??[...a.n,0],type:n,kind:n==="begin"?1:2,step:this.stepCount,isTrigger:a.isTrigger??(this.dimension===3?!!(this.solver.bodies[a.a].isTrigger||this.solver.bodies[a.b].isTrigger):this.solver.sensors.has(a.a)||this.solver.sensors.has(a.b))}):this.dropped=(this.dropped??0)+1},r=new Set,o=[];for(let a of e){if(!this.watched.has(a.a)&&!this.watched.has(a.b))continue;let n=`${Math.max(a.a,a.b)},${Math.min(a.a,a.b)}`;r.add(n),this.previous.has(n)&&t.size<this.maxPairs?t.set(n,a):o.push([n,a])}for(let[a,n]of o)if(!t.has(a)){if(t.size>=this.maxPairs){this.pairFull=!0;continue}t.set(a,n),this.previous.has(a)||i(n,"begin")}for(let[a,n]of this.previous)r.has(a)||i({...n,impulse:0},"end");this.previous=t}async readContactEvents(){this.watchContacts();let e={events:this.events,dropped:this.dropped??0,full:!!this.dropped||!!this.pairFull};return this.events=[],this.dropped=0,this.pairFull=!1,e}async raycast(e,t,i){return this.sphereCast(e,0,t,i)}async circleCast(e,t,i,r){return this.sphereCast(e,t,i,r)}async sphereCast(e,t,i,r){return this.flushEdits(),gu(this,e,t,i,r)}async raycastAll(e,t){return Promise.all(e.map(i=>this.raycast(i.origin,i.direction,{...t,maxDistance:i.maxDistance??t?.maxDistance})))}async sphereCastAll(e,t){return Promise.all(e.map(i=>this.sphereCast(i.origin,i.radius,i.direction,{...t,maxDistance:i.maxDistance??t?.maxDistance})))}async circleCastAll(e,t){return this.sphereCastAll(e,t)}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");return this.dimension===3?this.solver.bodies[e].allowSleep=t:(this.solver.avbdSleepEligible??=new Map,this.solver.avbdSleepEligible.set(e,t)),t&&!this.sleeping&&this.enableSleeping(),this.wakeAll(),this}enableSleeping(e={}){return this.assertLive(),this.disableSleeping(),this.sleeping=new ha(this,e),this}disableSleeping(){return this.sleeping?.wake(),this.sleeping=null,this}async readSleepStats(){return this.assertLive(),{sleeping:this.sleeping?.asleep.size??0,wakeRequested:!1}}wakeUp(e){return this.liveIndex(e),this.wakeAll()}wakeAll(){return this.sleeping?.wake(),this}destroy(){this.destroyed||(this.detachFromScene(),this.disableSleeping(),this.pending.count=0,this.events=[],this.previous.clear(),this.destroyed=!0)}};var bu=new WeakSet,ma=s=>s!==null&&typeof s=="object"&&bu.has(s),yu=s=>bu.add(s);async function vd({backend:s="webgpu",device:e,gl:t,validate3D:i=!0,...r}={}){if(!["webgpu","cpu"].includes(s))throw Error(`Unsupported solver backend: ${s}`);if(s!=="webgpu"&&e!==void 0)throw Error("Only WebGPU initialization accepts a GPU device");if(t!==void 0)throw Error("Solver pipelines do not accept a WebGL context");let o,a=!1,n=!1;s==="webgpu"&&(e?i&&await Cr(e):({device:e,adapter:o}=await ss({...r,preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024,...r.preferredLimits},validate3D:i}),a=!0));let l=new Set,c={backend:s,device:e,adapter:o,createSolver(u,f={}){if(n)throw Error("The solver pipeline has been destroyed");if(!u||typeof u.assertEditable!="function"||!u.ref&&!u.topology)throw Error("An AVBD scene builder is required");u.assertEditable();let d;if(s==="webgpu")d=u.createSolver(e,f);else{let m=u.ref?3:u.topology?2:0;if(f.bodyBuffer!==void 0)throw Error("A GPU body buffer requires the WebGPU backend");d=new pa(u,m,f),u.gpu=d,u.created=!0}Object.defineProperty(d,"backend",{value:s,configurable:!0}),l.add(d);let p=d.destroy;return d.destroy=function(){p.call(this),l.delete(this)},d},destroy(){if(!n){for(let u of l)u.destroy();l.clear(),a&&e.destroy(),n=!0}}};return yu(c),c}var ie=(s,e,t)=>{let i=Array.from(s);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i},K=(s,e,t=!1)=>{if(!Number.isFinite(s)||(t?s<0:s<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return s},vu=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s};function ga(s){for(let t of["allowSleep","isTrigger"])if(s[t]!==void 0&&typeof s[t]!="boolean")throw Error(`${t} must be a boolean`);let e=s.restitution??0;if(!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");if(s.mass!==void 0&&(K(s.mass,"mass",!0),s.mass>0&&s.density===0))throw Error("A positive mass needs positive density");for(let t of["velocity","angularVelocity"])s[t]!==void 0&&ie(s[t],3,t);if(s.rotation!==void 0&&!Math.hypot(...ie(s.rotation,4,"rotation")))throw Error("rotation must be nonzero");vu(s.group??4294967295),vu(s.collidesWith??4294967295)}function xu(s,e){if(e.timeStep!==void 0&&(s.dt=K(e.timeStep,"timeStep")),e.iterations!==void 0){if(K(e.iterations,"iterations"),!Number.isInteger(e.iterations))throw Error("iterations must be an integer");s.iterations=e.iterations}if(e.gravity!==void 0){if(!Number.isFinite(e.gravity))throw Error("gravity must be finite");s.gravity=e.gravity}}var Gn=class{constructor(e={}){if(this.ref=new ni,xu(this.ref,e),this.up=ie(e.up??[0,1,0],3,"up"),Math.abs(Math.hypot(...this.up)-1)>1e-5)throw Error("up must be a unit vector");this.points=!1,this.handles=new Map,this.bodySet=new Set}get bodies(){return this.ref.bodies}get constraints(){return[...this.handles.values()]}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use the solver's GPU edit methods afterwards")}addBox(e,t={}){this.assertEditable(),ga(t);let i=ie(e,3,"size");i.forEach(o=>K(o,"size"));let r=new Ze(this.ref,i,K(t.density??1,"density",!0),K(t.friction??.6,"friction",!0),ie(t.position??[0,0,0],3,"position"));return this.configure(r,t)}addSphere(e,t={}){return this.assertEditable(),ga(t),this.configure(ar(this.ref,K(e,"radius"),K(t.density??1,"density",!0),K(t.friction??.6,"friction",!0),ie(t.position??[0,0,0],3,"position")),t)}addHull(e,t={}){if(this.assertEditable(),ga(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=Ur(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(nr(this.ref,i,K(t.density??1,"density",!0),K(t.friction??.6,"friction",!0),ie(t.position??[0,0,0],3,"position")),t)}addCapsule(e,t,i={}){return this.assertEditable(),ga(i),this.configure(Br(this.ref,e,t,K(i.density??1,"density",!0),K(i.friction??.6,"friction",!0),ie(i.position??[0,0,0],3,"position")),i)}addHeightfield(e){return this.addTerrain(ls(e),e)}addStaticMesh(e){return this.addTerrain(cs(e),e)}addTerrain(e,t={}){this.assertEditable();let i=this.ref.bodies.length,r=go(this.ref,e,t);r.forEach(a=>this.bodySet.add(a)),this.hulls=!0;let o=!1;return{bodies:r,triangleCount:e.triangleCount,kind:e.kind,dispose:()=>{if(!o&&(o=!0,r.forEach(a=>{a.group=0,a.collidesWith=0}),this.gpu&&!this.gpu.destroyed)){let a=r.map((n,l)=>this.gpu.gpuIndex(i+l));this.gpu.setFilters(a,a.map(()=>0),a.map(()=>0)),this.gpu.wakeAll?.()}}}}configure(e,t){if(t.allowSleep!==void 0&&typeof t.allowSleep!="boolean")throw Error("allowSleep must be a boolean");e.allowSleep=t.allowSleep??!0,this.sleepOptIn||=t.allowSleep===!0;let i=t.restitution??0;if(!Number.isFinite(i)||i<0||i>1)throw Error("restitution must be between 0 and 1");if(e.restitution=i,t.isTrigger!==void 0&&typeof t.isTrigger!="boolean")throw Error("isTrigger must be a boolean");if(e.isTrigger=t.isTrigger??!1,e.group=t.group??4294967295,e.collidesWith=t.collidesWith??4294967295,t.mass!==void 0){let r=K(t.mass,"mass",!0);if(e.mass===0&&r>0)throw Error("A positive mass needs positive density");let o=e.mass>0?r/e.mass:0;e.mass=r,e.moment.forEach((a,n)=>e.moment[n]=a*o)}if(t.rotation){let r=ie(t.rotation,4,"rotation"),o=Math.hypot(...r);if(!o)throw Error("rotation must be nonzero");e.positionAng.set(r.map(a=>a/o))}return t.velocity&&e.velocityLin.set(ie(t.velocity,3,"velocity")),t.angularVelocity&&e.velocityAng.set(ie(t.angularVelocity,3,"angularVelocity")),this.bodySet.add(e),e}addPoint(e,{mass:t=.02,radius:i=.05,friction:r=.5}={}){let o=this.addSphere(i,{position:e,mass:t,friction:r});return o.moment.fill(0),this.points=!0,o}addFabric({columns:e=16,rows:t=16,spacing:i=.2,origin:r=[0,3,0],mass:o=.02,stiffness:a=1e3,anchorStiffness:n=a,materialAt:l,breakStrain:c=1/0,breakForce:u=1/0,bendingStiffness:f=Number.isFinite(c)||Number.isFinite(u)?0:a/50,pinCorners:d=!1,pinEdges:p=!d}={}){for(let[E,k]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(k)||k<2)throw Error(`${E} must be an integer >= 2`);if(K(i,"spacing"),K(o,"mass"),K(a,"stiffness"),K(n,"anchorStiffness"),l!==void 0&&typeof l!="function")throw Error("materialAt must be a function");if(K(f,"bendingStiffness",!0),typeof d!="boolean"||typeof p!="boolean")throw Error("Fabric pin options must be boolean");let m=It({breakStrain:c,breakForce:u}),h=ie(r,3,"origin"),g=(E,k)=>`${Math.min(E,k)},${Math.max(E,k)}`;function*b(){for(let E=0;E<t;E++)for(let k=0;k<e;k++){let P=E*e+k;k&&(yield[P,P-1,a]),E&&(yield[P,P-e,a]),k&&E&&(yield[P,P-e-1,a/2],yield[P-1,P-e,a/2]),k>1&&f&&(yield[P,P-2,f]),E>1&&f&&(yield[P,P-2*e,f])}}let y=new Map;if(l)for(let[E,k]of b()){let P=l((E%e+k%e)/(2*(e-1)),(Math.floor(E/e)+Math.floor(k/e))/(2*(t-1)))??{};if(typeof P!="object"||Array.isArray(P))throw Error("materialAt must return a material object");y.set(g(E,k),{scale:K(P.stiffnessScale??1,"materialAt.stiffnessScale"),material:It({...m,...P})})}let A=Array.from({length:t},(E,k)=>Array.from({length:e},(P,C)=>this.addPoint([h[0]+(C-(e-1)/2)*i,h[1],h[2]+(k-(t-1)/2)*i],{mass:p&&(C===0||k===0||C===e-1||k===t-1)||d&&(C===0||C===e-1)&&(k===0||k===t-1)?0:o,radius:i/Math.SQRT2}))),v=A.flat(),x=new Map(v.map((E,k)=>[E,k])),w=[],B=new Map,L=(E,k,P)=>{(E.mass===0||k.mass===0)&&(P*=n/a);let C=x.get(E),_=x.get(k),z=y.get(g(C,_)),R=this.addSpring(E,k,[0,0,0],[0,0,0],{stiffness:P*(z?.scale??1),...z?.material??m});return B.set(g(x.get(E),x.get(k)),w.length),w.push(R),R};for(let[E,k,P]of b())L(v[E],v[k],P);let M=[];for(let E=0;E<t;E++)for(let k=0;k<e;k++)if(k&&E){let P=(E-1)*e+k-1,C=P+1,_=E*e+k-1,z=_+1;M.push(P,_,C,C,_,z)}let I=[];for(let E=0;E<M.length;E+=3){let[k,P,C]=M.slice(E,E+3);I.push(B.get(g(k,P)),B.get(g(P,C)),B.get(g(C,k)))}return{points:v,grid:A,indices:Uint32Array.from(M),connections:w,triangleConnections:Uint32Array.from(I)}}addRope({segments:e=12,length:t=12,origin:i=[0,12,0],width:r=.15,endMass:o=20}={}){if(!Number.isInteger(e)||e<1)throw Error("segments must be a positive integer");K(t,"length"),K(r,"width"),K(o,"endMass",!0);let a=ie(i,3,"origin"),n=t/e,l=this.addBox([r,r,r],{mass:0,position:a}),c=[];for(let u=0;u<e;u++){let f=this.addBox([r,n,r],{mass:1,position:[a[0],a[1]-(u+.5)*n,a[2]]});this.addJoint(u?c[u-1]:l,f,[0,u?-n/2:0,0],[0,n/2,0]),c.push(f)}if(o){let u=this.addBox([.6,.6,.6],{mass:o,position:[a[0],a[1]-t-.3,a[2]]});this.addJoint(c.at(-1),u,[0,-n/2,0],[0,.3,0]),c.push(u)}return{anchor:l,links:c}}addRagdoll({origin:e=[0,2,0],mass:t=10}={}){let i=ie(e,3,"origin");K(t,"mass");let r=(l,c)=>this.addBox(l,{mass:t/10,position:c.map((u,f)=>u+i[f])}),o=r([.7,1.2,.35],[0,2,0]),a=this.addSphere(.25,{mass:t/10,position:[i[0],i[1]+2.9,i[2]]}),n=[o,a];this.addJoint(o,a,[0,.6,0],[0,-.3,0]);for(let l of[-1,1]){let c=r([.3,.8,.3],[l*.5,1.95,0]),u=r([.26,.7,.26],[l*.5,1.2,0]);this.addJoint(o,c,[l*.35,.35,0],[-l*.15,.4,0]),this.addJoint(c,u,[0,-.4,0],[0,.35,0]);let f=r([.28,.8,.3],[l*.2,1,0]),d=r([.26,.75,.28],[l*.2,.225,0]);this.addJoint(o,f,[l*.2,-.6,0],[0,.4,0]),this.addJoint(f,d,[0,-.4,0],[0,.375,0]),n.push(c,u,f,d)}return{torso:o,head:a,bodies:n}}addJoint(e,t,i=[0,0,0],r=[0,0,0],{linearStiffness:o=1/0,angularStiffness:a=0,breakForce:n=1/0,breakOnPull:l=!1}={}){this.assertEditable(),this.checkBodies(e,t);for(let c of[o,a,n])c!==1/0&&K(c,"joint stiffness/breakForce",!0);return this.handle(new ke(this.ref,e,t,ie(i,3,"anchorA"),ie(r,3,"anchorB"),o,a,l&&n!==1/0?-n:n))}addSpring(e,t,i=[0,0,0],r=[0,0,0],{stiffness:o=1e3,rest:a=-1,breakStrain:n=1/0,breakForce:l=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!e)throw Error("A spring requires two bodies");K(o,"stiffness",!0),a!==-1&&K(a,"rest",!0);let c=It({breakStrain:n,breakForce:l}),u=new ft(this.ref,e,t,ie(i,3,"anchorA"),ie(r,3,"anchorB"),o,a);return vr(u,c),this.handle(u)}checkBodies(e,t){if(!t||e===t||!this.bodySet.has(t)||e&&!this.bodySet.has(e))throw Error("Constraint endpoints must be distinct bodies in this scene")}addMotor(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=kt({...i,type:"motor"}),o=new ke(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);return wr(o,r),this.handle(o)}addHinge(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=kt({...i,type:"limit"}),o=K(i.span??1,"span"),a=ie(i.anchorA??[0,0,0],3,"anchorA"),n=ie(i.anchorB??[0,0,0],3,"anchorB"),l=[-1,1].map(f=>this.addJoint(e,t,a.map((d,p)=>d+f*o*.5*r.axisA[p]),n.map((d,p)=>d+f*o*.5*r.axisB[p]))),c,u;if(i.minAngle!==void 0||i.maxAngle!==void 0){let f=new ke(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);wr(f,r),c=this.handle(f)}return i.motor&&(u=this.addMotor(e,t,{axisA:r.axisA,axisB:r.axisB,...i.motor})),{joints:l,limit:c,motor:u,dispose(){for(let f of[...l,c,u])f?.dispose()}}}handle(e){let t={slot:-1,disposed:!1,dispose:()=>{t.disposed||(this.gpu?this.gpu.destroyed||this.gpu.releaseJoints([t.slot]):e.destroy(),this.handles.delete(e),t.disposed=!0)}};return t.destroy=t.dispose,this.handles.set(e,t),t}createSolver(e,t={}){if(ma(e))return e.createSolver(this,t);if(this.assertEditable(),this.hulls&&e.limits.maxStorageBuffersPerShaderStage<9)throw Error("Hull collisions require nine storage buffer bindings");let i=new Ko(e,this.ref,ho(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:Yc}}:{}})),r=[];this.ref.forces.forEach((l,c)=>{this.handles.get(l).slot=c,l instanceof ke&&l.stiffnessAng>0&&r.push({slot:c})}),i.captureConstraintFrames(r),i.params.up=[...this.up];let o=[],a=[],n=[];for(let[l,c]of this.ref.bodies.entries())(c.group!==4294967295||c.collidesWith!==4294967295)&&(o.push(i.gpuIndex(l)),a.push(c.group),n.push(c.collidesWith)),c.isTrigger&&i.setSensor(i.gpuIndex(l),!0),c.restitution>0&&i.setRestitution(i.gpuIndex(l),c.restitution);return o.length&&i.setFilters(o,a,n),(t.sleeping||this.sleepOptIn)&&i.enableSleeping(this.ref,t.sleeping===!0?{}:t.sleeping),this.gpu=i,this.created=!0,i}},Fn=class{constructor(e={}){if(this.topology=new zr,Object.assign(this.topology.params,So()),xu(this.topology.params,e),e.alpha!==void 0){if(!Number.isFinite(e.alpha)||e.alpha<0||e.alpha>1)throw Error("alpha must be between zero and one");this.topology.params.alpha=e.alpha}for(let t of["postStabilize","matchNearest"])if(e[t]!==void 0){if(typeof e[t]!="boolean")throw Error(`${t} must be boolean`);this.topology.params[t]=e[t]}}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use GPU edit methods afterwards")}get bodyCount(){return this.topology.bodyCount}addShape(e,t={}){let i=e.options??t,r=this.addBox(e.size,i);return Rr(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(No(e),t)}addHull(e,t={}){return this.addShape(Uo(e),t)}addCapsule(e,t,i={}){return this.addShape(qo(e,t),i)}addSegment(e,t,i={}){return this.addShape(Vo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Wo(e,t,i))}addBox(e,{density:t=1,friction:i=.6,position:r=[0,0],angle:o=0,velocity:a=[0,0,0],restitution:n=0,isTrigger:l=!1,group:c=4294967295,collidesWith:u=4294967295,allowSleep:f}={}){if(this.assertEditable(),f!==void 0&&typeof f!="boolean")throw Error("allowSleep must be boolean");let d=ie(e,2,"size");d.forEach(h=>K(h,"size"));let p=ie(r,2,"position");if(!Number.isFinite(o))throw Error("angle must be finite");if(!Number.isFinite(n)||n<0||n>1)throw Error("restitution must be between 0 and 1");if(typeof l!="boolean")throw Error("isTrigger must be boolean");for(let h of[c,u])if(!Number.isInteger(h)||h<0||h>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let m=this.topology.addBody(d,K(t,"density",!0),K(i,"friction",!0),[...p,o],ie(a,3,"velocity"));return this.restitution??=new Map,n>0&&this.restitution.set(m,n),this.sensors??=new Set,l&&this.sensors.add(m),this.filters??=new Map,(c!==4294967295||u!==4294967295)&&this.filters.set(m,[c,u]),this.topology.avbdSleepEligible??=new Map,f!==void 0&&this.topology.avbdSleepEligible.set(m,f),m}checkBodies(e,t){if(!Number.isInteger(e)||!Number.isInteger(t)||e<-1||e>=this.bodyCount||t<0||t>=this.bodyCount||e===t)throw Error("Invalid 2D constraint endpoints")}addJoint(e,t,i=[0,0],r=[0,0],{stiffness:o=[1/0,1/0,0],breakForce:a=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!o||o.length!==3||Array.from(o).some(l=>l!==1/0&&(!Number.isFinite(l)||l<0))||a!==1/0&&(!Number.isFinite(a)||a<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");let n=this.topology.addJoint(e,t,ie(i,2,"anchorA"),ie(r,2,"anchorB"),o,a);return this.constraintHandle(n.slot,n)}constraintHandle(e,t={}){return Object.assign(t,{slot:e,alive:!0,disposed:!1}),t.dispose=()=>{t.disposed||(this.gpu&&!this.gpu.destroyed?this.gpu.disableConstraint(e):this.gpu||this.topology.data.fill(0,e*O+Q,e*O+Q+3),t.alive=!1,t.disposed=!0)},t.destroy=t.dispose,this.topology.handles[e]=t,t}addSpring(e,t,i=[0,0],r=[0,0],{stiffness:o=1e3,rest:a=1}={}){if(this.assertEditable(),this.checkBodies(e,t),e<0)throw Error("A spring requires two bodies");let n=this.topology.jointCount;return this.topology.addSpring(e,t,ie(i,2,"anchorA"),ie(r,2,"anchorB"),K(o,"stiffness",!0),K(a,"rest",!0)),this.constraintHandle(n)}addMotor(e,t,{speed:i=1,maxTorque:r=100}={}){if(this.assertEditable(),this.checkBodies(e,t),!Number.isFinite(i))throw Error("speed must be finite");let o=this.topology.jointCount;return this.topology.addMotor(e,t,i,K(r,"maxTorque",!0)),this.constraintHandle(o)}addAngularLimit(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=ki(i),o=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);vs(this.topology,o.slot,r);let a=this.constraintHandle(o.slot,o);return a.setLimits=n=>{if(a.disposed)throw Error("The angular limit has been disposed");return this.gpu?this.gpu.setAngleLimits(a.slot,n):vs(this.topology,a.slot,ki(n)),a},a}addHinge(e,t,i=[0,0],r=[0,0],o={}){this.assertEditable(),this.checkBodies(e,t);let a=ie(i,2,"anchorA"),n=ie(r,2,"anchorB"),l=o.minAngle!==void 0||o.maxAngle!==void 0?ki(o):null,c=o.motor;if(c){if(!Number.isFinite(c.speed??1))throw Error("speed must be finite");K(c.maxTorque??100,"maxTorque",!0)}let u=this.addJoint(e,t,a,n),f=l?this.addAngularLimit(e,t,l):null,d=c?this.addMotor(e,t,c):null;return{joint:u,limits:f,motor:d,disposed:!1,dispose(){this.disposed||(this.joint.dispose(),this.limits?.dispose(),this.motor?.dispose(),this.disposed=!0)},destroy(){this.dispose()}}}createSolver(e,t={}){if(ma(e))return e.createSolver(this,t);this.assertEditable();let i=new Qo(e,this.topology,t);for(let[r,o]of this.restitution??[])i.setRestitution(r,o);for(let r of this.sensors??[])i.setSensor(r,!0);for(let[r,o]of this.filters??[])i.setFilters([r],[o[0]],[o[1]]);return this.gpu=i,this.created=!0,i}};var Cs=class{constructor(e){if(this.device=e,this.latestMs=null,this.samples=0,this.slots=[],!!e.features.has("timestamp-query"))for(let t=0;t<3;t++)this.slots.push({query:e.createQuerySet({type:"timestamp",count:2}),resolve:e.createBuffer({size:256,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:16,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),busy:!1})}begin(){let e=this.slots.find(t=>!t.busy);return!e||this.disposed?null:(e.busy=!0,e)}descriptor(e){return e?{timestampWrites:{querySet:e.query,beginningOfPassWriteIndex:0,endOfPassWriteIndex:1}}:{}}encode(e,t){t&&(e.resolveQuerySet(t.query,0,2,t.resolve,0),e.copyBufferToBuffer(t.resolve,0,t.read,0,16))}resolve(e){if(e)return e.pending=e.read.mapAsync(GPUMapMode.READ).then(()=>{let t=new BigUint64Array(e.read.getMappedRange());this.latestMs=Number(t[1]-t[0])/1e6,this.samples++,e.read.unmap()}).catch(()=>{}).finally(()=>{e.busy=!1,e.pending=null}),e.pending}settled(){return Promise.all(this.slots.map(e=>e.pending))}destroy(){this.disposed=!0;for(let e of this.slots)e.query.destroy(),e.resolve.destroy(),e.read.destroy()}};var Au=(s,e)=>Math.ceil(s/e)*e,wu=s=>String(s>>>0),Ss=class{constructor(e,t={}){this.device=e,this.maxFreeBytes=t.maxFreeBytes??201326592,this.freeBytes=0,this.free=new Map,this.all=new Set}acquire(e,t,i="recycled-buffer"){let r=Au(Math.max(4,e),4),o=wu(t),a=this.free.get(o);if(a){let l=-1,c=1/0;for(let u=0;u<a.length;u+=1){let f=a[u];f.size>=r&&f.size<c&&(l=u,c=f.size)}if(l>=0){let[u]=a.splice(l,1);return this.freeBytes-=u.size,u.buffer}}let n=this.device.createBuffer({label:i,size:r,usage:t,mappedAtCreation:!1});return this.all.add(n),n}release(e,t,i){if(!e)return;let r=Au(Math.max(4,t),4);if(this.freeBytes+r>this.maxFreeBytes){e.destroy(),this.all.delete(e);return}let o=wu(i),a=this.free.get(o);a||(a=[],this.free.set(o,a)),a.push({buffer:e,size:r,usage:i}),this.freeBytes+=r}trim(e=this.maxFreeBytes){this.maxFreeBytes=e;for(let[t,i]of this.free.entries()){for(;this.freeBytes>this.maxFreeBytes&&i.length>0;){let r=i.pop();r.buffer.destroy(),this.all.delete(r.buffer),this.freeBytes-=r.size}i.length===0&&this.free.delete(t)}}destroy(){for(let e of this.all)e.destroy();this.free.clear(),this.all.clear(),this.freeBytes=0}};var Bu=`const EPS : f32 = 1.0e-7;
|
|
5102
5188
|
|
|
5103
5189
|
struct Bullet {
|
|
5104
5190
|
posLife: vec4<f32>,
|
|
@@ -5299,7 +5385,7 @@ fn collisionBenchmark(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
|
5299
5385
|
bullets[id] = b;
|
|
5300
5386
|
atomicAdd(&counters.alive, 1u);
|
|
5301
5387
|
}
|
|
5302
|
-
`;var
|
|
5388
|
+
`;var ba=48,Un=48,ks=16,Nn=48,qn=class{constructor(e,t,i=3){this.device=e,this.recycler=t,this.slotCount=Math.max(2,i|0),this.usage=GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST,this.slots=[],this.cursor=0,this.latest={hits:0,alive:0};for(let r=0;r<this.slotCount;r+=1)this.slots.push({buffer:t.acquire(ks,this.usage,`collision-counter-readback-${r}`),pending:!1})}encodeCopy(e,t){let i=null;for(let r=0;r<this.slots.length;r+=1){let o=(this.cursor+r)%this.slots.length;if(!this.slots[o].pending){i=this.slots[o],this.cursor=(o+1)%this.slots.length;break}}return i?(i.pending=!0,e.copyBufferToBuffer(t,0,i.buffer,0,ks),i):null}resolve(e){return e?(e.buffer.mapAsync(GPUMapMode.READ).then(()=>{let t=new Uint32Array(e.buffer.getMappedRange(),0,4);this.latest={hits:t[0],alive:t[1]},e.buffer.unmap(),e.pending=!1}).catch(t=>{this.disposed||console.warn("Particle counter readback failed:",t),e.pending=!1}),this.latest):this.latest}destroy(){this.disposed=!0;for(let e of this.slots)e.pending||this.recycler.release(e.buffer,ks,this.usage);this.slots.length=0}},Vn=class{constructor(e,{capacity:t,triangleCount:i,readback:r=!0}={}){let o=e.limits.maxComputeWorkgroupsPerDimension**2*128,a=Math.min(e.limits.maxStorageBufferBindingSize,e.limits.maxBufferSize);for(let[n,l,c]of[["capacity",t,ba],["triangleCount",i,Un]])if(!Number.isSafeInteger(l)||l<1||l*c>a||n==="capacity"&&l>o)throw Error(`Invalid particle ${n}: exceeds GPU capacity or is not a positive integer`);this.device=e,this.timer=new Cs(e),this.capacity=t,this.triangleCount=i,this.frame=0,this.recycler=new Ss(e),this.storageUsage=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,this.uniformUsage=GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST,this.module=e.createShaderModule({label:"collision-benchmark-module",code:Bu}),this.pipeline=e.createComputePipeline({label:"collision-benchmark-pipeline",layout:"auto",compute:{module:this.module,entryPoint:"collisionBenchmark"}}),this.particleBuffer=this.recycler.acquire(this.capacity*ba,this.storageUsage,"collision-bullets"),this.triangleBuffer=this.recycler.acquire(this.triangleCount*Un,this.storageUsage,"collision-triangles"),this.counterBuffer=this.recycler.acquire(ks,this.storageUsage,"collision-counters"),this.paramBuffer=this.recycler.acquire(Nn,this.uniformUsage,"collision-params"),this.params=new ArrayBuffer(Nn),this.zeroCounters=new Uint32Array(4),this.readback=r?new qn(e,this.recycler,3):null,this.bindGroup=e.createBindGroup({label:"collision-benchmark-bg",layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.particleBuffer}},{binding:1,resource:{buffer:this.triangleBuffer}},{binding:2,resource:{buffer:this.counterBuffer}},{binding:3,resource:{buffer:this.paramBuffer}}]}),this.clear()}clear(){if(this.disposed)throw Error("Particle system has been destroyed");let e=this.device.createCommandEncoder({label:"Clear surface particles"});e.clearBuffer(this.particleBuffer),this.device.queue.submit([e.finish()]),this.frame=0,this.readback&&(this.readback.latest={hits:0,alive:0})}writeTriangles(e){if(this.disposed)throw Error("Particle system has been destroyed");if(!(e instanceof Float32Array)||e.length!==this.triangleCount*12||!e.every(Number.isFinite))throw Error("Triangles require 12 finite float32 values per triangle: padded a, b, c vertices");this.device.queue.writeBuffer(this.triangleBuffer,0,e)}step(e){if(this.disposed)throw Error("Particle system has been destroyed");let t={speed:80,life:3,bulletRadius:.09,spawnRadius:32,jitter:4.8,...e,target:e.count??this.capacity,frame:e.frame??this.frame++};if(!Number.isSafeInteger(t.target)||t.target<1||t.target>this.capacity)throw Error("Particle count exceeds allocated capacity");for(let d of["dt","speed","life","bulletRadius","spawnRadius"])if(!Number.isFinite(t[d])||t[d]<=0)throw Error(`Particle ${d} must be positive and finite`);if(!Number.isFinite(t.jitter)||t.jitter<0)throw Error("Particle jitter must be nonnegative and finite");let i=new Float32Array(this.params),r=new Uint32Array(this.params);i.fill(0),i[0]=t.dt,i[1]=t.speed,i[2]=t.life,i[3]=t.bulletRadius,i[4]=t.spawnRadius,i[5]=t.jitter,r[6]=t.target>>>0,r[7]=this.triangleCount>>>0,r[8]=t.frame>>>0;let o=Math.ceil(t.target/128),a=Math.ceil(o/this.device.limits.maxComputeWorkgroupsPerDimension),n=Math.ceil(o/a);r[9]=n*128,this.device.queue.writeBuffer(this.paramBuffer,0,this.params),this.device.queue.writeBuffer(this.counterBuffer,0,this.zeroCounters);let l=this.device.createCommandEncoder({label:"collision-benchmark-encoder"}),c=this.timer.begin(),u=l.beginComputePass({label:"collision-benchmark-pass",...this.timer.descriptor(c)});u.setPipeline(this.pipeline),u.setBindGroup(0,this.bindGroup),u.dispatchWorkgroups(n,a),u.end(),this.timer.encode(l,c);let f=this.readback?.encodeCopy(l,this.counterBuffer);return this.device.queue.submit([l.finish()]),this.timer.resolve(c),this.readback?.resolve(f)??null}destroy(){this.disposed||(this.disposed=!0,this.timer.destroy(),this.readback?.destroy(),this.recycler.release(this.particleBuffer,this.capacity*ba,this.storageUsage),this.recycler.release(this.triangleBuffer,this.triangleCount*Un,this.storageUsage),this.recycler.release(this.counterBuffer,ks,this.storageUsage),this.recycler.release(this.paramBuffer,Nn,this.uniformUsage),this.recycler.destroy())}};var Cu=`// Read the canonical solver's completed contact buffers directly. These layouts
|
|
5303
5389
|
// match avbd3d/gpu/layout.ts; no contact data is copied to the CPU for this view.
|
|
5304
5390
|
struct Body {pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f}
|
|
5305
5391
|
struct Camera {vp:mat4x4f,eye:vec4f,light:vec4f}
|
|
@@ -5329,11 +5415,11 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
|
|
|
5329
5415
|
return clip;
|
|
5330
5416
|
}
|
|
5331
5417
|
@fragment fn fragmentMain()->@location(0) vec4f {return vec4f(.83,.25,.39,1);}
|
|
5332
|
-
`;var
|
|
5418
|
+
`;var wd=Cu.replace("@vertex fn vertexMain","fn samplePoint").replaceAll("@builtin(vertex_index) ","").replaceAll("@builtin(instance_index) ","").replace("->@builtin(position) vec4f","->vec4f").replace(/clip\.x\+=.*?;/,"").replace(/clip\.y\+=.*?;/,"")+`
|
|
5333
5419
|
@group(0) @binding(5) var<storage,read_write> sampled:array<vec4f>;
|
|
5334
5420
|
@compute @workgroup_size(64) fn gather(@builtin(global_invocation_id) id:vec3u) {
|
|
5335
5421
|
if(id.x<arrayLength(&sampled)){sampled[id.x]=samplePoint(0u,id.x);}
|
|
5336
|
-
}`,
|
|
5422
|
+
}`,Bd=`
|
|
5337
5423
|
struct Camera {vp:mat4x4f,eye:vec4f,light:vec4f}
|
|
5338
5424
|
@group(0) @binding(0) var<uniform> camera:Camera;
|
|
5339
5425
|
@group(0) @binding(1) var<storage,read> points:array<vec4f>;
|
|
@@ -5345,4 +5431,4 @@ const CORNERS=array<vec2f,6>(vec2f(-1,-1),vec2f(1,-1),vec2f(1,1),vec2f(-1,-1),ve
|
|
|
5345
5431
|
return clip;
|
|
5346
5432
|
}
|
|
5347
5433
|
@fragment fn fragmentMain()->@location(0) vec4f {return vec4f(.83,.25,.39,1);}
|
|
5348
|
-
`,
|
|
5434
|
+
`,Wn=class{constructor(e,{count:t=2048}={}){if(!Number.isSafeInteger(t)||t<1||Math.ceil(t/64)>e.limits.maxComputeWorkgroupsPerDimension)throw Error("Contact sample count exceeds GPU dispatch capacity");this.device=e,this.count=t,this.buffer=e.createBuffer({label:"AVBD sampled contact dots",size:this.count*16,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC}),this.pipeline=e.createComputePipeline({label:"AVBD contact overlay sampling",layout:"auto",compute:{module:e.createShaderModule({code:wd}),entryPoint:"gather"}}),this.bindings=[]}encode(e,{bodyBuffer:t,cameraBuffer:i,contactStorage:r}){let o=[t,i,r.manifolds,r.contacts,r.counters,this.buffer],a=this.bindings.find(l=>l.buffers.every((c,u)=>c===o[u]));a||(a={buffers:o,bind:this.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:o.map((l,c)=>({binding:c,resource:{buffer:l}}))})},this.bindings.push(a),this.bindings.length>2&&this.bindings.shift());let n=e.beginComputePass();n.setPipeline(this.pipeline),n.setBindGroup(0,a.bind),n.dispatchWorkgroups(Math.ceil(this.count/64)),n.end()}dispose(){this.buffer.destroy(),this.bindings.length=0}};function Cd(s,e,t=8){let i=new Map(e.bodies.map((c,u)=>[c,u])),r=e.bodies.map(()=>new Set);for(let c of e.forces){let u=i.get(c.bodyA),f=i.get(c.bodyB);u===void 0||f===void 0||e.bodies[u].mass===0||e.bodies[f].mass===0||(r[u].add(f),r[f].add(u))}let o=e.bodies.map((c,u)=>u).filter(c=>e.bodies[c].mass>0).sort((c,u)=>r[u].size-r[c].size||c-u),a=new Uint32Array(s.bodyCapacity).fill(255),n=new Map,l=0;for(let c of o){let u=new Set([...r[c]].map(d=>n.get(d))),f=0;for(;u.has(f);)f++;if(f>=64)return;n.set(c,f),a[s.gpuIndex(c)]=f,l=Math.max(l,f+1)}s.device.queue.writeBuffer(s.colorBuffer,0,a),s.colorCap=Math.min(64,Math.max(t,l+3))}var Hn=new WeakMap;function Sd(s,{before:e,after:t}={}){if(!s.bodyBuffer||typeof s.step!="function")throw Error("GPU step passes require a WebGPU solver");if(!e&&!t||e!==void 0&&typeof e!="function"||t!==void 0&&typeof t!="function")throw Error("GPU step passes require synchronous before and/or after callbacks");let i=Hn.get(s);i||(i={hooks:new Set,previousBefore:s.externalBeforeStep,previousAfter:s.externalAfterStep},i.before=o=>{i.previousBefore?.call(s,o);for(let a of i.hooks)a.before?.(o,s)},i.after=o=>{i.previousAfter?.call(s,o);for(let a of i.hooks)a.after?.(o,s)},s.externalBeforeStep=i.before,s.externalAfterStep=i.after,Hn.set(s,i));let r={before:e,after:t};return i.hooks.add(r),()=>{i.hooks.delete(r)&&(i.hooks.size||(s.externalBeforeStep===i.before&&(s.externalBeforeStep=i.previousBefore),s.externalAfterStep===i.after&&(s.externalAfterStep=i.previousAfter),Hn.delete(s)))}}var $n=class{constructor(e,t,i){this.offscreenCanvas=e,this.events=new Map,this.resize(t,i)}resize(e,t){let i=Math.max(1,Math.floor(e)),r=Math.max(1,Math.floor(t));(i!==this.clientWidth||r!==this.clientHeight)&&(this.revision=(this.revision??0)+1),this.clientWidth=i,this.clientHeight=r}get width(){return this.offscreenCanvas.width}set width(e){this.offscreenCanvas.width=e}get height(){return this.offscreenCanvas.height}set height(e){this.offscreenCanvas.height=e}getContext(...e){return this.offscreenCanvas.getContext(...e)}getBoundingClientRect(){return{left:0,top:0,width:this.clientWidth,height:this.clientHeight}}addEventListener(e,t){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(t)}removeEventListener(e,t){this.events.get(e)?.delete(t)}setPointerCapture(){}dispatch(e,t){let i={...t,preventDefault(){}};for(let r of this.events.get(e)??[])r(i)}};var kd=9;function Pd(){return globalThis.crossOriginIsolated&&typeof SharedArrayBuffer=="function"?new Int32Array(new SharedArrayBuffer(36)):null}function Ld(s,e,t=0){Atomics.add(s,0,1),[Math.round(e.clientX*1024),Math.round(e.clientY*1024),e.pointerId??1,e.buttons??0,e.altKey?1:0,e.shiftKey?1:0,e.ctrlKey?1:0,Math.round(t*1024)].forEach((r,o)=>Atomics.store(s,o+1,r)),Atomics.add(s,0,1)}function _d(s,e=-1){let t=Atomics.load(s,0);if(t&1||t===e)return null;let i=Array.from({length:8},(r,o)=>Atomics.load(s,o+1));return Atomics.load(s,0)!==t?null:{sequence:t,event:{clientX:i[0]/1024,clientY:i[1]/1024,pointerId:i[2],buttons:i[3],altKey:!!i[4],shiftKey:!!i[5],ctrlKey:!!i[6]},wheel:i[7]/1024}}export{yo as AvbdPhysics,vo as AvbdPhysicsAggregate,fs as AvbdPhysicsBody,os as AvbdPhysicsConstraint,as as AvbdPhysicsHinge,Fn as AvbdScene2D,Gn as AvbdScene3D,mt as AvbdShapeType,Ss as GPUBufferRecycler,Wn as GpuContactSample,Vn as GpuSurfaceParticles,Cs as GpuTimer,kd as INPUT_WORDS,ba as PARTICLE_BYTE_STRIDE,$n as WorkerCanvas,Sd as addGpuStepPass,ls as cookHeightfield,cs as cookStaticMesh,Pd as createSharedInput,vd as createSolverPipeline,ss as createWebGPUDevice,Cr as prepareWebGPUDevice3D,_d as readSharedInput,Bd as sampledContactShader,Cd as seedConstraintColoring,Ld as writeSharedInput};
|