avbd-babylon 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2793 -2562
- package/dist/avbd.d.ts +1 -0
- package/dist/avbd.global.js +232 -138
- package/dist/avbd.js +232 -138
- package/dist/gpuDevice.d.ts +4 -0
- package/dist/native.d.ts +1 -0
- package/dist/native.js +1 -1
- package/dist/nativeScenes.d.ts +3 -0
- package/dist/solverPipeline.d.ts +62 -0
- package/docs/BABYLON_API.md +8 -0
- package/docs/README.md +465 -459
- package/docs/RELEASE.md +176 -122
- package/package.json +124 -116
- package/tests/README.md +218 -168
package/dist/avbd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var L=(o=0,e=0,t=0)=>Float64Array.of(o,e,t),Bi=(o=0,e=0,t=0,i=1)=>Float64Array.of(o,e,t,i),Y=()=>new Float64Array(9);var Yo=o=>o<0?-1:o>0?1:0,et=(o,e)=>o<e?o:e,Yn=(o,e)=>o>e?o:e,Re=(o,e,t)=>Yn(e,et(t,o)),Z=(o,e)=>o[0]*e[0]+o[1]*e[1]+o[2]*e[2],tt=o=>Z(o,o),wi=o=>Math.sqrt(tt(o));function qe(o,e,t,i){return o[0]=e,o[1]=t,o[2]=i,o}var Sr=(o,e,t)=>qe(o,e[0]+t[0],e[1]+t[1],e[2]+t[2]),X=(o,e,t)=>qe(o,e[0]-t[0],e[1]-t[1],e[2]-t[2]),we=(o,e,t)=>qe(o,e[0]*t,e[1]*t,e[2]*t),Jt=(o,e,t)=>qe(o,e[0]/t,e[1]/t,e[2]/t),de=(o,e)=>qe(o,-e[0],-e[1],-e[2]),Fs=(o,e)=>qe(o,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),U=(o,e,t,i)=>qe(o,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),Qn=(o,e)=>Jt(o,e,wi(e)),_t=(o,e,t)=>qe(o,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 Ai(o,e,t,i,r){return o[0]=e,o[1]=t,o[2]=i,o[3]=r,o}function Qo(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=t[0],l=t[1],c=t[2],f=t[3];return Ai(o,a*n+i*f+r*c-s*l,a*l-i*c+r*f+s*n,a*c+i*l-r*n+s*f,a*f-i*n-r*l-s*c)}var Kn=(o,e)=>Ai(o,-e[0],-e[1],-e[2],e[3]),Ko=o=>tt(o)+o[3]*o[3];function Zn(o,e){let t=Ko(e);return Ai(o,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function el(o,e){let t=Math.sqrt(Ko(e));return Ai(o,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var xi=new Float64Array(4),Wt=new Float64Array(4);function Et(o,e,t){return Qo(xi,e,Zn(xi,t)),qe(o,xi[0]*2,xi[1]*2,xi[2]*2)}function Cr(o,e,t){return Ai(Wt,t[0],t[1],t[2],0),Qo(Wt,Wt,e),el(o,Ai(o,e[0]+Wt[0]*.5,e[1]+Wt[1]*.5,e[2]+Wt[2]*.5,e[3]+Wt[3]*.5))}function ce(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=(r*t[2]-s*t[1])*2,l=(s*t[0]-i*t[2])*2,c=(i*t[1]-r*t[0])*2;return qe(o,t[0]+n*a+(r*c-s*l),t[1]+l*a+(s*n-i*c),t[2]+c*a+(i*l-r*n))}var Xo=new Float64Array(3);function ht(o,e,t,i){return ce(Xo,t,i),Sr(o,Xo,e)}function Si(o,e,t){return ce(o,Kn(xi,e),t)}function Oe(o,e,t,i){return o.fill(0),o[0]=e,o[4]=t,o[8]=i,o}function Ns(o,e){return o[0]=0,o[1]=-e[2],o[2]=e[1],o[3]=e[2],o[4]=0,o[5]=-e[0],o[6]=-e[1],o[7]=e[0],o[8]=0,o}function $t(o,e){let t=e[1],i=e[2],r=e[5];return o[0]=e[0],o[1]=e[3],o[2]=e[6],o[3]=t,o[4]=e[4],o[5]=e[7],o[6]=i,o[7]=r,o[8]=e[8],o}function je(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=e[4],l=e[5],c=e[6],f=e[7],u=e[8],d=t[0],h=t[1],m=t[2],p=t[3],b=t[4],y=t[5],A=t[6],w=t[7],g=t[8];return o[0]=i*d+r*p+s*A,o[1]=i*h+r*b+s*w,o[2]=i*m+r*y+s*g,o[3]=a*d+n*p+l*A,o[4]=a*h+n*b+l*w,o[5]=a*m+n*y+l*g,o[6]=c*d+f*p+u*A,o[7]=c*h+f*b+u*w,o[8]=c*m+f*y+u*g,o}function ie(o,e,t){let i=t[0],r=t[1],s=t[2];return qe(o,e[0]*i+e[1]*r+e[2]*s,e[3]*i+e[4]*r+e[5]*s,e[6]*i+e[7]*r+e[8]*s)}function Se(o,e){for(let t=0;t<9;t++)o[t]+=e[t];return o}function Xt(o,e,t){for(let i=0;i<9;i++)o[i]=e[i]*t;return o}function Zo(o,e){for(let t=0;t<9;t++)o[t]=-e[t];return o}function kr(o,e,t){for(let i=0;i<3;i++)for(let r=0;r<3;r++)o[i*3+r]=t[r]*e[i];return o}function ea(o,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 Oe(o,t,i,r)}function ta(o,e){let t=Math.abs(e[0])>Math.abs(e[2])?L(-e[1],e[0],0):L(0,-e[2],e[1]);Qn(t,t);let i=_t(L(),e,t);return o[0]=e[0],o[1]=e[1],o[2]=e[2],o.set(t,3),o.set(i,6),o}function ia(o,e,t,i,r,s,a){let n=o[0],l=o[3],c=o[4],f=o[6],u=o[7],d=o[8],h=t[0],m=t[1],p=t[2],b=e[0],y=t[3],A=t[4],w=t[5],g=e[3],x=e[4],v=t[6],B=t[7],E=t[8],k=e[6],z=e[7],M=e[8],P=l/n,_=f/n,S=h/n,C=y/n,R=v/n,I=n,O=c-P*P*I,q=(u-P*_*I)/O,G=(m-P*S*I)/O,j=(A-P*C*I)/O,F=(B-P*R*I)/O,W=d-(_*_*I+q*q*O),ee=(p-_*S*I-q*G*O)/W,ne=(w-_*C*I-q*j*O)/W,ve=(E-_*R*I-q*F*O)/W,N=b-(S*S*I+G*G*O+ee*ee*W),Ee=(g-S*C*I-G*j*O-ee*ne*W)/N,ze=(k-S*R*I-G*F*O-ee*ve*W)/N,le=x-(C*C*I+j*j*O+ne*ne*W+Ee*Ee*N),Fe=(z-C*R*I-j*F*O-ne*ve*W-Ee*ze*N)/le,Lt=M-(R*R*I+F*F*O+ve*ve*W+ze*ze*N+Fe*Fe*le),Me=i[0],Ne=i[1]-P*Me,Ie=i[2]-_*Me-q*Ne,Ue=r[0]-S*Me-G*Ne-ee*Ie,Ze=r[1]-C*Me-j*Ne-ne*Ie-Ee*Ue,bi=r[2]-R*Me-F*Ne-ve*Ie-ze*Ue-Fe*Ze,gi=Me/I,yi=Ne/O,vi=Ie/W,At=Ue/N,$n=Ze/le,Xn=bi/Lt;a[2]=Xn,a[1]=$n-Fe*a[2],a[0]=At-Ee*a[1]-ze*a[2],s[2]=vi-ee*a[0]-ne*a[1]-ve*a[2],s[1]=yi-q*s[2]-G*a[0]-j*a[1]-F*a[2],s[0]=gi-P*s[1]-_*s[2]-S*a[0]-C*a[1]-R*a[2]}var Ki=1,pt=1e10,ra=.01,sa=1e-5,at=class{solver;forces=[];positionLin;positionAng=Bi();initialLin=L();initialAng=Bi();inertialLin=L();inertialAng=Bi();velocityLin;velocityAng=L();prevVelocityLin;size;mass;moment;friction;radius;constructor(e,t,i,r,s,a=[0,0,0]){this.solver=e,this.size=L(t[0],t[1],t[2]),this.friction=r,this.positionLin=L(s[0],s[1],s[2]),this.velocityLin=L(a[0],a[1],a[2]),this.prevVelocityLin=L(a[0],a[1],a[2]);let[n,l,c]=this.size;this.mass=n*l*c*i,this.moment=L((l*l+c*c)/12*this.mass,(n*n+c*c)/12*this.mass,(n*n+l*l)/12*this.mass),this.radius=wi([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}},oa=()=>({lhsLin:Y(),lhsAng:Y(),lhsCross:Y(),rhsLin:L(),rhsAng:L()}),zt=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 ua=8,aa=16,Qt=1e-6,qs=1e-5,tl=1e-6,Vs=34028234663852886e22,Pr=0,fa=1,Lr=2;function na(o){let e=o.positionAng;return{center:o.positionLin,half:we(L(),o.size,.5),axis:[ce(L(),e,[1,0,0]),ce(L(),e,[0,1,0]),ce(L(),e,[0,0,1])]}}var Yt=(o,e)=>Math.abs(Z(o,e));function _r(o,e){let t=Z(e,o.axis[0])>=0?1:-1,i=Z(e,o.axis[1])>=0?1:-1,r=Z(e,o.axis[2])>=0?1:-1,s=U(L(),o.center,o.axis[0],o.half[0]*t);return U(s,s,o.axis[1],o.half[1]*i),U(s,s,o.axis[2],o.half[2]*r)}function da(o,e){return e===0?{u:o.axis[1],v:o.axis[2],extentU:o.half[1],extentV:o.half[2]}:e===1?{u:o.axis[0],v:o.axis[2],extentU:o.half[0],extentV:o.half[2]}:{u:o.axis[0],v:o.axis[1],extentU:o.half[0],extentV:o.half[1]}}function il(o,e,t){let i=Z(t,o.axis[e])>=0?1:-1,r=we(L(),o.axis[e],i),s=U(L(),o.center,r,o.half[e]);return{normal:r,center:s,...da(o,e)}}function rl(o,e){let t=0,i=-Vs;for(let r=0;r<3;r++){let s=Yt(o.axis[r],e);s>i&&(i=s,t=r)}return t}function sl(o,e,t){let i=Z(o.axis[e],t)>0?-1:1,r=we(L(),o.axis[e],i),s=U(L(),o.center,r,o.half[e]),{u:a,v:n,extentU:l,extentV:c}=da(o,e),f=(u,d)=>{let h=U(L(),s,a,u*l);return U(h,h,n,d*c)};return[f(1,1),f(-1,1),f(-1,-1),f(1,-1)]}function ol(o,e,t){let i=[];if(o.length===0)return i;let r=o[o.length-1],s=Z(e,r)-t;for(let a of o){let n=Z(e,a)-t,l=s<=qs,c=n<=qs;if(l!==c){let f=0,u=s-n;Math.abs(u)>Qt&&(f=Re(s/u,0,1)),i.length<aa&&i.push(U(L(),r,X(L(),a,r),f))}c&&i.length<aa&&i.push(a),r=a,s=n}return i}var Er=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=we(L(),Sr(L(),e,t),.5);for(let n of this.midpoints)if(tt(X(L(),r,n))<tl)return;if(this.contacts.length>=ua)return;let{bodyA:s,bodyB:a}=this;this.contacts.push({feature:i,rA:Si(L(),s.positionAng,X(L(),e,s.positionLin)),rB:Si(L(),a.positionAng,X(L(),t,a.positionLin))}),this.midpoints.push(r)}};function Us(o,e,t,i,r,s,a,n){let l=tt(i);if(l<Qt)return!0;let c=1/Math.sqrt(l),f=we(L(),i,c);Z(f,t)<0&&de(f,f);let u=Math.abs(Z(t,f)),d=o.half[0]*Yt(f,o.axis[0])+o.half[1]*Yt(f,o.axis[1])+o.half[2]*Yt(f,o.axis[2]),h=e.half[0]*Yt(f,e.axis[0])+e.half[1]*Yt(f,e.axis[1])+e.half[2]*Yt(f,e.axis[2]),m=u-(d+h);return m>0?!1:((!n.valid||m>n.separation)&&(n.valid=!0,n.type=r,n.indexA=s,n.indexB=a,n.separation=m,n.normalAB=f),!0)}function la(o,e,t){let i=(e+1)%3,r=(e+2)%3,s=Z(t,o.axis[i])>=0?1:-1,a=Z(t,o.axis[r])>=0?1:-1,n=U(L(),o.center,o.axis[i],o.half[i]*s);U(n,n,o.axis[r],o.half[r]*a);let l=we(L(),o.axis[e],o.half[e]);return[X(L(),n,l),Sr(L(),n,l)]}function al(o,e,t,i){let r=X(L(),e,o),s=X(L(),i,t),a=X(L(),o,t),n=Z(r,r),l=Z(s,s),c=Z(s,a),f=0,u=0;if(n<=Qt&&l<=Qt)return[o,t];if(n<=Qt)u=Re(c/l,0,1);else{let d=Z(r,a);if(l<=Qt)f=Re(-d/n,0,1);else{let h=Z(r,s),m=n*l-h*h;Math.abs(m)>Qt&&(f=Re((h*c-d*l)/m,0,1)),u=(h*f+c)/l,u<0?(u=0,f=Re(-d/n,0,1)):u>1&&(u=1,f=Re((h-d)/n,0,1))}}return[U(L(),o,r,f),U(L(),t,s,u)]}function ca(o,e,t,i,r,s,a){let n=r?t:i,l=r?i:t,c=r?a:de(L(),a),f=il(n,s,c),u=rl(l,f.normal),d=sl(l,u,f.normal),h=[[f.u,f.extentU],[de(L(),f.u),f.extentU],[f.v,f.extentV],[de(L(),f.v),f.extentV]];for(let[b,y]of h)if(d=ol(d,b,Z(b,f.center)+y),!d.length)return[];let m=new Er(o,e),p=(r?Pr:fa)<<24|(s&255)<<16|(u&255)<<8;for(let b=0;b<d.length&&m.contacts.length<ua;b++){let y=d[b],A=Z(X(L(),y,f.center),f.normal);if(A>qs)continue;let w=U(L(),y,f.normal,-A),g=r?w:y,x=r?y:w;m.add(g,x,p|b&255)}return m.contacts.length||m.add(_r(t,a),_r(i,de(L(),a)),p),m.contacts}function nl(o,e,t,i,r,s,a){let[n,l]=la(t,r,a),[c,f]=la(i,s,de(L(),a)),[u,d]=al(n,l,c,f),h=new Er(o,e),m=Lr<<24|(r&255)<<8|s&255;return h.add(u,d,m),h.contacts.length||h.add(_r(t,a),_r(i,de(L(),a)),m),h.contacts}function ha(o,e,t){let i=na(o),r=na(e),s=X(L(),r.center,i.center),a={type:Pr,indexA:0,indexB:0,separation:-Vs,normalAB:L(),valid:!1},n={type:Lr,indexA:0,indexB:0,separation:-Vs,normalAB:L(),valid:!1};for(let f=0;f<3;f++)if(!Us(i,r,s,i.axis[f],Pr,f,-1,a))return[];for(let f=0;f<3;f++)if(!Us(i,r,s,r.axis[f],fa,-1,f,a))return[];let l=L();for(let f=0;f<3;f++)for(let u=0;u<3;u++)if(_t(l,i.axis[f],r.axis[u]),!Us(i,r,s,l,Lr,f,u,n))return[];if(!a.valid)return[];let c=a;return n.valid&&.95*n.separation>a.separation+.01&&(c=n),ta(t,de(L(),c.normalAB)),c.type===Lr?nl(o,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===Pr?ca(o,e,i,r,!0,c.indexA,c.normalAB):ca(o,e,i,r,!1,c.indexB,c.normalAB)}var pa=L(),ma=L(),ba=L(),ga=L(),ya=L(),va=L(),zr=Y(),Hs=Y(),Ws=Y(),Mr=Y(),Js=Y(),$s=Y(),Xs=Y(),Ir=Y(),he=L(),xe=L(),Kt=L(),Mt=L(),xa=L(),Aa=L(),Ci=0,Zi=0;function Ba(o,e,t){for(let i=0;i<3;i++)Mt[0]=t[i*3],Mt[1]=t[i*3+1],Mt[2]=t[i*3+2],_t(Mt,e,Mt),o[i*3]=Mt[0],o[i*3+1]=Mt[1],o[i*3+2]=Mt[2];return o}var ki=class extends zt{contacts=[];basis=Y();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=ha(e,t,this.basis).map(s=>{let a=r.find(n=>n.feature===s.feature);return a?{feature:s.feature,rA:a.stick?a.rA:s.rA,rB:a.stick?a.rB:s.rB,C0:L(),penalty:a.penalty.slice(),lambda:a.lambda.slice(),stick:a.stick}:{feature:s.feature,rA:s.rA,rB:s.rB,C0:L(),penalty:L(),lambda:L(),stick:!1}});for(let s of this.contacts){ht(xa,e.positionLin,e.positionAng,s.rA),ht(Aa,t.positionLin,t.positionAng,s.rB),ie(s.C0,this.basis,X(Kt,xa,Aa)),s.C0[0]+=ra;for(let a=0;a<3;a++)s.lambda[a]=s.lambda[a]*i.alpha*i.gamma,s.penalty[a]=Re(s.penalty[a]*i.gamma,Ki,pt)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,s=this.basis;ce(ya,i.positionAng,e.rA),ce(va,r.positionAng,e.rB),Zo(zr,s),Ba(Hs,ya,s),Ba(Ws,va,zr),Oe(Mr,e.penalty[0],e.penalty[1],e.penalty[2]),we(he,e.C0,1-t),U(he,he,ie(Kt,s,pa),1),U(he,he,ie(Kt,zr,ba),1),U(he,he,ie(Kt,Hs,ma),1),U(he,he,ie(Kt,Ws,ga),1),ie(xe,Mr,he),U(xe,xe,e.lambda,1),xe[0]=et(xe[0],0),Zi=Math.abs(xe[0])*this.friction,Ci=Math.sqrt(xe[1]*xe[1]+xe[2]*xe[2]),Ci>Zi&&Ci>0&&(xe[1]*=Zi/Ci,xe[2]*=Zi/Ci)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;X(pa,e.positionLin,e.initialLin),Et(ma,e.positionAng,e.initialAng),X(ba,t.positionLin,t.initialLin),Et(ga,t.positionAng,t.initialAng)}updatePrimal(e,t,i){this.computeDisplacements();let r=e===this.bodyA;for(let s of this.contacts){this.evaluate(s,t);let a=r?this.basis:zr,n=r?Hs:Ws;$t(Js,a),$t($s,n),je(Xs,$s,Mr),Se(i.lhsLin,je(Ir,je(Ir,Js,Mr),a)),Se(i.lhsAng,je(Ir,Xs,n)),Se(i.lhsCross,je(Ir,Xs,a)),U(i.rhsLin,i.rhsLin,ie(Kt,Js,xe),1),U(i.rhsAng,i.rhsAng,ie(Kt,$s,xe),1)}}updateDual(e){let{solver:t}=this;this.computeDisplacements();for(let i of this.contacts)this.evaluate(i,e),i.lambda.set(xe),xe[0]<0&&(i.penalty[0]=et(i.penalty[0]+t.betaLin*Math.abs(he[0]),pt)),Ci<=Zi&&(i.penalty[1]=et(i.penalty[1]+t.betaLin*Math.abs(he[1]),pt),i.penalty[2]=et(i.penalty[2]+t.betaLin*Math.abs(he[2]),pt),i.stick=Math.sqrt(he[1]*he[1]+he[2]*he[2])<sa)}};var Ys=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),Pi=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,Ys())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,s=L(),a=L();for(let n=this.bodies.length-1;n>=0;n--){let l=this.bodies[n];if(l.mass<=0)continue;Si(s,l.positionAng,X(s,e,l.positionLin)),Si(a,l.positionAng,t);let c=0,f=1/0,u=!0;for(let h=0;h<3&&u;h++){let m=l.size[h]*.5;if(Math.abs(a[h])<1e-6){(s[h]<-m||s[h]>m)&&(u=!1);continue}let p=1/a[h],b=(-m-s[h])*p,y=(m-s[h])*p;b>y&&([b,y]=[y,b]),c=Math.max(c,b),f=Math.min(f,y),c>f&&(u=!1)}if(!u)continue;let d=c>=0?c:f;d<0||(!r||d<r.t)&&(r={body:l,local:U(L(),s,a,d),t:d})}return r}sys=oa();dxLin=L();dxAng=L();dp=L();accel=L();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:s}=this;for(let n=i.length-1;n>=0;n--){let l=i[n];for(let c=n-1;c>=0;c--){let f=i[c];X(s,l.positionLin,f.positionLin);let u=l.radius+f.radius;Z(s,s)<=u*u&&!l.constrainedTo(f)&&new ki(this,l,f)}}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];U(l.inertialLin,l.positionLin,l.velocityLin,e),l.mass>0&&(l.inertialLin[2]+=t*(e*e)),Cr(l.inertialAng,l.positionAng,wa(l.velocityAng,e)),Jt(this.accel,X(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*Yo(t),f=Re(c/Math.abs(t),0,1);Number.isFinite(f)||(f=0),l.initialLin.set(l.positionLin),l.initialAng.set(l.positionAng),l.mass>0&&(U(l.positionLin,l.positionLin,l.velocityLin,e),l.positionLin[2]+=t*(f*e*e),Cr(l.positionAng,l.positionAng,wa(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 f=c.mass,u=c.moment;Oe(r.lhsLin,f/(e*e),f/(e*e),f/(e*e)),Oe(r.lhsAng,u[0]/(e*e),u[1]/(e*e),u[2]/(e*e)),r.lhsCross.fill(0),ie(r.rhsLin,r.lhsLin,X(s,c.positionLin,c.inertialLin)),ie(r.rhsAng,r.lhsAng,Et(s,c.positionAng,c.inertialAng));let d=c.forces;for(let h=d.length-1;h>=0;h--)d[h].updatePrimal(c,this.alpha,r);ia(r.lhsLin,r.lhsAng,r.lhsCross,de(r.rhsLin,r.rhsLin),de(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),U(c.positionLin,c.positionLin,this.dxLin,1),Cr(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&&(Jt(n.velocityLin,X(n.velocityLin,n.positionLin,n.initialLin),e),Jt(n.velocityAng,Et(n.velocityAng,n.positionAng,n.initialAng),e))}},ll=L(),wa=(o,e)=>we(ll,o,e);var oe=(o,e)=>[o[0]-e[0],o[1]-e[1],o[2]-e[2]],nt=(o,e)=>o[0]*e[0]+o[1]*e[1]+o[2]*e[2],Zt=(o,e)=>[o[1]*e[2]-o[2]*e[1],o[2]*e[0]-o[0]*e[2],o[0]*e[1]-o[1]*e[0]],Te=o=>Math.sqrt(nt(o,o));function er(o){let e=Sa(o);return!e||e.vertices.length/3<=32?e:Sa(cl(e,32))}function cl(o,e){let[t,i,r,s]=o.rotation,a=u=>{let d=[2*(i*u[2]-r*u[1]),2*(r*u[0]-t*u[2]),2*(t*u[1]-i*u[0])];return[u[0]+s*d[0]+(i*d[2]-r*d[1])+o.center[0],u[1]+s*d[1]+(r*d[0]-t*d[2])+o.center[1],u[2]+s*d[2]+(t*d[1]-i*d[0])+o.center[2]]},n=[];for(let u=0;u<o.vertices.length;u+=3)n.push(a([o.vertices[u],o.vertices[u+1],o.vertices[u+2]]));let l=0;n.forEach((u,d)=>{Te(oe(u,o.center))>Te(oe(n[l],o.center))&&(l=d)});let c=[l],f=n.map(u=>Te(oe(u,n[l])));for(;c.length<e;){let u=0;f.forEach((d,h)=>{d>f[u]&&(u=h)}),c.push(u),n.forEach((d,h)=>{f[h]=Math.min(f[h],Te(oe(d,n[u])))})}return c.flatMap(u=>n[u])}function Sa(o){let e=0;for(let g=0;g<o.length;g++)e=Math.max(e,Math.abs(o[g]));let t=Math.max(e,1e-9)*1e-6,i=new Set,r=[];for(let g=0;g+2<o.length;g+=3){let x=[o[g],o[g+1],o[g+2]],v=x.map(B=>Math.round(B/t)).join(",");i.has(v)||(i.add(v),r.push(x))}if(r.length<4)return null;let s=Math.max(e,1e-9)*1e-10,a=0;for(let g=1;g<r.length;g++)r[g][0]<r[a][0]&&(a=g);let n=-1,l=0;for(let g=0;g<r.length;g++){let x=Te(oe(r[g],r[a]));x>l&&(l=x,n=g)}if(n<0||l<s)return null;let c=-1;l=0;let f=oe(r[n],r[a]);for(let g=0;g<r.length;g++){let x=Te(Zt(f,oe(r[g],r[a])));x>l&&(l=x,c=g)}if(c<0||l<s*Te(f))return null;let u=Zt(f,oe(r[c],r[a])),d=-1;l=0;for(let g=0;g<r.length;g++){let x=Math.abs(nt(u,oe(r[g],r[a])));x>l&&(l=x,d=g)}if(d<0||l<s*Te(u))return null;let h=[],m=(g,x,v)=>{let B=Zt(oe(r[x],r[g]),oe(r[v],r[g])),E=Te(B)||1,k=[B[0]/E,B[1]/E,B[2]/E];return{v:[g,x,v],n:k,d:nt(k,r[g])}},b=nt(u,oe(r[d],r[a]))>0?[a,c,n]:[a,n,c];h.push(m(b[0],b[1],b[2])),h.push(m(b[0],d,b[1])),h.push(m(b[1],d,b[2])),h.push(m(b[2],d,b[0]));let y=new Set([a,n,c,d]),A=r.map((g,x)=>x).filter(g=>!y.has(g)),w=[a,n,c,d].reduce((g,x)=>[g[0]+r[x][0]/4,g[1]+r[x][1]/4,g[2]+r[x][2]/4],[0,0,0]);A.sort((g,x)=>Te(oe(r[x],w))-Te(oe(r[g],w)));for(let g of A){let x=h.filter(P=>nt(P.n,r[g])-P.d>s);if(!x.length)continue;let v=new Map;for(let P of h)for(let _=0;_<3;_++)v.set(`${P.v[_]},${P.v[(_+1)%3]}`,P);let B=new Set(x),E=[...x];for(;E.length;){let P=E.pop();for(let _=0;_<3;_++){let S=v.get(`${P.v[(_+1)%3]},${P.v[_]}`);S&&!B.has(S)&&nt(S.n,r[g])-S.d>-s&&(B.add(S),E.push(S))}}let k=[...B],z=new Set;for(let P of k)for(let _=0;_<3;_++)z.add(`${P.v[_]},${P.v[(_+1)%3]}`);let M=[];for(let P of k)for(let _=0;_<3;_++){let S=P.v[_],C=P.v[(_+1)%3];z.has(`${C},${S}`)||M.push([S,C])}for(let P of k)h.splice(h.indexOf(P),1);for(let[P,_]of M)h.push(m(P,_,g))}return ul(r.flat(),h.flatMap(g=>g.v))}function ul(o,e){let t=[];for(let S=0;S+2<o.length;S+=3)t.push([o[S],o[S+1],o[S+2]]);let i=0;for(let S of t)i=Math.max(i,Math.abs(S[0]),Math.abs(S[1]),Math.abs(S[2]));let r=Math.max(i,1e-9)*1e-5,s=[];for(let S=0;S+2<e.length;S+=3){let C=[e[S],e[S+1],e[S+2]],R=Zt(oe(t[C[1]],t[C[0]]),oe(t[C[2]],t[C[0]])),I=Te(R);if(!(I>0))continue;let O=[R[0]/I,R[1]/I,R[2]/I];s.push({v:C,n:O,d:nt(O,t[C[0]])})}let a=new Map;s.forEach((S,C)=>{for(let R=0;R<3;R++)a.set(`${S.v[R]},${S.v[(R+1)%3]}`,C)});let n=new Array(s.length).fill(-1),l=[];s.forEach((S,C)=>{if(n[C]>=0)return;let R=[C];n[C]=l.length;for(let I=0;I<R.length;I++){let O=s[R[I]];for(let q=0;q<3;q++){let G=a.get(`${O.v[(q+1)%3]},${O.v[q]}`);if(G===void 0||n[G]>=0)continue;let j=s[G];nt(S.n,j.n)>1-1e-5&&j.v.every(F=>Math.abs(nt(S.n,t[F])-S.d)<=r)&&(n[G]=l.length,R.push(G))}}l.push(R)});let c=[];for(let S of l){let C=new Set;for(let j of S)for(let F=0;F<3;F++)C.add(`${s[j].v[F]},${s[j].v[(F+1)%3]}`);let R=new Map;for(let j of S)for(let F=0;F<3;F++){let W=s[j].v[F],ee=s[j].v[(F+1)%3];if(!C.has(`${ee},${W}`)){if(R.has(W))return null;R.set(W,ee)}}let I=[0,0,0];for(let j of S){let F=s[j],W=Zt(oe(t[F.v[1]],t[F.v[0]]),oe(t[F.v[2]],t[F.v[0]]));I[0]+=W[0],I[1]+=W[1],I[2]+=W[2]}let O=Te(I)||1,q=R.keys().next().value,G=[q];for(let j=R.get(q);j!==q&&G.length<=R.size;j=R.get(j))G.push(j);if(G.length!==R.size)return null;c.push({n:[I[0]/O,I[1]/O,I[2]/O],verts:G})}let f=new Map,u=[];for(let S of c)for(let C of S.verts)f.has(C)||(f.set(C,u.length),u.push(t[C]));for(let S of c)S.verts=S.verts.map(C=>f.get(C));let d=new Set,h=0;for(let S of c)S.verts.forEach((C,R)=>{d.add(`${C},${S.verts[(R+1)%S.verts.length]}`),h++});if(d.size!==h||![...d].every(S=>{let[C,R]=S.split(",");return d.has(`${R},${C}`)})||u.length-d.size/2+c.length!==2)return null;let m=0,p=[0,0,0],b=[0,0,0,0,0,0];for(let S of c){let C=u[S.verts[0]];for(let R=1;R+1<S.verts.length;R++){let I=u[S.verts[R]],O=u[S.verts[R+1]],q=nt(C,Zt(I,O));m+=q/6;for(let j=0;j<3;j++)p[j]+=q/24*(C[j]+I[j]+O[j]);let G=(j,F)=>q/120*(C[j]*C[F]+I[j]*I[F]+O[j]*O[F]+(C[j]+I[j]+O[j])*(C[F]+I[F]+O[F]));b[0]+=G(0,0),b[1]+=G(1,1),b[2]+=G(2,2),b[3]+=G(0,1),b[4]+=G(0,2),b[5]+=G(1,2)}}if(!(m>0))return null;for(let S=0;S<3;S++)p[S]/=m;b[0]-=m*p[0]*p[0],b[1]-=m*p[1]*p[1],b[2]-=m*p[2]*p[2],b[3]-=m*p[0]*p[1],b[4]-=m*p[0]*p[2],b[5]-=m*p[1]*p[2];let y=b[0]+b[1]+b[2],A=[[y-b[0],-b[3],-b[4]],[-b[3],y-b[1],-b[5]],[-b[4],-b[5],y-b[2]]],{values:w,vectors:g}=fl(A),x=g,v=S=>{let C=oe(S,p);return[x[0][0]*C[0]+x[1][0]*C[1]+x[2][0]*C[2],x[0][1]*C[0]+x[1][1]*C[1]+x[2][1]*C[2],x[0][2]*C[0]+x[1][2]*C[1]+x[2][2]*C[2]]},B=u.map(v),E=new Float32Array(B.flat()),k=c.map(S=>{let C=S.n,R=[x[0][0]*C[0]+x[1][0]*C[1]+x[2][0]*C[2],x[0][1]*C[0]+x[1][1]*C[1]+x[2][1]*C[2],x[0][2]*C[0]+x[1][2]*C[1]+x[2][2]*C[2]],I=-1/0;for(let O of S.verts)I=Math.max(I,nt(R,B[O]));return{normal:R,d:I,verts:S.verts}}),z=new Map;k.forEach((S,C)=>S.verts.forEach((R,I)=>z.set(`${R},${S.verts[(I+1)%S.verts.length]}`,C)));let M=[];k.forEach((S,C)=>S.verts.forEach((R,I)=>{let O=S.verts[(I+1)%S.verts.length],q=z.get(`${O},${R}`);R<O&&q!==void 0&&M.push([R,O,C,q])}));let P=[0,0,0],_=0;for(let S of B){for(let C=0;C<3;C++)P[C]=Math.max(P[C],2*Math.abs(S[C]));_=Math.max(_,Te(S))}return{vertices:E,faces:k,edges:M,volume:m,moments:[w[0],w[1],w[2]],size:P,radius:_,center:p,rotation:dl(x)}}function fl(o){let e=o.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 f=c+1;f<3;f++){if(Math.abs(e[c][f])<1e-300)continue;let u=(e[f][f]-e[c][c])/(2*e[c][f]),d=Math.sign(u||1)/(Math.abs(u)+Math.sqrt(u*u+1)),h=1/Math.sqrt(d*d+1),m=d*h;for(let p=0;p<3;p++){let b=e[p][c],y=e[p][f];e[p][c]=h*b-m*y,e[p][f]=m*b+h*y}for(let p=0;p<3;p++){let b=e[c][p],y=e[f][p];e[c][p]=h*b-m*y,e[f][p]=m*b+h*y}for(let p=0;p<3;p++){let b=t[p][c],y=t[p][f];t[p][c]=h*b-m*y,t[p][f]=m*b+h*y}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],s=Zt(i,r),a=[[i[0],r[0],s[0]],[i[1],r[1],s[1]],[i[2],r[2],s[2]]];return{values:[e[0][0],e[1][1],e[2][2]],vectors:a}}function dl(o){let e=o[0][0]+o[1][1]+o[2][2],t,i,r,s;if(e>0){let n=.5/Math.sqrt(e+1);s=.25/n,t=(o[2][1]-o[1][2])*n,i=(o[0][2]-o[2][0])*n,r=(o[1][0]-o[0][1])*n}else if(o[0][0]>o[1][1]&&o[0][0]>o[2][2]){let n=2*Math.sqrt(1+o[0][0]-o[1][1]-o[2][2]);s=(o[2][1]-o[1][2])/n,t=.25*n,i=(o[0][1]+o[1][0])/n,r=(o[0][2]+o[2][0])/n}else if(o[1][1]>o[2][2]){let n=2*Math.sqrt(1+o[1][1]-o[0][0]-o[2][2]);s=(o[0][2]-o[2][0])/n,t=(o[0][1]+o[1][0])/n,i=.25*n,r=(o[1][2]+o[2][1])/n}else{let n=2*Math.sqrt(1+o[2][2]-o[0][0]-o[1][1]);s=(o[1][0]-o[0][1])/n,t=(o[0][2]+o[2][0])/n,i=(o[1][2]+o[2][1])/n,r=.25*n}let a=Math.hypot(t,i,r,s);return[t/a,i/a,r/a,s/a]}var ka=new WeakSet,tr=o=>ka.has(o),hl=new WeakSet;var Pa=o=>hl.has(o);function Rr(o,e,t,i,r,s=[0,0,0]){let a=new at(o,[2*e,2*e,2*e],t,i,r,s);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,ka.add(a),a}var La=new WeakMap,_a=o=>La.get(o);function Or(o,e,t,i,r,s=[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 at(o,e.size,t,i,r,a);return n.positionAng.set([s[0],s[1],s[2],s[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,La.set(n,e),n}var Qs=`
|
|
1
|
+
var C=(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 us=s=>s<0?-1:s>0?1:0,xt=(s,e)=>s<e?s:e,Lc=(s,e)=>s>e?s:e,ce=(s,e,t)=>Lc(e,xt(t,s)),V=(s,e)=>s[0]*e[0]+s[1]*e[1]+s[2]*e[2],Ge=s=>V(s,s),ir=s=>Math.sqrt(Ge(s));function at(s,e,t,i){return s[0]=e,s[1]=t,s[2]=i,s}var ki=(s,e,t)=>at(s,e[0]+t[0],e[1]+t[1],e[2]+t[2]),$=(s,e,t)=>at(s,e[0]-t[0],e[1]-t[1],e[2]-t[2]),ne=(s,e,t)=>at(s,e[0]*t,e[1]*t,e[2]*t),_t=(s,e,t)=>at(s,e[0]/t,e[1]/t,e[2]/t),ee=(s,e)=>at(s,-e[0],-e[1],-e[2]),Xo=(s,e)=>at(s,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),D=(s,e,t,i)=>at(s,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),_c=(s,e)=>_t(s,e,ir(e)),Dt=(s,e,t)=>at(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 tr(s,e,t,i,r){return s[0]=e,s[1]=t,s[2]=i,s[3]=r,s}function Or(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 tr(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 Ec=(s,e)=>tr(s,-e[0],-e[1],-e[2],e[3]),ga=s=>Ge(s)+s[3]*s[3];function Yo(s,e){let t=ga(e);return tr(s,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function Mc(s,e){let t=Math.sqrt(ga(e));return tr(s,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var er=new Float64Array(4),Si=new Float64Array(4);function lt(s,e,t){return Or(er,e,Yo(er,t)),at(s,er[0]*2,er[1]*2,er[2]*2)}function ri(s,e,t){return tr(Si,t[0],t[1],t[2],0),Or(Si,Si,e),Mc(s,tr(s,e[0]+Si[0]*.5,e[1]+Si[1]*.5,e[2]+Si[2]*.5,e[3]+Si[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 at(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 ma=new Float64Array(3);function Ft(s,e,t,i){return ae(ma,t,i),ki(s,ma,e)}function Te(s,e,t){return ae(s,Ec(er,e),t)}function Ce(s,e,t,i){return s.fill(0),s[0]=e,s[4]=t,s[8]=i,s}function Qo(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 Pi(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],g=t[2],h=t[3],m=t[4],b=t[5],v=t[6],w=t[7],y=t[8];return s[0]=i*d+r*h+o*v,s[1]=i*p+r*m+o*w,s[2]=i*g+r*b+o*y,s[3]=n*d+a*h+l*v,s[4]=n*p+a*m+l*w,s[5]=n*g+a*b+l*y,s[6]=c*d+u*h+f*v,s[7]=c*p+u*m+f*w,s[8]=c*g+u*b+f*y,s}function ue(s,e,t){let i=t[0],r=t[1],o=t[2];return at(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 Ne(s,e){for(let t=0;t<9;t++)s[t]+=e[t];return s}function Li(s,e,t){for(let i=0;i<9;i++)s[i]=e[i]*t;return s}function ba(s,e){for(let t=0;t<9;t++)s[t]=-e[t];return s}function fs(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 ya(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 ds(s,e){let t=Math.abs(e[0])>Math.abs(e[2])?C(-e[1],e[0],0):C(0,-e[2],e[1]);_c(t,t);let i=Dt(C(),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 hs(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],g=t[1],h=t[2],m=e[0],b=t[3],v=t[4],w=t[5],y=e[3],A=e[4],x=t[6],B=t[7],E=t[8],L=e[6],M=e[7],z=e[8],_=l/a,k=u/a,S=p/a,P=b/a,I=x/a,R=a,F=c-_*_*R,X=(f-_*k*R)/F,W=(g-_*S*R)/F,N=(v-_*P*R)/F,H=(B-_*I*R)/F,Z=d-(k*k*R+X*X*F),he=(h-k*S*R-X*W*F)/Z,we=(w-k*P*R-X*N*F)/Z,Oe=(E-k*I*R-X*H*F)/Z,J=m-(S*S*R+W*W*F+he*he*Z),$e=(y-S*P*R-W*N*F-he*we*Z)/J,Xe=(L-S*I*R-W*H*F-he*Oe*Z)/J,Be=A-(P*P*R+N*N*F+we*we*Z+$e*$e*J),st=(M-P*I*R-N*H*F-we*Oe*Z-$e*Xe*J)/Be,ii=z-(I*I*R+H*H*F+Oe*Oe*Z+Xe*Xe*J+st*st*Be),Ye=i[0],ot=i[1]-_*Ye,Qe=i[2]-k*Ye-X*ot,nt=r[0]-S*Ye-W*ot-he*Qe,At=r[1]-P*Ye-N*ot-we*Qe-$e*nt,Yi=r[2]-I*Ye-H*ot-Oe*Qe-Xe*nt-st*At,Qi=Ye/R,Ki=ot/F,Zi=Qe/Z,Wt=nt/J,kc=At/Be,Pc=Yi/ii;n[2]=Pc,n[1]=kc-st*n[2],n[0]=Wt-$e*n[1]-Xe*n[2],o[2]=Zi-he*n[0]-we*n[1]-Oe*n[2],o[1]=Ki-X*o[2]-W*n[0]-N*n[1]-H*n[2],o[0]=Qi-_*o[1]-k*o[2]-S*n[0]-P*n[1]-I*n[2]}var Et=1,Ue=1e10,ps=.01,va=1e-5,Ze=class{solver;forces=[];positionLin;positionAng=jt();initialLin=C();initialAng=jt();inertialLin=C();inertialAng=jt();velocityLin;velocityAng=C();prevVelocityLin;size;mass;moment;friction;radius;constructor(e,t,i,r,o,n=[0,0,0]){this.solver=e,this.size=C(t[0],t[1],t[2]),this.friction=r,this.positionLin=C(o[0],o[1],o[2]),this.velocityLin=C(n[0],n[1],n[2]),this.prevVelocityLin=C(n[0],n[1],n[2]);let[a,l,c]=this.size;this.mass=a*l*c*i,this.moment=C((l*l+c*c)/12*this.mass,(a*a+c*c)/12*this.mass,(a*a+l*l)/12*this.mass),this.radius=ir([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}},ms=()=>({lhsLin:oe(),lhsAng:oe(),lhsCross:oe(),rhsLin:C(),rhsAng:C()}),Gt=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 Ca=8,Aa=16,Ei=1e-6,Zo=1e-5,zc=1e-6,en=34028234663852886e22,gs=0,Sa=1,bs=2;function xa(s){let e=s.positionAng;return{center:s.positionLin,half:ne(C(),s.size,.5),axis:[ae(C(),e,[1,0,0]),ae(C(),e,[0,1,0]),ae(C(),e,[0,0,1])]}}var _i=(s,e)=>Math.abs(V(s,e));function ys(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(C(),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 ka(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 Ic(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(C(),s.axis[e],i),o=D(C(),s.center,r,s.half[e]);return{normal:r,center:o,...ka(s,e)}}function Rc(s,e){let t=0,i=-en;for(let r=0;r<3;r++){let o=_i(s.axis[r],e);o>i&&(i=o,t=r)}return t}function Oc(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(C(),s.axis[e],i),o=D(C(),s.center,r,s.half[e]),{u:n,v:a,extentU:l,extentV:c}=ka(s,e),u=(f,d)=>{let p=D(C(),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 Tc(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<=Zo,c=a<=Zo;if(l!==c){let u=0,f=o-a;Math.abs(f)>Ei&&(u=ce(o/f,0,1)),i.length<Aa&&i.push(D(C(),r,$(C(),n,r),u))}c&&i.length<Aa&&i.push(n),r=n,o=a}return i}var vs=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(C(),ki(C(),e,t),.5);for(let a of this.midpoints)if(Ge($(C(),r,a))<zc)return;if(this.contacts.length>=Ca)return;let{bodyA:o,bodyB:n}=this;this.contacts.push({feature:i,rA:Te(C(),o.positionAng,$(C(),e,o.positionLin)),rB:Te(C(),n.positionAng,$(C(),t,n.positionLin))}),this.midpoints.push(r)}};function Ko(s,e,t,i,r,o,n,a){let l=Ge(i);if(l<Ei)return!0;let c=1/Math.sqrt(l),u=ne(C(),i,c);V(u,t)<0&&ee(u,u);let f=Math.abs(V(t,u)),d=s.half[0]*_i(u,s.axis[0])+s.half[1]*_i(u,s.axis[1])+s.half[2]*_i(u,s.axis[2]),p=e.half[0]*_i(u,e.axis[0])+e.half[1]*_i(u,e.axis[1])+e.half[2]*_i(u,e.axis[2]),g=f-(d+p);return g>0?!1:((!a.valid||g>a.separation)&&(a.valid=!0,a.type=r,a.indexA=o,a.indexB=n,a.separation=g,a.normalAB=u),!0)}function wa(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(C(),s.center,s.axis[i],s.half[i]*o);D(a,a,s.axis[r],s.half[r]*n);let l=ne(C(),s.axis[e],s.half[e]);return[$(C(),a,l),ki(C(),a,l)]}function jc(s,e,t,i){let r=$(C(),e,s),o=$(C(),i,t),n=$(C(),s,t),a=V(r,r),l=V(o,o),c=V(o,n),u=0,f=0;if(a<=Ei&&l<=Ei)return[s,t];if(a<=Ei)f=ce(c/l,0,1);else{let d=V(r,n);if(l<=Ei)u=ce(-d/a,0,1);else{let p=V(r,o),g=a*l-p*p;Math.abs(g)>Ei&&(u=ce((p*c-d*l)/g,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(C(),s,r,u),D(C(),t,o,f)]}function Ba(s,e,t,i,r,o,n){let a=r?t:i,l=r?i:t,c=r?n:ee(C(),n),u=Ic(a,o,c),f=Rc(l,u.normal),d=Oc(l,f,u.normal),p=[[u.u,u.extentU],[ee(C(),u.u),u.extentU],[u.v,u.extentV],[ee(C(),u.v),u.extentV]];for(let[m,b]of p)if(d=Tc(d,m,V(m,u.center)+b),!d.length)return[];let g=new vs(s,e),h=(r?gs:Sa)<<24|(o&255)<<16|(f&255)<<8;for(let m=0;m<d.length&&g.contacts.length<Ca;m++){let b=d[m],v=V($(C(),b,u.center),u.normal);if(v>Zo)continue;let w=D(C(),b,u.normal,-v),y=r?w:b,A=r?b:w;g.add(y,A,h|m&255)}return g.contacts.length||g.add(ys(t,n),ys(i,ee(C(),n)),h),g.contacts}function Dc(s,e,t,i,r,o,n){let[a,l]=wa(t,r,n),[c,u]=wa(i,o,ee(C(),n)),[f,d]=jc(a,l,c,u),p=new vs(s,e),g=bs<<24|(r&255)<<8|o&255;return p.add(f,d,g),p.contacts.length||p.add(ys(t,n),ys(i,ee(C(),n)),g),p.contacts}function Pa(s,e,t){let i=xa(s),r=xa(e),o=$(C(),r.center,i.center),n={type:gs,indexA:0,indexB:0,separation:-en,normalAB:C(),valid:!1},a={type:bs,indexA:0,indexB:0,separation:-en,normalAB:C(),valid:!1};for(let u=0;u<3;u++)if(!Ko(i,r,o,i.axis[u],gs,u,-1,n))return[];for(let u=0;u<3;u++)if(!Ko(i,r,o,r.axis[u],Sa,-1,u,n))return[];let l=C();for(let u=0;u<3;u++)for(let f=0;f<3;f++)if(Dt(l,i.axis[u],r.axis[f]),!Ko(i,r,o,l,bs,u,f,a))return[];if(!n.valid)return[];let c=n;return a.valid&&.95*a.separation>n.separation+.01&&(c=a),ds(t,ee(C(),c.normalAB)),c.type===bs?Dc(s,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===gs?Ba(s,e,i,r,!0,c.indexA,c.normalAB):Ba(s,e,i,r,!1,c.indexB,c.normalAB)}var La=C(),_a=C(),Ea=C(),Ma=C(),za=C(),Ia=C(),As=oe(),tn=oe(),rn=oe(),xs=oe(),sn=oe(),on=oe(),nn=oe(),ws=oe(),Ee=C(),je=C(),Mi=C(),si=C(),Ra=C(),Oa=C(),rr=0,Tr=0;function Ta(s,e,t){for(let i=0;i<3;i++)si[0]=t[i*3],si[1]=t[i*3+1],si[2]=t[i*3+2],Dt(si,e,si),s[i*3]=si[0],s[i*3+1]=si[1],s[i*3+2]=si[2];return s}var oi=class extends Gt{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=Pa(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:C(),penalty:n.penalty.slice(),lambda:n.lambda.slice(),stick:n.stick}:{feature:o.feature,rA:o.rA,rB:o.rB,C0:C(),penalty:C(),lambda:C(),stick:!1}});for(let o of this.contacts){Ft(Ra,e.positionLin,e.positionAng,o.rA),Ft(Oa,t.positionLin,t.positionAng,o.rB),ue(o.C0,this.basis,$(Mi,Ra,Oa)),o.C0[0]+=ps;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,Ue)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,o=this.basis;ae(za,i.positionAng,e.rA),ae(Ia,r.positionAng,e.rB),ba(As,o),Ta(tn,za,o),Ta(rn,Ia,As),Ce(xs,e.penalty[0],e.penalty[1],e.penalty[2]),ne(Ee,e.C0,1-t),D(Ee,Ee,ue(Mi,o,La),1),D(Ee,Ee,ue(Mi,As,Ea),1),D(Ee,Ee,ue(Mi,tn,_a),1),D(Ee,Ee,ue(Mi,rn,Ma),1),ue(je,xs,Ee),D(je,je,e.lambda,1),je[0]=xt(je[0],0),Tr=Math.abs(je[0])*this.friction,rr=Math.sqrt(je[1]*je[1]+je[2]*je[2]),rr>Tr&&rr>0&&(je[1]*=Tr/rr,je[2]*=Tr/rr)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;$(La,e.positionLin,e.initialLin),lt(_a,e.positionAng,e.initialAng),$(Ea,t.positionLin,t.initialLin),lt(Ma,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:As,a=r?tn:rn;Pi(sn,n),Pi(on,a),Ke(nn,on,xs),Ne(i.lhsLin,Ke(ws,Ke(ws,sn,xs),n)),Ne(i.lhsAng,Ke(ws,nn,a)),Ne(i.lhsCross,Ke(ws,nn,n)),D(i.rhsLin,i.rhsLin,ue(Mi,sn,je),1),D(i.rhsAng,i.rhsAng,ue(Mi,on,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]=xt(i.penalty[0]+t.betaLin*Math.abs(Ee[0]),Ue)),rr<=Tr&&(i.penalty[1]=xt(i.penalty[1]+t.betaLin*Math.abs(Ee[1]),Ue),i.penalty[2]=xt(i.penalty[2]+t.betaLin*Math.abs(Ee[2]),Ue),i.stick=Math.sqrt(Ee[1]*Ee[1]+Ee[2]*Ee[2])<va)}};var an=()=>({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,an())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=C(),n=C();for(let a=this.bodies.length-1;a>=0;a--){let l=this.bodies[a];if(l.mass<=0)continue;Te(o,l.positionAng,$(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 g=l.size[p]*.5;if(Math.abs(n[p])<1e-6){(o[p]<-g||o[p]>g)&&(f=!1);continue}let h=1/n[p],m=(-g-o[p])*h,b=(g-o[p])*h;m>b&&([m,b]=[b,m]),c=Math.max(c,m),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(C(),o,n,d),t:d})}return r}sys=ms();dxLin=C();dxAng=C();dp=C();accel=C();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];$(o,l.positionLin,u.positionLin);let f=l.radius+u.radius;V(o,o)<=f*f&&!l.constrainedTo(u)&&new oi(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)),ri(l.inertialAng,l.positionAng,ja(l.velocityAng,e)),_t(this.accel,$(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*us(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),ri(l.positionAng,l.positionAng,ja(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,$(o,c.positionLin,c.inertialLin)),ue(r.rhsAng,r.lhsAng,lt(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);hs(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),ri(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,$(a.velocityLin,a.positionLin,a.initialLin),e),_t(a.velocityAng,lt(a.velocityAng,a.positionAng,a.initialAng),e))}},Fc=C(),ja=(s,e)=>ne(Fc,s,e);var ve=(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],zi=(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 jr(s){let e=Da(s);return!e||e.vertices.length/3<=32?e:Da(Gc(e,32))}function Gc(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(ve(f,s.center))>et(ve(a[l],s.center))&&(l=d)});let c=[l],u=a.map(f=>et(ve(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(ve(d,a[f])))})}return c.flatMap(f=>a[f])}function Da(s){let e=0;for(let y=0;y<s.length;y++)e=Math.max(e,Math.abs(s[y]));let t=Math.max(e,1e-9)*1e-6,i=new Set,r=[];for(let y=0;y+2<s.length;y+=3){let A=[s[y],s[y+1],s[y+2]],x=A.map(B=>Math.round(B/t)).join(",");i.has(x)||(i.add(x),r.push(A))}if(r.length<4)return null;let o=Math.max(e,1e-9)*1e-10,n=0;for(let y=1;y<r.length;y++)r[y][0]<r[n][0]&&(n=y);let a=-1,l=0;for(let y=0;y<r.length;y++){let A=et(ve(r[y],r[n]));A>l&&(l=A,a=y)}if(a<0||l<o)return null;let c=-1;l=0;let u=ve(r[a],r[n]);for(let y=0;y<r.length;y++){let A=et(zi(u,ve(r[y],r[n])));A>l&&(l=A,c=y)}if(c<0||l<o*et(u))return null;let f=zi(u,ve(r[c],r[n])),d=-1;l=0;for(let y=0;y<r.length;y++){let A=Math.abs(Mt(f,ve(r[y],r[n])));A>l&&(l=A,d=y)}if(d<0||l<o*et(f))return null;let p=[],g=(y,A,x)=>{let B=zi(ve(r[A],r[y]),ve(r[x],r[y])),E=et(B)||1,L=[B[0]/E,B[1]/E,B[2]/E];return{v:[y,A,x],n:L,d:Mt(L,r[y])}},m=Mt(f,ve(r[d],r[n]))>0?[n,c,a]:[n,a,c];p.push(g(m[0],m[1],m[2])),p.push(g(m[0],d,m[1])),p.push(g(m[1],d,m[2])),p.push(g(m[2],d,m[0]));let b=new Set([n,a,c,d]),v=r.map((y,A)=>A).filter(y=>!b.has(y)),w=[n,a,c,d].reduce((y,A)=>[y[0]+r[A][0]/4,y[1]+r[A][1]/4,y[2]+r[A][2]/4],[0,0,0]);v.sort((y,A)=>et(ve(r[A],w))-et(ve(r[y],w)));for(let y of v){let A=p.filter(_=>Mt(_.n,r[y])-_.d>o);if(!A.length)continue;let x=new Map;for(let _ of p)for(let k=0;k<3;k++)x.set(`${_.v[k]},${_.v[(k+1)%3]}`,_);let B=new Set(A),E=[...A];for(;E.length;){let _=E.pop();for(let k=0;k<3;k++){let S=x.get(`${_.v[(k+1)%3]},${_.v[k]}`);S&&!B.has(S)&&Mt(S.n,r[y])-S.d>-o&&(B.add(S),E.push(S))}}let L=[...B],M=new Set;for(let _ of L)for(let k=0;k<3;k++)M.add(`${_.v[k]},${_.v[(k+1)%3]}`);let z=[];for(let _ of L)for(let k=0;k<3;k++){let S=_.v[k],P=_.v[(k+1)%3];M.has(`${P},${S}`)||z.push([S,P])}for(let _ of L)p.splice(p.indexOf(_),1);for(let[_,k]of z)p.push(g(_,k,y))}return Nc(r.flat(),p.flatMap(y=>y.v))}function Nc(s,e){let t=[];for(let S=0;S+2<s.length;S+=3)t.push([s[S],s[S+1],s[S+2]]);let i=0;for(let S of t)i=Math.max(i,Math.abs(S[0]),Math.abs(S[1]),Math.abs(S[2]));let r=Math.max(i,1e-9)*1e-5,o=[];for(let S=0;S+2<e.length;S+=3){let P=[e[S],e[S+1],e[S+2]],I=zi(ve(t[P[1]],t[P[0]]),ve(t[P[2]],t[P[0]])),R=et(I);if(!(R>0))continue;let F=[I[0]/R,I[1]/R,I[2]/R];o.push({v:P,n:F,d:Mt(F,t[P[0]])})}let n=new Map;o.forEach((S,P)=>{for(let I=0;I<3;I++)n.set(`${S.v[I]},${S.v[(I+1)%3]}`,P)});let a=new Array(o.length).fill(-1),l=[];o.forEach((S,P)=>{if(a[P]>=0)return;let I=[P];a[P]=l.length;for(let R=0;R<I.length;R++){let F=o[I[R]];for(let X=0;X<3;X++){let W=n.get(`${F.v[(X+1)%3]},${F.v[X]}`);if(W===void 0||a[W]>=0)continue;let N=o[W];Mt(S.n,N.n)>1-1e-5&&N.v.every(H=>Math.abs(Mt(S.n,t[H])-S.d)<=r)&&(a[W]=l.length,I.push(W))}}l.push(I)});let c=[];for(let S of l){let P=new Set;for(let N of S)for(let H=0;H<3;H++)P.add(`${o[N].v[H]},${o[N].v[(H+1)%3]}`);let I=new Map;for(let N of S)for(let H=0;H<3;H++){let Z=o[N].v[H],he=o[N].v[(H+1)%3];if(!P.has(`${he},${Z}`)){if(I.has(Z))return null;I.set(Z,he)}}let R=[0,0,0];for(let N of S){let H=o[N],Z=zi(ve(t[H.v[1]],t[H.v[0]]),ve(t[H.v[2]],t[H.v[0]]));R[0]+=Z[0],R[1]+=Z[1],R[2]+=Z[2]}let F=et(R)||1,X=I.keys().next().value,W=[X];for(let N=I.get(X);N!==X&&W.length<=I.size;N=I.get(N))W.push(N);if(W.length!==I.size)return null;c.push({n:[R[0]/F,R[1]/F,R[2]/F],verts:W})}let u=new Map,f=[];for(let S of c)for(let P of S.verts)u.has(P)||(u.set(P,f.length),f.push(t[P]));for(let S of c)S.verts=S.verts.map(P=>u.get(P));let d=new Set,p=0;for(let S of c)S.verts.forEach((P,I)=>{d.add(`${P},${S.verts[(I+1)%S.verts.length]}`),p++});if(d.size!==p||![...d].every(S=>{let[P,I]=S.split(",");return d.has(`${I},${P}`)})||f.length-d.size/2+c.length!==2)return null;let g=0,h=[0,0,0],m=[0,0,0,0,0,0];for(let S of c){let P=f[S.verts[0]];for(let I=1;I+1<S.verts.length;I++){let R=f[S.verts[I]],F=f[S.verts[I+1]],X=Mt(P,zi(R,F));g+=X/6;for(let N=0;N<3;N++)h[N]+=X/24*(P[N]+R[N]+F[N]);let W=(N,H)=>X/120*(P[N]*P[H]+R[N]*R[H]+F[N]*F[H]+(P[N]+R[N]+F[N])*(P[H]+R[H]+F[H]));m[0]+=W(0,0),m[1]+=W(1,1),m[2]+=W(2,2),m[3]+=W(0,1),m[4]+=W(0,2),m[5]+=W(1,2)}}if(!(g>0))return null;for(let S=0;S<3;S++)h[S]/=g;m[0]-=g*h[0]*h[0],m[1]-=g*h[1]*h[1],m[2]-=g*h[2]*h[2],m[3]-=g*h[0]*h[1],m[4]-=g*h[0]*h[2],m[5]-=g*h[1]*h[2];let b=m[0]+m[1]+m[2],v=[[b-m[0],-m[3],-m[4]],[-m[3],b-m[1],-m[5]],[-m[4],-m[5],b-m[2]]],{values:w,vectors:y}=Uc(v),A=y,x=S=>{let P=ve(S,h);return[A[0][0]*P[0]+A[1][0]*P[1]+A[2][0]*P[2],A[0][1]*P[0]+A[1][1]*P[1]+A[2][1]*P[2],A[0][2]*P[0]+A[1][2]*P[1]+A[2][2]*P[2]]},B=f.map(x),E=new Float32Array(B.flat()),L=c.map(S=>{let P=S.n,I=[A[0][0]*P[0]+A[1][0]*P[1]+A[2][0]*P[2],A[0][1]*P[0]+A[1][1]*P[1]+A[2][1]*P[2],A[0][2]*P[0]+A[1][2]*P[1]+A[2][2]*P[2]],R=-1/0;for(let F of S.verts)R=Math.max(R,Mt(I,B[F]));return{normal:I,d:R,verts:S.verts}}),M=new Map;L.forEach((S,P)=>S.verts.forEach((I,R)=>M.set(`${I},${S.verts[(R+1)%S.verts.length]}`,P)));let z=[];L.forEach((S,P)=>S.verts.forEach((I,R)=>{let F=S.verts[(R+1)%S.verts.length],X=M.get(`${F},${I}`);I<F&&X!==void 0&&z.push([I,F,P,X])}));let _=[0,0,0],k=0;for(let S of B){for(let P=0;P<3;P++)_[P]=Math.max(_[P],2*Math.abs(S[P]));k=Math.max(k,et(S))}return{vertices:E,faces:L,edges:z,volume:g,moments:[w[0],w[1],w[2]],size:_,radius:k,center:h,rotation:qc(A)}}function Uc(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),g=d*p;for(let h=0;h<3;h++){let m=e[h][c],b=e[h][u];e[h][c]=p*m-g*b,e[h][u]=g*m+p*b}for(let h=0;h<3;h++){let m=e[c][h],b=e[u][h];e[c][h]=p*m-g*b,e[u][h]=g*m+p*b}for(let h=0;h<3;h++){let m=t[h][c],b=t[h][u];t[h][c]=p*m-g*b,t[h][u]=g*m+p*b}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],o=zi(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 qc(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 Ga=new WeakSet,De=s=>Ga.has(s),Vc=new WeakSet;var sr=s=>Vc.has(s);function or(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,Ga.add(n),n}var Na=new WeakMap,wt=s=>Na.get(s);function Bs(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,Na.set(a,e),a}var ln=`
|
|
2
2
|
const T_NONE = 0;
|
|
3
3
|
|
|
4
4
|
const MAX_COLORS = 64u;
|
|
@@ -38,9 +38,9 @@ fn hash32(v: u32) -> u32 {
|
|
|
38
38
|
x ^= x >> 16u;
|
|
39
39
|
return x;
|
|
40
40
|
}
|
|
41
|
-
`,
|
|
41
|
+
`,Nt=`
|
|
42
42
|
// Shared by every 2D module (layout.ts)
|
|
43
|
-
${
|
|
43
|
+
${ln}
|
|
44
44
|
const T_JOINT = 1;
|
|
45
45
|
const T_SPRING = 2;
|
|
46
46
|
const T_MOTOR = 3;
|
|
@@ -148,8 +148,8 @@ fn rot(angle: f32, v: vec2f) -> vec2f {
|
|
|
148
148
|
return vec2f(cs.x * v.x - cs.y * v.y, cs.y * v.x + cs.x * v.y);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
`,
|
|
152
|
-
${
|
|
151
|
+
`,cn=(s,e={counter:"C_CONTACTS",prevCounter:"C_PREV_CONTACTS",capacity:"params.contactCapacity"})=>`
|
|
152
|
+
${s}
|
|
153
153
|
${e.colorThreads??"fn colorThreads(bodies: u32) -> u32 { return bodies; }"}
|
|
154
154
|
|
|
155
155
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
@@ -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
|
-
`,
|
|
188
|
+
`,Cs=cn(Nt),Se=["collision","adjacency","coloring","solve"];var Wc=`
|
|
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
|
-
`,
|
|
236
|
+
`,tt=class{levels=[];buffers=[];scanPipeline;addPipeline;constructor(e,t,i,r){let o=e.createShaderModule({label:"prefix scan",code:Wc}),n=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"}}]}),a=e.createPipelineLayout({bindGroupLayouts:[n]});this.scanPipeline=e.createComputePipeline({label:"scanBlocks",layout:a,compute:{module:o,entryPoint:"scanBlocks"}}),this.addPipeline=e.createComputePipeline({label:"addBlocks",layout:a,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 g=e.createBindGroup({layout:n,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:g}),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 Hc=`
|
|
237
237
|
alias TopoItem = Contact;
|
|
238
238
|
|
|
239
239
|
fn topoCount() -> u32 {
|
|
@@ -254,8 +254,8 @@ fn jointActive(j: u32) -> bool {
|
|
|
254
254
|
let s = joints[j].stiff;
|
|
255
255
|
return info[j].x != T_NONE && (s.x != 0.0 || s.y != 0.0 || s.z != 0.0);
|
|
256
256
|
}
|
|
257
|
-
`,
|
|
258
|
-
${
|
|
257
|
+
`,Ri=(s,e)=>`
|
|
258
|
+
${s}
|
|
259
259
|
${e}
|
|
260
260
|
|
|
261
261
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
@@ -532,11 +532,11 @@ 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
|
-
`,
|
|
535
|
+
`,Dr=Ri(Nt,Hc);var Jc=jt(),zt=oe(),Ss=oe(),Oi=oe(),un=oe(),nr=oe(),fn=oe(),ct=oe(),ks=oe(),Fr=oe(),be=C(),Me=C(),ye=C(),ai=C(),ar=C();function dn(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 Ps=(s,e,t)=>at(s,xt(e[0],t),xt(e[1],t),xt(e[2],t)),ke=class extends Gt{rA;rB;C0Lin=C();C0Ang=C();penaltyLin=C();penaltyAng=C();lambdaLin=C();lambdaAng=C();stiffnessLin;stiffnessAng;fracture;torqueArm;broken=!1;constructor(e,t,i,r,o,n=1/0,a=0,l=1/0){super(e,t,i),this.rA=C(r[0],r[1],r[2]),this.rB=C(o[0],o[1],o[2]),this.stiffnessLin=n,this.stiffnessAng=a,this.fracture=l;let c=t?t.size:[0,0,0];this.torqueArm=Ge([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?Ft(ai,t.positionLin,t.positionAng,this.rA):ai.set(this.rA),$(e,ai,Ft(ar,i.positionLin,i.positionAng,this.rB))}evaluateAng(e){return lt(e,this.bodyA?this.bodyA.positionAng:Jc,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,Ue),this.penaltyAng[t]=ce(this.penaltyAng[t]*e.gamma,Et,Ue);return Ps(this.penaltyLin,this.penaltyLin,this.stiffnessLin),Ps(this.penaltyAng,this.penaltyAng,this.stiffnessAng),!this.broken}updatePrimal(e,t,i){let r=e===this.bodyA;if(Ge(this.penaltyLin)>0){Ce(zt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(be),this.stiffnessLin===1/0&&D(be,be,this.C0Lin,-t),ue(Me,zt,be),D(Me,Me,this.lambdaLin,1);let o=r?1:-1;Ce(Ss,o,o,o),r?Qo(Oi,ee(ye,ae(ye,this.bodyA.positionAng,this.rA))):Qo(Oi,ae(ye,this.bodyB.positionAng,this.rB)),Pi(un,Ss),Pi(nr,Oi),Ke(fn,nr,zt),Ne(i.lhsLin,Ke(ct,Ke(ct,un,zt),Ss)),Ne(i.lhsAng,Ke(ct,fn,Oi)),Ne(i.lhsCross,Ke(ct,fn,Ss)),r?ae(ye,this.bodyA.positionAng,this.rA):ee(ye,ae(ye,this.bodyB.positionAng,this.rB)),Li(ks,dn(Fr,0,ye),Me[0]),Ne(ks,Li(Fr,dn(Fr,1,ye),Me[1])),Ne(ks,Li(Fr,dn(Fr,2,ye),Me[2])),Ne(i.lhsAng,ya(ct,ks)),D(i.rhsLin,i.rhsLin,ue(ye,un,Me),1),D(i.rhsAng,i.rhsAng,ue(ye,nr,Me),1)}if(Ge(this.penaltyAng)>0){Ce(zt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(be),this.stiffnessAng===1/0&&D(be,be,this.C0Ang,-t),ue(Me,zt,be),D(Me,Me,this.lambdaAng,1);let o=(r?1:-1)*this.torqueArm;Ce(Oi,o,o,o),Pi(nr,Oi),Ne(i.lhsAng,Ke(ct,Ke(ct,nr,zt),Oi)),D(i.rhsAng,i.rhsAng,ue(ye,nr,Me),1)}}updateDual(e){let{solver:t}=this;Ge(this.penaltyLin)>0&&(Ce(zt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(be),this.stiffnessLin===1/0&&(D(be,be,this.C0Lin,-e),ue(Me,zt,be),D(this.lambdaLin,Me,this.lambdaLin,1)),D(this.penaltyLin,this.penaltyLin,Xo(ye,be),t.betaLin),Ps(this.penaltyLin,this.penaltyLin,xt(this.stiffnessLin,Ue))),Ge(this.penaltyAng)>0&&(Ce(zt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(be),this.stiffnessAng===1/0&&(D(be,be,this.C0Ang,-e),ue(Me,zt,be),D(this.lambdaAng,Me,this.lambdaAng,1)),D(this.penaltyAng,this.penaltyAng,Xo(ye,be),t.betaAng),Ps(this.penaltyAng,this.penaltyAng,xt(this.stiffnessAng,Ue))),Ge(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)}},ut=class extends Gt{rA;rB;rest;stiffness;constructor(e,t,i,r,o,n,a=-1){super(e,t,i),this.rA=C(r[0],r[1],r[2]),this.rB=C(o[0],o[1],o[2]),this.stiffness=n,this.rest=a,this.rest<0&&(Ft(ai,t.positionLin,t.positionAng,this.rA),Ft(ar,i.positionLin,i.positionAng,this.rB),this.rest=ir($(be,ai,ar)))}initialize(){return!0}updatePrimal(e,t,i){let r=this.bodyA,o=this.bodyB;Ft(ai,r.positionLin,r.positionAng,this.rA),Ft(ar,o.positionLin,o.positionAng,this.rB);let n=$(be,ai,ar),a=ir(n);if(a<=1e-6)return;let l=_t(Me,n,a),c=a-this.rest,u=this.stiffness*c,f=ai,d=ar;e===r?(ae(ye,r.positionAng,this.rA),f.set(l),Dt(d,ye,l)):(ae(ye,o.positionAng,this.rB),ee(f,l),ee(d,Dt(d,ye,l))),Ne(i.lhsLin,Li(ct,fs(ct,f,f),this.stiffness)),Ne(i.lhsAng,Li(ct,fs(ct,d,d),this.stiffness)),Ne(i.lhsCross,Li(ct,fs(ct,d,f),this.stiffness)),D(i.rhsLin,i.rhsLin,ne(ye,f,u),1),D(i.rhsAng,i.rhsAng,ne(ye,d,u),1)}updateDual(){}},Ls=class extends Gt{initialize(){return!0}updatePrimal(){}updateDual(){}};var qe=40,lr=0,Gr=4,Ua=8,qa=12,hn=16,pn=20,Va=24,Wa=28,mn=32,gn=36,pe=32,li=0,Nr=4,cr=8,ur=12,Ha=16,Ja=20,ci=24,fr=28,Ut=8;var _s=4,Ht=16,dr=0,Es=4,Ms=8,zs=12,Ur=2147483648,hr=6,$c=7,$a=0,bn=1,yn=2,Is=3,qr=1,pr=2,vn=1,An=2,xn=4,wn=8,Bn=16,Xc=.1,Xa=.002,Ya=.002,Yc=.05,Cn=44,Ae=`
|
|
536
536
|
// Shared by every 3D module (avbd3d/gpu/layout.ts)
|
|
537
|
-
${
|
|
538
|
-
const T_JOINT = ${
|
|
539
|
-
const T_SPRING = ${
|
|
537
|
+
${ln}
|
|
538
|
+
const T_JOINT = ${qr};
|
|
539
|
+
const T_SPRING = ${pr};
|
|
540
540
|
|
|
541
541
|
const PENALTY_MIN = 1.0;
|
|
542
542
|
const PENALTY_MAX = 1e10;
|
|
@@ -545,22 +545,22 @@ 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 = ${
|
|
550
|
-
const SHAPE_HULL = ${
|
|
551
|
-
const STICK_BIT = ${
|
|
552
|
-
const C_MANIFOLDS = ${
|
|
553
|
-
const C_PREV_MANIFOLDS = ${
|
|
548
|
+
const SHAPE_SPHERE = ${bn}.0;
|
|
549
|
+
const SHAPE_SAIL = ${yn}.0;
|
|
550
|
+
const SHAPE_HULL = ${Is}.0;
|
|
551
|
+
const STICK_BIT = ${Ur}u;
|
|
552
|
+
const C_MANIFOLDS = ${hr}u;
|
|
553
|
+
const C_PREV_MANIFOLDS = ${$c}u;
|
|
554
554
|
/** Feature key of every contact involving a sphere (one contact per pair). */
|
|
555
555
|
const SPHERE_FEATURE = 3u << 24u;
|
|
556
556
|
|
|
557
|
-
const FLAG_MATCH_NEAREST = ${
|
|
558
|
-
const FLAG_FACE_BIAS = ${
|
|
559
|
-
const FLAG_REUSE_CONTACTS = ${
|
|
560
|
-
const FLAG_START_AT_REST = ${
|
|
561
|
-
const FLAG_MASS_PENALTY = ${
|
|
562
|
-
const REST_SPEED = ${
|
|
563
|
-
const NEAREST_FRACTION = ${
|
|
557
|
+
const FLAG_MATCH_NEAREST = ${vn}u;
|
|
558
|
+
const FLAG_FACE_BIAS = ${An}u;
|
|
559
|
+
const FLAG_REUSE_CONTACTS = ${xn}u;
|
|
560
|
+
const FLAG_START_AT_REST = ${wn}u;
|
|
561
|
+
const FLAG_MASS_PENALTY = ${Bn}u;
|
|
562
|
+
const REST_SPEED = ${Xc};
|
|
563
|
+
const NEAREST_FRACTION = ${Yc};
|
|
564
564
|
|
|
565
565
|
struct Body {
|
|
566
566
|
pos: vec4f, // xyz, w: friction
|
|
@@ -678,7 +678,7 @@ fn orthonormal(n: vec3f) -> mat3x3f {
|
|
|
678
678
|
// Columns of the WGSL matrix are the basis rows
|
|
679
679
|
return mat3x3f(n, t1, cross(n, t1));
|
|
680
680
|
}
|
|
681
|
-
`,
|
|
681
|
+
`,mr=`
|
|
682
682
|
alias TopoItem = Manifold;
|
|
683
683
|
|
|
684
684
|
/** A body pair has one manifold, so the other body alone orders a body's contacts. */
|
|
@@ -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
|
+
`,Sn=`
|
|
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
|
-
`,
|
|
714
|
+
`,Qa={counter:"C_MANIFOLDS",prevCounter:"C_PREV_MANIFOLDS",capacity:"params.manifoldCapacity",colorThreads:Sn};var Ka=`
|
|
715
715
|
const MAX_CLIP = 64u;
|
|
716
716
|
const NO_HULL = 0xffffffffu;
|
|
717
717
|
|
|
@@ -1095,8 +1095,8 @@ fn sphereHull(c: vec3f, r: f32, X: Poly) -> SphereBoxHit {
|
|
|
1095
1095
|
out.onSphere = c - n * r;
|
|
1096
1096
|
return out;
|
|
1097
1097
|
}
|
|
1098
|
-
`;var
|
|
1099
|
-
${
|
|
1098
|
+
`;var Rs=`
|
|
1099
|
+
${Ae}
|
|
1100
1100
|
|
|
1101
1101
|
struct RefPose {
|
|
1102
1102
|
pos: vec4f,
|
|
@@ -1116,15 +1116,15 @@ fn updateRefs(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
1116
1116
|
let last = refs[i];
|
|
1117
1117
|
// A zero quaternion marks a body without a reference yet
|
|
1118
1118
|
let moved = dot(last.rot, last.rot) == 0.0
|
|
1119
|
-
|| length(pos - last.pos.xyz) > ${
|
|
1120
|
-
|| length(qsub(rot, last.rot)) > ${
|
|
1119
|
+
|| length(pos - last.pos.xyz) > ${Xa}
|
|
1120
|
+
|| length(qsub(rot, last.rot)) > ${Ya};
|
|
1121
1121
|
if (moved) {
|
|
1122
1122
|
refs[i] = RefPose(vec4f(pos, 0.0), rot);
|
|
1123
1123
|
bodies[i].inertialPos.w = bitcast<f32>(params.step);
|
|
1124
1124
|
}
|
|
1125
1125
|
}
|
|
1126
|
-
`,
|
|
1127
|
-
${
|
|
1126
|
+
`,ui=`
|
|
1127
|
+
${Ae}
|
|
1128
1128
|
|
|
1129
1129
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
1130
1130
|
@group(0) @binding(1) var<storage, read> bodies: array<Body>;
|
|
@@ -1331,8 +1331,8 @@ fn findPairs(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
1331
1331
|
testPair(P, l);
|
|
1332
1332
|
}
|
|
1333
1333
|
}
|
|
1334
|
-
`,
|
|
1335
|
-
${
|
|
1334
|
+
`,Ti=s=>`
|
|
1335
|
+
${Ae}
|
|
1336
1336
|
|
|
1337
1337
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
1338
1338
|
@group(0) @binding(1) var<storage, read> bodies: array<Body>;
|
|
@@ -1343,7 +1343,7 @@ ${me}
|
|
|
1343
1343
|
@group(0) @binding(6) var<storage, read> prevManifolds: array<Manifold>;
|
|
1344
1344
|
@group(0) @binding(7) var<storage, read_write> table: array<atomic<u32>>;
|
|
1345
1345
|
@group(0) @binding(8) var<storage, read_write> counters: array<atomic<u32>>;
|
|
1346
|
-
${
|
|
1346
|
+
${s?"@group(0) @binding(9) var<storage, read> hulls: array<vec4u>;":""}
|
|
1347
1347
|
|
|
1348
1348
|
fn pairHash(a: u32, b: u32) -> u32 {
|
|
1349
1349
|
return hash32((a * 0x9e3779b1u) ^ hash32(b));
|
|
@@ -1747,7 +1747,7 @@ fn reuseContacts(a: u32, b: u32) -> bool {
|
|
|
1747
1747
|
return true;
|
|
1748
1748
|
}
|
|
1749
1749
|
|
|
1750
|
-
${
|
|
1750
|
+
${s?Ka:"fn isHull(i: u32) -> bool { return false; }"}
|
|
1751
1751
|
|
|
1752
1752
|
@compute @workgroup_size(64)
|
|
1753
1753
|
fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
@@ -1764,7 +1764,7 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
1764
1764
|
let sphereB = bodies[b].angVel.w == SHAPE_SPHERE;
|
|
1765
1765
|
if (sphereA && sphereB) {
|
|
1766
1766
|
found = sphereSphere(A, B, &sat);
|
|
1767
|
-
}${
|
|
1767
|
+
}${s?` else if (isHull(a) || isHull(b)) {
|
|
1768
1768
|
if (sphereA || sphereB) {
|
|
1769
1769
|
// sat.n points from A towards B
|
|
1770
1770
|
if (sphereA) {
|
|
@@ -1876,9 +1876,9 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
1876
1876
|
contacts[base + i] = Contact(rA, found.feature[i] | stick, rB, c0.x, pen, c0.y, lam, c0.z);
|
|
1877
1877
|
}
|
|
1878
1878
|
}
|
|
1879
|
-
`,
|
|
1880
|
-
${
|
|
1881
|
-
${
|
|
1879
|
+
`,Za=Ti(!1),el=Ti(!0);var ze=`
|
|
1880
|
+
${Ae}
|
|
1881
|
+
${Sn}
|
|
1882
1882
|
|
|
1883
1883
|
// Per-dispatch constants, selected with a dynamic uniform offset
|
|
1884
1884
|
struct PassConstants {
|
|
@@ -2418,36 +2418,36 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
2418
2418
|
bodies[i].vel.w = prevUp;
|
|
2419
2419
|
}
|
|
2420
2420
|
}
|
|
2421
|
-
`;function Wr(o,e){let t=0,i=o.length-1;for(;t<i;){let r=o[e],s=t,a=i;do{for(;o[s]<r;)s++;for(;r<o[a];)a--;if(s<=a){let n=o[s];o[s]=o[a],o[a]=n,s++,a--}}while(s<=a);a<e&&(t=s),e<s&&(i=a)}return o[e]}function bo(o){let e=o.length,t=e>0?Wr(o,e>>1):1,i=0;for(let r=0;r<e;r++)o[r]<=2*t&&(i=Math.max(i,o[r]));return e>64&&(i=Math.max(i,Wr(o,e-64-1))),i}var wt=o=>o===1/0?3e38:o===-1/0?-3e38:o,He=o=>Math.ceil(o/64),Ha=o=>2**Math.ceil(Math.log2(Math.max(o,2))),xl=o=>Math.max(o,1)*Ce*4,Al=8,Jr=3,Bl=8,wl=1.6,Sl=.8,Cl=2,kl=[0,0,1],Pl=()=>({...Ys(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function Ll(o,e){let t=o.vertices.length/3,i=o.faces.length,r=o.faces.reduce((m,p)=>m+p.verts.length,0),s=Math.ceil(r/4),a=o.edges.length,n=new Float32Array((2+t+2*i+s+a)*4),l=new Uint32Array(n.buffer),c=e+2,f=c+t,u=f+2*i,d=u+s;l.set([c,t,f,i,d,a,u,0],0);for(let m=0;m<t;m++)n.set(o.vertices.subarray(m*3,m*3+3),(2+m)*4);let h=0;return o.faces.forEach((m,p)=>{let b=(2+t+2*p)*4;n.set(m.normal,b),n[b+3]=m.d,l[b+4]=h,l[b+5]=m.verts.length;for(let y of m.verts)l[(2+t+2*i)*4+h++]=y}),o.edges.forEach((m,p)=>l.set(m,(2+t+2*i+s+p)*4)),n}var go=.001;function _l(o,e){let t=o.length,i=o.map(b=>[0,1,2].map(y=>{let A=[0,0,0];return A[y]=1,ce(L(),b.positionAng,A)})),r=o.map((b,y)=>{if(tr(b))return[b.radius,b.radius,b.radius];let A=[0,0,0];for(let w=0;w<3;w++)for(let g=0;g<3;g++)A[g]+=b.size[w]*.5*Math.abs(i[y][w][g]);return A}),s=bo(Float64Array.from(o,b=>b.radius)),a=Math.max(2*s,.001),n=(b,y)=>{let A=o[b],w=o[y];if(A.mass<=0&&w.mass<=0)return!1;let g=[0,1,2].map(v=>A.positionLin[v]-w.positionLin[v]),x=A.radius+w.radius;return g[0]*g[0]+g[1]*g[1]+g[2]*g[2]>x*x?!1:g.every((v,B)=>Math.abs(v)<=r[b][B]+r[y][B])},l=(b,y)=>b[0]*y[0]+b[1]*y[1]+b[2]*y[2],c=(b,y)=>{let A=o[b],w=o[y],g=[0,1,2].map(k=>w.positionLin[k]-A.positionLin[k]),[x,v]=[tr(A),tr(w)];if(x&&v)return Math.hypot(g[0],g[1],g[2])<=A.radius+w.radius+go;if(x||v){let[k,z,M]=x?[y,A.radius,-1]:[b,w.radius,1],P=0;for(let _=0;_<3;_++){let S=Math.abs(M*l(g,i[k][_]))-o[k].size[_]*.5;S>0&&(P+=S*S)}return P<=(z+go)**2}let B=(k,z)=>o[k].size[0]*.5*Math.abs(l(i[k][0],z))+o[k].size[1]*.5*Math.abs(l(i[k][1],z))+o[k].size[2]*.5*Math.abs(l(i[k][2],z)),E=k=>{let z=Math.hypot(k[0],k[1],k[2]);return z<1e-6?!1:Math.abs(l(g,k))-B(b,k)-B(y,k)>go*z};for(let k=0;k<3;k++)if(E(i[b][k])||E(i[y][k]))return!1;for(let k of i[b])for(let z of i[y])if(E([k[1]*z[2]-k[2]*z[1],k[2]*z[0]-k[0]*z[2],k[0]*z[1]-k[1]*z[0]]))return!1;return!0},f=new Set,u=new Map,d=(b,y,A)=>(b*73856093^y*19349663^A*83492791)>>>0,h=b=>[0,1,2].map(y=>Math.floor(o[b].positionLin[y]/a));for(let b=0;b<t;b++){if(o[b].radius>s){f.add(b);continue}let[y,A,w]=h(b),g=d(y,A,w),x=u.get(g);x?x.push(b):u.set(g,[b])}let m=0,p=0;for(let b=0;b<t;b++){if(f.has(b))continue;let[y,A,w]=h(b);for(let g=-1;g<=1;g++)for(let x=-1;x<=1;x++)for(let v=-1;v<=1;v++)for(let B of u.get(d(y+v,A+x,w+g))??[])B<b&&n(b,B)&&(m++,c(b,B)&&(p++,e[b]++,e[B]++))}for(let b of f)for(let y=0;y<t;y++)y!==b&&(!f.has(y)||y<b)&&n(b,y)&&(m++,c(b,y)&&(p++,e[b]++,e[y]++));return{pairs:m,touching:p}}function El(o){let e=[1/0,1/0,1/0],t=[-1/0,-1/0,-1/0];for(let s of o)for(let a=0;a<3;a++)e[a]=Math.min(e[a],s.positionLin[a]),t[a]=Math.max(t[a],s.positionLin[a]);let i=s=>{let a=s&1023;return a=(a|a<<16)&50331903,a=(a|a<<8)&50393103,a=(a|a<<4)&51130563,a=(a|a<<2)&153391689,a},r=o.map(s=>{let a=[0,1,2].map(n=>Math.min(1023,Math.floor((s.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 o.map((s,a)=>a).sort((s,a)=>r[s]-r[a]||s-a)}var fe=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,$r=class{device;params=Pl();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:s,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:f}=t;if(Object.assign(this.params,{dt:r,gravity:s,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:f,up:kl}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(Ce*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:xl(this.bodyCapacity),usage:fe()}),this.filterBuffer=e.createBuffer({label:"collision filters",size:Math.max(this.bodyCapacity,1)*8,usage:fe()}),e.queue.writeBuffer(this.filterBuffer,0,new Uint32Array(Math.max(this.bodyCapacity,1)*2).fill(4294967295));let u=this.bodyCapacity;this.tableSize=Ha(2*u),this.gridBuffer=e.createBuffer({label:"grid 3d",size:(2*this.tableSize+1+4*u)*4,usage:fe()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:fe()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:fe()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(u/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*u+65+64*this.colorGroups+1)*4,usage:fe()}),this.paramsBuffer=e.createBuffer({label:"params 3d",size:po*4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.refBuffer=e.createBuffer({label:"reference poses",size:u*32,usage:fe()}),this.hulls=(i.hulls??!0)&&e.limits.maxStorageBuffersPerShaderStage>=9,this.hullBuffer=e.createBuffer({label:"hulls",size:this.hullCapacity*16,usage:fe()}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(u).fill(255));let d=2*ue.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 De(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new De(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1);let h=i.spatialSort===!1?t.bodies.map((v,B)=>B):El(t.bodies);this.refToGpu=new Int32Array(h.length),h.forEach((v,B)=>this.refToGpu[v]=B);let m=new Map(t.bodies.map((v,B)=>[v,this.refToGpu[B]])),p=t.forces.filter(v=>v instanceof Ae||v instanceof ct);for(let v of t.forces){if(!(v instanceof Fr))continue;let B=m.get(v.bodyA),E=m.get(v.bodyB);this.pendingEntries.push([Math.max(B,E),Math.min(B,E),4294967295])}this.allocateJoints(Math.max(p.length+256,i.capacity?.joints??0));for(let v of p)this.writeJoint(this.jointCount++,v,m);let b=new Int32Array(t.bodies.length),{pairs:y,touching:A}=_l(t.bodies,b),w=new Map(t.bodies.map((v,B)=>[v,B]));for(let v of p)v.bodyA&&b[w.get(v.bodyA)]++,b[w.get(v.bodyB)]++;let g=0;t.bodies.forEach((v,B)=>{v.mass>0&&(g=Math.max(g,b[B]))}),this.colorCap=Math.min(64,Math.max(Bl,g+1+Jr,i.capacity?.colors??0));let x=i.capacity??{};this.allocateContacts(Math.max(8192,4*u,Al*A,x.contacts??0),Math.max(4096,4*u,2*y,x.pairs??0),Math.max(4096,u,wl*A,x.manifolds??0)),this.writeBodies(0,h.map(v=>t.bodies[v]))}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",s="uniform";return{broad:t("broadphase 3d",[s,i,r,r,r,i,i,i]),contacts:t("contacts 3d",[s,i,i,r,i,r,i,r,r,...this.hulls?[i]:[]]),topo:t("topology 3d",[s,i,i,i,i,r,r,r]),solve:t("solve 3d",[s,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",[s,i,i,r]),refs:t("refs 3d",[s,r,r])}}createPipelines(e){let t=this.device,i=(s,a,n)=>{let l=t.createShaderModule({code:s}),c=t.createPipelineLayout({bindGroupLayouts:a});for(let f of n)this.pipes[f]=t.createComputePipeline({label:f,layout:c,compute:{module:l,entryPoint:f}})},r=this.layouts;i(jt,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(Na,[r.refs],["updateRefs"]),i(e.contacts??Ua,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:s=>i(s,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(ti(me,Ti),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??ke,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(Ks(me,Ga),[r.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints 3d",size:Math.max(e,1)*te*4,usage:fe()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:fe()});if(this.jointBuffer){let a=t.createCommandEncoder();a.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*te*4),a.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([a.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}let s=new Int32Array(e*4);s.set(this.info.subarray(0,Math.min(this.info.length,s.length))),this.info=s,this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t,i){let r=this.device,s=r.limits.maxStorageBufferBindingSize,a=Math.min(Math.ceil(e),Math.floor(s/(Bt*4))),n=Math.min(Math.ceil(t),Math.floor(s/8)),l=Math.min(Math.ceil(i),n,Math.floor(s/(bt*4))),c={contacts:this.contactBuffers,manifolds:this.manifoldBuffers,contactCapacity:this.contactCapacity,manifoldCapacity:this.manifoldCapacity};for(let f of[this.pairBuffer,this.tableBuffer])f?.destroy();if(this.contactCapacity=a,this.pairCapacity=n,this.manifoldCapacity=l,this.hashSize=Ha(2*l),this.pairBuffer=r.createBuffer({label:"pairs",size:n*8,usage:fe()}),this.tableBuffer=r.createBuffer({label:"manifold hash",size:this.hashSize*4,usage:fe()}),this.contactBuffers=[0,1].map(f=>r.createBuffer({label:`contacts 3d ${f}`,size:a*Bt*4,usage:fe()})),this.manifoldBuffers=[0,1].map(f=>r.createBuffer({label:`manifolds 3d ${f}`,size:l*bt*4,usage:fe()})),c.contacts){let f=r.createCommandEncoder(),u=Math.min(c.contactCapacity,a)*Bt*4,d=Math.min(c.manifoldCapacity,l)*bt*4;c.contacts.forEach((h,m)=>f.copyBufferToBuffer(h,0,this.contactBuffers[m],0,u)),c.manifolds.forEach((h,m)=>f.copyBufferToBuffer(h,0,this.manifoldBuffers[m],0,d)),r.queue.submit([f.finish()]),[...c.contacts,...c.manifolds].forEach(h=>h.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:fe()}),this.adjScan?.destroy(),this.adjScan=new De(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:fe()});let i=(d,h)=>e.createBindGroup({layout:d,entries:h.map((m,p)=>({binding:p,resource:{buffer:m}}))}),r=this.paramsBuffer,[s,a]=this.contactBuffers,[n,l]=this.manifoldBuffers,c=this.adjBuffer,f=this.tableBuffer,u=this.counterBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,u,this.staticBuffer,this.jointBuffer,this.filterBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,s,a,n,l,f,u,...this.hulls?[this.hullBuffer]:[]]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,s,l,n,f,u,...this.hulls?[this.hullBuffer]:[]])],topo:[n,l].map(d=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,d,u,c,this.colorBuffer])),solve:[0,1].map(d=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,[s,a][d],c,this.colorBuffer,u,[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)*Ce);t.forEach((r,s)=>{let a=this.pack(r,i,s*Ce,this.bodies[e+s]?.hull);this.bodies[e+s]?.radius!==a.radius&&(this.radiiDirty=!0),this.bodies[e+s]=a}),this.device.queue.writeBuffer(this.bodyBuffer,e*Ce*4,i,0,t.length*Ce)}rewriteFixed(e,t,i,r,s){if(!e.length)return;let a=new Float32Array(Ce),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 f=c+1;for(;f<e.length&&e[f]===e[f-1]+1;)f++;let u=new Float32Array((f-c)*Ce);for(let d=c;d<f;d++){let h=(d-c)*Ce;u.set(a,h);for(let p=0;p<3;p++)u[h+Ei+p]=t[3*d+p];if(s)for(let p=0;p<4;p++)u[h+sr+p]=s[4*d+p];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]*Ce*4,u),this.device.queue.writeBuffer(this.colorBuffer,e[c]*4,new Uint32Array(f-c).fill(255)),c=f}}pack(e,t,i,r){{t.set(e.positionLin,i+Ei),t[i+Ei+3]=e.friction,t.set(e.positionAng,i+sr),t.set(e.size,i+io),t[i+io+3]=e.mass,t.set(e.moment,i+ro),t[i+ro+3]=e.radius,t.set(e.initialLin,i+Ea),t.set(e.initialAng,i+za),t.set(e.inertialLin,i+Ma),t.set(e.inertialAng,i+Ia),t.set(e.velocityLin,i+so);let s=this.params.up;t[i+so+3]=e.prevVelocityLin[0]*s[0]+e.prevVelocityLin[1]*s[1]+e.prevVelocityLin[2]*s[2],t.set(e.velocityAng,i+oo);let a=tr(e),n=this.hulls?_a(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+oo+3]=a?ao:l>=0?Hr+l:Pa(e)?no:ja,{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(te),s=t.bodyA?i.get(t.bodyA):-1,a=i.get(t.bodyB);t instanceof Ae?(r.set(t.penaltyLin,Rt),r[Rt+3]=wt(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,or),r[or+3]=wt(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,zi),r[zi+3]=wt(t.fracture),r.set(t.lambdaAng,Mi),r[Mi+3]=t.torqueArm,r.set(t.C0Lin,Ra),r.set(t.C0Ang,Oa),r.set(t.rA,Ot),r.set(t.rB,Ii)):(r[Rt+3]=t.stiffness,r.set(t.rA,Ot),r[Ot+3]=t.rest,r.set(t.rB,Ii)),this.info.set([t instanceof Ae?nr:ji,s,a,0],e*4),this.device.queue.writeBuffer(this.jointBuffer,e*te*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info,e*4,4),this.noCollide(s,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,s=new Float64Array(r);for(let c=0;c<r;c++)s[c]=this.bodies[c].radius;let a=bo(s),n=1/0;for(let c=0;c<r;c++)s[c]>a&&(n=Math.min(n,s[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],s=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<s.length||c<a.length;)c>=a.length||l<s.length&&r(s[l],a[c])<=0?n.push(s[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,s)=>i.set(r,e.length+3*s)),this.staticBuffer.size<i.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:i.byteLength,usage:fe()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,i)}seedFrom(e){if(this.refToGpu.some((m,p)=>m!==p))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((m,p)=>[m,p]));this.writeBodies(0,e.bodies);let i=0;for(let m of e.forces)(m instanceof Ae||m instanceof ct)&&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 ki),s=r.reduce((m,p)=>m+p.contacts.length,0);if(s>this.contactCapacity||r.length>this.manifoldCapacity)throw new Error("seedFrom: too many contacts");let a=new ArrayBuffer(Math.max(s,1)*Bt*4),n=new Uint32Array(a),l=new Float32Array(a),c=new ArrayBuffer(Math.max(r.length,1)*bt*4),f=new Uint32Array(c),u=new Float32Array(c),d=0;r.forEach((m,p)=>{f.set([t.get(m.bodyA),t.get(m.bodyB),d,m.contacts.length],p*bt),u.set([m.basis[0],m.basis[1],m.basis[2],m.friction],p*bt+Nr);for(let b of m.contacts){let y=d++*Bt;l.set(b.rA,y+Ri),n[y+Ri+3]=b.feature>>>0|(b.stick?ar:0),l.set([...b.rB,b.C0[0]],y+Ur),l.set([...b.penalty,b.C0[1]],y+qr),l.set([...b.lambda,b.C0[2]],y+Vr)}}),this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,a),this.device.queue.writeBuffer(this.manifoldBuffers[1-this.parity],0,c);let h=new Uint32Array(16);h[1]=s,h[Oi]=r.length,this.device.queue.writeBuffer(this.counterBuffer,0,h)}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(qa),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,s=this.hullFree.findIndex(a=>a.size>=i);if(s>=0){let a=this.hullFree[s];r=a.offset,a.size===i?this.hullFree.splice(s,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+Hr>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,Ll(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,s)=>r.offset-s.offset);let i=[];for(let r of this.hullFree){let s=i[i.length-1];s&&s.offset+s.size===r.offset?s.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:fe()}),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 De(this.device,this.adjBuffer,0,this.bodyCount+1),i}appendJoint(e,t,i,r,s,a){let n=this.jointCount;n>=this.jointCapacity&&this.allocateJoints(this.jointCapacity*2),this.jointCount++;let l=new Float32Array(te);l[Rt+3]=wt(s),l[or+3]=wt(a),l[zi+3]=3e38,l.set([i[0],i[1],i[2]],Ot),l.set([r[0],r[1],r[2]],Ii);let c=e>=0?this.bodies[e].size:[0,0,0],f=this.bodies[t].size;return l[Mi+3]=(c[0]+f[0])**2+(c[1]+f[1])**2+(c[2]+f[2])**2,this.info.set([nr,e,t,0],n*4),this.device.queue.writeBuffer(this.jointBuffer,n*te*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,s)=>{let{a,b:n,rA:l,rB:c,angular:f}=e[r];s[Rt+3]=3e38,s[or+3]=f===void 0?3e38:wt(f),s[zi+3]=i?-wt(t):wt(t),s.set([l[0],l[1],l[2]],Ot),s.set([c[0],c[1],c[2]],Ii);let[u,d]=[this.bodies[a].size,this.bodies[n].size];return s[Mi+3]=(u[0]+d[0])**2+(u[1]+d[1])**2+(u[2]+d[2])**2,[nr,a,n]})}appendSprings(e){return this.appendConstraints(e.length,(t,i)=>{let{a:r,b:s,rA:a,rB:n,stiffness:l,rest:c}=e[t];return i[Rt+3]=wt(l),i.set([a[0],a[1],a[2],c],Ot),i.set([n[0],n[1],n[2]],Ii),[ji,r,s]})}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,s=this.jointCapacity;for(;this.jointCount+r>s;)s*=2;s>this.jointCapacity&&this.allocateJoints(s);for(let n=0;n<r;n++)i.push(this.jointCount++);let a=new Float32Array(e*te);for(let n=0;n<e;n++){let[l,c,f]=t(n,a.subarray(n*te,(n+1)*te));this.info.set([l,c,f,0],i[n]*4),this.releasedSlots.delete(i[n]),this.noCollide(c,f,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]*te*4,a,n*te,(l-n)*te),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,s)=>r-s);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 s=r+1;for(;s<t.length&&t[s]===t[s-1]+1;)s++;i.length<(s-r)*te&&(i=new Float32Array((s-r)*te)),this.device.queue.writeBuffer(this.jointBuffer,t[r]*te*4,i,0,(s-r)*te),this.device.queue.writeBuffer(this.infoBuffer,t[r]*16,this.info,t[r]*4,(s-r)*4),r=s}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 s=r+1;for(;s<e.length&&e[s]===e[s-1]+1;)s++;let a=new Uint32Array(2*(s-r));for(let n=r;n<s;n++)a.set([t[n]>>>0,i[n]>>>0],2*(n-r));this.device.queue.writeBuffer(this.filterBuffer,e[r]*8,a),r=s}}setWorldAnchor(e,t){this.device.queue.writeBuffer(this.jointBuffer,(e*te+Ot)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*te+Rt)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+zi)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+Mi)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(po*4),r=new Float32Array(i),s=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,s[6]=(e.matchNearest?lo:0)|(e.faceBias?co:0)|(e.reuseContacts?uo:0)|(e.startAtRest?fo:0)|(e.massPenalty?ho:0),s[7]=this.bodyCount,s[8]=this.jointCount,s[9]=this.colorCap,r[10]=this.cellSize,s[11]=this.tableSize-1,r[12]=this.maxSmallRadius,s[13]=this.largeCount,s[14]=this.noCollideCount,s[15]=this.pairCapacity,s[16]=this.contactCapacity,s[17]=this.hashSize-1,s[18]=t+1,s[19]=2*t+1,s[20]=this.tableSize+1,s[21]=2*this.tableSize+1,s[22]=2*this.tableSize+1+t,s[23]=t,s[24]=this.colorHistOffset,s[25]=2*t,s[26]=this.colorGroups,s[27]=2*t+65,s[28]=this.colorRounds,s[29]=this.manifoldCapacity,s[30]=this.stepCount;let a=n=>Math.min(31,Math.max(0,Math.round(Math.log2(n))));s[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 s=new ArrayBuffer(r*256),a=new Uint32Array(s),n=new Float32Array(s);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let f=(l*i+c)*256/4;a[f]=c,n[f+1]=t}this.device.queue.writeBuffer(this.passBuffer,0,s)}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(A=>A===255?0:A+1))),this.stepCount++,this.writeParams(),this.writePassConstants(e.iterations,e.alpha);let t=this.parity,i=this.bodyCapacity,r=this.bodyCount,s=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,f=n||this.splitPasses,u=()=>{l&&!f||(l?.end(),l=a.beginComputePass({label:f?ue[c]:"avbd3d step",timestampWrites:n?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*c,endOfPassWriteIndex:2*c+1}:void 0}),c++)},d=this.groups,h=(A,w,g)=>{g<=0||(l.setPipeline(this.pipes[A]),l.setBindGroup(0,w),l.dispatchWorkgroups(g))},m=(A,w,g)=>{l.setPipeline(this.pipes[A]),l.setBindGroup(0,w),l.dispatchWorkgroupsIndirect(this.argsBuffer,g*4)};if(u(),h("beginFrame",d.broad,1),e.reuseContacts&&h("updateRefs",d.refs,He(r)),h("argsPrev",d.args,1),m("hashInsert",d.contacts[t],6),h("gridCount",d.broad,He(r)),this.gridScan.encode(l),h("gridScatter",d.broad,He(r)),h("findPairs",d.broad,He(r)),h("argsPairs",d.args,1),m("narrowphase",d.contacts[t],0),h("argsContacts",d.args,1),u(),h("degreeJoints",d.topo[t],He(s)),m("degreeContacts",d.topo[t],3),this.adjScan.encode(l),h("fillJoints",d.topo[t],He(s)),m("fillContacts",d.topo[t],3),h("sortAdjacency",d.topo[t],He(r)),u(),!this.fixedColors){h("colorCompact",d.topo[t],He(r)),h("colorMark",d.topo[t],He(r));for(let A=0;A<this.colorRounds;A++)h(A%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],He(r))}h("colorCount",d.topo[t],this.colorGroups),this.colorHistScan.encode(l),h("colorStarts",d.topo[t],1),h("argsColors",d.args,1),h("colorScatter",d.topo[t],this.colorGroups),u();let p=this.colorCap+1,b=A=>l.setBindGroup(1,this.passGroup,[A*256]);l.setBindGroup(0,d.solve[t]),b(0),s>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups(He(s))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups(He(r));for(let A=0;A<e.iterations;A++){l.setPipeline(this.pipes.primal);for(let w=0;w<this.colorCap;w++)b(A*p+w),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*w)*4);b(A*p+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups(He(r)),l.end();let y=2*ue.length;if(n){let{querySet:A,resolve:w,read:g}=this.timing;a.resolveQuerySet(A,0,y,w,0),a.copyBufferToBuffer(w,0,g,0,y*8)}if(this.device.queue.submit([a.finish()]),this.parity=1-t,n){let A=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let w=this.timing.read;w.mapAsync(GPUMapMode.READ).then(()=>{let g=new BigUint64Array(w.getMappedRange()).slice();if(w.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let x=(B,E)=>Number(g[E]-g[B])/1e6,v={total:x(0,y-1)};ue.forEach((B,E)=>v[B]=x(2*E,2*E+1)),A(v)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*Ce*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*te*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],manifolds:e[Oi],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,[s,a]=await Promise.all([this.read(this.manifoldBuffers[r],t*bt*4),this.read(this.contactBuffers[r],i*Bt*4)]),n=new Uint32Array(s),l=new Float32Array(s),c=new Uint32Array(a),f=new Float32Array(a),u=[];for(let d=0;d<t;d++){let h=d*bt,m=[...l.subarray(h+Nr,h+Nr+3)];for(let p=n[h+2];p<n[h+2]+(n[h+3]&15);p++){let b=p*Bt,y=c[b+Ri+3];u.push({a:n[h],b:n[h+1],feature:(y&~ar)>>>0,stick:(y&ar)!==0,rA:[...f.subarray(b+Ri,b+Ri+3)],rB:[...f.subarray(b+Ur,b+Ur+3)],pen:[...f.subarray(b+qr,b+qr+3)],lam:[...f.subarray(b+Vr,b+Vr+3)],normal:m})}}return u}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+Jr+2,this.colorCap+4)),this.shrinkVotes=0):t+Jr+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+Jr),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(n,l,c)=>(e.overflow&c)!==0||n>Sl*l?Math.max(Cl*n,1.25*l):l,r=i(e.contacts,this.contactCapacity,2),s=i(e.pairs,this.pairCapacity,1),a=i(e.manifolds,this.manifoldCapacity,4);this.capacityCanGrow(r,s,a)&&this.allocateContacts(r,s,a)}capacityCanGrow(e,t,i){let r=this.device.limits.maxStorageBufferBindingSize;return Math.min(e,r/(Bt*4))>this.contactCapacity||Math.min(t,r/8)>this.pairCapacity||Math.min(i,t,r/(bt*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 s=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),s}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 Wa=` e.C = vec3f(
|
|
2421
|
+
`;function Os(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 kn(s){let e=s.length,t=e>0?Os(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,Os(s,e-64-1))),i}var Jt=s=>s===1/0?3e38:s===-1/0?-3e38:s,ft=s=>Math.ceil(s/64),il=s=>2**Math.ceil(Math.log2(Math.max(s,2))),Qc=s=>Math.max(s,1)*qe*4,Kc=8,Ts=3,Zc=8,eu=1.6,tu=.8,iu=2,ru=[0,0,1],su=()=>({...an(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function ou(s,e){let t=s.vertices.length/3,i=s.faces.length,r=s.faces.reduce((g,h)=>g+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 g=0;g<t;g++)a.set(s.vertices.subarray(g*3,g*3+3),(2+g)*4);let p=0;return s.faces.forEach((g,h)=>{let m=(2+t+2*h)*4;a.set(g.normal,m),a[m+3]=g.d,l[m+4]=p,l[m+5]=g.verts.length;for(let b of g.verts)l[(2+t+2*i)*4+p++]=b}),s.edges.forEach((g,h)=>l.set(g,(2+t+2*i+o+h)*4)),a}var Pn=.001;function nu(s,e){let t=s.length,i=s.map(m=>[0,1,2].map(b=>{let v=[0,0,0];return v[b]=1,ae(C(),m.positionAng,v)})),r=s.map((m,b)=>{if(De(m))return[m.radius,m.radius,m.radius];let v=[0,0,0];for(let w=0;w<3;w++)for(let y=0;y<3;y++)v[y]+=m.size[w]*.5*Math.abs(i[b][w][y]);return v}),o=kn(Float64Array.from(s,m=>m.radius)),n=Math.max(2*o,.001),a=(m,b)=>{let v=s[m],w=s[b];if(v.mass<=0&&w.mass<=0)return!1;let y=[0,1,2].map(x=>v.positionLin[x]-w.positionLin[x]),A=v.radius+w.radius;return y[0]*y[0]+y[1]*y[1]+y[2]*y[2]>A*A?!1:y.every((x,B)=>Math.abs(x)<=r[m][B]+r[b][B])},l=(m,b)=>m[0]*b[0]+m[1]*b[1]+m[2]*b[2],c=(m,b)=>{let v=s[m],w=s[b],y=[0,1,2].map(L=>w.positionLin[L]-v.positionLin[L]),[A,x]=[De(v),De(w)];if(A&&x)return Math.hypot(y[0],y[1],y[2])<=v.radius+w.radius+Pn;if(A||x){let[L,M,z]=A?[b,v.radius,-1]:[m,w.radius,1],_=0;for(let k=0;k<3;k++){let S=Math.abs(z*l(y,i[L][k]))-s[L].size[k]*.5;S>0&&(_+=S*S)}return _<=(M+Pn)**2}let B=(L,M)=>s[L].size[0]*.5*Math.abs(l(i[L][0],M))+s[L].size[1]*.5*Math.abs(l(i[L][1],M))+s[L].size[2]*.5*Math.abs(l(i[L][2],M)),E=L=>{let M=Math.hypot(L[0],L[1],L[2]);return M<1e-6?!1:Math.abs(l(y,L))-B(m,L)-B(b,L)>Pn*M};for(let L=0;L<3;L++)if(E(i[m][L])||E(i[b][L]))return!1;for(let L of i[m])for(let M of i[b])if(E([L[1]*M[2]-L[2]*M[1],L[2]*M[0]-L[0]*M[2],L[0]*M[1]-L[1]*M[0]]))return!1;return!0},u=new Set,f=new Map,d=(m,b,v)=>(m*73856093^b*19349663^v*83492791)>>>0,p=m=>[0,1,2].map(b=>Math.floor(s[m].positionLin[b]/n));for(let m=0;m<t;m++){if(s[m].radius>o){u.add(m);continue}let[b,v,w]=p(m),y=d(b,v,w),A=f.get(y);A?A.push(m):f.set(y,[m])}let g=0,h=0;for(let m=0;m<t;m++){if(u.has(m))continue;let[b,v,w]=p(m);for(let y=-1;y<=1;y++)for(let A=-1;A<=1;A++)for(let x=-1;x<=1;x++)for(let B of f.get(d(b+x,v+A,w+y))??[])B<m&&a(m,B)&&(g++,c(m,B)&&(h++,e[m]++,e[B]++))}for(let m of u)for(let b=0;b<t;b++)b!==m&&(!u.has(b)||b<m)&&a(m,b)&&(g++,c(m,b)&&(h++,e[m]++,e[b]++));return{pairs:g,touching:h}}function au(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,js=class{device;params=su();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:ru}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(qe*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:Qc(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=il(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:Cn*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((x,B)=>B):au(t.bodies);this.refToGpu=new Int32Array(p.length),p.forEach((x,B)=>this.refToGpu[x]=B);let g=new Map(t.bodies.map((x,B)=>[x,this.refToGpu[B]])),h=t.forces.filter(x=>x instanceof ke||x instanceof ut);for(let x of t.forces){if(!(x instanceof Ls))continue;let B=g.get(x.bodyA),E=g.get(x.bodyB);this.pendingEntries.push([Math.max(B,E),Math.min(B,E),4294967295])}this.allocateJoints(Math.max(h.length+256,i.capacity?.joints??0));for(let x of h)this.writeJoint(this.jointCount++,x,g);let m=new Int32Array(t.bodies.length),{pairs:b,touching:v}=nu(t.bodies,m),w=new Map(t.bodies.map((x,B)=>[x,B]));for(let x of h)x.bodyA&&m[w.get(x.bodyA)]++,m[w.get(x.bodyB)]++;let y=0;t.bodies.forEach((x,B)=>{x.mass>0&&(y=Math.max(y,m[B]))}),this.colorCap=Math.min(64,Math.max(Zc,y+1+Ts,i.capacity?.colors??0));let A=i.capacity??{};this.allocateContacts(Math.max(8192,4*f,Kc*v,A.contacts??0),Math.max(4096,4*f,2*b,A.pairs??0),Math.max(4096,f,eu*v,A.manifolds??0)),this.writeBodies(0,p.map(x=>t.bodies[x]))}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(ui,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(Rs,[r.refs],["updateRefs"]),i(e.contacts??Za,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:o=>i(o,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(Ri(Ae,mr),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??ze,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(cn(Ae,Qa),[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/(Ut*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=il(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*Ut*4,usage:Pe()})),c.contacts){let u=r.createCommandEncoder(),f=Math.min(c.contactCapacity,n)*Ht*4,d=Math.min(c.manifoldCapacity,l)*Ut*4;c.contacts.forEach((p,g)=>u.copyBufferToBuffer(p,0,this.contactBuffers[g],0,f)),c.manifolds.forEach((p,g)=>u.copyBufferToBuffer(p,0,this.manifoldBuffers[g],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((g,h)=>({binding:h,resource:{buffer:g}}))}),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)*qe);t.forEach((r,o)=>{let n=this.pack(r,i,o*qe,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*qe*4,i,0,t.length*qe)}rewriteFixed(e,t,i,r,o){if(!e.length)return;let n=new Float32Array(qe),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)*qe);for(let d=c;d<u;d++){let p=(d-c)*qe;f.set(n,p);for(let h=0;h<3;h++)f[p+lr+h]=t[3*d+h];if(o)for(let h=0;h<4;h++)f[p+Gr+h]=o[4*d+h];let g=this.bodies[e[d]];g?.radius!==l.radius&&(this.radiiDirty=!0),g?.hull&&this.releaseHull(g.hull),this.bodies[e[d]]={...l,size:[...l.size]}}this.device.queue.writeBuffer(this.bodyBuffer,e[c]*qe*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+lr),t[i+lr+3]=e.friction,t.set(e.positionAng,i+Gr),t.set(e.size,i+hn),t[i+hn+3]=e.mass,t.set(e.moment,i+pn),t[i+pn+3]=e.radius,t.set(e.initialLin,i+Ua),t.set(e.initialAng,i+qa),t.set(e.inertialLin,i+Va),t.set(e.inertialAng,i+Wa),t.set(e.velocityLin,i+mn);let o=this.params.up;t[i+mn+3]=e.prevVelocityLin[0]*o[0]+e.prevVelocityLin[1]*o[1]+e.prevVelocityLin[2]*o[2],t.set(e.velocityAng,i+gn);let n=De(e),a=this.hulls?wt(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+gn+3]=n?bn:l>=0?Is+l:sr(e)?yn:$a,{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,li),r[li+3]=Jt(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,Nr),r[Nr+3]=Jt(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,cr),r[cr+3]=Jt(t.fracture),r.set(t.lambdaAng,ur),r[ur+3]=t.torqueArm,r.set(t.C0Lin,Ha),r.set(t.C0Ang,Ja),r.set(t.rA,ci),r.set(t.rB,fr)):(r[li+3]=t.stiffness,r.set(t.rA,ci),r[ci+3]=t.rest,r.set(t.rB,fr)),this.info.set([t instanceof ke?qr:pr,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=kn(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((g,h)=>g!==h))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((g,h)=>[g,h]));this.writeBodies(0,e.bodies);let i=0;for(let g of e.forces)(g instanceof ke||g instanceof ut)&&this.writeJoint(i++,g,t);if(i!==this.jointCount)throw new Error("seedFrom: joints differ from the reference");let r=e.forces.filter(g=>g instanceof oi),o=r.reduce((g,h)=>g+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)*Ut*4),u=new Uint32Array(c),f=new Float32Array(c),d=0;r.forEach((g,h)=>{u.set([t.get(g.bodyA),t.get(g.bodyB),d,g.contacts.length],h*Ut),f.set([g.basis[0],g.basis[1],g.basis[2],g.friction],h*Ut+_s);for(let m of g.contacts){let b=d++*Ht;l.set(m.rA,b+dr),a[b+dr+3]=m.feature>>>0|(m.stick?Ur:0),l.set([...m.rB,m.C0[0]],b+Es),l.set([...m.penalty,m.C0[1]],b+Ms),l.set([...m.lambda,m.C0[2]],b+zs)}}),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[hr]=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(el),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+Is>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,ou(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[li+3]=Jt(o),l[Nr+3]=Jt(n),l[cr+3]=3e38,l.set([i[0],i[1],i[2]],ci),l.set([r[0],r[1],r[2]],fr);let c=e>=0?this.bodies[e].size:[0,0,0],u=this.bodies[t].size;return l[ur+3]=(c[0]+u[0])**2+(c[1]+u[1])**2+(c[2]+u[2])**2,this.info.set([qr,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[li+3]=3e38,o[Nr+3]=u===void 0?3e38:Jt(u),o[cr+3]=i?-Jt(t):Jt(t),o.set([l[0],l[1],l[2]],ci),o.set([c[0],c[1],c[2]],fr);let[f,d]=[this.bodies[n].size,this.bodies[a].size];return o[ur+3]=(f[0]+d[0])**2+(f[1]+d[1])**2+(f[2]+d[2])**2,[qr,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[li+3]=Jt(l),i.set([n[0],n[1],n[2],c],ci),i.set([a[0],a[1],a[2]],fr),[pr,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+ci)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*pe+li)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+cr)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+ur)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(Cn*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?vn:0)|(e.faceBias?An:0)|(e.reuseContacts?xn:0)|(e.startAtRest?wn:0)|(e.massPenalty?Bn: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(v=>v===255?0:v+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=(v,w,y)=>{y<=0||(l.setPipeline(this.pipes[v]),l.setBindGroup(0,w),l.dispatchWorkgroups(y))},g=(v,w,y)=>{l.setPipeline(this.pipes[v]),l.setBindGroup(0,w),l.dispatchWorkgroupsIndirect(this.argsBuffer,y*4)};if(f(),p("beginFrame",d.broad,1),e.reuseContacts&&p("updateRefs",d.refs,ft(r)),p("argsPrev",d.args,1),g("hashInsert",d.contacts[t],6),p("gridCount",d.broad,ft(r)),this.gridScan.encode(l),p("gridScatter",d.broad,ft(r)),p("findPairs",d.broad,ft(r)),p("argsPairs",d.args,1),g("narrowphase",d.contacts[t],0),p("argsContacts",d.args,1),f(),p("degreeJoints",d.topo[t],ft(o)),g("degreeContacts",d.topo[t],3),this.adjScan.encode(l),p("fillJoints",d.topo[t],ft(o)),g("fillContacts",d.topo[t],3),p("sortAdjacency",d.topo[t],ft(r)),f(),!this.fixedColors){p("colorCompact",d.topo[t],ft(r)),p("colorMark",d.topo[t],ft(r));for(let v=0;v<this.colorRounds;v++)p(v%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],ft(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,m=v=>l.setBindGroup(1,this.passGroup,[v*256]);l.setBindGroup(0,d.solve[t]),m(0),o>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups(ft(o))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups(ft(r));for(let v=0;v<e.iterations;v++){l.setPipeline(this.pipes.primal);for(let w=0;w<this.colorCap;w++)m(v*h+w),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*w)*4);m(v*h+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups(ft(r)),l.end();let b=2*Se.length;if(a){let{querySet:v,resolve:w,read:y}=this.timing;n.resolveQuerySet(v,0,b,w,0),n.copyBufferToBuffer(w,0,y,0,b*8)}if(this.device.queue.submit([n.finish()]),this.parity=1-t,a){let v=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let w=this.timing.read;w.mapAsync(GPUMapMode.READ).then(()=>{let y=new BigUint64Array(w.getMappedRange()).slice();if(w.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let A=(B,E)=>Number(y[E]-y[B])/1e6,x={total:A(0,b-1)};Se.forEach((B,E)=>x[B]=A(2*E,2*E+1)),v(x)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*qe*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[hr],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*Ut*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*Ut,g=[...l.subarray(p+_s,p+_s+3)];for(let h=a[p+2];h<a[p+2]+(a[p+3]&15);h++){let m=h*Ht,b=c[m+dr+3];f.push({a:a[p],b:a[p+1],feature:(b&~Ur)>>>0,stick:(b&Ur)!==0,rA:[...u.subarray(m+dr,m+dr+3)],rB:[...u.subarray(m+Es,m+Es+3)],pen:[...u.subarray(m+Ms,m+Ms+3)],lam:[...u.subarray(m+zs,m+zs+3)],normal:g})}}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+Ts+2,this.colorCap+4)),this.shrinkVotes=0):t+Ts+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+Ts),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(a,l,c)=>(e.overflow&c)!==0||a>tu*l?Math.max(iu*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/(Ut*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 rl=` 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
|
-
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(
|
|
2425
|
-
e.C = vec3f(k.c0x, k.c0y, k.c0z) * keep + vec3f(dot(n, delta), dot(t1, delta), dot(t2, delta));`);function
|
|
2426
|
-
var e = begin;`);return
|
|
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(ze.split(rl).length!==2)throw Error("Pinned contact evaluation changed; review the app kernel variant");var Wr=ze.replace(rl,` let delta = A.dLin - B.dLin + cross(A.dAng, e.rAW) - cross(B.dAng, e.rBW);
|
|
2425
|
+
e.C = vec3f(k.c0x, k.c0y, k.c0z) * keep + vec3f(dot(n, delta), dot(t1, delta), dot(t2, delta));`);function St(s,e,t){if(s.split(e).length!==2)throw Error("Pinned solve layout changed; review the app kernel variant");return s.replace(e,t)}var Ds=(()=>{let s=St(Wr," var e = adj[i] + lane;",` let begin = adj[i];
|
|
2426
|
+
var e = begin;`);return s=St(s,` if (c == cEnd) {
|
|
2427
2427
|
`,` if (c >= cEnd) {
|
|
2428
|
-
`),
|
|
2428
|
+
`),s=St(s,` e += lanes;
|
|
2429
2429
|
if (id < params.jointCount) {
|
|
2430
2430
|
addJoint(&acc, id, pc.alpha, i);`,` e++;
|
|
2431
2431
|
if (id < params.jointCount) {
|
|
2432
2432
|
if ((e - begin - 1u) % lanes != lane) { continue; }
|
|
2433
|
-
addJoint(&acc, id, pc.alpha, i);`),
|
|
2433
|
+
addJoint(&acc, id, pc.alpha, i);`),s=St(s," isA = i == mf.ids.x;",` c = mf.ids.z + lane;
|
|
2434
2434
|
cEnd = mf.ids.z + pairCount(mf);
|
|
2435
2435
|
if (c >= cEnd) { continue; }
|
|
2436
|
-
isA = i == mf.ids.x;`),
|
|
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; }`,""),
|
|
2438
|
+
if (c == cEnd) { continue; }`,""),s=St(s," c++;"," c += lanes;"),s})();function Rn(s=ze){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
|
-
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) {`;
|
|
2444
|
-
if (!solverStepValid()) { return; }`)}return
|
|
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 On(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),
|
|
2448
2448
|
restTarget.w * rest.w - dot(restTarget.xyz, rest.xyz));
|
|
2449
2449
|
}
|
|
2450
|
-
return qsub(restTarget, bodies[b].rot) * k.lamAng.w;`),
|
|
2450
|
+
return qsub(restTarget, bodies[b].rot) * k.lamAng.w;`),s=St(s,"k.c0Lin = vec4f(jointLinC(k, a, b), 0.0);","k.c0Lin = vec4f(jointLinC(k, a, b), k.c0Lin.w);"),St(s,"k.c0Ang = vec4f(jointAngC(k, a, b), 0.0);","k.c0Ang = vec4f(jointAngC(k, a, b), k.c0Ang.w);")}var sl=`// Capture new constraints from live GPU poses; no stale mesh poses or readback.
|
|
2451
2451
|
struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
2452
2452
|
struct Joint { penLin:vec4f,penAng:vec4f,lamLin:vec4f,lamAng:vec4f,c0Lin:vec4f,c0Ang:vec4f,rA:vec4f,rB:vec4f }
|
|
2453
2453
|
@group(0) @binding(0) var<storage,read> bodies:array<Body>;
|
|
@@ -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
|
-
`;var
|
|
2475
|
+
`;var ol=`// 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 }
|
|
@@ -2522,13 +2522,13 @@ fn impact(a:u32,b:u32) {
|
|
|
2522
2522
|
}
|
|
2523
2523
|
if(states[i].asleep!=0u){atomicAdd(&globals[1],1u);}
|
|
2524
2524
|
}
|
|
2525
|
-
`;var
|
|
2525
|
+
`;var Tn=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,n=0,a=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 g=1;g<u;g++)if(f[g]<f[g-1]){d=!1;break}d||f.sort((g,h)=>g-h);let p=1;for(let g=1;g<u;g++)f[g]!==f[g-1]+1&&p++;if(p<=4||u<=8){for(let g=0;g<u;g++)c.data[g]=c.pending.get(f[g]);for(let g=0;g<u;){let h=g+1;for(;h<u&&f[h]===f[h-1]+1;)h++;t.queue.writeBuffer(l,f[g]*4,c.data.buffer,g*4,(h-g)*4),r++,g=h}a+=u*4}else{let g=u*8,h=Math.min(t.limits.maxBufferSize,t.limits.maxStorageBufferBindingSize);if(g>h)throw Error("Property batch exceeds GPU buffer limits");for(let w=0;w<u;w++)c.data[w*2]=f[w],c.data[w*2+1]=c.pending.get(f[w]);(!c.upload||c.upload.size<g)&&(c.upload?.destroy(),c.upload=t.createBuffer({size:Math.min(h,g*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),c.group=null),t.queue.writeBuffer(c.upload,0,c.data.buffer,0,g),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"applyEdits",module:t.createShaderModule({code:`
|
|
2526
2526
|
@group(0) @binding(0) var<storage,read_write> outputWords:array<u32>;
|
|
2527
2527
|
@group(0) @binding(1) var<storage,read> edits:array<vec2u>;
|
|
2528
2528
|
@compute @workgroup_size(64) fn applyEdits(@builtin(global_invocation_id) id:vec3u){
|
|
2529
2529
|
let i=id.x+id.y*${t.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
2530
2530
|
if(i>=arrayLength(&edits)){return;}let edit=edits[i];outputWords[edit.x]=edit.y;
|
|
2531
|
-
}`})}}),(!c.group||c.bytes!==
|
|
2531
|
+
}`})}}),(!c.group||c.bytes!==g)&&(c.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}},{binding:1,resource:{buffer:c.upload,size:g}}]}),c.bytes=g),i??=t.createCommandEncoder({label:"AVBD property edits"});let m=i.beginComputePass();m.setPipeline(this.pipeline),m.setBindGroup(0,c.group);let b=Math.ceil(u/64),v=t.limits.maxComputeWorkgroupsPerDimension;m.dispatchWorkgroups(Math.min(b,v),Math.ceil(b/v)),m.end(),r++,o++,a+=g}n+=u,c.pending.clear()}!e&&i&&t.queue.submit([i.finish()]),n&&(this.lastBatch={words:n,writes:r,dispatches:o,uploadedBytes:a,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 le(s){return s.propertyEdits??=new Tn(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,!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:ol});this.layout=i.createBindGroupLayout({entries:["storage","storage","read-only-storage","read-only-storage","read-only-storage","storage","uniform"].map((c,u)=>({binding:u,visibility:GPUShaderStage.COMPUTE,buffer:{type:c}}))});let n=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipelines=Object.fromEntries(["before","clear","contacts","joints","rest"].map(c=>[c,i.createComputePipeline({layout:n,compute:{module:o,entryPoint:c}})]));let a=new Float32Array(e.capacity*8),l=new Uint32Array(a.buffer);for(let c of e.aggregates)if(!c.disposed){let u=(c.gpuSlot??e.gpu.gpuIndex(c.index))*8;a[u]=c.rigid.mass,l[u+4]=c.allowSleep===!1||c.rigid.allowSleep===!1?0:1}i.queue.writeBuffer(this.state,0,a),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;let o=new Uint32Array(r.buffer),n=le(this.world.gpu);for(let a=0;a<8;a++)n.uint(this.state,e*8+a,o[a]);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],r=this.groups?.find(o=>o.buffers.every((n,a)=>n===i[a]));r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((o,n)=>({binding:n,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=t.contactStorage,r=new ArrayBuffer(48),o=new Uint32Array(r),n=new Float32Array(r);o.set([t.bodyCount,t.jointCount,t.manifoldCapacity,this.wakeRequested?1:0]),n.set([this.speed,this.delay,t.params.dt,t.params.gravity],4),n.set(t.params.up,8),this.device.queue.writeBuffer(this.params,0,r),this.wakeRequested=!1,this.bindContacts(),this.run([["before",t.bodyCount],["clear",1]],e)}after(e){let t=this.world.gpu,i=t.contactStorage;this.bindContacts(),this.run([["contacts",t.manifoldCapacity],["joints",t.jointCount],["rest",t.bodyCount]],e)}run(e,t){let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();r.setBindGroup(0,this.group);for(let[o,n]of e)n&&(r.setPipeline(this.pipelines[o]),r.dispatchWorkgroups(o==="clear"?1:Math.ceil(n/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()}};var jn=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 gr(s,e){let t=It(e);Number.isFinite(t.breakStrain)||Number.isFinite(t.breakForce)?jn.set(s,t):jn.delete(s)}var Gi=s=>jn.get(s);function Fs(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}
|
|
2532
2532
|
if (info[j].x == T_SPRING) {
|
|
2533
2533
|
var material = joints[j];
|
|
2534
2534
|
let strainLimit = material.lamLin.w;
|
|
@@ -2550,7 +2550,7 @@ fn impact(a:u32,b:u32) {
|
|
|
2550
2550
|
}
|
|
2551
2551
|
}
|
|
2552
2552
|
}
|
|
2553
|
-
`)}var
|
|
2553
|
+
`)}var uu=["auto","standard","optimized","points"];function br({requested:s="auto",bodyCount:e,constraintCount:t=0,contactScheduling:i="manifolds",custom:r=!1}){if(!uu.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",n=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:n,bodyCount:e,constraintCount:t}}var Gs=`// Portable hierarchical PLOC: Morton ordering and independent SAH treelets.
|
|
2554
2554
|
// Dispatch boundaries publish each hierarchy level; no cross-workgroup spin waits.
|
|
2555
2555
|
struct Node {
|
|
2556
2556
|
lo: vec3f,
|
|
@@ -2807,7 +2807,7 @@ fn ropes(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
2807
2807
|
}
|
|
2808
2808
|
links[build.escapeOffset + id] = escape;
|
|
2809
2809
|
}\r
|
|
2810
|
-
`;var
|
|
2810
|
+
`;var nl=`// Stable 4-bit GPU radix sort; PrefixScan scans digit-major block counts.
|
|
2811
2811
|
struct SortSettings { count: u32, blocks: u32, shift: u32, _pad: u32 }
|
|
2812
2812
|
@group(0) @binding(0) var<uniform> sort: SortSettings;
|
|
2813
2813
|
@group(0) @binding(1) var<storage, read> input: array<vec2u>;
|
|
@@ -2855,10 +2855,10 @@ fn scatterDigits(
|
|
|
2855
2855
|
}
|
|
2856
2856
|
output[hist[digit * sort.blocks + wid.x] + rank] = value;
|
|
2857
2857
|
}\r
|
|
2858
|
-
`;var
|
|
2859
|
-
fn gridCount`),
|
|
2860
|
-
fn findPairs`);if(
|
|
2861
|
-
${
|
|
2858
|
+
`;var hu=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,Rt=(s,e=128)=>Math.max(1,Math.ceil(s/e)),pu=4294967295,Dn=ui.indexOf(`@compute @workgroup_size(64)
|
|
2859
|
+
fn gridCount`),Fn=ui.indexOf("/** Binary search"),al=ui.indexOf(`@compute @workgroup_size(64)
|
|
2860
|
+
fn findPairs`);if(Dn<0||Fn<Dn||al<Fn)throw Error("Pinned broadphase changed; review H-PLOC integration");var mu=Gs.slice(0,Gs.indexOf("@group(0)")),gu=(ui.slice(0,Dn)+ui.slice(Fn,al)).replace("var<storage, read_write> grid: array<atomic<u32>>;","var<storage, read_write> tree: array<Node>;")+`
|
|
2861
|
+
${mu}
|
|
2862
2862
|
@group(1) @binding(0) var<uniform> build: BuildSettings;
|
|
2863
2863
|
@group(1) @binding(1) var<storage,read> links: array<u32>;
|
|
2864
2864
|
@compute @workgroup_size(64)
|
|
@@ -2880,11 +2880,11 @@ fn findTreePairs(@builtin(global_invocation_id) gid:vec3u) {
|
|
|
2880
2880
|
at=links[build.escapeOffset+at];
|
|
2881
2881
|
} else {at=node.left;}
|
|
2882
2882
|
}
|
|
2883
|
-
}`,
|
|
2883
|
+
}`,Ns=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=hu())=>{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 n=e.createShaderModule({label:"portable H-PLOC",code:Ae+Gs+`
|
|
2884
2884
|
@compute @workgroup_size(128)
|
|
2885
2885
|
fn initializeLinks(@builtin(global_invocation_id) gid:vec3u){if(gid.x<build.escapeOffset){links[gid.x]=0xffffffffu;}}
|
|
2886
|
-
`}),
|
|
2887
|
-
for (var i = lo + 1u; i < hi; i++) {`,
|
|
2886
|
+
`}),a=e.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["leaves","reduceBounds","mortonCodes","seedClusters","miniHploc","refitTreelets","ropes","initializeLinks"].map(u=>[u,e.createComputePipeline({layout:a,compute:{module:n,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:gu}),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,n=e,a=e===0?pu:0;for(;i>1;){let d=Math.ceil(i/16);t.push({inputCount:i,nodeBase:r,inputOffset:o,outputOffset:n,blocks:d}),d===1&&(a=r+i-2),r+=d*15,o=n,n+=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,a,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,m)=>({binding:m,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 g=this.uniform([e,this.sortBlocks,p*4,0]);return this.device.createBindGroup({layout:this.sortLayout,entries:[g,this.keys[p%2],this.keys[1-p%2],this.hist].map((h,m)=>({binding:m,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,n)=>{e.setPipeline(this.pipes[r]),e.setBindGroup(0,o),e.dispatchWorkgroups(n)};t("leaves",this.base.group,Rt(this.count));let i=!this.built||this.stepsSinceBuild>=this.rebuildInterval;if(this.lastRebuilt=i,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 ll(s,e={}){let t,i,r=!0,o=new Map,n=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},a=(l,c)=>{n(!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){a("dispatchWorkgroups",l)},dispatchWorkgroupsIndirect(...l){a("dispatchWorkgroupsIndirect",l)},end(){n(!0)}}}var dt=s=>Math.ceil(s/64);function cl(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(x=>x===255?0:x+1))),s.stepCount++,s.writeParams(),s.writePassConstants(e.iterations,e.alpha);let t=s.parity,i=s.bodyCapacity,r=s.bodyCount,o=s.jointCount,n=s.groups,a=s.device.createCommandEncoder({label:"AVBD app step"});s.encodeStepPrelude?.(a),s.bvh||a.clearBuffer(s.gridBuffer,0,(2*s.tableSize+1)*4),a.clearBuffer(s.tableBuffer),a.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,g,h=(x,B)=>{if(p&&!l&&(g===B||!u))return;p?.end(),g=B;let E=l?d.length:Se.indexOf(B);d.push({name:x,aggregate:B});let L={label:x,timestampWrites:c?{querySet:f.querySet,beginningOfPassWriteIndex:E*2,endOfPassWriteIndex:E*2+1}:void 0};p=s.dispatchIsolation?ll(a,L):a.beginComputePass(L)},m=(x,B,E)=>{E<=0||(p.setPipeline(s.pipes[x]),p.setBindGroup(0,B),p.dispatchWorkgroups(E))},b=(x,B,E)=>{p.setPipeline(s.pipes[x]),p.setBindGroup(0,B),p.dispatchWorkgroupsIndirect(s.argsBuffer,E*4)};if(h("contactCache","collision"),m("beginFrame",n.broad,1),e.reuseContacts&&m("updateRefs",n.refs,dt(r)),m("argsPrev",n.args,1),b("hashInsert",n.contacts[t],6),h("broadphaseBuild","collision"),s.bvh?(s.bvh.configure(r),s.bvh.encodeBuild(p)):(m("gridCount",n.broad,dt(r)),s.gridScan.encode(p),m("gridScatter",n.broad,dt(r))),h("broadphasePairs","collision"),s.bvh?s.bvh.encodePairs(p,s):m("findPairs",n.broad,dt(r)),h("narrowphase","collision"),m("argsPairs",n.args,1),b("narrowphase",n.contacts[t],0),m("argsContacts",n.args,1),h("adjacency","adjacency"),m("degreeJoints",n.topo[t],dt(o)),b("degreeContacts",n.topo[t],3),s.adjScan.encode(p),m("fillJoints",n.topo[t],dt(o)),b("fillContacts",n.topo[t],3),m("sortAdjacency",n.topo[t],dt(r)),h("coloring","coloring"),!s.fixedColors){m("colorCompact",n.topo[t],dt(r)),m("colorMark",n.topo[t],dt(r));for(let x=0;x<s.colorRounds;x++)m(x%2===0?"colorRoundAB":"colorRoundBA",n.topo[t],dt(r))}m("colorCount",n.topo[t],s.colorGroups),s.colorHistScan.encode(p),m("colorStarts",n.topo[t],1),m("argsColors",n.args,1),m("colorScatter",n.topo[t],s.colorGroups);let v=x=>p.setBindGroup(1,s.passGroup,[x*256]),w=()=>{p.setBindGroup(0,n.solve[t]),v(0)};h("warmStart","solve"),w(),o>0&&(p.setPipeline(s.pipes.warmStartJoints),p.dispatchWorkgroups(dt(o))),p.setPipeline(s.pipes.warmStartBodies),r&&p.dispatchWorkgroups(dt(r));for(let x=0;x<e.iterations;x++){h("bodySolve","solve"),w(),p.setPipeline(s.pipes.primal);for(let B=0;B<s.colorCap;B++)v(x*(s.colorCap+1)+B),p.dispatchWorkgroupsIndirect(s.argsBuffer,(12+3*B)*4);h("contactUpdate","solve"),w(),v(x*(s.colorCap+1)+s.colorCap),p.setPipeline(s.pipes.dual),p.dispatchWorkgroupsIndirect(s.argsBuffer,36)}h("velocities","solve"),w(),p.setPipeline(s.pipes.updateVelocities),r&&p.dispatchWorkgroups(dt(r)),p.end();let y=2*(l?d.length:Se.length);if(c&&(a.resolveQuerySet(f.querySet,0,y,f.resolve,0),a.copyBufferToBuffer(f.resolve,0,f.read,0,y*8)),s.parity=1-t,s.encodeStepPostlude?.(a),s.device.queue.submit([a.finish()]),!c)return;let A=l?s.detailCallback:s.timingCallback;l?s.detailCallback=null:s.timingCallback=null,s.timingBusy=!0,f.read.mapAsync(GPUMapMode.READ).then(()=>{let x=new BigUint64Array(f.read.getMappedRange()).slice();if(f.read.unmap(),s.timingBusy=!1,s.destroyed){s.releaseAppTiming(),s.releaseTiming();return}let B=(L,M)=>Number(x[M]-x[L])/1e6,E={total:B(0,y-1),broadphase:s.broadphase,bvhRebuilt:s.bvh?.lastRebuilt??!1};if(l){E.details={};for(let[L,M]of d.entries()){let z=B(L*2,L*2+1);E[M.aggregate]=(E[M.aggregate]??0)+z,E.details[M.name]=(E.details[M.name]??0)+z}}else Se.forEach((L,M)=>E[L]=B(M*2,M*2+1));A(E)},x=>{s.timingBusy=!1,s.profileErrors.push(x.message),s.destroyed&&(s.releaseAppTiming(),s.releaseTiming())})}var ul=` let list = params.adjListOffset;
|
|
2887
|
+
for (var i = lo + 1u; i < hi; i++) {`,fl=Ri(Ae,mr);if(fl.split(ul).length!==2)throw Error("Pinned adjacency sort changed; review the cached-key variant");var dl=fl.replace(ul,`
|
|
2888
2888
|
let list = params.adjListOffset;
|
|
2889
2889
|
let count = hi - lo;
|
|
2890
2890
|
if (count <= 16u) {
|
|
@@ -2913,13 +2913,23 @@ fn initializeLinks(@builtin(global_invocation_id) gid:vec3u){if(gid.x<build.esca
|
|
|
2913
2913
|
}
|
|
2914
2914
|
return;
|
|
2915
2915
|
}
|
|
2916
|
-
for (var i = lo + 1u; i < hi; i++) {`);function
|
|
2917
|
-
if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function
|
|
2916
|
+
for (var i = lo + 1u; i < hi; i++) {`);function hl(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 Gn(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 pl(s=Ri(Ae,mr)){return s.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
|
|
2917
|
+
if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function ml(s){let e=s.device,t=e.createShaderModule({code:`
|
|
2918
2918
|
@group(0) @binding(0) var<storage,read_write> records:array<vec4f>;
|
|
2919
2919
|
@compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){
|
|
2920
2920
|
if(id.x*10u+2u>=arrayLength(&records)){return;}
|
|
2921
2921
|
records[id.x*10u+2u].w=0.;
|
|
2922
|
-
}`}),i=e.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"clear"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:
|
|
2922
|
+
}`}),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(),n=o.beginComputePass();n.setPipeline(i),n.setBindGroup(0,r),n.dispatchWorkgroups(Math.ceil(s.bodyCount/64)),n.end(),e.queue.submit([o.finish()])}var bu=`
|
|
2923
|
+
fn contactGeneration(value: f32) -> u32 {
|
|
2924
|
+
return select(0u, bitcast<u32>(value) ^ 0x3f800000u, value != 0.0);
|
|
2925
|
+
}
|
|
2926
|
+
fn contactMovedAfter(value: f32, generated: u32) -> bool {
|
|
2927
|
+
let age = (contactGeneration(value) - generated) & 0x07ffffffu;
|
|
2928
|
+
return age != 0u && age < 0x04000000u;
|
|
2929
|
+
}
|
|
2930
|
+
`;function gl(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 Nn(s){let e=` let moved = max(bitcast<u32>(bodies[a].inertialPos.w), bitcast<u32>(bodies[b].inertialPos.w));
|
|
2931
|
+
if (count == 0u || generated < moved) { return false; }`;if(!s.includes(e))throw Error("Contact cache layout changed");return bu+s.replace(e,` let generation = generated & 0x07ffffffu;
|
|
2932
|
+
if (count == 0u || contactMovedAfter(bodies[a].inertialPos.w, generation) || contactMovedAfter(bodies[b].inertialPos.w, generation)) { return false; }`)}var yu=`${Ae}
|
|
2923
2933
|
struct Config { count:u32, joints:u32, capacity:u32, list:u32, threshold:f32, dt:f32, pad0:u32, pad1:u32 }
|
|
2924
2934
|
struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
|
|
2925
2935
|
struct Bounce { impulse:f32, delta:f32, reboundSpeed:f32, inverseMass:f32, rA:vec4f, rB:vec4f }
|
|
@@ -2995,7 +3005,7 @@ fn pointVelocity(i:u32,r:vec3f,incoming:bool)->vec3f {
|
|
|
2995
3005
|
let i=id.x;if(i>=cfg.count || bodies[i].size.w<=0.){return;}
|
|
2996
3006
|
bodies[i].vel=velocities[i].linear;bodies[i].angVel=velocities[i].angular;
|
|
2997
3007
|
}
|
|
2998
|
-
|
|
3008
|
+
`;var yr=class{constructor(e,{code:t=yu,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 n=r.createShaderModule({code:t}),a=r.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["captureIncoming","captureSolved","initialize","pairs","apply","writeBack"].map(l=>[l,r.createComputePipeline({layout:a,compute:{module:n,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),le(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((a,l)=>a!==this.buffers[l]))&&(this.buffers=o,this.group=t.createBindGroup({layout:this.layout,entries:o.map((a,l)=>({binding:l,resource:{buffer:a}}))}));let n=new Uint32Array([e.bodyCount,e.jointCount,r,2*e.bodyCapacity+1,0,0,0,0]);new Float32Array(n.buffer).set([1,e.params.dt],4),t.queue.writeBuffer(this.params,0,n)}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(["captureIncoming"],e)}after(e){this.active.size&&this.run(["captureSolved","initialize",...Array.from({length:12},()=>["pairs","apply"]).flat(),"writeBack"],e)}destroy(){for(let e of["materials","velocities","params","bounce"])this[e]?.destroy()}};var bl=new WeakMap,Hr=s=>bl.get(s);function vr(s,e){bl.set(s,e)}var Un=(s,e,t)=>{if(s.split(e).length!==2)throw Error("Pinned angular solver layout changed");return s.replace(e,t)};function qn(s){return s=Un(s,"fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {",`
|
|
2999
3009
|
fn angularFrame(k:Joint,a:i32,b:i32)->mat3x3f {
|
|
3000
3010
|
let qa=rotA(a);let axis=qrotate(qa,k.rA.xyz);
|
|
3001
3011
|
let ta=qrotate(qa,k.c0Lin.xyz);let tb=qrotate(bodies[b].rot,k.c0Ang.xyz);
|
|
@@ -3046,10 +3056,10 @@ fn dualAngular(j:u32){
|
|
|
3046
3056
|
}
|
|
3047
3057
|
`+`
|
|
3048
3058
|
fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {
|
|
3049
|
-
if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),
|
|
3059
|
+
if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),s=Un(s," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if(j>=params.jointCount){return;}
|
|
3050
3060
|
if(info[j].x==3 || info[j].x==4){warmAngular(j);return;}
|
|
3051
|
-
if(info[j].x!=T_JOINT){return;}`),
|
|
3052
|
-
if(info[j].x==3 || info[j].x==4){dualAngular(j);return;}`)}function
|
|
3061
|
+
if(info[j].x!=T_JOINT){return;}`),Un(s,"fn dualJoint(j: u32) {",`fn dualJoint(j: u32) {
|
|
3062
|
+
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:n=Math.PI}={}){let a=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(n)||o<-Math.PI||n>Math.PI||o>n)throw Error("Hinge angles must be ordered within -PI..PI radians");return{type:s,axisA:a(e),axisB:a(t),speed:i,maxTorque:r,minAngle:o,maxAngle:n}}var yl=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
3053
3063
|
struct Joint {penLin:vec4f,penAng:vec4f,lamLin:vec4f,lamAng:vec4f,c0Lin:vec4f,c0Ang:vec4f,rA:vec4f,rB:vec4f}
|
|
3054
3064
|
@group(0) @binding(0) var<storage,read> bodies:array<Body>;
|
|
3055
3065
|
@group(0) @binding(1) var<storage,read_write> joints:array<Joint>;
|
|
@@ -3067,7 +3077,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f{let t=2.*cross(q.xyz,v);return v+q.w*t+cross(q
|
|
|
3067
3077
|
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));}
|
|
3068
3078
|
joints[j].c0Lin=vec4f(tangent,0.);joints[j].c0Ang=vec4f(normalize(tangentB),0.);
|
|
3069
3079
|
}
|
|
3070
|
-
`;var
|
|
3080
|
+
`;var vl=new WeakSet,He=s=>vl.has(s);function Ar(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 n=new Ze(s,[2*e,t,2*e],i,r,o),a=t-2*e,l=Math.PI*e*e*a*i,c=4/3*Math.PI*e**3*i;n.mass=l+c;let u=l*e**2/2+c*2*e**2/5,f=l*(a**2/12+e**2/4)+c*(2*e**2/5+a**2/4+3*a*e/8);return n.moment.set([f,u,f]),n.radius=t/2,vl.add(n),n}var Al=`// Exact rounded capsule geometry. Shape -1: radius=size.x/2, segment along Y,
|
|
3071
3081
|
// half-length=size.y/2-radius. No tessellation, endpoint-only collision test,
|
|
3072
3082
|
// shape storage buffer or iterative support search for capsule/sphere/box.
|
|
3073
3083
|
struct Capsule {a:vec3f,b:vec3f,r:f32}
|
|
@@ -3118,7 +3128,12 @@ fn capSegmentBox(a:vec3f,b:vec3f,h:vec3f)->SegmentPair{
|
|
|
3118
3128
|
let lo=breaks[j];let hi=breaks[j+1u];let mid=.5*(lo+hi);let p=a+mid*d;var numerator=0.;var denominator=0.;
|
|
3119
3129
|
for(var k=0u;k<3u;k++){if(p[k]<-h[k]||p[k]>h[k]){let edge=clamp(p[k],-h[k],h[k]);numerator+=d[k]*(a[k]-edge);denominator+=d[k]*d[k];}}
|
|
3120
3130
|
var t=mid;if(denominator>1.e-20){t=clamp(-numerator/denominator,lo,hi);}let point=a+t*d;let box=clamp(point,-h,h);let dist=dot(point-box,point-box);
|
|
3121
|
-
|
|
3131
|
+
// An exact segment/box intersection must win over a nearby exterior
|
|
3132
|
+
// sample. A distance tie alone can select roundoff-sized separation and
|
|
3133
|
+
// normalize it into an arbitrary contact normal.
|
|
3134
|
+
let exactIntersection=dist==0.;let previousIntersection=best==0.;
|
|
3135
|
+
if((exactIntersection&&!previousIntersection)||dist<best-1.e-12 ||
|
|
3136
|
+
(exactIntersection==previousIntersection&&abs(dist-best)<=1.e-12&&abs(t-.5)<abs(bestT-.5))){best=dist;bestT=t;result=SegmentPair(point,box);}
|
|
3122
3137
|
}
|
|
3123
3138
|
return result;
|
|
3124
3139
|
}
|
|
@@ -3146,16 +3161,16 @@ fn capsuleBox(A:Capsule,B:Box,q:vec4f,sat:ptr<function,Sat>)->Found{
|
|
|
3146
3161
|
if(ends.count==2u){return ends;}
|
|
3147
3162
|
addFound(&found,B.c+qrotate(q,center)+n*A.r,B.c+qrotate(q,onBox),4u<<24u);return found;
|
|
3148
3163
|
}
|
|
3149
|
-
`;function
|
|
3150
|
-
fn narrowphase`;if(!
|
|
3151
|
-
`+e)),
|
|
3152
|
-
`+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
|
|
3164
|
+
`;function xl(s){if(s.includes("fn capsuleOf"))throw Error("Capsule collision kernel installed twice");let e=`@compute @workgroup_size(64)
|
|
3165
|
+
fn narrowphase`;if(!s.includes(e))throw Error("Pinned capsule narrowphase layout changed");let t=s.includes("struct Poly");t&&(s=s.replace(e,xu+`
|
|
3166
|
+
`+e)),s=s.replace(e,Al+`
|
|
3167
|
+
`+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.;
|
|
3153
3168
|
if(capsuleA || capsuleB){
|
|
3154
3169
|
if(capsuleA && capsuleB){found=capsuleCapsule(capsuleOf(a),capsuleOf(b),&sat);}
|
|
3155
3170
|
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);}}
|
|
3156
3171
|
else{if(sphereA){found=capsuleSphere(capsuleOf(b),A.c,A.h.x,&sat);}${r} else{found=capsuleBox(capsuleOf(b),A,bodies[a].rot,&sat);}
|
|
3157
3172
|
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;}}
|
|
3158
|
-
} else if (sphereA && sphereB) {`)}var
|
|
3173
|
+
} else if (sphereA && sphereB) {`)}var xu=`
|
|
3159
3174
|
fn capPolyAxis(A:Capsule,P:Poly,axis:vec3f,best:ptr<function,f32>,normal:ptr<function,vec3f>)->bool{
|
|
3160
3175
|
let l=length(axis);if(l<1.e-8){return true;}let n=axis/l;
|
|
3161
3176
|
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;
|
|
@@ -3187,7 +3202,7 @@ fn capsulePoly(A:Capsule,P:Poly,sat:ptr<function,Sat>)->Found{
|
|
|
3187
3202
|
}
|
|
3188
3203
|
(*sat).n=n;addFound(&found,points.a+n*A.r,points.b,4u<<24u);return found;
|
|
3189
3204
|
}
|
|
3190
|
-
`;var
|
|
3205
|
+
`;var Vn=`// GPU queries against current poses. Rounded box/hull sweeps test offset faces
|
|
3191
3206
|
// and rounded edges, so expanding a bounding box cannot create corner hits.
|
|
3192
3207
|
struct Query { origin:vec4f, direction:vec4f }
|
|
3193
3208
|
struct Params { head:vec4u, flags:vec4u, ignored:array<vec4u,4> }
|
|
@@ -3341,9 +3356,88 @@ fn nearest(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.x||igno
|
|
|
3341
3356
|
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);}}
|
|
3342
3357
|
@compute @workgroup_size(64)
|
|
3343
3358
|
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.);}
|
|
3344
|
-
`;var Dt=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?Oo.replace("const DIM=3u;","const DIM=2u;").replace("const STRIDE=10u;","const STRIDE=6u;"):Oo});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 s=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["nearest","owner","finish"].map(a=>[a,i.createComputePipeline({layout:s,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[s,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[s]?.destroy(),i[s]=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}={}){if(this.destroyed)throw Error("GPU queries have been destroyed");let s=this.solver,a=s.device,n=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(u=>!Number.isInteger(u)||u<0||u>=s.bodyCount))throw Error("Queries can ignore up to 16 live body indices");if(e.length>Math.min(65535,a.limits.maxComputeWorkgroupsPerDimension))throw Error("Too many queries in one batch");let l=e.map(u=>{let d=Array.from(u.origin),h=Array.from(u.direction),m=u.radius??0,p=u.maxDistance??3e38;if(d.length!==n||h.length!==n||![...d,...h].every(Number.isFinite)||Math.hypot(...h)===0)throw Error(`A query requires ${n} finite components and a nonzero direction`);if(!Number.isFinite(m)||m<0||!Number.isFinite(p)||p<0)throw Error("Query radius and maximum distance must be finite and nonnegative");let b=Math.hypot(...h);return{origin:d,direction:h.map(y=>y/b),radius:m,maxDistance:p}});if(!l.length)return[];let c=l.length,f=this.acquire(c);try{let u=f.data;l.forEach((k,z)=>{u.set([...k.origin,...Array(3-n).fill(0),k.maxDistance,...k.direction,...Array(3-n).fill(0),k.radius],z*8)});let d=f.params;d.fill(0),d.set([s.bodyCount,c,t.length,i,r?0:1,n===3||s.filters?1:0,s.sensorsEnabled?1:0,n===2&&s.shapeBuffer?1:0]),d.set(t,8);let h=f.initial;for(let k=0;k<c;k++)h.set([2139095040,4294967295],k*2);let{queryBuffer:m,best:p,hits:b,uniform:y,read:A}=f;a.queue.writeBuffer(m,0,u.buffer,0,c*32),a.queue.writeBuffer(p,0,h.buffer,0,c*8),a.queue.writeBuffer(y,0,d);let w=[s.bodyBuffer,n===3?s.hullStorage:s.shapeBuffer??this.dummy,m,p,b,y,n===3?s.filterStorage:s.filters??this.dummy];(!f.buffers||w.some((k,z)=>k!==f.buffers[z]))&&(f.group=a.createBindGroup({layout:this.layout,entries:w.map((k,z)=>({binding:z,resource:{buffer:k}}))}),f.buffers=w);let g=a.createCommandEncoder(),x=g.beginComputePass();x.setBindGroup(0,f.group);for(let k of["nearest","owner"])x.setPipeline(this.pipes[k]),x.dispatchWorkgroups(Math.ceil(s.bodyCount/64),c);x.setPipeline(this.pipes.finish),x.dispatchWorkgroups(Math.ceil(c/64)),x.end(),g.copyBufferToBuffer(b,0,A,0,c*32),a.queue.submit([g.finish()]),await A.mapAsync(GPUMapMode.READ,0,c*32);let v=A.getMappedRange(0,c*32),B=new Float32Array(v),E=new Uint32Array(v);return l.map((k,z)=>{let M=B[z*8+3];if(M<0)return null;let P=Array.from(B.slice(z*8,z*8+n)),_=k.origin.map((S,C)=>S+M*k.direction[C]);return{index:E[z*8+4],distance:M,normal:P,center:_,point:_.map((S,C)=>S-k.radius*P[C])}})}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 is(o,e){let t=this.passConstantCache??={alphas:[]},i=t.iterations!==o||t.colors!==this.colorCap||t.buffer!==this.passBuffer||!this.passGroup;for(let n=0;n<o;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,s=Math.max(o*r,1),a=s*256;(s>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=s,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));for(let n=0;n<o;n++)for(let l=0;l<=this.colorCap;l++){let c=(n*r+l)*256/4;t.u32[c]=l,t.f32[c+1]=t.alphas[n]}this.device.queue.writeBuffer(this.passBuffer,0,t.data,0,a),t.iterations=o,t.colors=this.colorCap,t.buffer=this.passBuffer,t.uploads=(t.uploads??0)+1}var Ni=class extends $r{writePassConstants(e,t){is.call(this,e,t)}constructor(e,t,i={}){let r=t.forces.some(u=>Io(u)),s=t.forces.some(u=>{let d=Lo(u);return d&&(Number.isFinite(d.breakStrain)||Number.isFinite(d.breakForce))}),a={requested:i.solverMode??"auto",contactScheduling:i.contactScheduling??"manifolds",custom:!!i.shaders?.solve},n=Di({...a,bodyCount:t.bodies.length,constraintCount:t.forces.filter(u=>u instanceof Ae||u instanceof ct).length}),l=n.selected==="custom"?i.shaders.solve:n.selected==="optimized"?ur:n.selected==="points"?Xr:void 0,c=i.broadphase??"auto";if(!["grid","hploc","auto"].includes(c))throw Error("broadphase must be 'grid', 'hploc' or 'auto'");let f=i.bvh?.rebuildInterval??64;if(!Number.isInteger(f)||f<1)throw Error("BVH rebuildInterval must be a positive integer");for(let[u,d]of[["minimumColors",64],["minimumColorRounds",32]]){let h=i[u]??0;if(!Number.isInteger(h)||h<0||h>d)throw Error(`${u} must be an integer between 0 and ${d}`);if(u==="minimumColorRounds"&&h%2)throw Error("minimumColorRounds must be even")}super(e,t,{...i,shaders:{...i.shaders,solve:So(Co(r?Ro(s?Qr(l??ke):l??ke):s?Qr(l??ke):l??ke))}}),this.solverPolicy=a,this.springFracture=s,this.angularConstraints=r,this.customSolveSource=i.shaders?.solve,t.forces.forEach((u,d)=>{let h=Lo(u);h&&this.setSpringMaterial(d,h)}),this.adaptiveScheduling=i.adaptiveScheduling??!0,this.defaultPrimalLanes=[...this.primalLanes],this.cacheAdjacencyKeys=i.cacheAdjacencyKeys,this.adjacencyPipelines={original:this.pipes.sortAdjacency},this.selectAdjacencyPipeline(),this.solverDecision=n,this.solvePipelineCache=new Map([[n.selected,Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(u=>[u,this.pipes[u]]))]]),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=c,this.broadphase=c==="hploc"?"hploc":"grid",this.bvhOptions=i.bvh??{},this.bvh=null,this.detailCallback=null,this.detailTiming=null,this.profileErrors=[],this.selectBroadphase(),t.forces.forEach((u,d)=>{let h=Io(u);h&&this.setAngularConstraint(d,h)}),t.bodies.forEach((u,d)=>{es(u)&&this.installCapsule(this.gpuIndex(d))})}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(cn(i)),this.contactShaders.make(lr(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 Dt(this),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async sphereCast(e,t,i,r={}){let[s]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}sphereCastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new Dt(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 h=this.bodies[d];if(h.radius>0&&(e[t++]=h.radius),h.dynamic){i++;let[m,p,b]=h.size,y=Math.min(m,p,b),A=Math.max(m,p,b),w=m+p+b-y-A;!h.sphere&&!h.hull&&y<=.4*w&&w>=.5*A&&r++}}let s=t?Wr(e.subarray(0,t),t>>1):1,a=this.cellSize/(2*s),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,f=n>=this.bodyCount&&l>=.25,u=c&&this.bodyCount>=1e4&&(a>=4||f);this.broadphase=u?"hploc":"grid",this.broadphaseDecision={requested:"auto",selected:this.broadphase,bodyCount:this.bodyCount,cellRatio:a,constraintCount:n,thinPlateFraction:l,reason:c?u?f?"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(){this.selectAdjacencyPipeline(),this.selectSolver(),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 Zr(this.device,this,this.bvhOptions)),this.bvh||this.detailCallback||this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep?Qa(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 Gi(this)),this.restitution?.set(e,t),this.wakeAll()}setSolverMode(e){return Di({...this.solverPolicy,requested:e,bodyCount:this.bodyCount}),this.solverPolicy.requested=e,this.selectSolver(),this.solverDecision}solveSource(e){return this.springFracture&&(e=Qr(e)),this.angularConstraints&&(e=Ro(e)),this.sensorsEnabled&&(e=tn(e)),So(Co(e))}setAngularConstraint(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount)throw Error("Angular constraint needs a live slot");let i=gt(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;
|
|
3345
|
-
|
|
3346
|
-
|
|
3359
|
+
`;var di=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?Vn.replace("const DIM=3u;","const DIM=2u;").replace("const STRIDE=10u;","const STRIDE=6u;"):Vn});this.layout=i.createBindGroupLayout({entries:["read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform","read-only-storage"].map((n,a)=>({binding:a,visibility:GPUShaderStage.COMPUTE,buffer:{type:n}}))});let o=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["nearest","owner","finish"].map(n=>[n,i.createComputePipeline({layout:o,compute:{module:r,entryPoint:n}})])),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,n,a]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:n,usage:a|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 n=this.solver,a=n.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>=n.bodyCount))throw Error("Queries can ignore up to 16 live body indices");if(e.length>Math.min(65535,a.limits.maxComputeWorkgroupsPerDimension))throw Error("Too many queries in one batch");let c=e.map(d=>{let p=Array.from(d.origin),g=Array.from(d.direction),h=d.radius??0,m=d.maxDistance??o??3e38;if(p.length!==l||g.length!==l||![...p,...g].every(Number.isFinite)||Math.hypot(...g)===0)throw Error(`A query requires ${l} finite components and a nonzero direction`);if(!Number.isFinite(h)||h<0||!Number.isFinite(m)||m<0)throw Error("Query radius and maximum distance must be finite and nonnegative");let b=Math.hypot(...g);return{origin:p,direction:g.map(v=>v/b),radius:h,maxDistance:m}});if(!c.length)return[];let u=c.length,f=this.acquire(u);try{let d=f.data;c.forEach((M,z)=>{d.set([...M.origin,...Array(3-l).fill(0),M.maxDistance,...M.direction,...Array(3-l).fill(0),M.radius],z*8)});let p=f.params;p.fill(0),p.set([n.bodyCount,u,t.length,i,r?0:1,l===3||n.filters?1:0,n.sensorsEnabled?1:0,l===2&&n.shapeBuffer?1:0]),p.set(t,8);let g=f.initial;for(let M=0;M<u;M++)g.set([2139095040,4294967295],M*2);let{queryBuffer:h,best:m,hits:b,uniform:v,read:w}=f;a.queue.writeBuffer(h,0,d.buffer,0,u*32),a.queue.writeBuffer(m,0,g.buffer,0,u*8),a.queue.writeBuffer(v,0,p);let y=[n.bodyBuffer,l===3?n.hullStorage:n.shapeBuffer??this.dummy,h,m,b,v,l===3?n.filterStorage:n.filters??this.dummy];(!f.buffers||y.some((M,z)=>M!==f.buffers[z]))&&(f.group=a.createBindGroup({layout:this.layout,entries:y.map((M,z)=>({binding:z,resource:{buffer:M}}))}),f.buffers=y);let A=a.createCommandEncoder(),x=A.beginComputePass();x.setBindGroup(0,f.group);for(let M of["nearest","owner"])x.setPipeline(this.pipes[M]),x.dispatchWorkgroups(Math.ceil(n.bodyCount/64),u);x.setPipeline(this.pipes.finish),x.dispatchWorkgroups(Math.ceil(u/64)),x.end(),A.copyBufferToBuffer(b,0,w,0,u*32),a.queue.submit([A.finish()]),await w.mapAsync(GPUMapMode.READ,0,u*32);let B=w.getMappedRange(0,u*32),E=new Float32Array(B),L=new Uint32Array(B);return c.map((M,z)=>{let _=E[z*8+3];if(_<0)return null;let k=Array.from(E.slice(z*8,z*8+l)),S=M.origin.map((P,I)=>P+_*M.direction[I]);return{index:L[z*8+4],distance:_,normal:k,center:S,point:S.map((P,I)=>P-M.radius*k[I])}})}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 Us(s,e){let t=this.passConstantCache??={alphas:[]},i=t.iterations!==s||t.colors!==this.colorCap||t.buffer!==this.passBuffer||!this.passGroup;for(let a=0;a<s;a++){let l=typeof e=="function"?e(a):e;t.alphas[a]!==l&&(i=!0),t.alphas[a]=l}if(!i)return;let r=this.colorCap+1,o=Math.max(s*r,1),n=o*256;(o>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"AVBD pass constants",size:n,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<n)&&(t.data=new ArrayBuffer(Math.max(n,(t.data?.byteLength??0)*2)),t.u32=new Uint32Array(t.data),t.f32=new Float32Array(t.data));for(let a=0;a<s;a++)for(let l=0;l<=this.colorCap;l++){let c=(a*r+l)*256/4;t.u32[c]=l,t.f32[c+1]=t.alphas[a]}this.device.queue.writeBuffer(this.passBuffer,0,t.data,0,n),t.iterations=s,t.colors=this.colorCap,t.buffer=this.passBuffer,t.uploads=(t.uploads??0)+1}var Wn=new WeakMap;function qs(s){return Wn.get(s)}function wl(s,e){e?Wn.set(s,Object.freeze({...e})):Wn.delete(s)}function Hn(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)+`
|
|
3360
|
+
@compute @workgroup_size(64)
|
|
3361
|
+
fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
3362
|
+
let start = color[params.colorStartOffset + pc.color];
|
|
3363
|
+
let count = color[params.colorStartOffset + pc.color + 1u] - start;
|
|
3364
|
+
if (gid.x >= count || !solverStepValid()) { return; }
|
|
3365
|
+
let i = color[params.colorBodiesOffset + start + gid.x];
|
|
3366
|
+
finishBody(i, accumulate(i, 0u, 1u));
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
`+s.slice(i)}function hi(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 Vs(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 Bl(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),n=s.indexOf("{",r)+1,a=1;for(;n<s.length&&a;n++)s[n]==="{"&&a++,s[n]==="}"&&a--;if(a)throw Error(`Incomplete 3D ${e} function`);return s.slice(0,r)+t(s.slice(r,n))+s.slice(n)}function Jn(s){s=Vs(s,"PairBody",`struct PairBody {
|
|
3370
|
+
ids: vec4u,
|
|
3371
|
+
rot: vec4f,
|
|
3372
|
+
dLin: vec4f,
|
|
3373
|
+
dAng: vec4f,
|
|
3374
|
+
}`),s=hi(s,"return PairBody(i, rot, bodies[i].pos.xyz - bodies[i].initialPos.xyz, qsub(rot, bodies[i].initialRot));","return PairBody(vec4u(i, 0u, 0u, 0u), rot, vec4f(bodies[i].pos.xyz - bodies[i].initialPos.xyz, 0.0), vec4f(qsub(rot, bodies[i].initialRot), 0.0));"),s=s.replace(/\b(A|B)\.index\b/g,"$1.ids.x").replace(/\b(A|B)\.(dLin|dAng)\b/g,"$1.$2.xyz"),s=Vs(s,"Contact",`struct Contact {
|
|
3375
|
+
anchorA: vec4u, // xyz: float bits, w: integer feature key
|
|
3376
|
+
anchorB: vec4f, // xyz: anchor, w: normal error
|
|
3377
|
+
penalty: vec4f, // xyz: penalties, w: first tangent error
|
|
3378
|
+
force: vec4f, // xyz: forces, w: second tangent error
|
|
3379
|
+
}`);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=Bl(s,t,i=>i.replace(/\bk\.(rA|rB|key|c0x|c0y|c0z|pen|lam)\b/g,(r,o)=>e[o]));return s=hi(s,"contacts[c].lam = e.F;","contacts[c].force = vec4f(e.F, k.force.w);"),s=hi(s,"contacts[c].key =","contacts[c].anchorA.w ="),s=hi(s,"contacts[c].pen = pen;","contacts[c].penalty = vec4f(pen, k.penalty.w);"),s=Bl(s,"evalContact",t=>{t=hi(t,` var e: ContactEval;
|
|
3380
|
+
`,""),t=hi(t," e.rAW ="," let rAW ="),t=hi(t," e.rBW ="," let rBW ="),t=t.replaceAll("e.rAW","rAW").replaceAll("e.rBW","rBW"),t=hi(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;
|
|
3381
|
+
let normalForce = select(0.0, raw.x, raw.x < 0.0);
|
|
3382
|
+
let bounds = abs(normalForce) * friction;
|
|
3383
|
+
let frictionScale = length(raw.yz);
|
|
3384
|
+
var scale = 1.0;
|
|
3385
|
+
if (frictionScale > bounds && frictionScale > 0.0) {
|
|
3386
|
+
scale = bounds / frictionScale;
|
|
3387
|
+
}
|
|
3388
|
+
let force = vec3f(normalForce, raw.yz * scale);
|
|
3389
|
+
return ContactEval(vec4f(rAW, 0.0), vec4f(rBW, 0.0),
|
|
3390
|
+
vec4f(C, 0.0), vec4f(force, 0.0), vec4f(frictionScale, bounds, 0.0, 0.0));
|
|
3391
|
+
}`}),s=Vs(s,"ContactEval",`struct ContactEval {
|
|
3392
|
+
rAW: vec4f,
|
|
3393
|
+
rBW: vec4f,
|
|
3394
|
+
C: vec4f,
|
|
3395
|
+
F: vec4f,
|
|
3396
|
+
limits: vec4f, // x: tangent force length, y: Coulomb bound
|
|
3397
|
+
}`),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=Vs(s,"PassConstants","struct PassConstants { data: vec4u, }"),s.replaceAll("pc.color","pc.data.x").replaceAll("pc.alpha","bitcast<f32>(pc.data.y)")}function $n(s){if(!s.includes("struct Poly"))return s;let e=`@compute @workgroup_size(64)
|
|
3398
|
+
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)")+`
|
|
3399
|
+
@compute @workgroup_size(32)
|
|
3400
|
+
fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
3401
|
+
for (var offset = 0u; offset < 2u; offset++) {
|
|
3402
|
+
narrowphasePair(vec3u(gid.x * 2u + offset, 0u, 0u));
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
`}var pi=class extends js{writePassConstants(e,t){Us.call(this,e,t)}constructor(e,t,i={}){let r=qs(e),o=i.scalarPrimal??r?.scalarPrimal??!1,n=i.portableContactMath??r?.portableContactMath??!1,a=t.forces.some(m=>Hr(m)),l=t.forces.some(m=>{let b=Gi(m);return b&&(Number.isFinite(b.breakStrain)||Number.isFinite(b.breakForce))}),c={requested:i.solverMode??"auto",contactScheduling:i.contactScheduling??"manifolds",custom:!!i.shaders?.solve},u=br({...c,bodyCount:t.bodies.length,constraintCount:t.forces.filter(m=>m instanceof ke||m instanceof ut).length}),f=u.selected==="custom"?i.shaders.solve:u.selected==="optimized"?Wr:u.selected==="points"?Ds: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[m,b]of[["minimumColors",64],["minimumColorRounds",32]]){let v=i[m]??0;if(!Number.isInteger(v)||v<0||v>b)throw Error(`${m} must be an integer between 0 and ${b}`);if(m==="minimumColorRounds"&&v%2)throw Error("minimumColorRounds must be even")}let g=Rn(On(a?qn(l?Fs(f??ze):f??ze):l?Fs(f??ze):f??ze));o&&(g=Hn(g)),n&&(g=Jn(g));let h=n&&!i.shaders?.contacts&&i.hulls!==!1&&e.limits.maxStorageBuffersPerShaderStage>=9&&t.bodies.some(m=>wt(m));if(super(e,t,{...i,shaders:{...i.shaders,contacts:h?$n(Ti(!0)):i.shaders?.contacts,solve:g}}),this.solverPolicy=c,this.scalarPrimal=o,this.portableContactMath=n,o&&(this.primalLanes=[1,1,1]),this.dispatchIsolation=r?.dispatchIsolation??!1,!i.shaders?.contacts){h&&(this.contactShaders.custom=!1,this.hullShaders=!0);let m=this.contactShaders.make;this.contactShaders.make=b=>m(n?$n(Nn(b)):Nn(b)),this.contactShaders.make(Ti(this.hullShaders??!1)),this.pipes.updateRefs=e.createComputePipeline({label:"Portable contact references",layout:e.createPipelineLayout({bindGroupLayouts:[this.layouts.refs]}),compute:{module:e.createShaderModule({code:gl(Rs)}),entryPoint:"updateRefs"}})}this.springFracture=l,this.angularConstraints=a,this.customSolveSource=i.shaders?.solve,t.forces.forEach((m,b)=>{let v=Gi(m);v&&this.setSpringMaterial(b,v)}),this.adaptiveScheduling=o?!1:i.adaptiveScheduling??!0,this.defaultPrimalLanes=[...this.primalLanes],this.cacheAdjacencyKeys=i.cacheAdjacencyKeys,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(m=>[m,this.pipes[m]]))]]),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((m,b)=>{let v=Hr(m);v&&this.setAngularConstraint(b,v)}),t.bodies.forEach((m,b)=>{He(m)&&this.installCapsule(this.gpuIndex(b))})}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(xl(i)),this.contactShaders.make(Ti(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 di(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 di(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[g,h,m]=p.size,b=Math.min(g,h,m),v=Math.max(g,h,m),w=g+h+m-b-v;!p.sphere&&!p.hull&&b<=.4*w&&w>=.5*v&&r++}}let o=t?Os(e.subarray(0,t),t>>1):1,n=this.cellSize/(2*o),a=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=a>=this.bodyCount&&l>=.25,f=c&&this.bodyCount>=1e4&&(n>=4||u);this.broadphase=f?"hploc":"grid",this.broadphaseDecision={requested:"auto",selected:this.broadphase,bodyCount:this.bodyCount,cellRatio:n,constraintCount:a,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(){this.selectAdjacencyPipeline(),this.selectSolver(),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 Ns(this.device,this,this.bvhOptions)),this.dispatchIsolation||this.bvh||this.detailCallback||this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep?cl(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 yr(this)),this.restitution?.set(e,t),this.wakeAll()}setSolverMode(e){return br({...this.solverPolicy,requested:e,bodyCount:this.bodyCount}),this.solverPolicy.requested=e,this.selectSolver(),this.solverDecision}solveSource(e){return this.springFracture&&(e=Fs(e)),this.angularConstraints&&(e=qn(e)),this.sensorsEnabled&&(e=hl(e)),e=Rn(On(e)),this.scalarPrimal&&(e=Hn(e)),this.portableContactMath&&(e=Jn(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;
|
|
3406
|
+
${yl.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&&le(this).float(this.jointBuffer,e*32+27,t),i!==void 0&&le(this).float(this.jointBuffer,e*32+31,i),this.wakeAll()}rebuildSolvePipelines(){this.solvePipelineCache.clear();let e=this.solverDecision.selected,t=e==="custom"?this.customSolveSource:e==="optimized"?Wr:e==="points"?Ds:ze,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(n=>[n,this.device.createComputePipeline({layout:r,compute:{module:i,entryPoint:n}})]));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,ml(this),this.rebuildSolvePipelines(),this.contactShaders.make(Gn(Ti(this.hullShaders??!1)));let i=this.contactShaders.make;this.contactShaders.make=n=>i(Gn(n));let r=this.device.createShaderModule({code:pl()}),o=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]});for(let n of["degreeContacts","fillContacts"])this.pipes[n]=this.device.createComputePipeline({layout:o,compute:{module:r,entryPoint:n}})}this.sensorsEnabled&&le(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=br({...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"?Wr:i.selected==="points"?Ds:void 0,n=this.device.createShaderModule({code:this.solveSource(o??ze)}),a=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:a,compute:{module:n,entryPoint:l}})])),this.solvePipelineCache.set(i.selected,r)}Object.assign(this.pipes,r)}this.solverDecision=i}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;
|
|
3407
|
+
${sl.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]!==pr)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,g,h)=>p+(g+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++)He(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++)He(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:dl}),entryPoint:"sortAdjacency"}})),this.pipes.sortAdjacency=e?this.adjacencyPipelines.cached:this.adjacencyPipelines.original,this.adjacencyCached=e)}};var Xn=new WeakMap,Cl=[{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 Sl(s){return Xn.has(s)||Xn.set(s,Bu(s)),Xn.get(s)}async function Bu(s,e){let t=[];for(let i of Cl){let r=await Lu(s,i,e);if(t.push(r),!!r.passed)return wl(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!==Cl[0].name,attempts:t}}return{...t[0],selected:null,compatibility:!1,attempts:t}}function Cu(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]),or(e,.5,1,.6,[0,s?2.3:.45,0]),Ar(e,.25,1.6,1,.6,[2,s?2.5:.75,0]),e}async function mi(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 Su(s){s.step();let e=await s.readCounters(),t=new Uint32Array(await mi(s.device,s.argsBuffer)),i=await Pu(s),r=new Uint32Array(i),o=new Float32Array(i),n=new Uint32Array(await mi(s.device,s.adjBuffer)),a=new Uint32Array(await mi(s.device,s.colorBuffer)),{manifolds:l,contacts:c}=s.contactStorage,u=await mi(s.device,l,Math.max(32,e.manifolds*32)),f=new Uint32Array(u),d=new Float32Array(u),p=new Float32Array(await mi(s.device,c,Math.max(64,e.contacts*64))),g=await s.readBodies(),h=e.manifolds>=3?await ku(s):[],m=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(g.subarray(b*40,b*40+3))),manifolds:Array.from({length:e.manifolds},(b,v)=>({ids:Array.from(f.subarray(v*8,v*8+4)),normalAndFriction:Array.from(d.subarray(v*8+4,v*8+8))})),contacts:Array.from({length:e.contacts},(b,v)=>({anchorA:Array.from(p.subarray(v*16,v*16+3)),anchorB:Array.from(p.subarray(v*16+4,v*16+7)),gap:p[v*16+7],penalty:Array.from(p.subarray(v*16+8,v*16+11)),force:Array.from(p.subarray(v*16+12,v*16+15))})),serialSystems:h,failure:m?"contact-evaluation":null},filters:Array.from(new Uint32Array(await mi(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 ku(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(ze)+`
|
|
3408
|
+
@compute @workgroup_size(1) fn inspectSystems(@builtin(global_invocation_id) gid: vec3u) {
|
|
3409
|
+
let acc = accumulate(gid.x + 1u, 0u, 1u);
|
|
3410
|
+
joints[gid.x].penLin = vec4f(acc.lin[0][0], acc.lin[1][1], acc.lin[2][2], 0.0);
|
|
3411
|
+
joints[gid.x].penAng = vec4f(acc.ang[0][0], acc.ang[1][1], acc.ang[2][2], 0.0);
|
|
3412
|
+
joints[gid.x].c0Lin = vec4f(acc.rLin, 0.0);
|
|
3413
|
+
joints[gid.x].c0Ang = vec4f(acc.rAng, 0.0);
|
|
3414
|
+
let mf = manifolds[gid.x];
|
|
3415
|
+
let k = contacts[mf.ids.z];
|
|
3416
|
+
let A = pairBody(mf.ids.x); let B = pairBody(mf.ids.y);
|
|
3417
|
+
let basis = orthonormal(mf.geo.xyz);
|
|
3418
|
+
let result = evalContact(k, basis, mf.geo.w, A, B, ${e});
|
|
3419
|
+
// Independently recompute the normal row from scalar inputs. Compare input
|
|
3420
|
+
// reads, pass alpha, the returned error and the returned clamped force.
|
|
3421
|
+
let dA = bodies[mf.ids.x].pos.xyz - bodies[mf.ids.x].initialPos.xyz;
|
|
3422
|
+
let dB = bodies[mf.ids.y].pos.xyz - bodies[mf.ids.y].initialPos.xyz;
|
|
3423
|
+
let wA = qsub(bodies[mf.ids.x].rot, bodies[mf.ids.x].initialRot);
|
|
3424
|
+
let wB = qsub(bodies[mf.ids.y].rot, bodies[mf.ids.y].initialRot);
|
|
3425
|
+
let expectedC = ${t} * (1.0 - ${e}) + dot(basis[0], dA - dB)
|
|
3426
|
+
+ dot(cross(result.rAW.xyz, basis[0]), wA)
|
|
3427
|
+
- dot(cross(result.rBW.xyz, basis[0]), wB);
|
|
3428
|
+
let raw = ${i} * expectedC + ${r};
|
|
3429
|
+
joints[gid.x].rA = vec4f(${t}, ${e}, result.C.x, result.F.x);
|
|
3430
|
+
joints[gid.x].rB = vec4f(${i}, ${r}, expectedC, min(raw, 0.0));
|
|
3431
|
+
}`}),n=s.device.createComputePipeline({layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.solve,s.layouts.pass]}),compute:{module:o,entryPoint:"inspectSystems"}}),a=s.device.createCommandEncoder(),l=a.beginComputePass();l.setPipeline(n),l.setBindGroup(0,s.groups.solve[1-s.parity]),l.setBindGroup(1,s.passGroup,[0]),l.dispatchWorkgroups(3),l.end(),s.device.queue.submit([a.finish()]);let c=new Float32Array(await mi(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 Pu(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:Ae+`
|
|
3432
|
+
@group(0) @binding(0) var<uniform> p: Params;
|
|
3433
|
+
@group(0) @binding(1) var<storage,read_write> out: array<u32>;
|
|
3434
|
+
@compute @workgroup_size(1) fn inspectParams() {
|
|
3435
|
+
out[0]=p.bodyCount; out[1]=p.largeCount; out[2]=p.noCollideCount;
|
|
3436
|
+
out[3]=p.pairCapacity; out[4]=p.contactCapacity;
|
|
3437
|
+
out[5]=bitcast<u32>(p.dt); out[6]=bitcast<u32>(p.cellSize); out[7]=bitcast<u32>(p.maxSmallRadius);
|
|
3438
|
+
out[8]=bitcast<u32>(p.up.x); out[9]=bitcast<u32>(p.up.y); out[10]=bitcast<u32>(p.up.z);
|
|
3439
|
+
out[11]=bitcast<u32>(p.alpha);
|
|
3440
|
+
}`}),entryPoint:"inspectParams"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:s.paramsBuffer}},{binding:1,resource:{buffer:t}}]}),o=e.createCommandEncoder(),n=o.beginComputePass();return n.setPipeline(i),n.setBindGroup(0,r),n.dispatchWorkgroups(1),n.end(),e.queue.submit([o.finish()]),await mi(e,t)}finally{t.destroy()}}async function Lu(s,e,t){let i={mode:e.name,passed:!1,steps:180,cases:[],errors:[],contactCache:"normal-float-generations-v1",diagnosticVersion:4,limits:{storageBindings:s.limits.maxStorageBuffersPerShaderStage,workgroupStorageBytes:s.limits.maxComputeWorkgroupStorageSize,workgroupInvocations:s.limits.maxComputeInvocationsPerWorkgroup,storageBindingBytes:s.limits.maxStorageBufferBindingSize}},r,o=n=>{let a=new pi(s,Cu(n),{spatialSort:!1,bodyCapacity:4,broadphase:e.broadphase,scalarPrimal:e.scalarPrimal??!1,portableContactMath:e.portableContactMath??!1,capacity:{pairs:32,manifolds:32,contacts:256,colors:8}});return a.params.up=[0,1,0],a.dispatchIsolation=e.dispatchIsolation,t?.(a,e,n),a};s.pushErrorScope("validation");try{if(r=o(!1),i.pipeline=await Su(r),i.pipeline.stage!=="contacts-created")i.reason=i.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{r.destroy(),r=o(!0);let n=0;i.samples=[];for(let l=0;l<i.steps;l++){if(r.step(),(l+1)%30&&l!==39)continue;let c=await r.readCounters();if(i.samples.push({step:l+1,...c}),c.manifolds&&n++,c.overflow||c.clashes)throw Error("Incomplete GPU contact graph")}let a=await r.readBodies();for(let l=1;l<4;l++){let c=l*40,[u,f,d,p]=a.subarray(c+4,c+8),g=l===1?.5*(Math.abs(2*(u*f+p*d))+Math.abs(1-2*(u*u+d*d))+Math.abs(2*(f*d-p*u))):l===2?.5:.25+.55*Math.abs(1-2*(u*u+d*d)),h=a[c+1]-g,m=a.subarray(c,c+40).every(Number.isFinite);i.cases.push({shape:["floor","box","sphere","capsule"][l],passed:m&&h>=-.06&&h<.12,floorGap:h,position:Array.from(a.subarray(c,c+3))})}i.contactSteps=n,i.passed=n>0&&i.cases.every(l=>l.passed),i.passed||(i.reason=i.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){i.errors.push(n.message)}finally{try{let n=await s.popErrorScope();n&&i.errors.push(n.message)}catch(n){i.errors.push(n.message)}r?.destroy()}return i.passed=i.passed&&!i.errors.length,i}async function Jr(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 n=e.limits[r];if(r.startsWith("min")&&o<n||!r.startsWith("min")&&o>n)throw Error(`GPU limit ${r} requires ${o}; this adapter supports ${n}`);t[r]=o}let i=await e.requestDevice({requiredLimits:t,requiredFeatures:e.features.has("timestamp-query")?["timestamp-query"]:[]});if(s.validate3D)try{await xr(i)}catch(r){throw i.destroy(),r}return{adapter:e,device:i}}async function xr(s){if(qs(s))return s;let e=await Sl(s);if(!e.passed)throw Error(`GPU 3D contact check failed: ${e.reason??e.errors.join("; ")}`);return s}var kl=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
3347
3441
|
struct Command { index:u32,mask:u32,pad0:u32,pad1:u32,velocity:vec4f,angular:vec4f,impulse:vec4f,torque:vec4f,position:vec4f,rotation:vec4f,pointImpulse:vec4f }
|
|
3348
3442
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
3349
3443
|
@group(0) @binding(1) var<storage,read> commands:array<Command>;
|
|
@@ -3365,37 +3459,37 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
|
|
|
3365
3459
|
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);}
|
|
3366
3460
|
bodies[c.index]=b;
|
|
3367
3461
|
}
|
|
3368
|
-
`;var
|
|
3462
|
+
`;var Qt=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,n=t===3?i==="angular"?9:8:3;this.pipeline=r.createComputePipeline({layout:"auto",compute:{entryPoint:"setVelocities",module:r.createShaderModule({code:`
|
|
3369
3463
|
@group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
|
|
3370
3464
|
@group(0) @binding(1) var<storage,read> indices:array<u32>;
|
|
3371
3465
|
@group(0) @binding(2) var<storage,read> velocities:array<f32>;
|
|
3372
3466
|
@compute @workgroup_size(64) fn setVelocities(@builtin(global_invocation_id) id:vec3u) {
|
|
3373
3467
|
let i=id.x+id.y*${r.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3374
3468
|
if(i>=arrayLength(&indices)){return;}
|
|
3375
|
-
let base=indices[i]*${
|
|
3469
|
+
let base=indices[i]*${o}u+${n}u;
|
|
3376
3470
|
var v=bodies[base];
|
|
3377
3471
|
${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];":""}`}
|
|
3378
3472
|
bodies[base]=v;
|
|
3379
|
-
}`})}})}set(e,t){let i=this.solver,r=i.device,
|
|
3473
|
+
}`})}})}set(e,t){let i=this.solver,r=i.device,o=e instanceof Uint32Array?e:Uint32Array.from(e),n=t instanceof Float32Array?t:Float32Array.from(t);if(n.length!==o.length*this.components)throw Error(`Provide ${this.components} velocity components per body`);let a=-1;for(let c=0;c<e.length;c++){if(i.liveIndex(e[c]),e[c]<=a)throw Error("Velocity batch indices must be unique and ascending GPU slots");a=e[c]}for(let c of n)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||n.byteLength>l)throw Error("Velocity batch exceeds GPU buffer limits");o.length&&(i.wakeAll(),i.flushEdits(),this.submitValidated(o,n))}submitValidated(e,t,i){if(!e.length)return;let r=this.solver,o=r.device,n=Math.min(o.limits.maxStorageBufferBindingSize,o.limits.maxBufferSize);if(e.byteLength>n||t.byteLength>n)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(n,d.byteLength*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),o.queue.writeBuffer(this[f],0,d);let a=i??o.createCommandEncoder(),l=a.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([a.finish()]),this.lastBatch={bodies:e.length,uploadedBytes:e.byteLength+t.byteLength}}destroy(){this.indices?.destroy(),this.values?.destroy()}},gi=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,n=o===3?3:1,a=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!==a.length*o)throw Error(`Provide ${o} linear components per body`);if(c&&c.length!==a.length*n)throw Error(`Provide ${n} 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 g of p)if(!Number.isFinite(g))throw Error("Velocities must be finite Float32 values")}let f=a.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<a.length;p++){let g=p*this.wordsPerBody;if(this.data[g]=a[p],this.data[g+1]=(l?1:0)|(c?2:0),l)for(let h=0;h<o;h++)this.floats[g+2+h]=l[p*o+h];if(c)for(let h=0;h<n;h++)this.floats[g+2+o+h]=c[p*n+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,n=Math.min(r.limits.maxStorageBufferBindingSize,r.limits.maxBufferSize);if(e.byteLength>n)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(n,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:`
|
|
3380
3474
|
@group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
|
|
3381
3475
|
@group(0) @binding(1) var<storage,read> commands:array<u32>;
|
|
3382
3476
|
@compute @workgroup_size(64) fn edit(@builtin(global_invocation_id) id:vec3u) {
|
|
3383
3477
|
let i=id.x+id.y*${r.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3384
|
-
let o=i*${
|
|
3478
|
+
let o=i*${o}u;if(o>=arrayLength(&commands)){return;}
|
|
3385
3479
|
let b=commands[o]*${this.dimension===3?10:6}u;
|
|
3386
3480
|
let mask=commands[o+1u];
|
|
3387
3481
|
var v=bodies[b+${this.dimension===3?8:3}u];
|
|
3388
3482
|
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]);":""}}
|
|
3389
3483
|
${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;}"}
|
|
3390
3484
|
bodies[b+${this.dimension===3?8:3}u]=v;
|
|
3391
|
-
}`})}}),(!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
|
|
3485
|
+
}`})}}),(!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 a=t??r.createCommandEncoder(),l=a.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([a.finish()]),this.lastBatch={bodies:e.length/o,uploadedBytes:e.byteLength}}destroy(){this.buffer?.destroy(),this.binding=null,this.data=this.floats=null}};function Ws(s,e={}){return br({...e,requested:e.solverMode??"auto",bodyCount:s.bodies.length,custom:!!e.shaders?.solve}),{...e,solverMode:e.solverMode??"auto"}}function wr(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 Hs(s,e){if(s!==1/0&&(!Number.isFinite(s)||s<0))throw Error(`${e} must be nonnegative or Infinity`);return s}function Pl(s,e){let t=wr(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 $r=class{constructor(e,t,i,r={}){e.assertAlive();for(let n of[t,i])if(n&&(n.assertAlive(),n.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 n=(a,l)=>a?new a.aggregate.mesh.position.constructor(...wr(l,"axis")).applyRotationQuaternion(a.aggregate.localRotation.conjugate()).asArray():wr(l,"axis");this.angularMaterial=kt({...r,type:o,axisA:n(t,r.axisA??[0,1,0]),axisB:n(i,r.axisB??[0,1,0])})}if(this.anchorA=Pl(t,r.anchorA??[0,0,0]),this.anchorB=Pl(i,r.anchorB??[0,0,0]),this.linear=Hs(o==="spring"?r.stiffness??1e3:r.stiffness??1/0,"stiffness"),this.angular=o==="fixed"?Hs(r.angularStiffness??1/0,"angularStiffness"):0,this.breakForce=Hs(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 ut(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"&&gr(this.force,this.material),this.force&&this.angularMaterial&&vr(this.force,this.angularMaterial),e.constraints.add(this),e.wakeAll?.()}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=wr(e,"world anchor"),this.world.gpu?this.world.gpu.setWorldAnchor(this.slot,this.anchorA):this.force.rA.set(this.anchorA),this.world.wakeAll?.(),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):vr(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.world.wakeAll?.()),this.world.constraints.delete(this),this.disposed=!0)}},Xr=class{constructor(e,t,i,r={}){let o=kt({...r,type:"limit"}),n=r.span??1;if(!Number.isFinite(n)||n<=0)throw Error("Hinge span must be positive");let a=wr(r.anchorA??[0,0,0],"anchorA"),l=wr(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:a.map((u,f)=>u+c*n*.5*o.axisA[f]),anchorB:l.map((u,f)=>u+c*n*.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 bi=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(n=>n.selection.size>=e&&n.selected.size>=t),o=r>=0?this.idle.splice(r,1)[0]:this.idle.pop()??{};for(let[n,a,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[n]||o[n].size<a)&&(o[n]?.destroy(),o[n]=i.createBuffer({size:a,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,n=this.device,a=n.limits.maxComputeWorkgroupsPerDimension,l=`${r}:${o}`,c=this.pipelines.get(l);c||(c=n.createComputePipeline({layout:"auto",compute:{entryPoint:"gather",module:n.createShaderModule({code:`
|
|
3392
3486
|
@group(0) @binding(0) var<storage,read> bodies:array<vec4f>;
|
|
3393
3487
|
@group(0) @binding(1) var<storage,read> indices:array<u32>;
|
|
3394
3488
|
@group(0) @binding(2) var<storage,read_write> selected:array<vec4f>;
|
|
3395
3489
|
@compute @workgroup_size(64) fn gather(@builtin(global_invocation_id) id:vec3u) {
|
|
3396
|
-
let i=id.x+id.y*${
|
|
3397
|
-
selected[i]=bodies[indices[i/${
|
|
3398
|
-
}`})}}),this.pipelines.set(l,c));let
|
|
3490
|
+
let i=id.x+id.y*${a*64}u; if(i>=arrayLength(&selected)){return;}
|
|
3491
|
+
selected[i]=bodies[indices[i/${o}u]*${r}u+i%${o}u];
|
|
3492
|
+
}`})}}),this.pipelines.set(l,c));let u=t.length*o*16,f=this.acquire(t.length*4,u),{selection:d,selected:p,staging:g}=f;try{n.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=n.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=n.createCommandEncoder(),m=h.beginComputePass();m.setPipeline(c),m.setBindGroup(0,f.group);let b=Math.ceil(t.length*o/64);return m.dispatchWorkgroups(Math.min(b,a),Math.ceil(b/a)),m.end(),h.copyBufferToBuffer(p,0,g,0,u),n.queue.submit([h.finish()]),await g.mapAsync(GPUMapMode.READ,0,u),new Float32Array(g.getMappedRange(0,u).slice(0))}finally{this.release(f)}}};var Yn=4,Br=3,vi=1,Eu=2,Yr=`
|
|
3399
3493
|
struct Manifold { ids: vec4u, geo: vec4f } // a, b (a > b), first contact, count (low 4 bits); normal (b to a)
|
|
3400
3494
|
struct Contact { rA: vec3f, key: u32, rB: vec3f, c0x: f32, pen: vec3f, c0y: f32, lam: vec3f, c0z: f32 }
|
|
3401
3495
|
struct Params { prev: u32, now: u32, mask: u32, capacity: u32, pairs: u32, step: u32, dt: f32, pad: u32 }
|
|
@@ -3409,7 +3503,7 @@ struct Params { prev: u32, now: u32, mask: u32, capacity: u32, pairs: u32, step:
|
|
|
3409
3503
|
@group(0) @binding(6) var<storage, read_write> events: array<vec4f>;
|
|
3410
3504
|
@group(0) @binding(7) var<uniform> params: Params;
|
|
3411
3505
|
|
|
3412
|
-
const STRIDE = ${
|
|
3506
|
+
const STRIDE = ${qe/4}u;
|
|
3413
3507
|
|
|
3414
3508
|
fn hash32(x0: u32) -> u32 {
|
|
3415
3509
|
var x = x0;
|
|
@@ -3424,7 +3518,7 @@ fn turn(q: vec4f, v: vec3f) -> vec3f {
|
|
|
3424
3518
|
}
|
|
3425
3519
|
|
|
3426
3520
|
fn slotOf(base: u32, h: u32) -> u32 {
|
|
3427
|
-
return ${
|
|
3521
|
+
return ${Yn}u + 2u * (base + h);
|
|
3428
3522
|
}
|
|
3429
3523
|
|
|
3430
3524
|
fn insert(base: u32, a: u32, b: u32) {
|
|
@@ -3457,16 +3551,16 @@ fn has(base: u32, a: u32, b: u32) -> bool {
|
|
|
3457
3551
|
fn emit(kind: u32, a: u32, b: u32, point: vec3f, impulse: f32, normal: vec3f) {
|
|
3458
3552
|
let e = atomicAdd(&table[0], 1u);
|
|
3459
3553
|
if (e >= params.capacity) { return; }
|
|
3460
|
-
events[${
|
|
3461
|
-
events[${
|
|
3462
|
-
events[${
|
|
3554
|
+
events[${Br}u * e] = bitcast<vec4f>(vec4u(kind, a, b, params.step));
|
|
3555
|
+
events[${Br}u * e + 1u] = vec4f(point, impulse);
|
|
3556
|
+
events[${Br}u * e + 2u] = vec4f(normal, 0.0);
|
|
3463
3557
|
}
|
|
3464
3558
|
|
|
3465
3559
|
/** Per pair this step: into this step's set, and a begin if it wasn't in last step's. */
|
|
3466
3560
|
@compute @workgroup_size(64)
|
|
3467
3561
|
fn touching(@builtin(global_invocation_id) id: vec3u) {
|
|
3468
3562
|
let m = id.x;
|
|
3469
|
-
if (m >= min(counters[${
|
|
3563
|
+
if (m >= min(counters[${hr}u], params.pairs)) { return; }
|
|
3470
3564
|
let mf = manifolds[m];
|
|
3471
3565
|
let count = mf.ids.w & 15u;
|
|
3472
3566
|
let a = mf.ids.x;
|
|
@@ -3475,8 +3569,8 @@ fn touching(@builtin(global_invocation_id) id: vec3u) {
|
|
|
3475
3569
|
insert(params.now, a, b);
|
|
3476
3570
|
if (has(params.prev, a, b)) { return; }
|
|
3477
3571
|
// Where (the points' average, on a) and how hard (the normal force over the step)
|
|
3478
|
-
let pA = bodies[a * STRIDE + ${
|
|
3479
|
-
let qA = bodies[a * STRIDE + ${
|
|
3572
|
+
let pA = bodies[a * STRIDE + ${lr/4}u].xyz;
|
|
3573
|
+
let qA = bodies[a * STRIDE + ${Gr/4}u];
|
|
3480
3574
|
var point = vec3f(0.0);
|
|
3481
3575
|
var force = 0.0;
|
|
3482
3576
|
for (var i = 0u; i < count; i++) {
|
|
@@ -3484,7 +3578,7 @@ fn touching(@builtin(global_invocation_id) id: vec3u) {
|
|
|
3484
3578
|
point += turn(qA, k.rA) + pA;
|
|
3485
3579
|
force += k.lam.x;
|
|
3486
3580
|
}
|
|
3487
|
-
emit(${
|
|
3581
|
+
emit(${vi}u, a, b, point / f32(count), abs(force) * params.dt, mf.geo.xyz);
|
|
3488
3582
|
}
|
|
3489
3583
|
|
|
3490
3584
|
/** Per slot of last step's set: an end if the pair isn't in this step's. */
|
|
@@ -3495,10 +3589,10 @@ fn ended(@builtin(global_invocation_id) id: vec3u) {
|
|
|
3495
3589
|
let k = atomicLoad(&table[s]);
|
|
3496
3590
|
if (k == 0u) { return; }
|
|
3497
3591
|
let b = atomicLoad(&table[s + 1u]);
|
|
3498
|
-
if (!has(params.now, k - 1u, b)) { emit(${
|
|
3592
|
+
if (!has(params.now, k - 1u, b)) { emit(${Eu}u, k - 1u, b, vec3f(0.0), 0.0, vec3f(0.0)); }
|
|
3499
3593
|
}
|
|
3500
|
-
`,
|
|
3501
|
-
fn touching`),
|
|
3594
|
+
`,Mu=Yr.replace("struct Manifold { ids: vec4u, geo: vec4f }","struct Manifold { ids:vec4u, pl:vec4f, anchors:vec4f, geo:vec4f, misc:vec4f }").replace(`const STRIDE = ${qe/4}u;`,"const STRIDE = 6u;").replace(Yr.slice(Yr.indexOf(`@compute @workgroup_size(64)
|
|
3595
|
+
fn touching`),Yr.indexOf("/** Per slot of last step")),`
|
|
3502
3596
|
@compute @workgroup_size(64) fn touching(@builtin(global_invocation_id) id:vec3u){
|
|
3503
3597
|
let m=id.x;let count=min(counters[1u],params.pairs);if(m>=count){return;}
|
|
3504
3598
|
let c=manifolds[m];let a=c.ids.x;let b=c.ids.y;
|
|
@@ -3507,12 +3601,12 @@ fn touching`),hr.indexOf("/** Per slot of last step")),`
|
|
|
3507
3601
|
insert(params.now,a,b);if(has(params.prev,a,b)){return;}
|
|
3508
3602
|
let pose=bodies[a*STRIDE];let cs=cos(pose.z);let sn=sin(pose.z);
|
|
3509
3603
|
let r=c.anchors.xy;let point=pose.xy+vec2f(cs*r.x-sn*r.y,sn*r.x+cs*r.y);
|
|
3510
|
-
emit(${
|
|
3604
|
+
emit(${vi}u,a,b,vec3f(point,0.),abs(c.pl.z)*params.dt,vec3f(c.geo.zw,0.));
|
|
3511
3605
|
}
|
|
3512
3606
|
|
|
3513
|
-
`),Nt=class{device;pipelines;layout;watch;table;events;params;setSlots;capacity;group=null;bound=[];flip=0;bodies;constructor(e,t,i,r,s,a=3){this.device=e,this.bodies=t,this.capacity=s,this.setSlots=2**Math.ceil(Math.log2(Math.max(64,2*r)));let n=e.createShaderModule({label:"contact events",code:a===2?Jl:hr}),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,h)=>({binding:h,visibility:GPUShaderStage.COMPUTE,buffer:{type:d}}))});let c=e.createPipelineLayout({bindGroupLayouts:[this.layout]}),f=d=>e.createComputePipeline({label:`contact events ${d}`,layout:c,compute:{module:n,entryPoint:d}});this.pipelines={touching:f("touching"),ended:f("ended")};let u=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.watch=e.createBuffer({label:"contact watch",size:Math.max(16,i*4),usage:u}),this.table=e.createBuffer({label:"contact sets",size:(To+4*this.setSlots)*4,usage:u}),this.events=e.createBuffer({label:"contact events",size:s*qi*16,usage:u}),this.params=e.createBuffer({label:"contact params",size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST})}setWatched(e,t){let i=e.map((r,s)=>[r,t[s]?1:0]).sort((r,s)=>r[0]-s[0]);for(let r=0;r<i.length;){let s=r+1;for(;s<i.length&&i[s][0]===i[s-1][0]+1;)s++;this.device.queue.writeBuffer(this.watch,i[r][0]*4,Uint32Array.from(i.slice(r,s),a=>a[1])),r=s}}run(e,t,i,r){let s=this.device,a=[this.bodies,e.manifolds,e.contacts,e.counters,this.watch,this.table,this.events,this.params];(!this.group||a.some((d,h)=>d!==this.bound[h]))&&(this.bound=a,this.group=s.createBindGroup({layout:this.layout,entries:a.map((d,h)=>({binding:h,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,s.queue.writeBuffer(this.params,0,c);let f=s.createCommandEncoder({label:"contact events"});f.clearBuffer(this.table,(To+2*l)*4,2*this.setSlots*4);let u=f.beginComputePass({label:"contact events"});u.setBindGroup(0,this.group),u.setPipeline(this.pipelines.touching),u.dispatchWorkgroups(Math.max(1,Math.ceil(t/64))),u.setPipeline(this.pipelines.ended),u.dispatchWorkgroups(Math.ceil(this.setSlots/64)),u.end(),s.queue.submit([f.finish()])}async read(){let e=this.device,t=16+this.capacity*qi*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 s=i.getMappedRange().slice(0);i.unmap(),i.destroy();let a=new Uint32Array(s),n=new Float32Array(s),l=Math.min(a[0],this.capacity),c=[];for(let f=0;f<l;f++){let u=4+f*qi*4;c.push({kind:a[u],a:a[u+1],b:a[u+2],step:a[u+3],point:[n[u+4],n[u+5],n[u+6]],impulse:n[u+7],normal:[n[u+8],n[u+9],n[u+10]]})}return c.sort((f,u)=>f.step-u.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 $e=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),Le=o=>o&&typeof o.x=="number"?[o.x,o.y,o.z,...typeof o.w=="number"?[o.w]:[]]:Array.from(o),pr=new WeakMap,Vi=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o},os=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},as=class o{static async create({device:e,...t}={}){if(t.scene&&pr.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await jo({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return r=new o(e,t),r.ownsDevice=i,r.scene&&r.autoAttach&&r.attachToScene(),r}catch(s){throw r?r.dispose():i&&e.destroy(),s}}constructor(e,{scene:t,gravity:i=[0,-10,0],timeStep:r=1/60,iterations:s=10,capacity:a=4096,syncMeshes:n=!0,maxSyncedBodies:l=2048,broadphase:c="auto",solverMode:f="auto",bvh:u={},autoAttach:d=!0,maxContactPairs:h=8192,maxContactEvents:m=4096,sleeping:p=!1}={}){if(this.device=e,this.scene=t,this.autoAttach=d,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:u,solverMode:f},this.ref=new Pi,this.ref.dt=os(r,"timeStep"),!Number.isInteger(s)||s<1)throw Error("iterations must be a positive integer");this.ref.iterations=s,this.aggregates=[],this.constraints=new Set,this.contactListeners=new Set;for(let[y,A]of Object.entries({maxContactPairs:h,maxContactEvents:m}))if(!Number.isInteger(A)||A<1)throw Error(`${y} must be a positive integer`);if(this.contactOptions={maxContactPairs:h,maxContactEvents:m},this.bodyReadback=new Ft(e),this.sleepOptions=p,p!==!1&&p!==!0&&(typeof p!="object"||p===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&&pr.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 Ni(this.device,this.ref,rs(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 s=e.get(r);s&&(s.attach(i++,!1),(s.type==="fixed"||s.type==="spring"&&s.rest===void 0)&&t.push({slot:s.slot,spring:s.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 li(this,this.sleepOptions===!0?{}:this.sleepOptions))}addAggregate(e,t,i={}){return new ns(e,t,i,this)}addAggregates(e){return e.map(({mesh:t,type:i,options:r})=>this.addAggregate(t,i,r))}addConstraint(e,t,i={}){return new fr(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 li(this,e)),this}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new dr(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new Nt(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===Ut?"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[s]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}async sphereCastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){this.assertAlive(),Vi(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}={}){Vi(i),this.initialize(),this.flushCommands();let s=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&&os(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(s,{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 s=Le(r);if(s.length!==(i==="rotation"?4:3)||!s.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]=s,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 p of this.commands.values())i|=p.mask;if(i===1||i===2){if(!this.velocityIndices||this.velocityIndices.length<t){let A=Math.max(t,(this.velocityIndices?.length??0)*2,64);this.velocityIndices=new Uint32Array(A),this.velocityValues=new Float32Array(A*3)}let p=0;for(let[A,w]of this.commands)this.velocityIndices[p]=A.aggregate.gpuSlot??this.gpu.gpuIndex(A.aggregate.index),this.velocityValues.set(i===2?w.angular:w.velocity,p++*3);let b=i===2?"angularVelocityBatch":"velocityBatch",y=this[b]??=new Ct(this.gpu,3,i===2?"angular":"linear");y.solver=this.gpu,y.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 p of this.commands.values())if((p.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 p=0;for(let[b,y]of this.commands){let A=p++*8;this.motionData[A]=b.aggregate.gpuSlot??this.gpu.gpuIndex(b.aggregate.index),this.motionData[A+1]=y.mask,y.velocity&&this.motionFloats.set(y.velocity,A+2),y.angular&&this.motionFloats.set(y.angular,A+5)}this.motionBatch??=new Gt(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 s=t*128,a=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize);if(s>a)throw Error("Body command batch exceeds GPU buffer limits");(!this.commandData||this.commandData.byteLength<s)&&(this.commandData=new ArrayBuffer(Math.min(a,Math.max(s,(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 f=0;for(let[p,b]of this.commands){let y=f++*32;c[y]=p.aggregate.gpuSlot??this.gpu.gpuIndex(p.aggregate.index),c[y+1]=b.mask;for(let[A,w]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])b[A]&&l.set(b[A],y+w)}this.commands.clear(),(!this.commandBuffer||this.commandBuffer.size<s)&&(this.commandBuffer?.destroy(),this.commandBuffer=this.device.createBuffer({size:Math.min(a,Math.max(s*2,128)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),this.device.queue.writeBuffer(this.commandBuffer,0,n,0,s),this.commandPipeline??=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3514
|
-
${un}`,label:"AVBD body commands"}),entryPoint:"applyCommands"}}),(!this.commandBinding||this.boundCommandBody!==this.gpu.bodyBuffer||this.boundCommandBuffer!==this.commandBuffer||this.boundCommandBytes!==s)&&(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:s}}]}),this.boundCommandBody=this.gpu.bodyBuffer,this.boundCommandBuffer=this.commandBuffer,this.boundCommandBytes=s);let u=e??this.device.createCommandEncoder(),d=u.beginComputePass();d.setPipeline(this.commandPipeline),d.setBindGroup(0,this.commandBinding);let h=Math.ceil(f/64),m=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(h,m),Math.ceil(h/m)),d.end(),e||this.device.queue.submit([u.finish()]),this.lastCommandBatch={bodies:t,path:"aggregate-commands",uploadedBytes:s}}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 s of this.contactListeners)s(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&&pr.get(this.scene)===this&&pr.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}},ns=class{constructor(e,t,{mass:i=1,friction:r=.6,restitution:s=0,allowSleep:a,isTrigger:n=!1,sync:l=!0,group:c=4294967295,collidesWith:f=4294967295}={},u){if(u=u instanceof as?u:pr.get(u??e.getScene()),!u)throw Error("Create an AVBD world for this scene before adding aggregates");if(u.assertAlive(),typeof t=="number"&&(t=[$e.SPHERE,$e.CAPSULE,$e.CYLINDER,$e.BOX,$e.CONVEX_HULL][t]),os(i,"mass",!0),os(r,"friction",!0),!Number.isFinite(s)||s<0||s>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($e).includes(t))throw Error(`Unsupported AVBD shape: ${t}`);if(u.aggregates.length>=u.capacity)throw Error("AVBD body capacity exceeded");if(this.world=u,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=s,a===!0&&!u.sleepOptions&&u.enableSleeping(),this.group=Vi(c),this.collidesWith=Vi(f),u.gpu&&u.syncEnabled&&l&&u.aggregates.filter(g=>g.sync&&!g.disposed).length>=u.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(![$e.BOX,$e.SPHERE,$e.CAPSULE].includes(t)&&u.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Convex hull collisions require nine storage buffer bindings");e.computeWorldMatrix(!0),e.refreshBoundingInfo();let d=e.getBoundingInfo().boundingBox,h=e.scaling;if([h.x,h.y,h.z].some(g=>g<=0))throw Error("Bake negative or zero mesh scaling before creating an aggregate");let m=d.maximum.subtract(d.minimum).multiply(h),p=d.center.multiply(h),b=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=p,this.localRotation=b.constructor.Identity();let y;if(t===$e.BOX)y=new at(u.ref,Le(m),1,r,[0,0,0]);else if(t===$e.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");y=Rr(u.ref,m.x/2,1,r,[0,0,0])}else if(t===$e.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");y=ts(u.ref,m.x/2,m.y,1,r,[0,0,0])}else{let g=e.getVerticesData("position");if(!g)throw Error("Hull aggregate requires mesh positions");let x=Float32Array.from(g,(B,E)=>B*[h.x,h.y,h.z][E%3]),v=er(x);if(!v)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...v.center),this.localRotation=b.constructor.FromArray(v.rotation),y=Or(u.ref,v,1,r,[0,0,0])}let A=i/y.mass;y.mass=i,y.moment.forEach((g,x)=>y.moment[x]=g*A);let w=e.position.add(this.localCenter.applyRotationQuaternion(b));if(y.positionLin.set(Le(w)),y.positionAng.set(b.multiply(this.localRotation).asArray()),this.rigid=y,this.index=u.aggregates.length,this.body=new ls(this),u.aggregates.push(this),e.rotationQuaternion=b,u.gpu){if(this.gpuSlot=u.gpu.addBody(y),this.gpuSlot<0)throw Error("AVBD body capacity exceeded");u.byGpuIndex.set(this.gpuSlot,this.body),u.gpu.setFilters([this.gpuSlot],[this.group],[this.collidesWith]),(this.isTrigger||u.gpu.sensorsEnabled)&&u.gpu.setSensor(this.gpuSlot,this.isTrigger),this.restitution>0&&u.gpu.setRestitution(this.gpuSlot,this.restitution),u.contactWatch?.setWatched([this.gpuSlot],[!0]),u.sleepManager?.register(this.gpuSlot,y.mass,this.allowSleep),u.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(){if(this.disposed)return;let e=this.world;for(let t of[...e.constraints])(t.bodyA===this.body||t.bodyB===this.body)&&t.dispose();if(e.gpu){let t=this.body.gpuIndex;e.queue(this.body,16,"velocity",[0,0,0]),e.gpu.setFilters([t],[0],[0]),e.gpu.setRestitution(t,0)}this.rigid.mass=0,this.rigid.moment.fill(0),this.sync=!1,this.disposed=this.body.disposed=!0,e.sleepManager?.register(this.gpuSlot??e.gpu?.gpuIndex(this.index)??this.index,0),e.wakeAll?.()}},ls=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,s=Le(e),a=r.commands.get(this),n=s.map((c,f)=>c+(a?.impulse?.[f]||0)),l=r.queue(this,4,"impulse",n);if(t){let c=[i[1]*s[2]-i[2]*s[1],i[2]*s[0]-i[0]*s[2],i[0]*s[1]-i[1]*s[0]],f=l.torque||[0,0,0];l.torque=Le(c).map((u,d)=>u+f[d]),l.pointImpulse=s.map((u,d)=>u+(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,s)=>r+i[s])),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=Vi(e),s=Vi(t);return i.group=r,i.collidesWith=s,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=this.aggregate.mesh.rotationQuaternion){let i=t.clone().normalize(),r=this.aggregate;return r.world.queue(this,8,"position",Le(e.add(r.localCenter.applyRotationQuaternion(i)))),r.world.queue(this,8,"rotation",i.multiply(r.localRotation).asArray()),this}};var ci=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 dn=o=>o<0?-1:o>0?1:0,Hi=(o,e)=>o<e?o:e,$l=(o,e)=>o>e?o:e,Wi=(o,e,t)=>$l(e,Hi(t,o));function mr(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function ui(o,e,t){let i=Math.cos(o[2]),r=Math.sin(o[2]);return[i*e-r*t+o[0],r*e+i*t+o[1]]}function kt(o,e,t,i,r){o[e*3]=t,o[e*3+1]=i,o[e*3+2]=r}var cs=class extends ci{rA;rB;C0=new Float64Array(3);torqueArm;restAngle;constructor(e,t,i,r,s,a=[1/0,1/0,1/0],n=1/0){super(e,t,i),this.rA=[r[0],r[1]],this.rB=[s[0],s[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?ui(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=ui(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=mr(e.position[2],this.rA[0],this.rA[1]);kt(t,0,1,0,-r[1]),kt(t,1,0,1,r[0]),kt(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=mr(e.position[2],this.rB[0],this.rB[1]);kt(t,0,-1,0,r[1]),kt(t,1,0,-1,-r[0]),kt(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},us=class extends ci{rA;rB;rest;constructor(e,t,i,r,s,a,n=-1){if(super(e,t,i),this.rA=[r[0],r[1]],this.rB=[s[0],s[1]],this.stiffness[0]=a,this.rest=n,this.rest<0){let l=ui(t.position,r[0],r[1]),c=ui(i.position,s[0],s[1]);this.rest=Math.hypot(l[0]-c[0],l[1]-c[1])}}rows(){return 1}initialize(){return!0}delta(){let e=ui(this.bodyA.position,this.rA[0],this.rA[1]),t=ui(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),s=t[0]/r,a=t[1]/r,n=(1-s*s)/r,l=(0-s*a)/r,c=(0-a*s)/r,f=(1-a*a)/r,u=e===this.bodyA,d=u?this.rA:this.rB,h=mr(e.position[2],-d[1],d[0]),m=mr(e.position[2],d[0],d[1]),p=n*h[0]+l*h[1],b=c*h[0]+f*h[1],y=s*m[0]+a*m[1],A=s*h[0]+a*h[1];u?kt(this.J,0,s,a,A):kt(this.J,0,-s,-a,-A);let w=u?-y-y:y+y,g=this.H;g[0]=n,g[1]=l,g[2]=p,g[3]=c,g[4]=f,g[5]=b,g[6]=p,g[7]=b,g[8]=w}},fs=class extends ci{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},ds=class extends ci{speed;constructor(e,t,i,r,s){super(e,t,i),this.speed=r,this.fmax[0]=s,this.fmin[0]=-s}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){kt(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var hs=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),ps=()=>({...hs(),postStabilize:!1,alpha:.95,matchNearest:!0});var Ji=(o,e)=>o>e?o*2097152+e:e*2097152+o;function Xl(o,e,t){let i=0,r=e;for(;i<r;){let s=i+r>>>1;o[s]<t?i=s+1:r=s}return i}function Do(o,e,t){let i=Xl(o,e,t);return i<e&&o[i]===t}var ms=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,s=0;for(let n=0;n<e;n++)i[n]<=4*r&&(s=Math.max(s,i[n]));this.cellSize=Math.max(2*s,.001),this.isLarge=new Uint8Array(e);let a=[];for(let n=0;n<e;n++)t[n*4+1]>s&&(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,s,a){e!==this.configuredFor&&this.configure(e,i),this.pairCount=0;let{cellX:n,cellY:l,isLarge:c,bucketStart:f,sorted:u}=this,d=1/this.cellSize;f.fill(0);for(let p=0;p<e;p++)c[p]||(n[p]=Math.floor(t[p*4]*d),l[p]=Math.floor(t[p*4+1]*d),f[this.hash(n[p],l[p])+1]++);for(let p=0;p<this.tableSize;p++)f[p+1]+=f[p];let h=f.slice(0,this.tableSize);for(let p=0;p<e;p++)c[p]||(u[h[this.hash(n[p],l[p])]++]=p);let m=(p,b)=>{if(!r[p]&&!r[b])return;let y=t[p*4]-t[b*4],A=t[p*4+1]-t[b*4+1],w=i[p*4+1]+i[b*4+1];if(y*y+A*A>w*w)return;let g=Ji(p,b);if(!Do(s,a,g)){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++]=g}};for(let p=0;p<e;p++){if(c[p])continue;let b=n[p],y=l[p];for(let A=-1;A<=1;A++)for(let w=-1;w<=1;w++){let g=this.hash(b+w,y+A);for(let x=f[g];x<f[g+1];x++){let v=u[x];v>=p||n[v]!==b+w||l[v]!==y+A||m(p,v)}}}for(let p of this.large)for(let b=0;b<e;b++)b===p||c[b]&&b>p||m(p,b);this.pairs.subarray(0,this.pairCount).sort()}};var Xe=new Float64Array(12),hn=new Float64Array(12),fi=new Float64Array(12);function yt(o,e,t,i,r,s,a,n){let l=e*6;o[l]=t,o[l+1]=i,o[l+2]=r,o[l+3]=s,o[l+4]=a,o[l+5]=n}function pn(o,e,t,i,r,s){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++)o[a*6+c]=e[c];a++}if(l<=0){for(let c=0;c<6;c++)o[a*6+c]=e[6+c];a++}if(n*l<0){let c=n/(n-l),f=e[0]+(e[6]-e[0])*c,u=e[1]+(e[7]-e[1])*c;n>0?yt(o,a,f,u,s,e[3],0,e[5]):yt(o,a,f,u,e[8],s,e[10],0),a++}return a}function bs(o,e,t,i,r,s,a,n){let l=-(r*a+s*n),c=-(-s*a+r*n);Math.abs(l)>Math.abs(c)?l>0?(yt(Xe,0,o,-e,0,0,3,4),yt(Xe,1,o,e,0,0,4,1)):(yt(Xe,0,-o,e,0,0,1,2),yt(Xe,1,-o,-e,0,0,2,3)):c>0?(yt(Xe,0,o,e,0,0,4,1),yt(Xe,1,-o,e,0,0,1,2)):(yt(Xe,0,-o,-e,0,0,2,3),yt(Xe,1,o,-e,0,0,3,4));for(let f=0;f<2;f++){let u=f*6,d=Xe[u],h=Xe[u+1];Xe[u]=t+r*d-s*h,Xe[u+1]=i+s*d+r*h}}function mn(o,e,t,i,r,s,a,n,l,c,f){let u=Math.cos(t),d=Math.sin(t),h=Math.cos(n),m=Math.sin(n),p=s-o,b=a-e,y=u*p+d*b,A=-d*p+u*b,w=h*p+m*b,g=-m*p+h*b,x=Math.abs(u*h+d*m),v=Math.abs(u*-m+d*h),B=Math.abs(-d*h+u*m),E=Math.abs(-d*-m+u*h),k=Math.abs(y)-i-(x*l+v*c),z=Math.abs(A)-r-(B*l+E*c);if(k>0||z>0)return 0;let M=Math.abs(w)-(x*i+B*r)-l,P=Math.abs(g)-(v*i+E*r)-c;if(M>0||P>0)return 0;let _=0,S=k,C=y>0?u:-u,R=y>0?d:-d;z>.95*S+.01*r&&(_=1,S=z,C=A>0?-d:d,R=A>0?u:-u),M>.95*S+.01*l&&(_=2,S=M,C=w>0?h:-h,R=w>0?m:-m),P>.95*S+.01*c&&(_=3,S=P,C=g>0?-m:m,R=g>0?h:-h);let I,O,q,G,j,F,W,ee,ne;if(_===0){I=C,O=R,q=o*I+e*O+i,G=-d,j=u;let N=o*G+e*j;F=-N+r,W=N+r,ee=3,ne=1,bs(l,c,s,a,h,m,I,O)}else if(_===1){I=C,O=R,q=o*I+e*O+r,G=u,j=d;let N=o*G+e*j;F=-N+i,W=N+i,ee=2,ne=4,bs(l,c,s,a,h,m,I,O)}else if(_===2){I=-C,O=-R,q=s*I+a*O+l,G=-m,j=h;let N=s*G+a*j;F=-N+c,W=N+c,ee=3,ne=1,bs(i,r,o,e,u,d,I,O)}else{I=-C,O=-R,q=s*I+a*O+c,G=h,j=m;let N=s*G+a*j;F=-N+l,W=N+l,ee=2,ne=4,bs(i,r,o,e,u,d,I,O)}if(pn(hn,Xe,-G,-j,F,ee)<2||pn(fi,hn,G,j,W,ne)<2)return 0;let ve=0;for(let N=0;N<2;N++){let Ee=N*6,ze=fi[Ee],le=fi[Ee+1],Fe=I*ze+O*le-q;if(Fe>0)continue;let Lt=ze-I*Fe,Me=le-O*Fe,Ne=fi[Ee+2],Ie=fi[Ee+3],Ue=fi[Ee+4],Ze=fi[Ee+5],bi,gi,yi,vi;_>=2?([Ne,Ie,Ue,Ze]=[Ue,Ze,Ne,Ie],bi=ze-o,gi=le-e,yi=Lt-s,vi=Me-a):(bi=Lt-o,gi=Me-e,yi=ze-s,vi=le-a);let At=ve*7;f[At+0]=Ne&255|(Ie&255)<<8|(Ue&255)<<16|(Ze&255)<<24,f[At+1]=u*bi+d*gi,f[At+1+1]=-d*bi+u*gi,f[At+3]=h*yi+m*vi,f[At+3+1]=-m*yi+h*vi,f[At+5]=-C,f[At+5+1]=-R,ve++}return ve}var bn=o=>31-Math.clz32(o&-o);function Yl(o){let e=o+2654435769;return e^=e>>>16,e=Math.imul(e,2146121005),e^=e>>>15,e=Math.imul(e,2221713035),e^=e>>>16,e>>>0}var Go=(o,e,t,i)=>o>t||o===t&&e>i,gs=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)=>Yl(r))}run(e,t,i,r,s,a){this.resize(e);let{colors:n,next:l,pending:c,prio:f}=this,u=(g,x)=>{let v=s[g*4+1],B=v===x?s[g*4+2]:v;return B>=0&&t[B]?B:-1},d=(g,x)=>{let v=0,B=0;for(let E=i[g];E<i[g+1];E++){let k=u(r[E],g);if(k<0||x&&c[k])continue;let z=n[k];z<0||(z<32?v|=1<<z:B|=1<<z-32)}return~v!==0?bn(~v):~B!==0?32+bn(~B):63};for(let g=0;g<e;g++){if(l[g]=n[g],!t[g]||n[g]<0)continue;let x=!0;for(let v=i[g];v<i[g+1]&&x;v++){let B=u(r[v],g);B>=0&&!Go(f[g],g,f[B],B)&&(x=!1)}x&&(l[g]=Math.min(n[g],d(g,!1)))}n.set(l);let h=()=>{let g=0;for(let x=0;x<e;x++)if(c[x]=0,!!t[x]){if(n[x]<0)c[x]=1;else for(let v=i[x];v<i[x+1];v++){let B=u(r[v],x);if(B>=0&&n[B]===n[x]&&Go(f[B],B,f[x],x)){c[x]=1;break}}g+=c[x]}return g},m=h(),p=0;for(;m>0&&p<a;){p++;for(let g=0;g<e;g++){if(l[g]=-2,!c[g])continue;let x=!0;for(let v=i[g];v<i[g+1]&&x;v++){let B=u(r[v],g);B>=0&&c[B]&&!Go(f[g],g,f[B],B)&&(x=!1)}x&&(l[g]=d(g,!0))}for(let g=0;g<e;g++)l[g]!==-2&&(n[g]=l[g],c[g]=0,m--)}let b=0;if(m>0){for(let g=0;g<e;g++)c[g]&&n[g]<0&&(n[g]=0);b=h()}let y=this.colorStart;y.fill(0);let A=0;for(let g=0;g<e;g++)t[g]&&(y[n[g]+1]++,A=Math.max(A,n[g]+1));for(let g=0;g<64;g++)y[g+1]+=y[g];let w=y.slice(0,64);for(let g=0;g<e;g++)t[g]&&(this.colorBodies[w[n[g]]++]=g);return{numColors:A,conflicts:b,rounds:p}}};var An=0,Vt=1,Bn=2,ys=3,Fo=4,wn=[0,3,1,1,2],ec=.05,D=4,T=32,be=0,ae=3,$=6,ft=9,dt=12,di=15,ot=18,H=21,K=23,ge=25,_e=26,vt=27,br=28,vs=class{params=hs();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 ms;coloring=new gs;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,s=[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([s[0],s[1],s[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*D),this.data=this.grow(this.data,t*T),this.contactCount>0&&(this.info.copyWithin((e+1)*D,e*D,(t-1)*D),this.data.copyWithin((e+1)*T,e*T,(t-1)*T)),this.data.fill(0,e*T,e*T+T);for(let i=0;i<3;i++)this.data[e*T+$+i]=1/0,this.data[e*T+ft+i]=-1/0,this.data[e*T+dt+i]=1/0,this.data[e*T+di+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*D)}addJoint(e,t,i,r,s=[1/0,1/0,1/0],a=1/0){let n=this.allocConstraint();this.setInfo(n,Vt,e,t);let l=this.data,c=n*T;l.set(s,c+$),l[c+dt+2]=a,l[c+ft+2]=-a,l[c+di+2]=a,l.set(i,c+H),l.set(r,c+K),l[c+ge]=(e>=0?this.pose[e*4+2]:0)-this.pose[t*4+2];let f=(e>=0?this.shape[e*4]:0)+this.shape[t*4],u=(e>=0?this.shape[e*4+1]:0)+this.shape[t*4+1];l[c+_e]=f*f+u*u;let d={slot:n,alive:!0};return this.handles[n]=d,d}setJointWorldAnchor(e,t,i){e.alive&&(this.data[e.slot*T+H]=t,this.data[e.slot*T+H+1]=i)}removeJoint(e){e.alive&&(this.disable(e.slot),e.alive=!1)}addSpring(e,t,i,r,s,a){let n=this.allocConstraint();this.setInfo(n,Bn,e,t);let l=n*T;this.data[l+$]=s,this.data.set(i,l+H),this.data.set(r,l+K),this.data[l+ge]=a}addMotor(e,t,i,r){let s=this.allocConstraint();this.setInfo(s,ys,e,t);let a=s*T;this.data[a+dt]=r,this.data[a+ft]=-r,this.data[a+ge]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(Ji(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(hs());Object.assign(this.params,Object.fromEntries(t.map(r=>[r,e[r]])));let i=new Map(e.bodies.map((r,s)=>[r,s]));for(let r of e.bodies){let s=this.addBody(r.size,0,r.friction,r.position,r.velocity);this.shape[s*4+2]=r.mass,this.shape[s*4+3]=r.moment,this.dynamic[s]=r.mass>0?1:0}for(let r of e.forces){let s=r.bodyA?i.get(r.bodyA):-1,a=i.get(r.bodyB);if(r instanceof cs){let n=this.addJoint(s,a,r.rA,r.rB,[r.stiffness[0],r.stiffness[1],r.stiffness[2]],r.fracture[2]);this.data[n.slot*T+ge]=r.restAngle,this.data[n.slot*T+_e]=r.torqueArm}else r instanceof us?this.addSpring(s,a,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof ds?this.addMotor(s,a,r.speed,r.fmax[0]):r instanceof fs&&this.addIgnoreCollision(s,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 s=0;s<r;s++){let a=t?s<e.iterations?1:0:i;this.primal(a),this.mark("primal"),s<e.iterations&&this.dual(a),s===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*D),this.prevData=new Float64Array(e*2*T));for(let i=0;i<e;i++){let r=t+i;this.prevKeys[i]=Ji(this.info[r*D+1],this.info[r*D+2])}this.prevInfo.set(this.info.subarray(t*D,(t+e)*D)),this.prevData.set(this.data.subarray(t*T,(t+e)*T)),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*D+1],r=this.info[t*D+2];this.info[t*D]!==An&&i>=0&&e.push(Ji(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 s=0;s<this.jointCount;s++){let a=s*T;if(e[s*D]===Vt){if(t[a+$]===0&&t[a+$+1]===0&&t[a+$+2]===0){let f=this.handles[s];f&&(f.alive=!1);continue}let l=e[s*D+1],c=e[s*D+2];this.jointC(l,c,a,i,t,a+ot)}r!==s&&(e.copyWithin(r*D,s*D,(s+1)*D),t.copyWithin(r*T,a,a+T),this.handles[r]=this.handles[s],this.handles[r]&&(this.handles[r].slot=r)),r++}if(r!==this.jointCount){for(let s=r;s<this.jointCount;s++)this.handles[s]=null;this.jointCount=r,this.noCollideDirty=!0}}jointC(e,t,i,r,s,a,n=s){let l,c,f;if(e>=0){let p=Math.cos(r[e*4+2]),b=Math.sin(r[e*4+2]),y=s[i+H],A=s[i+H+1];l=p*y-b*A+r[e*4],c=b*y+p*A+r[e*4+1],f=r[e*4+2]}else l=s[i+H],c=s[i+H+1],f=0;let u=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),h=s[i+K],m=s[i+K+1];n[a]=l-(u*h-d*m+r[t*4]),n[a+1]=c-(d*h+u*m+r[t*4+1]),n[a+2]=(f-r[t*4+2]-s[i+ge])*s[i+_e]}narrowphase(){let{pose:e,shape:t,props:i}=this,r=this.broadphase.pairs,s=this.contactOut;for(let a=0;a<this.broadphase.pairCount;a++){let n=r[a],l=Math.floor(n/2097152),c=n-l*2097152,f=mn(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,s);if(f===0)continue;let u=0,d=this.prevCount;for(;u<d;){let p=u+d>>>1;this.prevKeys[p]<n?u=p+1:d=p}let h=u,m=Math.sqrt(i[l*4]*i[c*4]);for(let p=0;p<f;p++){let b=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(b+1)*D),this.data=this.grow(this.data,(b+1)*T);let y=this.data,A=b*T,w=p*7,g=s[w+0];this.info.set([Fo,l,c,g],b*D),y.fill(0,A,A+T),y[A+$]=y[A+$+1]=1/0,y[A+ft]=-1/0,y[A+dt]=0,y[A+di]=y[A+di+1]=1/0,y[A+H]=s[w+1],y[A+H+1]=s[w+1+1],y[A+K]=s[w+3],y[A+K+1]=s[w+3+1],y[A+ge]=m,y[A+_e]=s[w+5],y[A+vt]=s[w+5+1];let x=-1;for(let S=h;S<this.prevCount&&this.prevKeys[S]===n;S++)this.prevInfo[S*D+3]===g&&(x=S);if(x<0&&this.params.matchNearest){let S=ec*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let C=h;C<this.prevCount&&this.prevKeys[C]===n;C++){let R=Math.hypot(this.prevData[C*T+H]-y[A+H],this.prevData[C*T+H+1]-y[A+H+1]);R<=S&&(S=R,x=C)}}if(x>=0){let S=x*T;y[A+be]=this.prevData[S+be],y[A+be+1]=this.prevData[S+be+1],y[A+ae]=this.prevData[S+ae],y[A+ae+1]=this.prevData[S+ae+1],this.prevData[S+br]&&(y[A+H]=this.prevData[S+H],y[A+H+1]=this.prevData[S+H+1],y[A+K]=this.prevData[S+K],y[A+K+1]=this.prevData[S+K+1])}let[v,B]=qt(e[l*4+2],y[A+H],y[A+H+1]),[E,k]=qt(e[c*4+2],y[A+K],y[A+K+1]),z=y[A+_e],M=y[A+vt],P=e[l*4]+v-e[c*4]-E,_=e[l*4+1]+B-e[c*4+1]-k;y[A+ot]=z*P+M*_+5e-4,y[A+ot+1]=M*P-z*_}}}warmStartConstraints(e,t){let i=this.params,r=this.data,s=this.jointCount+this.contactCount;for(let a=0;a<s;a++){let n=wn[this.info[a*D]],l=a*T;for(let c=0;c<n;c++){if(i.vbd){r[l+be+c]=Hi(r[l+$+c],i.vbdStiffness);continue}e||(r[l+ae+c]=r[l+ae+c]*t*i.gamma),r[l+be+c]=Hi(Wi(r[l+be+c]*i.gamma,1,1e9),r[l+$+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 s=0;s<t;s++)for(let a=1;a<=2;a++){let n=this.info[s*D+a];n>=0&&this.dynamic[n]&&i[n+1]++}for(let s=0;s<e;s++)i[s+1]+=i[s];let r=i.slice(0,e);for(let s=0;s<t;s++)for(let a=1;a<=2;a++){let n=this.info[s*D+a];n>=0&&this.dynamic[n]&&(this.adjList[r[n]++]=s)}}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:s,inertial:a,prevVelocity:n}=this;for(let l=0;l<this.bodyCount;l++){let c=l*4;r[c+2]=Wi(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 f=(r[c+1]-n[c+1])/e*dn(t),u=Wi(f/Math.abs(t),0,1);Number.isFinite(u)||(u=0),s[c]=i[c],s[c+1]=i[c+1],s[c+2]=i[c+2],i[c]=i[c]+r[c]*e,i[c+1]=i[c+1]+r[c+1]*e+t*u*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:s,pose:a,initial:n}=this,l=r[e*D],c=r[e*D+1],f=r[e*D+2],u=e*T,{eC:d,eMin:h,eMax:m,eJ:p,eG:b}=this,y=wn[l];for(let w=0;w<y;w++)h[w]=s[u+ft+w],m[w]=s[u+dt+w];let A=i===c;if(b.fill(0),l===Vt){this.jointC(c,f,u,a,s,0,d);for(let w=0;w<3;w++)s[u+$+w]===1/0&&(d[w]-=s[u+ot+w]*t);if(i>=0){let w=a[i*4+2],[g,x]=qt(w,s[u+(A?H:K)],s[u+(A?H:K)+1]),v=A?1:-1;p[0]=v,p[1]=0,p[2]=-v*x,p[3]=0,p[4]=v,p[5]=v*g,p[6]=0,p[7]=0,p[8]=v*s[u+_e],b[2]=Math.abs(g),b[5]=Math.abs(x)}}else if(l===Bn){let[w,g]=Sn(a,c,s[u+H],s[u+H+1]),[x,v]=Sn(a,f,s[u+K],s[u+K+1]),B=w-x,E=g-v,k=B*B+E*E,z=Math.sqrt(k);if(d[0]=z-s[u+ge],i>=0&&k!==0){let M=B/z,P=E/z,_=(1-M*M)/z,S=-M*P/z,C=(1-P*P)/z,R=a[i*4+2],I=A?H:K,[O,q]=qt(R,-s[u+I+1],s[u+I]),[G,j]=qt(R,s[u+I],s[u+I+1]),F=_*O+S*q,W=S*O+C*q,ee=M*G+P*j,ne=M*O+P*q,ve=A?1:-1;p[0]=ve*M,p[1]=ve*P,p[2]=ve*ne;let N=A?-ee-ee:ee+ee;b[0]=Math.hypot(_,S,F),b[1]=Math.hypot(S,C,W),b[2]=Math.hypot(F,W,N)}else i>=0&&p.fill(0,0,3)}else if(l===ys){let w=c>=0?a[c*4+2]-n[c*4+2]:0,g=a[f*4+2]-n[f*4+2];d[0]=w-g-s[u+ge]*this.params.dt,i>=0&&(p[0]=0,p[1]=0,p[2]=A?1:-1)}else if(l===Fo){let w=s[u+_e],g=s[u+vt],x=g,v=-w,[B,E]=qt(n[c*4+2],s[u+H],s[u+H+1]),[k,z]=qt(n[f*4+2],s[u+K],s[u+K+1]),M=B*g-E*w,P=-(k*g-z*w),_=B*v-E*x,S=-(k*v-z*x),C=a[c*4]-n[c*4],R=a[c*4+1]-n[c*4+1],I=a[c*4+2]-n[c*4+2],O=a[f*4]-n[f*4],q=a[f*4+1]-n[f*4+1],G=a[f*4+2]-n[f*4+2];d[0]=s[u+ot]*(1-t)+w*C+g*R+M*I+-w*O+-g*q+P*G,d[1]=s[u+ot+1]*(1-t)+x*C+v*R+_*I+-x*O+-v*q+S*G;let j=Math.abs(s[u+ae])*s[u+ge];m[1]=j,h[1]=-j,i>=0&&(A?(p[0]=w,p[1]=g,p[2]=M,p[3]=x,p[4]=v,p[5]=_):(p[0]=-w,p[1]=-g,p[2]=P,p[3]=-x,p[4]=-v,p[5]=S))}return y}solveBody(e,t){let i=this.params,{shape:r,pose:s,inertial:a,data:n}=this,l=i.dt*i.dt,c=r[e*4+2]/l,f=r[e*4+3]/l,u=c,d=0,h=c,m=0,p=0,b=f,y=c*(s[e*4]-a[e*4]),A=c*(s[e*4+1]-a[e*4+1]),w=f*(s[e*4+2]-a[e*4+2]),{eC:g,eMin:x,eMax:v,eJ:B,eG:E}=this,k=this.adjStart,z=this.adjList;for(let F=k[e+1]-1;F>=k[e];F--){let W=z[F],ee=this.evalConstraint(W,t,e),ne=W*T,ve=!i.vbd;for(let N=0;N<ee;N++){let Ee=n[ne+$+N],ze=ve&&Ee===1/0?n[ne+ae+N]:0,le=n[ne+be+N],Fe=g[N],Lt=le*Fe+ze,Me=Wi(Lt,x[N],v[N]),Ne=Math.abs(Me);i.stiffnessRescale&&Fe!==0&&(Lt<x[N]?le=Math.abs((x[N]-ze)/Fe):Lt>v[N]&&(le=Math.abs((v[N]-ze)/Fe)));let Ie=B[N*3],Ue=B[N*3+1],Ze=B[N*3+2];y+=Ie*Me,A+=Ue*Me,w+=Ze*Me,u+=Ie*Ie*le+E[N*3]*Ne,d+=Ue*Ie*le,h+=Ue*Ue*le+E[N*3+1]*Ne,m+=Ze*Ie*le,p+=Ze*Ue*le,b+=Ze*Ze*le+E[N*3+2]*Ne}}let M=u,P=d/u,_=m/u,S=h-P*P*M,C=(p-P*_*M)/S,R=b-(_*_*M+C*C*S),I=A-P*y,q=(w-_*y-C*I)/R,G=I/S-C*q,j=y/M-P*G-_*q;this.dxBuf[e*3]=j,this.dxBuf[e*3+1]=G,this.dxBuf[e*3+2]=q}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 s=t[r];s<t[r+1];s++)this.solveBody(i[s],e);for(let s=t[r];s<t[r+1];s++)this.applyDx(i[s])}}dual(e){let t=this.params,i=this.data,{eC:r,eMin:s,eMax:a}=this,n=this.jointCount+this.contactCount;for(let l=0;l<n;l++){let c=this.info[l*D];if(c===An)continue;let f=this.evalConstraint(l,e,-1),u=l*T;c===Fo&&(i[u+br]=Math.abs(i[u+ae+1])<a[1]&&Math.abs(i[u+ot+1])<.01?1:0);for(let d=0;d<f;d++){let h=i[u+$+d],m=!t.vbd&&h===1/0?i[u+ae+d]:0,p=Wi(i[u+be+d]*r[d]+m,s[d],a[d]);i[u+ae+d]=p,Math.abs(p)>=i[u+di+d]&&this.disable(l),!t.vbd&&p>s[d]&&p<a[d]&&(i[u+be+d]=Hi(i[u+be+d]+t.beta*Math.abs(r[d]),Hi(1e9,i[u+$+d])))}}}refreshStick(){let e=this.data;for(let t=0;t<this.contactCount;t++){let i=(this.jointCount+t)*T,r=Math.abs(e[i+ae])*e[i+ge];e[i+br]=Math.abs(e[i+ae+1])<r&&Math.abs(e[i+ot+1])<.01?1:0}}disable(e){let t=e*T;this.data.fill(0,t+be,t+be+3),this.data.fill(0,t+ae,t+ae+3),this.data.fill(0,t+$,t+$+3)}updateVelocities(){let{dt:e}=this.params,{pose:t,initial:i,velocity:r,prevVelocity:s}=this;for(let a=0;a<this.bodyCount;a++){let n=a*4;s[n]=r[n],s[n+1]=r[n+1],s[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,s]=qt(-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(s)<=this.shape[i*4+1]*.5)return{body:i,local:[r,s]}}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*D]!==Vt)continue;let r=i*T;this.jointC(this.info[i*D+1],this.info[i*D+2],r,this.pose,this.data,0,t);for(let s=0;s<2;s++)this.data[r+$+s]===1/0&&(e=Math.max(e,Math.abs(t[s])))}return e}isContactPairIgnored(e,t){return Do(this.noCollide,this.noCollideCount,Ji(e,t))}};function qt(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function Sn(o,e,t,i){let r=Math.cos(o[e*4+2]),s=Math.sin(o[e*4+2]);return[r*t-s*i+o[e*4],s*t+r*i+o[e*4+1]]}var gr=`
|
|
3515
|
-
${
|
|
3607
|
+
`),yi=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?Mu:Yr}),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:(Yn+4*this.setSlots)*4,usage:f}),this.events=e.createBuffer({label:"contact events",size:o*Br*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,(Yn+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*Br*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*Br*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 ht=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),Je=s=>s&&typeof s.x=="number"?[s.x,s.y,s.z,...typeof s.w=="number"?[s.w]:[]]:Array.from(s),Qr=new WeakMap,Cr=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s},Js=(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},$s=class s{static async create({device:e,...t}={}){if(t.scene&&Qr.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await Jr({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return await xr(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",bvh:f={},autoAttach:d=!0,maxContactPairs:p=8192,maxContactEvents:g=4096,sleeping:h=!1}={}){if(this.device=e,this.scene=t,this.autoAttach=d,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:f,solverMode:u},this.ref=new ni,this.ref.dt=Js(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[b,v]of Object.entries({maxContactPairs:p,maxContactEvents:g}))if(!Number.isInteger(v)||v<1)throw Error(`${b} must be a positive integer`);if(this.contactOptions={maxContactPairs:p,maxContactEvents:g},this.bodyReadback=new bi(e),this.sleepOptions=h,h!==!1&&h!==!0&&(typeof h!="object"||h===null))throw Error("sleeping must be a boolean or options object");this.commands=new Map,this.steps=0,this.errors=[],this.deviceLost=null;let m=new WeakRef(this);e.lost.then(b=>{let v=m.deref();!v||v.disposed||(v.deviceLost={reason:b.reason,message:b.message},v.errors.push(`WebGPU device lost: ${b.message||b.reason}`),v.dispose())}),this.setGravity(i),this.errorListener=b=>this.errors.push(b.error.message),e.addEventListener("uncapturederror",this.errorListener),t&&Qr.set(t,this)}setGravity(e){let t=Je(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 pi(this.device,this.ref,Ws(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 Xs(e,t,i,this)}addAggregates(e){return e.map(({mesh:t,type:i,options:r})=>this.addAggregate(t,i,r))}addConstraint(e,t,i={}){return new $r(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}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new Xr(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new yi(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===vi?"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(),Cr(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:Je(n.origin),direction:Je(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}={}){Cr(i),this.initialize(),this.flushCommands();let o=e.map(a=>{let l=Je(a.origin),c=Je(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&&Js(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=Je(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 v=Math.max(t,(this.velocityIndices?.length??0)*2,64);this.velocityIndices=new Uint32Array(v),this.velocityValues=new Float32Array(v*3)}let h=0;for(let[v,w]of this.commands)this.velocityIndices[h]=v.aggregate.gpuSlot??this.gpu.gpuIndex(v.aggregate.index),this.velocityValues.set(i===2?w.angular:w.velocity,h++*3);let m=i===2?"angularVelocityBatch":"velocityBatch",b=this[m]??=new Qt(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[m,b]of this.commands){let v=h++*8;this.motionData[v]=m.aggregate.gpuSlot??this.gpu.gpuIndex(m.aggregate.index),this.motionData[v+1]=b.mask,b.velocity&&this.motionFloats.set(b.velocity,v+2),b.angular&&this.motionFloats.set(b.angular,v+5)}this.motionBatch??=new gi(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,m]of this.commands){let b=u++*32;c[b]=h.aggregate.gpuSlot??this.gpu.gpuIndex(h.aggregate.index),c[b+1]=m.mask;for(let[v,w]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])m[v]&&l.set(m[v],b+w)}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;
|
|
3608
|
+
${kl}`,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),g=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(p,g),Math.ceil(p/g)),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&&Qr.get(this.scene)===this&&Qr.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}},Xs=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 $s?f:Qr.get(f??e.getScene()),!f)throw Error("Create an AVBD world for this scene before adding aggregates");if(f.assertAlive(),typeof t=="number"&&(t=[ht.SPHERE,ht.CAPSULE,ht.CYLINDER,ht.BOX,ht.CONVEX_HULL][t]),Js(i,"mass",!0),Js(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(ht).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=Cr(c),this.collidesWith=Cr(u),f.gpu&&f.syncEnabled&&l&&f.aggregates.filter(y=>y.sync&&!y.disposed).length>=f.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(![ht.BOX,ht.SPHERE,ht.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(y=>y<=0))throw Error("Bake negative or zero mesh scaling before creating an aggregate");let g=d.maximum.subtract(d.minimum).multiply(p),h=d.center.multiply(p),m=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=h,this.localRotation=m.constructor.Identity();let b;if(t===ht.BOX)b=new Ze(f.ref,Je(g),1,r,[0,0,0]);else if(t===ht.SPHERE){if(Math.max(g.x,g.y,g.z)-Math.min(g.x,g.y,g.z)>1e-4)throw Error("Sphere aggregate requires uniform dimensions");b=or(f.ref,g.x/2,1,r,[0,0,0])}else if(t===ht.CAPSULE){if(Math.abs(g.x-g.z)>1e-4)throw Error("Capsule aggregate requires equal X and Z diameters; bake an upright Y-axis capsule mesh");b=Ar(f.ref,g.x/2,g.y,1,r,[0,0,0])}else{let y=e.getVerticesData("position");if(!y)throw Error("Hull aggregate requires mesh positions");let A=Float32Array.from(y,(B,E)=>B*[p.x,p.y,p.z][E%3]),x=jr(A);if(!x)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...x.center),this.localRotation=m.constructor.FromArray(x.rotation),b=Bs(f.ref,x,1,r,[0,0,0])}let v=i/b.mass;b.mass=i,b.moment.forEach((y,A)=>b.moment[A]=y*v);let w=e.position.add(this.localCenter.applyRotationQuaternion(m));if(b.positionLin.set(Je(w)),b.positionAng.set(m.multiply(this.localRotation).asArray()),this.rigid=b,this.index=f.aggregates.length,this.body=new Ys(this),f.aggregates.push(this),e.rotationQuaternion=m,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(){if(this.disposed)return;let e=this.world;for(let t of[...e.constraints])(t.bodyA===this.body||t.bodyB===this.body)&&t.dispose();if(e.gpu){let t=this.body.gpuIndex;e.queue(this.body,16,"velocity",[0,0,0]),e.gpu.setFilters([t],[0],[0]),e.gpu.setRestitution(t,0)}this.rigid.mass=0,this.rigid.moment.fill(0),this.sync=!1,this.disposed=this.body.disposed=!0,e.sleepManager?.register(this.gpuSlot??e.gpu?.gpuIndex(this.index)??this.index,0),e.wakeAll?.()}},Ys=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?Je(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=Je(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=Je(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",Je(e).map((r,o)=>r+i[o])),this}applyForce(e,t){return this.applyImpulse(Je(e).map(i=>i*this.aggregate.world.ref.dt),t)}applyTorque(e){return this.applyAngularImpulse(Je(e).map(t=>t*this.aggregate.world.ref.dt))}setCollisionGroups(e,t=4294967295){this.assertAlive();let i=this.aggregate,r=Cr(e),o=Cr(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=this.aggregate.mesh.rotationQuaternion){let i=t.clone().normalize(),r=this.aggregate;return r.world.queue(this,8,"position",Je(e.add(r.localCenter.applyRotationQuaternion(i)))),r.world.queue(this,8,"rotation",i.multiply(r.localRotation).asArray()),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 Ll=s=>s<0?-1:s>0?1:0,Sr=(s,e)=>s<e?s:e,zu=(s,e)=>s>e?s:e,kr=(s,e,t)=>zu(e,Sr(t,s));function Kr(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function Ui(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 Kt(s,e,t,i,r){s[e*3]=t,s[e*3+1]=i,s[e*3+2]=r}var Qs=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?Ui(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=Ui(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=Kr(e.position[2],this.rA[0],this.rA[1]);Kt(t,0,1,0,-r[1]),Kt(t,1,0,1,r[0]),Kt(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=Kr(e.position[2],this.rB[0],this.rB[1]);Kt(t,0,-1,0,r[1]),Kt(t,1,0,-1,-r[0]),Kt(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},Ks=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=Ui(t.position,r[0],r[1]),c=Ui(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=Ui(this.bodyA.position,this.rA[0],this.rA[1]),t=Ui(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=Kr(e.position[2],-d[1],d[0]),g=Kr(e.position[2],d[0],d[1]),h=a*p[0]+l*p[1],m=c*p[0]+u*p[1],b=o*g[0]+n*g[1],v=o*p[0]+n*p[1];f?Kt(this.J,0,o,n,v):Kt(this.J,0,-o,-n,-v);let w=f?-b-b:b+b,y=this.H;y[0]=a,y[1]=l,y[2]=h,y[3]=c,y[4]=u,y[5]=m,y[6]=h,y[7]=m,y[8]=w}},Zs=class extends Ni{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},eo=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){Kt(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var to=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),io=()=>({...to(),postStabilize:!1,alpha:.95,matchNearest:!0});var Pr=(s,e)=>s>e?s*2097152+e:e*2097152+s;function Iu(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 Qn(s,e,t){let i=Iu(s,e,t);return i<e&&s[i]===t}var ro=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 g=(h,m)=>{if(!r[h]&&!r[m])return;let b=t[h*4]-t[m*4],v=t[h*4+1]-t[m*4+1],w=i[h*4+1]+i[m*4+1];if(b*b+v*v>w*w)return;let y=Pr(h,m);if(!Qn(o,n,y)){if(this.pairCount===this.pairs.length){let A=new Float64Array(this.pairs.length*2);A.set(this.pairs),this.pairs=A}this.pairs[this.pairCount++]=y}};for(let h=0;h<e;h++){if(c[h])continue;let m=a[h],b=l[h];for(let v=-1;v<=1;v++)for(let w=-1;w<=1;w++){let y=this.hash(m+w,b+v);for(let A=u[y];A<u[y+1];A++){let x=f[A];x>=h||a[x]!==m+w||l[x]!==b+v||g(h,x)}}}for(let h of this.large)for(let m=0;m<e;m++)m===h||c[m]&&m>h||g(h,m);this.pairs.subarray(0,this.pairCount).sort()}};var pt=new Float64Array(12),_l=new Float64Array(12),qi=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 El(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 so(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(pt,0,s,-e,0,0,3,4),qt(pt,1,s,e,0,0,4,1)):(qt(pt,0,-s,e,0,0,1,2),qt(pt,1,-s,-e,0,0,2,3)):c>0?(qt(pt,0,s,e,0,0,4,1),qt(pt,1,-s,e,0,0,1,2)):(qt(pt,0,-s,-e,0,0,2,3),qt(pt,1,s,-e,0,0,3,4));for(let u=0;u<2;u++){let f=u*6,d=pt[f],p=pt[f+1];pt[f]=t+r*d-o*p,pt[f+1]=i+o*d+r*p}}function oo(s,e,t,i,r,o,n,a,l,c,u){let f=Math.cos(t),d=Math.sin(t),p=Math.cos(a),g=Math.sin(a),h=o-s,m=n-e,b=f*h+d*m,v=-d*h+f*m,w=p*h+g*m,y=-g*h+p*m,A=Math.abs(f*p+d*g),x=Math.abs(f*-g+d*p),B=Math.abs(-d*p+f*g),E=Math.abs(-d*-g+f*p),L=Math.abs(b)-i-(A*l+x*c),M=Math.abs(v)-r-(B*l+E*c);if(L>0||M>0)return 0;let z=Math.abs(w)-(A*i+B*r)-l,_=Math.abs(y)-(x*i+E*r)-c;if(z>0||_>0)return 0;let k=0,S=L,P=b>0?f:-f,I=b>0?d:-d;M>.95*S+.01*r&&(k=1,S=M,P=v>0?-d:d,I=v>0?f:-f),z>.95*S+.01*l&&(k=2,S=z,P=w>0?p:-p,I=w>0?g:-g),_>.95*S+.01*c&&(k=3,S=_,P=y>0?-g:g,I=y>0?p:-p);let R,F,X,W,N,H,Z,he,we;if(k===0){R=P,F=I,X=s*R+e*F+i,W=-d,N=f;let J=s*W+e*N;H=-J+r,Z=J+r,he=3,we=1,so(l,c,o,n,p,g,R,F)}else if(k===1){R=P,F=I,X=s*R+e*F+r,W=f,N=d;let J=s*W+e*N;H=-J+i,Z=J+i,he=2,we=4,so(l,c,o,n,p,g,R,F)}else if(k===2){R=-P,F=-I,X=o*R+n*F+l,W=-g,N=p;let J=o*W+n*N;H=-J+c,Z=J+c,he=3,we=1,so(i,r,s,e,f,d,R,F)}else{R=-P,F=-I,X=o*R+n*F+c,W=p,N=g;let J=o*W+n*N;H=-J+l,Z=J+l,he=2,we=4,so(i,r,s,e,f,d,R,F)}if(El(_l,pt,-W,-N,H,he)<2||El(qi,_l,W,N,Z,we)<2)return 0;let Oe=0;for(let J=0;J<2;J++){let $e=J*6,Xe=qi[$e],Be=qi[$e+1],st=R*Xe+F*Be-X;if(st>0)continue;let ii=Xe-R*st,Ye=Be-F*st,ot=qi[$e+2],Qe=qi[$e+3],nt=qi[$e+4],At=qi[$e+5],Yi,Qi,Ki,Zi;k>=2?([ot,Qe,nt,At]=[nt,At,ot,Qe],Yi=Xe-s,Qi=Be-e,Ki=ii-o,Zi=Ye-n):(Yi=ii-s,Qi=Ye-e,Ki=Xe-o,Zi=Be-n);let Wt=Oe*7;u[Wt+0]=ot&255|(Qe&255)<<8|(nt&255)<<16|(At&255)<<24,u[Wt+1]=f*Yi+d*Qi,u[Wt+1+1]=-d*Yi+f*Qi,u[Wt+3]=p*Ki+g*Zi,u[Wt+3+1]=-g*Ki+p*Zi,u[Wt+5]=-P,u[Wt+5+1]=-I,Oe++}return Oe}var Ml=s=>31-Math.clz32(s&-s);function Ru(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 Kn=(s,e,t,i)=>s>t||s===t&&e>i,no=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)=>Ru(r))}run(e,t,i,r,o,n){this.resize(e);let{colors:a,next:l,pending:c,prio:u}=this,f=(y,A)=>{let x=o[y*4+1],B=x===A?o[y*4+2]:x;return B>=0&&t[B]?B:-1},d=(y,A)=>{let x=0,B=0;for(let E=i[y];E<i[y+1];E++){let L=f(r[E],y);if(L<0||A&&c[L])continue;let M=a[L];M<0||(M<32?x|=1<<M:B|=1<<M-32)}return~x!==0?Ml(~x):~B!==0?32+Ml(~B):63};for(let y=0;y<e;y++){if(l[y]=a[y],!t[y]||a[y]<0)continue;let A=!0;for(let x=i[y];x<i[y+1]&&A;x++){let B=f(r[x],y);B>=0&&!Kn(u[y],y,u[B],B)&&(A=!1)}A&&(l[y]=Math.min(a[y],d(y,!1)))}a.set(l);let p=()=>{let y=0;for(let A=0;A<e;A++)if(c[A]=0,!!t[A]){if(a[A]<0)c[A]=1;else for(let x=i[A];x<i[A+1];x++){let B=f(r[x],A);if(B>=0&&a[B]===a[A]&&Kn(u[B],B,u[A],A)){c[A]=1;break}}y+=c[A]}return y},g=p(),h=0;for(;g>0&&h<n;){h++;for(let y=0;y<e;y++){if(l[y]=-2,!c[y])continue;let A=!0;for(let x=i[y];x<i[y+1]&&A;x++){let B=f(r[x],y);B>=0&&c[B]&&!Kn(u[y],y,u[B],B)&&(A=!1)}A&&(l[y]=d(y,!0))}for(let y=0;y<e;y++)l[y]!==-2&&(a[y]=l[y],c[y]=0,g--)}let m=0;if(g>0){for(let y=0;y<e;y++)c[y]&&a[y]<0&&(a[y]=0);m=p()}let b=this.colorStart;b.fill(0);let v=0;for(let y=0;y<e;y++)t[y]&&(b[a[y]+1]++,v=Math.max(v,a[y]+1));for(let y=0;y<64;y++)b[y+1]+=b[y];let w=b.slice(0,64);for(let y=0;y<e;y++)t[y]&&(this.colorBodies[w[a[y]]++]=y);return{numColors:v,conflicts:m,rounds:h}}};var zl=0,xi=1,Il=2,uo=3,Zr=4,Rl=[0,3,1,1,2],ea=.05,j=4,O=32,se=0,te=3,Q=6,Le=9,xe=12,Pt=15,fe=18,U=21,Y=23,re=25,ge=26,Fe=27,Vi=28,Lr=class{params=to();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 ro;coloring=new no;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+Le+i]=-1/0,this.data[e*O+xe+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,xi,e,t);let l=this.data,c=a*O;l.set(o,c+Q),l[c+xe+2]=n,l[c+Le+2]=-n,l[c+Pt+2]=n,l.set(i,c+U),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+ge]=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+U]=t,this.data[e.slot*O+U+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,Il,e,t);let l=a*O;this.data[l+Q]=o,this.data.set(i,l+U),this.data.set(r,l+Y),this.data[l+re]=n}addMotor(e,t,i,r){let o=this.allocConstraint();this.setInfo(o,uo,e,t);let n=o*O;this.data[n+xe]=r,this.data[n+Le]=-r,this.data[n+re]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(Pr(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(to());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 Qs){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+ge]=r.torqueArm}else r instanceof Ks?this.addSpring(o,n,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof eo?this.addMotor(o,n,r.speed,r.fmax[0]):r instanceof Zs&&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]=Pr(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]!==zl&&i>=0&&e.push(Pr(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]===xi){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]),m=Math.sin(r[e*4+2]),b=o[i+U],v=o[i+U+1];l=h*b-m*v+r[e*4],c=m*b+h*v+r[e*4+1],u=r[e*4+2]}else l=o[i+U],c=o[i+U+1],u=0;let f=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),p=o[i+Y],g=o[i+Y+1];a[n]=l-(f*p-d*g+r[t*4]),a[n+1]=c-(d*p+f*g+r[t*4+1]),a[n+2]=(u-r[t*4+2]-o[i+re])*o[i+ge]}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=oo(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,g=Math.sqrt(i[l*4]*i[c*4]);for(let h=0;h<u;h++){let m=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(m+1)*j),this.data=this.grow(this.data,(m+1)*O);let b=this.data,v=m*O,w=h*7,y=o[w+0];this.info.set([Zr,l,c,y],m*j),b.fill(0,v,v+O),b[v+Q]=b[v+Q+1]=1/0,b[v+Le]=-1/0,b[v+xe]=0,b[v+Pt]=b[v+Pt+1]=1/0,b[v+U]=o[w+1],b[v+U+1]=o[w+1+1],b[v+Y]=o[w+3],b[v+Y+1]=o[w+3+1],b[v+re]=g,b[v+ge]=o[w+5],b[v+Fe]=o[w+5+1];let A=-1;for(let S=p;S<this.prevCount&&this.prevKeys[S]===a;S++)this.prevInfo[S*j+3]===y&&(A=S);if(A<0&&this.params.matchNearest){let S=ea*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let P=p;P<this.prevCount&&this.prevKeys[P]===a;P++){let I=Math.hypot(this.prevData[P*O+U]-b[v+U],this.prevData[P*O+U+1]-b[v+U+1]);I<=S&&(S=I,A=P)}}if(A>=0){let S=A*O;b[v+se]=this.prevData[S+se],b[v+se+1]=this.prevData[S+se+1],b[v+te]=this.prevData[S+te],b[v+te+1]=this.prevData[S+te+1],this.prevData[S+Vi]&&(b[v+U]=this.prevData[S+U],b[v+U+1]=this.prevData[S+U+1],b[v+Y]=this.prevData[S+Y],b[v+Y+1]=this.prevData[S+Y+1])}let[x,B]=Ai(e[l*4+2],b[v+U],b[v+U+1]),[E,L]=Ai(e[c*4+2],b[v+Y],b[v+Y+1]),M=b[v+ge],z=b[v+Fe],_=e[l*4]+x-e[c*4]-E,k=e[l*4+1]+B-e[c*4+1]-L;b[v+fe]=M*_+z*k+5e-4,b[v+fe+1]=z*_-M*k}}}warmStartConstraints(e,t){let i=this.params,r=this.data,o=this.jointCount+this.contactCount;for(let n=0;n<o;n++){let a=Rl[this.info[n*j]],l=n*O;for(let c=0;c<a;c++){if(i.vbd){r[l+se+c]=Sr(r[l+Q+c],i.vbdStiffness);continue}e||(r[l+te+c]=r[l+te+c]*t*i.gamma),r[l+se+c]=Sr(kr(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]=kr(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*Ll(t),f=kr(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:g,eJ:h,eG:m}=this,b=Rl[l];for(let w=0;w<b;w++)p[w]=o[f+Le+w],g[w]=o[f+xe+w];let v=i===c;if(m.fill(0),l===xi){this.jointC(c,u,f,n,o,0,d);for(let w=0;w<3;w++)o[f+Q+w]===1/0&&(d[w]-=o[f+fe+w]*t);if(i>=0){let w=n[i*4+2],[y,A]=Ai(w,o[f+(v?U:Y)],o[f+(v?U:Y)+1]),x=v?1:-1;h[0]=x,h[1]=0,h[2]=-x*A,h[3]=0,h[4]=x,h[5]=x*y,h[6]=0,h[7]=0,h[8]=x*o[f+ge],m[2]=Math.abs(y),m[5]=Math.abs(A)}}else if(l===Il){let[w,y]=Ol(n,c,o[f+U],o[f+U+1]),[A,x]=Ol(n,u,o[f+Y],o[f+Y+1]),B=w-A,E=y-x,L=B*B+E*E,M=Math.sqrt(L);if(d[0]=M-o[f+re],i>=0&&L!==0){let z=B/M,_=E/M,k=(1-z*z)/M,S=-z*_/M,P=(1-_*_)/M,I=n[i*4+2],R=v?U:Y,[F,X]=Ai(I,-o[f+R+1],o[f+R]),[W,N]=Ai(I,o[f+R],o[f+R+1]),H=k*F+S*X,Z=S*F+P*X,he=z*W+_*N,we=z*F+_*X,Oe=v?1:-1;h[0]=Oe*z,h[1]=Oe*_,h[2]=Oe*we;let J=v?-he-he:he+he;m[0]=Math.hypot(k,S,H),m[1]=Math.hypot(S,P,Z),m[2]=Math.hypot(H,Z,J)}else i>=0&&h.fill(0,0,3)}else if(l===uo){let w=c>=0?n[c*4+2]-a[c*4+2]:0,y=n[u*4+2]-a[u*4+2];d[0]=w-y-o[f+re]*this.params.dt,i>=0&&(h[0]=0,h[1]=0,h[2]=v?1:-1)}else if(l===Zr){let w=o[f+ge],y=o[f+Fe],A=y,x=-w,[B,E]=Ai(a[c*4+2],o[f+U],o[f+U+1]),[L,M]=Ai(a[u*4+2],o[f+Y],o[f+Y+1]),z=B*y-E*w,_=-(L*y-M*w),k=B*x-E*A,S=-(L*x-M*A),P=n[c*4]-a[c*4],I=n[c*4+1]-a[c*4+1],R=n[c*4+2]-a[c*4+2],F=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)+w*P+y*I+z*R+-w*F+-y*X+_*W,d[1]=o[f+fe+1]*(1-t)+A*P+x*I+k*R+-A*F+-x*X+S*W;let N=Math.abs(o[f+te])*o[f+re];g[1]=N,p[1]=-N,i>=0&&(v?(h[0]=w,h[1]=y,h[2]=z,h[3]=A,h[4]=x,h[5]=k):(h[0]=-w,h[1]=-y,h[2]=_,h[3]=-A,h[4]=-x,h[5]=S))}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,g=0,h=0,m=u,b=c*(o[e*4]-n[e*4]),v=c*(o[e*4+1]-n[e*4+1]),w=u*(o[e*4+2]-n[e*4+2]),{eC:y,eMin:A,eMax:x,eJ:B,eG:E}=this,L=this.adjStart,M=this.adjList;for(let H=L[e+1]-1;H>=L[e];H--){let Z=M[H],he=this.evalConstraint(Z,t,e),we=Z*O,Oe=!i.vbd;for(let J=0;J<he;J++){let $e=a[we+Q+J],Xe=Oe&&$e===1/0?a[we+te+J]:0,Be=a[we+se+J],st=y[J],ii=Be*st+Xe,Ye=kr(ii,A[J],x[J]),ot=Math.abs(Ye);i.stiffnessRescale&&st!==0&&(ii<A[J]?Be=Math.abs((A[J]-Xe)/st):ii>x[J]&&(Be=Math.abs((x[J]-Xe)/st)));let Qe=B[J*3],nt=B[J*3+1],At=B[J*3+2];b+=Qe*Ye,v+=nt*Ye,w+=At*Ye,f+=Qe*Qe*Be+E[J*3]*ot,d+=nt*Qe*Be,p+=nt*nt*Be+E[J*3+1]*ot,g+=At*Qe*Be,h+=At*nt*Be,m+=At*At*Be+E[J*3+2]*ot}}let z=f,_=d/f,k=g/f,S=p-_*_*z,P=(h-_*k*z)/S,I=m-(k*k*z+P*P*S),R=v-_*b,X=(w-k*b-P*R)/I,W=R/S-P*X,N=b/z-_*W-k*X;this.dxBuf[e*3]=N,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===zl)continue;let u=this.evalConstraint(l,e,-1),f=l*O;c===Zr&&(i[f+Vi]=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],g=!t.vbd&&p===1/0?i[f+te+d]:0,h=kr(i[f+se+d]*r[d]+g,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]=Sr(i[f+se+d]+t.beta*Math.abs(r[d]),Sr(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+Vi]=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]=Ai(-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]!==xi)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 Qn(this.noCollide,this.noCollideCount,Pr(e,t))}};function Ai(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function Ol(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 es=`
|
|
3609
|
+
${Nt}
|
|
3516
3610
|
|
|
3517
3611
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
3518
3612
|
@group(0) @binding(1) var<storage, read> bodies: array<Body>;
|
|
@@ -3651,8 +3745,8 @@ fn findPairs(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3651
3745
|
testPair(l, i);
|
|
3652
3746
|
}
|
|
3653
3747
|
}
|
|
3654
|
-
`,
|
|
3655
|
-
${
|
|
3748
|
+
`,ts=`
|
|
3749
|
+
${Nt}
|
|
3656
3750
|
|
|
3657
3751
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
3658
3752
|
@group(0) @binding(1) var<storage, read> bodies: array<Body>;
|
|
@@ -3978,8 +4072,8 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3978
4072
|
contacts[k] = rec;
|
|
3979
4073
|
}
|
|
3980
4074
|
}
|
|
3981
|
-
`;var
|
|
3982
|
-
${
|
|
4075
|
+
`;var is=`
|
|
4076
|
+
${Nt}
|
|
3983
4077
|
|
|
3984
4078
|
// Per-dispatch constants, selected with a dynamic uniform offset
|
|
3985
4079
|
struct PassConstants {
|
|
@@ -4351,7 +4445,7 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
4351
4445
|
if (b.shape.z > 0.0) { b.vel = vec4f((b.pose.xyz - b.initial.xyz) / params.dt, 0.0); }
|
|
4352
4446
|
bodies[i] = b;
|
|
4353
4447
|
}
|
|
4354
|
-
`;var xs=o=>o===1/0?3e38:o===-1/0?-3e38:o,Ye=o=>Math.ceil(o/64),Pn=o=>2**Math.ceil(Math.log2(Math.max(o,2))),lc=o=>Math.max(o,1)*24*4,cc=4,Qe=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,As=class{device;params=ps();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:lc(this.bodyCapacity),usage:Qe()});let r=this.bodyCapacity;this.tableSize=Pn(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:Qe()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:Qe()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:Qe()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:Qe()}),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 s=2*ue.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:s}),resolve:e.createBuffer({size:s*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:s*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(),this.gridScan=new De(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new De(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",s="uniform";return{broad:t("broadphase",[s,i,r,r,r,i,i]),contacts:t("contacts",[s,i,i,r,i,r,r]),topo:t("topology",[s,i,i,i,i,r,r,r]),solve:t("solve",[s,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",[s,i,i,r])}}createPipelines(){let e=this.device,t=(r,s,a)=>{let n=e.createShaderModule({code:r}),l=e.createPipelineLayout({bindGroupLayouts:s});for(let c of a)this.pipes[c]=e.createComputePipeline({label:c,layout:l,compute:{module:n,entryPoint:c}})},i=this.layouts;t(gr,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(yr,[i.contacts],["hashInsert","narrowphase"]),t(ir,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(vr,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(jr,[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:Qe()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:Qe()});if(this.jointBuffer){let s=t.createCommandEncoder();s.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*36*4),s.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([s.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,s=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=s,this.pairCapacity=a,this.hashSize=Pn(4*s),this.pairBuffer=i.createBuffer({label:"pairs",size:a*8,usage:Qe()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:Qe()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:s*20*4,usage:Qe()})),n){let c=i.createCommandEncoder(),f=Math.min(l,s)*20*4;n.forEach((u,d)=>c.copyBufferToBuffer(u,0,this.contactBuffers[d],0,f)),i.queue.submit([c.finish()]),n.forEach(u=>u.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:Qe()}),this.adjScan?.destroy(),this.adjScan=new De(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:Qe()});let i=(l,c)=>e.createBindGroup({layout:l,entries:c.map((f,u)=>({binding:u,resource:{buffer:f}}))}),r=this.paramsBuffer,[s,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,s,a,this.tableBuffer,this.counterBuffer]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,s,this.tableBuffer,this.counterBuffer])],topo:[s,a].map(l=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,this.counterBuffer,n,this.colorBuffer])),solve:[s,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 s=0;s<t;s++){let a=e+s,n=s*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 s=0;s<t;s++){let a=(e+s)*T,n=s*36;for(let l=0;l<3;l++)r[n+0+l]=i.data[a+be+l],r[n+4+l]=i.data[a+ae+l],r[n+8+l]=xs(i.data[a+$+l]),r[n+12+l]=xs(i.data[a+ft+l]),r[n+16+l]=xs(i.data[a+dt+l]),r[n+20+l]=xs(i.data[a+di+l]),r[n+24+l]=i.data[a+ot+l];r[n+28]=i.data[a+H],r[n+28+1]=i.data[a+H+1],r[n+28+2]=i.data[a+K],r[n+28+3]=i.data[a+K+1],r[n+32]=i.data[a+ge],r[n+32+1]=i.data[a+_e]}this.device.queue.writeBuffer(this.jointBuffer,e*36*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,i.info,e*D,t*D)}uploadStatics(){let e=this.topology,t=this.bodyCount,i=Float64Array.from({length:t},(u,d)=>e.props[d*4+1]).sort(),r=t>0?i[t>>1]:1,s=0;for(let u=0;u<t;u++)i[u]<=cc*r&&(s=Math.max(s,i[u]));let a=1/0;for(let u=0;u<t;u++)i[u]>s&&(a=Math.min(a,i[u]));let n=a===1/0?s*1.5:(s+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*s*(1+1e-4),.001);let l=[];for(let u=0;u<t;u++)e.props[u*4+1]>n&&l.push(u);let c=[];for(let u=0;u<e.jointCount;u++){let d=e.info[u*D+1],h=e.info[u*D+2];d>=0&&c.push([Math.max(d,h),Math.min(d,h),u])}for(let u of e.ignoredPairs()){let d=Math.floor(u/2097152);c.push([d,u-d*2097152,4294967295])}c.sort((u,d)=>u[0]-d[0]||u[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let f=new Uint32Array(Math.max(l.length+3*c.length,4));f.set(l,0),c.forEach((u,d)=>f.set(u,l.length+3*d)),this.staticBuffer.size<f.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:f.byteLength,usage:Qe()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,f),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),s=new Float32Array(i);for(let l=0;l<t;l++){let c=e.jointCount+l,f=c*T,u=l*20;r[u]=e.info[c*D+1],r[u+1]=e.info[c*D+2],r[u+2]=e.info[c*D+3],r[u+3]=e.data[f+br]?1:0,s.set([e.data[f+be],e.data[f+be+1],e.data[f+ae],e.data[f+ae+1]],u+4),s.set([e.data[f+H],e.data[f+H+1],e.data[f+K],e.data[f+K+1]],u+8),s.set([e.data[f+ot],e.data[f+ot+1],e.data[f+_e],e.data[f+vt]],u+12),s[u+16]=e.data[f+ge]}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,s){if(this.bodyCount>=this.bodyCapacity)return-1;let a=this.topology.addBody(e,t,i,r,s);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 De(this.device,this.adjBuffer,0,this.bodyCount+1),a}appendJoint(e,t,i,r,s,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,s,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*T+$,e*T+$+3)}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(e,t){let i=this.params,r=this.bodyCapacity,s=new ArrayBuffer(128),a=new Float32Array(s),n=new Uint32Array(s);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,s)}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 s=new ArrayBuffer(r*256),a=new Uint32Array(s),n=new Float32Array(s);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let f=(l*i+c)*256/4;a[f]=c,n[f+1]=t(l)}this.device.queue.writeBuffer(this.passBuffer,0,s)}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 s=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 f=this.timingCallback!==null&&this.timing!==null,u,d=0,h=f||this.splitPasses,m=()=>{u&&!h||(u?.end(),u=c.beginComputePass({label:h?ue[d]:"avbd2d step",timestampWrites:f?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),d++)},p=this.groups,b=(x,v,B)=>{B<=0||(u.setPipeline(this.pipes[x]),u.setBindGroup(0,v),u.dispatchWorkgroups(B))},y=(x,v,B)=>{u.setPipeline(this.pipes[x]),u.setBindGroup(0,v),u.dispatchWorkgroupsIndirect(this.argsBuffer,B*4)};m(),b("beginFrame",p.broad,1),b("argsPrev",p.args,1),y("hashInsert",p.contacts[s],6),b("gridCount",p.broad,Ye(n)),this.gridScan.encode(u),b("gridScatter",p.broad,Ye(n)),b("findPairs",p.broad,Ye(n)),b("argsPairs",p.args,1),y("narrowphase",p.contacts[s],0),b("argsContacts",p.args,1),m(),b("degreeJoints",p.topo[s],Ye(l)),y("degreeContacts",p.topo[s],3),this.adjScan.encode(u),b("fillJoints",p.topo[s],Ye(l)),y("fillContacts",p.topo[s],3),b("sortAdjacency",p.topo[s],Ye(n)),m(),b("colorCompact",p.topo[s],Ye(n)),b("colorMark",p.topo[s],Ye(n));for(let x=0;x<this.colorRounds;x++)b(x%2===0?"colorRoundAB":"colorRoundBA",p.topo[s],Ye(n));b("colorCount",p.topo[s],this.colorGroups),this.colorHistScan.encode(u),b("colorStarts",p.topo[s],1),b("argsColors",p.args,1),b("colorScatter",p.topo[s],this.colorGroups),m();let A=this.colorCap+1,w=x=>u.setBindGroup(1,this.passGroup,[x*256]);u.setBindGroup(0,p.solve[s]),w(0),l>0&&(u.setPipeline(this.pipes.warmStartJoints),u.dispatchWorkgroups(Ye(l))),u.setPipeline(this.pipes.warmStartBodies),u.dispatchWorkgroups(Ye(n));for(let x=0;x<r;x++){let v=this.primalMode==="scan";u.setPipeline(v?this.pipes.primalScan:this.pipes.primal);for(let B=0;B<this.colorCap;B++)w(x*A+B),v?u.dispatchWorkgroups(Ye(n)):u.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);x<e.iterations&&(w(x*A+this.colorCap),u.setPipeline(this.pipes.dual),u.dispatchWorkgroupsIndirect(this.argsBuffer,36)),x===e.iterations-1&&(u.setPipeline(this.pipes.updateVelocities),u.dispatchWorkgroups(Ye(n)))}t&&(u.setPipeline(this.pipes.refreshStick),u.dispatchWorkgroupsIndirect(this.argsBuffer,12)),u.end();let g=2*ue.length;if(f){let{querySet:x,resolve:v,read:B}=this.timing;c.resolveQuerySet(x,0,g,v,0),c.copyBufferToBuffer(v,0,B,0,g*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-s,f){let x=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let v=this.timing.read;v.mapAsync(GPUMapMode.READ).then(()=>{let B=new BigUint64Array(v.getMappedRange()).slice();if(v.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let E=(z,M)=>Number(B[M]-B[z])/1e6,k={total:E(0,g-1)};ue.forEach((z,M)=>k[z]=E(2*M,2*M+1)),x(k)},()=>{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,s=this.contactCapacity*(i?2:1),a=this.pairCapacity*(r?2:1);(i||r)&&this.capacityCanGrow(s,a)&&this.allocateContacts(s,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 f=c*24,u=t[f+22],d=t[f+12],h=t[f+13],m=t[f+14];u>0&&(r+=.5*u*(d*d+h*h)+.5*t[f+23]*m*m)}let s=0,a=0,n=this.topology.info,l=(c,f,u)=>{let d=c*24,h=Math.cos(t[d+2]),m=Math.sin(t[d+2]);return[h*f-m*u+t[d],m*f+h*u+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(n[c*D]!==Vt)continue;let f=c*36;if(i[f+8]===0&&i[f+8+1]===0&&i[f+8+2]===0)continue;a++;let u=n[c*D+1],d=n[c*D+2],h=u>=0?l(u,i[f+28],i[f+28+1]):[i[f+28],i[f+28+1]],m=l(d,i[f+28+2],i[f+28+3]);for(let p=0;p<2;p++)i[f+8+p]>=1e30&&(s=Math.max(s,Math.abs(h[p]-m[p])))}return{kineticEnergy:r,maxJointError:s,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 s=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),s}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 uc=`${mt}
|
|
4448
|
+
`;var fo=s=>s===1/0?3e38:s===-1/0?-3e38:s,mt=s=>Math.ceil(s/64),Fl=s=>2**Math.ceil(Math.log2(Math.max(s,2))),Wu=s=>Math.max(s,1)*24*4,Hu=4,gt=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,ho=class{device;params=io();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:Wu(this.bodyCapacity),usage:gt()});let r=this.bodyCapacity;this.tableSize=Fl(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:gt()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:gt()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:gt()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:gt()}),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(es,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(ts,[i.contacts],["hashInsert","narrowphase"]),t(Dr,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(is,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(Cs,[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:gt()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:gt()});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=Fl(4*o),this.pairBuffer=i.createBuffer({label:"pairs",size:n*8,usage:gt()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:gt()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:o*20*4,usage:gt()})),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:gt()}),this.adjScan?.destroy(),this.adjScan=new tt(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:gt()});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]=fo(i.data[n+Q+l]),r[a+12+l]=fo(i.data[n+Le+l]),r[a+16+l]=fo(i.data[n+xe+l]),r[a+20+l]=fo(i.data[n+Pt+l]),r[a+24+l]=i.data[n+fe+l];r[a+28]=i.data[n+U],r[a+28+1]=i.data[n+U+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+ge]}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]<=Hu*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:gt()}),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+Vi]?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+U],e.data[u+U+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+ge],e.data[u+Fe]],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,A=>t?A<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,g=()=>{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,m=(A,x,B)=>{B<=0||(f.setPipeline(this.pipes[A]),f.setBindGroup(0,x),f.dispatchWorkgroups(B))},b=(A,x,B)=>{f.setPipeline(this.pipes[A]),f.setBindGroup(0,x),f.dispatchWorkgroupsIndirect(this.argsBuffer,B*4)};g(),m("beginFrame",h.broad,1),m("argsPrev",h.args,1),b("hashInsert",h.contacts[o],6),m("gridCount",h.broad,mt(a)),this.gridScan.encode(f),m("gridScatter",h.broad,mt(a)),m("findPairs",h.broad,mt(a)),m("argsPairs",h.args,1),b("narrowphase",h.contacts[o],0),m("argsContacts",h.args,1),g(),m("degreeJoints",h.topo[o],mt(l)),b("degreeContacts",h.topo[o],3),this.adjScan.encode(f),m("fillJoints",h.topo[o],mt(l)),b("fillContacts",h.topo[o],3),m("sortAdjacency",h.topo[o],mt(a)),g(),m("colorCompact",h.topo[o],mt(a)),m("colorMark",h.topo[o],mt(a));for(let A=0;A<this.colorRounds;A++)m(A%2===0?"colorRoundAB":"colorRoundBA",h.topo[o],mt(a));m("colorCount",h.topo[o],this.colorGroups),this.colorHistScan.encode(f),m("colorStarts",h.topo[o],1),m("argsColors",h.args,1),m("colorScatter",h.topo[o],this.colorGroups),g();let v=this.colorCap+1,w=A=>f.setBindGroup(1,this.passGroup,[A*256]);f.setBindGroup(0,h.solve[o]),w(0),l>0&&(f.setPipeline(this.pipes.warmStartJoints),f.dispatchWorkgroups(mt(l))),f.setPipeline(this.pipes.warmStartBodies),f.dispatchWorkgroups(mt(a));for(let A=0;A<r;A++){let x=this.primalMode==="scan";f.setPipeline(x?this.pipes.primalScan:this.pipes.primal);for(let B=0;B<this.colorCap;B++)w(A*v+B),x?f.dispatchWorkgroups(mt(a)):f.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);A<e.iterations&&(w(A*v+this.colorCap),f.setPipeline(this.pipes.dual),f.dispatchWorkgroupsIndirect(this.argsBuffer,36)),A===e.iterations-1&&(f.setPipeline(this.pipes.updateVelocities),f.dispatchWorkgroups(mt(a)))}t&&(f.setPipeline(this.pipes.refreshStick),f.dispatchWorkgroupsIndirect(this.argsBuffer,12)),f.end();let y=2*Se.length;if(u){let{querySet:A,resolve:x,read:B}=this.timing;c.resolveQuerySet(A,0,y,x,0),c.copyBufferToBuffer(x,0,B,0,y*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-o,u){let A=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let x=this.timing.read;x.mapAsync(GPUMapMode.READ).then(()=>{let B=new BigUint64Array(x.getMappedRange()).slice();if(x.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let E=(M,z)=>Number(B[z]-B[M])/1e6,L={total:E(0,y-1)};Se.forEach((M,z)=>L[M]=E(2*z,2*z+1)),A(L)},()=>{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],g=t[u+14];f>0&&(r+=.5*f*(d*d+p*p)+.5*t[u+23]*g*g)}let o=0,n=0,a=this.topology.info,l=(c,u,f)=>{let d=c*24,p=Math.cos(t[d+2]),g=Math.sin(t[d+2]);return[p*u-g*f+t[d],g*u+p*f+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(a[c*j]!==xi)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]],g=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]-g[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 Ju=`${Nt}
|
|
4355
4449
|
struct Config { count:u32,joints:u32,capacity:u32,list:u32,threshold:f32,dt:f32,pad0:u32,pad1:u32 }
|
|
4356
4450
|
struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
|
|
4357
4451
|
struct Bounce { impulse:f32,delta:f32,reboundSpeed:f32,inverseMass:f32,rA:vec4f,rB:vec4f }
|
|
@@ -4397,9 +4491,9 @@ fn pointVelocity(i:u32,r:vec2f,incoming:bool)->vec2f{let v=select(velocities[i].
|
|
|
4397
4491
|
velocities[i].linear+=vec4f(dv,0.);
|
|
4398
4492
|
}
|
|
4399
4493
|
@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;}
|
|
4400
|
-
|
|
4401
|
-
if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return
|
|
4402
|
-
if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var
|
|
4494
|
+
`;var po=class extends yr{constructor(e){super(e,{code:Ju,dimension:2})}};function Gl(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}
|
|
4495
|
+
if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return s}function Nl(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}
|
|
4496
|
+
if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var Ul=`// Sleeping is scheduling metadata in velocity.w; physical mass and inertia stay intact.
|
|
4403
4497
|
struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,vel:vec4f,prev:vec4f,shape:vec4f}
|
|
4404
4498
|
struct Joint {pen:vec4f,lam:vec4f,stiff:vec4f,fmin:vec4f,fmax:vec4f,frac:vec4f,c0:vec4f,anchors:vec4f,param:vec4f}
|
|
4405
4499
|
struct Contact {ids:vec4u,pl:vec4f,anchors:vec4f,geo:vec4f,misc:vec4f}
|
|
@@ -4470,10 +4564,10 @@ fn rotate(angle:f32,p:vec2f)->vec2f{let c=cos(angle);let s=sin(angle);return vec
|
|
|
4470
4564
|
}
|
|
4471
4565
|
if(asleep(i)){atomicAdd(&globals[1],1u);}
|
|
4472
4566
|
}
|
|
4473
|
-
`;function
|
|
4474
|
-
if(gid.x<params.bodyCount && bodies[gid.x].vel.w!=0.){return;}`);return
|
|
4475
|
-
if(bodies[i].vel.w!=0.){return;}`)}function
|
|
4476
|
-
case 5: { return 2u; }`),
|
|
4567
|
+
`;function go(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 ql(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) {
|
|
4568
|
+
if(gid.x<params.bodyCount && bodies[gid.x].vel.w!=0.){return;}`);return s.replace("fn solveBody(i: u32) {",`fn solveBody(i: u32) {
|
|
4569
|
+
if(bodies[i].vel.w!=0.){return;}`)}function Vl(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 mo=class{constructor(e,t){this.gpu=e,this.device=e.device,this.options=go(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:Ul});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 n=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["clear","before","contactsWake","jointsWake","decide","applyWake","rest"].map(l=>[l,i.createComputePipeline({layout:n,compute:{module:o,entryPoint:l}})]));let a=new Uint32Array(e.bodyCapacity*4);for(let l=0;l<e.bodyCount;l++)a[l*4+1]=e.sleepEligible?.get(l)===!1?0:1;i.queue.writeBuffer(this.state,0,a),this.wakeRequested=!1}register(e,t=!0){let i=le(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((n,a)=>n===i[a]));return r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((o,n)=>({binding:n,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=new ArrayBuffer(32),r=new Uint32Array(i),o=new Float32Array(i);r.set([t.bodyCount,t.jointCount,t.contactCapacity,Number(this.wakeRequested)]),o.set([this.options.speedThreshold,this.options.timeThreshold,t.params.dt,t.params.gravity],4),this.device.queue.writeBuffer(this.params,0,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 bt=s=>Math.ceil(s/64);function Wl(){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,y=>e?y<s.iterations?1:0:t);let r=this.parity,o=this.bodyCapacity,n=this.bodyCount,a=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=0,d=c||this.splitPasses,p=()=>{u&&!d||(u?.end(),u=l.beginComputePass({label:d?Se[f]:"avbd2d step",timestampWrites:c?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*f,endOfPassWriteIndex:2*f+1}:void 0}),f++)},g=this.groups,h=(y,A,x)=>{x<=0||(u.setPipeline(this.pipes[y]),u.setBindGroup(0,A),u.dispatchWorkgroups(x))},m=(y,A,x)=>{u.setPipeline(this.pipes[y]),u.setBindGroup(0,A),u.dispatchWorkgroupsIndirect(this.argsBuffer,x*4)};p(),h("beginFrame",g.broad,1),h("argsPrev",g.args,1),m("hashInsert",g.contacts[r],6),h("gridCount",g.broad,bt(n)),this.gridScan.encode(u),h("gridScatter",g.broad,bt(n)),h("findPairs",g.broad,bt(n)),h("argsPairs",g.args,1),m("narrowphase",g.contacts[r],0),h("argsContacts",g.args,1),this.sleeping?.encodeWake(u,r),p(),h("degreeJoints",g.topo[r],bt(a)),m("degreeContacts",g.topo[r],3),this.adjScan.encode(u),h("fillJoints",g.topo[r],bt(a)),m("fillContacts",g.topo[r],3),h("sortAdjacency",g.topo[r],bt(n)),p(),h("colorCompact",g.topo[r],bt(n)),h("colorMark",g.topo[r],bt(n));for(let y=0;y<this.colorRounds;y++)h(y%2===0?"colorRoundAB":"colorRoundBA",g.topo[r],bt(n));h("colorCount",g.topo[r],this.colorGroups),this.colorHistScan.encode(u),h("colorStarts",g.topo[r],1),h("argsColors",g.args,1),h("colorScatter",g.topo[r],this.colorGroups),p();let b=this.colorCap+1,v=y=>u.setBindGroup(1,this.passGroup,[y*256]);u.setBindGroup(0,g.solve[r]),v(0),a>0&&(u.setPipeline(this.pipes.warmStartJoints),u.dispatchWorkgroups(bt(a))),u.setPipeline(this.pipes.warmStartBodies),u.dispatchWorkgroups(bt(n));for(let y=0;y<i;y++){let A=this.primalMode==="scan";u.setPipeline(A?this.pipes.primalScan:this.pipes.primal);for(let x=0;x<this.colorCap;x++)v(y*b+x),A?u.dispatchWorkgroups(bt(n)):u.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*x)*4);y<s.iterations&&(v(y*b+this.colorCap),u.setPipeline(this.pipes.dual),u.dispatchWorkgroupsIndirect(this.argsBuffer,36)),y===s.iterations-1&&(u.setPipeline(this.pipes.updateVelocities),u.dispatchWorkgroups(bt(n)))}e&&(u.setPipeline(this.pipes.refreshStick),u.dispatchWorkgroupsIndirect(this.argsBuffer,12)),u.end();let w=2*Se.length;if(c){let{querySet:y,resolve:A,read:x}=this.timing;l.resolveQuerySet(y,0,w,A,0),l.copyBufferToBuffer(A,0,x,0,w*8)}if(this.parity=1-r,this.encodeStepPostlude?.(l),this.device.queue.submit([l.finish()]),c){let y=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let A=this.timing.read;A.mapAsync(GPUMapMode.READ).then(()=>{let x=new BigUint64Array(A.getMappedRange()).slice();if(A.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let B=(L,M)=>Number(x[M]-x[L])/1e6,E={total:B(0,w-1)};Se.forEach((L,M)=>E[L]=B(2*M,2*M+1)),y(E)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}var bo=5;function Ci({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 os(s,e,t){let i=e*O;s.info[e*j]=bo,s.data.set([1/0,1/0,0],i+Q),s.data.set([-1/0,0,0],i+Le),s.data.set([0,1/0,0],i+xe),s.data[i+ge]=t.minAngle,s.data[i+Fe]=t.maxAngle}function Hl(s){let e=s.topology;for(let t=0;t<e.jointCount;t++)e.info[t*j]===bo&&s.device.queue.writeBuffer(s.jointBuffer,(t*36+32)*4,new Float32Array([e.data[t*O+re],e.data[t*O+ge],e.data[t*O+Fe],0]))}var ss=(s,e,t)=>{if(s.split(e).length!==2)throw Error("Pinned 2D angular layout changed");return s.replace(e,t)};function Jl(s){return s=ss(s,"case T_JOINT: { return 3u; }",`case T_JOINT: { return 3u; }
|
|
4570
|
+
case 5: { return 2u; }`),s=ss(s,"fn jointRowsC(j: u32, alpha: f32) -> vec3f {",`
|
|
4477
4571
|
fn limitAngle2D(k:Joint,a:i32,b:i32)->f32 {
|
|
4478
4572
|
var angleA=0.;if(a>=0){angleA=bodies[a].pose.z;}
|
|
4479
4573
|
return angleA-bodies[b].pose.z-k.param.x;
|
|
@@ -4483,10 +4577,10 @@ fn limitRows2D(k:Joint,a:i32,b:i32,alpha:f32)->vec3f {
|
|
|
4483
4577
|
return vec3f(angle-k.param.y,angle-k.param.z,0.)-alpha*k.c0.xyz;
|
|
4484
4578
|
}
|
|
4485
4579
|
`+`
|
|
4486
|
-
fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),
|
|
4580
|
+
fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),s=ss(s,` if (t == T_JOINT) {
|
|
4487
4581
|
// Hard rows`,` if(t==5){return limitRows2D(k,a,b,alpha);}
|
|
4488
4582
|
if (t == T_JOINT) {
|
|
4489
|
-
// Hard rows`),
|
|
4583
|
+
// Hard rows`),s=ss(s," let C = jointRowsC(j, alpha);",` let C = jointRowsC(j, alpha);
|
|
4490
4584
|
if(t==5){
|
|
4491
4585
|
// Unilateral rows only contribute while their force is active. Inside the
|
|
4492
4586
|
// interval a hinge has no angular stiffness and remains free to rotate.
|
|
@@ -4494,8 +4588,8 @@ fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),o=Ar(o,` if (t == T_JOINT) {
|
|
|
4494
4588
|
if(F.x!=0.){addRow(acc,vec3f(0.,0.,sg),vec3f(0.),C.x,k.pen.x,k.lam.x,k.stiff.x,k.fmin.x,k.fmax.x);}
|
|
4495
4589
|
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);}
|
|
4496
4590
|
return;
|
|
4497
|
-
}`),
|
|
4498
|
-
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.);}`),
|
|
4591
|
+
}`),s=ss(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); }
|
|
4592
|
+
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 $l=`// Analytic circles and capsules, finite two-sided segments and solid half-space planes.
|
|
4499
4593
|
// Kind 4 stores bounding radius, vertex-record offset and vertex count.
|
|
4500
4594
|
@group(0) @binding(7) var<storage,read> geometry:array<vec4f>;
|
|
4501
4595
|
struct Face2D { a:vec2f, b:vec2f, feature:u32 }
|
|
@@ -4648,7 +4742,7 @@ fn collideShapes2D(a:u32,b:u32)->Collision {
|
|
|
4648
4742
|
}
|
|
4649
4743
|
out.count=count;return out;
|
|
4650
4744
|
}
|
|
4651
|
-
`;var
|
|
4745
|
+
`;var ra=new WeakMap,_r=(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=(s,e)=>{if(!s||s.length!==2||!Array.from(s).every(Number.isFinite))throw Error(`${e} requires two finite components`);return Array.from(s)},Zt=s=>ra.get(s);function vo(s){let e=Array.from(s??[]),t=typeof e[0]=="number"?e:e.flatMap(x=>yo(x,"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 x=0;x<t.length;x+=2)i.push([t[x],t[x+1]]);i.sort((x,B)=>x[0]-B[0]||x[1]-B[1]);let r=i.filter((x,B)=>!B||x[0]!==i[B-1][0]||x[1]!==i[B-1][1]),o=(x,B,E)=>(B[0]-x[0])*(E[1]-x[1])-(B[1]-x[1])*(E[0]-x[0]),n=x=>{let B=[];for(let E of x){for(;B.length>1&&o(B.at(-2),B.at(-1),E)<=0;)B.pop();B.push(E)}return B.slice(0,-1)},a=[...n(r),...n([...r].reverse())];if(a.length<3||a.length>32)throw Error("A 2D hull needs 3\u201332 boundary vertices spanning an area");let l=a[0],c=a.map(x=>[x[0]-l[0],x[1]-l[1]]),u=0,f=0,d=0;for(let x=0;x<c.length;x++){let B=c[x],E=c[(x+1)%c.length],L=B[0]*E[1]-E[0]*B[1];u+=L,f+=(B[0]+E[0])*L,d+=(B[1]+E[1])*L}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(x=>[x[0]-f,x[1]-d]),g=0,h=0,m=0,b=0,v=[];for(let x=0;x<p.length;x++){let B=p[x],E=p[(x+1)%p.length],L=B[0]*E[1]-E[0]*B[1];g+=L*(B[0]*B[0]+B[0]*E[0]+E[0]*E[0]+B[1]*B[1]+B[1]*E[1]+E[1]*E[1])/12;let M=E[0]-B[0],z=E[1]-B[1],_=Math.hypot(M,z);v.push(B[0],B[1],z/_,-M/_),h=Math.max(h,Math.hypot(...B)),m=Math.max(m,Math.abs(B[0])),b=Math.max(b,Math.abs(B[1]))}let w=u/2,y=g/w;if(![w,y,h,m,b,...v].every(x=>Number.isFinite(Math.fround(x)))||Math.fround(w)<=0||Math.fround(y)<=0)throw Error("Hull geometry exceeds GPU floating-point range");let A=[4,h,0,p.length];return A.vertices=v,{geometry:A,size:[2*m,2*b],area:w,momentPerMass:y,radius:h}}function Ao(s){let e=_r(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 xo(s,e){let t=_r(s,"radius");if(_r(e,"length"),e<2*t)throw Error("Capsule length must be at least twice its radius");return Xl((e-2*t)/2,t)}function Xl(s,e){let t=2*s,i=2*e*t,r=Math.PI*e*e,o=i+r,n=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?n/o:0,radius:s+e}}function wo(s,e,t={}){let i=yo(s,"start"),r=yo(e,"end"),o=r[0]-i[0],n=r[1]-i[1],a=Math.hypot(o,n),l=_r(t.radius??0,"radius",!0);if(_r(a,"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{...Xl(a/2,l),options:{...t,density:t.density??0,position:[(i[0]+r[0])/2,(i[1]+r[1])/2],angle:Math.atan2(n,o)}}}function Bo(s,e=0,t={}){let i=yo(s,"normal"),r=_r(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,n=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,n*e],angle:Math.atan2(-o,n)}}}function Er(s,e,t,i){let r=ra.get(s);r||ra.set(s,r=new Map),r.set(e,t.geometry);let o=t.area*i,n=e*4;s.shape[n+2]=o,s.shape[n+3]=o*t.momentPerMass,s.props[n+1]=t.radius,s.dynamic[e]=+(o>0)}var Yl=`struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,velocity:vec4f,previous:vec4f,shape:vec4f}
|
|
4652
4746
|
struct Command { header:vec4u, value:vec4f, point:vec4f }
|
|
4653
4747
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
4654
4748
|
@group(0) @binding(1) var<storage,read> commands:array<Command>;
|
|
@@ -4684,11 +4778,11 @@ fn edit(@builtin(global_invocation_id) id:vec3u) {
|
|
|
4684
4778
|
bodies[index]=b;
|
|
4685
4779
|
}
|
|
4686
4780
|
\r
|
|
4687
|
-
`;function
|
|
4688
|
-
`+this.shader})}}),(!this.group||this.boundBody!==this.solver.bodyBuffer||this.boundCommands!==this.commands||this.boundRanges!==this.ranges||this.boundRangeBytes!==
|
|
4689
|
-
fn narrowphase
|
|
4781
|
+
`;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 ei=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),Ql(this.words,this.u32,this.count++*12,e,t,i,r,o)}};function Ql(s,e,t,i,r,o,n,a){e[t]=i,e[t+1]=r,e[t+2]=Number(a),e[t+3]=0;for(let l=0;l<4;l++)s[t+4+l]=o[l]??0,s[t+8+l]=n?.[l]??0}var Mr=class{constructor(e,t=Yl,i=2){this.solver=e,this.shader=t,this.dimension=i,this.pending=new Map,this.queue=new ei,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 n=this.slot(e,t);Ql(this.queue.words,this.queue.u32,n*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 n=this.angularOnly?"angularVelocityBatch":"velocityBatch",a=this.solver[n]??=new Qt(this.solver,this.dimension,this.angularOnly?"angular":"linear");a.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:a.lastBatch.uploadedBytes,path:this.angularOnly?"packed-angular-velocities":"packed-velocities",submissions:e?0:1}}else if(this.motionOnly){let r=this.motionBatch??=new gi(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 n=0;for(let[a,l]of this.pending){let c=n++*o;this.motionData[c]=a;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),g=d===0||this.dimension===3?this.dimension:1;for(let h=0;h<g;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 ei,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 g=p.head;g!==-1;g=this.next[g]){for(let h=0;h<12;h++)this.upload.u32[r*12+h]=this.queue.u32[g*12+h];r++}}let n=r*48,a=i*8,l=Math.min(t.limits.maxStorageBufferBindingSize,t.limits.maxBufferSize);if(n>l||a>l)throw Error("Body command batch exceeds GPU buffer limits");for(let[p,g]of[["commands",n],["ranges",a]])(!this[p]||this[p].size<g)&&(this[p]?.destroy(),this[p]=t.createBuffer({size:Math.min(l,Math.max(g*2,16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}));t.queue.writeBuffer(this.commands,0,this.upload.words.buffer,0,n),t.queue.writeBuffer(this.ranges,0,this.rangeData.buffer,0,a),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"edit",module:t.createShaderModule({code:"const DISPATCH_STRIDE = "+t.limits.maxComputeWorkgroupsPerDimension*64+`u;
|
|
4782
|
+
`+this.shader})}}),(!this.group||this.boundBody!==this.solver.bodyBuffer||this.boundCommands!==this.commands||this.boundRanges!==this.ranges||this.boundRangeBytes!==a)&&(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:a}}]}),this.boundBody=this.solver.bodyBuffer,this.boundCommands=this.commands,this.boundRanges=this.ranges,this.boundRangeBytes=a);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:n+a,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 Co=class extends ho{writePassConstants(e,t){Us.call(this,e,t)}createPipelines(){let e=this.layouts;this.compile(es,[e.broad],["beginFrame","gridCount","gridScatter","findPairs"]),this.compile(ts,[e.contacts],["hashInsert","narrowphase"]),this.compile(Dr,[e.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),this.compile(is,[e.solve,e.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),this.compile(Cs,[e.args],["argsPrev","argsPairs","argsContacts","argsColors"])}constructor(e,t,i={}){i.sleeping!==void 0&&i.sleeping!==!1&&go(i.sleeping),super(e,t,i),this.sleepEligible=new Map(t.avbdSleepEligible??[]),Hl(this),Zt(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 Zt(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 Zt(this.topology)??[]){let n=Array.from(o);o.vertices&&(n[2]=i,t.set(o.vertices,i*4),i+=o.vertices.length/4),t.set(n,r*4),this.packedShapes.set(r,n)}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 n=this.device.createCommandEncoder();n.copyBufferToBuffer(o,0,this.shapeBuffer,0,this.geometryRecords*16),this.device.queue.submit([n.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=ts;return this.shapeBuffer&&(e=e.replace(`@compute @workgroup_size(64)
|
|
4783
|
+
fn narrowphase`,$l+`
|
|
4690
4784
|
@compute @workgroup_size(64)
|
|
4691
|
-
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=
|
|
4785
|
+
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=es;if(this.shapeBuffer&&(e.push("read-only-storage"),t=t.replace(`fn radius(i: u32) -> f32 {
|
|
4692
4786
|
return 0.5 * length(bodies[i].shape.xy);
|
|
4693
4787
|
}`,`@group(0) @binding(7) var<storage,read> geometry:array<vec4f>;
|
|
4694
4788
|
fn radius(i:u32)->f32{let g=geometry[i];if(g.x==4.){return g.y;}if(g.x==1.||g.x==2.){return g.y+g.z;}return .5*length(bodies[i].shape.xy);}`).replace("return radius(i) > params.maxSmallRadius;","return geometry[i].x==3. || radius(i) > params.maxSmallRadius;").replace("if (dot(d, d) > r * r) { return; }",`if(geometry[a].x==3.||geometry[b].x==3.) {
|
|
@@ -4696,13 +4790,13 @@ let plane=select(b,a,geometry[a].x==3.);let other=select(a,b,geometry[a].x==3.);
|
|
|
4696
4790
|
if(dot(bodies[other].pose.xy-bodies[plane].pose.xy,normal)>radius(other)){return;}
|
|
4697
4791
|
} 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>;
|
|
4698
4792
|
fn ignored(hi:u32,lo:u32)->bool{
|
|
4699
|
-
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 s=e;s<e+t;s++)this.sleeping?.register(s,this.sleepEligible?.get(s)!==!1);if(!this.shapeBuffer&&!this.triggers?.size)return;let i=this.shapeBuffer&&t>0?new Float32Array(t*4):null,r=this.packedShapes;for(let s=e;s<e+t;s++){let a=r?.get(s);i&&a&&i.set(a,(s-e)*4),this.triggers?.has(s)&&this.device.queue.writeBuffer(this.bodyBuffer,s*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=Br(e),i=[];for(let u=0;u<this.bodyCount;u++)t?.get(u)?.[0]!==3&&i.push(e.props[u*4+1]);i.sort((u,d)=>u-d);let r=i[i.length>>1]??1,s=0,a=1/0;for(let u of i)u<=4*r&&(s=Math.max(s,u));for(let u of i)u>s&&(a=Math.min(a,u));let n=a===1/0?s*1.5:(s+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*s*(1+1e-4),.001);let l=[],c=[];for(let u=0;u<this.bodyCount;u++)(t?.get(u)?.[0]===3||e.props[u*4+1]>n)&&l.push(u);for(let u=0;u<e.jointCount;u++){let d=e.info[u*4+1],h=e.info[u*4+2];d>=0&&c.push([Math.max(d,h),Math.min(d,h),u])}for(let u of e.ignoredPairs()){let d=Math.floor(u/2097152);c.push([d,u-d*2097152,4294967295])}c.sort((u,d)=>u[0]-d[0]||u[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let f=new Uint32Array(Math.max(l.length+3*c.length,4));f.set(l),c.forEach((u,d)=>f.set(u,l.length+3*d)),this.staticBuffer.size<f.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"2D boundaries",size:f.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,f),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 Dt(this,2),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async circleCast(e,t,i,r={}){let[s]=await this.circleCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}circleCastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new Dt(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,s=!1){return this.liveIndex(e),this.prepareBodyCommands().enqueue(e,t,i,r,s),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands||(this.bodyCommands=new Qi(this),this.compileContacts()),this.bodyCommands}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,xt(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,xt(t,"impulse"),i===void 0?void 0:xt(i,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...xt(t,"force"),0,this.params.dt],i===void 0?void 0:xt(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,[...xt(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 s=new Float32Array(i.getMappedRange());return{position:[s[0],s[1]],angle:s[2],linearVelocity:[s[12],s[13]],angularVelocity:s[14],mass:s[22],moment:s[23],sleeping:s[15]!==0}}finally{i.destroy()}}addBox(e,t={}){return this.addShape({size:Array.from(e)},t)}addCircle(e,t={}){return this.addShape(Ls(e),t)}addHull(e,t={}){return this.addShape(Ps(e),t)}addCapsule(e,t,i={}){return this.addShape(_s(e,t),i)}addSegment(e,t,i={}){return this.addShape(Es(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(zs(e,t,i))}addShape(e,t={}){t=e.options??t;let{density:i=1,friction:r=.6,position:s=[0,0],angle:a=0,velocity:n=[0,0,0],restitution:l=0,isTrigger:c=!1,group:f=4294967295,collidesWith:u=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(p=>Number.isFinite(p)&&p>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([f,u].some(p=>!Number.isInteger(p)||p<0||p>4294967295))throw Error("Collision masks must be unsigned 32-bit integers");let h=xt(s,"position");e.geometry&&this.geometryBytes(e.geometry.vertices?.length??0);let m=super.addBody(e.size,i,r,[...h,a],Array.from(n));return e.geometry&&(Ms(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),(f!==4294967295||u!==4294967295)&&this.setFilters([m],[f],[u]),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 Bs(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*D]!==t)throw Error("The constraint has a different type");return e}setMotor(e,{speed:t,maxTorque:i}={}){if(this.constraintSlot(e,ys),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*T,s=e*36;t!==void 0&&(this.topology.data[r+ge]=t,Q(this).float(this.jointBuffer,s+32,t)),i!==void 0&&(this.topology.data[r+ft]=-i,this.topology.data[r+dt]=i,Q(this).float(this.jointBuffer,s+12,-i),Q(this).float(this.jointBuffer,s+16,i)),this.wakeAll()}setAngleLimits(e,t){this.constraintSlot(e,Cs);let i=Xi(t);this.topology.data[e*T+_e]=i.minAngle,this.topology.data[e*T+vt]=i.maxAngle,Q(this).float(this.jointBuffer,e*36+32+1,i.minAngle),Q(this).float(this.jointBuffer,e*36+32+2,i.maxAngle);for(let r=4;r<8;r++)Q(this).float(this.jointBuffer,e*36+r,0);this.wakeAll()}setWorldAnchor(e,t,i){if(this.constraintSlot(e,Vt),this.topology.info[e*D+1]!==-1||!Number.isFinite(t)||!Number.isFinite(i))throw Error("A world joint and finite xy anchor are required");Q(this).float(this.jointBuffer,e*36+28,t),Q(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,s,a=1/0){if(e!==-1&&this.liveIndex(e),this.liveIndex(t),e===t)throw Error("Constraint endpoints must differ");let n=xt(i,"anchorA"),l=xt(r,"anchorB");if(!s||s.length!==3||Array.from(s).some(f=>f!==1/0&&(!Number.isFinite(f)||f<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(s),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},s=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=Q(this);for(let[n,l,c]of s)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"}}),s=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,s),n.dispatchWorkgroups(Math.ceil(this.bodyCount/64)),n.end(),i.queue.submit([a.finish()]),this.compileContacts();let l=vr.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) {
|
|
4793
|
+
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=Zt(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 di(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 di(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 Mr(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(Ao(e),t)}addHull(e,t={}){return this.addShape(vo(e),t)}addCapsule(e,t,i={}){return this.addShape(xo(e,t),i)}addSegment(e,t,i={}){return this.addShape(wo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Bo(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 g=super.addBody(e.size,i,r,[...p,n],Array.from(a));return e.geometry&&(Er(this.topology,g,e,i),this.shapeBuffer?this.appendShapeGeometry(g,e.geometry):this.enableShapes(),this.uploadBodies(g,1)),l&&this.setRestitution(g,l),c&&this.setSensor(g,!0),(u!==4294967295||f!==4294967295)&&this.setFilters([g],[u],[f]),this.contactWatch?.setWatched([g],[!this.watchedIndices||this.watchedIndices.has(g)]),this.sleepEligible.set(g,d??!0),d===!0&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(g,d!==!1),this.wakeAll(),g}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 po(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,uo),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+Le]=-i,this.topology.data[r+xe]=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,bo);let i=Ci(t);this.topology.data[e*O+ge]=i.minAngle,this.topology.data[e*O+Fe]=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,xi),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=is.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) {
|
|
4700
4794
|
if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace("fn dualContact(c: u32) {",`fn dualContact(c: u32) {
|
|
4701
4795
|
if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace(` let k = contacts[c];
|
|
4702
4796
|
let bound`,` let k = contacts[c];
|
|
4703
4797
|
if((k.ids.w&0x80000000u)!=0u){return;}
|
|
4704
|
-
let bound`);this.compile(l,[this.layouts.solve,this.layouts.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]);let c=
|
|
4705
|
-
if((contacts[gid.x].ids.w&0x80000000u)!=0u){return;}`);this.compile(c,[this.layouts.topo],["degreeContacts","fillContacts"])}this.sensorsEnabled&&Q(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=Ln(zn(On(e)))),e.includes("fn dynamicBody(")&&(e=Mn(e)),e.includes("fn colorCount(")&&(e=_n(e));let r=this.device,s=r.createShaderModule({code:e}),a=r.createPipelineLayout({bindGroupLayouts:t});for(let n of i)this.pipes[n]=r.createComputePipeline({layout:a,compute:{module:s,entryPoint:n}})}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 Nt(this.device,this.bodyBuffer,this.bodyCapacity,e,t,2),i===void 0&&this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,s)=>s),Array(this.bodyCount).fill(!0))),i!==void 0&&(this.watchedIndices=new Set(i),this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,s)=>s),Array.from({length:this.bodyCount},(r,s)=>this.watchedIndices.has(s)))),this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Ut?"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");this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending?In.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)}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(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=Ss(e):this.sleeping=new ws(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 s=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;s=Math.min(t*l,s+Math.min(i,Math.max(0,c)));for(let f=0;s+1e-12>=l&&f<t&&(this.step(),s=Math.max(0,s-l),r?.(this),!(!this.sceneBinding||this.destroyed));f++);}),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 Rs=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function Be(o,e,t){let i=o&&typeof o.x=="number"?[o.x,o.y,...e>=3?[o.z]:[],...e===4?[o.w]:[]]:Array.isArray(o)||ArrayBuffer.isView(o)&&!(o instanceof DataView)?o:Array.from(o??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i}var Fn=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function pc(o,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new mi:this.editStaging??=new mi;this.editInProgress=!0;try{return mc.call(this,o,e,i)}finally{this.editInProgress=t}}function mc(o,e,t){t.count=0;let i,r=(s,a)=>{if(e===3)return Be(s,3,a);if(!Number.isFinite(s))throw Error(`${a} must be finite`);return[s,0,0]};for(let s of o){let a=this.bodyIndex(s.body),{isTrigger:n,restitution:l,group:c,collidesWith:f,allowSleep:u}=s;if(n!==void 0||l!==void 0||c!==void 0||f!==void 0||u!==void 0){if(n!==void 0&&typeof n!="boolean")throw Error("isTrigger must be a boolean");if(u!==void 0&&typeof u!="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(f!==void 0&&c===void 0)throw Error("A collision-mask edit requires group as well");c!==void 0&&Rs(c),f!==void 0&&Rs(f),(i??=[]).push({index:a,isTrigger:n,restitution:l,group:c,collidesWith:f??4294967295,allowSleep:u})}let d=s.worldPoint===void 0?void 0:Be(s.worldPoint,e,"worldPoint");if(s.position!==void 0){let h=Be(s.position,e,"position"),m;if(s.rotation!==void 0){if(m=e===3?Be(s.rotation,4,"rotation"):[s.rotation],e===3){let p=Math.hypot(...m);if(!p)throw Error("rotation must be nonzero");m=m.map(b=>b/p)}else if(!Number.isFinite(s.rotation))throw Error("angle must be finite")}t.append(a,4,h,e===3?m:void 0,m!==void 0),e===2&&(t.words[(t.count-1)*12+6]=m?.[0]??0)}else if(s.rotation!==void 0)throw Error("A rotation edit requires position; use teleport for a full pose");for(let h=0;h<Fn.length;h++){if(h===4)continue;let m=Fn[h];if(s[m]===void 0)continue;let b=h===1||h>=5?r(s[m],m):Be(s[m],e,m);t.append(a,h,b,h===2||h===3?d:void 0,(h===2||h===3)&&d!==void 0),(h===3||h===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 s of i??[])s.isTrigger!==void 0&&this.setSensor(s.index,s.isTrigger),s.restitution!==void 0&&this.setRestitution(s.index,s.restitution),s.group!==void 0&&this.setFilters([s.index],[s.group],[s.collidesWith]),s.allowSleep!==void 0&&this.setSleepEnabled(s.index,s.allowSleep);return this}var Os=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],[Rs(e)],[Rs(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 Nn(o,{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=o.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 Un(o,e,t,i){if(o.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:s,maxFrameTime:a}=Nn(o,t);o.timeAccumulator=Math.min(s*r,(o.timeAccumulator??0)+Math.min(a,e));let n=0;for(;o.timeAccumulator+1e-12>=r&&n<s;)o.step(),o.timeAccumulator=Math.max(0,o.timeAccumulator-r),n++,i?.(o);return n}function bc(o,e){return Un(this,o,e)}function qn(o,{afterStep:e,...t}={}){if(this.destroyed)throw Error("The solver has been destroyed");if(!o?.onBeforeRenderObservable?.add||!o?.onDisposeObservable?.addOnce||!o?.getEngine)throw Error("A Babylon scene is required");if(e!==void 0&&typeof e!="function")throw Error("afterStep must be a function");if(Nn(this,t),this.sceneBinding){if(this.sceneBinding.scene===o)return this;throw Error("The solver is already attached to another scene")}let i=o.onBeforeRenderObservable.add(()=>{Un(this,Math.max(0,o.getEngine().getDeltaTime()/1e3),t,e)}),r=o.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:o,before:i,dispose:r},this}function Vn(){let o=this.sceneBinding;return o&&(o.scene.onBeforeRenderObservable.remove(o.before),o.scene.onDisposeObservable.remove(o.dispose),this.sceneBinding=null,this.timeAccumulator=0),this}function js(o,e,t){o.advance=bc;let i,r;o.bodyIndex=function(a){if(this.destroyed)throw Error("The solver has been destroyed");let n=a;if(a instanceof Os){if(a.solver!==this)throw Error("Body belongs to another solver");return a.gpuIndex}return typeof n!="number"&&t===3&&(r||(r=new WeakMap,e.bodies.forEach((l,c)=>r.set(l,this.gpuIndex(c)))),n=r.get(a)),this.liveIndex(n),n},o.body=function(a){let n=this.bodyIndex(a);return i??=new Map,i.has(n)||i.set(n,new Os(this,n)),i.get(n)},o.editBodies=function(a){return pc.call(this,a,t)},o.setLinearVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return this.velocityBatch??=new Ct(this,t),this.velocityBatch.set(a,n),this},o.setVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return n?.angular===void 0&&n?.linear!==void 0?this.setLinearVelocities(a,n.linear):n?.linear===void 0&&n?.angular!==void 0?this.setAngularVelocities(a,n.angular):(this.motionBatch??=new Gt(this,t),this.motionBatch.set(a,n),this)},o.setAngularVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return this.angularVelocityBatch??=new Ct(this,t,"angular"),this.angularVelocityBatch.set(a,n),this};let s=o.destroy;return o.destroy=function(){this.destroyed||(this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback?.destroy(),this.editStaging=null,s.call(this),i?.clear(),r=null,e=null)},t===2&&(o.readSelectedBodies=function(a,{posesOnly:n=!1}={}){let l=a instanceof Uint32Array?a:Array.from(a);return l.forEach(c=>this.liveIndex(c)),this.flushEdits(),this.bodyReadback??=new Ft(this.device),this.bodyReadback.read(this.bodyBuffer,l,n,6)}),o}var Ts=class extends Is{constructor(e,t,i){super(e,t,i),js(this,null,2)}};var Ho=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
|
|
4798
|
+
let bound`);this.compile(l,[this.layouts.solve,this.layouts.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]);let c=Dr.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
|
|
4799
|
+
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=Gl(ql(Jl(e)))),e.includes("fn dynamicBody(")&&(e=Vl(e)),e.includes("fn colorCount(")&&(e=Nl(e));let r=this.device,o=r.createShaderModule({code:e}),n=r.createPipelineLayout({bindGroupLayouts:t});for(let a of i)this.pipes[a]=r.createComputePipeline({layout:n,compute:{module:o,entryPoint:a}})}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 yi(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===vi?"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");this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending?Wl.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)}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(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=go(e):this.sleeping=new mo(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 So=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 Kl=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function Qu(s,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new ei:this.editStaging??=new ei;this.editInProgress=!0;try{return Ku.call(this,s,e,i)}finally{this.editInProgress=t}}function Ku(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&&So(c),u!==void 0&&So(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"),g;if(o.rotation!==void 0){if(g=e===3?me(o.rotation,4,"rotation"):[o.rotation],e===3){let h=Math.hypot(...g);if(!h)throw Error("rotation must be nonzero");g=g.map(m=>m/h)}else if(!Number.isFinite(o.rotation))throw Error("angle must be finite")}t.append(n,4,p,e===3?g:void 0,g!==void 0),e===2&&(t.words[(t.count-1)*12+6]=g?.[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<Kl.length;p++){if(p===4)continue;let g=Kl[p];if(o[g]===void 0)continue;let m=p===1||p>=5?r(o[g],g):me(o[g],e,g);t.append(n,p,m,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 ko=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],[So(e)],[So(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 Zl(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 ec(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}=Zl(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 Zu(s,e){return ec(this,s,e)}function Po(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(Zl(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(()=>{ec(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 Lo(){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 zr(s,e,t,i={}){s.advance=Zu;let r,o;s.bodyIndex=function(a){if(this.destroyed)throw Error("The solver has been destroyed");let l=a;if(a instanceof ko){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 ko(this,l)),r.get(l)},s.editBodies=function(a){return Qu.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 Qt(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 gi(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 Qt(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 bi(this.device),this.bodyReadback.read(this.bodyBuffer,c,l,6)}),s}var _o=class extends Co{constructor(e,t,i){super(e,t,i),zr(this,null,2)}};var sa=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
|
|
4706
4800
|
struct Command { header:vec4u, value:vec4f, point:vec4f }
|
|
4707
4801
|
struct Manifold { ids:vec4u, geo:vec4f }
|
|
4708
4802
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
@@ -4755,26 +4849,26 @@ fn angularImpulse(b:Body,impulse:vec3f)->vec3f {
|
|
|
4755
4849
|
let index=commands[ranges[i].x].header.x;
|
|
4756
4850
|
if(bodies[index].initialPos.w<0.){bodies[index].initialPos.w=-bodies[index].initialPos.w-1.;}
|
|
4757
4851
|
}
|
|
4758
|
-
`;var
|
|
4759
|
-
${
|
|
4852
|
+
`;var oa=class extends Mr{constructor(e){super(e,sa,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;
|
|
4853
|
+
${sa}`});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 n=Math.ceil(i.manifoldCapacity/64),a=r.limits.maxComputeWorkgroupsPerDimension;o.dispatchWorkgroups(Math.min(n,a),Math.ceil(n/a)),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,a),Math.ceil(l/a)),o.end()}},Eo=class extends pi{constructor(e,t,i){super(e,t,i),this.nativeRef=t,zr(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 oa(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 n=Math.hypot(...o);if(!n)throw Error("rotation must be nonzero");o=o.map(a=>a/n)}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 bi(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 yi(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,n)=>n),Array.from({length:this.bodyCount},(o,n)=>r.has(n)))}return this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===vi?"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 Po.call(this,e,t)}detachFromScene(){return Lo.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 ns(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 ti=ns(ze," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if (j >= params.jointCount) { return; }
|
|
4760
4854
|
if (info[j].x == T_SPRING) {
|
|
4761
4855
|
// Finite material stiffness: Eq. 16, with no hard-constraint multiplier.
|
|
4762
4856
|
joints[j].penLin.x = min(joints[j].penLin.w, max(PENALTY_MIN, joints[j].penLin.x * params.gamma));
|
|
4763
4857
|
return;
|
|
4764
4858
|
}
|
|
4765
|
-
if (info[j].x != T_JOINT) { return; }`);
|
|
4859
|
+
if (info[j].x != T_JOINT) { return; }`);ti=ns(ti," let stiffness = k.penLin.w;"," let stiffness = k.penLin.x;");ti=ns(ti," addRow(acc, n * sg, cross(r, n) * sg, stiffness, stiffness * (len - k.rA.w));",` let force = stiffness * (len - k.rA.w);
|
|
4766
4860
|
addRow(acc, n * sg, cross(r, n) * sg, stiffness, force);
|
|
4767
4861
|
// Eq. 17: positive diagonal lumping of the geometric Hessian's column
|
|
4768
4862
|
// norms. For C=|xA-xB|-L, G=(force/len)(I-n*n^T).
|
|
4769
4863
|
let g = abs(force / len) * sqrt(max(vec3f(0.0), vec3f(1.0) - n*n));
|
|
4770
|
-
(*acc).lin += mat3x3f(vec3f(g.x,0.0,0.0), vec3f(0.0,g.y,0.0), vec3f(0.0,0.0,g.z));`);
|
|
4864
|
+
(*acc).lin += mat3x3f(vec3f(g.x,0.0,0.0), vec3f(0.0,g.y,0.0), vec3f(0.0,0.0,g.z));`);ti=ns(ti," if (t != T_JOINT) { return; }",` if (t == T_SPRING) {
|
|
4771
4865
|
let a = info[j].y;
|
|
4772
4866
|
let b = info[j].z;
|
|
4773
4867
|
let C = length(anchorA(joints[j], a) - (qrotate(bodies[b].rot, joints[j].rB.xyz) + bodies[b].pos.xyz)) - joints[j].rA.w;
|
|
4774
4868
|
joints[j].penLin.x = min(joints[j].penLin.w, joints[j].penLin.x + params.betaLin * abs(C));
|
|
4775
4869
|
return;
|
|
4776
4870
|
}
|
|
4777
|
-
if (t != T_JOINT) { return; }`);
|
|
4871
|
+
if (t != T_JOINT) { return; }`);ti=ns(ti,"fn finishBody(i: u32, acc: Acc) {",`fn finishBody(i: u32, acc: Acc) {
|
|
4778
4872
|
if (all(bodies[i].moment.xyz == vec3f(0.0))) {
|
|
4779
4873
|
// LDL^T of the point's 3x3 SPD system, using the same pivot floor as
|
|
4780
4874
|
// rigid bodies. Contact friction moves the point; it cannot spin it.
|
|
@@ -4792,4 +4886,4 @@ ${Ho}`});this.invalidate=r.createComputePipeline({layout:"auto",compute:{module:
|
|
|
4792
4886
|
let v1 = y1/d1 - l21*v2 - l31*v3;
|
|
4793
4887
|
bodies[i].pos = vec4f(bodies[i].pos.xyz - vec3f(v1,v2,v3), bodies[i].pos.w);
|
|
4794
4888
|
return;
|
|
4795
|
-
}`);var Hn=Pt;var J=(o,e,t)=>{let i=Array.from(o);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i},V=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},Wn=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function Gs(o){for(let t of["allowSleep","isTrigger"])if(o[t]!==void 0&&typeof o[t]!="boolean")throw Error(`${t} must be a boolean`);let e=o.restitution??0;if(!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");if(o.mass!==void 0&&(V(o.mass,"mass",!0),o.mass>0&&o.density===0))throw Error("A positive mass needs positive density");for(let t of["velocity","angularVelocity"])o[t]!==void 0&&J(o[t],3,t);if(o.rotation!==void 0&&!Math.hypot(...J(o.rotation,4,"rotation")))throw Error("rotation must be nonzero");Wn(o.group??4294967295),Wn(o.collidesWith??4294967295)}function Jn(o,e){if(e.timeStep!==void 0&&(o.dt=V(e.timeStep,"timeStep")),e.iterations!==void 0){if(V(e.iterations,"iterations"),!Number.isInteger(e.iterations))throw Error("iterations must be an integer");o.iterations=e.iterations}if(e.gravity!==void 0){if(!Number.isFinite(e.gravity))throw Error("gravity must be finite");o.gravity=e.gravity}}var Jo=class{constructor(e={}){if(this.ref=new Pi,Jn(this.ref,e),this.up=J(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(),Gs(t);let i=J(e,3,"size");i.forEach(s=>V(s,"size"));let r=new at(this.ref,i,V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position"));return this.configure(r,t)}addSphere(e,t={}){return this.assertEditable(),Gs(t),this.configure(Rr(this.ref,V(e,"radius"),V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position")),t)}addHull(e,t={}){if(this.assertEditable(),Gs(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=er(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(Or(this.ref,i,V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position")),t)}addCapsule(e,t,i={}){return this.assertEditable(),Gs(i),this.configure(ts(this.ref,e,t,V(i.density??1,"density",!0),V(i.friction??.6,"friction",!0),J(i.position??[0,0,0],3,"position")),i)}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=V(t.mass,"mass",!0);if(e.mass===0&&r>0)throw Error("A positive mass needs positive density");let s=e.mass>0?r/e.mass:0;e.mass=r,e.moment.forEach((a,n)=>e.moment[n]=a*s)}if(t.rotation){let r=J(t.rotation,4,"rotation"),s=Math.hypot(...r);if(!s)throw Error("rotation must be nonzero");e.positionAng.set(r.map(a=>a/s))}return t.velocity&&e.velocityLin.set(J(t.velocity,3,"velocity")),t.angularVelocity&&e.velocityAng.set(J(t.angularVelocity,3,"angularVelocity")),this.bodySet.add(e),e}addPoint(e,{mass:t=.02,radius:i=.05,friction:r=.5}={}){let s=this.addSphere(i,{position:e,mass:t,friction:r});return s.moment.fill(0),this.points=!0,s}addFabric({columns:e=16,rows:t=16,spacing:i=.2,origin:r=[0,3,0],mass:s=.02,stiffness:a=1e3,anchorStiffness:n=a,materialAt:l,breakStrain:c=1/0,breakForce:f=1/0,bendingStiffness:u=Number.isFinite(c)||Number.isFinite(f)?0:a/50,pinCorners:d=!1,pinEdges:h=!d}={}){for(let[M,P]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(P)||P<2)throw Error(`${M} must be an integer >= 2`);if(V(i,"spacing"),V(s,"mass"),V(a,"stiffness"),V(n,"anchorStiffness"),l!==void 0&&typeof l!="function")throw Error("materialAt must be a function");if(V(u,"bendingStiffness",!0),typeof d!="boolean"||typeof h!="boolean")throw Error("Fabric pin options must be boolean");let m=St({breakStrain:c,breakForce:f}),p=J(r,3,"origin"),b=(M,P)=>`${Math.min(M,P)},${Math.max(M,P)}`;function*y(){for(let M=0;M<t;M++)for(let P=0;P<e;P++){let _=M*e+P;P&&(yield[_,_-1,a]),M&&(yield[_,_-e,a]),P&&M&&(yield[_,_-e-1,a/2],yield[_-1,_-e,a/2]),P>1&&u&&(yield[_,_-2,u]),M>1&&u&&(yield[_,_-2*e,u])}}let A=new Map;if(l)for(let[M,P]of y()){let _=l((M%e+P%e)/(2*(e-1)),(Math.floor(M/e)+Math.floor(P/e))/(2*(t-1)))??{};if(typeof _!="object"||Array.isArray(_))throw Error("materialAt must return a material object");A.set(b(M,P),{scale:V(_.stiffnessScale??1,"materialAt.stiffnessScale"),material:St({...m,..._})})}let w=Array.from({length:t},(M,P)=>Array.from({length:e},(_,S)=>this.addPoint([p[0]+(S-(e-1)/2)*i,p[1],p[2]+(P-(t-1)/2)*i],{mass:h&&(S===0||P===0||S===e-1||P===t-1)||d&&(S===0||S===e-1)&&(P===0||P===t-1)?0:s,radius:i/Math.SQRT2}))),g=w.flat(),x=new Map(g.map((M,P)=>[M,P])),v=[],B=new Map,E=(M,P,_)=>{(M.mass===0||P.mass===0)&&(_*=n/a);let S=x.get(M),C=x.get(P),R=A.get(b(S,C)),I=this.addSpring(M,P,[0,0,0],[0,0,0],{stiffness:_*(R?.scale??1),...R?.material??m});return B.set(b(x.get(M),x.get(P)),v.length),v.push(I),I};for(let[M,P,_]of y())E(g[M],g[P],_);let k=[];for(let M=0;M<t;M++)for(let P=0;P<e;P++)if(P&&M){let _=(M-1)*e+P-1,S=_+1,C=M*e+P-1,R=C+1;k.push(_,C,S,S,C,R)}let z=[];for(let M=0;M<k.length;M+=3){let[P,_,S]=k.slice(M,M+3);z.push(B.get(b(P,_)),B.get(b(_,S)),B.get(b(S,P)))}return{points:g,grid:w,indices:Uint32Array.from(k),connections:v,triangleConnections:Uint32Array.from(z)}}addRope({segments:e=12,length:t=12,origin:i=[0,12,0],width:r=.15,endMass:s=20}={}){if(!Number.isInteger(e)||e<1)throw Error("segments must be a positive integer");V(t,"length"),V(r,"width"),V(s,"endMass",!0);let a=J(i,3,"origin"),n=t/e,l=this.addBox([r,r,r],{mass:0,position:a}),c=[];for(let f=0;f<e;f++){let u=this.addBox([r,n,r],{mass:1,position:[a[0],a[1]-(f+.5)*n,a[2]]});this.addJoint(f?c[f-1]:l,u,[0,f?-n/2:0,0],[0,n/2,0]),c.push(u)}if(s){let f=this.addBox([.6,.6,.6],{mass:s,position:[a[0],a[1]-t-.3,a[2]]});this.addJoint(c.at(-1),f,[0,-n/2,0],[0,.3,0]),c.push(f)}return{anchor:l,links:c}}addRagdoll({origin:e=[0,2,0],mass:t=10}={}){let i=J(e,3,"origin");V(t,"mass");let r=(l,c)=>this.addBox(l,{mass:t/10,position:c.map((f,u)=>f+i[u])}),s=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=[s,a];this.addJoint(s,a,[0,.6,0],[0,-.3,0]);for(let l of[-1,1]){let c=r([.3,.8,.3],[l*.5,1.95,0]),f=r([.26,.7,.26],[l*.5,1.2,0]);this.addJoint(s,c,[l*.35,.35,0],[-l*.15,.4,0]),this.addJoint(c,f,[0,-.4,0],[0,.35,0]);let u=r([.28,.8,.3],[l*.2,1,0]),d=r([.26,.75,.28],[l*.2,.225,0]);this.addJoint(s,u,[l*.2,-.6,0],[0,.4,0]),this.addJoint(u,d,[0,-.4,0],[0,.375,0]),n.push(c,f,u,d)}return{torso:s,head:a,bodies:n}}addJoint(e,t,i=[0,0,0],r=[0,0,0],{linearStiffness:s=1/0,angularStiffness:a=0,breakForce:n=1/0,breakOnPull:l=!1}={}){this.assertEditable(),this.checkBodies(e,t);for(let c of[s,a,n])c!==1/0&&V(c,"joint stiffness/breakForce",!0);return this.handle(new Ae(this.ref,e,t,J(i,3,"anchorA"),J(r,3,"anchorB"),s,a,l&&n!==1/0?-n:n))}addSpring(e,t,i=[0,0,0],r=[0,0,0],{stiffness:s=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");V(s,"stiffness",!0),a!==-1&&V(a,"rest",!0);let c=St({breakStrain:n,breakForce:l}),f=new ct(this.ref,e,t,J(i,3,"anchorA"),J(r,3,"anchorB"),s,a);return Yr(f,c),this.handle(f)}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=gt({...i,type:"motor"}),s=new Ae(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);return Fi(s,r),this.handle(s)}addHinge(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=gt({...i,type:"limit"}),s=V(i.span??1,"span"),a=J(i.anchorA??[0,0,0],3,"anchorA"),n=J(i.anchorB??[0,0,0],3,"anchorB"),l=[-1,1].map(u=>this.addJoint(e,t,a.map((d,h)=>d+u*s*.5*r.axisA[h]),n.map((d,h)=>d+u*s*.5*r.axisB[h]))),c,f;if(i.minAngle!==void 0||i.maxAngle!==void 0){let u=new Ae(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);Fi(u,r),c=this.handle(u)}return i.motor&&(f=this.addMotor(e,t,{axisA:r.axisA,axisB:r.axisB,...i.motor})),{joints:l,limit:c,motor:f,dispose(){for(let u of[...l,c,f])u?.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(this.assertEditable(),this.hulls&&e.limits.maxStorageBuffersPerShaderStage<9)throw Error("Hull collisions require nine storage buffer bindings");let i=new Ds(e,this.ref,rs(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:Hn}}:{}})),r=[];this.ref.forces.forEach((l,c)=>{this.handles.get(l).slot=c,l instanceof Ae&&l.stiffnessAng>0&&r.push({slot:c})}),i.captureConstraintFrames(r),i.params.up=[...this.up];let s=[],a=[],n=[];for(let[l,c]of this.ref.bodies.entries())(c.group!==4294967295||c.collidesWith!==4294967295)&&(s.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 s.length&&i.setFilters(s,a,n),(t.sleeping||this.sleepOptIn)&&i.enableSleeping(this.ref,t.sleeping===!0?{}:t.sleeping),this.gpu=i,this.created=!0,i}},$o=class{constructor(e={}){if(this.topology=new vs,Object.assign(this.topology.params,ps()),Jn(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 Ms(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(Ls(e),t)}addHull(e,t={}){return this.addShape(Ps(e),t)}addCapsule(e,t,i={}){return this.addShape(_s(e,t),i)}addSegment(e,t,i={}){return this.addShape(Es(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(zs(e,t,i))}addBox(e,{density:t=1,friction:i=.6,position:r=[0,0],angle:s=0,velocity:a=[0,0,0],restitution:n=0,isTrigger:l=!1,group:c=4294967295,collidesWith:f=4294967295,allowSleep:u}={}){if(this.assertEditable(),u!==void 0&&typeof u!="boolean")throw Error("allowSleep must be boolean");let d=J(e,2,"size");d.forEach(p=>V(p,"size"));let h=J(r,2,"position");if(!Number.isFinite(s))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 p of[c,f])if(!Number.isInteger(p)||p<0||p>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let m=this.topology.addBody(d,V(t,"density",!0),V(i,"friction",!0),[...h,s],J(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||f!==4294967295)&&this.filters.set(m,[c,f]),this.topology.avbdSleepEligible??=new Map,u!==void 0&&this.topology.avbdSleepEligible.set(m,u),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:s=[1/0,1/0,0],breakForce:a=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!s||s.length!==3||Array.from(s).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,J(i,2,"anchorA"),J(r,2,"anchorB"),s,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*T+$,e*T+$+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:s=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,J(i,2,"anchorA"),J(r,2,"anchorB"),V(s,"stiffness",!0),V(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 s=this.topology.jointCount;return this.topology.addMotor(e,t,i,V(r,"maxTorque",!0)),this.constraintHandle(s)}addAngularLimit(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=Xi(i),s=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);qo(this.topology,s.slot,r);let a=this.constraintHandle(s.slot,s);return a.setLimits=n=>{if(a.disposed)throw Error("The angular limit has been disposed");return this.gpu?this.gpu.setAngleLimits(a.slot,n):qo(this.topology,a.slot,Xi(n)),a},a}addHinge(e,t,i=[0,0],r=[0,0],s={}){this.assertEditable(),this.checkBodies(e,t);let a=J(i,2,"anchorA"),n=J(r,2,"anchorB"),l=s.minAngle!==void 0||s.maxAngle!==void 0?Xi(s):null,c=s.motor;if(c){if(!Number.isFinite(c.speed??1))throw Error("speed must be finite");V(c.maxTorque??100,"maxTorque",!0)}let f=this.addJoint(e,t,a,n),u=l?this.addAngularLimit(e,t,l):null,d=c?this.addMotor(e,t,c):null;return{joint:f,limits:u,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={}){this.assertEditable();let i=new Ts(e,this.topology,t);for(let[r,s]of this.restitution??[])i.setRestitution(r,s);for(let r of this.sensors??[])i.setSensor(r,!0);for(let[r,s]of this.filters??[])i.setFilters([r],[s[0]],[s[1]]);return this.gpu=i,this.created=!0,i}};export{as as AvbdPhysics,ns as AvbdPhysicsAggregate,ls as AvbdPhysicsBody,fr as AvbdPhysicsConstraint,dr as AvbdPhysicsHinge,$o as AvbdScene2D,Jo as AvbdScene3D,$e as AvbdShapeType,jo as createWebGPUDevice};
|
|
4889
|
+
}`);var tc=ti;var nc=8,ic=16,$i=1e-6,aa=1e-5,tf=1e-6,la=34028234663852886e22,Mo=0,ac=1,zo=2;function rc(s){let e=s.positionAng;return{center:s.positionLin,half:ne(C(),s.size,.5),axis:[ae(C(),e,[1,0,0]),ae(C(),e,[0,1,0]),ae(C(),e,[0,0,1])]}}var Ji=(s,e)=>Math.abs(V(s,e));function Io(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(C(),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 lc(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 rf(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(C(),s.axis[e],i),o=D(C(),s.center,r,s.half[e]);return{normal:r,center:o,...lc(s,e)}}function sf(s,e){let t=0,i=-la;for(let r=0;r<3;r++){let o=Ji(s.axis[r],e);o>i&&(i=o,t=r)}return t}function of(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(C(),s.axis[e],i),o=D(C(),s.center,r,s.half[e]),{u:n,v:a,extentU:l,extentV:c}=lc(s,e),u=(f,d)=>{let p=D(C(),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 nf(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<=aa,c=a<=aa;if(l!==c){let u=0,f=o-a;Math.abs(f)>$i&&(u=ce(o/f,0,1)),i.length<ic&&i.push(D(C(),r,$(C(),n,r),u))}c&&i.length<ic&&i.push(n),r=n,o=a}return i}var Ro=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(C(),ki(C(),e,t),.5);for(let a of this.midpoints)if(Ge($(C(),r,a))<tf)return;if(this.contacts.length>=nc)return;let{bodyA:o,bodyB:n}=this;this.contacts.push({feature:i,rA:Te(C(),o.positionAng,$(C(),e,o.positionLin)),rB:Te(C(),n.positionAng,$(C(),t,n.positionLin))}),this.midpoints.push(r)}};function na(s,e,t,i,r,o,n,a){let l=Ge(i);if(l<$i)return!0;let c=1/Math.sqrt(l),u=ne(C(),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]),g=f-(d+p);return g>0?!1:((!a.valid||g>a.separation)&&(a.valid=!0,a.type=r,a.indexA=o,a.indexB=n,a.separation=g,a.normalAB=u),!0)}function sc(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(C(),s.center,s.axis[i],s.half[i]*o);D(a,a,s.axis[r],s.half[r]*n);let l=ne(C(),s.axis[e],s.half[e]);return[$(C(),a,l),ki(C(),a,l)]}function af(s,e,t,i){let r=$(C(),e,s),o=$(C(),i,t),n=$(C(),s,t),a=V(r,r),l=V(o,o),c=V(o,n),u=0,f=0;if(a<=$i&&l<=$i)return[s,t];if(a<=$i)f=ce(c/l,0,1);else{let d=V(r,n);if(l<=$i)u=ce(-d/a,0,1);else{let p=V(r,o),g=a*l-p*p;Math.abs(g)>$i&&(u=ce((p*c-d*l)/g,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(C(),s,r,u),D(C(),t,o,f)]}function oc(s,e,t,i,r,o,n){let a=r?t:i,l=r?i:t,c=r?n:ee(C(),n),u=rf(a,o,c),f=sf(l,u.normal),d=of(l,f,u.normal),p=[[u.u,u.extentU],[ee(C(),u.u),u.extentU],[u.v,u.extentV],[ee(C(),u.v),u.extentV]];for(let[m,b]of p)if(d=nf(d,m,V(m,u.center)+b),!d.length)return[];let g=new Ro(s,e),h=(r?Mo:ac)<<24|(o&255)<<16|(f&255)<<8;for(let m=0;m<d.length&&g.contacts.length<nc;m++){let b=d[m],v=V($(C(),b,u.center),u.normal);if(v>aa)continue;let w=D(C(),b,u.normal,-v),y=r?w:b,A=r?b:w;g.add(y,A,h|m&255)}return g.contacts.length||g.add(Io(t,n),Io(i,ee(C(),n)),h),g.contacts}function lf(s,e,t,i,r,o,n){let[a,l]=sc(t,r,n),[c,u]=sc(i,o,ee(C(),n)),[f,d]=af(a,l,c,u),p=new Ro(s,e),g=zo<<24|(r&255)<<8|o&255;return p.add(f,d,g),p.contacts.length||p.add(Io(t,n),Io(i,ee(C(),n)),g),p.contacts}function cc(s,e,t,i=!0){let r=rc(s),o=rc(e),n=$(C(),o.center,r.center),a={type:Mo,indexA:0,indexB:0,separation:-la,normalAB:C(),valid:!1},l={type:zo,indexA:0,indexB:0,separation:-la,normalAB:C(),valid:!1};for(let f=0;f<3;f++)if(!na(r,o,n,r.axis[f],Mo,f,-1,a))return[];for(let f=0;f<3;f++)if(!na(r,o,n,o.axis[f],ac,-1,f,a))return[];let c=C();for(let f=0;f<3;f++)for(let d=0;d<3;d++)if(Dt(c,r.axis[f],o.axis[d]),!na(r,o,n,c,zo,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),ds(t,ee(C(),u.normalAB)),u.type===zo?lf(s,e,r,o,u.indexA,u.indexB,u.normalAB):u.type===Mo?oc(s,e,r,o,!0,u.indexA,u.normalAB):oc(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),G=(s,e)=>s.reduce((t,i,r)=>t+i*e[r],0),yt=(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)),_e=(s,e)=>Array.from(ae(C(),s,e)),uc=(s,e)=>Te(C(),s.positionAng,T(e,s.positionLin)),Ot=(s,e)=>de(s.positionLin,_e(s.positionAng,e)),cf=[[1,3,7,5],[0,4,6,2],[2,6,7,3],[0,1,5,4],[4,5,7,6],[0,2,3,1]],uf=[[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]];function Ir(s){if(De(s)||He(s)){let n=(De(s),s.size[0]/2),a=He(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=wt(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:_e(s.positionAng,n.normal)})):cf.map((n,a)=>({vertices:n.map(l=>t[l]),normal:_e(s.positionAng,uf[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)G(r,e)>G(i,e)&&(i=r);return s.spine?de(i,q(e,s.radius)):i}function ff(s,e,t){let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,G(T(t,s),i)/(G(i,i)||1)))))}function Oo([s,e],[t,i]){let r=T(e,s),o=T(i,t),n=T(s,t),a=G(r,r),l=G(o,o),c=G(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=G(r,n);if(l<1e-16)u=d(-p/a);else{let g=G(r,o),h=a*l-g*g;u=h>1e-12*a*l?d((g*c-p*l)/h):0,f=(g*u+c)/l,f<0?(f=0,u=d(-p/a)):f>1&&(f=1,u=d((g-p)/a))}}return[de(s,q(r,u)),de(t,q(o,f))]}function df(s,e,t){let i=[];for(let r=0;r<s.length;r++){let o=s[r],n=s[(r+1)%s.length],a=G(o,e)-t,l=G(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 hf(s,e,t,i){let r=T(t,e),o=T(i,e),n=T(s,e),a=G(r,n),l=G(o,n);if(a<=0&&l<=0)return e;let c=T(s,t),u=G(r,c),f=G(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),g=G(r,p),h=G(o,p);if(h>=0&&g<=h)return i;let m=g*l-a*h;if(m<=0&&l>=0&&h<=0)return de(e,q(o,l/(l-h)));let b=u*h-g*f;if(b<=0&&f-u>=0&&g-h>=0)return de(t,q(T(i,t),(f-u)/(f-u+g-h)));let v=1/(b+m+d);return de(e,de(q(r,m*v),q(o,d*v)))}function pf(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=hf(e,r.vertices[0],r.vertices[o],r.vertices[o+1]),a=G(T(e,n),T(e,n));a<i&&(t=n,i=a)}return t}function fc(s,e,t,i,r){if(!De(s)&&!De(e)&&!He(s)&&!He(e)&&!wt(s)&&!wt(e))return cc(s,e,t,s.solver.faceBias!==!1);i??=Ir(s),r??=Ir(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),g=G(T(as(i,q(p,-1)),as(r,p)),p),h=G(T(as(r,q(p,-1)),as(i,p)),p);h>g&&(g=h,p=q(p,-1)),g>o+1e-7&&(o=g,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(yt(T(f[1],f[0]),T(d[1],d[0])),[f,d]);if(i.spine&&r.spine){let f=Oo(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,ff(...f.spine,p)));for(let p of d.edges){let g=Oo(f.spine,p);l(T(...g))}}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(yt(n,Math.abs(n[0])<.7?[1,0,0]:[0,1,0]));t.set([...n,...c,...yt(n,c)]);let u=[];if(i.spine&&r.spine){let[f,d]=Oo(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,g=as(f,p),h=pf(d,g);u=[i.spine?[g,h]:[h,g]]}else if(a)u=[Oo(...a)];else{let f=(w,y)=>w.faces.reduce((A,x)=>G(x.normal,y)>G(A.normal,y)?x:A,w.faces[0]),d=f(i,q(n,-1)),p=f(r,n),g=G(d.normal,q(n,-1))>=G(p.normal,n),h=g?d:p,b=f(g?r:i,q(h.normal,-1)).vertices,v=q(h.vertices.reduce((w,y)=>de(w,y),[0,0,0]),1/h.vertices.length);for(let w=0;w<h.vertices.length&&b.length;w++){let y=h.vertices[w],A=h.vertices[(w+1)%h.vertices.length],x=Tt(yt(T(A,y),h.normal));G(T(v,y),x)>0&&(x=q(x,-1)),b=df(b,x,G(y,x))}for(let w of b){let y=G(T(w,h.vertices[0]),h.normal);if(y<=1e-6){let A=T(w,q(h.normal,y));u.push(g?[A,w]:[w,A])}}}return u.slice(0,8).map(([f,d],p)=>({feature:p+1,rA:uc(s,f),rB:uc(e,d)}))}var To=(s,e,t)=>Math.max(e,Math.min(t,s)),mf=(s,e)=>[G(s.slice(0,3),e),G(s.slice(3,6),e),G(s.slice(6,9),e)];function fa(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=Or(jt(),Yo(jt(),t),s.bodyB.positionAng),s.evaluateAng=i=>{let r=Or(jt(),s.bodyA?.positionAng??[0,0,0,1],s.cpuRest);lt(i,r,s.bodyB.positionAng);for(let o=0;o<3;o++)i[o]*=s.torqueArm;return i}}var ca=class extends oi{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)||He(e)||He(t);this.friction=Math.sqrt(e.friction*t.friction),this.contacts=fc(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:C(),penalty:n?n.penalty.slice():C(),lambda:n?n.lambda.slice():C(),stick:n?.stick??!1}});for(let o of this.contacts){o.C0.set(mf(this.basis,T(Ot(e,o.rA),Ot(t,o.rB)))),o.C0[0]+=ps;for(let n=0;n<3;n++)o.lambda[n]*=this.solver.alpha*this.solver.gamma,o.penalty[n]=To(o.penalty[n]*this.solver.gamma,Et,Ue);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],To(n/this.solver.dt**2,Et,Ue))}}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)}},ua=class extends Gt{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?_e(this.bodyA.positionAng,this.ta):this.ta,n=Array.from(Te(C(),this.bodyB.positionAng,o));n=T(n,q(i.axisB,G(n,i.axisB))),this.tb=Tt(n)}frame(){let e=this.bodyA?_e(this.bodyA.positionAng,this.options.axisA):this.options.axisA,t=this.bodyA?_e(this.bodyA.positionAng,this.ta):this.ta,i=_e(this.bodyB.positionAng,this.tb);return{axis:e,angle:Math.atan2(G(e,yt(t,i)),G(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]=To(this.pen[t]*this.solver.gamma,Et,Ue);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"?[To(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(Ue,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 jo(s){if(!s.cpuPrepared){s.cpuPrepared=!0;for(let e of[...s.forces]){let t=Hr(e);if(t){e.unlinkFromBodies(),s.forces.splice(s.forces.indexOf(e),1),e.cpuAngular=new ua(s,e,t);continue}if(e instanceof ke&&e.stiffnessAng>0&&fa(e),e instanceof ut){let i=e.initialize.bind(e);e.initialize=()=>{if(e.broken)return!1;let r=Gi(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 dc(s){let e=s.bodies;s.cpuGeometry=new Map;for(let l of e)(De(l)||He(l)||wt(l))&&s.cpuGeometry.set(l,Ir(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],g=e[f];if(!((p.group??4294967295)&(g.collidesWith??4294967295))||!((g.group??4294967295)&(p.collidesWith??4294967295)))return;let h=p.radius+g.radius;G(T(p.positionLin,g.positionLin),T(p.positionLin,g.positionLin))<=h*h&&!p.constrainedTo(g)&&new ca(s,p,g)};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 g=u[2];g<=f[2];g++){let h=`${d},${p},${g}`,m=r.get(h)??[];for(let b of m)a(l,b);m.push(l),r.set(h,m)}}for(let l of o)for(let c=0;c<e.length;c++)a(l,c)}function Do(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 Fo(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,m)=>{o.set(h.velocityLin,m*r),o.set(h.velocityAng,m*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,m)=>{if(!n(h))return e===3?[0,0,0]:0;if(e===2)return s.shape[h*4+3]>0?m/s.shape[h*4+3]:0;let b=s.bodies[h],v=b.positionAng,w=_e([-v[0],-v[1],-v[2],v[3]],m);return _e(v,w.map((y,A)=>b.moment[A]>0?y/b.moment[A]:0))},l=(h,m,b)=>{let v=m*r;return e===3?T(h.subarray(v,v+3),yt(b,h.subarray(v+3,v+6))):[h[v]-h[v+2]*b[1],h[v+1]+h[v+2]*b[0]]},c=(h,m)=>e===3?yt(h,m):h[0]*m[1]-h[1]*m[0],u=(h,m)=>e===3?G(h,m):h*m,f;if(e===3){let h=new Map(s.bodies.map((m,b)=>[m,b]));f=s.forces.filter(m=>m.contacts?.length&&!m.bodyA.isTrigger&&!m.bodyB.isTrigger).map(m=>{let b=(v,w)=>{let y=[0,0,0];for(let A of m.contacts){let x=_e(v.positionAng,A[w]);for(let B=0;B<3;B++)y[B]+=x[B]/m.contacts.length}return y};return{a:h.get(m.bodyA),b:h.get(m.bodyB),n:m.basis.slice(0,3),ra:b(m.bodyA,"rA"),rb:b(m.bodyB,"rB"),e:Math.max(m.bodyA.restitution??0,m.bodyB.restitution??0)}})}else{let h=(m,b)=>{let v=Math.cos(s.pose[m*4+2]),w=Math.sin(s.pose[m*4+2]);return[v*b[0]-w*b[1],w*b[0]+v*b[1]]};f=s.contactPairs.filter(m=>!s.sensors.has(m.a)&&!s.sensors.has(m.b)).map(m=>({...m,ra:h(m.a,m.ra),rb:h(m.b,m.rb),e:Math.max(s.restitution.get(m.a)??0,s.restitution.get(m.b)??0)}))}let d=new Uint32Array(i);if(e===3)s.bodies.forEach((h,m)=>{d[m]=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 m=G(h.n,T(l(t,h.a,h.ra),l(t,h.b,h.rb)));if(m>=-1)continue;let b=c(h.ra,h.n),v=c(h.rb,h.n),w=a(h.a,b),y=a(h.b,v),A=n(h.a)+n(h.b)+u(b,w)+u(v,y);A>0&&p.push({...h,inertiaA:w,inertiaB:y,k:A,target:-h.e*m,impulse:0})}let g=new Float64Array(o.length);for(let h=0;h<12&&p.length;h++){g.fill(0);for(let m of p){let b=G(m.n,T(l(o,m.a,m.ra),l(o,m.b,m.rb))),v=Math.max(0,m.impulse+(m.target-b)/(m.k*Math.max(1,d[m.a],d[m.b]))),w=v-m.impulse;m.impulse=v;for(let[y,A,x]of[[m.a,1,m.inertiaA],[m.b,-1,m.inertiaB]]){let B=y*r,E=n(y);for(let L=0;L<e;L++)g[B+L]+=A*w*E*m.n[L];if(e===3)for(let L=0;L<3;L++)g[B+3+L]+=A*w*x[L];else g[B+2]+=A*w*x}}for(let m=0;m<o.length;m++)o[m]+=g[m]}e===3&&s.bodies.forEach((h,m)=>{h.velocityLin.set(o.subarray(m*r,m*r+3)),h.velocityAng.set(o.subarray(m*r+3,m*r+6))})}function hc(s,e,t){let i=e.windSpeed??0;if(!sr(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=G(a,o),u=G(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),g=e.windGust??.4,h=o.map((A,x)=>i*A*(1+g*f)+g*i*(n[x]*.25*d+(x===2?.1*p:0))-s.velocityLin[x]),m=_e(s.positionAng,[0,0,1]),b=(e.windPressure??.72)*s.size[0]*s.size[1]/s.mass,v=G(h,m),w=h.map((A,x)=>A-m[x]*v),y=b*v*Math.abs(v)/(1+2*e.dt*b*Math.abs(v));return m.map((A,x)=>A*y+w[x]*.06*b*Math.hypot(...w))}var gf=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),Go=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,gf())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=C(),n=C();for(let a=this.bodies.length-1;a>=0;a--){let l=this.bodies[a];if(l.mass<=0)continue;Te(o,l.positionAng,$(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 g=l.size[p]*.5;if(Math.abs(n[p])<1e-6){(o[p]<-g||o[p]>g)&&(f=!1);continue}let h=1/n[p],m=(-g-o[p])*h,b=(g-o[p])*h;m>b&&([m,b]=[b,m]),c=Math.max(c,m),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(C(),o,n,d),t:d})}return r}sys=ms();dxLin=C();dxAng=C();dp=C();accel=C();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:o}=this,n=Do(this,3);jo(this),dc(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=hc(c,this,this.cpuStep);u&&D(c.inertialLin,c.inertialLin,u,e*e),ri(c.inertialAng,c.positionAng,pc(c.velocityAng,e)),_t(this.accel,$(this.accel,c.velocityLin,c.prevVelocityLin),e);let f=V(this.accel,this.up)*us(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),ri(c.positionAng,c.positionAng,pc(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,$(o,u.positionLin,u.inertialLin)),ue(r.rhsAng,r.lhsAng,lt(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 g=u.moment.every(h=>h===0);g&&(Ce(r.lhsAng,1,1,1),r.lhsCross.fill(0),r.rhsAng.fill(0)),hs(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),g||ri(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,$(l.velocityLin,l.positionLin,l.initialLin),e),_t(l.velocityAng,lt(l.velocityAng,l.positionAng,l.initialAng),e));Fo(this,3,n),this.cpuStep++}},bf=C(),pc=(s,e)=>ne(bf,s,e);var cs=(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),rt=(s,e)=>s[0]*e[0]+s[1]*e[1],ls=(s,e)=>[Math.cos(s)*e[0]-Math.sin(s)*e[1],Math.sin(s)*e[0]+Math.cos(s)*e[1]],gc=s=>Math.max(0,Math.min(1,s)),mc=(s,e,t)=>cs(s,Lt(Re(e,s),gc(rt(Re(t,s),Re(e,s))/(rt(Re(e,s),Re(e,s))||1))));function No(s,e){let t=Zt(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=>cs(i,ls(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 Xi(s,e){if(s.vertices){let r=-1/0,o=[];for(let n of s.vertices){let a=rt(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=cs(s.spine[0],Lt(e,s.radius)),i=cs(s.spine[1],Lt(e,s.radius));return Math.abs(rt(t,e)-rt(i,e))<1e-7?[t,i]:rt(t,e)>rt(i,e)?[t,t]:[i,i]}function bc(s,e,t,i){let r=No(s,e),o=No(s,t),n=s.pose,a=s.shape;if(r.kind===0&&o.kind===0)return oo(...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=ls(f.angle,[0,1]),g=Xi(d,Lt(p,-1));for(let h of g){let m=rt(Re(h,f.p),p);if(m<=0){let b=Re(h,Lt(p,m));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=A=>{let x=Math.hypot(...A);if(x<1e-9)return;let B=Lt(A,1/x),E=rt(Re(Xi(r,Lt(B,-1))[0],Xi(o,B)[0]),B),L=rt(Re(Xi(o,Lt(B,-1))[0],Xi(r,B)[0]),B);Math.max(E,L)>u+1e-7&&(u=Math.max(E,L),f=E>=L?B:Lt(B,-1))};for(let[A,x]of[[r,o],[o,r]])if(A.vertices)for(let B=0;B<A.vertices.length;B++){let E=Re(A.vertices[(B+1)%A.vertices.length],A.vertices[B]);d([E[1],-E[0]]),x.vertices||d(Re(A.vertices[B],mc(...x.spine,A.vertices[B])))}else if(d(ls(A.angle,[0,1])),!x.vertices)for(let B of A.spine)d(Re(B,mc(...x.spine,B)));if(u===-1/0&&d([0,1]),u>0)return 0;let p=Xi(r,Lt(f,-1)),g=Xi(o,f),h=[-f[1],f[0]],m=p.map(A=>rt(A,h)),b=g.map(A=>rt(A,h)),v=Math.max(Math.min(...m),Math.min(...b)),w=Math.min(Math.max(...m),Math.max(...b)),y=(A,x)=>{let B=rt(Re(A[1],A[0]),h);return Math.abs(B)<1e-9?A[0]:cs(A[0],Lt(Re(A[1],A[0]),gc((x-rt(A[0],h))/B)))};for(let A of w-v>1e-6?[v,w]:[(v+w)/2])l.push({a:y(p,A),b:y(g,A),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=ls(-r.angle,Re(u.a,r.p)),d=ls(-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 yc=(s,e,t)=>[Math.cos(s)*e-Math.sin(s)*t,Math.sin(s)*e+Math.cos(s)*t],vc=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 g=u+f>>>1;this.prevKeys[g]<n?u=g+1:f=g}let d=u,p=Math.sqrt(t[a*4]*t[l*4]);for(let g=0;g<c;g++){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 m=this.data,b=h*O,v=g*7,w=r[v+0];this.info.set([Zr,a,l,w],h*j),m.fill(0,b,b+O),m[b+Q]=m[b+Q+1]=1/0,m[b+Le]=-1/0,m[b+xe]=0,m[b+Pt]=m[b+Pt+1]=1/0,m[b+U]=r[v+1],m[b+U+1]=r[v+1+1],m[b+Y]=r[v+3],m[b+Y+1]=r[v+3+1],m[b+re]=p,m[b+ge]=r[v+5],m[b+Fe]=r[v+5+1];let y=-1;for(let k=d;k<this.prevCount&&this.prevKeys[k]===n;k++)this.prevInfo[k*j+3]===w&&(y=k);if(y<0&&this.params.matchNearest){let k=ea*Math.min(e[a*4],e[a*4+1],e[l*4],e[l*4+1]);for(let S=d;S<this.prevCount&&this.prevKeys[S]===n;S++){let P=Math.hypot(this.prevData[S*O+U]-m[b+U],this.prevData[S*O+U+1]-m[b+U+1]);P<=k&&(k=P,y=S)}}if(y>=0){let k=y*O;m[b+se]=this.prevData[k+se],m[b+se+1]=this.prevData[k+se+1],m[b+te]=this.prevData[k+te],m[b+te+1]=this.prevData[k+te+1],this.prevData[k+Vi]&&(m[b+U]=this.prevData[k+U],m[b+U+1]=this.prevData[k+U+1],m[b+Y]=this.prevData[k+Y],m[b+Y+1]=this.prevData[k+Y+1])}let[A,x]=yc(s[a*4+2],m[b+U],m[b+U+1]),[B,E]=yc(s[l*4+2],m[b+Y],m[b+Y+1]),L=m[b+ge],M=m[b+Fe],z=s[a*4]+A-s[l*4]-B,_=s[a*4+1]+x-s[l*4+1]-E;m[b+fe]=L*z+M*_+5e-4,m[b+fe+1]=M*z-L*_}}};var Uo=class extends Lr{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 Zt(e)??[]){let n=this.shape[r*4+2];Er(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=[],vc.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=bc(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 g=0;g<2;g++)l[g]+=i[p*7+1+g]/n,c[g]+=i[p*7+3+g]/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+ge]),this.data[t+fe+1]=Math.max(0,o-this.data[t+Fe])}}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+ge]-t*a[r+fe],this.eC[1]=l-a[r+Fe]-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+Le+c],this.eMax[c]=a[r+xe+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=Do(this,2);super.step(),Fo(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 Vo=(s,e,t)=>{let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,G(T(t,s),i)/(G(i,i)||1)))))};function qo(s,e,t,i){let r=T(s,t),o=G(r,e),n=o*o-G(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 Rr(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=G(o,o);if(a<1e-16)return qo(s,e,t,r);let l=G(o,e),c=G(o,n),u=G(n,e),f=a-l*l,d=a*u-c*l,p=a*G(n,n)-c*c-r*r*a,g=d*d-f*p,h=null;if(f>1e-12*a&&g>=0){let m=(-d-Math.sqrt(g))/f,b=c+m*l;m>=0&&b>=0&&b<=a&&(h={t:m,n:Tt(T(de(s,q(e,m)),de(t,q(o,b/a))))})}return Rr(Rr(h,qo(s,e,t,r)),qo(s,e,i,r))}function Ac(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=yt(T(o,r),e.normal);if(G(n,T(s,r))*(G(n,T(t,r))>=0?1:-1)<-1e-6)return!1}return!0}function yf(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=G(u.normal,t),d=G(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=G(a.normal,T(e,a.vertices[0]));l>0&&(r=!1),Ac(T(e,q(a.normal,l)),a)&&(o=Math.min(o,l*l));let c=G(a.normal,t);if(c<-1e-12){let u=(i-l)/c;u>=0&&Ac(T(de(e,q(t,u)),q(a.normal,i)),a)&&(n=Rr(n,{t:u,n:a.normal}))}}for(let[a,l]of s.edges){let c=T(e,Vo(a,l,e));o=Math.min(o,G(c,c)),n=Rr(n,da(e,t,a,l,i))}return r||o<=i*i?{t:0,n:q(t,-1)}:n}var vt=(s,e)=>s[0]*e[0]+s[1]*e[1];function vf(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=vt(T(e,l),f);d>0&&(r=!1);let p=T(e,Vo(l,c,e));o=Math.min(o,vt(p,p));let g=vt(t,f);if(g<-1e-12){let h=(i-d)/g,m=T(de(e,q(t,h)),q(f,i)),b=vt(T(m,l),u),v=1e-6*vt(u,u);h>=0&&b>=-v&&b<=vt(u,u)+v&&(n=Rr(n,{t:h,n:f}))}i>0&&(n=Rr(n,qo(e,t,l,i)))}return r||o<=i*i?{t:0,n:q(t,-1)}:n}function Af(s,e,t,i){if(s.vertices)return vf(s,e,t,i);if(s.kind===3){let p=[-Math.sin(s.angle),Math.cos(s.angle)],g=vt(T(e,s.p),p)-i,h=vt(t,p);return g<=0?{t:0,n:q(t,-1)}:h<-1e-12?{t:-g/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,Vo(r,o,e));if(vt(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(vt(T(r,e),t),vt(T(o,e),t));return Math.max(vt(T(r,e),t),vt(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 xc(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),g=null;for(let h=0;h<s.bodyCount;h++){if(d.has(h))continue;let m=l===3?s.solver.bodies[h]:null,b=m?m.group??4294967295:s.solver.filters.get(h)?.[0]??4294967295,v=m?m.isTrigger:s.solver.sensors.has(h);if(!(b&n)||!a&&v)continue;let w;if(l===2)w=Af(No(s.solver,h),c,p,t);else{let A=Ir(m);if(A.spine){let x=A.radius+t,B=T(c,Vo(...A.spine,c));w=G(B,B)<=x*x?{t:0,n:q(p,-1)}:da(c,p,...A.spine,x)}else w=yf(A,c,p,t)}if(!w||w.t>r||g&&w.t>=g.distance)continue;let y=de(c,q(p,w.t));g={index:h,distance:w.t,normal:w.n,center:y,point:T(y,q(w.n,t))}}return g}var Wo=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,g)=>[p,g]));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),g=e.dimension===3?t.bodies[d]:null,h=g?g.allowSleep!==!1:t.avbdSleepEligible?.get(d)!==!1,m=g?Math.hypot(...g.velocityLin,...g.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&&m<=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 Ho=class{constructor(e,t,i={}){if(this.backend="cpu",this.dimension=t,this.scene=e,this.stepCount=0,this.pending=new ei,this.events=[],this.previous=new Map,this.watched=null,t===3){this.solver=Object.assign(new Go,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)}),jo(this.solver)}else this.solver=new Uo(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,zr(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(g=>Number.isFinite(Math.fround(g)))))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],g=f?.positionLin??r.pose.subarray(l*4,l*4+2),h=m=>{if(!(p<=0))if(o===2)r.velocity[l*4+2]+=m[0]/Math.max(1e-10,r.shape[l*4+3]);else{let b=f.positionAng,v=_e([-b[0],-b[1],-b[2],b[3]],m),w=_e(b,v.map((y,A)=>y/Math.max(1e-10,f.moment[A])));for(let y=0;y<3;y++)f.velocityAng[y]+=w[y]}};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(g.set(e.subarray(a+4,a+4+o)),f){u&&f.positionAng.set(e.subarray(a+8,a+12)),f.initialLin.set(g),f.inertialLin.set(g),f.initialAng.set(f.positionAng),f.inertialAng.set(f.positionAng);for(let m of[...f.forces])m.contacts&&m.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 m=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]+=m[b]/p;if(u){let b=Array.from(e.subarray(a+8,a+8+o),(v,w)=>v-g[w]);h(o===3?yt(b,m):[b[0]*m[1]-b[1]*m[0]])}}else(c===5||c===6)&&h(Array.from(e.subarray(a+4,a+4+(o===3?3:1)),m=>m*(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 Po.call(this,e,t)}detachFromScene(){return Lo.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+xe];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+Le]=-n,i.data[r+xe]=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");os(this.solver,e,Ci(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)fa(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");gr(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,g)=>d+p*i.up[g],0),a.set(f.velocityAng,u+36),a[u+39]=De(f)?1:He(f)?-1:wt(f)?3:sr(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,Le,xe,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+U],r[a+U+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=Gi(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=_e(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(),xc(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 Wo(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 wc=new WeakSet,Jo=s=>s!==null&&typeof s=="object"&&wc.has(s),Bc=s=>wc.add(s);async function xf({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 xr(e):({device:e,adapter:o}=await Jr({...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 g=u.ref?3:u.topology?2:0;if(f.bodyBuffer!==void 0)throw Error("A GPU body buffer requires the WebGPU backend");d=new Ho(u,g,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 Bc(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},Cc=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s};function $o(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");Cc(s.group??4294967295),Cc(s.collidesWith??4294967295)}function Sc(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 ha=class{constructor(e={}){if(this.ref=new ni,Sc(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(),$o(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(),$o(t),this.configure(or(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(),$o(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=jr(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(Bs(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(),$o(i),this.configure(Ar(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)}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[z,_]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(_)||_<2)throw Error(`${z} 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 g=It({breakStrain:c,breakForce:u}),h=ie(r,3,"origin"),m=(z,_)=>`${Math.min(z,_)},${Math.max(z,_)}`;function*b(){for(let z=0;z<t;z++)for(let _=0;_<e;_++){let k=z*e+_;_&&(yield[k,k-1,n]),z&&(yield[k,k-e,n]),_&&z&&(yield[k,k-e-1,n/2],yield[k-1,k-e,n/2]),_>1&&f&&(yield[k,k-2,f]),z>1&&f&&(yield[k,k-2*e,f])}}let v=new Map;if(l)for(let[z,_]of b()){let k=l((z%e+_%e)/(2*(e-1)),(Math.floor(z/e)+Math.floor(_/e))/(2*(t-1)))??{};if(typeof k!="object"||Array.isArray(k))throw Error("materialAt must return a material object");v.set(m(z,_),{scale:K(k.stiffnessScale??1,"materialAt.stiffnessScale"),material:It({...g,...k})})}let w=Array.from({length:t},(z,_)=>Array.from({length:e},(k,S)=>this.addPoint([h[0]+(S-(e-1)/2)*i,h[1],h[2]+(_-(t-1)/2)*i],{mass:p&&(S===0||_===0||S===e-1||_===t-1)||d&&(S===0||S===e-1)&&(_===0||_===t-1)?0:o,radius:i/Math.SQRT2}))),y=w.flat(),A=new Map(y.map((z,_)=>[z,_])),x=[],B=new Map,E=(z,_,k)=>{(z.mass===0||_.mass===0)&&(k*=a/n);let S=A.get(z),P=A.get(_),I=v.get(m(S,P)),R=this.addSpring(z,_,[0,0,0],[0,0,0],{stiffness:k*(I?.scale??1),...I?.material??g});return B.set(m(A.get(z),A.get(_)),x.length),x.push(R),R};for(let[z,_,k]of b())E(y[z],y[_],k);let L=[];for(let z=0;z<t;z++)for(let _=0;_<e;_++)if(_&&z){let k=(z-1)*e+_-1,S=k+1,P=z*e+_-1,I=P+1;L.push(k,P,S,S,P,I)}let M=[];for(let z=0;z<L.length;z+=3){let[_,k,S]=L.slice(z,z+3);M.push(B.get(m(_,k)),B.get(m(k,S)),B.get(m(S,_)))}return{points:y,grid:w,indices:Uint32Array.from(L),connections:x,triangleConnections:Uint32Array.from(M)}}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 ut(this.ref,e,t,ie(i,3,"anchorA"),ie(r,3,"anchorB"),o,n);return gr(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 vr(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);vr(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(Jo(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 Eo(e,this.ref,Ws(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:tc}}:{}})),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}},pa=class{constructor(e={}){if(this.topology=new Lr,Object.assign(this.topology.params,io()),Sc(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 Er(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(Ao(e),t)}addHull(e,t={}){return this.addShape(vo(e),t)}addCapsule(e,t,i={}){return this.addShape(xo(e,t),i)}addSegment(e,t,i={}){return this.addShape(wo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Bo(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 g=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(g,a),this.sensors??=new Set,l&&this.sensors.add(g),this.filters??=new Map,(c!==4294967295||u!==4294967295)&&this.filters.set(g,[c,u]),this.topology.avbdSleepEligible??=new Map,f!==void 0&&this.topology.avbdSleepEligible.set(g,f),g}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=Ci(i),o=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);os(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):os(this.topology,n.slot,Ci(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?Ci(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(Jo(e))return e.createSolver(this,t);this.assertEditable();let i=new _o(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}};export{$s as AvbdPhysics,Xs as AvbdPhysicsAggregate,Ys as AvbdPhysicsBody,$r as AvbdPhysicsConstraint,Xr as AvbdPhysicsHinge,pa as AvbdScene2D,ha as AvbdScene3D,ht as AvbdShapeType,xf as createSolverPipeline,Jr as createWebGPUDevice,xr as prepareWebGPUDevice3D};
|