avbd-babylon 0.2.0 → 0.4.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 +864 -116
- package/dist/avbd.d.ts +2 -0
- package/dist/avbd.global.js +617 -153
- package/dist/avbd.js +617 -153
- package/dist/babylonAvbd.d.ts +31 -6
- package/dist/native.d.ts +1 -0
- package/dist/native.js +1 -1
- package/dist/nativeScenes.d.ts +81 -5
- package/dist/solverPipeline.d.ts +62 -0
- package/dist/utilities.d.ts +161 -0
- package/dist/utilities.js +1 -0
- package/docs/BABYLON_API.md +34 -5
- package/docs/HPLOC.md +119 -115
- package/docs/README.md +58 -14
- package/docs/RELEASE.md +26 -2
- package/package.json +38 -4
- package/tests/README.md +188 -25
package/dist/avbd.global.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(()=>{var pl=Object.defineProperty;var ml=(o,e)=>{for(var t in e)pl(o,t,{get:e[t],enumerable:!0})};var Ys={};ml(Ys,{AvbdPhysics:()=>xr,AvbdPhysicsAggregate:()=>Ar,AvbdPhysicsBody:()=>Br,AvbdPhysicsConstraint:()=>$i,AvbdPhysicsHinge:()=>Xi,AvbdScene2D:()=>Xs,AvbdScene3D:()=>$s,AvbdShapeType:()=>Ue,createWebGPUDevice:()=>hs,prepareWebGPUDevice3D:()=>gr});var L=(o=0,e=0,t=0)=>Float64Array.of(o,e,t),Pi=(o=0,e=0,t=0,i=1)=>Float64Array.of(o,e,t,i),Y=()=>new Float64Array(9);var ua=o=>o<0?-1:o>0?1:0,tt=(o,e)=>o<e?o:e,bl=(o,e)=>o>e?o:e,Oe=(o,e,t)=>bl(e,tt(t,o)),Z=(o,e)=>o[0]*e[0]+o[1]*e[1]+o[2]*e[2],it=o=>Z(o,o),Li=o=>Math.sqrt(it(o));function He(o,e,t,i){return o[0]=e,o[1]=t,o[2]=i,o}var Mr=(o,e,t)=>He(o,e[0]+t[0],e[1]+t[1],e[2]+t[2]),X=(o,e,t)=>He(o,e[0]-t[0],e[1]-t[1],e[2]-t[2]),Ce=(o,e,t)=>He(o,e[0]*t,e[1]*t,e[2]*t),ei=(o,e,t)=>He(o,e[0]/t,e[1]/t,e[2]/t),he=(o,e)=>He(o,-e[0],-e[1],-e[2]),Qs=(o,e)=>He(o,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),N=(o,e,t,i)=>He(o,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),gl=(o,e)=>ei(o,e,Li(e)),Mt=(o,e,t)=>He(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 ki(o,e,t,i,r){return o[0]=e,o[1]=t,o[2]=i,o[3]=r,o}function fa(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 ki(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 yl=(o,e)=>ki(o,-e[0],-e[1],-e[2],e[3]),da=o=>it(o)+o[3]*o[3];function vl(o,e){let t=da(e);return ki(o,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function xl(o,e){let t=Math.sqrt(da(e));return ki(o,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var Si=new Float64Array(4),Zt=new Float64Array(4);function It(o,e,t){return fa(Si,e,vl(Si,t)),He(o,Si[0]*2,Si[1]*2,Si[2]*2)}function Ir(o,e,t){return ki(Zt,t[0],t[1],t[2],0),fa(Zt,Zt,e),xl(o,ki(o,e[0]+Zt[0]*.5,e[1]+Zt[1]*.5,e[2]+Zt[2]*.5,e[3]+Zt[3]*.5))}function ue(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 He(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 ca=new Float64Array(3);function ht(o,e,t,i){return ue(ca,t,i),Mr(o,ca,e)}function _i(o,e,t){return ue(o,yl(Si,e),t)}function je(o,e,t,i){return o.fill(0),o[0]=e,o[4]=t,o[8]=i,o}function Ks(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 ti(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 Te(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],p=t[1],m=t[2],h=t[3],b=t[4],y=t[5],v=t[6],w=t[7],g=t[8];return o[0]=i*d+r*h+s*v,o[1]=i*p+r*b+s*w,o[2]=i*m+r*y+s*g,o[3]=a*d+n*h+l*v,o[4]=a*p+n*b+l*w,o[5]=a*m+n*y+l*g,o[6]=c*d+f*h+u*v,o[7]=c*p+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 He(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 ii(o,e,t){for(let i=0;i<9;i++)o[i]=e[i]*t;return o}function ha(o,e){for(let t=0;t<9;t++)o[t]=-e[t];return o}function Rr(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 pa(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 je(o,t,i,r)}function ma(o,e){let t=Math.abs(e[0])>Math.abs(e[2])?L(-e[1],e[0],0):L(0,-e[2],e[1]);gl(t,t);let i=Mt(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 ba(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],p=t[0],m=t[1],h=t[2],b=e[0],y=t[3],v=t[4],w=t[5],g=e[3],A=e[4],x=t[6],B=t[7],E=t[8],k=e[6],z=e[7],M=e[8],P=l/n,_=f/n,C=p/n,S=y/n,R=x/n,I=n,O=c-P*P*I,q=(u-P*_*I)/O,G=(m-P*C*I)/O,j=(v-P*S*I)/O,F=(B-P*R*I)/O,H=d-(_*_*I+q*q*O),ee=(h-_*C*I-q*G*O)/H,le=(w-_*S*I-q*j*O)/H,xe=(E-_*R*I-q*F*O)/H,U=b-(C*C*I+G*G*O+ee*ee*H),ze=(g-C*S*I-G*j*O-ee*le*H)/U,Me=(k-C*R*I-G*F*O-ee*xe*H)/U,ce=A-(S*S*I+j*j*O+le*le*H+ze*ze*U),qe=(z-S*R*I-j*F*O-le*xe*H-ze*Me*U)/ce,zt=M-(R*R*I+F*F*O+xe*xe*H+Me*Me*U+qe*qe*ce),Ie=i[0],Ve=i[1]-P*Ie,Re=i[2]-_*Ie-q*Ve,We=r[0]-C*Ie-G*Ve-ee*Re,et=r[1]-S*Ie-j*Ve-le*Re-ze*We,Ai=r[2]-R*Ie-F*Ve-xe*Re-Me*We-qe*et,Bi=Ie/I,wi=Ve/O,Ci=Re/H,At=We/U,dl=et/ce,hl=Ai/zt;a[2]=hl,a[1]=dl-qe*a[2],a[0]=At-ze*a[1]-Me*a[2],s[2]=Ci-ee*a[0]-le*a[1]-xe*a[2],s[1]=wi-q*s[2]-G*a[0]-j*a[1]-F*a[2],s[0]=Bi-P*s[1]-_*s[2]-C*a[0]-S*a[1]-R*a[2]}var or=1,pt=1e10,ga=.01,ya=1e-5,De=class{solver;forces=[];positionLin;positionAng=Pi();initialLin=L();initialAng=Pi();inertialLin=L();inertialAng=Pi();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=Li([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}},va=()=>({lhsLin:Y(),lhsAng:Y(),lhsCross:Y(),rhsLin:L(),rhsAng:L()}),Rt=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,xa=16,si=1e-6,eo=1e-5,Al=1e-6,to=34028234663852886e22,Or=0,Sa=1,jr=2;function Aa(o){let e=o.positionAng;return{center:o.positionLin,half:Ce(L(),o.size,.5),axis:[ue(L(),e,[1,0,0]),ue(L(),e,[0,1,0]),ue(L(),e,[0,0,1])]}}var ri=(o,e)=>Math.abs(Z(o,e));function Tr(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=N(L(),o.center,o.axis[0],o.half[0]*t);return N(s,s,o.axis[1],o.half[1]*i),N(s,s,o.axis[2],o.half[2]*r)}function ka(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 Bl(o,e,t){let i=Z(t,o.axis[e])>=0?1:-1,r=Ce(L(),o.axis[e],i),s=N(L(),o.center,r,o.half[e]);return{normal:r,center:s,...ka(o,e)}}function wl(o,e){let t=0,i=-to;for(let r=0;r<3;r++){let s=ri(o.axis[r],e);s>i&&(i=s,t=r)}return t}function Cl(o,e,t){let i=Z(o.axis[e],t)>0?-1:1,r=Ce(L(),o.axis[e],i),s=N(L(),o.center,r,o.half[e]),{u:a,v:n,extentU:l,extentV:c}=ka(o,e),f=(u,d)=>{let p=N(L(),s,a,u*l);return N(p,p,n,d*c)};return[f(1,1),f(-1,1),f(-1,-1),f(1,-1)]}function Sl(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<=eo,c=n<=eo;if(l!==c){let f=0,u=s-n;Math.abs(u)>si&&(f=Oe(s/u,0,1)),i.length<xa&&i.push(N(L(),r,X(L(),a,r),f))}c&&i.length<xa&&i.push(a),r=a,s=n}return i}var Dr=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=Ce(L(),Mr(L(),e,t),.5);for(let n of this.midpoints)if(it(X(L(),r,n))<Al)return;if(this.contacts.length>=Ca)return;let{bodyA:s,bodyB:a}=this;this.contacts.push({feature:i,rA:_i(L(),s.positionAng,X(L(),e,s.positionLin)),rB:_i(L(),a.positionAng,X(L(),t,a.positionLin))}),this.midpoints.push(r)}};function Zs(o,e,t,i,r,s,a,n){let l=it(i);if(l<si)return!0;let c=1/Math.sqrt(l),f=Ce(L(),i,c);Z(f,t)<0&&he(f,f);let u=Math.abs(Z(t,f)),d=o.half[0]*ri(f,o.axis[0])+o.half[1]*ri(f,o.axis[1])+o.half[2]*ri(f,o.axis[2]),p=e.half[0]*ri(f,e.axis[0])+e.half[1]*ri(f,e.axis[1])+e.half[2]*ri(f,e.axis[2]),m=u-(d+p);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 Ba(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=N(L(),o.center,o.axis[i],o.half[i]*s);N(n,n,o.axis[r],o.half[r]*a);let l=Ce(L(),o.axis[e],o.half[e]);return[X(L(),n,l),Mr(L(),n,l)]}function kl(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<=si&&l<=si)return[o,t];if(n<=si)u=Oe(c/l,0,1);else{let d=Z(r,a);if(l<=si)f=Oe(-d/n,0,1);else{let p=Z(r,s),m=n*l-p*p;Math.abs(m)>si&&(f=Oe((p*c-d*l)/m,0,1)),u=(p*f+c)/l,u<0?(u=0,f=Oe(-d/n,0,1)):u>1&&(u=1,f=Oe((p-d)/n,0,1))}}return[N(L(),o,r,f),N(L(),t,s,u)]}function wa(o,e,t,i,r,s,a){let n=r?t:i,l=r?i:t,c=r?a:he(L(),a),f=Bl(n,s,c),u=wl(l,f.normal),d=Cl(l,u,f.normal),p=[[f.u,f.extentU],[he(L(),f.u),f.extentU],[f.v,f.extentV],[he(L(),f.v),f.extentV]];for(let[b,y]of p)if(d=Sl(d,b,Z(b,f.center)+y),!d.length)return[];let m=new Dr(o,e),h=(r?Or:Sa)<<24|(s&255)<<16|(u&255)<<8;for(let b=0;b<d.length&&m.contacts.length<Ca;b++){let y=d[b],v=Z(X(L(),y,f.center),f.normal);if(v>eo)continue;let w=N(L(),y,f.normal,-v),g=r?w:y,A=r?y:w;m.add(g,A,h|b&255)}return m.contacts.length||m.add(Tr(t,a),Tr(i,he(L(),a)),h),m.contacts}function Pl(o,e,t,i,r,s,a){let[n,l]=Ba(t,r,a),[c,f]=Ba(i,s,he(L(),a)),[u,d]=kl(n,l,c,f),p=new Dr(o,e),m=jr<<24|(r&255)<<8|s&255;return p.add(u,d,m),p.contacts.length||p.add(Tr(t,a),Tr(i,he(L(),a)),m),p.contacts}function Pa(o,e,t){let i=Aa(o),r=Aa(e),s=X(L(),r.center,i.center),a={type:Or,indexA:0,indexB:0,separation:-to,normalAB:L(),valid:!1},n={type:jr,indexA:0,indexB:0,separation:-to,normalAB:L(),valid:!1};for(let f=0;f<3;f++)if(!Zs(i,r,s,i.axis[f],Or,f,-1,a))return[];for(let f=0;f<3;f++)if(!Zs(i,r,s,r.axis[f],Sa,-1,f,a))return[];let l=L();for(let f=0;f<3;f++)for(let u=0;u<3;u++)if(Mt(l,i.axis[f],r.axis[u]),!Zs(i,r,s,l,jr,f,u,n))return[];if(!a.valid)return[];let c=a;return n.valid&&.95*n.separation>a.separation+.01&&(c=n),ma(t,he(L(),c.normalAB)),c.type===jr?Pl(o,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===Or?wa(o,e,i,r,!0,c.indexA,c.normalAB):wa(o,e,i,r,!1,c.indexB,c.normalAB)}var La=L(),_a=L(),Ea=L(),za=L(),Ma=L(),Ia=L(),Gr=Y(),io=Y(),ro=Y(),Fr=Y(),so=Y(),oo=Y(),ao=Y(),Ur=Y(),pe=L(),Ae=L(),oi=L(),Ot=L(),Ra=L(),Oa=L(),Ei=0,ar=0;function ja(o,e,t){for(let i=0;i<3;i++)Ot[0]=t[i*3],Ot[1]=t[i*3+1],Ot[2]=t[i*3+2],Mt(Ot,e,Ot),o[i*3]=Ot[0],o[i*3+1]=Ot[1],o[i*3+2]=Ot[2];return o}var zi=class extends Rt{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=Pa(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(Ra,e.positionLin,e.positionAng,s.rA),ht(Oa,t.positionLin,t.positionAng,s.rB),ie(s.C0,this.basis,X(oi,Ra,Oa)),s.C0[0]+=ga;for(let a=0;a<3;a++)s.lambda[a]=s.lambda[a]*i.alpha*i.gamma,s.penalty[a]=Oe(s.penalty[a]*i.gamma,or,pt)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,s=this.basis;ue(Ma,i.positionAng,e.rA),ue(Ia,r.positionAng,e.rB),ha(Gr,s),ja(io,Ma,s),ja(ro,Ia,Gr),je(Fr,e.penalty[0],e.penalty[1],e.penalty[2]),Ce(pe,e.C0,1-t),N(pe,pe,ie(oi,s,La),1),N(pe,pe,ie(oi,Gr,Ea),1),N(pe,pe,ie(oi,io,_a),1),N(pe,pe,ie(oi,ro,za),1),ie(Ae,Fr,pe),N(Ae,Ae,e.lambda,1),Ae[0]=tt(Ae[0],0),ar=Math.abs(Ae[0])*this.friction,Ei=Math.sqrt(Ae[1]*Ae[1]+Ae[2]*Ae[2]),Ei>ar&&Ei>0&&(Ae[1]*=ar/Ei,Ae[2]*=ar/Ei)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;X(La,e.positionLin,e.initialLin),It(_a,e.positionAng,e.initialAng),X(Ea,t.positionLin,t.initialLin),It(za,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:Gr,n=r?io:ro;ti(so,a),ti(oo,n),Te(ao,oo,Fr),Se(i.lhsLin,Te(Ur,Te(Ur,so,Fr),a)),Se(i.lhsAng,Te(Ur,ao,n)),Se(i.lhsCross,Te(Ur,ao,a)),N(i.rhsLin,i.rhsLin,ie(oi,so,Ae),1),N(i.rhsAng,i.rhsAng,ie(oi,oo,Ae),1)}}updateDual(e){let{solver:t}=this;this.computeDisplacements();for(let i of this.contacts)this.evaluate(i,e),i.lambda.set(Ae),Ae[0]<0&&(i.penalty[0]=tt(i.penalty[0]+t.betaLin*Math.abs(pe[0]),pt)),Ei<=ar&&(i.penalty[1]=tt(i.penalty[1]+t.betaLin*Math.abs(pe[1]),pt),i.penalty[2]=tt(i.penalty[2]+t.betaLin*Math.abs(pe[2]),pt),i.stick=Math.sqrt(pe[1]*pe[1]+pe[2]*pe[2])<ya)}};var no=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),jt=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,no())}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;_i(s,l.positionAng,X(s,e,l.positionLin)),_i(a,l.positionAng,t);let c=0,f=1/0,u=!0;for(let p=0;p<3&&u;p++){let m=l.size[p]*.5;if(Math.abs(a[p])<1e-6){(s[p]<-m||s[p]>m)&&(u=!1);continue}let h=1/a[p],b=(-m-s[p])*h,y=(m-s[p])*h;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:N(L(),s,a,d),t:d})}return r}sys=va();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 zi(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];N(l.inertialLin,l.positionLin,l.velocityLin,e),l.mass>0&&(l.inertialLin[2]+=t*(e*e)),Ir(l.inertialAng,l.positionAng,Ta(l.velocityAng,e)),ei(this.accel,X(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*ua(t),f=Oe(c/Math.abs(t),0,1);Number.isFinite(f)||(f=0),l.initialLin.set(l.positionLin),l.initialAng.set(l.positionAng),l.mass>0&&(N(l.positionLin,l.positionLin,l.velocityLin,e),l.positionLin[2]+=t*(f*e*e),Ir(l.positionAng,l.positionAng,Ta(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;je(r.lhsLin,f/(e*e),f/(e*e),f/(e*e)),je(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,It(s,c.positionAng,c.inertialAng));let d=c.forces;for(let p=d.length-1;p>=0;p--)d[p].updatePrimal(c,this.alpha,r);ba(r.lhsLin,r.lhsAng,r.lhsCross,he(r.rhsLin,r.rhsLin),he(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),N(c.positionLin,c.positionLin,this.dxLin,1),Ir(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&&(ei(n.velocityLin,X(n.velocityLin,n.positionLin,n.initialLin),e),ei(n.velocityAng,It(n.velocityAng,n.positionAng,n.initialAng),e))}},Ll=L(),Ta=(o,e)=>Ce(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],ai=(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]],Ge=o=>Math.sqrt(nt(o,o));function nr(o){let e=Da(o);return!e||e.vertices.length/3<=32?e:Da(_l(e,32))}function _l(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)=>{Ge(oe(u,o.center))>Ge(oe(n[l],o.center))&&(l=d)});let c=[l],f=n.map(u=>Ge(oe(u,n[l])));for(;c.length<e;){let u=0;f.forEach((d,p)=>{d>f[u]&&(u=p)}),c.push(u),n.forEach((d,p)=>{f[p]=Math.min(f[p],Ge(oe(d,n[u])))})}return c.flatMap(u=>n[u])}function Da(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 A=[o[g],o[g+1],o[g+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 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 A=Ge(oe(r[g],r[a]));A>l&&(l=A,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 A=Ge(ai(f,oe(r[g],r[a])));A>l&&(l=A,c=g)}if(c<0||l<s*Ge(f))return null;let u=ai(f,oe(r[c],r[a])),d=-1;l=0;for(let g=0;g<r.length;g++){let A=Math.abs(nt(u,oe(r[g],r[a])));A>l&&(l=A,d=g)}if(d<0||l<s*Ge(u))return null;let p=[],m=(g,A,x)=>{let B=ai(oe(r[A],r[g]),oe(r[x],r[g])),E=Ge(B)||1,k=[B[0]/E,B[1]/E,B[2]/E];return{v:[g,A,x],n:k,d:nt(k,r[g])}},b=nt(u,oe(r[d],r[a]))>0?[a,c,n]:[a,n,c];p.push(m(b[0],b[1],b[2])),p.push(m(b[0],d,b[1])),p.push(m(b[1],d,b[2])),p.push(m(b[2],d,b[0]));let y=new Set([a,n,c,d]),v=r.map((g,A)=>A).filter(g=>!y.has(g)),w=[a,n,c,d].reduce((g,A)=>[g[0]+r[A][0]/4,g[1]+r[A][1]/4,g[2]+r[A][2]/4],[0,0,0]);v.sort((g,A)=>Ge(oe(r[A],w))-Ge(oe(r[g],w)));for(let g of v){let A=p.filter(P=>nt(P.n,r[g])-P.d>s);if(!A.length)continue;let x=new Map;for(let P of p)for(let _=0;_<3;_++)x.set(`${P.v[_]},${P.v[(_+1)%3]}`,P);let B=new Set(A),E=[...A];for(;E.length;){let P=E.pop();for(let _=0;_<3;_++){let C=x.get(`${P.v[(_+1)%3]},${P.v[_]}`);C&&!B.has(C)&&nt(C.n,r[g])-C.d>-s&&(B.add(C),E.push(C))}}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 C=P.v[_],S=P.v[(_+1)%3];z.has(`${S},${C}`)||M.push([C,S])}for(let P of k)p.splice(p.indexOf(P),1);for(let[P,_]of M)p.push(m(P,_,g))}return El(r.flat(),p.flatMap(g=>g.v))}function El(o,e){let t=[];for(let C=0;C+2<o.length;C+=3)t.push([o[C],o[C+1],o[C+2]]);let i=0;for(let C of t)i=Math.max(i,Math.abs(C[0]),Math.abs(C[1]),Math.abs(C[2]));let r=Math.max(i,1e-9)*1e-5,s=[];for(let C=0;C+2<e.length;C+=3){let S=[e[C],e[C+1],e[C+2]],R=ai(oe(t[S[1]],t[S[0]]),oe(t[S[2]],t[S[0]])),I=Ge(R);if(!(I>0))continue;let O=[R[0]/I,R[1]/I,R[2]/I];s.push({v:S,n:O,d:nt(O,t[S[0]])})}let a=new Map;s.forEach((C,S)=>{for(let R=0;R<3;R++)a.set(`${C.v[R]},${C.v[(R+1)%3]}`,S)});let n=new Array(s.length).fill(-1),l=[];s.forEach((C,S)=>{if(n[S]>=0)return;let R=[S];n[S]=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(C.n,j.n)>1-1e-5&&j.v.every(F=>Math.abs(nt(C.n,t[F])-C.d)<=r)&&(n[G]=l.length,R.push(G))}}l.push(R)});let c=[];for(let C of l){let S=new Set;for(let j of C)for(let F=0;F<3;F++)S.add(`${s[j].v[F]},${s[j].v[(F+1)%3]}`);let R=new Map;for(let j of C)for(let F=0;F<3;F++){let H=s[j].v[F],ee=s[j].v[(F+1)%3];if(!S.has(`${ee},${H}`)){if(R.has(H))return null;R.set(H,ee)}}let I=[0,0,0];for(let j of C){let F=s[j],H=ai(oe(t[F.v[1]],t[F.v[0]]),oe(t[F.v[2]],t[F.v[0]]));I[0]+=H[0],I[1]+=H[1],I[2]+=H[2]}let O=Ge(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 C of c)for(let S of C.verts)f.has(S)||(f.set(S,u.length),u.push(t[S]));for(let C of c)C.verts=C.verts.map(S=>f.get(S));let d=new Set,p=0;for(let C of c)C.verts.forEach((S,R)=>{d.add(`${S},${C.verts[(R+1)%C.verts.length]}`),p++});if(d.size!==p||![...d].every(C=>{let[S,R]=C.split(",");return d.has(`${R},${S}`)})||u.length-d.size/2+c.length!==2)return null;let m=0,h=[0,0,0],b=[0,0,0,0,0,0];for(let C of c){let S=u[C.verts[0]];for(let R=1;R+1<C.verts.length;R++){let I=u[C.verts[R]],O=u[C.verts[R+1]],q=nt(S,ai(I,O));m+=q/6;for(let j=0;j<3;j++)h[j]+=q/24*(S[j]+I[j]+O[j]);let G=(j,F)=>q/120*(S[j]*S[F]+I[j]*I[F]+O[j]*O[F]+(S[j]+I[j]+O[j])*(S[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 C=0;C<3;C++)h[C]/=m;b[0]-=m*h[0]*h[0],b[1]-=m*h[1]*h[1],b[2]-=m*h[2]*h[2],b[3]-=m*h[0]*h[1],b[4]-=m*h[0]*h[2],b[5]-=m*h[1]*h[2];let y=b[0]+b[1]+b[2],v=[[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}=zl(v),A=g,x=C=>{let S=oe(C,h);return[A[0][0]*S[0]+A[1][0]*S[1]+A[2][0]*S[2],A[0][1]*S[0]+A[1][1]*S[1]+A[2][1]*S[2],A[0][2]*S[0]+A[1][2]*S[1]+A[2][2]*S[2]]},B=u.map(x),E=new Float32Array(B.flat()),k=c.map(C=>{let S=C.n,R=[A[0][0]*S[0]+A[1][0]*S[1]+A[2][0]*S[2],A[0][1]*S[0]+A[1][1]*S[1]+A[2][1]*S[2],A[0][2]*S[0]+A[1][2]*S[1]+A[2][2]*S[2]],I=-1/0;for(let O of C.verts)I=Math.max(I,nt(R,B[O]));return{normal:R,d:I,verts:C.verts}}),z=new Map;k.forEach((C,S)=>C.verts.forEach((R,I)=>z.set(`${R},${C.verts[(I+1)%C.verts.length]}`,S)));let M=[];k.forEach((C,S)=>C.verts.forEach((R,I)=>{let O=C.verts[(I+1)%C.verts.length],q=z.get(`${O},${R}`);R<O&&q!==void 0&&M.push([R,O,S,q])}));let P=[0,0,0],_=0;for(let C of B){for(let S=0;S<3;S++)P[S]=Math.max(P[S],2*Math.abs(C[S]));_=Math.max(_,Ge(C))}return{vertices:E,faces:k,edges:M,volume:m,moments:[w[0],w[1],w[2]],size:P,radius:_,center:h,rotation:Ml(A)}}function zl(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)),p=1/Math.sqrt(d*d+1),m=d*p;for(let h=0;h<3;h++){let b=e[h][c],y=e[h][f];e[h][c]=p*b-m*y,e[h][f]=m*b+p*y}for(let h=0;h<3;h++){let b=e[c][h],y=e[f][h];e[c][h]=p*b-m*y,e[f][h]=m*b+p*y}for(let h=0;h<3;h++){let b=t[h][c],y=t[h][f];t[h][c]=p*b-m*y,t[h][f]=m*b+p*y}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],s=ai(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 Ml(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 Fa=new WeakSet,lr=o=>Fa.has(o),Il=new WeakSet;var Ua=o=>Il.has(o);function Mi(o,e,t,i,r,s=[0,0,0]){let a=new De(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,Fa.add(a),a}var Na=new WeakMap,Nr=o=>Na.get(o);function qr(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 De(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,Na.set(n,e),n}var lo=`
|
|
1
|
+
var globalThis;(globalThis||={}).AVBD=(()=>{var Cn=Object.defineProperty;var Iu=Object.getOwnPropertyDescriptor;var Ru=Object.getOwnPropertyNames;var Ou=Object.prototype.hasOwnProperty;var Tu=(s,e)=>{for(var t in e)Cn(s,t,{get:e[t],enumerable:!0})},ju=(s,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ru(e))!Ou.call(s,r)&&r!==t&&Cn(s,r,{get:()=>e[r],enumerable:!(i=Iu(e,r))||i.enumerable});return s};var Du=s=>ju(Cn({},"__esModule",{value:!0}),s);var Od={};Tu(Od,{AvbdPhysics:()=>ps,AvbdPhysicsAggregate:()=>ms,AvbdPhysicsBody:()=>Rr,AvbdPhysicsConstraint:()=>Pr,AvbdPhysicsHinge:()=>Lr,AvbdScene2D:()=>xn,AvbdScene3D:()=>vn,AvbdShapeType:()=>rt,GPUBufferRecycler:()=>Hr,GpuContactSample:()=>wn,GpuSurfaceParticles:()=>An,GpuTimer:()=>Wr,INPUT_WORDS:()=>Pu,PARTICLE_BYTE_STRIDE:()=>Ls,WorkerCanvas:()=>Bn,addGpuStepPass:()=>ku,cookHeightfield:()=>Mr,cookStaticMesh:()=>zr,createSharedInput:()=>Lu,createSolverPipeline:()=>bu,createWebGPUDevice:()=>Sr,prepareWebGPUDevice3D:()=>Ni,readSharedInput:()=>Eu,sampledContactShader:()=>Cu,seedConstraintColoring:()=>Su,writeSharedInput:()=>_u});var S=(s=0,e=0,t=0)=>Float64Array.of(s,e,t),jt=(s=0,e=0,t=0,i=1)=>Float64Array.of(s,e,t,i),oe=()=>new Float64Array(9);var Es=s=>s<0?-1:s>0?1:0,Bt=(s,e)=>s<e?s:e,Gu=(s,e)=>s>e?s:e,ce=(s,e,t)=>Gu(e,Bt(t,s)),V=(s,e)=>s[0]*e[0]+s[1]*e[1]+s[2]*e[2],Ne=s=>V(s,s),sr=s=>Math.sqrt(Ne(s));function ct(s,e,t,i){return s[0]=e,s[1]=t,s[2]=i,s}var Li=(s,e,t)=>ct(s,e[0]+t[0],e[1]+t[1],e[2]+t[2]),J=(s,e,t)=>ct(s,e[0]-t[0],e[1]-t[1],e[2]-t[2]),ne=(s,e,t)=>ct(s,e[0]*t,e[1]*t,e[2]*t),_t=(s,e,t)=>ct(s,e[0]/t,e[1]/t,e[2]/t),ee=(s,e)=>ct(s,-e[0],-e[1],-e[2]),Sn=(s,e)=>ct(s,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),D=(s,e,t,i)=>ct(s,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),Fu=(s,e)=>_t(s,e,sr(e)),Dt=(s,e,t)=>ct(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 rr(s,e,t,i,r){return s[0]=e,s[1]=t,s[2]=i,s[3]=r,s}function $r(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 rr(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 Uu=(s,e)=>rr(s,-e[0],-e[1],-e[2],e[3]),Ya=s=>Ne(s)+s[3]*s[3];function kn(s,e){let t=Ya(e);return rr(s,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function Nu(s,e){let t=Math.sqrt(Ya(e));return rr(s,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var ir=new Float64Array(4),Pi=new Float64Array(4);function ut(s,e,t){return $r(ir,e,kn(ir,t)),ct(s,ir[0]*2,ir[1]*2,ir[2]*2)}function si(s,e,t){return rr(Pi,t[0],t[1],t[2],0),$r(Pi,Pi,e),Nu(s,rr(s,e[0]+Pi[0]*.5,e[1]+Pi[1]*.5,e[2]+Pi[2]*.5,e[3]+Pi[3]*.5))}function ae(s,e,t){let i=e[0],r=e[1],o=e[2],n=e[3],a=(r*t[2]-o*t[1])*2,l=(o*t[0]-i*t[2])*2,c=(i*t[1]-r*t[0])*2;return ct(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 Xa=new Float64Array(3);function Gt(s,e,t,i){return ae(Xa,t,i),Li(s,Xa,e)}function Te(s,e,t){return ae(s,Uu(ir,e),t)}function Ce(s,e,t,i){return s.fill(0),s[0]=e,s[4]=t,s[8]=i,s}function Pn(s,e){return s[0]=0,s[1]=-e[2],s[2]=e[1],s[3]=e[2],s[4]=0,s[5]=-e[0],s[6]=-e[1],s[7]=e[0],s[8]=0,s}function _i(s,e){let t=e[1],i=e[2],r=e[5];return s[0]=e[0],s[1]=e[3],s[2]=e[6],s[3]=t,s[4]=e[4],s[5]=e[7],s[6]=i,s[7]=r,s[8]=e[8],s}function Ke(s,e,t){let i=e[0],r=e[1],o=e[2],n=e[3],a=e[4],l=e[5],c=e[6],u=e[7],f=e[8],d=t[0],p=t[1],m=t[2],h=t[3],g=t[4],b=t[5],y=t[6],A=t[7],v=t[8];return s[0]=i*d+r*h+o*y,s[1]=i*p+r*g+o*A,s[2]=i*m+r*b+o*v,s[3]=n*d+a*h+l*y,s[4]=n*p+a*g+l*A,s[5]=n*m+a*b+l*v,s[6]=c*d+u*h+f*y,s[7]=c*p+u*g+f*A,s[8]=c*m+u*b+f*v,s}function ue(s,e,t){let i=t[0],r=t[1],o=t[2];return ct(s,e[0]*i+e[1]*r+e[2]*o,e[3]*i+e[4]*r+e[5]*o,e[6]*i+e[7]*r+e[8]*o)}function qe(s,e){for(let t=0;t<9;t++)s[t]+=e[t];return s}function Ei(s,e,t){for(let i=0;i<9;i++)s[i]=e[i]*t;return s}function Qa(s,e){for(let t=0;t<9;t++)s[t]=-e[t];return s}function Ms(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 Ka(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 zs(s,e){let t=Math.abs(e[0])>Math.abs(e[2])?S(-e[1],e[0],0):S(0,-e[2],e[1]);Fu(t,t);let i=Dt(S(),e,t);return s[0]=e[0],s[1]=e[1],s[2]=e[2],s.set(t,3),s.set(i,6),s}function Is(s,e,t,i,r,o,n){let a=s[0],l=s[3],c=s[4],u=s[6],f=s[7],d=s[8],p=t[0],m=t[1],h=t[2],g=e[0],b=t[3],y=t[4],A=t[5],v=e[3],x=e[4],w=t[6],B=t[7],L=t[8],M=e[6],I=e[7],E=e[8],k=l/a,P=u/a,C=p/a,_=b/a,z=w/a,R=a,G=c-k*k*R,X=(f-k*P*R)/G,W=(m-k*C*R)/G,U=(y-k*_*R)/G,H=(B-k*z*R)/G,Z=d-(P*P*R+X*X*G),he=(h-P*C*R-X*W*G)/Z,we=(A-P*_*R-X*U*G)/Z,Oe=(L-P*z*R-X*H*G)/Z,$=g-(C*C*R+W*W*G+he*he*Z),Je=(v-C*_*R-W*U*G-he*we*Z)/$,Xe=(M-C*z*R-W*H*G-he*Oe*Z)/$,Be=x-(_*_*R+U*U*G+we*we*Z+Je*Je*$),nt=(I-_*z*R-U*H*G-we*Oe*Z-Je*Xe*$)/Be,ri=E-(z*z*R+H*H*G+Oe*Oe*Z+Xe*Xe*$+nt*nt*Be),Ye=i[0],at=i[1]-k*Ye,Qe=i[2]-P*Ye-X*at,lt=r[0]-C*Ye-W*at-he*Qe,wt=r[1]-_*Ye-U*at-we*Qe-Je*lt,Ki=r[2]-z*Ye-H*at-Oe*Qe-Xe*lt-nt*wt,Zi=Ye/R,er=at/G,tr=Qe/Z,Wt=lt/$,Mu=wt/Be,zu=Ki/ri;n[2]=zu,n[1]=Mu-nt*n[2],n[0]=Wt-Je*n[1]-Xe*n[2],o[2]=tr-he*n[0]-we*n[1]-Oe*n[2],o[1]=er-X*o[2]-W*n[0]-U*n[1]-H*n[2],o[0]=Zi-k*o[1]-P*o[2]-C*n[0]-_*n[1]-z*n[2]}var Et=1,Ve=1e10,Rs=.01,Za=1e-5,Ze=class{solver;forces=[];positionLin;positionAng=jt();initialLin=S();initialAng=jt();inertialLin=S();inertialAng=jt();velocityLin;velocityAng=S();prevVelocityLin;size;mass;moment;friction;radius;constructor(e,t,i,r,o,n=[0,0,0]){this.solver=e,this.size=S(t[0],t[1],t[2]),this.friction=r,this.positionLin=S(o[0],o[1],o[2]),this.velocityLin=S(n[0],n[1],n[2]),this.prevVelocityLin=S(n[0],n[1],n[2]);let[a,l,c]=this.size;this.mass=a*l*c*i,this.moment=S((l*l+c*c)/12*this.mass,(a*a+c*c)/12*this.mass,(a*a+l*l)/12*this.mass),this.radius=sr([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}},Os=()=>({lhsLin:oe(),lhsAng:oe(),lhsCross:oe(),rhsLin:S(),rhsAng:S()}),Ft=class{solver;bodyA;bodyB;constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}};var sl=8,el=16,zi=1e-6,_n=1e-5,qu=1e-6,En=34028234663852886e22,Ts=0,ol=1,js=2;function tl(s){let e=s.positionAng;return{center:s.positionLin,half:ne(S(),s.size,.5),axis:[ae(S(),e,[1,0,0]),ae(S(),e,[0,1,0]),ae(S(),e,[0,0,1])]}}var Mi=(s,e)=>Math.abs(V(s,e));function Ds(s,e){let t=V(e,s.axis[0])>=0?1:-1,i=V(e,s.axis[1])>=0?1:-1,r=V(e,s.axis[2])>=0?1:-1,o=D(S(),s.center,s.axis[0],s.half[0]*t);return D(o,o,s.axis[1],s.half[1]*i),D(o,o,s.axis[2],s.half[2]*r)}function nl(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 Vu(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]);return{normal:r,center:o,...nl(s,e)}}function Wu(s,e){let t=0,i=-En;for(let r=0;r<3;r++){let o=Mi(s.axis[r],e);o>i&&(i=o,t=r)}return t}function Hu(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]),{u:n,v:a,extentU:l,extentV:c}=nl(s,e),u=(f,d)=>{let p=D(S(),o,n,f*l);return D(p,p,a,d*c)};return[u(1,1),u(-1,1),u(-1,-1),u(1,-1)]}function $u(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<=_n,c=a<=_n;if(l!==c){let u=0,f=o-a;Math.abs(f)>zi&&(u=ce(o/f,0,1)),i.length<el&&i.push(D(S(),r,J(S(),n,r),u))}c&&i.length<el&&i.push(n),r=n,o=a}return i}var Gs=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(S(),Li(S(),e,t),.5);for(let a of this.midpoints)if(Ne(J(S(),r,a))<qu)return;if(this.contacts.length>=sl)return;let{bodyA:o,bodyB:n}=this;this.contacts.push({feature:i,rA:Te(S(),o.positionAng,J(S(),e,o.positionLin)),rB:Te(S(),n.positionAng,J(S(),t,n.positionLin))}),this.midpoints.push(r)}};function Ln(s,e,t,i,r,o,n,a){let l=Ne(i);if(l<zi)return!0;let c=1/Math.sqrt(l),u=ne(S(),i,c);V(u,t)<0&&ee(u,u);let f=Math.abs(V(t,u)),d=s.half[0]*Mi(u,s.axis[0])+s.half[1]*Mi(u,s.axis[1])+s.half[2]*Mi(u,s.axis[2]),p=e.half[0]*Mi(u,e.axis[0])+e.half[1]*Mi(u,e.axis[1])+e.half[2]*Mi(u,e.axis[2]),m=f-(d+p);return m>0?!1:((!a.valid||m>a.separation)&&(a.valid=!0,a.type=r,a.indexA=o,a.indexB=n,a.separation=m,a.normalAB=u),!0)}function il(s,e,t){let i=(e+1)%3,r=(e+2)%3,o=V(t,s.axis[i])>=0?1:-1,n=V(t,s.axis[r])>=0?1:-1,a=D(S(),s.center,s.axis[i],s.half[i]*o);D(a,a,s.axis[r],s.half[r]*n);let l=ne(S(),s.axis[e],s.half[e]);return[J(S(),a,l),Li(S(),a,l)]}function Ju(s,e,t,i){let r=J(S(),e,s),o=J(S(),i,t),n=J(S(),s,t),a=V(r,r),l=V(o,o),c=V(o,n),u=0,f=0;if(a<=zi&&l<=zi)return[s,t];if(a<=zi)f=ce(c/l,0,1);else{let d=V(r,n);if(l<=zi)u=ce(-d/a,0,1);else{let p=V(r,o),m=a*l-p*p;Math.abs(m)>zi&&(u=ce((p*c-d*l)/m,0,1)),f=(p*u+c)/l,f<0?(f=0,u=ce(-d/a,0,1)):f>1&&(f=1,u=ce((p-d)/a,0,1))}}return[D(S(),s,r,u),D(S(),t,o,f)]}function rl(s,e,t,i,r,o,n){let a=r?t:i,l=r?i:t,c=r?n:ee(S(),n),u=Vu(a,o,c),f=Wu(l,u.normal),d=Hu(l,f,u.normal),p=[[u.u,u.extentU],[ee(S(),u.u),u.extentU],[u.v,u.extentV],[ee(S(),u.v),u.extentV]];for(let[g,b]of p)if(d=$u(d,g,V(g,u.center)+b),!d.length)return[];let m=new Gs(s,e),h=(r?Ts:ol)<<24|(o&255)<<16|(f&255)<<8;for(let g=0;g<d.length&&m.contacts.length<sl;g++){let b=d[g],y=V(J(S(),b,u.center),u.normal);if(y>_n)continue;let A=D(S(),b,u.normal,-y),v=r?A:b,x=r?b:A;m.add(v,x,h|g&255)}return m.contacts.length||m.add(Ds(t,n),Ds(i,ee(S(),n)),h),m.contacts}function Xu(s,e,t,i,r,o,n){let[a,l]=il(t,r,n),[c,u]=il(i,o,ee(S(),n)),[f,d]=Ju(a,l,c,u),p=new Gs(s,e),m=js<<24|(r&255)<<8|o&255;return p.add(f,d,m),p.contacts.length||p.add(Ds(t,n),Ds(i,ee(S(),n)),m),p.contacts}function al(s,e,t){let i=tl(s),r=tl(e),o=J(S(),r.center,i.center),n={type:Ts,indexA:0,indexB:0,separation:-En,normalAB:S(),valid:!1},a={type:js,indexA:0,indexB:0,separation:-En,normalAB:S(),valid:!1};for(let u=0;u<3;u++)if(!Ln(i,r,o,i.axis[u],Ts,u,-1,n))return[];for(let u=0;u<3;u++)if(!Ln(i,r,o,r.axis[u],ol,-1,u,n))return[];let l=S();for(let u=0;u<3;u++)for(let f=0;f<3;f++)if(Dt(l,i.axis[u],r.axis[f]),!Ln(i,r,o,l,js,u,f,a))return[];if(!n.valid)return[];let c=n;return a.valid&&.95*a.separation>n.separation+.01&&(c=a),zs(t,ee(S(),c.normalAB)),c.type===js?Xu(s,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===Ts?rl(s,e,i,r,!0,c.indexA,c.normalAB):rl(s,e,i,r,!1,c.indexB,c.normalAB)}var ll=S(),cl=S(),ul=S(),fl=S(),dl=S(),hl=S(),Fs=oe(),Mn=oe(),zn=oe(),Us=oe(),In=oe(),Rn=oe(),On=oe(),Ns=oe(),Me=S(),je=S(),Ii=S(),oi=S(),pl=S(),ml=S(),or=0,Jr=0;function gl(s,e,t){for(let i=0;i<3;i++)oi[0]=t[i*3],oi[1]=t[i*3+1],oi[2]=t[i*3+2],Dt(oi,e,oi),s[i*3]=oi[0],s[i*3+1]=oi[1],s[i*3+2]=oi[2];return s}var ni=class extends Ft{contacts=[];basis=oe();friction=0;constructor(e,t,i){super(e,t,i)}get numContacts(){return this.contacts.length}initialize(){let e=this.bodyA,t=this.bodyB,{solver:i}=this;this.friction=Math.sqrt(e.friction*t.friction);let r=this.contacts;this.contacts=al(e,t,this.basis).map(o=>{let n=r.find(a=>a.feature===o.feature);return n?{feature:o.feature,rA:n.stick?n.rA:o.rA,rB:n.stick?n.rB:o.rB,C0:S(),penalty:n.penalty.slice(),lambda:n.lambda.slice(),stick:n.stick}:{feature:o.feature,rA:o.rA,rB:o.rB,C0:S(),penalty:S(),lambda:S(),stick:!1}});for(let o of this.contacts){Gt(pl,e.positionLin,e.positionAng,o.rA),Gt(ml,t.positionLin,t.positionAng,o.rB),ue(o.C0,this.basis,J(Ii,pl,ml)),o.C0[0]+=Rs;for(let n=0;n<3;n++)o.lambda[n]=o.lambda[n]*i.alpha*i.gamma,o.penalty[n]=ce(o.penalty[n]*i.gamma,Et,Ve)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,o=this.basis;ae(dl,i.positionAng,e.rA),ae(hl,r.positionAng,e.rB),Qa(Fs,o),gl(Mn,dl,o),gl(zn,hl,Fs),Ce(Us,e.penalty[0],e.penalty[1],e.penalty[2]),ne(Me,e.C0,1-t),D(Me,Me,ue(Ii,o,ll),1),D(Me,Me,ue(Ii,Fs,ul),1),D(Me,Me,ue(Ii,Mn,cl),1),D(Me,Me,ue(Ii,zn,fl),1),ue(je,Us,Me),D(je,je,e.lambda,1),je[0]=Bt(je[0],0),Jr=Math.abs(je[0])*this.friction,or=Math.sqrt(je[1]*je[1]+je[2]*je[2]),or>Jr&&or>0&&(je[1]*=Jr/or,je[2]*=Jr/or)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;J(ll,e.positionLin,e.initialLin),ut(cl,e.positionAng,e.initialAng),J(ul,t.positionLin,t.initialLin),ut(fl,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:Fs,a=r?Mn:zn;_i(In,n),_i(Rn,a),Ke(On,Rn,Us),qe(i.lhsLin,Ke(Ns,Ke(Ns,In,Us),n)),qe(i.lhsAng,Ke(Ns,On,a)),qe(i.lhsCross,Ke(Ns,On,n)),D(i.rhsLin,i.rhsLin,ue(Ii,In,je),1),D(i.rhsAng,i.rhsAng,ue(Ii,Rn,je),1)}}updateDual(e){let{solver:t}=this;this.computeDisplacements();for(let i of this.contacts)this.evaluate(i,e),i.lambda.set(je),je[0]<0&&(i.penalty[0]=Bt(i.penalty[0]+t.betaLin*Math.abs(Me[0]),Ve)),or<=Jr&&(i.penalty[1]=Bt(i.penalty[1]+t.betaLin*Math.abs(Me[1]),Ve),i.penalty[2]=Bt(i.penalty[2]+t.betaLin*Math.abs(Me[2]),Ve),i.stick=Math.sqrt(Me[1]*Me[1]+Me[2]*Me[2])<Za)}};var Tn=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),ai=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,Tn())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=S(),n=S();for(let a=this.bodies.length-1;a>=0;a--){let l=this.bodies[a];if(l.mass<=0)continue;Te(o,l.positionAng,J(o,e,l.positionLin)),Te(n,l.positionAng,t);let c=0,u=1/0,f=!0;for(let p=0;p<3&&f;p++){let m=l.size[p]*.5;if(Math.abs(n[p])<1e-6){(o[p]<-m||o[p]>m)&&(f=!1);continue}let h=1/n[p],g=(-m-o[p])*h,b=(m-o[p])*h;g>b&&([g,b]=[b,g]),c=Math.max(c,g),u=Math.min(u,b),c>u&&(f=!1)}if(!f)continue;let d=c>=0?c:u;d<0||(!r||d<r.t)&&(r={body:l,local:D(S(),o,n,d),t:d})}return r}sys=Os();dxLin=S();dxAng=S();dp=S();accel=S();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:o}=this;for(let a=i.length-1;a>=0;a--){let l=i[a];for(let c=a-1;c>=0;c--){let u=i[c];J(o,l.positionLin,u.positionLin);let f=l.radius+u.radius;V(o,o)<=f*f&&!l.constrainedTo(u)&&new ni(this,l,u)}}let n=[];for(let a of this.forces)a.initialize()?n.push(a):a.unlinkFromBodies();this.forces=n;for(let a=i.length-1;a>=0;a--){let l=i[a];D(l.inertialLin,l.positionLin,l.velocityLin,e),l.mass>0&&(l.inertialLin[2]+=t*(e*e)),si(l.inertialAng,l.positionAng,bl(l.velocityAng,e)),_t(this.accel,J(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*Es(t),u=ce(c/Math.abs(t),0,1);Number.isFinite(u)||(u=0),l.initialLin.set(l.positionLin),l.initialAng.set(l.positionAng),l.mass>0&&(D(l.positionLin,l.positionLin,l.velocityLin,e),l.positionLin[2]+=t*(u*e*e),si(l.positionAng,l.positionAng,bl(l.velocityAng,e)))}for(let a=0;a<this.iterations;a++){for(let l=i.length-1;l>=0;l--){let c=i[l];if(c.mass<=0)continue;let u=c.mass,f=c.moment;Ce(r.lhsLin,u/(e*e),u/(e*e),u/(e*e)),Ce(r.lhsAng,f[0]/(e*e),f[1]/(e*e),f[2]/(e*e)),r.lhsCross.fill(0),ue(r.rhsLin,r.lhsLin,J(o,c.positionLin,c.inertialLin)),ue(r.rhsAng,r.lhsAng,ut(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);Is(r.lhsLin,r.lhsAng,r.lhsCross,ee(r.rhsLin,r.rhsLin),ee(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),D(c.positionLin,c.positionLin,this.dxLin,1),si(c.positionAng,c.positionAng,this.dxAng)}for(let l of this.forces)l.updateDual(this.alpha)}for(let a of i)a.prevVelocityLin.set(a.velocityLin),a.mass>0&&(_t(a.velocityLin,J(a.velocityLin,a.positionLin,a.initialLin),e),_t(a.velocityAng,ut(a.velocityAng,a.positionAng,a.initialAng),e))}},Yu=S(),bl=(s,e)=>ne(Yu,s,e);var xe=(s,e)=>[s[0]-e[0],s[1]-e[1],s[2]-e[2]],Mt=(s,e)=>s[0]*e[0]+s[1]*e[1]+s[2]*e[2],Ri=(s,e)=>[s[1]*e[2]-s[2]*e[1],s[2]*e[0]-s[0]*e[2],s[0]*e[1]-s[1]*e[0]],et=s=>Math.sqrt(Mt(s,s));function Xr(s){let e=jn(s);return!e||e.vertices.length/3<=32?e:jn(yl(e,32))}function yl(s,e){let[t,i,r,o]=s.rotation,n=f=>{let d=[2*(i*f[2]-r*f[1]),2*(r*f[0]-t*f[2]),2*(t*f[1]-i*f[0])];return[f[0]+o*d[0]+(i*d[2]-r*d[1])+s.center[0],f[1]+o*d[1]+(r*d[0]-t*d[2])+s.center[1],f[2]+o*d[2]+(t*d[1]-i*d[0])+s.center[2]]},a=[];for(let f=0;f<s.vertices.length;f+=3)a.push(n([s.vertices[f],s.vertices[f+1],s.vertices[f+2]]));let l=0;a.forEach((f,d)=>{et(xe(f,s.center))>et(xe(a[l],s.center))&&(l=d)});let c=[l],u=a.map(f=>et(xe(f,a[l])));for(;c.length<e;){let f=0;u.forEach((d,p)=>{d>u[f]&&(f=p)}),c.push(f),a.forEach((d,p)=>{u[p]=Math.min(u[p],et(xe(d,a[f])))})}return c.flatMap(f=>a[f])}function jn(s){let e=0;for(let v=0;v<s.length;v++)e=Math.max(e,Math.abs(s[v]));let t=Math.max(e,1e-9)*1e-6,i=new Set,r=[];for(let v=0;v+2<s.length;v+=3){let x=[s[v],s[v+1],s[v+2]],w=x.map(B=>Math.round(B/t)).join(",");i.has(w)||(i.add(w),r.push(x))}if(r.length<4)return null;let o=Math.max(e,1e-9)*1e-10,n=0;for(let v=1;v<r.length;v++)r[v][0]<r[n][0]&&(n=v);let a=-1,l=0;for(let v=0;v<r.length;v++){let x=et(xe(r[v],r[n]));x>l&&(l=x,a=v)}if(a<0||l<o)return null;let c=-1;l=0;let u=xe(r[a],r[n]);for(let v=0;v<r.length;v++){let x=et(Ri(u,xe(r[v],r[n])));x>l&&(l=x,c=v)}if(c<0||l<o*et(u))return null;let f=Ri(u,xe(r[c],r[n])),d=-1;l=0;for(let v=0;v<r.length;v++){let x=Math.abs(Mt(f,xe(r[v],r[n])));x>l&&(l=x,d=v)}if(d<0||l<o*et(f))return null;let p=[],m=(v,x,w)=>{let B=Ri(xe(r[x],r[v]),xe(r[w],r[v])),L=et(B)||1,M=[B[0]/L,B[1]/L,B[2]/L];return{v:[v,x,w],n:M,d:Mt(M,r[v])}},g=Mt(f,xe(r[d],r[n]))>0?[n,c,a]:[n,a,c];p.push(m(g[0],g[1],g[2])),p.push(m(g[0],d,g[1])),p.push(m(g[1],d,g[2])),p.push(m(g[2],d,g[0]));let b=new Set([n,a,c,d]),y=r.map((v,x)=>x).filter(v=>!b.has(v)),A=[n,a,c,d].reduce((v,x)=>[v[0]+r[x][0]/4,v[1]+r[x][1]/4,v[2]+r[x][2]/4],[0,0,0]);y.sort((v,x)=>et(xe(r[x],A))-et(xe(r[v],A)));for(let v of y){let x=p.filter(k=>Mt(k.n,r[v])-k.d>o);if(!x.length)continue;let w=new Map;for(let k of p)for(let P=0;P<3;P++)w.set(`${k.v[P]},${k.v[(P+1)%3]}`,k);let B=new Set(x),L=[...x];for(;L.length;){let k=L.pop();for(let P=0;P<3;P++){let C=w.get(`${k.v[(P+1)%3]},${k.v[P]}`);C&&!B.has(C)&&Mt(C.n,r[v])-C.d>-o&&(B.add(C),L.push(C))}}let M=[...B],I=new Set;for(let k of M)for(let P=0;P<3;P++)I.add(`${k.v[P]},${k.v[(P+1)%3]}`);let E=[];for(let k of M)for(let P=0;P<3;P++){let C=k.v[P],_=k.v[(P+1)%3];I.has(`${_},${C}`)||E.push([C,_])}for(let k of M)p.splice(p.indexOf(k),1);for(let[k,P]of E)p.push(m(k,P,v))}return vl(r.flat(),p.flatMap(v=>v.v))}function Dn(s,e){let t=vl(s,e);return t&&t.vertices.length/3>32?jn(yl(t,32)):t}function vl(s,e){let t=[];for(let C=0;C+2<s.length;C+=3)t.push([s[C],s[C+1],s[C+2]]);let i=0;for(let C of t)i=Math.max(i,Math.abs(C[0]),Math.abs(C[1]),Math.abs(C[2]));let r=Math.max(i,1e-9)*1e-5,o=[];for(let C=0;C+2<e.length;C+=3){let _=[e[C],e[C+1],e[C+2]],z=Ri(xe(t[_[1]],t[_[0]]),xe(t[_[2]],t[_[0]])),R=et(z);if(!(R>0))continue;let G=[z[0]/R,z[1]/R,z[2]/R];o.push({v:_,n:G,d:Mt(G,t[_[0]])})}let n=new Map;o.forEach((C,_)=>{for(let z=0;z<3;z++)n.set(`${C.v[z]},${C.v[(z+1)%3]}`,_)});let a=new Array(o.length).fill(-1),l=[];o.forEach((C,_)=>{if(a[_]>=0)return;let z=[_];a[_]=l.length;for(let R=0;R<z.length;R++){let G=o[z[R]];for(let X=0;X<3;X++){let W=n.get(`${G.v[(X+1)%3]},${G.v[X]}`);if(W===void 0||a[W]>=0)continue;let U=o[W];Mt(C.n,U.n)>1-1e-5&&U.v.every(H=>Math.abs(Mt(C.n,t[H])-C.d)<=r)&&(a[W]=l.length,z.push(W))}}l.push(z)});let c=[];for(let C of l){let _=new Set;for(let U of C)for(let H=0;H<3;H++)_.add(`${o[U].v[H]},${o[U].v[(H+1)%3]}`);let z=new Map;for(let U of C)for(let H=0;H<3;H++){let Z=o[U].v[H],he=o[U].v[(H+1)%3];if(!_.has(`${he},${Z}`)){if(z.has(Z))return null;z.set(Z,he)}}let R=[0,0,0];for(let U of C){let H=o[U],Z=Ri(xe(t[H.v[1]],t[H.v[0]]),xe(t[H.v[2]],t[H.v[0]]));R[0]+=Z[0],R[1]+=Z[1],R[2]+=Z[2]}let G=et(R)||1,X=z.keys().next().value,W=[X];for(let U=z.get(X);U!==X&&W.length<=z.size;U=z.get(U))W.push(U);if(W.length!==z.size)return null;c.push({n:[R[0]/G,R[1]/G,R[2]/G],verts:W})}let u=new Map,f=[];for(let C of c)for(let _ of C.verts)u.has(_)||(u.set(_,f.length),f.push(t[_]));for(let C of c)C.verts=C.verts.map(_=>u.get(_));let d=new Set,p=0;for(let C of c)C.verts.forEach((_,z)=>{d.add(`${_},${C.verts[(z+1)%C.verts.length]}`),p++});if(d.size!==p||![...d].every(C=>{let[_,z]=C.split(",");return d.has(`${z},${_}`)})||f.length-d.size/2+c.length!==2)return null;let m=0,h=[0,0,0],g=[0,0,0,0,0,0];for(let C of c){let _=f[C.verts[0]];for(let z=1;z+1<C.verts.length;z++){let R=f[C.verts[z]],G=f[C.verts[z+1]],X=Mt(_,Ri(R,G));m+=X/6;for(let U=0;U<3;U++)h[U]+=X/24*(_[U]+R[U]+G[U]);let W=(U,H)=>X/120*(_[U]*_[H]+R[U]*R[H]+G[U]*G[H]+(_[U]+R[U]+G[U])*(_[H]+R[H]+G[H]));g[0]+=W(0,0),g[1]+=W(1,1),g[2]+=W(2,2),g[3]+=W(0,1),g[4]+=W(0,2),g[5]+=W(1,2)}}if(!(m>0))return null;for(let C=0;C<3;C++)h[C]/=m;g[0]-=m*h[0]*h[0],g[1]-=m*h[1]*h[1],g[2]-=m*h[2]*h[2],g[3]-=m*h[0]*h[1],g[4]-=m*h[0]*h[2],g[5]-=m*h[1]*h[2];let b=g[0]+g[1]+g[2],y=[[b-g[0],-g[3],-g[4]],[-g[3],b-g[1],-g[5]],[-g[4],-g[5],b-g[2]]],{values:A,vectors:v}=Qu(y),x=v,w=C=>{let _=xe(C,h);return[x[0][0]*_[0]+x[1][0]*_[1]+x[2][0]*_[2],x[0][1]*_[0]+x[1][1]*_[1]+x[2][1]*_[2],x[0][2]*_[0]+x[1][2]*_[1]+x[2][2]*_[2]]},B=f.map(w),L=new Float32Array(B.flat()),M=c.map(C=>{let _=C.n,z=[x[0][0]*_[0]+x[1][0]*_[1]+x[2][0]*_[2],x[0][1]*_[0]+x[1][1]*_[1]+x[2][1]*_[2],x[0][2]*_[0]+x[1][2]*_[1]+x[2][2]*_[2]],R=-1/0;for(let G of C.verts)R=Math.max(R,Mt(z,B[G]));return{normal:z,d:R,verts:C.verts}}),I=new Map;M.forEach((C,_)=>C.verts.forEach((z,R)=>I.set(`${z},${C.verts[(R+1)%C.verts.length]}`,_)));let E=[];M.forEach((C,_)=>C.verts.forEach((z,R)=>{let G=C.verts[(R+1)%C.verts.length],X=I.get(`${G},${z}`);z<G&&X!==void 0&&E.push([z,G,_,X])}));let k=[0,0,0],P=0;for(let C of B){for(let _=0;_<3;_++)k[_]=Math.max(k[_],2*Math.abs(C[_]));P=Math.max(P,et(C))}return{vertices:L,faces:M,edges:E,volume:m,moments:[A[0],A[1],A[2]],size:k,radius:P,center:h,rotation:Ku(x)}}function Qu(s){let e=s.map(a=>[...a]),t=[[1,0,0],[0,1,0],[0,0,1]];for(let a=0;a<50&&!(e[0][1]**2+e[0][2]**2+e[1][2]**2<1e-30*(e[0][0]**2+e[1][1]**2+e[2][2]**2+1e-300));a++)for(let c=0;c<3;c++)for(let u=c+1;u<3;u++){if(Math.abs(e[c][u])<1e-300)continue;let f=(e[u][u]-e[c][c])/(2*e[c][u]),d=Math.sign(f||1)/(Math.abs(f)+Math.sqrt(f*f+1)),p=1/Math.sqrt(d*d+1),m=d*p;for(let h=0;h<3;h++){let g=e[h][c],b=e[h][u];e[h][c]=p*g-m*b,e[h][u]=m*g+p*b}for(let h=0;h<3;h++){let g=e[c][h],b=e[u][h];e[c][h]=p*g-m*b,e[u][h]=m*g+p*b}for(let h=0;h<3;h++){let g=t[h][c],b=t[h][u];t[h][c]=p*g-m*b,t[h][u]=m*g+p*b}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],o=Ri(i,r),n=[[i[0],r[0],o[0]],[i[1],r[1],o[1]],[i[2],r[2],o[2]]];return{values:[e[0][0],e[1][1],e[2][2]],vectors:n}}function Ku(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 Al=new WeakSet,De=s=>Al.has(s),Zu=new WeakSet;var nr=s=>Zu.has(s);function ar(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,Al.add(n),n}var wl=new WeakMap,Ct=s=>wl.get(s);function lr(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,wl.set(a,e),a}var Gn=`
|
|
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
|
+
`,Ut=`
|
|
42
42
|
// Shared by every 2D module (layout.ts)
|
|
43
|
-
${
|
|
43
|
+
${Gn}
|
|
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
|
+
`,Yr=(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
|
+
`,qs=Yr(Ut),Se=["collision","adjacency","coloring","solve"];var ef=`
|
|
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:ef}),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 m=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:m}),f===1)break;l=d,c=0,u=f}}encode(e){e.setPipeline(this.scanPipeline);for(let t of this.levels)e.setBindGroup(0,t.group),e.dispatchWorkgroups(t.blocks);e.setPipeline(this.addPipeline);for(let t=this.levels.length-2;t>=0;t--)e.setBindGroup(0,this.levels[t].group),e.dispatchWorkgroups(this.levels[t].blocks)}destroy(){for(let e of this.buffers)e.destroy()}};var tf=`
|
|
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
|
+
`,Ti=(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
|
+
`,Qr=Ti(Ut,tf);var rf=jt(),zt=oe(),Vs=oe(),ji=oe(),Fn=oe(),cr=oe(),Un=oe(),ft=oe(),Ws=oe(),Kr=oe(),ye=S(),ze=S(),ve=S(),li=S(),ur=S();function Nn(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 Hs=(s,e,t)=>ct(s,Bt(e[0],t),Bt(e[1],t),Bt(e[2],t)),ke=class extends Ft{rA;rB;C0Lin=S();C0Ang=S();penaltyLin=S();penaltyAng=S();lambdaLin=S();lambdaAng=S();stiffnessLin;stiffnessAng;fracture;torqueArm;broken=!1;constructor(e,t,i,r,o,n=1/0,a=0,l=1/0){super(e,t,i),this.rA=S(r[0],r[1],r[2]),this.rB=S(o[0],o[1],o[2]),this.stiffnessLin=n,this.stiffnessAng=a,this.fracture=l;let c=t?t.size:[0,0,0];this.torqueArm=Ne([c[0]+i.size[0],c[1]+i.size[1],c[2]+i.size[2]])}evaluateLin(e){let{bodyA:t,bodyB:i}=this;return t?Gt(li,t.positionLin,t.positionAng,this.rA):li.set(this.rA),J(e,li,Gt(ur,i.positionLin,i.positionAng,this.rB))}evaluateAng(e){return ut(e,this.bodyA?this.bodyA.positionAng:rf,this.bodyB.positionAng),ne(e,e,this.torqueArm)}initialize(){let{solver:e}=this;this.evaluateLin(this.C0Lin),this.evaluateAng(this.C0Ang);for(let t=0;t<3;t++)this.lambdaLin[t]=this.lambdaLin[t]*e.alpha*e.gamma,this.lambdaAng[t]=this.lambdaAng[t]*e.alpha*e.gamma,this.penaltyLin[t]=ce(this.penaltyLin[t]*e.gamma,Et,Ve),this.penaltyAng[t]=ce(this.penaltyAng[t]*e.gamma,Et,Ve);return Hs(this.penaltyLin,this.penaltyLin,this.stiffnessLin),Hs(this.penaltyAng,this.penaltyAng,this.stiffnessAng),!this.broken}updatePrimal(e,t,i){let r=e===this.bodyA;if(Ne(this.penaltyLin)>0){Ce(zt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(ye),this.stiffnessLin===1/0&&D(ye,ye,this.C0Lin,-t),ue(ze,zt,ye),D(ze,ze,this.lambdaLin,1);let o=r?1:-1;Ce(Vs,o,o,o),r?Pn(ji,ee(ve,ae(ve,this.bodyA.positionAng,this.rA))):Pn(ji,ae(ve,this.bodyB.positionAng,this.rB)),_i(Fn,Vs),_i(cr,ji),Ke(Un,cr,zt),qe(i.lhsLin,Ke(ft,Ke(ft,Fn,zt),Vs)),qe(i.lhsAng,Ke(ft,Un,ji)),qe(i.lhsCross,Ke(ft,Un,Vs)),r?ae(ve,this.bodyA.positionAng,this.rA):ee(ve,ae(ve,this.bodyB.positionAng,this.rB)),Ei(Ws,Nn(Kr,0,ve),ze[0]),qe(Ws,Ei(Kr,Nn(Kr,1,ve),ze[1])),qe(Ws,Ei(Kr,Nn(Kr,2,ve),ze[2])),qe(i.lhsAng,Ka(ft,Ws)),D(i.rhsLin,i.rhsLin,ue(ve,Fn,ze),1),D(i.rhsAng,i.rhsAng,ue(ve,cr,ze),1)}if(Ne(this.penaltyAng)>0){Ce(zt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(ye),this.stiffnessAng===1/0&&D(ye,ye,this.C0Ang,-t),ue(ze,zt,ye),D(ze,ze,this.lambdaAng,1);let o=(r?1:-1)*this.torqueArm;Ce(ji,o,o,o),_i(cr,ji),qe(i.lhsAng,Ke(ft,Ke(ft,cr,zt),ji)),D(i.rhsAng,i.rhsAng,ue(ve,cr,ze),1)}}updateDual(e){let{solver:t}=this;Ne(this.penaltyLin)>0&&(Ce(zt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(ye),this.stiffnessLin===1/0&&(D(ye,ye,this.C0Lin,-e),ue(ze,zt,ye),D(this.lambdaLin,ze,this.lambdaLin,1)),D(this.penaltyLin,this.penaltyLin,Sn(ve,ye),t.betaLin),Hs(this.penaltyLin,this.penaltyLin,Bt(this.stiffnessLin,Ve))),Ne(this.penaltyAng)>0&&(Ce(zt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(ye),this.stiffnessAng===1/0&&(D(ye,ye,this.C0Ang,-e),ue(ze,zt,ye),D(this.lambdaAng,ze,this.lambdaAng,1)),D(this.penaltyAng,this.penaltyAng,Sn(ve,ye),t.betaAng),Hs(this.penaltyAng,this.penaltyAng,Bt(this.stiffnessAng,Ve))),Ne(this.lambdaAng)>this.fracture*this.fracture&&(this.penaltyLin.fill(0),this.penaltyAng.fill(0),this.lambdaLin.fill(0),this.lambdaAng.fill(0),this.broken=!0)}},dt=class extends Ft{rA;rB;rest;stiffness;constructor(e,t,i,r,o,n,a=-1){super(e,t,i),this.rA=S(r[0],r[1],r[2]),this.rB=S(o[0],o[1],o[2]),this.stiffness=n,this.rest=a,this.rest<0&&(Gt(li,t.positionLin,t.positionAng,this.rA),Gt(ur,i.positionLin,i.positionAng,this.rB),this.rest=sr(J(ye,li,ur)))}initialize(){return!0}updatePrimal(e,t,i){let r=this.bodyA,o=this.bodyB;Gt(li,r.positionLin,r.positionAng,this.rA),Gt(ur,o.positionLin,o.positionAng,this.rB);let n=J(ye,li,ur),a=sr(n);if(a<=1e-6)return;let l=_t(ze,n,a),c=a-this.rest,u=this.stiffness*c,f=li,d=ur;e===r?(ae(ve,r.positionAng,this.rA),f.set(l),Dt(d,ve,l)):(ae(ve,o.positionAng,this.rB),ee(f,l),ee(d,Dt(d,ve,l))),qe(i.lhsLin,Ei(ft,Ms(ft,f,f),this.stiffness)),qe(i.lhsAng,Ei(ft,Ms(ft,d,d),this.stiffness)),qe(i.lhsCross,Ei(ft,Ms(ft,d,f),this.stiffness)),D(i.rhsLin,i.rhsLin,ne(ve,f,u),1),D(i.rhsAng,i.rhsAng,ne(ve,d,u),1)}updateDual(){}},$s=class extends Ft{initialize(){return!0}updatePrimal(){}updateDual(){}};var We=40,fr=0,Zr=4,Bl=8,Cl=12,qn=16,Vn=20,Sl=24,kl=28,Wn=32,Hn=36,pe=32,ci=0,es=4,dr=8,hr=12,Pl=16,Ll=20,ui=24,pr=28,Nt=8;var Js=4,Ht=16,mr=0,Xs=4,Ys=8,Qs=12,ts=2147483648,gr=6,sf=7,_l=0,$n=1,Jn=2,Ks=3,is=1,br=2,rs=1,ss=2,os=4,ns=8,as=16,of=.1,El=.002,Ml=.002,nf=.05,ls=44,ge=`
|
|
536
536
|
// Shared by every 3D module (avbd3d/gpu/layout.ts)
|
|
537
|
-
${
|
|
538
|
-
const T_JOINT = ${
|
|
539
|
-
const T_SPRING = ${
|
|
537
|
+
${Gn}
|
|
538
|
+
const T_JOINT = ${is};
|
|
539
|
+
const T_SPRING = ${br};
|
|
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 = ${$n}.0;
|
|
549
|
+
const SHAPE_SAIL = ${Jn}.0;
|
|
550
|
+
const SHAPE_HULL = ${Ks}.0;
|
|
551
|
+
const STICK_BIT = ${ts}u;
|
|
552
|
+
const C_MANIFOLDS = ${gr}u;
|
|
553
|
+
const C_PREV_MANIFOLDS = ${sf}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 = ${rs}u;
|
|
558
|
+
const FLAG_FACE_BIAS = ${ss}u;
|
|
559
|
+
const FLAG_REUSE_CONTACTS = ${os}u;
|
|
560
|
+
const FLAG_START_AT_REST = ${ns}u;
|
|
561
|
+
const FLAG_MASS_PENALTY = ${as}u;
|
|
562
|
+
const REST_SPEED = ${of};
|
|
563
|
+
const NEAREST_FRACTION = ${nf};
|
|
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
|
+
`,yr=`
|
|
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
|
+
`,Xn=`
|
|
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
|
+
`,Zs={counter:"C_MANIFOLDS",prevCounter:"C_PREV_MANIFOLDS",capacity:"params.manifoldCapacity",colorThreads:Xn};var zl=`
|
|
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 eo=`
|
|
1099
|
+
${ge}
|
|
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) > ${El}
|
|
1120
|
+
|| length(qsub(rot, last.rot)) > ${Ml};
|
|
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
|
+
`,fi=`
|
|
1127
|
+
${ge}
|
|
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
|
+
`,vr=s=>`
|
|
1335
|
+
${ge}
|
|
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 @@ ${ae}
|
|
|
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?zl:"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
|
+
`,Il=vr(!1),Rl=vr(!0);var Ge=`
|
|
1880
|
+
${ge}
|
|
1881
|
+
${Xn}
|
|
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 ts(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 Po(o){let e=o.length,t=e>0?ts(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,ts(o,e-64-1))),i}var wt=o=>o===1/0?3e38:o===-1/0?-3e38:o,$e=o=>Math.ceil(o/64),sn=o=>2**Math.ceil(Math.log2(Math.max(o,2))),Fl=o=>Math.max(o,1)*ke*4,Ul=8,is=3,Nl=8,ql=1.6,Vl=.8,Wl=2,Hl=[0,0,1],Jl=()=>({...no(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function $l(o,e){let t=o.vertices.length/3,i=o.faces.length,r=o.faces.reduce((m,h)=>m+h.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 p=0;return o.faces.forEach((m,h)=>{let b=(2+t+2*h)*4;n.set(m.normal,b),n[b+3]=m.d,l[b+4]=p,l[b+5]=m.verts.length;for(let y of m.verts)l[(2+t+2*i)*4+p++]=y}),o.edges.forEach((m,h)=>l.set(m,(2+t+2*i+s+h)*4)),n}var Lo=.001;function Xl(o,e){let t=o.length,i=o.map(b=>[0,1,2].map(y=>{let v=[0,0,0];return v[y]=1,ue(L(),b.positionAng,v)})),r=o.map((b,y)=>{if(lr(b))return[b.radius,b.radius,b.radius];let v=[0,0,0];for(let w=0;w<3;w++)for(let g=0;g<3;g++)v[g]+=b.size[w]*.5*Math.abs(i[y][w][g]);return v}),s=Po(Float64Array.from(o,b=>b.radius)),a=Math.max(2*s,.001),n=(b,y)=>{let v=o[b],w=o[y];if(v.mass<=0&&w.mass<=0)return!1;let g=[0,1,2].map(x=>v.positionLin[x]-w.positionLin[x]),A=v.radius+w.radius;return g[0]*g[0]+g[1]*g[1]+g[2]*g[2]>A*A?!1:g.every((x,B)=>Math.abs(x)<=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 v=o[b],w=o[y],g=[0,1,2].map(k=>w.positionLin[k]-v.positionLin[k]),[A,x]=[lr(v),lr(w)];if(A&&x)return Math.hypot(g[0],g[1],g[2])<=v.radius+w.radius+Lo;if(A||x){let[k,z,M]=A?[y,v.radius,-1]:[b,w.radius,1],P=0;for(let _=0;_<3;_++){let C=Math.abs(M*l(g,i[k][_]))-o[k].size[_]*.5;C>0&&(P+=C*C)}return P<=(z+Lo)**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)>Lo*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,v)=>(b*73856093^y*19349663^v*83492791)>>>0,p=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,v,w]=p(b),g=d(y,v,w),A=u.get(g);A?A.push(b):u.set(g,[b])}let m=0,h=0;for(let b=0;b<t;b++){if(f.has(b))continue;let[y,v,w]=p(b);for(let g=-1;g<=1;g++)for(let A=-1;A<=1;A++)for(let x=-1;x<=1;x++)for(let B of u.get(d(y+x,v+A,w+g))??[])B<b&&n(b,B)&&(m++,c(b,B)&&(h++,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)&&(h++,e[b]++,e[y]++));return{pairs:m,touching:h}}function Yl(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 de=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,rs=class{device;params=Jl();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:Hl}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(ke*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:Fl(this.bodyCapacity),usage:de()}),this.filterBuffer=e.createBuffer({label:"collision filters",size:Math.max(this.bodyCapacity,1)*8,usage:de()}),e.queue.writeBuffer(this.filterBuffer,0,new Uint32Array(Math.max(this.bodyCapacity,1)*2).fill(4294967295));let u=this.bodyCapacity;this.tableSize=sn(2*u),this.gridBuffer=e.createBuffer({label:"grid 3d",size:(2*this.tableSize+1+4*u)*4,usage:de()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:de()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:de()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(u/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*u+65+64*this.colorGroups+1)*4,usage:de()}),this.paramsBuffer=e.createBuffer({label:"params 3d",size:So*4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.refBuffer=e.createBuffer({label:"reference poses",size:u*32,usage:de()}),this.hulls=(i.hulls??!0)&&e.limits.maxStorageBuffersPerShaderStage>=9,this.hullBuffer=e.createBuffer({label:"hulls",size:this.hullCapacity*16,usage:de()}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(u).fill(255));let d=2*fe.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 Fe(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new Fe(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1);let p=i.spatialSort===!1?t.bodies.map((x,B)=>B):Yl(t.bodies);this.refToGpu=new Int32Array(p.length),p.forEach((x,B)=>this.refToGpu[x]=B);let m=new Map(t.bodies.map((x,B)=>[x,this.refToGpu[B]])),h=t.forces.filter(x=>x instanceof Be||x instanceof ct);for(let x of t.forces){if(!(x instanceof $r))continue;let B=m.get(x.bodyA),E=m.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,m);let b=new Int32Array(t.bodies.length),{pairs:y,touching:v}=Xl(t.bodies,b),w=new Map(t.bodies.map((x,B)=>[x,B]));for(let x of h)x.bodyA&&b[w.get(x.bodyA)]++,b[w.get(x.bodyB)]++;let g=0;t.bodies.forEach((x,B)=>{x.mass>0&&(g=Math.max(g,b[B]))}),this.colorCap=Math.min(64,Math.max(Nl,g+1+is,i.capacity?.colors??0));let A=i.capacity??{};this.allocateContacts(Math.max(8192,4*u,Ul*v,A.contacts??0),Math.max(4096,4*u,2*y,A.pairs??0),Math.max(4096,u,ql*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=(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(Ft,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(es,[r.refs],["updateRefs"]),i(e.contacts??en,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:s=>i(s,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(li(ae,Ni),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??be,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(co(ae,Ka),[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:de()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:de()});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=sn(2*l),this.pairBuffer=r.createBuffer({label:"pairs",size:n*8,usage:de()}),this.tableBuffer=r.createBuffer({label:"manifold hash",size:this.hashSize*4,usage:de()}),this.contactBuffers=[0,1].map(f=>r.createBuffer({label:`contacts 3d ${f}`,size:a*Bt*4,usage:de()})),this.manifoldBuffers=[0,1].map(f=>r.createBuffer({label:`manifolds 3d ${f}`,size:l*bt*4,usage:de()})),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((p,m)=>f.copyBufferToBuffer(p,0,this.contactBuffers[m],0,u)),c.manifolds.forEach((p,m)=>f.copyBufferToBuffer(p,0,this.manifoldBuffers[m],0,d)),r.queue.submit([f.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:de()}),this.adjScan?.destroy(),this.adjScan=new Fe(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:de()});let i=(d,p)=>e.createBindGroup({layout:d,entries:p.map((m,h)=>({binding:h,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)*ke);t.forEach((r,s)=>{let a=this.pack(r,i,s*ke,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*ke*4,i,0,t.length*ke)}rewriteFixed(e,t,i,r,s){if(!e.length)return;let a=new Float32Array(ke),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)*ke);for(let d=c;d<f;d++){let p=(d-c)*ke;u.set(a,p);for(let h=0;h<3;h++)u[p+Oi+h]=t[3*d+h];if(s)for(let h=0;h<4;h++)u[p+fr+h]=s[4*d+h];let m=this.bodies[e[d]];m?.radius!==l.radius&&(this.radiiDirty=!0),m?.hull&&this.releaseHull(m.hull),this.bodies[e[d]]={...l,size:[...l.size]}}this.device.queue.writeBuffer(this.bodyBuffer,e[c]*ke*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+Oi),t[i+Oi+3]=e.friction,t.set(e.positionAng,i+fr),t.set(e.size,i+po),t[i+po+3]=e.mass,t.set(e.moment,i+mo),t[i+mo+3]=e.radius,t.set(e.initialLin,i+qa),t.set(e.initialAng,i+Va),t.set(e.inertialLin,i+Wa),t.set(e.inertialAng,i+Ha),t.set(e.velocityLin,i+bo);let s=this.params.up;t[i+bo+3]=e.prevVelocityLin[0]*s[0]+e.prevVelocityLin[1]*s[1]+e.prevVelocityLin[2]*s[2],t.set(e.velocityAng,i+go);let a=lr(e),n=this.hulls?Nr(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+go+3]=a?yo:l>=0?Zr+l:Ua(e)?vo:Xa,{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 Be?(r.set(t.penaltyLin,Dt),r[Dt+3]=wt(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,dr),r[dr+3]=wt(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,ji),r[ji+3]=wt(t.fracture),r.set(t.lambdaAng,Ti),r[Ti+3]=t.torqueArm,r.set(t.C0Lin,Ja),r.set(t.C0Ang,$a),r.set(t.rA,Gt),r.set(t.rB,Di)):(r[Dt+3]=t.stiffness,r.set(t.rA,Gt),r[Gt+3]=t.rest,r.set(t.rB,Di)),this.info.set([t instanceof Be?pr:Ui,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=Po(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:de()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,i)}seedFrom(e){if(this.refToGpu.some((m,h)=>m!==h))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((m,h)=>[m,h]));this.writeBodies(0,e.bodies);let i=0;for(let m of e.forces)(m instanceof Be||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 zi),s=r.reduce((m,h)=>m+h.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,h)=>{f.set([t.get(m.bodyA),t.get(m.bodyB),d,m.contacts.length],h*bt),u.set([m.basis[0],m.basis[1],m.basis[2],m.friction],h*bt+Xr);for(let b of m.contacts){let y=d++*Bt;l.set(b.rA,y+Gi),n[y+Gi+3]=b.feature>>>0|(b.stick?hr:0),l.set([...b.rB,b.C0[0]],y+Yr),l.set([...b.penalty,b.C0[1]],y+Qr),l.set([...b.lambda,b.C0[2]],y+Kr)}}),this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,a),this.device.queue.writeBuffer(this.manifoldBuffers[1-this.parity],0,c);let p=new Uint32Array(16);p[1]=s,p[Fi]=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(tn),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+Zr>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,$l(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:de()}),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 Fe(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[Dt+3]=wt(s),l[dr+3]=wt(a),l[ji+3]=3e38,l.set([i[0],i[1],i[2]],Gt),l.set([r[0],r[1],r[2]],Di);let c=e>=0?this.bodies[e].size:[0,0,0],f=this.bodies[t].size;return l[Ti+3]=(c[0]+f[0])**2+(c[1]+f[1])**2+(c[2]+f[2])**2,this.info.set([pr,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[Dt+3]=3e38,s[dr+3]=f===void 0?3e38:wt(f),s[ji+3]=i?-wt(t):wt(t),s.set([l[0],l[1],l[2]],Gt),s.set([c[0],c[1],c[2]],Di);let[u,d]=[this.bodies[a].size,this.bodies[n].size];return s[Ti+3]=(u[0]+d[0])**2+(u[1]+d[1])**2+(u[2]+d[2])**2,[pr,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[Dt+3]=wt(l),i.set([a[0],a[1],a[2],c],Gt),i.set([n[0],n[1],n[2]],Di),[Ui,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+Gt)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*te+Dt)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+ji)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+Ti)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(So*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?xo:0)|(e.faceBias?Ao:0)|(e.reuseContacts?Bo:0)|(e.startAtRest?wo:0)|(e.massPenalty?Co: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(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,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?fe[c]:"avbd3d step",timestampWrites:n?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*c,endOfPassWriteIndex:2*c+1}:void 0}),c++)},d=this.groups,p=(v,w,g)=>{g<=0||(l.setPipeline(this.pipes[v]),l.setBindGroup(0,w),l.dispatchWorkgroups(g))},m=(v,w,g)=>{l.setPipeline(this.pipes[v]),l.setBindGroup(0,w),l.dispatchWorkgroupsIndirect(this.argsBuffer,g*4)};if(u(),p("beginFrame",d.broad,1),e.reuseContacts&&p("updateRefs",d.refs,$e(r)),p("argsPrev",d.args,1),m("hashInsert",d.contacts[t],6),p("gridCount",d.broad,$e(r)),this.gridScan.encode(l),p("gridScatter",d.broad,$e(r)),p("findPairs",d.broad,$e(r)),p("argsPairs",d.args,1),m("narrowphase",d.contacts[t],0),p("argsContacts",d.args,1),u(),p("degreeJoints",d.topo[t],$e(s)),m("degreeContacts",d.topo[t],3),this.adjScan.encode(l),p("fillJoints",d.topo[t],$e(s)),m("fillContacts",d.topo[t],3),p("sortAdjacency",d.topo[t],$e(r)),u(),!this.fixedColors){p("colorCompact",d.topo[t],$e(r)),p("colorMark",d.topo[t],$e(r));for(let v=0;v<this.colorRounds;v++)p(v%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],$e(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),u();let h=this.colorCap+1,b=v=>l.setBindGroup(1,this.passGroup,[v*256]);l.setBindGroup(0,d.solve[t]),b(0),s>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups($e(s))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups($e(r));for(let v=0;v<e.iterations;v++){l.setPipeline(this.pipes.primal);for(let w=0;w<this.colorCap;w++)b(v*h+w),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*w)*4);b(v*h+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups($e(r)),l.end();let y=2*fe.length;if(n){let{querySet:v,resolve:w,read:g}=this.timing;a.resolveQuerySet(v,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 v=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 A=(B,E)=>Number(g[E]-g[B])/1e6,x={total:A(0,y-1)};fe.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*ke*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[Fi],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 p=d*bt,m=[...l.subarray(p+Xr,p+Xr+3)];for(let h=n[p+2];h<n[p+2]+(n[p+3]&15);h++){let b=h*Bt,y=c[b+Gi+3];u.push({a:n[p],b:n[p+1],feature:(y&~hr)>>>0,stick:(y&hr)!==0,rA:[...f.subarray(b+Gi,b+Gi+3)],rB:[...f.subarray(b+Yr,b+Yr+3)],pen:[...f.subarray(b+Qr,b+Qr+3)],lam:[...f.subarray(b+Kr,b+Kr+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+is+2,this.colorCap+4)),this.shrinkVotes=0):t+is+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+is),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(n,l,c)=>(e.overflow&c)!==0||n>Vl*l?Math.max(Wl*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 on=` e.C = vec3f(
|
|
2421
|
+
`;function to(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 Yn(s){let e=s.length,t=e>0?to(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,to(s,e-64-1))),i}var $t=s=>s===1/0?3e38:s===-1/0?-3e38:s,ht=s=>Math.ceil(s/64),Tl=s=>2**Math.ceil(Math.log2(Math.max(s,2))),af=s=>Math.max(s,1)*We*4,lf=8,io=3,cf=8,uf=1.6,ff=.8,df=2,hf=[0,0,1],pf=()=>({...Tn(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function mf(s,e){let t=s.vertices.length/3,i=s.faces.length,r=s.faces.reduce((m,h)=>m+h.verts.length,0),o=Math.ceil(r/4),n=s.edges.length,a=new Float32Array((2+t+2*i+o+n)*4),l=new Uint32Array(a.buffer),c=e+2,u=c+t,f=u+2*i,d=f+o;l.set([c,t,u,i,d,n,f,0],0);for(let m=0;m<t;m++)a.set(s.vertices.subarray(m*3,m*3+3),(2+m)*4);let p=0;return s.faces.forEach((m,h)=>{let g=(2+t+2*h)*4;a.set(m.normal,g),a[g+3]=m.d,l[g+4]=p,l[g+5]=m.verts.length;for(let b of m.verts)l[(2+t+2*i)*4+p++]=b}),s.edges.forEach((m,h)=>l.set(m,(2+t+2*i+o+h)*4)),a}var Qn=.001;function gf(s,e){let t=s.length,i=s.map(y=>{let A=y;return[A.group??4294967295,A.collidesWith??4294967295]}),r=i.map(([y,A])=>y!==0&&A!==0),o=s.map(y=>[0,1,2].map(A=>{let v=[0,0,0];return v[A]=1,ae(S(),y.positionAng,v)})),n=s.map((y,A)=>{if(De(y))return[y.radius,y.radius,y.radius];let v=[0,0,0];for(let x=0;x<3;x++)for(let w=0;w<3;w++)v[w]+=y.size[x]*.5*Math.abs(o[A][x][w]);return v}),a=Yn(Float64Array.from(s,y=>y.radius)),l=Math.max(2*a,.001),c=(y,A)=>{let v=s[y],x=s[A];if(v.mass<=0&&x.mass<=0||!(i[y][0]&i[A][1])||!(i[A][0]&i[y][1]))return!1;let w=v.positionLin[0]-x.positionLin[0],B=v.positionLin[1]-x.positionLin[1],L=v.positionLin[2]-x.positionLin[2];if(!(Math.abs(w)<=n[y][0]+n[A][0]&&Math.abs(B)<=n[y][1]+n[A][1]&&Math.abs(L)<=n[y][2]+n[A][2]))return!1;let M=v.radius+x.radius;return w*w+B*B+L*L<=M*M},u=(y,A)=>y[0]*A[0]+y[1]*A[1]+y[2]*A[2],f=(y,A)=>{let v=s[y],x=s[A],w=[0,1,2].map(E=>x.positionLin[E]-v.positionLin[E]),[B,L]=[De(v),De(x)];if(B&&L)return Math.hypot(w[0],w[1],w[2])<=v.radius+x.radius+Qn;if(B||L){let[E,k,P]=B?[A,v.radius,-1]:[y,x.radius,1],C=0;for(let _=0;_<3;_++){let z=Math.abs(P*u(w,o[E][_]))-s[E].size[_]*.5;z>0&&(C+=z*z)}return C<=(k+Qn)**2}let M=(E,k)=>s[E].size[0]*.5*Math.abs(u(o[E][0],k))+s[E].size[1]*.5*Math.abs(u(o[E][1],k))+s[E].size[2]*.5*Math.abs(u(o[E][2],k)),I=E=>{let k=Math.hypot(E[0],E[1],E[2]);return k<1e-6?!1:Math.abs(u(w,E))-M(y,E)-M(A,E)>Qn*k};for(let E=0;E<3;E++)if(I(o[y][E])||I(o[A][E]))return!1;for(let E of o[y])for(let k of o[A])if(I([E[1]*k[2]-E[2]*k[1],E[2]*k[0]-E[0]*k[2],E[0]*k[1]-E[1]*k[0]]))return!1;return!0},d=new Set,p=new Map,m=(y,A,v)=>(y*73856093^A*19349663^v*83492791)>>>0,h=y=>[0,1,2].map(A=>Math.floor(s[y].positionLin[A]/l));for(let y=0;y<t;y++){if(!r[y])continue;if(s[y].radius>a){d.add(y);continue}let[A,v,x]=h(y),w=m(A,v,x),B=p.get(w);B?B.push(y):p.set(w,[y])}let g=0,b=0;for(let y=0;y<t;y++){if(!r[y]||d.has(y))continue;let[A,v,x]=h(y);for(let w=-1;w<=1;w++)for(let B=-1;B<=1;B++)for(let L=-1;L<=1;L++)for(let M of p.get(m(A+L,v+B,x+w))??[])M<y&&c(y,M)&&(g++,f(y,M)&&(b++,e[y]++,e[M]++))}for(let y of d)for(let A=0;A<t;A++)r[A]&&A!==y&&(!d.has(A)||A<y)&&c(y,A)&&(g++,f(y,A)&&(b++,e[y]++,e[A]++));return{pairs:g,touching:b}}function bf(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,ro=class{device;params=pf();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:hf}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(We*4)):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies 3d",size:af(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=Tl(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:ls*4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.refBuffer=e.createBuffer({label:"reference poses",size:f*32,usage:Pe()}),this.hulls=(i.hulls??!0)&&e.limits.maxStorageBuffersPerShaderStage>=9,this.hullBuffer=e.createBuffer({label:"hulls",size:this.hullCapacity*16,usage:Pe()}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(f).fill(255));let d=2*Se.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:d}),resolve:e.createBuffer({size:d*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:d*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(i.shaders??{}),this.gridScan=new tt(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new tt(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1);let p=i.spatialSort===!1?t.bodies.map((w,B)=>B):bf(t.bodies);this.refToGpu=new Int32Array(p.length),p.forEach((w,B)=>this.refToGpu[w]=B);let m=new Map(t.bodies.map((w,B)=>[w,this.refToGpu[B]])),h=t.forces.filter(w=>w instanceof ke||w instanceof dt);for(let w of t.forces){if(!(w instanceof $s))continue;let B=m.get(w.bodyA),L=m.get(w.bodyB);this.pendingEntries.push([Math.max(B,L),Math.min(B,L),4294967295])}this.allocateJoints(Math.max(h.length+256,i.capacity?.joints??0));for(let w of h)this.writeJoint(this.jointCount++,w,m);let g=new Int32Array(t.bodies.length),{pairs:b,touching:y}=gf(t.bodies,g),A=new Map(t.bodies.map((w,B)=>[w,B]));for(let w of h)w.bodyA&&g[A.get(w.bodyA)]++,g[A.get(w.bodyB)]++;let v=0;t.bodies.forEach((w,B)=>{w.mass>0&&(v=Math.max(v,g[B]))}),this.colorCap=Math.min(64,Math.max(cf,v+1+io,i.capacity?.colors??0));let x=i.capacity??{};this.allocateContacts(Math.max(8192,4*f,lf*y,x.contacts??0),Math.max(4096,4*f,2*b,x.pairs??0),Math.max(4096,f,uf*y,x.manifolds??0)),this.writeBodies(0,p.map(w=>t.bodies[w]))}gpuIndex(e){return e<this.refToGpu.length?this.refToGpu[e]:e}createLayouts(){let e=this.device,t=(n,a)=>e.createBindGroupLayout({label:n,entries:a.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",o="uniform";return{broad:t("broadphase 3d",[o,i,r,r,r,i,i,i]),contacts:t("contacts 3d",[o,i,i,r,i,r,i,r,r,...this.hulls?[i]:[]]),topo:t("topology 3d",[o,i,i,i,i,r,r,r]),solve:t("solve 3d",[o,r,r,i,r,i,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args 3d",[o,i,i,r]),refs:t("refs 3d",[o,r,r])}}createPipelines(e){let t=this.device,i=(o,n,a)=>{let l=t.createShaderModule({code:o}),c=t.createPipelineLayout({bindGroupLayouts:n});for(let u of a)this.pipes[u]=t.createComputePipeline({label:u,layout:c,compute:{module:l,entryPoint:u}})},r=this.layouts;i(fi,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(e.refs??eo,[r.refs],["updateRefs"]),i(e.contacts??Il,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:o=>i(o,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(e.topology??Ti(ge,yr),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??Ge,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(Yr(ge,Zs),[r.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints 3d",size:Math.max(e,1)*pe*4,usage:Pe()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:Pe()});if(this.jointBuffer){let n=t.createCommandEncoder();n.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*pe*4),n.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([n.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}let o=new Int32Array(e*4);o.set(this.info.subarray(0,Math.min(this.info.length,o.length))),this.info=o,this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t,i){let r=this.device,o=r.limits.maxStorageBufferBindingSize,n=Math.min(Math.ceil(e),Math.floor(o/(Ht*4))),a=Math.min(Math.ceil(t),Math.floor(o/8)),l=Math.min(Math.ceil(i),a,Math.floor(o/(Nt*4))),c={contacts:this.contactBuffers,manifolds:this.manifoldBuffers,contactCapacity:this.contactCapacity,manifoldCapacity:this.manifoldCapacity};for(let u of[this.pairBuffer,this.tableBuffer])u?.destroy();if(this.contactCapacity=n,this.pairCapacity=a,this.manifoldCapacity=l,this.hashSize=Tl(2*l),this.pairBuffer=r.createBuffer({label:"pairs",size:a*8,usage:Pe()}),this.tableBuffer=r.createBuffer({label:"manifold hash",size:this.hashSize*4,usage:Pe()}),this.contactBuffers=[0,1].map(u=>r.createBuffer({label:`contacts 3d ${u}`,size:n*Ht*4,usage:Pe()})),this.manifoldBuffers=[0,1].map(u=>r.createBuffer({label:`manifolds 3d ${u}`,size:l*Nt*4,usage:Pe()})),c.contacts){let u=r.createCommandEncoder(),f=Math.min(c.contactCapacity,n)*Ht*4,d=Math.min(c.manifoldCapacity,l)*Nt*4;c.contacts.forEach((p,m)=>u.copyBufferToBuffer(p,0,this.contactBuffers[m],0,f)),c.manifolds.forEach((p,m)=>u.copyBufferToBuffer(p,0,this.manifoldBuffers[m],0,d)),r.queue.submit([u.finish()]),[...c.contacts,...c.manifolds].forEach(p=>p.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.manifoldCapacity))*4,usage:Pe()}),this.adjScan?.destroy(),this.adjScan=new tt(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:Pe()});let i=(d,p)=>e.createBindGroup({layout:d,entries:p.map((m,h)=>({binding:h,resource:{buffer:m}}))}),r=this.paramsBuffer,[o,n]=this.contactBuffers,[a,l]=this.manifoldBuffers,c=this.adjBuffer,u=this.tableBuffer,f=this.counterBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,f,this.staticBuffer,this.jointBuffer,this.filterBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,o,n,a,l,u,f,...this.hulls?[this.hullBuffer]:[]]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,n,o,l,a,u,f,...this.hulls?[this.hullBuffer]:[]])],topo:[a,l].map(d=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,d,f,c,this.colorBuffer])),solve:[0,1].map(d=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,[o,n][d],c,this.colorBuffer,f,[a,l][d]])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer]),refs:i(this.layouts.refs,[r,this.bodyBuffer,this.refBuffer])}}writeBodies(e,t){let i=new Float32Array(Math.max(t.length,1)*We);t.forEach((r,o)=>{let n=this.pack(r,i,o*We,this.bodies[e+o]?.hull);this.bodies[e+o]?.radius!==n.radius&&(this.radiiDirty=!0),this.bodies[e+o]=n}),this.device.queue.writeBuffer(this.bodyBuffer,e*We*4,i,0,t.length*We)}rewriteFixed(e,t,i,r,o){if(!e.length)return;let n=new Float32Array(We),a={positionLin:[0,0,0],positionAng:[0,0,0,1],initialLin:[0,0,0],initialAng:[0,0,0,1],inertialLin:[0,0,0],inertialAng:[0,0,0,1],velocityLin:[0,0,0],prevVelocityLin:[0,0,0],velocityAng:[0,0,0],size:[i[0],i[1],i[2]],mass:0,moment:[0,0,0],friction:r,radius:Math.hypot(i[0]/2,i[1]/2,i[2]/2)},l=this.pack(a,n,0);for(let c=0;c<e.length;){let u=c+1;for(;u<e.length&&e[u]===e[u-1]+1;)u++;let f=new Float32Array((u-c)*We);for(let d=c;d<u;d++){let p=(d-c)*We;f.set(n,p);for(let h=0;h<3;h++)f[p+fr+h]=t[3*d+h];if(o)for(let h=0;h<4;h++)f[p+Zr+h]=o[4*d+h];let m=this.bodies[e[d]];m?.radius!==l.radius&&(this.radiiDirty=!0),m?.hull&&this.releaseHull(m.hull),this.bodies[e[d]]={...l,size:[...l.size]}}this.device.queue.writeBuffer(this.bodyBuffer,e[c]*We*4,f),this.device.queue.writeBuffer(this.colorBuffer,e[c]*4,new Uint32Array(u-c).fill(255)),c=u}}pack(e,t,i,r){{t.set(e.positionLin,i+fr),t[i+fr+3]=e.friction,t.set(e.positionAng,i+Zr),t.set(e.size,i+qn),t[i+qn+3]=e.mass,t.set(e.moment,i+Vn),t[i+Vn+3]=e.radius,t.set(e.initialLin,i+Bl),t.set(e.initialAng,i+Cl),t.set(e.inertialLin,i+Sl),t.set(e.inertialAng,i+kl),t.set(e.velocityLin,i+Wn);let o=this.params.up;t[i+Wn+3]=e.prevVelocityLin[0]*o[0]+e.prevVelocityLin[1]*o[1]+e.prevVelocityLin[2]*o[2],t.set(e.velocityAng,i+Hn);let n=De(e),a=this.hulls?Ct(e):void 0;r&&r!==a&&this.releaseHull(r);let l=a?r===a?this.hullSlots.get(a).offset:this.acquireHull(a):-1;return t[i+Hn+3]=n?$n:l>=0?Ks+l:nr(e)?Jn:_l,{radius:e.radius,dynamic:e.mass>0,sphere:n,size:[e.size[0],e.size[1],e.size[2]],hull:a}}}writeJoint(e,t,i){let r=new Float32Array(pe),o=t.bodyA?i.get(t.bodyA):-1,n=i.get(t.bodyB);t instanceof ke?(r.set(t.penaltyLin,ci),r[ci+3]=$t(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,es),r[es+3]=$t(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,dr),r[dr+3]=$t(t.fracture),r.set(t.lambdaAng,hr),r[hr+3]=t.torqueArm,r.set(t.C0Lin,Pl),r.set(t.C0Ang,Ll),r.set(t.rA,ui),r.set(t.rB,pr)):(r[ci+3]=t.stiffness,r.set(t.rA,ui),r[ui+3]=t.rest,r.set(t.rB,pr)),this.info.set([t instanceof ke?is:br,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=Yn(o),a=1/0;for(let c=0;c<r;c++)o[c]>n&&(a=Math.min(a,o[c]));let l=a===1/0?n*1.5:(n+a)/2;this.maxSmallRadius=l,this.cellSize=Math.max(2*n*(1+1e-4),.001),this.large=[];for(let c=0;c<r;c++)this.bodies[c].radius>l&&this.large.push(c);this.radiiDirty=!1}if(this.noCollideDirty){let r=(l,c)=>l[0]-c[0]||l[1]-c[1],o=this.releasedSlots.size?this.entries.filter(l=>!this.releasedSlots.has(l[2])):this.entries;this.releasedSlots.clear();let n=this.pendingEntries.sort(r);this.pendingEntries=[];let a=[];for(let l=0,c=0;l<o.length||c<n.length;)c>=n.length||l<o.length&&r(o[l],n[c])<=0?a.push(o[l++]):a.push(n[c++]);this.entries=a,this.noCollideDirty=!1}let{large:e,entries:t}=this;this.largeCount=e.length,this.noCollideCount=t.length;let i=new Uint32Array(Math.max(e.length+3*t.length,4));i.set(e,0),t.forEach((r,o)=>i.set(r,e.length+3*o)),this.staticBuffer.size<i.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:i.byteLength,usage:Pe()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,i)}seedFrom(e){if(this.refToGpu.some((m,h)=>m!==h))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((m,h)=>[m,h]));this.writeBodies(0,e.bodies);let i=0;for(let m of e.forces)(m instanceof ke||m instanceof dt)&&this.writeJoint(i++,m,t);if(i!==this.jointCount)throw new Error("seedFrom: joints differ from the reference");let r=e.forces.filter(m=>m instanceof ni),o=r.reduce((m,h)=>m+h.contacts.length,0);if(o>this.contactCapacity||r.length>this.manifoldCapacity)throw new Error("seedFrom: too many contacts");let n=new ArrayBuffer(Math.max(o,1)*Ht*4),a=new Uint32Array(n),l=new Float32Array(n),c=new ArrayBuffer(Math.max(r.length,1)*Nt*4),u=new Uint32Array(c),f=new Float32Array(c),d=0;r.forEach((m,h)=>{u.set([t.get(m.bodyA),t.get(m.bodyB),d,m.contacts.length],h*Nt),f.set([m.basis[0],m.basis[1],m.basis[2],m.friction],h*Nt+Js);for(let g of m.contacts){let b=d++*Ht;l.set(g.rA,b+mr),a[b+mr+3]=g.feature>>>0|(g.stick?ts:0),l.set([...g.rB,g.C0[0]],b+Xs),l.set([...g.penalty,g.C0[1]],b+Ys),l.set([...g.lambda,g.C0[2]],b+Qs)}}),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[gr]=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(Rl),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+Ks>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,mf(e,r)),this.hullSlots.set(e,{offset:r,size:i,refs:1}),r}releaseHull(e){let t=this.hullSlots.get(e);if(!t||--t.refs>0)return;this.hullSlots.delete(e),this.hullFree.push({offset:t.offset,size:t.size}),this.hullFree.sort((r,o)=>r.offset-o.offset);let i=[];for(let r of this.hullFree){let o=i[i.length-1];o&&o.offset+o.size===r.offset?o.size+=r.size:i.push({...r})}this.hullFree=i}growHulls(e){let t=this.hullCapacity;for(;t<e;)t*=2;let i=this.device.createBuffer({label:"hulls",size:t*16,usage:Pe()}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(this.hullBuffer,0,i,0,this.hullCapacity*16),this.device.queue.submit([r.finish()]),this.hullBuffer.destroy(),this.hullBuffer=i,this.hullCapacity=t,this.contactBuffers&&this.rebuildBindings()}addBody(e){return this.addBodies([e])}addBodies(e){let t=Math.min(e.length,this.bodyCapacity-this.bodyCount);if(t<=0)return-1;let i=this.bodyCount;return this.bodyCount+=t,this.writeBodies(i,e.slice(0,t)),this.device.queue.writeBuffer(this.colorBuffer,i*4,new Uint32Array(t).fill(255)),this.adjScan.destroy(),this.adjScan=new tt(this.device,this.adjBuffer,0,this.bodyCount+1),i}appendJoint(e,t,i,r,o,n){let a=this.jointCount;a>=this.jointCapacity&&this.allocateJoints(this.jointCapacity*2),this.jointCount++;let l=new Float32Array(pe);l[ci+3]=$t(o),l[es+3]=$t(n),l[dr+3]=3e38,l.set([i[0],i[1],i[2]],ui),l.set([r[0],r[1],r[2]],pr);let c=e>=0?this.bodies[e].size:[0,0,0],u=this.bodies[t].size;return l[hr+3]=(c[0]+u[0])**2+(c[1]+u[1])**2+(c[2]+u[2])**2,this.info.set([is,e,t,0],a*4),this.device.queue.writeBuffer(this.jointBuffer,a*pe*4,l),this.device.queue.writeBuffer(this.infoBuffer,a*16,this.info,a*4,4),this.noCollide(e,t,a),a}appendJoints(e,t,i=!1){return this.appendConstraints(e.length,(r,o)=>{let{a:n,b:a,rA:l,rB:c,angular:u}=e[r];o[ci+3]=3e38,o[es+3]=u===void 0?3e38:$t(u),o[dr+3]=i?-$t(t):$t(t),o.set([l[0],l[1],l[2]],ui),o.set([c[0],c[1],c[2]],pr);let[f,d]=[this.bodies[n].size,this.bodies[a].size];return o[hr+3]=(f[0]+d[0])**2+(f[1]+d[1])**2+(f[2]+d[2])**2,[is,n,a]})}appendSprings(e){return this.appendConstraints(e.length,(t,i)=>{let{a:r,b:o,rA:n,rB:a,stiffness:l,rest:c}=e[t];return i[ci+3]=$t(l),i.set([n[0],n[1],n[2],c],ui),i.set([a[0],a[1],a[2]],pr),[br,r,o]})}appendConstraints(e,t){let i=[];if(this.freeSlots.length)for(this.freeSlots.sort((a,l)=>l-a);i.length<e&&this.freeSlots.length;)i.push(this.freeSlots.pop());let r=e-i.length,o=this.jointCapacity;for(;this.jointCount+r>o;)o*=2;o>this.jointCapacity&&this.allocateJoints(o);for(let a=0;a<r;a++)i.push(this.jointCount++);let n=new Float32Array(e*pe);for(let a=0;a<e;a++){let[l,c,u]=t(a,n.subarray(a*pe,(a+1)*pe));this.info.set([l,c,u,0],i[a]*4),this.releasedSlots.delete(i[a]),this.noCollide(c,u,i[a])}for(let a=0;a<e;){let l=a+1;for(;l<e&&i[l]===i[l-1]+1;)l++;this.device.queue.writeBuffer(this.jointBuffer,i[a]*pe*4,n,a*pe,(l-a)*pe),this.device.queue.writeBuffer(this.infoBuffer,i[a]*16,this.info,i[a]*4,(l-a)*4),a=l}return i}releaseJoints(e){let t=[...new Set(Array.from(e))].filter(r=>r<this.jointCount&&this.info[r*4]!==0).sort((r,o)=>r-o);if(!t.length)return;for(let r of t)this.info.set([0,-1,-1,0],r*4),this.releasedSlots.add(r),this.freeSlots.push(r);let i=new Float32Array(0);for(let r=0;r<t.length;){let o=r+1;for(;o<t.length&&t[o]===t[o-1]+1;)o++;i.length<(o-r)*pe&&(i=new Float32Array((o-r)*pe)),this.device.queue.writeBuffer(this.jointBuffer,t[r]*pe*4,i,0,(o-r)*pe),this.device.queue.writeBuffer(this.infoBuffer,t[r]*16,this.info,t[r]*4,(o-r)*4),r=o}this.noCollideDirty=!0}rewriteBodies(e,t){for(let i=0;i<e.length;){let r=i+1;for(;r<e.length&&e[r]===e[r-1]+1;)r++;this.writeBodies(e[i],t.slice(i,r)),this.device.queue.writeBuffer(this.colorBuffer,e[i]*4,new Uint32Array(r-i).fill(255)),i=r}}setFilters(e,t,i){for(let r=0;r<e.length;){let o=r+1;for(;o<e.length&&e[o]===e[o-1]+1;)o++;let n=new Uint32Array(2*(o-r));for(let a=r;a<o;a++)n.set([t[a]>>>0,i[a]>>>0],2*(a-r));this.device.queue.writeBuffer(this.filterBuffer,e[r]*8,n),r=o}}setWorldAnchor(e,t){this.device.queue.writeBuffer(this.jointBuffer,(e*pe+ui)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*pe+ci)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+dr)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*pe+hr)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(ls*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?rs:0)|(e.faceBias?ss:0)|(e.reuseContacts?os:0)|(e.startAtRest?ns:0)|(e.massPenalty?as:0),o[7]=this.bodyCount,o[8]=this.jointCount,o[9]=this.colorCap,r[10]=this.cellSize,o[11]=this.tableSize-1,r[12]=this.maxSmallRadius,o[13]=this.largeCount,o[14]=this.noCollideCount,o[15]=this.pairCapacity,o[16]=this.contactCapacity,o[17]=this.hashSize-1,o[18]=t+1,o[19]=2*t+1,o[20]=this.tableSize+1,o[21]=2*this.tableSize+1,o[22]=2*this.tableSize+1+t,o[23]=t,o[24]=this.colorHistOffset,o[25]=2*t,o[26]=this.colorGroups,o[27]=2*t+65,o[28]=this.colorRounds,o[29]=this.manifoldCapacity,o[30]=this.stepCount;let n=a=>Math.min(31,Math.max(0,Math.round(Math.log2(a))));o[31]=n(this.primalLanes[0])|n(this.primalLanes[1])<<8|n(this.primalLanes[2])<<16,r.set([e.windSpeed*Math.cos(e.windAngle),e.windSpeed*Math.sin(e.windAngle),0,e.windPressure,e.windGust],32),r.set([e.up[0],e.up[1],e.up[2],0],40),this.device.queue.writeBuffer(this.paramsBuffer,0,i)}writePassConstants(e,t){let i=this.colorCap+1,r=Math.max(e*i,1);(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let o=new ArrayBuffer(r*256),n=new Uint32Array(o),a=new Float32Array(o);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let u=(l*i+c)*256/4;n[u]=c,a[u+1]=t}this.device.queue.writeBuffer(this.passBuffer,0,o)}step(){(this.radiiDirty||this.noCollideDirty)&&this.uploadStatics();let e=this.params;this.fixedColors&&(this.device.queue.writeBuffer(this.colorBuffer,0,this.fixedColors),this.colorCap=Math.max(1,...this.fixedColors.map(y=>y===255?0:y+1))),this.stepCount++,this.writeParams(),this.writePassConstants(e.iterations,e.alpha);let t=this.parity,i=this.bodyCapacity,r=this.bodyCount,o=this.jointCount,n=this.device.createCommandEncoder({label:"avbd3d step"});n.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),n.clearBuffer(this.tableBuffer),n.clearBuffer(this.adjBuffer,0,(2*i+1)*4);let a=this.timingCallback!==null&&this.timing!==null,l,c=0,u=a||this.splitPasses,f=()=>{l&&!u||(l?.end(),l=n.beginComputePass({label:u?Se[c]:"avbd3d step",timestampWrites:a?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*c,endOfPassWriteIndex:2*c+1}:void 0}),c++)},d=this.groups,p=(y,A,v)=>{v<=0||(l.setPipeline(this.pipes[y]),l.setBindGroup(0,A),l.dispatchWorkgroups(v))},m=(y,A,v)=>{l.setPipeline(this.pipes[y]),l.setBindGroup(0,A),l.dispatchWorkgroupsIndirect(this.argsBuffer,v*4)};if(f(),p("beginFrame",d.broad,1),e.reuseContacts&&p("updateRefs",d.refs,ht(r)),p("argsPrev",d.args,1),m("hashInsert",d.contacts[t],6),p("gridCount",d.broad,ht(r)),this.gridScan.encode(l),p("gridScatter",d.broad,ht(r)),p("findPairs",d.broad,ht(r)),p("argsPairs",d.args,1),m("narrowphase",d.contacts[t],0),p("argsContacts",d.args,1),f(),p("degreeJoints",d.topo[t],ht(o)),m("degreeContacts",d.topo[t],3),this.adjScan.encode(l),p("fillJoints",d.topo[t],ht(o)),m("fillContacts",d.topo[t],3),p("sortAdjacency",d.topo[t],ht(r)),f(),!this.fixedColors){p("colorCompact",d.topo[t],ht(r)),p("colorMark",d.topo[t],ht(r));for(let y=0;y<this.colorRounds;y++)p(y%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],ht(r))}p("colorCount",d.topo[t],this.colorGroups),this.colorHistScan.encode(l),p("colorStarts",d.topo[t],1),p("argsColors",d.args,1),p("colorScatter",d.topo[t],this.colorGroups),f();let h=this.colorCap+1,g=y=>l.setBindGroup(1,this.passGroup,[y*256]);l.setBindGroup(0,d.solve[t]),g(0),o>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups(ht(o))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups(ht(r));for(let y=0;y<e.iterations;y++){l.setPipeline(this.pipes.primal);for(let A=0;A<this.colorCap;A++)g(y*h+A),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*A)*4);g(y*h+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups(ht(r)),l.end();let b=2*Se.length;if(a){let{querySet:y,resolve:A,read:v}=this.timing;n.resolveQuerySet(y,0,b,A,0),n.copyBufferToBuffer(A,0,v,0,b*8)}if(this.device.queue.submit([n.finish()]),this.parity=1-t,a){let y=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let A=this.timing.read;A.mapAsync(GPUMapMode.READ).then(()=>{let v=new BigUint64Array(A.getMappedRange()).slice();if(A.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let x=(B,L)=>Number(v[L]-v[B])/1e6,w={total:x(0,b-1)};Se.forEach((B,L)=>w[B]=x(2*L,2*L+1)),y(w)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*We*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*pe*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],manifolds:e[gr],overflow:e[3],clashes:e[4],colors:e[5]}}async readContactList(){let e=await this.readCounters(),t=Math.min(e.manifolds,this.manifoldCapacity),i=Math.min(e.contacts,this.contactCapacity),r=1-this.parity,[o,n]=await Promise.all([this.read(this.manifoldBuffers[r],t*Nt*4),this.read(this.contactBuffers[r],i*Ht*4)]),a=new Uint32Array(o),l=new Float32Array(o),c=new Uint32Array(n),u=new Float32Array(n),f=[];for(let d=0;d<t;d++){let p=d*Nt,m=[...l.subarray(p+Js,p+Js+3)];for(let h=a[p+2];h<a[p+2]+(a[p+3]&15);h++){let g=h*Ht,b=c[g+mr+3];f.push({a:a[p],b:a[p+1],feature:(b&~ts)>>>0,stick:(b&ts)!==0,rA:[...u.subarray(g+mr,g+mr+3)],rB:[...u.subarray(g+Xs,g+Xs+3)],pen:[...u.subarray(g+Ys,g+Ys+3)],lam:[...u.subarray(g+Qs,g+Qs+3)],normal:m})}}return f}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}jointInfo(){return this.info.subarray(0,this.jointCount*4)}adapt(e){if(this.fixedColors)return;let t=e.colors;e.clashes>0&&(this.colorRounds=Math.min(32,this.colorRounds*2)),e.clashes>0||this.colorCap-t<2?(this.colorCap=Math.min(64,Math.max(t+io+2,this.colorCap+4)),this.shrinkVotes=0):t+io+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+io),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(a,l,c)=>(e.overflow&c)!==0||a>ff*l?Math.max(df*a,1.25*l):l,r=i(e.contacts,this.contactCapacity,2),o=i(e.pairs,this.pairCapacity,1),n=i(e.manifolds,this.manifoldCapacity,4);this.capacityCanGrow(r,o,n)&&this.allocateContacts(r,o,n)}capacityCanGrow(e,t,i){let r=this.device.limits.maxStorageBufferBindingSize;return Math.min(e,r/(Ht*4))>this.contactCapacity||Math.min(t,r/8)>this.pairCapacity||Math.min(i,t,r/(Nt*4))>this.manifoldCapacity}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),o}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,...this.manifoldBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.refBuffer,this.hullBuffer,this.passBuffer,this.filterBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var jl=` 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(Ge.split(jl).length!==2)throw Error("Pinned contact evaluation changed; review the app kernel variant");var us=Ge.replace(jl,` 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 so=(()=>{let s=St(us," 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 sa(s=Ge){s=St(s,"fn manifoldCount() -> u32 {",`fn solverStepValid() -> bool {
|
|
2439
2439
|
return counters[C_OVERFLOW] == 0u && counters[C_CLASHES] == 0u;
|
|
2440
2440
|
}
|
|
2441
2441
|
|
|
2442
2442
|
|
|
2443
|
-
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 oa(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 Dl=`// 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
|
+
`;function hi(s){let e=new ArrayBuffer(s*4);return{f:new Float32Array(e),u:new Uint32Array(e),previous:new Uint32Array(s),buffer:null,uploads:0,bytes:0}}function Qt(s,e,t){let{u:i,previous:r}=t,o=0,n=i.length-1;if(t.buffer===e){for(;o<=n&&i[o]===r[o];)o++;for(;n>=o&&i[n]===r[n];)n--;if(o>n)return}s.queue.writeBuffer(e,o*4,i,o,n-o+1),r.set(i),t.buffer=e,t.uploads++,t.bytes+=(n-o+1)*4}var Gl=`// 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,131 @@ 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 Fl=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }\r
|
|
2526
|
+
struct State { mass:f32, quiet:f32, asleep:u32, support:atomic<u32>, eligible:u32, parent:atomic<u32>, wake:atomic<u32>, pad2:u32 }\r
|
|
2527
|
+
struct Manifold { ids:vec4u, geo:vec4f }\r
|
|
2528
|
+
struct Params { count:u32,joints:u32,manifolds:u32,forceWake:u32,speed:f32,delay:f32,dt:f32,gravity:f32,up:vec4f }\r
|
|
2529
|
+
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;\r
|
|
2530
|
+
@group(0) @binding(1) var<storage,read_write> states:array<State>;\r
|
|
2531
|
+
@group(0) @binding(2) var<storage,read> info:array<vec4i>;\r
|
|
2532
|
+
@group(0) @binding(3) var<storage,read> manifolds:array<Manifold>;\r
|
|
2533
|
+
@group(0) @binding(4) var<storage,read> counters:array<u32>;\r
|
|
2534
|
+
@group(0) @binding(5) var<storage,read_write> globals:array<atomic<u32>>;\r
|
|
2535
|
+
@group(0) @binding(6) var<uniform> params:Params;\r
|
|
2536
|
+
fn speed(i:u32)->f32 {return length(bodies[i].velocity.xyz)+length(bodies[i].angular.xyz)*bodies[i].moment.w;}\r
|
|
2537
|
+
fn wake(i:u32) {if(states[i].mass>0.){bodies[i].size.w=states[i].mass;states[i].asleep=0u;states[i].quiet=0.;}}\r
|
|
2538
|
+
@compute @workgroup_size(64) fn before(@builtin(global_invocation_id) id:vec3u) {\r
|
|
2539
|
+
let i=id.x;if(i>=params.count){return;}\r
|
|
2540
|
+
if(params.forceWake!=0u||atomicLoad(&globals[0])!=0u){wake(i);atomicStore(&states[i].parent,i);}\r
|
|
2541
|
+
atomicStore(&states[i].wake,0u);\r
|
|
2542
|
+
atomicStore(&states[i].support,0u);\r
|
|
2543
|
+
}\r
|
|
2544
|
+
@compute @workgroup_size(1) fn clear() {atomicStore(&globals[0],0u);atomicStore(&globals[1],0u);}\r
|
|
2545
|
+
fn impact(a:u32,b:u32) {\r
|
|
2546
|
+
if(states[a].asleep!=0u && states[b].asleep==0u && speed(b)>2.*params.speed){let r=root(a);if(r<params.count){atomicStore(&states[r].wake,1u);}else{atomicStore(&globals[0],1u);}}\r
|
|
2547
|
+
}\r
|
|
2548
|
+
@compute @workgroup_size(64) fn contacts(@builtin(global_invocation_id) id:vec3u) {\r
|
|
2549
|
+
let i=id.x;if(i>=min(counters[6],params.manifolds)){return;}\r
|
|
2550
|
+
let m=manifolds[i];if((m.ids.w&15u)==0u || (m.ids.w&0x80000000u)!=0u){return;}\r
|
|
2551
|
+
let a=m.ids.x;let b=m.ids.y;impact(a,b);impact(b,a);\r
|
|
2552
|
+
let up=dot(m.geo.xyz,params.up.xyz);\r
|
|
2553
|
+
if(bodies[b].size.w==0. && up>0.3){atomicStore(&states[a].support,1u);}\r
|
|
2554
|
+
if(bodies[a].size.w==0. && up < -0.3){atomicStore(&states[b].support,1u);}\r
|
|
2555
|
+
}\r
|
|
2556
|
+
@compute @workgroup_size(64) fn joints(@builtin(global_invocation_id) id:vec3u) {\r
|
|
2557
|
+
let i=id.x;if(i>=params.joints){return;}let j=info[i];if(j.x==0||j.y<0){return;}\r
|
|
2558
|
+
impact(u32(j.y),u32(j.z));impact(u32(j.z),u32(j.y));\r
|
|
2559
|
+
}\r
|
|
2560
|
+
@compute @workgroup_size(64) fn rest(@builtin(global_invocation_id) id:vec3u) {\r
|
|
2561
|
+
let i=id.x;if(i>=params.count||states[i].mass<=0.){return;}\r
|
|
2562
|
+
if(states[i].eligible==0u){wake(i);return;}\r
|
|
2563
|
+
let r=root(i);\r
|
|
2564
|
+
if(r>=params.count || atomicLoad(&states[min(r,params.count-1u)].wake)!=0u || atomicLoad(&globals[0])!=0u || counters[3]!=0u || counters[4]!=0u){wake(i);return;}\r
|
|
2565
|
+
if(states[i].asleep==0u){\r
|
|
2566
|
+
if(speed(i)<=params.speed && (params.gravity==0. || atomicLoad(&states[i].support)!=0u)) {states[i].quiet+=params.dt;}\r
|
|
2567
|
+
else {states[i].quiet=0.;}\r
|
|
2568
|
+
if(states[i].quiet>=params.delay){\r
|
|
2569
|
+
states[i].asleep=1u;bodies[i].size.w=0.;bodies[i].velocity=vec4f(0.);bodies[i].angular=vec4f(vec3f(0.),bodies[i].angular.w);\r
|
|
2570
|
+
}\r
|
|
2571
|
+
}\r
|
|
2572
|
+
if(states[i].asleep!=0u){atomicAdd(&globals[1],1u);}\r
|
|
2573
|
+
}\r
|
|
2574
|
+
\r
|
|
2575
|
+
// Dynamic connectivity is conservative: unions persist while asleep because\r
|
|
2576
|
+
// collision detection intentionally omits pairs of sleeping bodies. Explicit\r
|
|
2577
|
+
// edits wake the world and reset this history. Shared static ground never joins\r
|
|
2578
|
+
// otherwise independent piles. No thread waits for another workgroup.\r
|
|
2579
|
+
fn root(index:u32)->u32 {\r
|
|
2580
|
+
var i=index;\r
|
|
2581
|
+
for(var attempt=0u;attempt<256u;attempt++) {\r
|
|
2582
|
+
if(i>=params.count){atomicStore(&globals[0],1u);return params.count;}\r
|
|
2583
|
+
let p=atomicLoad(&states[i].parent);\r
|
|
2584
|
+
if(p==i){return i;}\r
|
|
2585
|
+
if(p>i){atomicStore(&globals[0],1u);return params.count;}\r
|
|
2586
|
+
let gp=atomicLoad(&states[p].parent);\r
|
|
2587
|
+
atomicMin(&states[i].parent,gp);\r
|
|
2588
|
+
i=gp;\r
|
|
2589
|
+
}\r
|
|
2590
|
+
// Excessive depth is a safe wake-all fallback, never a cross-group wait.\r
|
|
2591
|
+
atomicStore(&globals[0],1u);return params.count;\r
|
|
2592
|
+
}\r
|
|
2593
|
+
fn connect(a:u32,b:u32) {\r
|
|
2594
|
+
if(a>=params.count || b>=params.count || states[a].mass<=0. || states[b].mass<=0.){return;}\r
|
|
2595
|
+
for(var attempt=0u;attempt<256u;attempt++) {\r
|
|
2596
|
+
let ra=root(a);let rb=root(b);\r
|
|
2597
|
+
if(ra==rb || ra>=params.count || rb>=params.count){return;}\r
|
|
2598
|
+
let hi=max(ra,rb);let lo=min(ra,rb);\r
|
|
2599
|
+
let result=atomicCompareExchangeWeak(&states[hi].parent,hi,lo);\r
|
|
2600
|
+
if(result.exchanged){return;}\r
|
|
2601
|
+
}\r
|
|
2602
|
+
atomicStore(&globals[0],1u);\r
|
|
2603
|
+
}\r
|
|
2604
|
+
@compute @workgroup_size(64) fn connectContacts(@builtin(global_invocation_id) id:vec3u){\r
|
|
2605
|
+
let i=id.x;if(i>=min(counters[6],params.manifolds)){return;}\r
|
|
2606
|
+
let m=manifolds[i];if((m.ids.w&15u)==0u || (m.ids.w&0x80000000u)!=0u){return;}\r
|
|
2607
|
+
connect(m.ids.x,m.ids.y);\r
|
|
2608
|
+
}\r
|
|
2609
|
+
@compute @workgroup_size(64) fn connectJoints(@builtin(global_invocation_id) id:vec3u){\r
|
|
2610
|
+
let i=id.x;if(i>=params.joints){return;}let j=info[i];\r
|
|
2611
|
+
if(j.x!=0 && j.y>=0 && j.z>=0){connect(u32(j.y),u32(j.z));}\r
|
|
2612
|
+
}\r
|
|
2613
|
+
`;var na=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 m=1;m<u;m++)if(f[m]<f[m-1]){d=!1;break}d||f.sort((m,h)=>m-h);let p=1;for(let m=1;m<u;m++)f[m]!==f[m-1]+1&&p++;if(p<=4||u<=8){for(let m=0;m<u;m++)c.data[m]=c.pending.get(f[m]);for(let m=0;m<u;){let h=m+1;for(;h<u&&f[h]===f[h-1]+1;)h++;t.queue.writeBuffer(l,f[m]*4,c.data.buffer,m*4,(h-m)*4),r++,m=h}a+=u*4}else{let m=u*8,h=Math.min(t.limits.maxBufferSize,t.limits.maxStorageBufferBindingSize);if(m>h)throw Error("Property batch exceeds GPU buffer limits");for(let A=0;A<u;A++)c.data[A*2]=f[A],c.data[A*2+1]=c.pending.get(f[A]);(!c.upload||c.upload.size<m)&&(c.upload?.destroy(),c.upload=t.createBuffer({size:Math.min(h,m*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),c.group=null),t.queue.writeBuffer(c.upload,0,c.data.buffer,0,m),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"applyEdits",module:t.createShaderModule({code:`
|
|
2526
2614
|
@group(0) @binding(0) var<storage,read_write> outputWords:array<u32>;
|
|
2527
2615
|
@group(0) @binding(1) var<storage,read> edits:array<vec2u>;
|
|
2528
2616
|
@compute @workgroup_size(64) fn applyEdits(@builtin(global_invocation_id) id:vec3u){
|
|
2529
2617
|
let i=id.x+id.y*${t.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
2530
2618
|
if(i>=arrayLength(&edits)){return;}let edit=edits[i];outputWords[edit.x]=edit.y;
|
|
2531
|
-
}`})}}),(!c.group||c.bytes!==m)&&(c.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}},{binding:1,resource:{buffer:c.upload,size:m}}]}),c.bytes=m),i??=t.createCommandEncoder({label:"AVBD property edits"});let
|
|
2619
|
+
}`})}}),(!c.group||c.bytes!==m)&&(c.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}},{binding:1,resource:{buffer:c.upload,size:m}}]}),c.bytes=m),i??=t.createCommandEncoder({label:"AVBD property edits"});let g=i.beginComputePass();g.setPipeline(this.pipeline),g.setBindGroup(0,c.group);let b=Math.ceil(u/64),y=t.limits.maxComputeWorkgroupsPerDimension;g.dispatchWorkgroups(Math.min(b,y),Math.ceil(b/y)),g.end(),r++,o++,a+=m}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 na(s.device)}var Fi=class{constructor(e,t={}){if(this.world=e,this.device=e.device,this.speed=t.speedThreshold??.03,this.delay=t.timeThreshold??.5,t.islands!==void 0&&typeof t.islands!="boolean")throw Error("Sleep islands must be a boolean");if(this.islands=t.islands??(e.gpu.bodyCount>=4096&&e.gpu.jointCount===0&&!e.gpu.portableContactMath&&!e.gpu.dispatchIsolation),!Number.isFinite(this.speed)||this.speed<=0||!Number.isFinite(this.delay)||this.delay<=0)throw Error("Sleep speedThreshold and timeThreshold must be positive and finite");let i=this.device,r=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.state=i.createBuffer({size:e.capacity*32,usage:r}),this.globals=i.createBuffer({size:8,usage:r}),this.params=i.createBuffer({size:48,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let o=i.createShaderModule({code:this.islands?Fl:Gl});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",...this.islands?["connectContacts","connectJoints"]:[],"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,this.islands&&(l[u+5]=u/8)}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,this.islands&&(new Uint32Array(r.buffer)[5]=e);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=this.paramData??=hi(12),{u:r,f:o}=i;r[0]=t.bodyCount,r[1]=t.jointCount,r[2]=t.manifoldCapacity,r[3]=Number(this.wakeRequested),o[4]=this.speed,o[5]=this.delay,o[6]=t.params.dt,o[7]=t.params.gravity,o[8]=t.params.up[0],o[9]=t.params.up[1],o[10]=t.params.up[2],Qt(this.device,this.params,i),this.wakeRequested=!1,this.bindContacts(),this.run([["before",t.bodyCount],["clear",1]],e)}after(e){let t=this.world.gpu;this.bindContacts(),this.run([...this.islands?[["connectContacts",t.manifoldCapacity],["connectJoints",t.jointCount]]:[],["contacts",t.manifoldCapacity],["joints",t.jointCount],["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()}};function Af(s){s=s.replaceAll("bodies[","localBodies["),s=s.replace("var<storage, read_write> bodies: array<Body>;","var<storage, read_write> globalBodies: array<Body>;");let e=s.indexOf(`@compute @workgroup_size(64)
|
|
2620
|
+
fn primal(`),t=s.indexOf(`
|
|
2621
|
+
/**`,e);if(e<0||t<e)throw Error("Review small-group primal integration");let i=s.slice(e,t).replace(`@compute @workgroup_size(64)
|
|
2622
|
+
`,"").replace("fn primal(@builtin(local_invocation_id) lid: vec3u, @builtin(workgroup_id) wid: vec3u)","fn localPrimal(lid: vec3u, wid: vec3u, col: u32)").replaceAll("pc.color","col");return s.slice(0,e)+i+s.slice(t)+`
|
|
2623
|
+
var<workgroup> localBodies: array<Body,32>;
|
|
2624
|
+
var<workgroup> bucketBlocks: u32;
|
|
2625
|
+
@compute @workgroup_size(64)
|
|
2626
|
+
fn smallGroup(@builtin(local_invocation_id) lid: vec3u) {
|
|
2627
|
+
if(lid.x<params.bodyCount){localBodies[lid.x]=globalBodies[lid.x];}
|
|
2628
|
+
workgroupBarrier();
|
|
2629
|
+
for(var it=0u;it<pc.pad0;it++) {
|
|
2630
|
+
for(var col=0u;col<params.colorCap;col++) {
|
|
2631
|
+
if(lid.x==0u){
|
|
2632
|
+
let start=color[params.colorStartOffset+col];
|
|
2633
|
+
let count=color[params.colorStartOffset+col+1u]-start;
|
|
2634
|
+
let perBlock=64u/lanesFor(count);
|
|
2635
|
+
bucketBlocks=(count+perBlock-1u)/perBlock;
|
|
2636
|
+
}
|
|
2637
|
+
let blocks=workgroupUniformLoad(&bucketBlocks);
|
|
2638
|
+
for(var block=0u;block<blocks;block++) {
|
|
2639
|
+
localPrimal(lid,vec3u(block,0u,0u),col);
|
|
2640
|
+
workgroupBarrier();
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
for(var m=lid.x;m<manifoldCount();m+=64u){dualManifold(m);}
|
|
2644
|
+
storageBarrier();
|
|
2645
|
+
workgroupBarrier();
|
|
2646
|
+
}
|
|
2647
|
+
if(lid.x<params.bodyCount){globalBodies[lid.x]=localBodies[lid.x];}
|
|
2648
|
+
}
|
|
2649
|
+
`}function Ul(s){if(!(s.smallGroups!==!1&&s.bodyCount>0&&s.bodyCount<=32&&s.jointCount===0&&s.solverDecision.selected==="standard"&&!s.scalarPrimal&&!s.portableContactMath&&!s.dispatchIsolation)){s.smallGroupPipeline=null;return}let t=`${s.sensorsEnabled?1:0}`;if(s.smallGroupPipelines??=new Map,!s.smallGroupPipelines.has(t)){let i=Af(s.solveSource(Ge,"standard"));s.smallGroupPipelines.set(t,s.device.createComputePipeline({label:"AVBD closed small group",layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.solve,s.layouts.pass]}),compute:{module:s.device.createShaderModule({code:i}),entryPoint:"smallGroup"}}))}s.smallGroupPipeline=s.smallGroupPipelines.get(t)}var aa=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 xr(s,e){let t=It(e);Number.isFinite(t.breakStrain)||Number.isFinite(t.breakForce)?aa.set(s,t):aa.delete(s)}var Ui=s=>aa.get(s);function oo(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
2650
|
if (info[j].x == T_SPRING) {
|
|
2533
2651
|
var material = joints[j];
|
|
2534
2652
|
let strainLimit = material.lamLin.w;
|
|
@@ -2550,7 +2668,7 @@ fn impact(a:u32,b:u32) {
|
|
|
2550
2668
|
}
|
|
2551
2669
|
}
|
|
2552
2670
|
}
|
|
2553
|
-
`)}var
|
|
2671
|
+
`)}var wf=["auto","standard","optimized","points"];function Ar({requested:s="auto",bodyCount:e,constraintCount:t=0,contactScheduling:i="manifolds",custom:r=!1}){if(!wf.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 no=`// Portable hierarchical PLOC: Morton ordering and independent SAH treelets.
|
|
2554
2672
|
// Dispatch boundaries publish each hierarchy level; no cross-workgroup spin waits.
|
|
2555
2673
|
struct Node {
|
|
2556
2674
|
lo: vec3f,
|
|
@@ -2593,6 +2711,17 @@ fn bodyHalf(i: u32) -> vec3f {
|
|
|
2593
2711
|
+ abs(qrotate(b.rot, vec3f(0, 0, 1))) * h.z;
|
|
2594
2712
|
}
|
|
2595
2713
|
|
|
2714
|
+
// Refit only consumes leaf bounds. Scene bounds and their workgroup reductions
|
|
2715
|
+
// are needed when Morton ordering is rebuilt, not on intervening steps.
|
|
2716
|
+
@compute @workgroup_size(128)
|
|
2717
|
+
fn refitLeaves(@builtin(global_invocation_id) gid: vec3u) {
|
|
2718
|
+
let i = gid.x;
|
|
2719
|
+
if (i >= build.count) { return; }
|
|
2720
|
+
let h = bodyHalf(i);
|
|
2721
|
+
let p = inputBodies[i].pos.xyz;
|
|
2722
|
+
nodes[i] = Node(p - h, i, p + h, END);
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2596
2725
|
@compute @workgroup_size(128)
|
|
2597
2726
|
fn leaves(
|
|
2598
2727
|
@builtin(global_invocation_id) gid: vec3u,
|
|
@@ -2806,8 +2935,8 @@ fn ropes(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
2806
2935
|
at = p;
|
|
2807
2936
|
}
|
|
2808
2937
|
links[build.escapeOffset + id] = escape;
|
|
2809
|
-
}
|
|
2810
|
-
`;var
|
|
2938
|
+
}
|
|
2939
|
+
`;var Nl=`// Stable 4-bit GPU radix sort; PrefixScan scans digit-major block counts.
|
|
2811
2940
|
struct SortSettings { count: u32, blocks: u32, shift: u32, _pad: u32 }
|
|
2812
2941
|
@group(0) @binding(0) var<uniform> sort: SortSettings;
|
|
2813
2942
|
@group(0) @binding(1) var<storage, read> input: array<vec2u>;
|
|
@@ -2855,15 +2984,16 @@ fn scatterDigits(
|
|
|
2855
2984
|
}
|
|
2856
2985
|
output[hist[digit * sort.blocks + wid.x] + rank] = value;
|
|
2857
2986
|
}\r
|
|
2858
|
-
`;var
|
|
2859
|
-
fn gridCount`),
|
|
2860
|
-
fn findPairs`);if(
|
|
2861
|
-
${
|
|
2987
|
+
`;var Sf=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,Rt=(s,e=128)=>Math.max(1,Math.ceil(s/e)),kf=4294967295,la=fi.indexOf(`@compute @workgroup_size(64)
|
|
2988
|
+
fn gridCount`),ca=fi.indexOf("/** Binary search"),ql=fi.indexOf(`@compute @workgroup_size(64)
|
|
2989
|
+
fn findPairs`);if(la<0||ca<la||ql<ca)throw Error("Pinned broadphase changed; review H-PLOC integration");var Pf=no.slice(0,no.indexOf("@group(0)")),Lf=(fi.slice(0,la)+fi.slice(ca,ql)).replace("var<storage, read_write> grid: array<atomic<u32>>;","var<storage, read_write> tree: array<Node>;")+`
|
|
2990
|
+
${Pf}
|
|
2862
2991
|
@group(1) @binding(0) var<uniform> build: BuildSettings;
|
|
2863
2992
|
@group(1) @binding(1) var<storage,read> links: array<u32>;
|
|
2864
2993
|
@compute @workgroup_size(64)
|
|
2865
2994
|
fn findTreePairs(@builtin(global_invocation_id) gid:vec3u) {
|
|
2866
2995
|
let i=gid.x; if(i>=params.bodyCount || build.root==0xffffffffu){return;}
|
|
2996
|
+
let collisionFilter=filters[i];if(collisionFilter.x==0u || collisionFilter.y==0u){return;}
|
|
2867
2997
|
let P=probeOf(i);
|
|
2868
2998
|
// Static/kinematic leaves are found from dynamic queries. Traversing a
|
|
2869
2999
|
// scene-wide ground slab would otherwise serialize an entire tree in one lane.
|
|
@@ -2880,11 +3010,52 @@ fn findTreePairs(@builtin(global_invocation_id) gid:vec3u) {
|
|
|
2880
3010
|
at=links[build.escapeOffset+at];
|
|
2881
3011
|
} else {at=node.left;}
|
|
2882
3012
|
}
|
|
2883
|
-
}`,
|
|
3013
|
+
}`,ao=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=Sf())=>{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:ge+no+`
|
|
2884
3014
|
@compute @workgroup_size(128)
|
|
2885
3015
|
fn initializeLinks(@builtin(global_invocation_id) gid:vec3u){if(gid.x<build.escapeOffset){links[gid.x]=0xffffffffu;}}
|
|
2886
|
-
`}),
|
|
2887
|
-
|
|
3016
|
+
`}),a=e.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["leaves","refitLeaves","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:Lf}),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?kf: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,g)=>({binding:g,resource:{buffer:h}}))}),buffer:p}};this.base=l(),this.levels=t.map(d=>({...d,...l(d)})),this.reductions=[];let c=Rt(e),u=0,f=c*2;for(;c>1;){let d=Rt(c);this.reductions.push({blocks:d,...l({inputCount:c,inputOffset:u,outputOffset:f})}),u=f,f+=d*2,c=d}this.morton=l({boundsOffset:u}),this.sortGroups=Array.from({length:8},(d,p)=>{let m=this.uniform([e,this.sortBlocks,p*4,0]);return this.device.createBindGroup({layout:this.sortLayout,entries:[m,this.keys[p%2],this.keys[1-p%2],this.hist].map((h,g)=>({binding:g,resource:{buffer:h}}))})}),this.extra=this.device.createBindGroup({layout:this.extraLayout,entries:[{binding:0,resource:{buffer:this.base.buffer}},{binding:1,resource:{buffer:this.links}}]}),this.broadBuffers=null}encodeBuild(e){let t=(r,o,n)=>{e.setPipeline(this.pipes[r]),e.setBindGroup(0,o),e.dispatchWorkgroups(n)},i=!this.built||this.stepsSinceBuild>=this.rebuildInterval;if(this.lastRebuilt=i,t(i?"leaves":"refitLeaves",this.base.group,Rt(this.count)),i){t("initializeLinks",this.base.group,Rt(this.nodeCapacity));for(let r of this.reductions)t("reduceBounds",r.group,r.blocks);t("mortonCodes",this.morton.group,Rt(this.count));for(let r of this.sortGroups)e.setPipeline(this.sortPipes.countDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks),this.scan.encode(e),e.setPipeline(this.sortPipes.scatterDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks);t("seedClusters",this.base.group,Rt(this.count));for(let r of this.levels)t("miniHploc",r.group,r.blocks);t("ropes",this.base.group,Rt(this.nodeCapacity)),this.built=!0,this.stepsSinceBuild=0,this.stats.builds++}else{for(let r of this.levels)t("refitTreelets",r.group,Rt(r.blocks,64));this.stats.refits++}this.stepsSinceBuild++}encodePairs(e,t){this.configure(t.bodyCount);let i=[t.paramsBuffer,t.bodyBuffer,this.nodes,t.pairBuffer,t.counterBuffer,t.staticBuffer,t.jointBuffer,t.filterBuffer];(!this.broadBuffers||i.some((r,o)=>r!==this.broadBuffers[o]))&&(this.broadBuffers=i,this.broad=this.device.createBindGroup({layout:t.layouts.broad,entries:i.map((r,o)=>({binding:o,resource:{buffer:r}}))})),e.setPipeline(this.traverse),e.setBindGroup(0,this.broad),e.setBindGroup(1,this.extra),e.dispatchWorkgroups(Rt(this.count,64))}destroy(){this.scan.destroy();for(let e of[...this.buffers,...this.uniforms])e.destroy()}};function Vl(s,e={}){let t,i,r=!0,o=new Map,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 mt=s=>Math.ceil(s/64);function Wl(s){(s.radiiDirty||s.noCollideDirty)&&s.uploadStatics();let e=s.params;s.fixedColors&&(s.device.queue.writeBuffer(s.colorBuffer,0,s.fixedColors),s.colorCap=Math.max(1,...s.fixedColors.map(k=>k===255?0:k+1))),s.stepCount++,s.writeParams(),s.writePassConstants(e.iterations,e.alpha);let t=s.parity,i=s.bodyCapacity,r=s.bodyCount,o=s.jointCount,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,m,h,g=(k,P)=>{if(p&&!l&&(m===P||!u))return;p?.end(),m=P;let C=l?d.length:Se.indexOf(P);d.push({name:k,aggregate:P});let _={label:k,timestampWrites:c?{querySet:f.querySet,beginningOfPassWriteIndex:C*2,endOfPassWriteIndex:C*2+1}:void 0};p=s.dispatchIsolation?Vl(a,_):a.beginComputePass(_),h=void 0},b=k=>{h!==k&&(p.setBindGroup(0,k),h=k)},y=(k,P,C)=>{C<=0||(p.setPipeline(s.pipes[k]),b(P),p.dispatchWorkgroups(C))},A=(k,P,C)=>{p.setPipeline(s.pipes[k]),b(P),p.dispatchWorkgroupsIndirect(s.argsBuffer,C*4)};if(g("contactCache","collision"),y("beginFrame",n.broad,1),e.reuseContacts&&y("updateRefs",n.refs,mt(r)),y("argsPrev",n.args,1),A("hashInsert",n.contacts[t],6),g("broadphaseBuild","collision"),s.bvh?(s.bvh.configure(r),s.bvh.encodeBuild(p),h=void 0):(y("gridCount",n.broad,mt(r)),s.gridScan.encode(p),h=void 0,y("gridScatter",n.broad,mt(r))),g("broadphasePairs","collision"),s.bvh?(s.bvh.encodePairs(p,s),h=void 0):y("findPairs",n.broad,mt(r)),g("narrowphase","collision"),y("argsPairs",n.args,1),A("narrowphase",n.contacts[t],0),y("argsContacts",n.args,1),g("adjacency","adjacency"),y("degreeJoints",n.topo[t],mt(o)),A("degreeContacts",n.topo[t],3),s.adjScan.encode(p),h=void 0,y("fillJoints",n.topo[t],mt(o)),A("fillContacts",n.topo[t],3),y("sortAdjacency",n.topo[t],mt(r)),g("coloring","coloring"),!s.fixedColors){y("colorCompact",n.topo[t],mt(r)),y("colorMark",n.topo[t],mt(r));for(let k=0;k<s.colorRounds;k++)y(k%2===0?"colorRoundAB":"colorRoundBA",n.topo[t],mt(r))}y("colorCount",n.topo[t],s.colorGroups),s.colorHistScan.encode(p),h=void 0,y("colorStarts",n.topo[t],1),y("argsColors",n.args,1),y("colorScatter",n.topo[t],s.colorGroups);let v=s.passOffsets??=[0],x,w,B,L=k=>{w===p&&B===k||(v[0]=k*256,p.setBindGroup(1,s.passGroup,v),w=p,B=k)},M=()=>{x!==p&&(p.setBindGroup(0,n.solve[t]),x=p)};if(g("warmStart","solve"),M(),L(0),o>0&&(p.setPipeline(s.pipes.warmStartJoints),p.dispatchWorkgroups(mt(o))),p.setPipeline(s.pipes.warmStartBodies),r&&p.dispatchWorkgroups(mt(r)),s.smallGroupPipeline)g("smallGroupSolve","solve"),M(),L(0),p.setPipeline(s.smallGroupPipeline),p.dispatchWorkgroups(1);else for(let k=0;k<e.iterations;k++){g("bodySolve","solve"),M(),p.setPipeline(s.pipes.primal);for(let P=0;P<s.colorCap;P++)L(k*(s.colorCap+1)+P),p.dispatchWorkgroupsIndirect(s.argsBuffer,(12+3*P)*4);g("contactUpdate","solve"),M(),L(k*(s.colorCap+1)+s.colorCap),p.setPipeline(s.pipes.dual),p.dispatchWorkgroupsIndirect(s.argsBuffer,36)}g("velocities","solve"),M(),L(0),p.setPipeline(s.pipes.updateVelocities),r&&p.dispatchWorkgroups(mt(r)),p.end();let I=2*(l?d.length:Se.length);if(c&&(a.resolveQuerySet(f.querySet,0,I,f.resolve,0),a.copyBufferToBuffer(f.resolve,0,f.read,0,I*8)),s.parity=1-t,s.encodeStepPostlude?.(a),s.device.queue.submit([a.finish()]),!c)return;let E=l?s.detailCallback:s.timingCallback;l?s.detailCallback=null:s.timingCallback=null,s.timingBusy=!0,f.read.mapAsync(GPUMapMode.READ).then(()=>{let k=new BigUint64Array(f.read.getMappedRange()).slice();if(f.read.unmap(),s.timingBusy=!1,s.destroyed){s.releaseAppTiming(),s.releaseTiming();return}let P=(_,z)=>Number(k[z]-k[_])/1e6,C={total:P(0,I-1),broadphase:s.broadphase,bvhRebuilt:s.bvh?.lastRebuilt??!1};if(l){C.details={};for(let[_,z]of d.entries()){let R=P(_*2,_*2+1);C[z.aggregate]=(C[z.aggregate]??0)+R,C.details[z.name]=(C.details[z.name]??0)+R}}else Se.forEach((_,z)=>C[_]=P(z*2,z*2+1));E(C)},k=>{s.timingBusy=!1,s.profileErrors.push(k.message),s.destroyed&&(s.releaseAppTiming(),s.releaseTiming())})}function lo(s){let e=s.indexOf(`@compute @workgroup_size(256)
|
|
3017
|
+
fn colorCount(`),t=s.indexOf(`
|
|
3018
|
+
// 5.`,e);if(e<0||t<e)throw Error("Pinned color buckets changed; review the bitset rank kernel");return s.slice(0,e).replace(`var<workgroup> hist: array<atomic<u32>, 64>;
|
|
3019
|
+
var<workgroup> chunkColors: array<u32, 256>;
|
|
3020
|
+
`,"")+`
|
|
3021
|
+
var<workgroup> colorBits:array<atomic<u32>,512>;
|
|
3022
|
+
@compute @workgroup_size(256)
|
|
3023
|
+
fn colorCount(@builtin(global_invocation_id) gid:vec3u,
|
|
3024
|
+
@builtin(local_invocation_id) lid:vec3u,
|
|
3025
|
+
@builtin(workgroup_id) wid:vec3u) {
|
|
3026
|
+
atomicStore(&colorBits[lid.x],0u);
|
|
3027
|
+
atomicStore(&colorBits[lid.x+256u],0u);
|
|
3028
|
+
workgroupBarrier();
|
|
3029
|
+
let b=gid.x;
|
|
3030
|
+
var col=NO_COLOR;
|
|
3031
|
+
if(b<params.bodyCount && dynamicBody(i32(b))) {
|
|
3032
|
+
let s=state(0u,b);
|
|
3033
|
+
col=s&0xffu;
|
|
3034
|
+
if((s&PENDING)!=0u){atomicAdd(&counters[C_CLASHES],1u);}
|
|
3035
|
+
if (col == NO_COLOR) { col = params.colorCap - 1u; }
|
|
3036
|
+
atomicStore(&color[b],col);
|
|
3037
|
+
atomicMax(&counters[C_NUM_COLORS],col+1u);
|
|
3038
|
+
}
|
|
3039
|
+
let word=lid.x>>5u;
|
|
3040
|
+
let bit=lid.x&31u;
|
|
3041
|
+
if(col!=NO_COLOR){atomicOr(&colorBits[col*8u+word],1u<<bit);}
|
|
3042
|
+
workgroupBarrier();
|
|
3043
|
+
if(col!=NO_COLOR){
|
|
3044
|
+
var rank=0u;
|
|
3045
|
+
for(var k=0u;k<word;k++){rank+=countOneBits(atomicLoad(&colorBits[col*8u+k]));}
|
|
3046
|
+
rank+=countOneBits(atomicLoad(&colorBits[col*8u+word])&((1u<<bit)-1u));
|
|
3047
|
+
atomicStore(&color[params.stateBOffset+b],rank);
|
|
3048
|
+
}
|
|
3049
|
+
if(lid.x<MAX_COLORS){
|
|
3050
|
+
var count=0u;
|
|
3051
|
+
for(var k=0u;k<8u;k++){count+=countOneBits(atomicLoad(&colorBits[lid.x*8u+k]));}
|
|
3052
|
+
atomicStore(&color[params.colorHistOffset+lid.x*params.colorGroups+wid.x],count);
|
|
3053
|
+
}
|
|
3054
|
+
if(gid.x==0u){atomicStore(&color[params.colorHistOffset+MAX_COLORS*params.colorGroups],0u);}
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
`+s.slice(t)}function co(s,e){let t=s.bodyCount>=1024;if(s.bitsetColorBuckets===t)return;let i=s.colorBucketPipelines??={scan:s.pipes.colorCount};t&&!i.bitset&&(i.bitset=s.device.createComputePipeline({label:"Stable bitset color buckets",layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.topo]}),compute:{module:s.device.createShaderModule({code:e}),entryPoint:"colorCount"}})),s.pipes.colorCount=t?i.bitset:i.scan,s.bitsetColorBuckets=t}var Hl=` let list = params.adjListOffset;
|
|
3058
|
+
for (var i = lo + 1u; i < hi; i++) {`,ua=Ti(ge,yr),$l=lo(ua);if(ua.split(Hl).length!==2)throw Error("Pinned adjacency sort changed; review the cached-key variant");var Jl=ua.replace(Hl,`
|
|
2888
3059
|
let list = params.adjListOffset;
|
|
2889
3060
|
let count = hi - lo;
|
|
2890
3061
|
if (count <= 16u) {
|
|
@@ -2913,13 +3084,13 @@ fn initializeLinks(@builtin(global_invocation_id) gid:vec3u){if(gid.x<build.esca
|
|
|
2913
3084
|
}
|
|
2914
3085
|
return;
|
|
2915
3086
|
}
|
|
2916
|
-
for (var i = lo + 1u; i < hi; i++) {`);function
|
|
2917
|
-
if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function
|
|
3087
|
+
for (var i = lo + 1u; i < hi; i++) {`);function fa(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 uo(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 da(s=Ti(ge,yr)){return s.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
|
|
3088
|
+
if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function ha(s){let e=s.device,t=e.createShaderModule({code:`
|
|
2918
3089
|
@group(0) @binding(0) var<storage,read_write> records:array<vec4f>;
|
|
2919
3090
|
@compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){
|
|
2920
3091
|
if(id.x*10u+2u>=arrayLength(&records)){return;}
|
|
2921
3092
|
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:
|
|
3093
|
+
}`}),i=e.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"clear"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:s.bodyBuffer}}]}),o=e.createCommandEncoder(),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 _f=`
|
|
2923
3094
|
fn contactGeneration(value: f32) -> u32 {
|
|
2924
3095
|
return select(0u, bitcast<u32>(value) ^ 0x3f800000u, value != 0.0);
|
|
2925
3096
|
}
|
|
@@ -2927,9 +3098,9 @@ fn contactMovedAfter(value: f32, generated: u32) -> bool {
|
|
|
2927
3098
|
let age = (contactGeneration(value) - generated) & 0x07ffffffu;
|
|
2928
3099
|
return age != 0u && age < 0x04000000u;
|
|
2929
3100
|
}
|
|
2930
|
-
`;function
|
|
2931
|
-
if (count == 0u || generated < moved) { return false; }`;if(!
|
|
2932
|
-
if (count == 0u || contactMovedAfter(bodies[a].inertialPos.w, generation) || contactMovedAfter(bodies[b].inertialPos.w, generation)) { return false; }`)}var
|
|
3101
|
+
`;function Xl(s){let e="bodies[i].inertialPos.w = bitcast<f32>(params.step);";if(!s.includes(e))throw Error("Contact reference layout changed");return s.replace(e,"bodies[i].inertialPos.w = bitcast<f32>((params.step & 0x07ffffffu) ^ 0x3f800000u);")}function Yl(s){let e=` let moved = max(bitcast<u32>(bodies[a].inertialPos.w), bitcast<u32>(bodies[b].inertialPos.w));
|
|
3102
|
+
if (count == 0u || generated < moved) { return false; }`;if(!s.includes(e))throw Error("Contact cache layout changed");return _f+s.replace(e,` let generation = generated & 0x07ffffffu;
|
|
3103
|
+
if (count == 0u || contactMovedAfter(bodies[a].inertialPos.w, generation) || contactMovedAfter(bodies[b].inertialPos.w, generation)) { return false; }`)}var Ef=["captureIncoming"],Mf=["captureSolved","initialize",...Array.from({length:12},()=>["pairs","apply"]).flat(),"writeBack"],zf=`${ge}
|
|
2933
3104
|
struct Config { count:u32, joints:u32, capacity:u32, list:u32, threshold:f32, dt:f32, pad0:u32, pad1:u32 }
|
|
2934
3105
|
struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
|
|
2935
3106
|
struct Bounce { impulse:f32, delta:f32, reboundSpeed:f32, inverseMass:f32, rA:vec4f, rB:vec4f }
|
|
@@ -3005,7 +3176,7 @@ fn pointVelocity(i:u32,r:vec3f,incoming:bool)->vec3f {
|
|
|
3005
3176
|
let i=id.x;if(i>=cfg.count || bodies[i].size.w<=0.){return;}
|
|
3006
3177
|
bodies[i].vel=velocities[i].linear;bodies[i].angVel=velocities[i].angular;
|
|
3007
3178
|
}
|
|
3008
|
-
|
|
3179
|
+
`;var wr=class{constructor(e,{code:t=zf,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((c,u)=>c!==this.buffers[u]))&&(this.buffers=o,this.group=t.createBindGroup({layout:this.layout,entries:o.map((c,u)=>({binding:u,resource:{buffer:c}}))}));let n=this.paramData??=hi(8),{u:a,f:l}=n;a[0]=e.bodyCount,a[1]=e.jointCount,a[2]=r,a[3]=2*e.bodyCapacity+1,l[4]=1,l[5]=e.params.dt,Qt(t,this.params,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(Ef,e)}after(e){this.active.size&&this.run(Mf,e)}destroy(){for(let e of["materials","velocities","params","bounce"])this[e]?.destroy()}};var Ql=new WeakMap,fs=s=>Ql.get(s);function Br(s,e){Ql.set(s,e)}var pa=(s,e,t)=>{if(s.split(e).length!==2)throw Error("Pinned angular solver layout changed");return s.replace(e,t)};function ma(s){return s=pa(s,"fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {",`
|
|
3009
3180
|
fn angularFrame(k:Joint,a:i32,b:i32)->mat3x3f {
|
|
3010
3181
|
let qa=rotA(a);let axis=qrotate(qa,k.rA.xyz);
|
|
3011
3182
|
let ta=qrotate(qa,k.c0Lin.xyz);let tb=qrotate(bodies[b].rot,k.c0Ang.xyz);
|
|
@@ -3056,10 +3227,10 @@ fn dualAngular(j:u32){
|
|
|
3056
3227
|
}
|
|
3057
3228
|
`+`
|
|
3058
3229
|
fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {
|
|
3059
|
-
if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),
|
|
3230
|
+
if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),s=pa(s," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if(j>=params.jointCount){return;}
|
|
3060
3231
|
if(info[j].x==3 || info[j].x==4){warmAngular(j);return;}
|
|
3061
|
-
if(info[j].x!=T_JOINT){return;}`),
|
|
3062
|
-
if(info[j].x==3 || info[j].x==4){dualAngular(j);return;}`)}function
|
|
3232
|
+
if(info[j].x!=T_JOINT){return;}`),pa(s,"fn dualJoint(j: u32) {",`fn dualJoint(j: u32) {
|
|
3233
|
+
if(info[j].x==3 || info[j].x==4){dualAngular(j);return;}`)}function kt({type:s,axisA:e=[0,1,0],axisB:t=[0,1,0],speed:i=1,maxTorque:r=100,minAngle:o=-Math.PI,maxAngle: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 Kl=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
3063
3234
|
struct Joint {penLin:vec4f,penAng:vec4f,lamLin:vec4f,lamAng:vec4f,c0Lin:vec4f,c0Ang:vec4f,rA:vec4f,rB:vec4f}
|
|
3064
3235
|
@group(0) @binding(0) var<storage,read> bodies:array<Body>;
|
|
3065
3236
|
@group(0) @binding(1) var<storage,read_write> joints:array<Joint>;
|
|
@@ -3077,7 +3248,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f{let t=2.*cross(q.xyz,v);return v+q.w*t+cross(q
|
|
|
3077
3248
|
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));}
|
|
3078
3249
|
joints[j].c0Lin=vec4f(tangent,0.);joints[j].c0Ang=vec4f(normalize(tangentB),0.);
|
|
3079
3250
|
}
|
|
3080
|
-
`;var
|
|
3251
|
+
`;var Zl=new WeakSet,Fe=s=>Zl.has(s);function Cr(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,Zl.add(n),n}var ec=`// Exact rounded capsule geometry. Shape -1: radius=size.x/2, segment along Y,
|
|
3081
3252
|
// half-length=size.y/2-radius. No tessellation, endpoint-only collision test,
|
|
3082
3253
|
// shape storage buffer or iterative support search for capsule/sphere/box.
|
|
3083
3254
|
struct Capsule {a:vec3f,b:vec3f,r:f32}
|
|
@@ -3128,7 +3299,12 @@ fn capSegmentBox(a:vec3f,b:vec3f,h:vec3f)->SegmentPair{
|
|
|
3128
3299
|
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.;
|
|
3129
3300
|
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];}}
|
|
3130
3301
|
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);
|
|
3131
|
-
|
|
3302
|
+
// An exact segment/box intersection must win over a nearby exterior
|
|
3303
|
+
// sample. A distance tie alone can select roundoff-sized separation and
|
|
3304
|
+
// normalize it into an arbitrary contact normal.
|
|
3305
|
+
let exactIntersection=dist==0.;let previousIntersection=best==0.;
|
|
3306
|
+
if((exactIntersection&&!previousIntersection)||dist<best-1.e-12 ||
|
|
3307
|
+
(exactIntersection==previousIntersection&&abs(dist-best)<=1.e-12&&abs(t-.5)<abs(bestT-.5))){best=dist;bestT=t;result=SegmentPair(point,box);}
|
|
3132
3308
|
}
|
|
3133
3309
|
return result;
|
|
3134
3310
|
}
|
|
@@ -3156,16 +3332,16 @@ fn capsuleBox(A:Capsule,B:Box,q:vec4f,sat:ptr<function,Sat>)->Found{
|
|
|
3156
3332
|
if(ends.count==2u){return ends;}
|
|
3157
3333
|
addFound(&found,B.c+qrotate(q,center)+n*A.r,B.c+qrotate(q,onBox),4u<<24u);return found;
|
|
3158
3334
|
}
|
|
3159
|
-
`;function
|
|
3160
|
-
fn narrowphase`;if(!
|
|
3161
|
-
`+e)),
|
|
3162
|
-
`+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
|
|
3335
|
+
`;function ga(s){if(s.includes("fn capsuleOf"))throw Error("Capsule collision kernel installed twice");let e=`@compute @workgroup_size(64)
|
|
3336
|
+
fn narrowphase`;if(!s.includes(e))throw Error("Pinned capsule narrowphase layout changed");let t=s.includes("struct Poly");t&&(s=s.replace(e,Of+`
|
|
3337
|
+
`+e)),s=s.replace(e,ec+`
|
|
3338
|
+
`+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.;
|
|
3163
3339
|
if(capsuleA || capsuleB){
|
|
3164
3340
|
if(capsuleA && capsuleB){found=capsuleCapsule(capsuleOf(a),capsuleOf(b),&sat);}
|
|
3165
3341
|
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);}}
|
|
3166
3342
|
else{if(sphereA){found=capsuleSphere(capsuleOf(b),A.c,A.h.x,&sat);}${r} else{found=capsuleBox(capsuleOf(b),A,bodies[a].rot,&sat);}
|
|
3167
3343
|
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;}}
|
|
3168
|
-
} else if (sphereA && sphereB) {`)}var
|
|
3344
|
+
} else if (sphereA && sphereB) {`)}var Of=`
|
|
3169
3345
|
fn capPolyAxis(A:Capsule,P:Poly,axis:vec3f,best:ptr<function,f32>,normal:ptr<function,vec3f>)->bool{
|
|
3170
3346
|
let l=length(axis);if(l<1.e-8){return true;}let n=axis/l;
|
|
3171
3347
|
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;
|
|
@@ -3197,7 +3373,7 @@ fn capsulePoly(A:Capsule,P:Poly,sat:ptr<function,Sat>)->Found{
|
|
|
3197
3373
|
}
|
|
3198
3374
|
(*sat).n=n;addFound(&found,points.a+n*A.r,points.b,4u<<24u);return found;
|
|
3199
3375
|
}
|
|
3200
|
-
`;var
|
|
3376
|
+
`;var ba=`// GPU queries against current poses. Rounded box/hull sweeps test offset faces
|
|
3201
3377
|
// and rounded edges, so expanding a bounding box cannot create corner hits.
|
|
3202
3378
|
struct Query { origin:vec4f, direction:vec4f }
|
|
3203
3379
|
struct Params { head:vec4u, flags:vec4u, ignored:array<vec4u,4> }
|
|
@@ -3351,7 +3527,42 @@ fn nearest(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.x||igno
|
|
|
3351
3527
|
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);}}
|
|
3352
3528
|
@compute @workgroup_size(64)
|
|
3353
3529
|
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.);}
|
|
3354
|
-
`;var
|
|
3530
|
+
`;var pi=class{constructor(e,t=3){this.solver=e,this.dimension=t,this.jobs=new Set,this.idle=[],this.destroyed=!1;let i=e.device,r=i.createShaderModule({label:`AVBD ${t}D queries`,code:t===2?ba.replace("const DIM=3u;","const DIM=2u;").replace("const STRIDE=10u;","const STRIDE=6u;"):ba});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),m=Array.from(d.direction),h=d.radius??0,g=d.maxDistance??o??3e38;if(p.length!==l||m.length!==l||![...p,...m].every(Number.isFinite)||Math.hypot(...m)===0)throw Error(`A query requires ${l} finite components and a nonzero direction`);if(!Number.isFinite(h)||h<0||!Number.isFinite(g)||g<0)throw Error("Query radius and maximum distance must be finite and nonnegative");let b=Math.hypot(...m);return{origin:p,direction:m.map(y=>y/b),radius:h,maxDistance:g}});if(!c.length)return[];let u=c.length,f=this.acquire(u);try{let d=f.data;c.forEach((I,E)=>{d.set([...I.origin,...Array(3-l).fill(0),I.maxDistance,...I.direction,...Array(3-l).fill(0),I.radius],E*8)});let p=f.params;p.fill(0),p.set([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 m=f.initial;for(let I=0;I<u;I++)m.set([2139095040,4294967295],I*2);let{queryBuffer:h,best:g,hits:b,uniform:y,read:A}=f;a.queue.writeBuffer(h,0,d.buffer,0,u*32),a.queue.writeBuffer(g,0,m.buffer,0,u*8),a.queue.writeBuffer(y,0,p);let v=[n.bodyBuffer,l===3?n.hullStorage:n.shapeBuffer??this.dummy,h,g,b,y,l===3?n.filterStorage:n.filters??this.dummy];(!f.buffers||v.some((I,E)=>I!==f.buffers[E]))&&(f.group=a.createBindGroup({layout:this.layout,entries:v.map((I,E)=>({binding:E,resource:{buffer:I}}))}),f.buffers=v);let x=a.createCommandEncoder(),w=x.beginComputePass();w.setBindGroup(0,f.group);for(let I of["nearest","owner"])w.setPipeline(this.pipes[I]),w.dispatchWorkgroups(Math.ceil(n.bodyCount/64),u);w.setPipeline(this.pipes.finish),w.dispatchWorkgroups(Math.ceil(u/64)),w.end(),x.copyBufferToBuffer(b,0,A,0,u*32),a.queue.submit([x.finish()]),await A.mapAsync(GPUMapMode.READ,0,u*32);let B=A.getMappedRange(0,u*32),L=new Float32Array(B),M=new Uint32Array(B);return c.map((I,E)=>{let k=L[E*8+3];if(k<0)return null;let P=Array.from(L.slice(E*8,E*8+l)),C=I.origin.map((_,z)=>_+k*I.direction[z]);return{index:M[E*8+4],distance:k,normal:P,center:C,point:C.map((_,z)=>_-I.radius*P[z])}})}finally{this.release(f)}}destroy(){this.destroyed=!0;for(let e of this.jobs)for(let t of["queryBuffer","best","hits","uniform","read"])e[t].destroy();this.jobs.clear(),this.idle.length=0,this.dummy.destroy()}};function fo(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)),t.u32[2]=s;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.u32[c+2]=s,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}function oc(s,e){return s.stepParamsCache??=hi(e)}function nc(s,e,t){let i=s.bodyCapacity;t[7]=s.bodyCount,t[8]=s.jointCount,t[9]=s.colorCap,e[10]=s.cellSize,t[11]=s.tableSize-1,e[12]=s.maxSmallRadius,t[13]=s.largeCount,t[14]=s.noCollideCount,t[15]=s.pairCapacity,t[16]=s.contactCapacity,t[17]=s.hashSize-1,t[18]=i+1,t[19]=2*i+1,t[20]=s.tableSize+1,t[21]=2*s.tableSize+1,t[22]=2*s.tableSize+1+i,t[23]=i,t[24]=s.colorHistOffset,t[25]=2*i,t[26]=s.colorGroups,t[27]=2*i+65,t[28]=s.colorRounds}function ac(s,e){let t=oc(this,32),{f:i,u:r}=t,o=this.params;i[0]=o.dt,i[1]=o.gravity,i[2]=o.beta,i[3]=o.gamma,i[4]=s,i[5]=o.vbdStiffness,r[6]=(o.vbd?1:0)|(o.stiffnessRescale?2:0)|(e?4:0)|(o.matchNearest?8:0),nc(this,i,r),Qt(this.device,this.paramsBuffer,t)}function lc(){let s=oc(this,ls),{f:e,u:t}=s,i=this.params;e[0]=i.dt,e[1]=i.gravity,e[2]=i.betaLin,e[3]=i.betaAng,e[4]=i.gamma,e[5]=i.alpha,t[6]=(i.matchNearest?rs:0)|(i.faceBias?ss:0)|(i.reuseContacts?os:0)|(i.startAtRest?ns:0)|(i.massPenalty?as:0),nc(this,e,t),t[29]=this.manifoldCapacity,t[30]=this.stepCount;let r=o=>Math.min(31,Math.max(0,Math.round(Math.log2(o))));t[31]=r(this.primalLanes[0])|r(this.primalLanes[1])<<8|r(this.primalLanes[2])<<16,e[32]=i.windSpeed*Math.cos(i.windAngle),e[33]=i.windSpeed*Math.sin(i.windAngle),e[34]=0,e[35]=i.windPressure,e[36]=i.windGust,e[40]=i.up[0],e[41]=i.up[1],e[42]=i.up[2],e[43]=0,Qt(this.device,this.paramsBuffer,s)}function cc(s){let e=(i,r)=>{if(s.split(i).length!==2)throw Error("Review packed Newton accumulator integration");s=s.replace(i,r)};e(` lin: mat3x3f,
|
|
3531
|
+
ang: mat3x3f,`,` lin: mat2x3f,
|
|
3532
|
+
ang: mat2x3f,`),e("fn addRow(acc:",`fn symmetricOuter(v: vec3f) -> mat2x3f {
|
|
3533
|
+
return mat2x3f(v * v, vec3f(v.y * v.x, v.z * v.x, v.z * v.y));
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
fn addRow(acc:`),e("outer(l, l) * k","symmetricOuter(l) * k"),e("outer(a, a) * k","symmetricOuter(a) * k"),e("mat3x3f(vec3f(g.x, 0.0, 0.0), vec3f(0.0, g.y, 0.0), vec3f(0.0, 0.0, g.z))","mat2x3f(g, vec3f(0.0))"),e("mat3x3f(vec3f(m, 0.0, 0.0), vec3f(0.0, m, 0.0), vec3f(0.0, 0.0, m))","mat2x3f(vec3f(m), vec3f(0.0))"),e("mat3x3f(vec3f(I.x, 0.0, 0.0), vec3f(0.0, I.y, 0.0), vec3f(0.0, 0.0, I.z))","mat2x3f(I, vec3f(0.0))");let t={"0][0":"0][0","0][1":"1][0","0][2":"1][1","1][1":"0][1","1][2":"1][2","2][2":"0][2"};return s=s.replace(/acc\.(lin|ang)\[(\d)\]\[(\d)\]/g,(i,r,o,n)=>{let a=t[`${o}][${n}`];if(!a)throw Error("Unexpected Newton accumulator entry");return`acc.${r}[${a}]`}),s}var va=["pos","rot","initialPos","initialRot","size","moment","inertialPos","inertialRot","vel","angVel"],jf=/bodies\[([^\]]+)\]\.([A-Za-z]+)/g;function Df(s){let e=new Map;for(let t of s.matchAll(/\bfn (\w+)\(/g)){let i=t.index,r=s.indexOf("{",i),o=1,n=r+1;for(;o&&n<s.length;n++)s[n]==="{"&&o++,s[n]==="}"&&o--;if(o)throw Error("Review compact body function boundaries");e.set(t[1],{start:i,brace:r,end:n,text:s.slice(i,n),body:s.slice(r+1,n-1)})}return e}function xa(s){let e=Df(s),t=m=>{let h=e.get(m);if(!h)throw Error(`Review compact body integration: ${m}`);return h},i=(m,h)=>m.replace(jf,(g,b,y)=>{let A=va.indexOf(y);if(A<0)throw Error(`Review compact body field: ${y}`);return h?`bodies[compactBodySlot(u32(${b}), ${A}u)]`:`bodies[u32(${b}) * 10u + ${A}u]`}),r=m=>[...m.matchAll(/\b(\w+)\s*\(/g)].map(h=>h[1]).filter(h=>e.has(h)),o=new Set([...e].filter(([,m])=>m.body.includes("bodies[")).map(([m])=>m)),n;do{n=!1;for(let[m,h]of e)!o.has(m)&&r(h.body).some(g=>o.has(g))&&(o.add(m),n=!0)}while(n);let a=new Set,l=m=>{for(let h of r(t(m).body))!o.has(h)||a.has(h)||(a.add(h),l(h))};l("warmStartJoints"),l("warmStartBodies");let c=m=>m.replace(/\b(\w+)(?=\s*\()/g,h=>a.has(h)?`${h}BodyRecords`:h),u=[...a].map(m=>c(i(t(m).text,!1))).join(`
|
|
3537
|
+
|
|
3538
|
+
`),f=(m,h)=>{let g=t(m).body;for(let y of[" if (!solverStepValid()) { return; }"," let i = gid.x;"," if (i >= params.bodyCount) { return; }"]){if(g.split(y).length!==2)throw Error(`Review compact body entry: ${m}`);g=g.replace(y,"")}g=g.replace(/bodies\[i\]\.([A-Za-z]+)/g,"value.$1"),h||(g=c(g));let b=(y,A)=>A?`compactBodySlot(i, ${y}u)`:`i * 10u + ${y}u`;return`fn ${m}(@builtin(global_invocation_id) gid: vec3u) {
|
|
3539
|
+
let i = gid.x;
|
|
3540
|
+
let allocated = i < params.stateBOffset;
|
|
3541
|
+
var value: Body;
|
|
3542
|
+
if (allocated) { value = Body(${va.map((y,A)=>`bodies[${b(A,h)}]`).join(", ")}); }
|
|
3543
|
+
if (i < params.bodyCount && solverStepValid()) { ${g} }
|
|
3544
|
+
// All threads finish reading their tile before any thread overwrites it.
|
|
3545
|
+
// Inactive and unused records participate, including a partial final tile.
|
|
3546
|
+
storageBarrier();
|
|
3547
|
+
if (allocated) { ${va.map((y,A)=>`bodies[${b(A,!h)}] = value.${y};`).join(`
|
|
3548
|
+
`)} }
|
|
3549
|
+
}`},d=[[t("warmStartJoints"),c(i(t("warmStartJoints").text,!1))],[t("warmStartBodies"),f("warmStartBodies",!1)],[t("updateVelocities"),f("updateVelocities",!0)]].sort(([m],[h])=>h.start-m.start);for(let[m,h]of d)s=s.slice(0,m.start)+h+s.slice(m.end);let p="var<storage, read_write> bodies: array<Body>;";if(s.split(p).length!==2)throw Error("Review compact body storage binding");return s=s.replace(p,"var<storage, read_write> bodies: array<vec4f>;"),i(s,!0)+`
|
|
3550
|
+
${u}
|
|
3551
|
+
fn compactBodySlot(i: u32, field: u32) -> u32 {
|
|
3552
|
+
let tile = i / 64u * 64u;
|
|
3553
|
+
let width = min(64u, params.stateBOffset - tile);
|
|
3554
|
+
if (field < 4u) { return tile * 10u + (i % 64u) * 4u + field; }
|
|
3555
|
+
return tile * 10u + width * 4u + (i % 64u) * 6u + field - 4u;
|
|
3556
|
+
}
|
|
3557
|
+
`}var ho=256;function uc(s){let e=(t,i)=>{if(s.split(t).length!==2)throw Error("Review parallel dual integration");s=s.replace(t,i)};return e("fn dualManifold(m: u32) {","fn dualManifold(m: u32, lane: u32) {"),e("for (var c = mf.ids.z; c < end; c++) {","for (var c = mf.ids.z + lane; c < end; c += 4u) {"),e(` let id = gid.x;
|
|
3558
|
+
if (id < params.jointCount) { dualJoint(id); }
|
|
3559
|
+
else if (id - params.jointCount < manifoldCount()) { dualManifold(id - params.jointCount); }`,` let id = (gid.x + gid.y * ${ho*64}u) / 4u;
|
|
3560
|
+
let lane = gid.x % 4u;
|
|
3561
|
+
if (id < params.jointCount) { if (lane == 0u) { dualJoint(id); } }
|
|
3562
|
+
else if (id - params.jointCount < manifoldCount()) { dualManifold(id - params.jointCount, lane); }`),s}function Aa(s,e){let t=s.dualArgPipelines??={serial:s.pipes.argsContacts};if(e&&!t.parallel){let i=Yr(ge,Zs),r="setArgs(IA_CONSTRAINTS, params.jointCount + items);";if(i.split(r).length!==2)throw Error("Review parallel contact dispatch sizing");let o=i.replace(r,`let blocks = groupsFor((params.jointCount + items) * 4u);
|
|
3563
|
+
args[IA_CONSTRAINTS] = min(blocks, ${ho}u);
|
|
3564
|
+
args[IA_CONSTRAINTS + 1u] = max(1u, (blocks + ${ho-1}u) / ${ho}u);
|
|
3565
|
+
args[IA_CONSTRAINTS + 2u] = 1u;`);t.parallel=s.device.createComputePipeline({layout:s.device.createPipelineLayout({bindGroupLayouts:[s.layouts.args]}),compute:{module:s.device.createShaderModule({code:o}),entryPoint:"argsContacts"}})}s.pipes.argsContacts=e?t.parallel:t.serial,s.parallelDual=e}var wa=new WeakMap;function po(s){return wa.get(s)}function fc(s,e){e?wa.set(s,Object.freeze({...e})):wa.delete(s)}function Ba(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)+`
|
|
3355
3566
|
@compute @workgroup_size(64)
|
|
3356
3567
|
fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
3357
3568
|
let start = color[params.colorStartOffset + pc.color];
|
|
@@ -3361,18 +3572,18 @@ fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3361
3572
|
finishBody(i, accumulate(i, 0u, 1u));
|
|
3362
3573
|
}
|
|
3363
3574
|
|
|
3364
|
-
`+
|
|
3575
|
+
`+s.slice(i)}function mi(s,e,t){if(s.split(e).length!==2)throw Error("3D contact layout changed; review the compatibility kernel");return s.replace(e,t)}function mo(s,e,t){let i=new RegExp(`struct ${e} \\{[^}]*\\}`,"g");if([...s.matchAll(i)].length!==1)throw Error(`Expected one 3D ${e} layout`);return s.replace(i,t)}function dc(s,e,t){let i=`fn ${e}(`;if(s.split(i).length!==2)throw Error(`Expected one 3D ${e} function`);let r=s.indexOf(i),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 Ca(s){s=mo(s,"PairBody",`struct PairBody {
|
|
3365
3576
|
ids: vec4u,
|
|
3366
3577
|
rot: vec4f,
|
|
3367
3578
|
dLin: vec4f,
|
|
3368
3579
|
dAng: vec4f,
|
|
3369
|
-
}`),
|
|
3580
|
+
}`),s=mi(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=mo(s,"Contact",`struct Contact {
|
|
3370
3581
|
anchorA: vec4u, // xyz: float bits, w: integer feature key
|
|
3371
3582
|
anchorB: vec4f, // xyz: anchor, w: normal error
|
|
3372
3583
|
penalty: vec4f, // xyz: penalties, w: first tangent error
|
|
3373
3584
|
force: vec4f, // xyz: forces, w: second tangent error
|
|
3374
|
-
}`);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"])
|
|
3375
|
-
`,""),t=
|
|
3585
|
+
}`);let e={rA:"bitcast<vec3f>(k.anchorA.xyz)",rB:"k.anchorB.xyz",key:"k.anchorA.w",c0x:"k.anchorB.w",c0y:"k.penalty.w",c0z:"k.force.w",pen:"k.penalty.xyz",lam:"k.force.xyz"};for(let t of["evalContact","accumulate","dualManifold"])s=dc(s,t,i=>i.replace(/\bk\.(rA|rB|key|c0x|c0y|c0z|pen|lam)\b/g,(r,o)=>e[o]));return s=mi(s,"contacts[c].lam = e.F;","contacts[c].force = vec4f(e.F, k.force.w);"),s=mi(s,"contacts[c].key =","contacts[c].anchorA.w ="),s=mi(s,"contacts[c].pen = pen;","contacts[c].penalty = vec4f(pen, k.penalty.w);"),s=dc(s,"evalContact",t=>{t=mi(t,` var e: ContactEval;
|
|
3586
|
+
`,""),t=mi(t," e.rAW ="," let rAW ="),t=mi(t," e.rBW ="," let rBW ="),t=t.replaceAll("e.rAW","rAW").replaceAll("e.rBW","rBW"),t=mi(t," e.C ="," let C =");let i=t.indexOf(" var F ="),r=t.indexOf(" return e;",i);if(i<0||r<0)throw Error("Expected 3D contact force projection");return t.slice(0,i)+` let raw = k.penalty.xyz * C + k.force.xyz;
|
|
3376
3587
|
let normalForce = select(0.0, raw.x, raw.x < 0.0);
|
|
3377
3588
|
let bounds = abs(normalForce) * friction;
|
|
3378
3589
|
let frictionScale = length(raw.yz);
|
|
@@ -3383,23 +3594,23 @@ fn primal(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3383
3594
|
let force = vec3f(normalForce, raw.yz * scale);
|
|
3384
3595
|
return ContactEval(vec4f(rAW, 0.0), vec4f(rBW, 0.0),
|
|
3385
3596
|
vec4f(C, 0.0), vec4f(force, 0.0), vec4f(frictionScale, bounds, 0.0, 0.0));
|
|
3386
|
-
}`}),
|
|
3597
|
+
}`}),s=mo(s,"ContactEval",`struct ContactEval {
|
|
3387
3598
|
rAW: vec4f,
|
|
3388
3599
|
rBW: vec4f,
|
|
3389
3600
|
C: vec4f,
|
|
3390
3601
|
F: vec4f,
|
|
3391
3602
|
limits: vec4f, // x: tangent force length, y: Coulomb bound
|
|
3392
|
-
}`),
|
|
3393
|
-
fn narrowphase(@builtin(global_invocation_id) gid: vec3u)`;if(
|
|
3603
|
+
}`),s=s.replace(/\b(ev|e)\.(rAW|rBW|C|F)\b/g,"$1.$2.xyz").replace(/\be\.frictionScale\b/g,"e.limits.x").replace(/\be\.bounds\b/g,"e.limits.y"),s=mo(s,"PassConstants","struct PassConstants { data: vec4u, }"),s.replaceAll("pc.color","pc.data.x").replaceAll("pc.alpha","bitcast<f32>(pc.data.y)")}function hc(s){if(!s.includes("struct Poly"))return s;let e=`@compute @workgroup_size(64)
|
|
3604
|
+
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)")+`
|
|
3394
3605
|
@compute @workgroup_size(32)
|
|
3395
3606
|
fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
3396
3607
|
for (var offset = 0u; offset < 2u; offset++) {
|
|
3397
3608
|
narrowphasePair(vec3u(gid.x * 2u + offset, 0u, 0u));
|
|
3398
3609
|
}
|
|
3399
3610
|
}
|
|
3400
|
-
`}var
|
|
3401
|
-
${
|
|
3402
|
-
${an.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 s=this.device,a=(this.captureUploads??={})[e]??={};(!a.buffer||a.buffer.size<i.byteLength)&&(a.buffer?.destroy(),a.buffer=s.createBuffer({size:i.byteLength*2,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),a.buffers=null),s.queue.writeBuffer(a.buffer,0,i);let n=[this.bodyBuffer,this.jointBuffer,this.infoBuffer,a.buffer];(!a.buffers||n.some((d,p)=>d!==a.buffers[p])||a.bytes!==i.byteLength)&&(a.group=s.createBindGroup({layout:t.getBindGroupLayout(0),entries:n.map((d,p)=>({binding:p,resource:{buffer:d,...p===3?{size:i.byteLength}:{}}}))}),a.buffers=n,a.bytes=i.byteLength);let l=s.createCommandEncoder(),c=l.beginComputePass();c.setPipeline(t),c.setBindGroup(0,a.group);let f=Math.ceil(r/64),u=s.limits.maxComputeWorkgroupsPerDimension;c.dispatchWorkgroups(Math.min(f,u),Math.ceil(f/u)),c.end(),s.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]!==Ui)throw Error("Spring material requires an active spring slot");let i=Pt(t);!this.springFracture&&(Number.isFinite(i.breakStrain)||Number.isFinite(i.breakForce))&&(this.springFracture=!0,this.rebuildSolvePipelines());for(let[r,s]of[["breakStrain",11],["breakForce",15]])Q(this).float(this.jointBuffer,e*32+s,i[r]===1/0?0:i[r]);this.wakeAll()}appendConstraints(...e){return this.flushPropertyEdits(),super.appendConstraints(...e)}appendJoint(e,t,i,r,s,a){let n=c=>c===1/0?3e38:c,[l]=this.appendConstraints(1,(c,f)=>{f[3]=n(s),f[7]=n(a),f[11]=3e38,f.set(i,24),f.set(r,28);let u=e>=0?this.bodies[e].size:[0,0,0],d=this.bodies[t].size;return f[15]=u.reduce((p,m,h)=>p+(m+d[h])**2,0),[1,e,t]});return this.wakeAll(),l}appendJoints(...e){let t=super.appendJoints(...e);return this.wakeAll(),t}appendSprings(...e){let t=super.appendSprings(...e);return this.wakeAll(),t}enableSleeping(e,t={}){this.sleeping||(this.sleeping=new hi({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++)cs(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++)cs(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++)Q(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 s=0;s<e.length;s++){if(!Number.isInteger(e[s])||e[s]<0||e[s]>=this.bodyCount)throw Error("Collision filters require live body indices");for(let a of[t[s],i[s]])if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers")}let r=Q(this);for(let s=0;s<e.length;s++)r.uint(this.filterBuffer,e[s]*2,t[s]),r.uint(this.filterBuffer,e[s]*2+1,i[s]);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:pn}),entryPoint:"sortAdjacency"}})),this.pipes.sortAdjacency=e?this.adjacencyPipelines.cached:this.adjacencyPipelines.original,this.adjacencyCached=e)}};var Ko=new WeakMap,kn=[{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 Pn(o){return Ko.has(o)||Ko.set(o,dc(o)),Ko.get(o)}async function dc(o,e){let t=[];for(let i of kn){let r=await gc(o,i,e);if(t.push(r),!!r.passed)return Cn(o,{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!==kn[0].name,attempts:t}}return{...t[0],selected:null,compatibility:!1,attempts:t}}function hc(o){let e=new jt;return e.iterations=5,o||(e.gravity=0),new De(e,[16,.25,16],0,.6,[0,-.125,0]),new De(e,[1,1,1],1,.6,[-2,o?2:.45,0]),Mi(e,.5,1,.6,[0,o?2.3:.45,0]),Hi(e,.25,1.6,1,.6,[2,o?2.5:.75,0]),e}async function Wt(o,e,t=e.size){let i=o.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST});try{let r=o.createCommandEncoder();return r.copyBufferToBuffer(e,0,i,0,t),o.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ),i.getMappedRange().slice(0)}finally{i.destroy()}}async function pc(o){o.step();let e=await o.readCounters(),t=new Uint32Array(await Wt(o.device,o.argsBuffer)),i=await bc(o),r=new Uint32Array(i),s=new Float32Array(i),a=new Uint32Array(await Wt(o.device,o.adjBuffer)),n=new Uint32Array(await Wt(o.device,o.colorBuffer)),{manifolds:l,contacts:c}=o.contactStorage,f=await Wt(o.device,l,Math.max(32,e.manifolds*32)),u=new Uint32Array(f),d=new Float32Array(f),p=new Float32Array(await Wt(o.device,c,Math.max(64,e.contacts*64))),m=await o.readBodies(),h=e.manifolds>=3?await mc(o):[],b=h.some(({contactEvaluation:y})=>y.returnedForce===0&&y.independentForce<-1e-5);return{expectedFloorPairs:3,counters:e,pairs:e.pairs?Array.from(await o.readPairs()):[],narrowphaseWorkgroups:Array.from(t.subarray(0,3)),solver:{scalarPrimal:o.scalarPrimal,portableContactMath:o.portableContactMath,lanes:[...o.primalLanes],contactWorkgroups:Array.from(t.subarray(3,6)),dualWorkgroups:Array.from(t.subarray(9,12)),colorWorkgroups:Array.from(t.subarray(12,12+3*o.colorCap)),adjacencyStarts:Array.from(a.subarray(0,5)),adjacencyEntries:Array.from(a.subarray(2*o.bodyCapacity+1,2*o.bodyCapacity+1+a[4])),colorStarts:Array.from(n.subarray(2*o.bodyCapacity,2*o.bodyCapacity+o.colorCap+1)),colorBodies:Array.from(n.subarray(2*o.bodyCapacity+65,2*o.bodyCapacity+68)),positionsAfterOverlap:[1,2,3].map(y=>Array.from(m.subarray(y*40,y*40+3))),manifolds:Array.from({length:e.manifolds},(y,v)=>({ids:Array.from(u.subarray(v*8,v*8+4)),normalAndFriction:Array.from(d.subarray(v*8+4,v*8+8))})),contacts:Array.from({length:e.contacts},(y,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:b?"contact-evaluation":null},filters:Array.from(new Uint32Array(await Wt(o.device,o.filterBuffer))),params:{bodyCount:r[0],largeCount:r[1],noCollideCount:r[2],pairCapacity:r[3],contactCapacity:r[4],dt:s[5],cellSize:s[6],maxSmallRadius:s[7],up:Array.from(s.subarray(8,11)),alpha:s[11]},stage:e.pairs<3?"collision-pairs":e.manifolds<3?"contact-generation":"contacts-created"}}async function mc(o){let e=o.portableContactMath?"bitcast<f32>(pc.data.y)":"pc.alpha",t=o.portableContactMath?"k.anchorB.w":"k.c0x",i=o.portableContactMath?"k.penalty.x":"k.pen.x",r=o.portableContactMath?"k.force.x":"k.lam.x",s=o.device.createShaderModule({code:o.solveSource(be)+`
|
|
3611
|
+
`}function pc(s,e){return e==="points"&&(s=cc(s)),(e==="points"||e==="optimized")&&(s=uc(s)),s}var gi=class extends ro{writeParams(){lc.call(this)}writePassConstants(e,t){fo.call(this,e,t)}constructor(e,t,i={}){if(i.smallGroups!==void 0&&typeof i.smallGroups!="boolean")throw Error("smallGroups must be a boolean");let r=po(e),o=i.scalarPrimal??r?.scalarPrimal??!1,n=i.portableContactMath??r?.portableContactMath??!1,a=t.forces.some(v=>fs(v)),l=t.forces.some(v=>{let x=Ui(v);return x&&(Number.isFinite(x.breakStrain)||Number.isFinite(x.breakForce))}),c={requested:i.solverMode??"auto",contactScheduling:i.contactScheduling??"manifolds",custom:!!i.shaders?.solve},u=Ar({...c,bodyCount:t.bodies.length,constraintCount:t.forces.filter(v=>v instanceof ke||v instanceof dt).length}),f=u.selected==="custom"?i.shaders.solve:u.selected==="optimized"?us:u.selected==="points"?so:void 0,d=r?.broadphase??i.broadphase??"auto";if(!["grid","hploc","auto"].includes(d))throw Error("broadphase must be 'grid', 'hploc' or 'auto'");let p=i.bvh?.rebuildInterval??64;if(!Number.isInteger(p)||p<1)throw Error("BVH rebuildInterval must be a positive integer");for(let[v,x]of[["minimumColors",64],["minimumColorRounds",32]]){let w=i[v]??0;if(!Number.isInteger(w)||w<0||w>x)throw Error(`${v} must be an integer between 0 and ${x}`);if(v==="minimumColorRounds"&&w%2)throw Error("minimumColorRounds must be even")}let m=pc(f??Ge,u.selected),h=sa(oa(a?ma(l?oo(m):m):l?oo(m):m));o&&(h=Ba(h)),n&&(h=Ca(h)),u.selected==="points"&&!o&&!n&&!r?.dispatchIsolation&&(h=xa(h));let g=!i.shaders?.contacts&&i.hulls!==!1&&e.limits.maxStorageBuffersPerShaderStage>=9&&t.bodies.some(v=>Ct(v)),b=!i.shaders?.contacts&&t.bodies.some(v=>Fe(v)),y=!i.shaders?.contacts&&t.bodies.some(v=>v.isTrigger);y&&(h=fa(h));let A=v=>(b&&(v=ga(v)),y&&(v=uo(v)),v=Yl(v),n?hc(v):v);if(super(e,t,{...i,shaders:{...i.shaders,contacts:i.shaders?.contacts??A(vr(g)),solve:h,refs:i.shaders?.refs??(i.shaders?.contacts?void 0:Xl(eo)),topology:i.shaders?.topology??(y?da():void 0)}}),this.solverPolicy=c,this.scalarPrimal=o,this.portableContactMath=n,y&&(this.sensorsEnabled=!0,ha(this)),o&&(this.primalLanes=[1,1,1]),this.dispatchIsolation=r?.dispatchIsolation??!1,!i.shaders?.contacts){this.contactShaders.custom=!1,this.hullShaders=g,this.capsulesEnabled=b;let v=this.contactShaders.make;this.contactShaders.make=x=>v(A(x))}this.springFracture=l,this.angularConstraints=a,this.customSolveSource=i.shaders?.solve,t.forces.forEach((v,x)=>{let w=Ui(v);w&&this.setSpringMaterial(x,w)}),this.adaptiveScheduling=o?!1:i.adaptiveScheduling??!0,this.defaultPrimalLanes=[...this.primalLanes],this.cacheAdjacencyKeys=i.cacheAdjacencyKeys,this.smallGroups=i.smallGroups,this.adjacencyPipelines={original:this.pipes.sortAdjacency},this.selectAdjacencyPipeline(),this.solverDecision=u,this.solvePipelineCache=new Map([[u.selected,Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(v=>[v,this.pipes[v]]))]]),Aa(this,u.selected==="points"||u.selected==="optimized"),this.minimumColors=i.minimumColors??0,this.minimumColorRounds=i.minimumColorRounds??4,this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.broadphaseRequested=d,this.broadphase=d==="hploc"?"hploc":"grid",this.bvhOptions=i.bvh??{},this.bvh=null,this.detailCallback=null,this.detailTiming=null,this.profileErrors=[],this.selectBroadphase(),t.forces.forEach((v,x)=>{let w=fs(v);w&&this.setAngularConstraint(x,w)}),t.bodies.forEach((v,x)=>{Fe(v)&&this.installCapsule(this.gpuIndex(x))})}installCapsule(e){if(!this.capsulesEnabled){if(this.contactShaders.custom)throw Error("Analytic capsules require the built-in contact kernel");this.capsulesEnabled=!0;let t=this.contactShaders.make;this.contactShaders.make=i=>t(ga(i)),this.contactShaders.make(vr(this.hullShaders??!1))}this.device.queue.writeBuffer(this.bodyBuffer,e*160+156,new Float32Array([-1]))}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new pi(this),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async sphereCast(e,t,i,r={}){let[o]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}sphereCastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new pi(this),this.shapeQueries.cast(e,t)}selectBroadphase(){if(this.broadphaseRequested!=="auto"){this.broadphaseDecision={requested:this.broadphaseRequested,selected:this.broadphase,reason:"Explicit selection"};return}let e=new Float64Array(this.bodyCount),t=0,i=0,r=0;for(let d=0;d<this.bodyCount;d++){let p=this.bodies[d];if(p.radius>0&&(e[t++]=p.radius),p.dynamic){i++;let[m,h,g]=p.size,b=Math.min(m,h,g),y=Math.max(m,h,g),A=m+h+g-b-y;!p.sphere&&!p.hull&&b<=.4*A&&A>=.5*y&&r++}}let o=t?to(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(){co(this,$l),this.selectAdjacencyPipeline(),this.selectSolver(),Ul(this),this.radiiDirty&&(this.uploadStatics(),this.selectBroadphase()),this.broadphaseRequested==="auto"&&this.broadphaseDecision.constraintCount!==this.jointCount-this.freeSlots.length&&this.selectBroadphase(),this.broadphase==="grid"&&this.bvh&&(this.bvh.destroy(),this.bvh=null),this.broadphase==="hploc"&&(!this.bvh||this.bvh.bodyBuffer!==this.bodyBuffer)&&(this.bvh?.destroy(),this.bvh=new ao(this.device,this,this.bvhOptions)),this.dispatchIsolation||this.smallGroupPipeline||this.bvh||this.detailCallback||this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep?Wl(this):super.step()}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.externalBeforeStep?.(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e),this.externalAfterStep?.(e)}setRestitution(e,t){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a value between 0 and 1");t>0&&!this.restitution&&(this.restitution=new wr(this)),this.restitution?.set(e,t),this.wakeAll()}setSolverMode(e){return Ar({...this.solverPolicy,requested:e,bodyCount:this.bodyCount}),this.solverPolicy.requested=e,this.selectSolver(),this.solverDecision}solveSource(e,t=this.solverDecision.selected){return e=pc(e,t),this.springFracture&&(e=oo(e)),this.angularConstraints&&(e=ma(e)),this.sensorsEnabled&&(e=fa(e)),e=sa(oa(e)),this.scalarPrimal&&(e=Ba(e)),this.portableContactMath&&(e=Ca(e)),t==="points"&&!this.scalarPrimal&&!this.portableContactMath&&!this.dispatchIsolation&&(e=xa(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;
|
|
3612
|
+
${Kl.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}),this.runCapture("angular",this.angularFramePipeline,Uint32Array.from(e),e.length)}setMotor(e,{speed:t,maxTorque:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(this.info[e*4]!==3)throw Error("Motor needs an active motor slot");if(t!==void 0&&!Number.isFinite(t))throw Error("Motor speed must be finite");if(i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("maxTorque must be nonnegative");t!==void 0&&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(),this.smallGroupPipelines?.clear(),this.smallGroupPipeline=null;let e=this.solverDecision.selected,t=e==="custom"?this.customSolveSource:e==="optimized"?us:e==="points"?so:Ge,i=this.device.createShaderModule({code:this.solveSource(t)}),r=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.solve,this.layouts.pass]}),o=Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(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,ha(this),this.rebuildSolvePipelines(),this.contactShaders.make(uo(vr(this.hullShaders??!1)));let i=this.contactShaders.make;this.contactShaders.make=n=>i(uo(n));let r=this.device.createShaderModule({code:da()}),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=Ar({...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"?us:i.selected==="points"?so:void 0,n=this.device.createShaderModule({code:this.solveSource(o??Ge,i.selected)}),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,Aa(this,i.selected==="points"||i.selected==="optimized")}profileDetailedNextStep(e){if(!this.device.features.has("timestamp-query")||this.timingBusy||this.timingCallback||this.detailCallback)return!1;let t=2*(9+2*this.params.iterations);return(!this.detailTiming||this.detailTiming.count<t)&&(this.releaseAppTiming(),this.detailTiming={count:t,querySet:this.device.createQuerySet({type:"timestamp",count:t}),resolve:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ})}),this.detailCallback=e,!0}captureConstraintFrames(e){if(e.length){this.flushPropertyEdits(),this.framePipeline||(this.framePipeline=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3613
|
+
${Dl.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]!==br)throw Error("Spring material requires an active spring slot");let i=It(t);!this.springFracture&&(Number.isFinite(i.breakStrain)||Number.isFinite(i.breakForce))&&(this.springFracture=!0,this.rebuildSolvePipelines());for(let[r,o]of[["breakStrain",11],["breakForce",15]])le(this).float(this.jointBuffer,e*32+o,i[r]===1/0?0:i[r]);this.wakeAll()}appendConstraints(...e){return this.flushPropertyEdits(),super.appendConstraints(...e)}appendJoint(e,t,i,r,o,n){let a=c=>c===1/0?3e38:c,[l]=this.appendConstraints(1,(c,u)=>{u[3]=a(o),u[7]=a(n),u[11]=3e38,u.set(i,24),u.set(r,28);let f=e>=0?this.bodies[e].size:[0,0,0],d=this.bodies[t].size;return u[15]=f.reduce((p,m,h)=>p+(m+d[h])**2,0),[1,e,t]});return this.wakeAll(),l}appendJoints(...e){let t=super.appendJoints(...e);return this.wakeAll(),t}appendSprings(...e){let t=super.appendSprings(...e);return this.wakeAll(),t}enableSleeping(e,t={}){this.sleeping||(this.sleeping=new Fi({device:this.device,gpu:this,capacity:this.bodyCapacity,aggregates:e.bodies.map((i,r)=>({rigid:i,index:r}))},t))}wakeAll(){this.sleeping&&(this.sleeping.wakeRequested=!0)}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}addBodies(e){this.flushPropertyEdits();let t=super.addBodies(e);if(t>=0)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)Fe(e[i])&&this.installCapsule(t+i);if(t>=0&&this.sleeping)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)this.sleeping.register(t+i,e[i].mass,e[i].allowSleep!==!1);return t}rewriteBodies(e,t){this.flushPropertyEdits(),super.rewriteBodies(e,t);for(let i=0;i<e.length;i++)Fe(t[i])&&this.installCapsule(e[i]);if(this.sleeping)for(let i=0;i<e.length;i++)this.sleeping.register(e[i],t[i].mass,t[i].allowSleep!==!1)}releaseJoints(e){this.flushPropertyEdits(),super.releaseJoints(e),this.wakeAll()}setWorldAnchor(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4+1]!==-1||t?.length!==3||!Array.from(t).every(Number.isFinite))throw Error("A world joint and finite xyz anchor are required");for(let i=0;i<3;i++)le(this).float(this.jointBuffer,e*32+24+i,t[i]);this.wakeAll()}disableConstraint(e){this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll()}setFilters(e,t,i){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");for(let o=0;o<e.length;o++){if(!Number.isInteger(e[o])||e[o]<0||e[o]>=this.bodyCount)throw Error("Collision filters require live body indices");for(let n of[t[o],i[o]])if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers")}let r=le(this);for(let o=0;o<e.length;o++)r.uint(this.filterBuffer,e[o]*2,t[o]),r.uint(this.filterBuffer,e[o]*2+1,i[o]);this.wakeAll()}profileNextStep(e){return this.detailCallback?!1:super.profileNextStep(t=>e({...t,broadphase:this.broadphase,bvhRebuilt:this.bvh?.lastRebuilt??!1}))}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}releaseAppTiming(){if(this.detailTiming){for(let e of["querySet","resolve","read"])this.detailTiming[e].destroy();this.detailTiming=null}}destroy(){this.passConstantCache=null;for(let e of Object.values(this.captureUploads??{}))e.buffer.destroy();this.captureUploads=null,this.frameData=null,this.propertyEdits?.destroy(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.sleeping?.dispose(),this.bvh?.destroy(),this.bvh=null,this.timingBusy||this.releaseAppTiming(),super.destroy(),this.solvePipelineCache.clear(),this.adjacencyPipelines={}}adapt(e){if(super.adapt(e),this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.adaptiveScheduling){let t=this.bodyCount>=25e4&&this.jointCount-this.freeSlots.length===0&&e.manifolds>=1.75*this.bodyCount&&!e.overflow&&!e.clashes;this.primalLanes=t?[2**31,2**31,2**31]:[...this.defaultPrimalLanes],this.schedulingDecision={bodyCount:this.bodyCount,manifolds:e.manifolds,dense:t,cachedAdjacency:this.adjacencyCached,reason:t?"Eight threads share each dense body solve":"Threads per body follow solve-group size"}}}selectAdjacencyPipeline(){let e=this.cacheAdjacencyKeys??this.bodyCount>=25e4;this.adjacencyCached!==e&&(e&&!this.adjacencyPipelines.cached&&(this.adjacencyPipelines.cached=this.device.createComputePipeline({label:"Cached adjacency keys",layout:this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]}),compute:{module:this.device.createShaderModule({code:Jl}),entryPoint:"sortAdjacency"}})),this.pipes.sortAdjacency=e?this.adjacencyPipelines.cached:this.adjacencyPipelines.original,this.adjacencyCached=e)}};var Sa=new WeakMap,mc=[{name:"batched-grid",broadphase:"grid",dispatchIsolation:!1},{name:"isolated-grid",broadphase:"grid",dispatchIsolation:!0},{name:"batched-hploc",broadphase:"hploc",dispatchIsolation:!1},{name:"isolated-hploc",broadphase:"hploc",dispatchIsolation:!0},{name:"scalar-grid",broadphase:"grid",dispatchIsolation:!1,scalarPrimal:!0},{name:"scalar-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0},{name:"portable-contact-grid",broadphase:"grid",dispatchIsolation:!1,portableContactMath:!0},{name:"portable-contact-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0,portableContactMath:!0}];function gc(s){return Sa.has(s)||Sa.set(s,Gf(s)),Sa.get(s)}async function Gf(s,e){let t=[];for(let i of mc){let r=await Vf(s,i,e);if(t.push(r),!!r.passed)return fc(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!==mc[0].name,attempts:t}}return{...t[0],selected:null,compatibility:!1,attempts:t}}function Ff(s){let e=new ai;return e.iterations=5,s||(e.gravity=0),new Ze(e,[16,.25,16],0,.6,[0,-.125,0]),new Ze(e,[1,1,1],1,.6,[-2,s?2:.45,0]),ar(e,.5,1,.6,[0,s?2.3:.45,0]),Cr(e,.25,1.6,1,.6,[2,s?2.5:.75,0]),e}async function bi(s,e,t=e.size){let i=s.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST});try{let r=s.createCommandEncoder();return r.copyBufferToBuffer(e,0,i,0,t),s.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ),i.getMappedRange().slice(0)}finally{i.destroy()}}async function Uf(s){s.step();let e=await s.readCounters(),t=new Uint32Array(await bi(s.device,s.argsBuffer)),i=await qf(s),r=new Uint32Array(i),o=new Float32Array(i),n=new Uint32Array(await bi(s.device,s.adjBuffer)),a=new Uint32Array(await bi(s.device,s.colorBuffer)),{manifolds:l,contacts:c}=s.contactStorage,u=await bi(s.device,l,Math.max(32,e.manifolds*32)),f=new Uint32Array(u),d=new Float32Array(u),p=new Float32Array(await bi(s.device,c,Math.max(64,e.contacts*64))),m=await s.readBodies(),h=e.manifolds>=3?await Nf(s):[],g=h.some(({contactEvaluation:b})=>b.returnedForce===0&&b.independentForce<-1e-5);return{expectedFloorPairs:3,counters:e,pairs:e.pairs?Array.from(await s.readPairs()):[],narrowphaseWorkgroups:Array.from(t.subarray(0,3)),solver:{scalarPrimal:s.scalarPrimal,portableContactMath:s.portableContactMath,lanes:[...s.primalLanes],contactWorkgroups:Array.from(t.subarray(3,6)),dualWorkgroups:Array.from(t.subarray(9,12)),colorWorkgroups:Array.from(t.subarray(12,12+3*s.colorCap)),adjacencyStarts:Array.from(n.subarray(0,5)),adjacencyEntries:Array.from(n.subarray(2*s.bodyCapacity+1,2*s.bodyCapacity+1+n[4])),colorStarts:Array.from(a.subarray(2*s.bodyCapacity,2*s.bodyCapacity+s.colorCap+1)),colorBodies:Array.from(a.subarray(2*s.bodyCapacity+65,2*s.bodyCapacity+68)),positionsAfterOverlap:[1,2,3].map(b=>Array.from(m.subarray(b*40,b*40+3))),manifolds:Array.from({length:e.manifolds},(b,y)=>({ids:Array.from(f.subarray(y*8,y*8+4)),normalAndFriction:Array.from(d.subarray(y*8+4,y*8+8))})),contacts:Array.from({length:e.contacts},(b,y)=>({anchorA:Array.from(p.subarray(y*16,y*16+3)),anchorB:Array.from(p.subarray(y*16+4,y*16+7)),gap:p[y*16+7],penalty:Array.from(p.subarray(y*16+8,y*16+11)),force:Array.from(p.subarray(y*16+12,y*16+15))})),serialSystems:h,failure:g?"contact-evaluation":null},filters:Array.from(new Uint32Array(await bi(s.device,s.filterBuffer))),params:{bodyCount:r[0],largeCount:r[1],noCollideCount:r[2],pairCapacity:r[3],contactCapacity:r[4],dt:o[5],cellSize:o[6],maxSmallRadius:o[7],up:Array.from(o.subarray(8,11)),alpha:o[11]},stage:e.pairs<3?"collision-pairs":e.manifolds<3?"contact-generation":"contacts-created"}}async function Nf(s){let e=s.portableContactMath?"bitcast<f32>(pc.data.y)":"pc.alpha",t=s.portableContactMath?"k.anchorB.w":"k.c0x",i=s.portableContactMath?"k.penalty.x":"k.pen.x",r=s.portableContactMath?"k.force.x":"k.lam.x",o=s.device.createShaderModule({code:s.solveSource(Ge)+`
|
|
3403
3614
|
@compute @workgroup_size(1) fn inspectSystems(@builtin(global_invocation_id) gid: vec3u) {
|
|
3404
3615
|
let acc = accumulate(gid.x + 1u, 0u, 1u);
|
|
3405
3616
|
joints[gid.x].penLin = vec4f(acc.lin[0][0], acc.lin[1][1], acc.lin[2][2], 0.0);
|
|
@@ -3423,7 +3634,7 @@ ${an.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}
|
|
|
3423
3634
|
let raw = ${i} * expectedC + ${r};
|
|
3424
3635
|
joints[gid.x].rA = vec4f(${t}, ${e}, result.C.x, result.F.x);
|
|
3425
3636
|
joints[gid.x].rB = vec4f(${i}, ${r}, expectedC, min(raw, 0.0));
|
|
3426
|
-
}`}),
|
|
3637
|
+
}`}),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 bi(s.device,s.jointBuffer,384));return[0,1,2].map(u=>({linearDiagonal:Array.from(c.subarray(u*32,u*32+3)),angularDiagonal:Array.from(c.subarray(u*32+4,u*32+7)),linearForce:Array.from(c.subarray(u*32+16,u*32+19)),angularForce:Array.from(c.subarray(u*32+20,u*32+23)),contactEvaluation:{inputGap:c[u*32+24],passAlpha:c[u*32+25],returnedError:c[u*32+26],returnedForce:c[u*32+27],inputPenalty:c[u*32+28],inputForce:c[u*32+29],independentError:c[u*32+30],independentForce:c[u*32+31]}}))}async function qf(s){let{device:e}=s,t=e.createBuffer({size:48,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC});try{let i=e.createComputePipeline({layout:"auto",compute:{module:e.createShaderModule({code:ge+`
|
|
3427
3638
|
@group(0) @binding(0) var<uniform> p: Params;
|
|
3428
3639
|
@group(0) @binding(1) var<storage,read_write> out: array<u32>;
|
|
3429
3640
|
@compute @workgroup_size(1) fn inspectParams() {
|
|
@@ -3432,7 +3643,7 @@ ${an.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}
|
|
|
3432
3643
|
out[5]=bitcast<u32>(p.dt); out[6]=bitcast<u32>(p.cellSize); out[7]=bitcast<u32>(p.maxSmallRadius);
|
|
3433
3644
|
out[8]=bitcast<u32>(p.up.x); out[9]=bitcast<u32>(p.up.y); out[10]=bitcast<u32>(p.up.z);
|
|
3434
3645
|
out[11]=bitcast<u32>(p.alpha);
|
|
3435
|
-
}`}),entryPoint:"inspectParams"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:
|
|
3646
|
+
}`}),entryPoint:"inspectParams"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:s.paramsBuffer}},{binding:1,resource:{buffer:t}}]}),o=e.createCommandEncoder(),n=o.beginComputePass();return n.setPipeline(i),n.setBindGroup(0,r),n.dispatchWorkgroups(1),n.end(),e.queue.submit([o.finish()]),await bi(e,t)}finally{t.destroy()}}async function Vf(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 gi(s,Ff(n),{spatialSort:!1,bodyCapacity:4,smallGroups:!1,broadphase:e.broadphase,scalarPrimal:e.scalarPrimal??!1,portableContactMath:e.portableContactMath??!1,capacity:{pairs:32,manifolds:32,contacts:256,colors:8}});return 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 Uf(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),m=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]-m,g=a.subarray(c,c+40).every(Number.isFinite);i.cases.push({shape:["floor","box","sphere","capsule"][l],passed:g&&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 Sr(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 Ni(i)}catch(r){throw i.destroy(),r}return{adapter:e,device:i}}async function Ni(s){if(po(s))return s;let e=await gc(s);if(!e.passed)throw Error(`GPU 3D contact check failed: ${e.reason??e.errors.join("; ")}`);return s}var bc=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
|
|
3436
3647
|
struct Command { index:u32,mask:u32,pad0:u32,pad1:u32,velocity:vec4f,angular:vec4f,impulse:vec4f,torque:vec4f,position:vec4f,rotation:vec4f,pointImpulse:vec4f }
|
|
3437
3648
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
3438
3649
|
@group(0) @binding(1) var<storage,read> commands:array<Command>;
|
|
@@ -3450,41 +3661,47 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
|
|
|
3450
3661
|
let local=rotate(vec4f(-b.rot.xyz,b.rot.w),torque);
|
|
3451
3662
|
b.angular=vec4f(b.angular.xyz+rotate(b.rot,local/max(b.moment.xyz,vec3f(1.e-10))),b.angular.w);
|
|
3452
3663
|
}
|
|
3453
|
-
if((c.mask&8u)!=0u){
|
|
3664
|
+
if((c.mask&8u)!=0u){
|
|
3665
|
+
if((c.mask&32u)!=0u){
|
|
3666
|
+
// Position-only teleports preserve the live orientation, including the
|
|
3667
|
+
// principal-frame offset of meshes whose origin is not their mass centre.
|
|
3668
|
+
b.pos=vec4f(c.position.xyz+rotate(b.rot,c.rotation.xyz),b.pos.w);
|
|
3669
|
+
}else{b.pos=vec4f(c.position.xyz,b.pos.w);b.rot=c.rotation;}
|
|
3670
|
+
}
|
|
3454
3671
|
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);}
|
|
3455
3672
|
bodies[c.index]=b;
|
|
3456
3673
|
}
|
|
3457
|
-
`;var
|
|
3674
|
+
`;var Kt=class{constructor(e,t,i="linear"){this.solver=e,this.dimension=t,this.components=i==="angular"&&t===2?1:t;let r=e.device,o=t===3?10:6,n=t===3?i==="angular"?9:8:3;this.pipeline=r.createComputePipeline({layout:"auto",compute:{entryPoint:"setVelocities",module:r.createShaderModule({code:`
|
|
3458
3675
|
@group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
|
|
3459
3676
|
@group(0) @binding(1) var<storage,read> indices:array<u32>;
|
|
3460
3677
|
@group(0) @binding(2) var<storage,read> velocities:array<f32>;
|
|
3461
3678
|
@compute @workgroup_size(64) fn setVelocities(@builtin(global_invocation_id) id:vec3u) {
|
|
3462
3679
|
let i=id.x+id.y*${r.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3463
3680
|
if(i>=arrayLength(&indices)){return;}
|
|
3464
|
-
let base=indices[i]*${
|
|
3681
|
+
let base=indices[i]*${o}u+${n}u;
|
|
3465
3682
|
var v=bodies[base];
|
|
3466
3683
|
${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];":""}`}
|
|
3467
3684
|
bodies[base]=v;
|
|
3468
|
-
}`})}})}set(e,t){let i=this.solver,r=i.device,
|
|
3685
|
+
}`})}})}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()}},yi=class{constructor(e,t){this.solver=e,this.dimension=t,this.wordsPerBody=t===3?8:5}set(e,{linear:t,angular:i}={}){let r=this.solver,o=this.dimension,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 m of p)if(!Number.isFinite(m))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 m=p*this.wordsPerBody;if(this.data[m]=a[p],this.data[m+1]=(l?1:0)|(c?2:0),l)for(let h=0;h<o;h++)this.floats[m+2+h]=l[p*o+h];if(c)for(let h=0;h<n;h++)this.floats[m+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:`
|
|
3469
3686
|
@group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
|
|
3470
3687
|
@group(0) @binding(1) var<storage,read> commands:array<u32>;
|
|
3471
3688
|
@compute @workgroup_size(64) fn edit(@builtin(global_invocation_id) id:vec3u) {
|
|
3472
3689
|
let i=id.x+id.y*${r.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3473
|
-
let o=i*${
|
|
3690
|
+
let o=i*${o}u;if(o>=arrayLength(&commands)){return;}
|
|
3474
3691
|
let b=commands[o]*${this.dimension===3?10:6}u;
|
|
3475
3692
|
let mask=commands[o+1u];
|
|
3476
3693
|
var v=bodies[b+${this.dimension===3?8:3}u];
|
|
3477
3694
|
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]);":""}}
|
|
3478
3695
|
${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;}"}
|
|
3479
3696
|
bodies[b+${this.dimension===3?8:3}u]=v;
|
|
3480
|
-
}`})}}),(!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
|
|
3697
|
+
}`})}}),(!this.binding||this.boundBody!==i.bodyBuffer||this.boundBuffer!==this.buffer||this.boundBytes!==e.byteLength)&&(this.binding=r.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.buffer,size:e.byteLength}}]}),this.boundBody=i.bodyBuffer,this.boundBuffer=this.buffer,this.boundBytes=e.byteLength);let 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 go(s,e={}){return Ar({...e,requested:e.solverMode??"auto",bodyCount:s.bodies.length,custom:!!e.shaders?.solve}),{...e,solverMode:e.solverMode??"auto"}}function kr(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 bo(s,e){if(s!==1/0&&(!Number.isFinite(s)||s<0))throw Error(`${e} must be nonnegative or Infinity`);return s}function yc(s,e){let t=kr(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 Pr=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(...kr(l,"axis")).applyRotationQuaternion(a.aggregate.localRotation.conjugate()).asArray():kr(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=yc(t,r.anchorA??[0,0,0]),this.anchorB=yc(i,r.anchorB??[0,0,0]),this.linear=bo(o==="spring"?r.stiffness??1e3:r.stiffness??1/0,"stiffness"),this.angular=o==="fixed"?bo(r.angularStiffness??1/0,"angularStiffness"):0,this.breakForce=bo(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 dt(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"&&xr(this.force,this.material),this.force&&this.angularMaterial&&Br(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=kr(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):Br(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)}},Lr=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=kr(r.anchorA??[0,0,0],"anchorA"),l=kr(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 vi=class{constructor(e){this.device=e,this.pipelines=new Map,this.idle=[],this.jobs=new Set,this.destroyed=!1}acquire(e,t){if(this.destroyed)throw Error("Pose readback has been destroyed");let i=this.device;if(e>i.limits.maxStorageBufferBindingSize||t>Math.min(i.limits.maxStorageBufferBindingSize,i.limits.maxBufferSize))throw Error("Pose selection exceeds GPU buffer limits");let r=this.idle.findIndex(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:`
|
|
3481
3698
|
@group(0) @binding(0) var<storage,read> bodies:array<vec4f>;
|
|
3482
3699
|
@group(0) @binding(1) var<storage,read> indices:array<u32>;
|
|
3483
3700
|
@group(0) @binding(2) var<storage,read_write> selected:array<vec4f>;
|
|
3484
3701
|
@compute @workgroup_size(64) fn gather(@builtin(global_invocation_id) id:vec3u) {
|
|
3485
|
-
let i=id.x+id.y*${
|
|
3486
|
-
selected[i]=bodies[indices[i/${
|
|
3487
|
-
}`})}}),this.pipelines.set(l,c));let
|
|
3702
|
+
let i=id.x+id.y*${a*64}u; if(i>=arrayLength(&selected)){return;}
|
|
3703
|
+
selected[i]=bodies[indices[i/${o}u]*${r}u+i%${o}u];
|
|
3704
|
+
}`})}}),this.pipelines.set(l,c));let u=t.length*o*16,f=this.acquire(t.length*4,u),{selection:d,selected:p,staging:m}=f;try{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(),g=h.beginComputePass();g.setPipeline(c),g.setBindGroup(0,f.group);let b=Math.ceil(t.length*o/64);return g.dispatchWorkgroups(Math.min(b,a),Math.ceil(b/a)),g.end(),h.copyBufferToBuffer(p,0,m,0,u),n.queue.submit([h.finish()]),await m.mapAsync(GPUMapMode.READ,0,u),new Float32Array(m.getMappedRange(0,u).slice(0))}finally{this.release(f)}}};var ka=4,_r=3,Ai=1,Hf=2,ds=`
|
|
3488
3705
|
struct Manifold { ids: vec4u, geo: vec4f } // a, b (a > b), first contact, count (low 4 bits); normal (b to a)
|
|
3489
3706
|
struct Contact { rA: vec3f, key: u32, rB: vec3f, c0x: f32, pen: vec3f, c0y: f32, lam: vec3f, c0z: f32 }
|
|
3490
3707
|
struct Params { prev: u32, now: u32, mask: u32, capacity: u32, pairs: u32, step: u32, dt: f32, pad: u32 }
|
|
@@ -3498,7 +3715,7 @@ struct Params { prev: u32, now: u32, mask: u32, capacity: u32, pairs: u32, step:
|
|
|
3498
3715
|
@group(0) @binding(6) var<storage, read_write> events: array<vec4f>;
|
|
3499
3716
|
@group(0) @binding(7) var<uniform> params: Params;
|
|
3500
3717
|
|
|
3501
|
-
const STRIDE = ${
|
|
3718
|
+
const STRIDE = ${We/4}u;
|
|
3502
3719
|
|
|
3503
3720
|
fn hash32(x0: u32) -> u32 {
|
|
3504
3721
|
var x = x0;
|
|
@@ -3513,7 +3730,7 @@ fn turn(q: vec4f, v: vec3f) -> vec3f {
|
|
|
3513
3730
|
}
|
|
3514
3731
|
|
|
3515
3732
|
fn slotOf(base: u32, h: u32) -> u32 {
|
|
3516
|
-
return ${
|
|
3733
|
+
return ${ka}u + 2u * (base + h);
|
|
3517
3734
|
}
|
|
3518
3735
|
|
|
3519
3736
|
fn insert(base: u32, a: u32, b: u32) {
|
|
@@ -3546,16 +3763,16 @@ fn has(base: u32, a: u32, b: u32) -> bool {
|
|
|
3546
3763
|
fn emit(kind: u32, a: u32, b: u32, point: vec3f, impulse: f32, normal: vec3f) {
|
|
3547
3764
|
let e = atomicAdd(&table[0], 1u);
|
|
3548
3765
|
if (e >= params.capacity) { return; }
|
|
3549
|
-
events[${
|
|
3550
|
-
events[${
|
|
3551
|
-
events[${
|
|
3766
|
+
events[${_r}u * e] = bitcast<vec4f>(vec4u(kind, a, b, params.step));
|
|
3767
|
+
events[${_r}u * e + 1u] = vec4f(point, impulse);
|
|
3768
|
+
events[${_r}u * e + 2u] = vec4f(normal, 0.0);
|
|
3552
3769
|
}
|
|
3553
3770
|
|
|
3554
3771
|
/** Per pair this step: into this step's set, and a begin if it wasn't in last step's. */
|
|
3555
3772
|
@compute @workgroup_size(64)
|
|
3556
3773
|
fn touching(@builtin(global_invocation_id) id: vec3u) {
|
|
3557
3774
|
let m = id.x;
|
|
3558
|
-
if (m >= min(counters[${
|
|
3775
|
+
if (m >= min(counters[${gr}u], params.pairs)) { return; }
|
|
3559
3776
|
let mf = manifolds[m];
|
|
3560
3777
|
let count = mf.ids.w & 15u;
|
|
3561
3778
|
let a = mf.ids.x;
|
|
@@ -3564,8 +3781,8 @@ fn touching(@builtin(global_invocation_id) id: vec3u) {
|
|
|
3564
3781
|
insert(params.now, a, b);
|
|
3565
3782
|
if (has(params.prev, a, b)) { return; }
|
|
3566
3783
|
// Where (the points' average, on a) and how hard (the normal force over the step)
|
|
3567
|
-
let pA = bodies[a * STRIDE + ${
|
|
3568
|
-
let qA = bodies[a * STRIDE + ${
|
|
3784
|
+
let pA = bodies[a * STRIDE + ${fr/4}u].xyz;
|
|
3785
|
+
let qA = bodies[a * STRIDE + ${Zr/4}u];
|
|
3569
3786
|
var point = vec3f(0.0);
|
|
3570
3787
|
var force = 0.0;
|
|
3571
3788
|
for (var i = 0u; i < count; i++) {
|
|
@@ -3573,7 +3790,7 @@ fn touching(@builtin(global_invocation_id) id: vec3u) {
|
|
|
3573
3790
|
point += turn(qA, k.rA) + pA;
|
|
3574
3791
|
force += k.lam.x;
|
|
3575
3792
|
}
|
|
3576
|
-
emit(${
|
|
3793
|
+
emit(${Ai}u, a, b, point / f32(count), abs(force) * params.dt, mf.geo.xyz);
|
|
3577
3794
|
}
|
|
3578
3795
|
|
|
3579
3796
|
/** Per slot of last step's set: an end if the pair isn't in this step's. */
|
|
@@ -3584,10 +3801,10 @@ fn ended(@builtin(global_invocation_id) id: vec3u) {
|
|
|
3584
3801
|
let k = atomicLoad(&table[s]);
|
|
3585
3802
|
if (k == 0u) { return; }
|
|
3586
3803
|
let b = atomicLoad(&table[s + 1u]);
|
|
3587
|
-
if (!has(params.now, k - 1u, b)) { emit(${
|
|
3804
|
+
if (!has(params.now, k - 1u, b)) { emit(${Hf}u, k - 1u, b, vec3f(0.0), 0.0, vec3f(0.0)); }
|
|
3588
3805
|
}
|
|
3589
|
-
|
|
3590
|
-
fn touching`),
|
|
3806
|
+
`,$f=ds.replace("struct Manifold { ids: vec4u, geo: vec4f }","struct Manifold { ids:vec4u, pl:vec4f, anchors:vec4f, geo:vec4f, misc:vec4f }").replace(`const STRIDE = ${We/4}u;`,"const STRIDE = 6u;").replace(ds.slice(ds.indexOf(`@compute @workgroup_size(64)
|
|
3807
|
+
fn touching`),ds.indexOf("/** Per slot of last step")),`
|
|
3591
3808
|
@compute @workgroup_size(64) fn touching(@builtin(global_invocation_id) id:vec3u){
|
|
3592
3809
|
let m=id.x;let count=min(counters[1u],params.pairs);if(m>=count){return;}
|
|
3593
3810
|
let c=manifolds[m];let a=c.ids.x;let b=c.ids.y;
|
|
@@ -3596,12 +3813,12 @@ fn touching`),yr.indexOf("/** Per slot of last step")),`
|
|
|
3596
3813
|
insert(params.now,a,b);if(has(params.prev,a,b)){return;}
|
|
3597
3814
|
let pose=bodies[a*STRIDE];let cs=cos(pose.z);let sn=sin(pose.z);
|
|
3598
3815
|
let r=c.anchors.xy;let point=pose.xy+vec2f(cs*r.x-sn*r.y,sn*r.x+cs*r.y);
|
|
3599
|
-
emit(${
|
|
3816
|
+
emit(${Ai}u,a,b,vec3f(point,0.),abs(c.pl.z)*params.dt,vec3f(c.geo.zw,0.));
|
|
3600
3817
|
}
|
|
3601
3818
|
|
|
3602
|
-
`),$t=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?xc: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]}),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:(Zo+4*this.setSlots)*4,usage:u}),this.events=e.createBuffer({label:"contact events",size:s*Yi*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,p)=>d!==this.bound[p]))&&(this.bound=a,this.group=s.createBindGroup({layout:this.layout,entries:a.map((d,p)=>({binding:p,resource:{buffer:d}}))})),this.flip=1-this.flip;let[n,l]=this.flip?[0,this.setSlots]:[this.setSlots,0],c=new Uint32Array([n,l,this.setSlots-1,this.capacity,t,i,0,0]);new Float32Array(c.buffer)[6]=r,s.queue.writeBuffer(this.params,0,c);let f=s.createCommandEncoder({label:"contact events"});f.clearBuffer(this.table,(Zo+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*Yi*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*Yi*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 Ue=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),_e=o=>o&&typeof o.x=="number"?[o.x,o.y,o.z,...typeof o.w=="number"?[o.w]:[]]:Array.from(o),vr=new WeakMap,Qi=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o},bs=(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},xr=class o{static async create({device:e,...t}={}){if(t.scene&&vr.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await hs({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return await gr(e),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:p=8192,maxContactEvents:m=4096,sleeping:h=!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 jt,this.ref.dt=bs(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,v]of Object.entries({maxContactPairs:p,maxContactEvents:m}))if(!Number.isInteger(v)||v<1)throw Error(`${y} must be a positive integer`);if(this.contactOptions={maxContactPairs:p,maxContactEvents:m},this.bodyReadback=new Jt(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 b=new WeakRef(this);e.lost.then(y=>{let v=b.deref();!v||v.disposed||(v.deviceLost={reason:y.reason,message:y.message},v.errors.push(`WebGPU device lost: ${y.message||y.reason}`),v.dispose())}),this.setGravity(i),this.errorListener=y=>this.errors.push(y.error.message),e.addEventListener("uncapturederror",this.errorListener),t&&vr.set(t,this)}setGravity(e){let t=_e(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 Vt(this.device,this.ref,ps(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 hi(this,this.sleepOptions===!0?{}:this.sleepOptions))}addAggregate(e,t,i={}){return new Ar(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 $i(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 hi(this,e)),this}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new Xi(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new $t(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===Xt?"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(),Qi(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:_e(a.origin),direction:_e(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}={}){Qi(i),this.initialize(),this.flushCommands();let s=e.map(n=>{let l=_e(n.origin),c=_e(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&&bs(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=_e(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 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 b=i===2?"angularVelocityBatch":"velocityBatch",y=this[b]??=new Lt(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 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[b,y]of this.commands){let v=h++*8;this.motionData[v]=b.aggregate.gpuSlot??this.gpu.gpuIndex(b.aggregate.index),this.motionData[v+1]=y.mask,y.velocity&&this.motionFloats.set(y.velocity,v+2),y.angular&&this.motionFloats.set(y.angular,v+5)}this.motionBatch??=new Ht(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[h,b]of this.commands){let y=f++*32;c[y]=h.aggregate.gpuSlot??this.gpu.gpuIndex(h.aggregate.index),c[y+1]=b.mask;for(let[v,w]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])b[v]&&l.set(b[v],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;
|
|
3603
|
-
${Ln}`,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 p=Math.ceil(f/64),m=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(p,m),Math.ceil(p/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&&vr.get(this.scene)===this&&vr.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}},Ar=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 xr?u:vr.get(u??e.getScene()),!u)throw Error("Create an AVBD world for this scene before adding aggregates");if(u.assertAlive(),typeof t=="number"&&(t=[Ue.SPHERE,Ue.CAPSULE,Ue.CYLINDER,Ue.BOX,Ue.CONVEX_HULL][t]),bs(i,"mass",!0),bs(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(Ue).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=Qi(c),this.collidesWith=Qi(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(![Ue.BOX,Ue.SPHERE,Ue.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,p=e.scaling;if([p.x,p.y,p.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(p),h=d.center.multiply(p),b=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=h,this.localRotation=b.constructor.Identity();let y;if(t===Ue.BOX)y=new De(u.ref,_e(m),1,r,[0,0,0]);else if(t===Ue.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=Mi(u.ref,m.x/2,1,r,[0,0,0])}else if(t===Ue.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=Hi(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 A=Float32Array.from(g,(B,E)=>B*[p.x,p.y,p.z][E%3]),x=nr(A);if(!x)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...x.center),this.localRotation=b.constructor.FromArray(x.rotation),y=qr(u.ref,x,1,r,[0,0,0])}let v=i/y.mass;y.mass=i,y.moment.forEach((g,A)=>y.moment[A]=g*v);let w=e.position.add(this.localCenter.applyRotationQuaternion(b));if(y.positionLin.set(_e(w)),y.positionAng.set(b.multiply(this.localRotation).asArray()),this.rigid=y,this.index=u.aggregates.length,this.body=new Br(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?.()}},Br=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?_e(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=_e(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=_e(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",_e(e).map((r,s)=>r+i[s])),this}applyForce(e,t){return this.applyImpulse(_e(e).map(i=>i*this.aggregate.world.ref.dt),t)}applyTorque(e){return this.applyAngularImpulse(_e(e).map(t=>t*this.aggregate.world.ref.dt))}setCollisionGroups(e,t=4294967295){this.assertAlive();let i=this.aggregate,r=Qi(e),s=Qi(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",_e(e.add(r.localCenter.applyRotationQuaternion(i)))),r.world.queue(this,8,"rotation",i.multiply(r.localRotation).asArray()),this}};var pi=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 En=o=>o<0?-1:o>0?1:0,Ki=(o,e)=>o<e?o:e,Ac=(o,e)=>o>e?o:e,Zi=(o,e,t)=>Ac(e,Ki(t,o));function wr(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function mi(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 _t(o,e,t,i,r){o[e*3]=t,o[e*3+1]=i,o[e*3+2]=r}var gs=class extends pi{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?mi(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=mi(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=wr(e.position[2],this.rA[0],this.rA[1]);_t(t,0,1,0,-r[1]),_t(t,1,0,1,r[0]),_t(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=wr(e.position[2],this.rB[0],this.rB[1]);_t(t,0,-1,0,r[1]),_t(t,1,0,-1,-r[0]),_t(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},ys=class extends pi{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=mi(t.position,r[0],r[1]),c=mi(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=mi(this.bodyA.position,this.rA[0],this.rA[1]),t=mi(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,p=wr(e.position[2],-d[1],d[0]),m=wr(e.position[2],d[0],d[1]),h=n*p[0]+l*p[1],b=c*p[0]+f*p[1],y=s*m[0]+a*m[1],v=s*p[0]+a*p[1];u?_t(this.J,0,s,a,v):_t(this.J,0,-s,-a,-v);let w=u?-y-y:y+y,g=this.H;g[0]=n,g[1]=l,g[2]=h,g[3]=c,g[4]=f,g[5]=b,g[6]=h,g[7]=b,g[8]=w}},vs=class extends pi{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},xs=class extends pi{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){_t(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var As=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),Bs=()=>({...As(),postStabilize:!1,alpha:.95,matchNearest:!0});var er=(o,e)=>o>e?o*2097152+e:e*2097152+o;function Bc(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 ea(o,e,t){let i=Bc(o,e,t);return i<e&&o[i]===t}var ws=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 h=0;h<e;h++)c[h]||(n[h]=Math.floor(t[h*4]*d),l[h]=Math.floor(t[h*4+1]*d),f[this.hash(n[h],l[h])+1]++);for(let h=0;h<this.tableSize;h++)f[h+1]+=f[h];let p=f.slice(0,this.tableSize);for(let h=0;h<e;h++)c[h]||(u[p[this.hash(n[h],l[h])]++]=h);let m=(h,b)=>{if(!r[h]&&!r[b])return;let y=t[h*4]-t[b*4],v=t[h*4+1]-t[b*4+1],w=i[h*4+1]+i[b*4+1];if(y*y+v*v>w*w)return;let g=er(h,b);if(!ea(s,a,g)){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++]=g}};for(let h=0;h<e;h++){if(c[h])continue;let b=n[h],y=l[h];for(let v=-1;v<=1;v++)for(let w=-1;w<=1;w++){let g=this.hash(b+w,y+v);for(let A=f[g];A<f[g+1];A++){let x=u[A];x>=h||n[x]!==b+w||l[x]!==y+v||m(h,x)}}}for(let h of this.large)for(let b=0;b<e;b++)b===h||c[b]&&b>h||m(h,b);this.pairs.subarray(0,this.pairCount).sort()}};var Ye=new Float64Array(12),zn=new Float64Array(12),bi=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 Mn(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 Cs(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(Ye,0,o,-e,0,0,3,4),yt(Ye,1,o,e,0,0,4,1)):(yt(Ye,0,-o,e,0,0,1,2),yt(Ye,1,-o,-e,0,0,2,3)):c>0?(yt(Ye,0,o,e,0,0,4,1),yt(Ye,1,-o,e,0,0,1,2)):(yt(Ye,0,-o,-e,0,0,2,3),yt(Ye,1,o,-e,0,0,3,4));for(let f=0;f<2;f++){let u=f*6,d=Ye[u],p=Ye[u+1];Ye[u]=t+r*d-s*p,Ye[u+1]=i+s*d+r*p}}function In(o,e,t,i,r,s,a,n,l,c,f){let u=Math.cos(t),d=Math.sin(t),p=Math.cos(n),m=Math.sin(n),h=s-o,b=a-e,y=u*h+d*b,v=-d*h+u*b,w=p*h+m*b,g=-m*h+p*b,A=Math.abs(u*p+d*m),x=Math.abs(u*-m+d*p),B=Math.abs(-d*p+u*m),E=Math.abs(-d*-m+u*p),k=Math.abs(y)-i-(A*l+x*c),z=Math.abs(v)-r-(B*l+E*c);if(k>0||z>0)return 0;let M=Math.abs(w)-(A*i+B*r)-l,P=Math.abs(g)-(x*i+E*r)-c;if(M>0||P>0)return 0;let _=0,C=k,S=y>0?u:-u,R=y>0?d:-d;z>.95*C+.01*r&&(_=1,C=z,S=v>0?-d:d,R=v>0?u:-u),M>.95*C+.01*l&&(_=2,C=M,S=w>0?p:-p,R=w>0?m:-m),P>.95*C+.01*c&&(_=3,C=P,S=g>0?-m:m,R=g>0?p:-p);let I,O,q,G,j,F,H,ee,le;if(_===0){I=S,O=R,q=o*I+e*O+i,G=-d,j=u;let U=o*G+e*j;F=-U+r,H=U+r,ee=3,le=1,Cs(l,c,s,a,p,m,I,O)}else if(_===1){I=S,O=R,q=o*I+e*O+r,G=u,j=d;let U=o*G+e*j;F=-U+i,H=U+i,ee=2,le=4,Cs(l,c,s,a,p,m,I,O)}else if(_===2){I=-S,O=-R,q=s*I+a*O+l,G=-m,j=p;let U=s*G+a*j;F=-U+c,H=U+c,ee=3,le=1,Cs(i,r,o,e,u,d,I,O)}else{I=-S,O=-R,q=s*I+a*O+c,G=p,j=m;let U=s*G+a*j;F=-U+l,H=U+l,ee=2,le=4,Cs(i,r,o,e,u,d,I,O)}if(Mn(zn,Ye,-G,-j,F,ee)<2||Mn(bi,zn,G,j,H,le)<2)return 0;let xe=0;for(let U=0;U<2;U++){let ze=U*6,Me=bi[ze],ce=bi[ze+1],qe=I*Me+O*ce-q;if(qe>0)continue;let zt=Me-I*qe,Ie=ce-O*qe,Ve=bi[ze+2],Re=bi[ze+3],We=bi[ze+4],et=bi[ze+5],Ai,Bi,wi,Ci;_>=2?([Ve,Re,We,et]=[We,et,Ve,Re],Ai=Me-o,Bi=ce-e,wi=zt-s,Ci=Ie-a):(Ai=zt-o,Bi=Ie-e,wi=Me-s,Ci=ce-a);let At=xe*7;f[At+0]=Ve&255|(Re&255)<<8|(We&255)<<16|(et&255)<<24,f[At+1]=u*Ai+d*Bi,f[At+1+1]=-d*Ai+u*Bi,f[At+3]=p*wi+m*Ci,f[At+3+1]=-m*wi+p*Ci,f[At+5]=-S,f[At+5+1]=-R,xe++}return xe}var Rn=o=>31-Math.clz32(o&-o);function wc(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 ta=(o,e,t,i)=>o>t||o===t&&e>i,Ss=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)=>wc(r))}run(e,t,i,r,s,a){this.resize(e);let{colors:n,next:l,pending:c,prio:f}=this,u=(g,A)=>{let x=s[g*4+1],B=x===A?s[g*4+2]:x;return B>=0&&t[B]?B:-1},d=(g,A)=>{let x=0,B=0;for(let E=i[g];E<i[g+1];E++){let k=u(r[E],g);if(k<0||A&&c[k])continue;let z=n[k];z<0||(z<32?x|=1<<z:B|=1<<z-32)}return~x!==0?Rn(~x):~B!==0?32+Rn(~B):63};for(let g=0;g<e;g++){if(l[g]=n[g],!t[g]||n[g]<0)continue;let A=!0;for(let x=i[g];x<i[g+1]&&A;x++){let B=u(r[x],g);B>=0&&!ta(f[g],g,f[B],B)&&(A=!1)}A&&(l[g]=Math.min(n[g],d(g,!1)))}n.set(l);let p=()=>{let g=0;for(let A=0;A<e;A++)if(c[A]=0,!!t[A]){if(n[A]<0)c[A]=1;else for(let x=i[A];x<i[A+1];x++){let B=u(r[x],A);if(B>=0&&n[B]===n[A]&&ta(f[B],B,f[A],A)){c[A]=1;break}}g+=c[A]}return g},m=p(),h=0;for(;m>0&&h<a;){h++;for(let g=0;g<e;g++){if(l[g]=-2,!c[g])continue;let A=!0;for(let x=i[g];x<i[g+1]&&A;x++){let B=u(r[x],g);B>=0&&c[B]&&!ta(f[g],g,f[B],B)&&(A=!1)}A&&(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=p()}let y=this.colorStart;y.fill(0);let v=0;for(let g=0;g<e;g++)t[g]&&(y[n[g]+1]++,v=Math.max(v,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:v,conflicts:b,rounds:h}}};var Gn=0,Qt=1,Fn=2,ks=3,ia=4,Un=[0,3,1,1,2],Pc=.05,D=4,T=32,ge=0,ne=3,$=6,ft=9,dt=12,gi=15,at=18,W=21,K=23,ye=25,Ee=26,vt=27,Cr=28,Ps=class{params=As();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 ws;coloring=new Ss;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+gi+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,Qt,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+gi+2]=a,l.set(i,c+W),l.set(r,c+K),l[c+ye]=(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+Ee]=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+W]=t,this.data[e.slot*T+W+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,Fn,e,t);let l=n*T;this.data[l+$]=s,this.data.set(i,l+W),this.data.set(r,l+K),this.data[l+ye]=a}addMotor(e,t,i,r){let s=this.allocConstraint();this.setInfo(s,ks,e,t);let a=s*T;this.data[a+dt]=r,this.data[a+ft]=-r,this.data[a+ye]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(er(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(As());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 gs){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+ye]=r.restAngle,this.data[n.slot*T+Ee]=r.torqueArm}else r instanceof ys?this.addSpring(s,a,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof xs?this.addMotor(s,a,r.speed,r.fmax[0]):r instanceof vs&&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]=er(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]!==Gn&&i>=0&&e.push(er(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]===Qt){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+at)}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 h=Math.cos(r[e*4+2]),b=Math.sin(r[e*4+2]),y=s[i+W],v=s[i+W+1];l=h*y-b*v+r[e*4],c=b*y+h*v+r[e*4+1],f=r[e*4+2]}else l=s[i+W],c=s[i+W+1],f=0;let u=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),p=s[i+K],m=s[i+K+1];n[a]=l-(u*p-d*m+r[t*4]),n[a+1]=c-(d*p+u*m+r[t*4+1]),n[a+2]=(f-r[t*4+2]-s[i+ye])*s[i+Ee]}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=In(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 h=u+d>>>1;this.prevKeys[h]<n?u=h+1:d=h}let p=u,m=Math.sqrt(i[l*4]*i[c*4]);for(let h=0;h<f;h++){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,v=b*T,w=h*7,g=s[w+0];this.info.set([ia,l,c,g],b*D),y.fill(0,v,v+T),y[v+$]=y[v+$+1]=1/0,y[v+ft]=-1/0,y[v+dt]=0,y[v+gi]=y[v+gi+1]=1/0,y[v+W]=s[w+1],y[v+W+1]=s[w+1+1],y[v+K]=s[w+3],y[v+K+1]=s[w+3+1],y[v+ye]=m,y[v+Ee]=s[w+5],y[v+vt]=s[w+5+1];let A=-1;for(let C=p;C<this.prevCount&&this.prevKeys[C]===n;C++)this.prevInfo[C*D+3]===g&&(A=C);if(A<0&&this.params.matchNearest){let C=Pc*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let S=p;S<this.prevCount&&this.prevKeys[S]===n;S++){let R=Math.hypot(this.prevData[S*T+W]-y[v+W],this.prevData[S*T+W+1]-y[v+W+1]);R<=C&&(C=R,A=S)}}if(A>=0){let C=A*T;y[v+ge]=this.prevData[C+ge],y[v+ge+1]=this.prevData[C+ge+1],y[v+ne]=this.prevData[C+ne],y[v+ne+1]=this.prevData[C+ne+1],this.prevData[C+Cr]&&(y[v+W]=this.prevData[C+W],y[v+W+1]=this.prevData[C+W+1],y[v+K]=this.prevData[C+K],y[v+K+1]=this.prevData[C+K+1])}let[x,B]=Yt(e[l*4+2],y[v+W],y[v+W+1]),[E,k]=Yt(e[c*4+2],y[v+K],y[v+K+1]),z=y[v+Ee],M=y[v+vt],P=e[l*4]+x-e[c*4]-E,_=e[l*4+1]+B-e[c*4+1]-k;y[v+at]=z*P+M*_+5e-4,y[v+at+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=Un[this.info[a*D]],l=a*T;for(let c=0;c<n;c++){if(i.vbd){r[l+ge+c]=Ki(r[l+$+c],i.vbdStiffness);continue}e||(r[l+ne+c]=r[l+ne+c]*t*i.gamma),r[l+ge+c]=Ki(Zi(r[l+ge+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]=Zi(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*En(t),u=Zi(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:p,eMax:m,eJ:h,eG:b}=this,y=Un[l];for(let w=0;w<y;w++)p[w]=s[u+ft+w],m[w]=s[u+dt+w];let v=i===c;if(b.fill(0),l===Qt){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+at+w]*t);if(i>=0){let w=a[i*4+2],[g,A]=Yt(w,s[u+(v?W:K)],s[u+(v?W:K)+1]),x=v?1:-1;h[0]=x,h[1]=0,h[2]=-x*A,h[3]=0,h[4]=x,h[5]=x*g,h[6]=0,h[7]=0,h[8]=x*s[u+Ee],b[2]=Math.abs(g),b[5]=Math.abs(A)}}else if(l===Fn){let[w,g]=Nn(a,c,s[u+W],s[u+W+1]),[A,x]=Nn(a,f,s[u+K],s[u+K+1]),B=w-A,E=g-x,k=B*B+E*E,z=Math.sqrt(k);if(d[0]=z-s[u+ye],i>=0&&k!==0){let M=B/z,P=E/z,_=(1-M*M)/z,C=-M*P/z,S=(1-P*P)/z,R=a[i*4+2],I=v?W:K,[O,q]=Yt(R,-s[u+I+1],s[u+I]),[G,j]=Yt(R,s[u+I],s[u+I+1]),F=_*O+C*q,H=C*O+S*q,ee=M*G+P*j,le=M*O+P*q,xe=v?1:-1;h[0]=xe*M,h[1]=xe*P,h[2]=xe*le;let U=v?-ee-ee:ee+ee;b[0]=Math.hypot(_,C,F),b[1]=Math.hypot(C,S,H),b[2]=Math.hypot(F,H,U)}else i>=0&&h.fill(0,0,3)}else if(l===ks){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+ye]*this.params.dt,i>=0&&(h[0]=0,h[1]=0,h[2]=v?1:-1)}else if(l===ia){let w=s[u+Ee],g=s[u+vt],A=g,x=-w,[B,E]=Yt(n[c*4+2],s[u+W],s[u+W+1]),[k,z]=Yt(n[f*4+2],s[u+K],s[u+K+1]),M=B*g-E*w,P=-(k*g-z*w),_=B*x-E*A,C=-(k*x-z*A),S=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+at]*(1-t)+w*S+g*R+M*I+-w*O+-g*q+P*G,d[1]=s[u+at+1]*(1-t)+A*S+x*R+_*I+-A*O+-x*q+C*G;let j=Math.abs(s[u+ne])*s[u+ye];m[1]=j,p[1]=-j,i>=0&&(v?(h[0]=w,h[1]=g,h[2]=M,h[3]=A,h[4]=x,h[5]=_):(h[0]=-w,h[1]=-g,h[2]=P,h[3]=-A,h[4]=-x,h[5]=C))}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,p=c,m=0,h=0,b=f,y=c*(s[e*4]-a[e*4]),v=c*(s[e*4+1]-a[e*4+1]),w=f*(s[e*4+2]-a[e*4+2]),{eC:g,eMin:A,eMax:x,eJ:B,eG:E}=this,k=this.adjStart,z=this.adjList;for(let F=k[e+1]-1;F>=k[e];F--){let H=z[F],ee=this.evalConstraint(H,t,e),le=H*T,xe=!i.vbd;for(let U=0;U<ee;U++){let ze=n[le+$+U],Me=xe&&ze===1/0?n[le+ne+U]:0,ce=n[le+ge+U],qe=g[U],zt=ce*qe+Me,Ie=Zi(zt,A[U],x[U]),Ve=Math.abs(Ie);i.stiffnessRescale&&qe!==0&&(zt<A[U]?ce=Math.abs((A[U]-Me)/qe):zt>x[U]&&(ce=Math.abs((x[U]-Me)/qe)));let Re=B[U*3],We=B[U*3+1],et=B[U*3+2];y+=Re*Ie,v+=We*Ie,w+=et*Ie,u+=Re*Re*ce+E[U*3]*Ve,d+=We*Re*ce,p+=We*We*ce+E[U*3+1]*Ve,m+=et*Re*ce,h+=et*We*ce,b+=et*et*ce+E[U*3+2]*Ve}}let M=u,P=d/u,_=m/u,C=p-P*P*M,S=(h-P*_*M)/C,R=b-(_*_*M+S*S*C),I=v-P*y,q=(w-_*y-S*I)/R,G=I/C-S*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===Gn)continue;let f=this.evalConstraint(l,e,-1),u=l*T;c===ia&&(i[u+Cr]=Math.abs(i[u+ne+1])<a[1]&&Math.abs(i[u+at+1])<.01?1:0);for(let d=0;d<f;d++){let p=i[u+$+d],m=!t.vbd&&p===1/0?i[u+ne+d]:0,h=Zi(i[u+ge+d]*r[d]+m,s[d],a[d]);i[u+ne+d]=h,Math.abs(h)>=i[u+gi+d]&&this.disable(l),!t.vbd&&h>s[d]&&h<a[d]&&(i[u+ge+d]=Ki(i[u+ge+d]+t.beta*Math.abs(r[d]),Ki(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+ne])*e[i+ye];e[i+Cr]=Math.abs(e[i+ne+1])<r&&Math.abs(e[i+at+1])<.01?1:0}}disable(e){let t=e*T;this.data.fill(0,t+ge,t+ge+3),this.data.fill(0,t+ne,t+ne+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]=Yt(-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]!==Qt)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 ea(this.noCollide,this.noCollideCount,er(e,t))}};function Yt(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function Nn(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 Sr=`
|
|
3604
|
-
${
|
|
3819
|
+
`),xi=class{device;pipelines;layout;watch;table;events;params;setSlots;capacity;group=null;bound=[];flip=0;bodies;constructor(e,t,i,r,o,n=3){this.device=e,this.bodies=t,this.capacity=o,this.setSlots=2**Math.ceil(Math.log2(Math.max(64,2*r)));let a=e.createShaderModule({label:"contact events",code:n===2?$f:ds}),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:(ka+4*this.setSlots)*4,usage:f}),this.events=e.createBuffer({label:"contact events",size:o*_r*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,(ka+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*_r*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*_r*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 vc=[0,1,2,3,5,4,0,3,4,0,4,1,1,4,5,1,5,2,2,5,3,2,3,0],Er=vc.slice();for(let s=0;s<Er.length;s+=3)[Er[s+1],Er[s+2]]=[Er[s+2],Er[s+1]];var yo=(s,e,t)=>{let i=Array.from(s??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite values`);return i},xc=(s,e)=>{if(!Number.isFinite(s)||s<=0)throw Error(`${e} must be positive and finite`);return s};function Ac(s){let e=[0,1,2].map(u=>s.reduce((f,d)=>f+d[u],0)/s.length),[t,i,r,o]=s,n=i.map((u,f)=>u-t[f]),a=r.map((u,f)=>u-t[f]),l=(n[1]*a[2]-n[2]*a[1])*(t[0]-o[0])+(n[2]*a[0]-n[0]*a[2])*(t[1]-o[1])+(n[0]*a[1]-n[1]*a[0])*(t[2]-o[2]),c=Dn(s.flatMap(u=>u.map((f,d)=>f-e[d])),l>0?vc:Er);if(!c)throw Error("Terrain contains a degenerate collision piece");return{shape:c,position:c.center.map((u,f)=>u+e[f]),rotation:c.rotation}}function Mr({heights:s,columns:e,rows:t,spacing:i=1,origin:r=[0,0,0],upAxis:o=1,baseHeight:n,holes:a}={}){for(let[g,b]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(b)||b<2)throw Error(`${g} must be an integer >= 2`);let l=yo(s,e*t,"heights"),c=yo(r,3,"origin");if(![0,1,2].includes(o))throw Error("upAxis must be 0, 1 or 2");let u=typeof i=="number"?[i,i]:yo(i,2,"spacing");u.forEach(g=>xc(g,"spacing"));let f=n??l.reduce((g,b)=>Math.min(g,b),1/0)-2;if(!Number.isFinite(f)||l.some(g=>g<=f))throw Error("baseHeight must be below every terrain height");let d=(e-1)*(t-1);if(a&&a.length!==d)throw Error("holes requires one value per terrain cell");let p=[0,1,2].filter(g=>g!==o),m=[],h=(g,b)=>{let y=[...c];return y[p[0]]+=g*u[0],y[p[1]]+=b*u[1],y[o]+=l[b*e+g],y};for(let g=0;g<t-1;g++)for(let b=0;b<e-1;b++){if(a?.[g*(e-1)+b])continue;let y=h(b,g),A=h(b+1,g),v=h(b,g+1),x=h(b+1,g+1);for(let w of[[y,A,v],[A,x,v]])m.push(Ac([...w,...w.map(B=>{let L=[...B];return L[o]=c[o]+f,L})]))}return{kind:"heightfield",pieces:m,triangleCount:m.length}}function zr({positions:s,indices:e,thickness:t=.1}={}){if(!s||s.length%3)throw Error("positions requires xyz triples");let i=yo(s,s.length,"positions"),r=i.length/3;if(e===void 0&&r%3)throw Error("Nonindexed positions require three vertices per triangle");let o=e===void 0?Array.from({length:r},(a,l)=>l):Array.from(e);if(!o.length||o.length%3||!o.every(a=>Number.isInteger(a)&&a>=0&&a<r))throw Error("indices requires valid triangle vertex indices");xc(t,"thickness");let n=[];for(let a=0;a<o.length;a+=3){let l=o.slice(a,a+3).map(g=>i.slice(g*3,g*3+3)),[c,u,f]=l,d=u.map((g,b)=>g-c[b]),p=f.map((g,b)=>g-c[b]),m=[d[1]*p[2]-d[2]*p[1],d[2]*p[0]-d[0]*p[2],d[0]*p[1]-d[1]*p[0]],h=Math.hypot(...m);h!==0&&n.push(Ac([...l,...l.map(g=>g.map((b,y)=>b-m[y]*t/h))]))}if(!n.length)throw Error("Static mesh requires at least one nondegenerate triangle");return{kind:"static-mesh",pieces:n,triangleCount:n.length}}function vo(s,e,t={}){if(t.mass!==void 0&&t.mass!==0)throw Error("Terrain is static and requires zero mass");let i=t.friction??.6,r=t.restitution??0;if(!Number.isFinite(i)||i<0)throw Error("friction must be nonnegative and finite");if(!Number.isFinite(r)||r<0||r>1)throw Error("restitution must be between 0 and 1");for(let o of["group","collidesWith"]){let n=t[o]??4294967295;if(!Number.isInteger(n)||n<0||n>4294967295)throw Error(`${o} must be an unsigned 32-bit integer`)}if(!e?.pieces?.length)throw Error("Terrain requires collision pieces");return e.pieces.map(o=>{let n=lr(s,o.shape,0,i,o.position,o.rotation);return Object.assign(n,{allowSleep:!1,isTrigger:!1,restitution:r,group:t.group??4294967295,collidesWith:t.collidesWith??4294967295}),n})}var rt=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),Le=s=>s&&typeof s.x=="number"?[s.x,s.y,s.z,...typeof s.w=="number"?[s.w]:[]]:Array.from(s),hs=new WeakMap,Ir=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s},xo=(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},ps=class s{static async create({device:e,...t}={}){if(t.scene&&hs.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await Sr({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return await Ni(e),r=new s(e,t),r.ownsDevice=i,r.scene&&r.autoAttach&&r.attachToScene(),r}catch(o){throw r?r.dispose():i&&e.destroy(),o}}constructor(e,{scene:t,gravity:i=[0,-10,0],timeStep:r=1/60,iterations:o=10,capacity:n=4096,syncMeshes:a=!0,maxSyncedBodies:l=2048,broadphase:c="auto",solverMode:u="auto",smallGroups:f,bvh:d={},autoAttach:p=!0,maxContactPairs:m=8192,maxContactEvents:h=4096,sleeping:g=!1}={}){if(this.device=e,this.scene=t,this.autoAttach=p,this.syncEnabled=a,this.maxSyncedBodies=l,!Number.isInteger(n)||n<1||!Number.isInteger(l)||l<0)throw Error("capacity and maxSyncedBodies must be valid integer counts");if(this.capacity=n,this.collisionOptions={broadphase:c,bvh:d,solverMode:u,smallGroups:f},this.ref=new ai,this.ref.dt=xo(r,"timeStep"),!Number.isInteger(o)||o<1)throw Error("iterations must be a positive integer");this.ref.iterations=o,this.aggregates=[],this.constraints=new Set,this.contactListeners=new Set;for(let[y,A]of Object.entries({maxContactPairs:m,maxContactEvents:h}))if(!Number.isInteger(A)||A<1)throw Error(`${y} must be a positive integer`);if(this.contactOptions={maxContactPairs:m,maxContactEvents:h},this.bodyReadback=new vi(e),this.sleepOptions=g,g!==!1&&g!==!0&&(typeof g!="object"||g===null))throw Error("sleeping must be a boolean or options object");this.commands=new Map,this.steps=0,this.errors=[],this.deviceLost=null;let b=new WeakRef(this);e.lost.then(y=>{let A=b.deref();!A||A.disposed||(A.deviceLost={reason:y.reason,message:y.message},A.errors.push(`WebGPU device lost: ${y.message||y.reason}`),A.dispose())}),this.setGravity(i),this.errorListener=y=>this.errors.push(y.error.message),e.addEventListener("uncapturederror",this.errorListener),t&&hs.set(t,this)}setGravity(e){let t=Le(e);if(t.length!==3||!t.every(Number.isFinite))throw Error("gravity requires three finite components");let i=Math.hypot(...t);this.gravity=-i,this.up=i?t.map(r=>-r/i):[0,1,0],this.gpu&&(this.gpu.params.gravity=this.gravity,this.gpu.params.up=this.up),this.wakeAll()}get solverDecision(){return this.initialize(),{...this.gpu.solverDecision}}setSolverMode(e){this.initialize();let t=this.gpu.setSolverMode(e);return this.collisionOptions.solverMode=e,{...t}}initialize(){if(this.assertAlive(),this.gpu)return;if(this.syncEnabled&&this.aggregates.filter(r=>r.sync).length>this.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(this.aggregates.length>this.capacity)throw Error(`Body capacity ${this.capacity} exceeded; size the world before initialization`);this.gpu=new gi(this.device,this.ref,go(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 ms(e,t,i,this)}addAggregates(e){return e.map(({mesh:t,type:i,options:r})=>this.addAggregate(t,i,r))}addHeightfield(e){return this.addTerrain(Mr(e),e)}addStaticMesh(e,t={}){e.computeWorldMatrix(!0);let i=e.getVerticesData("position"),r=e.getWorldMatrix().m;if(!i)throw Error("Static mesh requires vertex positions");let o=new Float64Array(i.length);for(let u=0;u<i.length;u+=3){let f=i[u],d=i[u+1],p=i[u+2];for(let m=0;m<3;m++)o[u+m]=r[m]*f+r[4+m]*d+r[8+m]*p+r[12+m]}let n=e.getIndices(),a=n?.length?Uint32Array.from(n):Uint32Array.from({length:i.length/3},(u,f)=>f),l=e.getVerticesData("normal"),c=e.getWorldMatrix().determinant()<0;for(let u=0;u<a.length;u+=3){let f=a[u]*3,d=a[u+1]*3,p=a[u+2]*3,m=[0,1,2].map(b=>i[d+b]-i[f+b]),h=[0,1,2].map(b=>i[p+b]-i[f+b]),g=!1;l?.length===i.length&&(g=[m[1]*h[2]-m[2]*h[1],m[2]*h[0]-m[0]*h[2],m[0]*h[1]-m[1]*h[0]].reduce((y,A,v)=>y+A*(l[f+v]+l[d+v]+l[p+v]),0)<0),g!==c&&([a[u+1],a[u+2]]=[a[u+2],a[u+1]])}return this.addTerrain(zr({positions:o,indices:a,thickness:t.thickness}),t)}addTerrain(e,t={}){if(this.assertAlive(),this.gpu)throw Error("Add static terrain before initializing the physics world");if(this.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Terrain collisions require nine storage buffer bindings");if(this.aggregates.length+(e?.pieces?.length??0)>this.capacity)throw Error("AVBD terrain exceeds body capacity; reserve space for its collision pieces");let r=vo(this.ref,e,t).map(n=>new Pa(this,n)),o=!1;return{bodies:r.map(n=>n.body),triangleCount:e.triangleCount,kind:e.kind,dispose:()=>{if(o)return;o=!0;let n=new Set(r.map(a=>a.body));for(let a of[...this.constraints])(n.has(a.bodyA)||n.has(a.bodyB))&&a.dispose();if(this.gpu&&!this.disposed){let a=r.map(c=>c.body.gpuIndex),l=new Uint32Array(a.length);this.gpu.setFilters(a,l,l)}for(let a of r)a.disposed=a.body.disposed=!0,a.group=a.collidesWith=0,this.commands.delete(a.body);this.wakeAll()}}}addConstraint(e,t,i={}){return new Pr(this,e,t,i)}addJoint(e,t,i={}){return this.addConstraint(e,t,{...i,type:i.type??"ball"})}addWeld(e,t,i={}){return this.addConstraint(e,t,{...i,type:"fixed"})}addSpring(e,t,i={}){return this.addConstraint(e,t,{...i,type:"spring"})}enableSleeping(e={}){return this.assertAlive(),this.sleepOptions=e,this.gpu&&!this.sleepManager&&(this.sleepManager=new Fi(this,e)),this}disableSleeping(){return this.assertAlive(),this.sleepOptions=!1,this.gpu?(this.flushCommands(),this.sleepManager&&(this.sleepManager.wakeRequested=!0,this.sleepManager.before(),this.sleepManager.dispose(),this.sleepManager=null),this):this}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new Lr(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new xi(this.device,this.gpu.bodyBuffer,this.capacity,e,t);let i=this.aggregates.filter(r=>!r.disposed);this.contactWatch.setWatched(i.map(r=>r.body.gpuIndex),i.map(()=>!0))}onContact(e){if(this.assertAlive(),typeof e!="function")throw Error("A contact callback is required");return this.contactListeners.add(e),this.gpu&&this.initializeContacts(),()=>this.contactListeners.delete(e)}async readContactEvents(){return this.initialize(),this.initializeContacts(),this.contactPending?this.contactPending:(this.contactPending=this.contactWatch.read().then(e=>({...e,events:e.events.map(t=>({...t,type:t.kind===Ai?"begin":"end",isTrigger:!!(this.byGpuIndex.get(t.a)?.aggregate.isTrigger||this.byGpuIndex.get(t.b)?.aggregate.isTrigger),a:this.byGpuIndex.get(t.a),b:this.byGpuIndex.get(t.b)}))})).finally(()=>this.contactPending=null),this.contactPending)}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}async sphereCast(e,t,i,r={}){let[o]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}async sphereCastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){this.assertAlive(),Ir(i),this.initialize(),this.flushCommands();for(let n of t)if(n.aggregate.world!==this||n.disposed)throw Error("Ignored cast bodies must belong to this world and be alive");return(await this.gpu.sphereCastAll(e.map(n=>({...n,origin:Le(n.origin),direction:Le(n.direction)})),{ignore:t.map(n=>n.gpuIndex),collidesWith:i,includeTriggers:r})).map(n=>n?{...n,body:this.byGpuIndex.get(n.index)??null}:null)}async raycastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){Ir(i),this.initialize(),this.flushCommands();let o=e.map(a=>{let l=Le(a.origin),c=Le(a.direction);if(l.length!==3||c.length!==3||![...l,...c].every(Number.isFinite)||!Math.hypot(...c))throw Error("A ray requires a finite origin and nonzero direction");return a.maxDistance!==void 0&&xo(a.maxDistance,"maxDistance",!0),{...a,origin:l,direction:c}});for(let a of t)if(a.aggregate.world!==this)throw Error("Ignored ray bodies must belong to this world");return(await this.gpu.raycastAll(o,{ignore:t.map(a=>a.gpuIndex),collidesWith:i,includeTriggers:r})).map(a=>a?{...a,body:this.byGpuIndex.get(a.index)??null}:null)}getRenderBinding(e=this.device){if(this.initialize(),e!==this.device)throw Error("Direct GPU rendering must share the physics GPUDevice");return{device:e,buffer:this.bodyBuffer,stride:160,positionOffset:0,rotationOffset:16,sizeOffset:64,count:this.gpu.bodyCount}}wakeAll(){this.sleepManager&&(this.sleepManager.wakeRequested=!0)}async readSleepStats(){return this.initialize(),this.sleepManager?this.sleepManager.readStats():{sleeping:0,wakeRequested:!1}}assertAlive(){if(this.disposed)throw Error("AVBD world is disposed")}queue(e,t,i,r){this.assertAlive(),e.assertAlive();let o=Le(r);if(o.length!==(i==="rotation"?4:3)||!o.every(Number.isFinite))throw Error(`${i} requires ${i==="rotation"?4:3} finite components`);let n=this.commands.get(e);return n||this.commands.set(e,n={mask:0}),n.mask|=t,n[i]=o,this.wakeAll(),n}flushCommands(e){if(this.initialize(),this.gpu.flushPropertyEdits(e),!this.commands.size)return;this.sleepManager?.wakeRequested&&this.sleepManager.before(e);let t=this.commands.size,i=0;for(let h of this.commands.values())i|=h.mask;if(i===1||i===2){if(!this.velocityIndices||this.velocityIndices.length<t){let y=Math.max(t,(this.velocityIndices?.length??0)*2,64);this.velocityIndices=new Uint32Array(y),this.velocityValues=new Float32Array(y*3)}let h=0;for(let[y,A]of this.commands)this.velocityIndices[h]=y.aggregate.gpuSlot??this.gpu.gpuIndex(y.aggregate.index),this.velocityValues.set(i===2?A.angular:A.velocity,h++*3);let g=i===2?"angularVelocityBatch":"velocityBatch",b=this[g]??=new Kt(this.gpu,3,i===2?"angular":"linear");b.solver=this.gpu,b.submitValidated(this.velocityIndices.subarray(0,t),this.velocityValues.subarray(0,t*3),e),this.lastCommandBatch={bodies:t,path:i===2?"packed-angular-velocities":"packed-velocities",uploadedBytes:t*16},this.commands.clear();return}let r=!0;for(let h of this.commands.values())if((h.mask&-4)!==0){r=!1;break}if(r){(!this.motionData||this.motionData.length<t*8)&&(this.motionData=new Uint32Array(Math.max(t*8,(this.motionData?.length??0)*2,512)),this.motionFloats=new Float32Array(this.motionData.buffer));let h=0;for(let[g,b]of this.commands){let y=h++*8;this.motionData[y]=g.aggregate.gpuSlot??this.gpu.gpuIndex(g.aggregate.index),this.motionData[y+1]=b.mask,b.velocity&&this.motionFloats.set(b.velocity,y+2),b.angular&&this.motionFloats.set(b.angular,y+5)}this.motionBatch??=new yi(this.gpu,3),this.motionBatch.submitValidated(this.motionData.subarray(0,t*8),e),this.lastCommandBatch={bodies:t,path:"packed-motion",uploadedBytes:t*32},this.commands.clear();return}let o=t*128,n=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize);if(o>n)throw Error("Body command batch exceeds GPU buffer limits");(!this.commandData||this.commandData.byteLength<o)&&(this.commandData=new ArrayBuffer(Math.min(n,Math.max(o,(this.commandData?.byteLength??0)*2,128))));let a=this.commandData,l=new Float32Array(a),c=new Uint32Array(a);l.fill(0,0,t*32);let u=0;for(let[h,g]of this.commands){let b=u++*32;c[b]=h.aggregate.gpuSlot??this.gpu.gpuIndex(h.aggregate.index),c[b+1]=g.mask;for(let[y,A]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])g[y]&&l.set(g[y],b+A)}this.commands.clear(),(!this.commandBuffer||this.commandBuffer.size<o)&&(this.commandBuffer?.destroy(),this.commandBuffer=this.device.createBuffer({size:Math.min(n,Math.max(o*2,128)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),this.device.queue.writeBuffer(this.commandBuffer,0,a,0,o),this.commandPipeline??=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
|
|
3820
|
+
${bc}`,label:"AVBD body commands"}),entryPoint:"applyCommands"}}),(!this.commandBinding||this.boundCommandBody!==this.gpu.bodyBuffer||this.boundCommandBuffer!==this.commandBuffer||this.boundCommandBytes!==o)&&(this.commandBinding=this.device.createBindGroup({layout:this.commandPipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.gpu.bodyBuffer}},{binding:1,resource:{buffer:this.commandBuffer,size:o}}]}),this.boundCommandBody=this.gpu.bodyBuffer,this.boundCommandBuffer=this.commandBuffer,this.boundCommandBytes=o);let f=e??this.device.createCommandEncoder(),d=f.beginComputePass();d.setPipeline(this.commandPipeline),d.setBindGroup(0,this.commandBinding);let p=Math.ceil(u/64),m=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(p,m),Math.ceil(p/m)),d.end(),e||this.device.queue.submit([f.finish()]),this.lastCommandBatch={bodies:t,path:"aggregate-commands",uploadedBytes:o}}step(){this.initialize(),this.gpu.step(),this.steps++,this.contactWatch&&(this.contactWatch.run(this.gpu.contactStorage,this.gpu.manifoldCapacity,this.steps,this.ref.dt),this.contactListeners.size&&!this.contactPending&&this.readContactEvents().then(({events:e,dropped:t,full:i})=>{if(!this.disposed){(t||i)&&this.errors.push(`Contact event storage full: ${t} events dropped; increase maxContactEvents/maxContactPairs`);for(let r of e)for(let o of this.contactListeners)o(r)}}).catch(e=>{this.disposed||this.errors.push(e.message)})),this.syncEnabled&&!this.syncPending&&this.syncMeshes().catch(e=>this.errors.push(e.message)),this.steps%20===0&&!this.counterPending&&(this.counterPending=!0,this.gpu.readCounters().then(e=>{this.disposed||(this.counters=e,this.gpu.adapt(e),(e.overflow||e.clashes)&&this.errors.push(`Collision capacity/color conflict: ${JSON.stringify(e)}`))}).catch(e=>{this.disposed||this.errors.push(e.message)}).finally(()=>this.counterPending=!1))}async syncMeshes(){if(this.initialize(),this.flushCommands(),this.syncPending)return this.syncPending;let e=this.aggregates.filter(t=>t.sync&&!t.disposed);return this.syncPending=this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.body.gpuIndex),!0).then(t=>{if(!this.disposed){for(let i=0;i<e.length;i++)e[i].disposed||e[i].syncPose(t,i*8);this.lastSyncBytes=t.byteLength}}).finally(()=>this.syncPending=null),this.syncPending}get bodyBuffer(){return this.initialize(),this.gpu.bodyBuffer}async readBodies(e){if(this.initialize(),this.flushCommands(),e){for(let t of e)if(t.aggregate.world!==this)throw Error("Selected bodies must belong to this world");return this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.gpuIndex))}return this.gpu.readBodies()}dispose(){if(!this.disposed){this.disposed=!0,this.observer&&this.scene?.onBeforeRenderObservable.remove(this.observer),this.disposeObserver&&this.scene?.onDisposeObservable.remove(this.disposeObserver),this.scene&&hs.get(this.scene)===this&&hs.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}},ms=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 ps?f:hs.get(f??e.getScene()),!f)throw Error("Create an AVBD world for this scene before adding aggregates");if(f.assertAlive(),typeof t=="number"&&(t=[rt.SPHERE,rt.CAPSULE,rt.CYLINDER,rt.BOX,rt.CONVEX_HULL][t]),xo(i,"mass",!0),xo(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(rt).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=Ir(c),this.collidesWith=Ir(u),f.gpu&&f.syncEnabled&&l&&f.aggregates.filter(v=>v.sync&&!v.disposed).length>=f.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(![rt.BOX,rt.SPHERE,rt.CAPSULE].includes(t)&&f.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Convex hull collisions require nine storage buffer bindings");e.computeWorldMatrix(!0),e.refreshBoundingInfo();let d=e.getBoundingInfo().boundingBox,p=e.scaling;if([p.x,p.y,p.z].some(v=>v<=0))throw Error("Bake negative or zero mesh scaling before creating an aggregate");let m=d.maximum.subtract(d.minimum).multiply(p),h=d.center.multiply(p),g=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=h,this.localRotation=g.constructor.Identity();let b;if(t===rt.BOX)b=new Ze(f.ref,Le(m),1,r,[0,0,0]);else if(t===rt.SPHERE){if(Math.max(m.x,m.y,m.z)-Math.min(m.x,m.y,m.z)>1e-4)throw Error("Sphere aggregate requires uniform dimensions");b=ar(f.ref,m.x/2,1,r,[0,0,0])}else if(t===rt.CAPSULE){if(Math.abs(m.x-m.z)>1e-4)throw Error("Capsule aggregate requires equal X and Z diameters; bake an upright Y-axis capsule mesh");b=Cr(f.ref,m.x/2,m.y,1,r,[0,0,0])}else{let v=e.getVerticesData("position");if(!v)throw Error("Hull aggregate requires mesh positions");let x=Float32Array.from(v,(B,L)=>B*[p.x,p.y,p.z][L%3]),w=Xr(x);if(!w)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...w.center),this.localRotation=g.constructor.FromArray(w.rotation),b=lr(f.ref,w,1,r,[0,0,0])}let y=i/b.mass;b.mass=i,b.moment.forEach((v,x)=>b.moment[x]=v*y);let A=e.position.add(this.localCenter.applyRotationQuaternion(g));if(b.positionLin.set(Le(A)),b.positionAng.set(g.multiply(this.localRotation).asArray()),this.rigid=b,this.index=f.aggregates.length,this.body=new Rr(this),f.aggregates.push(this),e.rotationQuaternion=g,f.gpu){if(this.gpuSlot=f.gpu.addBody(b),this.gpuSlot<0)throw Error("AVBD body capacity exceeded");f.byGpuIndex.set(this.gpuSlot,this.body),f.gpu.setFilters([this.gpuSlot],[this.group],[this.collidesWith]),(this.isTrigger||f.gpu.sensorsEnabled)&&f.gpu.setSensor(this.gpuSlot,this.isTrigger),this.restitution>0&&f.gpu.setRestitution(this.gpuSlot,this.restitution),f.contactWatch?.setWatched([this.gpuSlot],[!0]),f.sleepManager?.register(this.gpuSlot,b.mass,this.allowSleep),f.wakeAll?.()}}syncPose(e,t){let i=t??this.body.gpuIndex*40,r=this.localRotation.constructor.FromArray(e,i+4).multiply(this.localRotation.conjugate());this.mesh.rotationQuaternion.copyFrom(r),this.mesh.position.copyFrom(new this.mesh.position.constructor(...e.subarray(i,i+3)).subtract(this.localCenter.applyRotationQuaternion(r)))}dispose(){wc(this)}},Pa=class{constructor(e,t){Object.assign(this,{world:e,rigid:t,index:e.aggregates.length,sync:!1,allowSleep:!1,isTrigger:!1,restitution:t.restitution,group:t.group,collidesWith:t.collidesWith,staticTerrain:!0}),this.body=new Rr(this),e.aggregates.push(this)}dispose(){wc(this)}};function wc(s){if(s.disposed)return;let e=s.world;for(let t of[...e.constraints])(t.bodyA===s.body||t.bodyB===s.body)&&t.dispose();if(e.gpu){let t=s.body.gpuIndex;e.queue(s.body,16,"velocity",[0,0,0]),e.gpu.setFilters([t],[0],[0]),e.gpu.setRestitution(t,0)}s.rigid.mass=0,s.rigid.moment.fill(0),s.sync=!1,s.disposed=s.body.disposed=!0,e.sleepManager?.register(s.gpuSlot??e.gpu?.gpuIndex(s.index)??s.index,0),e.wakeAll?.()}var Rr=class{constructor(e){this.aggregate=e}assertAlive(){if(this.disposed||this.aggregate.disposed)throw Error("AVBD body is disposed")}get gpuIndex(){this.assertAlive();let e=this.aggregate.world;return e.initialize(),this.aggregate.gpuSlot??e.gpu.gpuIndex(this.aggregate.index)}setLinearVelocity(e){return this.aggregate.world.queue(this,1,"velocity",e),this}setAngularVelocity(e){return this.aggregate.world.queue(this,2,"angular",e),this}applyImpulse(e,t){let i=t?Le(t):null;if(i&&(i.length!==3||!i.every(Number.isFinite)))throw Error("Impulse point requires three finite components");let r=this.aggregate.world,o=Le(e),n=r.commands.get(this),a=o.map((c,u)=>c+(n?.impulse?.[u]||0)),l=r.queue(this,4,"impulse",a);if(t){let c=[i[1]*o[2]-i[2]*o[1],i[2]*o[0]-i[0]*o[2],i[0]*o[1]-i[1]*o[0]],u=l.torque||[0,0,0];l.torque=Le(c).map((f,d)=>f+u[d]),l.pointImpulse=o.map((f,d)=>f+(l.pointImpulse?.[d]||0))}return this}applyAngularImpulse(e){let t=this.aggregate.world,i=t.commands.get(this)?.torque||[0,0,0];return t.queue(this,4,"torque",Le(e).map((r,o)=>r+i[o])),this}applyForce(e,t){return this.applyImpulse(Le(e).map(i=>i*this.aggregate.world.ref.dt),t)}applyTorque(e){return this.applyAngularImpulse(Le(e).map(t=>t*this.aggregate.world.ref.dt))}setCollisionGroups(e,t=4294967295){this.assertAlive();let i=this.aggregate,r=Ir(e),o=Ir(t);return i.group=r,i.collidesWith=o,i.world.gpu&&i.world.gpu.setFilters([this.gpuIndex],[i.group],[i.collidesWith]),i.world.wakeAll?.(),this}setTrigger(e){if(this.assertAlive(),typeof e!="boolean")throw Error("isTrigger must be a boolean");let t=this.aggregate;return t.isTrigger=e,t.world.gpu&&t.world.gpu.setSensor(this.gpuIndex,e),t.world.wakeAll(),this}setRestitution(e){if(this.assertAlive(),!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");let t=this.aggregate;return t.restitution=e,t.world.gpu&&t.world.gpu.setRestitution(this.gpuIndex,e),t.world.wakeAll(),this}async readState(){let e=await this.aggregate.world.readBodies([this]),t=this.aggregate.rigid.mass;return{position:Array.from(e.subarray(0,3)),rotation:Array.from(e.subarray(4,8)),linearVelocity:Array.from(e.subarray(32,35)),angularVelocity:Array.from(e.subarray(36,39)),mass:t,effectiveMass:e[19],sleeping:t>0&&e[19]===0}}wakeUp(){return this.assertAlive(),this.aggregate.world.wakeAll(),this}setSleepEnabled(e){if(this.assertAlive(),typeof e!="boolean")throw Error("Sleep eligibility must be a boolean");let t=this.aggregate,i=t.world;return t.allowSleep=e,e&&!i.sleepOptions&&i.enableSleeping(),i.gpu&&i.sleepManager?.register(this.gpuIndex,t.rigid.mass,e),i.wakeAll(),this}teleport(e,t){if(this.assertAlive(),this.aggregate.staticTerrain)throw Error("Terrain is static; set its geometry transform before registration");let i=this.aggregate;if(t===void 0){let n=i.world.commands.get(this);if(n&&(n.mask&8)!==0&&(n.mask&32)===0){let a=i.mesh.rotationQuaternion.clone();a.copyFromFloats(...n.rotation);let l=i.localCenter.applyRotationQuaternion(i.localRotation.conjugate());i.world.queue(this,8,"position",Le(e.add(l.applyRotationQuaternion(a))))}else{let a=i.localCenter.applyRotationQuaternion(i.localRotation.conjugate());i.world.queue(this,40,"position",Le(e)),i.world.queue(this,40,"rotation",[...Le(a),0])}return this}let r=t.clone().normalize();i.world.queue(this,8,"position",Le(e.add(i.localCenter.applyRotationQuaternion(r))));let o=i.world.queue(this,8,"rotation",r.multiply(i.localRotation).asArray());return o.mask&=-33,this}};var qi=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 Bc=s=>s<0?-1:s>0?1:0,Or=(s,e)=>s<e?s:e,Jf=(s,e)=>s>e?s:e,Tr=(s,e,t)=>Jf(e,Or(t,s));function gs(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function Vi(s,e,t){let i=Math.cos(s[2]),r=Math.sin(s[2]);return[i*e-r*t+s[0],r*e+i*t+s[1]]}function Zt(s,e,t,i,r){s[e*3]=t,s[e*3+1]=i,s[e*3+2]=r}var Ao=class extends qi{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?Vi(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=Vi(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=gs(e.position[2],this.rA[0],this.rA[1]);Zt(t,0,1,0,-r[1]),Zt(t,1,0,1,r[0]),Zt(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=gs(e.position[2],this.rB[0],this.rB[1]);Zt(t,0,-1,0,r[1]),Zt(t,1,0,-1,-r[0]),Zt(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},wo=class extends qi{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=Vi(t.position,r[0],r[1]),c=Vi(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=Vi(this.bodyA.position,this.rA[0],this.rA[1]),t=Vi(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=gs(e.position[2],-d[1],d[0]),m=gs(e.position[2],d[0],d[1]),h=a*p[0]+l*p[1],g=c*p[0]+u*p[1],b=o*m[0]+n*m[1],y=o*p[0]+n*p[1];f?Zt(this.J,0,o,n,y):Zt(this.J,0,-o,-n,-y);let A=f?-b-b:b+b,v=this.H;v[0]=a,v[1]=l,v[2]=h,v[3]=c,v[4]=u,v[5]=g,v[6]=h,v[7]=g,v[8]=A}},Bo=class extends qi{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},Co=class extends qi{speed;constructor(e,t,i,r,o){super(e,t,i),this.speed=r,this.fmax[0]=o,this.fmin[0]=-o}rows(){return 1}initialize(){return!0}computeConstraint(){let e=this.bodyA?this.bodyA.position[2]-this.bodyA.initial[2]:0,t=this.bodyB.position[2]-this.bodyB.initial[2];this.C[0]=e-t-this.speed*this.solver.dt}computeDerivatives(e){Zt(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var So=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),ko=()=>({...So(),postStabilize:!1,alpha:.95,matchNearest:!0});var jr=(s,e)=>s>e?s*2097152+e:e*2097152+s;function Xf(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 La(s,e,t){let i=Xf(s,e,t);return i<e&&s[i]===t}var Po=class{cellSize=1;pairs=new Float64Array(1024);pairCount=0;configuredFor=-1;large=new Int32Array(0);isLarge=new Uint8Array(0);cellX=new Int32Array(0);cellY=new Int32Array(0);tableSize=0;bucketStart=new Int32Array(0);sorted=new Int32Array(0);configure(e,t){this.configuredFor=e;let i=Float64Array.from({length:e},(a,l)=>t[l*4+1]).sort(),r=e>0?i[e>>1]:1,o=0;for(let a=0;a<e;a++)i[a]<=4*r&&(o=Math.max(o,i[a]));this.cellSize=Math.max(2*o,.001),this.isLarge=new Uint8Array(e);let n=[];for(let a=0;a<e;a++)t[a*4+1]>o&&(this.isLarge[a]=1,n.push(a));for(this.large=Int32Array.from(n),this.cellX=new Int32Array(e),this.cellY=new Int32Array(e),this.tableSize=1;this.tableSize<2*e;)this.tableSize<<=1;this.bucketStart=new Int32Array(this.tableSize+1),this.sorted=new Int32Array(e)}hash(e,t){return(Math.imul(e,73856093)^Math.imul(t,19349663))&this.tableSize-1}findPairs(e,t,i,r,o,n){e!==this.configuredFor&&this.configure(e,i),this.pairCount=0;let{cellX:a,cellY:l,isLarge:c,bucketStart:u,sorted:f}=this,d=1/this.cellSize;u.fill(0);for(let h=0;h<e;h++)c[h]||(a[h]=Math.floor(t[h*4]*d),l[h]=Math.floor(t[h*4+1]*d),u[this.hash(a[h],l[h])+1]++);for(let h=0;h<this.tableSize;h++)u[h+1]+=u[h];let p=u.slice(0,this.tableSize);for(let h=0;h<e;h++)c[h]||(f[p[this.hash(a[h],l[h])]++]=h);let m=(h,g)=>{if(!r[h]&&!r[g])return;let b=t[h*4]-t[g*4],y=t[h*4+1]-t[g*4+1],A=i[h*4+1]+i[g*4+1];if(b*b+y*y>A*A)return;let v=jr(h,g);if(!La(o,n,v)){if(this.pairCount===this.pairs.length){let x=new Float64Array(this.pairs.length*2);x.set(this.pairs),this.pairs=x}this.pairs[this.pairCount++]=v}};for(let h=0;h<e;h++){if(c[h])continue;let g=a[h],b=l[h];for(let y=-1;y<=1;y++)for(let A=-1;A<=1;A++){let v=this.hash(g+A,b+y);for(let x=u[v];x<u[v+1];x++){let w=f[x];w>=h||a[w]!==g+A||l[w]!==b+y||m(h,w)}}}for(let h of this.large)for(let g=0;g<e;g++)g===h||c[g]&&g>h||m(h,g);this.pairs.subarray(0,this.pairCount).sort()}};var gt=new Float64Array(12),Cc=new Float64Array(12),Wi=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 Sc(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 Lo(s,e,t,i,r,o,n,a){let l=-(r*n+o*a),c=-(-o*n+r*a);Math.abs(l)>Math.abs(c)?l>0?(qt(gt,0,s,-e,0,0,3,4),qt(gt,1,s,e,0,0,4,1)):(qt(gt,0,-s,e,0,0,1,2),qt(gt,1,-s,-e,0,0,2,3)):c>0?(qt(gt,0,s,e,0,0,4,1),qt(gt,1,-s,e,0,0,1,2)):(qt(gt,0,-s,-e,0,0,2,3),qt(gt,1,s,-e,0,0,3,4));for(let u=0;u<2;u++){let f=u*6,d=gt[f],p=gt[f+1];gt[f]=t+r*d-o*p,gt[f+1]=i+o*d+r*p}}function _o(s,e,t,i,r,o,n,a,l,c,u){let f=Math.cos(t),d=Math.sin(t),p=Math.cos(a),m=Math.sin(a),h=o-s,g=n-e,b=f*h+d*g,y=-d*h+f*g,A=p*h+m*g,v=-m*h+p*g,x=Math.abs(f*p+d*m),w=Math.abs(f*-m+d*p),B=Math.abs(-d*p+f*m),L=Math.abs(-d*-m+f*p),M=Math.abs(b)-i-(x*l+w*c),I=Math.abs(y)-r-(B*l+L*c);if(M>0||I>0)return 0;let E=Math.abs(A)-(x*i+B*r)-l,k=Math.abs(v)-(w*i+L*r)-c;if(E>0||k>0)return 0;let P=0,C=M,_=b>0?f:-f,z=b>0?d:-d;I>.95*C+.01*r&&(P=1,C=I,_=y>0?-d:d,z=y>0?f:-f),E>.95*C+.01*l&&(P=2,C=E,_=A>0?p:-p,z=A>0?m:-m),k>.95*C+.01*c&&(P=3,C=k,_=v>0?-m:m,z=v>0?p:-p);let R,G,X,W,U,H,Z,he,we;if(P===0){R=_,G=z,X=s*R+e*G+i,W=-d,U=f;let $=s*W+e*U;H=-$+r,Z=$+r,he=3,we=1,Lo(l,c,o,n,p,m,R,G)}else if(P===1){R=_,G=z,X=s*R+e*G+r,W=f,U=d;let $=s*W+e*U;H=-$+i,Z=$+i,he=2,we=4,Lo(l,c,o,n,p,m,R,G)}else if(P===2){R=-_,G=-z,X=o*R+n*G+l,W=-m,U=p;let $=o*W+n*U;H=-$+c,Z=$+c,he=3,we=1,Lo(i,r,s,e,f,d,R,G)}else{R=-_,G=-z,X=o*R+n*G+c,W=p,U=m;let $=o*W+n*U;H=-$+l,Z=$+l,he=2,we=4,Lo(i,r,s,e,f,d,R,G)}if(Sc(Cc,gt,-W,-U,H,he)<2||Sc(Wi,Cc,W,U,Z,we)<2)return 0;let Oe=0;for(let $=0;$<2;$++){let Je=$*6,Xe=Wi[Je],Be=Wi[Je+1],nt=R*Xe+G*Be-X;if(nt>0)continue;let ri=Xe-R*nt,Ye=Be-G*nt,at=Wi[Je+2],Qe=Wi[Je+3],lt=Wi[Je+4],wt=Wi[Je+5],Ki,Zi,er,tr;P>=2?([at,Qe,lt,wt]=[lt,wt,at,Qe],Ki=Xe-s,Zi=Be-e,er=ri-o,tr=Ye-n):(Ki=ri-s,Zi=Ye-e,er=Xe-o,tr=Be-n);let Wt=Oe*7;u[Wt+0]=at&255|(Qe&255)<<8|(lt&255)<<16|(wt&255)<<24,u[Wt+1]=f*Ki+d*Zi,u[Wt+1+1]=-d*Ki+f*Zi,u[Wt+3]=p*er+m*tr,u[Wt+3+1]=-m*er+p*tr,u[Wt+5]=-_,u[Wt+5+1]=-z,Oe++}return Oe}var kc=s=>31-Math.clz32(s&-s);function Yf(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 _a=(s,e,t,i)=>s>t||s===t&&e>i,Eo=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)=>Yf(r))}run(e,t,i,r,o,n){this.resize(e);let{colors:a,next:l,pending:c,prio:u}=this,f=(v,x)=>{let w=o[v*4+1],B=w===x?o[v*4+2]:w;return B>=0&&t[B]?B:-1},d=(v,x)=>{let w=0,B=0;for(let L=i[v];L<i[v+1];L++){let M=f(r[L],v);if(M<0||x&&c[M])continue;let I=a[M];I<0||(I<32?w|=1<<I:B|=1<<I-32)}return~w!==0?kc(~w):~B!==0?32+kc(~B):63};for(let v=0;v<e;v++){if(l[v]=a[v],!t[v]||a[v]<0)continue;let x=!0;for(let w=i[v];w<i[v+1]&&x;w++){let B=f(r[w],v);B>=0&&!_a(u[v],v,u[B],B)&&(x=!1)}x&&(l[v]=Math.min(a[v],d(v,!1)))}a.set(l);let p=()=>{let v=0;for(let x=0;x<e;x++)if(c[x]=0,!!t[x]){if(a[x]<0)c[x]=1;else for(let w=i[x];w<i[x+1];w++){let B=f(r[w],x);if(B>=0&&a[B]===a[x]&&_a(u[B],B,u[x],x)){c[x]=1;break}}v+=c[x]}return v},m=p(),h=0;for(;m>0&&h<n;){h++;for(let v=0;v<e;v++){if(l[v]=-2,!c[v])continue;let x=!0;for(let w=i[v];w<i[v+1]&&x;w++){let B=f(r[w],v);B>=0&&c[B]&&!_a(u[v],v,u[B],B)&&(x=!1)}x&&(l[v]=d(v,!0))}for(let v=0;v<e;v++)l[v]!==-2&&(a[v]=l[v],c[v]=0,m--)}let g=0;if(m>0){for(let v=0;v<e;v++)c[v]&&a[v]<0&&(a[v]=0);g=p()}let b=this.colorStart;b.fill(0);let y=0;for(let v=0;v<e;v++)t[v]&&(b[a[v]+1]++,y=Math.max(y,a[v]+1));for(let v=0;v<64;v++)b[v+1]+=b[v];let A=b.slice(0,64);for(let v=0;v<e;v++)t[v]&&(this.colorBodies[A[a[v]]++]=v);return{numColors:y,conflicts:g,rounds:h}}};var Pc=0,Bi=1,Lc=2,Ro=3,bs=4,_c=[0,3,1,1,2],Ma=.05,j=4,O=32,se=0,te=3,Q=6,_e=9,Ae=12,Pt=15,fe=18,N=21,Y=23,re=25,be=26,Ue=27,Hi=28,Dr=class{params=So();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 Po;coloring=new Eo;numColors=0;colorConflicts=0;colorRounds=0;profiling=!1;profile={};phaseStart=0;contactOut=new Float64Array(14);dxBuf=new Float64Array(0);constructor(e={}){this.options={precision:"f32",order:"colored",colorRounds:32,...e},this.pose=this.real(0),this.initial=this.real(0),this.inertial=this.real(0),this.velocity=this.real(0),this.prevVelocity=this.real(0),this.shape=this.real(0),this.props=this.real(0),this.data=this.real(0)}real(e){return this.options.precision==="f32"?new Float32Array(e):new Float64Array(e)}grow(e,t){if(e.length>=t)return e;let i=new e.constructor(Math.max(t,e.length*2,16));return i.set(e),i}clear(){this.bodyCount=0,this.jointCount=0,this.contactCount=0,this.prevCount=0,this.handles=[],this.ignorePairs=[],this.noCollideDirty=!0,this.coloring.resize(0)}addBody(e,t,i,r,o=[0,0,0]){let n=this.bodyCount++,a=this.bodyCount*4;this.pose=this.grow(this.pose,a),this.initial=this.grow(this.initial,a),this.inertial=this.grow(this.inertial,a),this.velocity=this.grow(this.velocity,a),this.prevVelocity=this.grow(this.prevVelocity,a),this.shape=this.grow(this.shape,a),this.props=this.grow(this.props,a),this.dynamic=this.grow(this.dynamic,this.bodyCount);let l=e[0]*e[1]*t;return this.pose.set([r[0],r[1],r[2]??0,0],n*4),this.velocity.set([o[0],o[1],o[2]??0,0],n*4),this.prevVelocity.set(this.velocity.subarray(n*4,n*4+4),n*4),this.shape.set([e[0],e[1],l,l*(e[0]*e[0]+e[1]*e[1])/12],n*4),this.props.set([i,Math.hypot(e[0]*.5,e[1]*.5),0,0],n*4),this.dynamic[n]=l>0?1:0,n}allocConstraint(){let e=this.jointCount++,t=this.jointCount+this.contactCount;this.info=this.grow(this.info,t*j),this.data=this.grow(this.data,t*O),this.contactCount>0&&(this.info.copyWithin((e+1)*j,e*j,(t-1)*j),this.data.copyWithin((e+1)*O,e*O,(t-1)*O)),this.data.fill(0,e*O,e*O+O);for(let i=0;i<3;i++)this.data[e*O+Q+i]=1/0,this.data[e*O+_e+i]=-1/0,this.data[e*O+Ae+i]=1/0,this.data[e*O+Pt+i]=1/0;return this.handles[e]=null,this.noCollideDirty=!0,e}setInfo(e,t,i,r){this.info.set([t,i,r,0],e*j)}addJoint(e,t,i,r,o=[1/0,1/0,1/0],n=1/0){let a=this.allocConstraint();this.setInfo(a,Bi,e,t);let l=this.data,c=a*O;l.set(o,c+Q),l[c+Ae+2]=n,l[c+_e+2]=-n,l[c+Pt+2]=n,l.set(i,c+N),l.set(r,c+Y),l[c+re]=(e>=0?this.pose[e*4+2]:0)-this.pose[t*4+2];let u=(e>=0?this.shape[e*4]:0)+this.shape[t*4],f=(e>=0?this.shape[e*4+1]:0)+this.shape[t*4+1];l[c+be]=u*u+f*f;let d={slot:a,alive:!0};return this.handles[a]=d,d}setJointWorldAnchor(e,t,i){e.alive&&(this.data[e.slot*O+N]=t,this.data[e.slot*O+N+1]=i)}removeJoint(e){e.alive&&(this.disable(e.slot),e.alive=!1)}addSpring(e,t,i,r,o,n){let a=this.allocConstraint();this.setInfo(a,Lc,e,t);let l=a*O;this.data[l+Q]=o,this.data.set(i,l+N),this.data.set(r,l+Y),this.data[l+re]=n}addMotor(e,t,i,r){let o=this.allocConstraint();this.setInfo(o,Ro,e,t);let n=o*O;this.data[n+Ae]=r,this.data[n+_e]=-r,this.data[n+re]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(jr(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(So());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 Ao){let a=this.addJoint(o,n,r.rA,r.rB,[r.stiffness[0],r.stiffness[1],r.stiffness[2]],r.fracture[2]);this.data[a.slot*O+re]=r.restAngle,this.data[a.slot*O+be]=r.torqueArm}else r instanceof wo?this.addSpring(o,n,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof Co?this.addMotor(o,n,r.speed,r.fmax[0]):r instanceof Bo&&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]=jr(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]!==Pc&&i>=0&&e.push(jr(i,r))}this.noCollide=Float64Array.from(e).sort(),this.noCollideCount=e.length,this.noCollideDirty=!1}this.broadphase.findPairs(this.bodyCount,this.pose,this.props,this.dynamic,this.noCollide,this.noCollideCount)}initJoints(){let{info:e,data:t,pose:i}=this,r=0;for(let o=0;o<this.jointCount;o++){let n=o*O;if(e[o*j]===Bi){if(t[n+Q]===0&&t[n+Q+1]===0&&t[n+Q+2]===0){let u=this.handles[o];u&&(u.alive=!1);continue}let l=e[o*j+1],c=e[o*j+2];this.jointC(l,c,n,i,t,n+fe)}r!==o&&(e.copyWithin(r*j,o*j,(o+1)*j),t.copyWithin(r*O,n,n+O),this.handles[r]=this.handles[o],this.handles[r]&&(this.handles[r].slot=r)),r++}if(r!==this.jointCount){for(let o=r;o<this.jointCount;o++)this.handles[o]=null;this.jointCount=r,this.noCollideDirty=!0}}jointC(e,t,i,r,o,n,a=o){let l,c,u;if(e>=0){let h=Math.cos(r[e*4+2]),g=Math.sin(r[e*4+2]),b=o[i+N],y=o[i+N+1];l=h*b-g*y+r[e*4],c=g*b+h*y+r[e*4+1],u=r[e*4+2]}else l=o[i+N],c=o[i+N+1],u=0;let f=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),p=o[i+Y],m=o[i+Y+1];a[n]=l-(f*p-d*m+r[t*4]),a[n+1]=c-(d*p+f*m+r[t*4+1]),a[n+2]=(u-r[t*4+2]-o[i+re])*o[i+be]}narrowphase(){let{pose:e,shape:t,props:i}=this,r=this.broadphase.pairs,o=this.contactOut;for(let n=0;n<this.broadphase.pairCount;n++){let a=r[n],l=Math.floor(a/2097152),c=a-l*2097152,u=_o(e[l*4],e[l*4+1],e[l*4+2],t[l*4]*.5,t[l*4+1]*.5,e[c*4],e[c*4+1],e[c*4+2],t[c*4]*.5,t[c*4+1]*.5,o);if(u===0)continue;let f=0,d=this.prevCount;for(;f<d;){let h=f+d>>>1;this.prevKeys[h]<a?f=h+1:d=h}let p=f,m=Math.sqrt(i[l*4]*i[c*4]);for(let h=0;h<u;h++){let g=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(g+1)*j),this.data=this.grow(this.data,(g+1)*O);let b=this.data,y=g*O,A=h*7,v=o[A+0];this.info.set([bs,l,c,v],g*j),b.fill(0,y,y+O),b[y+Q]=b[y+Q+1]=1/0,b[y+_e]=-1/0,b[y+Ae]=0,b[y+Pt]=b[y+Pt+1]=1/0,b[y+N]=o[A+1],b[y+N+1]=o[A+1+1],b[y+Y]=o[A+3],b[y+Y+1]=o[A+3+1],b[y+re]=m,b[y+be]=o[A+5],b[y+Ue]=o[A+5+1];let x=-1;for(let C=p;C<this.prevCount&&this.prevKeys[C]===a;C++)this.prevInfo[C*j+3]===v&&(x=C);if(x<0&&this.params.matchNearest){let C=Ma*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let _=p;_<this.prevCount&&this.prevKeys[_]===a;_++){let z=Math.hypot(this.prevData[_*O+N]-b[y+N],this.prevData[_*O+N+1]-b[y+N+1]);z<=C&&(C=z,x=_)}}if(x>=0){let C=x*O;b[y+se]=this.prevData[C+se],b[y+se+1]=this.prevData[C+se+1],b[y+te]=this.prevData[C+te],b[y+te+1]=this.prevData[C+te+1],this.prevData[C+Hi]&&(b[y+N]=this.prevData[C+N],b[y+N+1]=this.prevData[C+N+1],b[y+Y]=this.prevData[C+Y],b[y+Y+1]=this.prevData[C+Y+1])}let[w,B]=wi(e[l*4+2],b[y+N],b[y+N+1]),[L,M]=wi(e[c*4+2],b[y+Y],b[y+Y+1]),I=b[y+be],E=b[y+Ue],k=e[l*4]+w-e[c*4]-L,P=e[l*4+1]+B-e[c*4+1]-M;b[y+fe]=I*k+E*P+5e-4,b[y+fe+1]=E*k-I*P}}}warmStartConstraints(e,t){let i=this.params,r=this.data,o=this.jointCount+this.contactCount;for(let n=0;n<o;n++){let a=_c[this.info[n*j]],l=n*O;for(let c=0;c<a;c++){if(i.vbd){r[l+se+c]=Or(r[l+Q+c],i.vbdStiffness);continue}e||(r[l+te+c]=r[l+te+c]*t*i.gamma),r[l+se+c]=Or(Tr(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]=Tr(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*Bc(t),f=Tr(u/Math.abs(t),0,1);Number.isFinite(f)||(f=0),o[c]=i[c],o[c+1]=i[c+1],o[c+2]=i[c+2],i[c]=i[c]+r[c]*e,i[c+1]=i[c+1]+r[c+1]*e+t*f*e*e,i[c+2]=i[c+2]+r[c+2]*e}}eC=new Float64Array(3);eMin=new Float64Array(3);eMax=new Float64Array(3);eJ=new Float64Array(9);eG=new Float64Array(9);evalConstraint(e,t,i){let{info:r,data:o,pose:n,initial:a}=this,l=r[e*j],c=r[e*j+1],u=r[e*j+2],f=e*O,{eC:d,eMin:p,eMax:m,eJ:h,eG:g}=this,b=_c[l];for(let A=0;A<b;A++)p[A]=o[f+_e+A],m[A]=o[f+Ae+A];let y=i===c;if(g.fill(0),l===Bi){this.jointC(c,u,f,n,o,0,d);for(let A=0;A<3;A++)o[f+Q+A]===1/0&&(d[A]-=o[f+fe+A]*t);if(i>=0){let A=n[i*4+2],[v,x]=wi(A,o[f+(y?N:Y)],o[f+(y?N:Y)+1]),w=y?1:-1;h[0]=w,h[1]=0,h[2]=-w*x,h[3]=0,h[4]=w,h[5]=w*v,h[6]=0,h[7]=0,h[8]=w*o[f+be],g[2]=Math.abs(v),g[5]=Math.abs(x)}}else if(l===Lc){let[A,v]=Ec(n,c,o[f+N],o[f+N+1]),[x,w]=Ec(n,u,o[f+Y],o[f+Y+1]),B=A-x,L=v-w,M=B*B+L*L,I=Math.sqrt(M);if(d[0]=I-o[f+re],i>=0&&M!==0){let E=B/I,k=L/I,P=(1-E*E)/I,C=-E*k/I,_=(1-k*k)/I,z=n[i*4+2],R=y?N:Y,[G,X]=wi(z,-o[f+R+1],o[f+R]),[W,U]=wi(z,o[f+R],o[f+R+1]),H=P*G+C*X,Z=C*G+_*X,he=E*W+k*U,we=E*G+k*X,Oe=y?1:-1;h[0]=Oe*E,h[1]=Oe*k,h[2]=Oe*we;let $=y?-he-he:he+he;g[0]=Math.hypot(P,C,H),g[1]=Math.hypot(C,_,Z),g[2]=Math.hypot(H,Z,$)}else i>=0&&h.fill(0,0,3)}else if(l===Ro){let A=c>=0?n[c*4+2]-a[c*4+2]:0,v=n[u*4+2]-a[u*4+2];d[0]=A-v-o[f+re]*this.params.dt,i>=0&&(h[0]=0,h[1]=0,h[2]=y?1:-1)}else if(l===bs){let A=o[f+be],v=o[f+Ue],x=v,w=-A,[B,L]=wi(a[c*4+2],o[f+N],o[f+N+1]),[M,I]=wi(a[u*4+2],o[f+Y],o[f+Y+1]),E=B*v-L*A,k=-(M*v-I*A),P=B*w-L*x,C=-(M*w-I*x),_=n[c*4]-a[c*4],z=n[c*4+1]-a[c*4+1],R=n[c*4+2]-a[c*4+2],G=n[u*4]-a[u*4],X=n[u*4+1]-a[u*4+1],W=n[u*4+2]-a[u*4+2];d[0]=o[f+fe]*(1-t)+A*_+v*z+E*R+-A*G+-v*X+k*W,d[1]=o[f+fe+1]*(1-t)+x*_+w*z+P*R+-x*G+-w*X+C*W;let U=Math.abs(o[f+te])*o[f+re];m[1]=U,p[1]=-U,i>=0&&(y?(h[0]=A,h[1]=v,h[2]=E,h[3]=x,h[4]=w,h[5]=P):(h[0]=-A,h[1]=-v,h[2]=k,h[3]=-x,h[4]=-w,h[5]=C))}return b}solveBody(e,t){let i=this.params,{shape:r,pose:o,inertial:n,data:a}=this,l=i.dt*i.dt,c=r[e*4+2]/l,u=r[e*4+3]/l,f=c,d=0,p=c,m=0,h=0,g=u,b=c*(o[e*4]-n[e*4]),y=c*(o[e*4+1]-n[e*4+1]),A=u*(o[e*4+2]-n[e*4+2]),{eC:v,eMin:x,eMax:w,eJ:B,eG:L}=this,M=this.adjStart,I=this.adjList;for(let H=M[e+1]-1;H>=M[e];H--){let Z=I[H],he=this.evalConstraint(Z,t,e),we=Z*O,Oe=!i.vbd;for(let $=0;$<he;$++){let Je=a[we+Q+$],Xe=Oe&&Je===1/0?a[we+te+$]:0,Be=a[we+se+$],nt=v[$],ri=Be*nt+Xe,Ye=Tr(ri,x[$],w[$]),at=Math.abs(Ye);i.stiffnessRescale&&nt!==0&&(ri<x[$]?Be=Math.abs((x[$]-Xe)/nt):ri>w[$]&&(Be=Math.abs((w[$]-Xe)/nt)));let Qe=B[$*3],lt=B[$*3+1],wt=B[$*3+2];b+=Qe*Ye,y+=lt*Ye,A+=wt*Ye,f+=Qe*Qe*Be+L[$*3]*at,d+=lt*Qe*Be,p+=lt*lt*Be+L[$*3+1]*at,m+=wt*Qe*Be,h+=wt*lt*Be,g+=wt*wt*Be+L[$*3+2]*at}}let E=f,k=d/f,P=m/f,C=p-k*k*E,_=(h-k*P*E)/C,z=g-(P*P*E+_*_*C),R=y-k*b,X=(A-P*b-_*R)/z,W=R/C-_*X,U=b/E-k*W-P*X;this.dxBuf[e*3]=U,this.dxBuf[e*3+1]=W,this.dxBuf[e*3+2]=X}applyDx(e){this.pose[e*4]-=this.dxBuf[e*3],this.pose[e*4+1]-=this.dxBuf[e*3+1],this.pose[e*4+2]-=this.dxBuf[e*3+2]}primal(e){if(this.dxBuf.length<this.bodyCount*3&&(this.dxBuf=new Float64Array(this.bodyCount*3)),this.options.order==="sequential"){for(let r=this.bodyCount-1;r>=0;r--)this.dynamic[r]&&(this.solveBody(r,e),this.applyDx(r));return}let{colorStart:t,colorBodies:i}=this.coloring;for(let r=0;r<this.numColors;r++){for(let o=t[r];o<t[r+1];o++)this.solveBody(i[o],e);for(let o=t[r];o<t[r+1];o++)this.applyDx(i[o])}}dual(e){let t=this.params,i=this.data,{eC:r,eMin:o,eMax:n}=this,a=this.jointCount+this.contactCount;for(let l=0;l<a;l++){let c=this.info[l*j];if(c===Pc)continue;let u=this.evalConstraint(l,e,-1),f=l*O;c===bs&&(i[f+Hi]=Math.abs(i[f+te+1])<n[1]&&Math.abs(i[f+fe+1])<.01?1:0);for(let d=0;d<u;d++){let p=i[f+Q+d],m=!t.vbd&&p===1/0?i[f+te+d]:0,h=Tr(i[f+se+d]*r[d]+m,o[d],n[d]);i[f+te+d]=h,Math.abs(h)>=i[f+Pt+d]&&this.disable(l),!t.vbd&&h>o[d]&&h<n[d]&&(i[f+se+d]=Or(i[f+se+d]+t.beta*Math.abs(r[d]),Or(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+Hi]=Math.abs(e[i+te+1])<r&&Math.abs(e[i+fe+1])<.01?1:0}}disable(e){let t=e*O;this.data.fill(0,t+se,t+se+3),this.data.fill(0,t+te,t+te+3),this.data.fill(0,t+Q,t+Q+3)}updateVelocities(){let{dt:e}=this.params,{pose:t,initial:i,velocity:r,prevVelocity:o}=this;for(let n=0;n<this.bodyCount;n++){let a=n*4;o[a]=r[a],o[a+1]=r[a+1],o[a+2]=r[a+2],this.dynamic[n]&&(r[a]=(t[a]-i[a])/e,r[a+1]=(t[a+1]-i[a+1])/e,r[a+2]=(t[a+2]-i[a+2])/e)}}pick(e,t){for(let i=this.bodyCount-1;i>=0;i--){let[r,o]=wi(-this.pose[i*4+2],e-this.pose[i*4],t-this.pose[i*4+1]);if(Math.abs(r)<=this.shape[i*4]*.5&&Math.abs(o)<=this.shape[i*4+1]*.5)return{body:i,local:[r,o]}}return null}kineticEnergy(){let e=0;for(let t=0;t<this.bodyCount;t++){if(!this.dynamic[t])continue;let i=t*4,r=this.velocity;e+=.5*this.shape[i+2]*(r[i]*r[i]+r[i+1]*r[i+1])+.5*this.shape[i+3]*r[i+2]*r[i+2]}return e}maxJointError(){let e=0,t=new Float64Array(3);for(let i=0;i<this.jointCount;i++){if(this.info[i*j]!==Bi)continue;let r=i*O;this.jointC(this.info[i*j+1],this.info[i*j+2],r,this.pose,this.data,0,t);for(let o=0;o<2;o++)this.data[r+Q+o]===1/0&&(e=Math.max(e,Math.abs(t[o])))}return e}isContactPairIgnored(e,t){return La(this.noCollide,this.noCollideCount,jr(e,t))}};function wi(s,e,t){let i=Math.cos(s),r=Math.sin(s);return[i*e-r*t,r*e+i*t]}function Ec(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 ys=`
|
|
3821
|
+
${Ut}
|
|
3605
3822
|
|
|
3606
3823
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
3607
3824
|
@group(0) @binding(1) var<storage, read> bodies: array<Body>;
|
|
@@ -3740,8 +3957,8 @@ fn findPairs(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
3740
3957
|
testPair(l, i);
|
|
3741
3958
|
}
|
|
3742
3959
|
}
|
|
3743
|
-
`,
|
|
3744
|
-
${
|
|
3960
|
+
`,vs=`
|
|
3961
|
+
${Ut}
|
|
3745
3962
|
|
|
3746
3963
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
3747
3964
|
@group(0) @binding(1) var<storage, read> bodies: array<Body>;
|
|
@@ -4067,8 +4284,8 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
4067
4284
|
contacts[k] = rec;
|
|
4068
4285
|
}
|
|
4069
4286
|
}
|
|
4070
|
-
`;var
|
|
4071
|
-
${
|
|
4287
|
+
`;var xs=`
|
|
4288
|
+
${Ut}
|
|
4072
4289
|
|
|
4073
4290
|
// Per-dispatch constants, selected with a dynamic uniform offset
|
|
4074
4291
|
struct PassConstants {
|
|
@@ -4440,7 +4657,7 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
|
|
|
4440
4657
|
if (b.shape.z > 0.0) { b.vel = vec4f((b.pose.xyz - b.initial.xyz) / params.dt, 0.0); }
|
|
4441
4658
|
bodies[i] = b;
|
|
4442
4659
|
}
|
|
4443
|
-
`;var Ls=o=>o===1/0?3e38:o===-1/0?-3e38:o,Qe=o=>Math.ceil(o/64),Wn=o=>2**Math.ceil(Math.log2(Math.max(o,2))),Oc=o=>Math.max(o,1)*24*4,jc=4,Ke=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,_s=class{device;params=Bs();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:Oc(this.bodyCapacity),usage:Ke()});let r=this.bodyCapacity;this.tableSize=Wn(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:Ke()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:Ke()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:Ke()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:Ke()}),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*fe.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 Fe(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new Fe(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(Sr,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(kr,[i.contacts],["hashInsert","narrowphase"]),t(cr,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(Pr,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(Vr,[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:Ke()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:Ke()});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=Wn(4*s),this.pairBuffer=i.createBuffer({label:"pairs",size:a*8,usage:Ke()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:Ke()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:s*20*4,usage:Ke()})),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:Ke()}),this.adjScan?.destroy(),this.adjScan=new Fe(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:Ke()});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+ge+l],r[n+4+l]=i.data[a+ne+l],r[n+8+l]=Ls(i.data[a+$+l]),r[n+12+l]=Ls(i.data[a+ft+l]),r[n+16+l]=Ls(i.data[a+dt+l]),r[n+20+l]=Ls(i.data[a+gi+l]),r[n+24+l]=i.data[a+at+l];r[n+28]=i.data[a+W],r[n+28+1]=i.data[a+W+1],r[n+28+2]=i.data[a+K],r[n+28+3]=i.data[a+K+1],r[n+32]=i.data[a+ye],r[n+32+1]=i.data[a+Ee]}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]<=jc*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],p=e.info[u*D+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),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:Ke()}),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+Cr]?1:0,s.set([e.data[f+ge],e.data[f+ge+1],e.data[f+ne],e.data[f+ne+1]],u+4),s.set([e.data[f+W],e.data[f+W+1],e.data[f+K],e.data[f+K+1]],u+8),s.set([e.data[f+at],e.data[f+at+1],e.data[f+Ee],e.data[f+vt]],u+12),s[u+16]=e.data[f+ye]}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 Fe(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,A=>t?A<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,p=f||this.splitPasses,m=()=>{u&&!p||(u?.end(),u=c.beginComputePass({label:p?fe[d]:"avbd2d step",timestampWrites:f?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),d++)},h=this.groups,b=(A,x,B)=>{B<=0||(u.setPipeline(this.pipes[A]),u.setBindGroup(0,x),u.dispatchWorkgroups(B))},y=(A,x,B)=>{u.setPipeline(this.pipes[A]),u.setBindGroup(0,x),u.dispatchWorkgroupsIndirect(this.argsBuffer,B*4)};m(),b("beginFrame",h.broad,1),b("argsPrev",h.args,1),y("hashInsert",h.contacts[s],6),b("gridCount",h.broad,Qe(n)),this.gridScan.encode(u),b("gridScatter",h.broad,Qe(n)),b("findPairs",h.broad,Qe(n)),b("argsPairs",h.args,1),y("narrowphase",h.contacts[s],0),b("argsContacts",h.args,1),m(),b("degreeJoints",h.topo[s],Qe(l)),y("degreeContacts",h.topo[s],3),this.adjScan.encode(u),b("fillJoints",h.topo[s],Qe(l)),y("fillContacts",h.topo[s],3),b("sortAdjacency",h.topo[s],Qe(n)),m(),b("colorCompact",h.topo[s],Qe(n)),b("colorMark",h.topo[s],Qe(n));for(let A=0;A<this.colorRounds;A++)b(A%2===0?"colorRoundAB":"colorRoundBA",h.topo[s],Qe(n));b("colorCount",h.topo[s],this.colorGroups),this.colorHistScan.encode(u),b("colorStarts",h.topo[s],1),b("argsColors",h.args,1),b("colorScatter",h.topo[s],this.colorGroups),m();let v=this.colorCap+1,w=A=>u.setBindGroup(1,this.passGroup,[A*256]);u.setBindGroup(0,h.solve[s]),w(0),l>0&&(u.setPipeline(this.pipes.warmStartJoints),u.dispatchWorkgroups(Qe(l))),u.setPipeline(this.pipes.warmStartBodies),u.dispatchWorkgroups(Qe(n));for(let A=0;A<r;A++){let x=this.primalMode==="scan";u.setPipeline(x?this.pipes.primalScan:this.pipes.primal);for(let B=0;B<this.colorCap;B++)w(A*v+B),x?u.dispatchWorkgroups(Qe(n)):u.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);A<e.iterations&&(w(A*v+this.colorCap),u.setPipeline(this.pipes.dual),u.dispatchWorkgroupsIndirect(this.argsBuffer,36)),A===e.iterations-1&&(u.setPipeline(this.pipes.updateVelocities),u.dispatchWorkgroups(Qe(n)))}t&&(u.setPipeline(this.pipes.refreshStick),u.dispatchWorkgroupsIndirect(this.argsBuffer,12)),u.end();let g=2*fe.length;if(f){let{querySet:A,resolve:x,read:B}=this.timing;c.resolveQuerySet(A,0,g,x,0),c.copyBufferToBuffer(x,0,B,0,g*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-s,f){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=(z,M)=>Number(B[M]-B[z])/1e6,k={total:E(0,g-1)};fe.forEach((z,M)=>k[z]=E(2*M,2*M+1)),A(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],p=t[f+13],m=t[f+14];u>0&&(r+=.5*u*(d*d+p*p)+.5*t[f+23]*m*m)}let s=0,a=0,n=this.topology.info,l=(c,f,u)=>{let d=c*24,p=Math.cos(t[d+2]),m=Math.sin(t[d+2]);return[p*f-m*u+t[d],m*f+p*u+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(n[c*D]!==Qt)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],p=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 h=0;h<2;h++)i[f+8+h]>=1e30&&(s=Math.max(s,Math.abs(p[h]-m[h])))}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 Tc=`${mt}
|
|
4660
|
+
`;var Oo=s=>s===1/0?3e38:s===-1/0?-3e38:s,bt=s=>Math.ceil(s/64),Ic=s=>2**Math.ceil(Math.log2(Math.max(s,2))),rd=s=>Math.max(s,1)*24*4,sd=4,yt=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,To=class{device;params=ko();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:rd(this.bodyCapacity),usage:yt()});let r=this.bodyCapacity;this.tableSize=Ic(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:yt()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:yt()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:yt()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:yt()}),this.paramsBuffer=e.createBuffer({label:"params",size:128,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(r).fill(255));let o=2*Se.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:o}),resolve:e.createBuffer({size:o*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:o*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(),this.gridScan=new tt(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new tt(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1),this.jointCount=t.jointCount,this.allocateJoints(t.jointCount+256),this.uploadJoints(0,t.jointCount),this.allocateContacts(Math.max(4096,4*r)),this.uploadBodies()}createLayouts(){let e=this.device,t=(n,a)=>e.createBindGroupLayout({label:n,entries:a.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",o="uniform";return{broad:t("broadphase",[o,i,r,r,r,i,i]),contacts:t("contacts",[o,i,i,r,i,r,r]),topo:t("topology",[o,i,i,i,i,r,r,r]),solve:t("solve",[o,r,r,i,r,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args",[o,i,i,r])}}createPipelines(){let e=this.device,t=(r,o,n)=>{let a=e.createShaderModule({code:r}),l=e.createPipelineLayout({bindGroupLayouts:o});for(let c of n)this.pipes[c]=e.createComputePipeline({label:c,layout:l,compute:{module:a,entryPoint:c}})},i=this.layouts;t(ys,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(vs,[i.contacts],["hashInsert","narrowphase"]),t(Qr,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(xs,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(qs,[i.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints",size:Math.max(e,1)*36*4,usage:yt()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:yt()});if(this.jointBuffer){let o=t.createCommandEncoder();o.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*36*4),o.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([o.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t=2*e){let i=this.device,r=i.limits.maxStorageBufferBindingSize,o=Math.min(e,Math.floor(r/80),Math.floor(r/16)),n=Math.min(t,Math.floor(r/8)),a=this.contactBuffers,l=this.contactCapacity;for(let c of[this.pairBuffer,this.tableBuffer])c?.destroy();if(this.contactCapacity=o,this.pairCapacity=n,this.hashSize=Ic(4*o),this.pairBuffer=i.createBuffer({label:"pairs",size:n*8,usage:yt()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:yt()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:o*20*4,usage:yt()})),a){let c=i.createCommandEncoder(),u=Math.min(l,o)*20*4;a.forEach((f,d)=>c.copyBufferToBuffer(f,0,this.contactBuffers[d],0,u)),i.queue.submit([c.finish()]),a.forEach(f=>f.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.contactCapacity))*4,usage:yt()}),this.adjScan?.destroy(),this.adjScan=new tt(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:yt()});let i=(l,c)=>e.createBindGroup({layout:l,entries:c.map((u,f)=>({binding:f,resource:{buffer:u}}))}),r=this.paramsBuffer,[o,n]=this.contactBuffers,a=this.adjBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,o,n,this.tableBuffer,this.counterBuffer]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,n,o,this.tableBuffer,this.counterBuffer])],topo:[o,n].map(l=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,this.counterBuffer,a,this.colorBuffer])),solve:[o,n].map(l=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,a,this.colorBuffer,this.counterBuffer])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer])}}uploadBodies(e=0,t=this.topology.bodyCount-e){let i=this.topology,r=new Float32Array(Math.max(t,1)*24);for(let o=0;o<t;o++){let n=e+o,a=o*24;r.set(i.pose.subarray(n*4,n*4+3),a),r[a+3]=i.props[n*4],r.set(i.initial.subarray(n*4,n*4+4),a+4),r.set(i.inertial.subarray(n*4,n*4+4),a+8),r.set(i.velocity.subarray(n*4,n*4+4),a+12),r.set(i.prevVelocity.subarray(n*4,n*4+4),a+16),r.set(i.shape.subarray(n*4,n*4+4),a+20)}this.device.queue.writeBuffer(this.bodyBuffer,e*24*4,r),this.staticsDirty=!0}uploadJoints(e,t){if(t===0)return;let i=this.topology,r=new Float32Array(t*36);for(let o=0;o<t;o++){let n=(e+o)*O,a=o*36;for(let l=0;l<3;l++)r[a+0+l]=i.data[n+se+l],r[a+4+l]=i.data[n+te+l],r[a+8+l]=Oo(i.data[n+Q+l]),r[a+12+l]=Oo(i.data[n+_e+l]),r[a+16+l]=Oo(i.data[n+Ae+l]),r[a+20+l]=Oo(i.data[n+Pt+l]),r[a+24+l]=i.data[n+fe+l];r[a+28]=i.data[n+N],r[a+28+1]=i.data[n+N+1],r[a+28+2]=i.data[n+Y],r[a+28+3]=i.data[n+Y+1],r[a+32]=i.data[n+re],r[a+32+1]=i.data[n+be]}this.device.queue.writeBuffer(this.jointBuffer,e*36*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,i.info,e*j,t*j)}uploadStatics(){let e=this.topology,t=this.bodyCount,i=Float64Array.from({length:t},(f,d)=>e.props[d*4+1]).sort(),r=t>0?i[t>>1]:1,o=0;for(let f=0;f<t;f++)i[f]<=sd*r&&(o=Math.max(o,i[f]));let n=1/0;for(let f=0;f<t;f++)i[f]>o&&(n=Math.min(n,i[f]));let a=n===1/0?o*1.5:(o+n)/2;this.maxSmallRadius=a,this.cellSize=Math.max(2*o*(1+1e-4),.001);let l=[];for(let f=0;f<t;f++)e.props[f*4+1]>a&&l.push(f);let c=[];for(let f=0;f<e.jointCount;f++){let d=e.info[f*j+1],p=e.info[f*j+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),f])}for(let f of e.ignoredPairs()){let d=Math.floor(f/2097152);c.push([d,f-d*2097152,4294967295])}c.sort((f,d)=>f[0]-d[0]||f[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let u=new Uint32Array(Math.max(l.length+3*c.length,4));u.set(l,0),c.forEach((f,d)=>u.set(f,l.length+3*d)),this.staticBuffer.size<u.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:u.byteLength,usage:yt()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,u),this.staticsDirty=!1}seedContactsFrom(e){let t=e.contactCount;if(t>this.contactCapacity)throw new Error("seedContactsFrom: too many contacts");let i=new ArrayBuffer(Math.max(t,1)*20*4),r=new Uint32Array(i),o=new Float32Array(i);for(let l=0;l<t;l++){let c=e.jointCount+l,u=c*O,f=l*20;r[f]=e.info[c*j+1],r[f+1]=e.info[c*j+2],r[f+2]=e.info[c*j+3],r[f+3]=e.data[u+Hi]?1:0,o.set([e.data[u+se],e.data[u+se+1],e.data[u+te],e.data[u+te+1]],f+4),o.set([e.data[u+N],e.data[u+N+1],e.data[u+Y],e.data[u+Y+1]],f+8),o.set([e.data[u+fe],e.data[u+fe+1],e.data[u+be],e.data[u+Ue]],f+12),o[f+16]=e.data[u+re]}this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,i);let n=new Uint32Array(16);n[1]=t,this.device.queue.writeBuffer(this.counterBuffer,0,n);let a=Uint32Array.from({length:this.bodyCount},(l,c)=>(e.coloring.colors[c]??-1)<0?255:e.coloring.colors[c]);this.device.queue.writeBuffer(this.colorBuffer,0,a)}addBody(e,t,i,r,o){if(this.bodyCount>=this.bodyCapacity)return-1;let n=this.topology.addBody(e,t,i,r,o);return this.bodyCount=this.topology.bodyCount,this.uploadBodies(n,1),this.device.queue.writeBuffer(this.colorBuffer,n*4,new Uint32Array([255])),this.adjScan.destroy(),this.adjScan=new tt(this.device,this.adjBuffer,0,this.bodyCount+1),n}appendJoint(e,t,i,r,o,n=1/0){let a=this.topology;if(a.contactCount>0)throw new Error("appendJoint: the CPU mirror must not hold contacts");let l=a.addJoint(e,t,i,r,o,n).slot;return l>=this.jointCapacity&&this.allocateJoints(Math.max(l+1,this.jointCapacity*2)),this.jointCount=l+1,this.uploadJoints(l,1),e>=0&&(this.staticsDirty=!0),l}setWorldAnchor(e,t,i){this.device.queue.writeBuffer(this.jointBuffer,(e*36+28)*4,new Float32Array([t,i]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*36+0)*4,new Float32Array(12)),this.topology.data.fill(0,e*O+Q,e*O+Q+3)}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(e,t){let i=this.params,r=this.bodyCapacity,o=new ArrayBuffer(128),n=new Float32Array(o),a=new Uint32Array(o);n[0]=i.dt,n[1]=i.gravity,n[2]=i.beta,n[3]=i.gamma,n[4]=e,n[5]=i.vbdStiffness,a[6]=(i.vbd?1:0)|(i.stiffnessRescale?2:0)|(t?4:0)|(i.matchNearest?8:0),a[7]=this.bodyCount,a[8]=this.jointCount,a[9]=this.colorCap,n[10]=this.cellSize,a[11]=this.tableSize-1,n[12]=this.maxSmallRadius,a[13]=this.largeCount,a[14]=this.noCollideCount,a[15]=this.pairCapacity,a[16]=this.contactCapacity,a[17]=this.hashSize-1,a[18]=r+1,a[19]=2*r+1,a[20]=this.tableSize+1,a[21]=2*this.tableSize+1,a[22]=2*this.tableSize+1+r,a[23]=r,a[24]=this.colorHistOffset,a[25]=2*r,a[26]=this.colorGroups,a[27]=2*r+65,a[28]=this.colorRounds,this.device.queue.writeBuffer(this.paramsBuffer,0,o)}writePassConstants(e,t){let i=this.colorCap+1,r=e*i;(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let o=new ArrayBuffer(r*256),n=new Uint32Array(o),a=new Float32Array(o);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let u=(l*i+c)*256/4;n[u]=c,a[u+1]=t(l)}this.device.queue.writeBuffer(this.passBuffer,0,o)}step(){this.staticsDirty&&this.uploadStatics();let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha,r=e.iterations+(t?1:0);this.writeParams(i,t),this.writePassConstants(r,x=>t?x<e.iterations?1:0:i);let o=this.parity,n=this.bodyCapacity,a=this.bodyCount,l=this.jointCount,c=this.device.createCommandEncoder({label:"avbd2d step"});c.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),c.clearBuffer(this.tableBuffer),c.clearBuffer(this.adjBuffer,0,(2*n+1)*4);let u=this.timingCallback!==null&&this.timing!==null,f,d=0,p=u||this.splitPasses,m=()=>{f&&!p||(f?.end(),f=c.beginComputePass({label:p?Se[d]:"avbd2d step",timestampWrites:u?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),d++)},h=this.groups,g=(x,w,B)=>{B<=0||(f.setPipeline(this.pipes[x]),f.setBindGroup(0,w),f.dispatchWorkgroups(B))},b=(x,w,B)=>{f.setPipeline(this.pipes[x]),f.setBindGroup(0,w),f.dispatchWorkgroupsIndirect(this.argsBuffer,B*4)};m(),g("beginFrame",h.broad,1),g("argsPrev",h.args,1),b("hashInsert",h.contacts[o],6),g("gridCount",h.broad,bt(a)),this.gridScan.encode(f),g("gridScatter",h.broad,bt(a)),g("findPairs",h.broad,bt(a)),g("argsPairs",h.args,1),b("narrowphase",h.contacts[o],0),g("argsContacts",h.args,1),m(),g("degreeJoints",h.topo[o],bt(l)),b("degreeContacts",h.topo[o],3),this.adjScan.encode(f),g("fillJoints",h.topo[o],bt(l)),b("fillContacts",h.topo[o],3),g("sortAdjacency",h.topo[o],bt(a)),m(),g("colorCompact",h.topo[o],bt(a)),g("colorMark",h.topo[o],bt(a));for(let x=0;x<this.colorRounds;x++)g(x%2===0?"colorRoundAB":"colorRoundBA",h.topo[o],bt(a));g("colorCount",h.topo[o],this.colorGroups),this.colorHistScan.encode(f),g("colorStarts",h.topo[o],1),g("argsColors",h.args,1),g("colorScatter",h.topo[o],this.colorGroups),m();let y=this.colorCap+1,A=x=>f.setBindGroup(1,this.passGroup,[x*256]);f.setBindGroup(0,h.solve[o]),A(0),l>0&&(f.setPipeline(this.pipes.warmStartJoints),f.dispatchWorkgroups(bt(l))),f.setPipeline(this.pipes.warmStartBodies),f.dispatchWorkgroups(bt(a));for(let x=0;x<r;x++){let w=this.primalMode==="scan";f.setPipeline(w?this.pipes.primalScan:this.pipes.primal);for(let B=0;B<this.colorCap;B++)A(x*y+B),w?f.dispatchWorkgroups(bt(a)):f.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);x<e.iterations&&(A(x*y+this.colorCap),f.setPipeline(this.pipes.dual),f.dispatchWorkgroupsIndirect(this.argsBuffer,36)),x===e.iterations-1&&(f.setPipeline(this.pipes.updateVelocities),f.dispatchWorkgroups(bt(a)))}t&&(f.setPipeline(this.pipes.refreshStick),f.dispatchWorkgroupsIndirect(this.argsBuffer,12)),f.end();let v=2*Se.length;if(u){let{querySet:x,resolve:w,read:B}=this.timing;c.resolveQuerySet(x,0,v,w,0),c.copyBufferToBuffer(w,0,B,0,v*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-o,u){let x=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let w=this.timing.read;w.mapAsync(GPUMapMode.READ).then(()=>{let B=new BigUint64Array(w.getMappedRange()).slice();if(w.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let L=(I,E)=>Number(B[E]-B[I])/1e6,M={total:L(0,v-1)};Se.forEach((I,E)=>M[I]=L(2*E,2*E+1)),x(M)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*24*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*36*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],overflow:e[3],clashes:e[4],colors:e[5]}}async readContacts(){let{contacts:e}=await this.readCounters(),t=Math.min(e,this.contactCapacity);return this.read(this.contactBuffers[1-this.parity],t*20*4)}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}async readColors(){return new Uint32Array(await this.read(this.colorBuffer,this.bodyCount*4))}async readAdjacency(){let e=new Uint32Array(await this.read(this.adjBuffer,this.adjBuffer.size)),t=e.slice(0,this.bodyCount+1),i=2*this.bodyCapacity+1;return{start:t,list:e.slice(i,i+t[this.bodyCount])}}adapt(e){let t=e.colors;e.clashes>0||t>=this.colorCap-1?(this.colorCap=Math.min(64,Math.max(t+4,this.colorCap+4)),this.shrinkVotes=0):t+2<this.colorCap?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,Math.max(4,t+2)),this.shrinkVotes=0):this.shrinkVotes=0;let i=(e.overflow&2)!==0||e.contacts>.8*this.contactCapacity,r=(e.overflow&1)!==0||e.pairs>.8*this.pairCapacity,o=this.contactCapacity*(i?2:1),n=this.pairCapacity*(r?2:1);(i||r)&&this.capacityCanGrow(o,n)&&this.allocateContacts(o,n)}capacityCanGrow(e,t){let i=this.device.limits.maxStorageBufferBindingSize,r=Math.min(Math.floor(i/80),Math.floor(i/16));return Math.min(e,r)>this.contactCapacity||Math.min(t,Math.floor(i/8))>this.pairCapacity}async readStats(e){let[t,i]=await Promise.all([e??this.readBodies(),this.readJoints()]),r=0;for(let c=0;c<this.bodyCount;c++){let u=c*24,f=t[u+22],d=t[u+12],p=t[u+13],m=t[u+14];f>0&&(r+=.5*f*(d*d+p*p)+.5*t[u+23]*m*m)}let o=0,n=0,a=this.topology.info,l=(c,u,f)=>{let d=c*24,p=Math.cos(t[d+2]),m=Math.sin(t[d+2]);return[p*u-m*f+t[d],m*u+p*f+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(a[c*j]!==Bi)continue;let u=c*36;if(i[u+8]===0&&i[u+8+1]===0&&i[u+8+2]===0)continue;n++;let f=a[c*j+1],d=a[c*j+2],p=f>=0?l(f,i[u+28],i[u+28+1]):[i[u+28],i[u+28+1]],m=l(d,i[u+28+2],i[u+28+3]);for(let h=0;h<2;h++)i[u+8+h]>=1e30&&(o=Math.max(o,Math.abs(p[h]-m[h])))}return{kineticEnergy:r,maxJointError:o,joints:n}}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let o=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),o}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.passBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var od=`${Ut}
|
|
4444
4661
|
struct Config { count:u32,joints:u32,capacity:u32,list:u32,threshold:f32,dt:f32,pad0:u32,pad1:u32 }
|
|
4445
4662
|
struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
|
|
4446
4663
|
struct Bounce { impulse:f32,delta:f32,reboundSpeed:f32,inverseMass:f32,rA:vec4f,rB:vec4f }
|
|
@@ -4486,9 +4703,9 @@ fn pointVelocity(i:u32,r:vec2f,incoming:bool)->vec2f{let v=select(velocities[i].
|
|
|
4486
4703
|
velocities[i].linear+=vec4f(dv,0.);
|
|
4487
4704
|
}
|
|
4488
4705
|
@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;}
|
|
4489
|
-
|
|
4490
|
-
if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return
|
|
4491
|
-
if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var
|
|
4706
|
+
`;var jo=class extends wr{constructor(e){super(e,{code:od,dimension:2})}};function Rc(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}
|
|
4707
|
+
if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return s}function Ra(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}
|
|
4708
|
+
if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var Oc=`// Sleeping is scheduling metadata in velocity.w; physical mass and inertia stay intact.
|
|
4492
4709
|
struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,vel:vec4f,prev:vec4f,shape:vec4f}
|
|
4493
4710
|
struct Joint {pen:vec4f,lam:vec4f,stiff:vec4f,fmin:vec4f,fmax:vec4f,frac:vec4f,c0:vec4f,anchors:vec4f,param:vec4f}
|
|
4494
4711
|
struct Contact {ids:vec4u,pl:vec4f,anchors:vec4f,geo:vec4f,misc:vec4f}
|
|
@@ -4559,10 +4776,10 @@ fn rotate(angle:f32,p:vec2f)->vec2f{let c=cos(angle);let s=sin(angle);return vec
|
|
|
4559
4776
|
}
|
|
4560
4777
|
if(asleep(i)){atomicAdd(&globals[1],1u);}
|
|
4561
4778
|
}
|
|
4562
|
-
`;function
|
|
4563
|
-
if(gid.x<params.bodyCount && bodies[gid.x].vel.w!=0.){return;}`);return
|
|
4564
|
-
if(bodies[i].vel.w!=0.){return;}`)}function
|
|
4565
|
-
case 5: { return 2u; }`),
|
|
4779
|
+
`;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 Tc(s){for(let e of["warmStartBodies","updateVelocities"])s=s.replace(`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {`,`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {
|
|
4780
|
+
if(gid.x<params.bodyCount && bodies[gid.x].vel.w!=0.){return;}`);return s.replace("fn solveBody(i: u32) {",`fn solveBody(i: u32) {
|
|
4781
|
+
if(bodies[i].vel.w!=0.){return;}`)}function jc(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 Do=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:Oc});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=this.paramData??=hi(8),{u:r,f:o}=i;r[0]=t.bodyCount,r[1]=t.jointCount,r[2]=t.contactCapacity,r[3]=Number(this.wakeRequested),o[4]=this.options.speedThreshold,o[5]=this.options.timeThreshold,o[6]=t.params.dt,o[7]=t.params.gravity,Qt(this.device,this.params,i),this.wakeRequested=!1,this.run([["clear",1],["before",t.bodyCount]],e)}encodeWake(e,t){let i=this.gpu;this.dispatch(e,t,[["contactsWake",i.contactCapacity],["jointsWake",i.jointCount],["decide",i.bodyCount],["applyWake",i.bodyCount]])}after(e){this.run([["rest",this.gpu.bodyCount]],e)}async readStats(){let e=new Uint32Array(await this.gpu.read(this.globals,8));return{sleeping:e[1],wakeRequested:this.wakeRequested||!!e[0]}}destroy(){this.gpu.propertyEdits?.forget(this.state),this.state.destroy(),this.globals.destroy(),this.params.destroy()}};var vt=s=>Math.ceil(s/64);function Dc(){this.staticsDirty&&this.uploadStatics();let s=this.params,e=s.postStabilize&&!s.vbd,t=s.vbd?0:s.alpha,i=s.iterations+(e?1:0);this.writeParams(t,e),this.writePassConstants(i,L=>e?L<s.iterations?1:0:t);let r=this.parity,o=this.bodyCapacity,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,d=0,p=c||this.splitPasses,m=()=>{u&&!p||(u?.end(),u=l.beginComputePass({label:p?Se[d]:"avbd2d step",timestampWrites:c?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),f=void 0,d++)},h=this.groups,g=L=>{f!==L&&(u.setBindGroup(0,L),f=L)},b=(L,M,I)=>{I<=0||(u.setPipeline(this.pipes[L]),g(M),u.dispatchWorkgroups(I))},y=(L,M,I)=>{u.setPipeline(this.pipes[L]),g(M),u.dispatchWorkgroupsIndirect(this.argsBuffer,I*4)};m(),b("beginFrame",h.broad,1),b("argsPrev",h.args,1),y("hashInsert",h.contacts[r],6),b("gridCount",h.broad,vt(n)),this.gridScan.encode(u),f=void 0,b("gridScatter",h.broad,vt(n)),b("findPairs",h.broad,vt(n)),b("argsPairs",h.args,1),y("narrowphase",h.contacts[r],0),b("argsContacts",h.args,1),this.sleeping?.encodeWake(u,r),f=void 0,m(),b("degreeJoints",h.topo[r],vt(a)),y("degreeContacts",h.topo[r],3),this.adjScan.encode(u),f=void 0,b("fillJoints",h.topo[r],vt(a)),y("fillContacts",h.topo[r],3),b("sortAdjacency",h.topo[r],vt(n)),m(),b("colorCompact",h.topo[r],vt(n)),b("colorMark",h.topo[r],vt(n));for(let L=0;L<this.colorRounds;L++)b(L%2===0?"colorRoundAB":"colorRoundBA",h.topo[r],vt(n));b("colorCount",h.topo[r],this.colorGroups),this.colorHistScan.encode(u),f=void 0,b("colorStarts",h.topo[r],1),b("argsColors",h.args,1),b("colorScatter",h.topo[r],this.colorGroups),m();let A=this.colorCap+1,v=this.passOffsets??=[0],x=-1,w=L=>{L!==x&&(v[0]=L*256,u.setBindGroup(1,this.passGroup,v),x=L)};u.setBindGroup(0,h.solve[r]),w(0),a>0&&(u.setPipeline(this.pipes.warmStartJoints),u.dispatchWorkgroups(vt(a))),u.setPipeline(this.pipes.warmStartBodies),u.dispatchWorkgroups(vt(n));for(let L=0;L<i;L++){let M=this.primalMode==="scan";u.setPipeline(M?this.pipes.primalScan:this.pipes.primal);for(let I=0;I<this.colorCap;I++)w(L*A+I),M?u.dispatchWorkgroups(vt(n)):u.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*I)*4);L<s.iterations&&(w(L*A+this.colorCap),u.setPipeline(this.pipes.dual),u.dispatchWorkgroupsIndirect(this.argsBuffer,36)),L===s.iterations-1&&(u.setPipeline(this.pipes.updateVelocities),u.dispatchWorkgroups(vt(n)))}e&&(u.setPipeline(this.pipes.refreshStick),u.dispatchWorkgroupsIndirect(this.argsBuffer,12)),u.end();let B=2*Se.length;if(c){let{querySet:L,resolve:M,read:I}=this.timing;l.resolveQuerySet(L,0,B,M,0),l.copyBufferToBuffer(M,0,I,0,B*8)}if(this.parity=1-r,this.encodeStepPostlude?.(l),this.device.queue.submit([l.finish()]),c){let L=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let M=this.timing.read;M.mapAsync(GPUMapMode.READ).then(()=>{let I=new BigUint64Array(M.getMappedRange()).slice();if(M.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let E=(P,C)=>Number(I[C]-I[P])/1e6,k={total:E(0,B-1)};Se.forEach((P,C)=>k[P]=E(2*C,2*C+1)),L(k)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}var Fo=5;function ki({minAngle:s=-Math.PI,maxAngle:e=Math.PI}={}){if(!Number.isFinite(s)||!Number.isFinite(e)||s>e)throw Error("Angle limits require finite minAngle <= maxAngle, in radians");return{minAngle:s,maxAngle:e}}function Bs(s,e,t){let i=e*O;s.info[e*j]=Fo,s.data.set([1/0,1/0,0],i+Q),s.data.set([-1/0,0,0],i+_e),s.data.set([0,1/0,0],i+Ae),s.data[i+be]=t.minAngle,s.data[i+Ue]=t.maxAngle}function Gc(s){let e=s.topology;for(let t=0;t<e.jointCount;t++)e.info[t*j]===Fo&&s.device.queue.writeBuffer(s.jointBuffer,(t*36+32)*4,new Float32Array([e.data[t*O+re],e.data[t*O+be],e.data[t*O+Ue],0]))}var ws=(s,e,t)=>{if(s.split(e).length!==2)throw Error("Pinned 2D angular layout changed");return s.replace(e,t)};function Fc(s){return s=ws(s,"case T_JOINT: { return 3u; }",`case T_JOINT: { return 3u; }
|
|
4782
|
+
case 5: { return 2u; }`),s=ws(s,"fn jointRowsC(j: u32, alpha: f32) -> vec3f {",`
|
|
4566
4783
|
fn limitAngle2D(k:Joint,a:i32,b:i32)->f32 {
|
|
4567
4784
|
var angleA=0.;if(a>=0){angleA=bodies[a].pose.z;}
|
|
4568
4785
|
return angleA-bodies[b].pose.z-k.param.x;
|
|
@@ -4572,10 +4789,10 @@ fn limitRows2D(k:Joint,a:i32,b:i32,alpha:f32)->vec3f {
|
|
|
4572
4789
|
return vec3f(angle-k.param.y,angle-k.param.z,0.)-alpha*k.c0.xyz;
|
|
4573
4790
|
}
|
|
4574
4791
|
`+`
|
|
4575
|
-
fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),
|
|
4792
|
+
fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),s=ws(s,` if (t == T_JOINT) {
|
|
4576
4793
|
// Hard rows`,` if(t==5){return limitRows2D(k,a,b,alpha);}
|
|
4577
4794
|
if (t == T_JOINT) {
|
|
4578
|
-
// Hard rows`),
|
|
4795
|
+
// Hard rows`),s=ws(s," let C = jointRowsC(j, alpha);",` let C = jointRowsC(j, alpha);
|
|
4579
4796
|
if(t==5){
|
|
4580
4797
|
// Unilateral rows only contribute while their force is active. Inside the
|
|
4581
4798
|
// interval a hinge has no angular stiffness and remains free to rotate.
|
|
@@ -4583,8 +4800,8 @@ fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),o=_r(o,` if (t == T_JOINT) {
|
|
|
4583
4800
|
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);}
|
|
4584
4801
|
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);}
|
|
4585
4802
|
return;
|
|
4586
|
-
}`),
|
|
4587
|
-
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.);}`),
|
|
4803
|
+
}`),s=ws(s," if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }",` if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }
|
|
4804
|
+
if(t==5){let angle=limitAngle2D(k,info[j].y,info[j].z);k.c0=vec4f(min(0.,angle-k.param.y),max(0.,angle-k.param.z),0.,0.);}`),s}var Uc=`// Analytic circles and capsules, finite two-sided segments and solid half-space planes.
|
|
4588
4805
|
// Kind 4 stores bounding radius, vertex-record offset and vertex count.
|
|
4589
4806
|
@group(0) @binding(7) var<storage,read> geometry:array<vec4f>;
|
|
4590
4807
|
struct Face2D { a:vec2f, b:vec2f, feature:u32 }
|
|
@@ -4737,7 +4954,7 @@ fn collideShapes2D(a:u32,b:u32)->Collision {
|
|
|
4737
4954
|
}
|
|
4738
4955
|
out.count=count;return out;
|
|
4739
4956
|
}
|
|
4740
|
-
`;var
|
|
4957
|
+
`;var Oa=new WeakMap,Gr=(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},Uo=(s,e)=>{if(!s||s.length!==2||!Array.from(s).every(Number.isFinite))throw Error(`${e} requires two finite components`);return Array.from(s)},ei=s=>Oa.get(s);function No(s){let e=Array.from(s??[]),t=typeof e[0]=="number"?e:e.flatMap(w=>Uo(w,"Hull point"));if(t.length<6||t.length%2||!t.every(Number.isFinite))throw Error("A 2D hull requires finite xy pairs spanning an area");let i=[];for(let w=0;w<t.length;w+=2)i.push([t[w],t[w+1]]);i.sort((w,B)=>w[0]-B[0]||w[1]-B[1]);let r=i.filter((w,B)=>!B||w[0]!==i[B-1][0]||w[1]!==i[B-1][1]),o=(w,B,L)=>(B[0]-w[0])*(L[1]-w[1])-(B[1]-w[1])*(L[0]-w[0]),n=w=>{let B=[];for(let L of w){for(;B.length>1&&o(B.at(-2),B.at(-1),L)<=0;)B.pop();B.push(L)}return B.slice(0,-1)},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(w=>[w[0]-l[0],w[1]-l[1]]),u=0,f=0,d=0;for(let w=0;w<c.length;w++){let B=c[w],L=c[(w+1)%c.length],M=B[0]*L[1]-L[0]*B[1];u+=M,f+=(B[0]+L[0])*M,d+=(B[1]+L[1])*M}if(!(u>0)||!Number.isFinite(u))throw Error("Hull area must be positive and finite");f/=3*u,d/=3*u;let p=c.map(w=>[w[0]-f,w[1]-d]),m=0,h=0,g=0,b=0,y=[];for(let w=0;w<p.length;w++){let B=p[w],L=p[(w+1)%p.length],M=B[0]*L[1]-L[0]*B[1];m+=M*(B[0]*B[0]+B[0]*L[0]+L[0]*L[0]+B[1]*B[1]+B[1]*L[1]+L[1]*L[1])/12;let I=L[0]-B[0],E=L[1]-B[1],k=Math.hypot(I,E);y.push(B[0],B[1],E/k,-I/k),h=Math.max(h,Math.hypot(...B)),g=Math.max(g,Math.abs(B[0])),b=Math.max(b,Math.abs(B[1]))}let A=u/2,v=m/A;if(![A,v,h,g,b,...y].every(w=>Number.isFinite(Math.fround(w)))||Math.fround(A)<=0||Math.fround(v)<=0)throw Error("Hull geometry exceeds GPU floating-point range");let x=[4,h,0,p.length];return x.vertices=y,{geometry:x,size:[2*g,2*b],area:A,momentPerMass:v,radius:h}}function qo(s){let e=Gr(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 Vo(s,e){let t=Gr(s,"radius");if(Gr(e,"length"),e<2*t)throw Error("Capsule length must be at least twice its radius");return Nc((e-2*t)/2,t)}function Nc(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=Uo(s,"start"),r=Uo(e,"end"),o=r[0]-i[0],n=r[1]-i[1],a=Math.hypot(o,n),l=Gr(t.radius??0,"radius",!0);if(Gr(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{...Nc(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 Ho(s,e=0,t={}){let i=Uo(s,"normal"),r=Gr(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 Fr(s,e,t,i){let r=Oa.get(s);r||Oa.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 qc=`struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,velocity:vec4f,previous:vec4f,shape:vec4f}
|
|
4741
4958
|
struct Command { header:vec4u, value:vec4f, point:vec4f }
|
|
4742
4959
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
4743
4960
|
@group(0) @binding(1) var<storage,read> commands:array<Command>;
|
|
@@ -4773,11 +4990,11 @@ fn edit(@builtin(global_invocation_id) id:vec3u) {
|
|
|
4773
4990
|
bodies[index]=b;
|
|
4774
4991
|
}
|
|
4775
4992
|
\r
|
|
4776
|
-
`;function
|
|
4777
|
-
`+this.shader})}}),(!this.group||this.boundBody!==this.solver.bodyBuffer||this.boundCommands!==this.commands||this.boundRanges!==this.ranges||this.boundRangeBytes!==
|
|
4778
|
-
fn narrowphase`,
|
|
4993
|
+
`;function Vt(s,e){let t=s&&typeof s.x=="number"?[s.x,s.y]:Array.isArray(s)||ArrayBuffer.isView(s)&&!(s instanceof DataView)?s:Array.from(s??[]);if(t.length!==2||!t.every(Number.isFinite))throw Error(`${e} requires two finite components`);return t}var ti=class{constructor(){this.count=0,this.capacity=0}reserve(e){if(e<=this.capacity)return;let t=Math.max(e,this.capacity*2,64),i=new ArrayBuffer(t*48),r=new Float32Array(i),o=new Uint32Array(i);this.u32&&o.set(this.u32.subarray(0,this.count*12)),this.words=r,this.u32=o,this.capacity=t}append(e,t,i,r,o=!1){this.reserve(this.count+1),Vc(this.words,this.u32,this.count++*12,e,t,i,r,o)}};function Vc(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 Ur=class{constructor(e,t=qc,i=2){this.solver=e,this.shader=t,this.dimension=i,this.pending=new Map,this.queue=new ti,this.requested=0,this.linearOnly=!0,this.angularOnly=!0,this.hasTeleports=!1,this.motionOnly=!0,this.freeRanges=[]}slot(e,t){this.requested++,this.linearOnly&&=t===0,this.angularOnly&&=t===1,this.motionOnly&&=t===0||t===1,this.hasTeleports||=t===4;let i=this.pending.get(e);if(i&&(t===0||t===1)&&this.queue.u32[i.tail*12+1]===t)return i.tail;if(this.queue.reserve(this.queue.count+1),!this.next||this.next.length<this.queue.capacity){let o=new Int32Array(this.queue.capacity);this.next&&o.set(this.next),this.next=o}let r=this.queue.count++;return this.next[r]=-1,i?(this.next[i.tail]=r,i.tail=r,i.length++):(i=this.freeRanges.pop()??{},i.head=i.tail=r,i.length=1,this.pending.set(e,i)),r}enqueue(e,t,i,r,o=!1){let n=this.slot(e,t);Vc(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 Kt(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 yi(this.solver,this.dimension),o=r.wordsPerBody;(!this.motionData||this.motionData.length<i*o)&&(this.motionData=new Uint32Array(Math.max(i*o,(this.motionData?.length??0)*2,64*o)),this.motionFloats=new Float32Array(this.motionData.buffer));let 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),m=d===0||this.dimension===3?this.dimension:1;for(let h=0;h<m;h++)this.motionFloats[p+h]=this.queue.words[f*12+4+h]}this.motionData[c+1]=u}r.submitValidated(this.motionData.subarray(0,i*o),e),this.lastBatch={bodies:i,commands:this.queue.count,requestedCommands:this.requested,uploadedBytes:r.lastBatch.uploadedBytes,path:"packed-motion",submissions:e?0:1}}else{this.upload??=new ti,this.upload.count=0,this.upload.reserve(this.queue.count),(!this.rangeData||this.rangeData.length<i*2)&&(this.rangeData=new Uint32Array(Math.max(i*2,(this.rangeData?.length??0)*2,128)));let r=0,o=0;for(let p of this.pending.values()){this.rangeData[o++]=r,this.rangeData[o++]=p.length;for(let m=p.head;m!==-1;m=this.next[m]){for(let h=0;h<12;h++)this.upload.u32[r*12+h]=this.queue.u32[m*12+h];r++}}let 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,m]of[["commands",n],["ranges",a]])(!this[p]||this[p].size<m)&&(this[p]?.destroy(),this[p]=t.createBuffer({size:Math.min(l,Math.max(m*2,16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}));t.queue.writeBuffer(this.commands,0,this.upload.words.buffer,0,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;
|
|
4994
|
+
`+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 $o=class extends To{writeParams(e,t){ac.call(this,e,t)}writePassConstants(e,t){fo.call(this,e,t)}createPipelines(){let e=this.layouts;this.compile(ys,[e.broad],["beginFrame","gridCount","gridScatter","findPairs"]),this.compile(vs,[e.contacts],["hashInsert","narrowphase"]),this.compile(Qr,[e.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),this.compile(xs,[e.solve,e.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),this.compile(qs,[e.args],["argsPrev","argsPairs","argsContacts","argsColors"])}constructor(e,t,i={}){if(i.minimumColors!==void 0&&(!Number.isInteger(i.minimumColors)||i.minimumColors<4||i.minimumColors>64))throw Error("minimumColors must be an integer from 4 to 64");i.sleeping!==void 0&&i.sleeping!==!1&&Go(i.sleeping),super(e,t,i),this.minimumColors=i.minimumColors??4,this.colorCap=Math.max(this.colorCap,this.minimumColors),this.sleepEligible=new Map(t.avbdSleepEligible??[]),Gc(this),ei(t)?.size&&this.enableShapes(),(i.sleeping||[...this.sleepEligible.values()].some(r=>r===!0))&&this.enableSleeping(i.sleeping||{})}enableShapes(){this.shapeBuffer||(this.syncShapeGeometry(),this.shapeContactLayout=this.makeLayout(["uniform","read-only-storage","read-only-storage","storage","read-only-storage","storage","storage","read-only-storage"]),this.compileBroadphase(),this.compileContacts(),this.bindContacts(),this.staticsDirty=!0)}geometryBytes(e=0){let t=(this.geometryRecords??this.bodyCapacity)*4+e;if(this.geometryRecords===void 0)for(let r of ei(this.topology)?.values()??[])t+=r.vertices?.length??0;let i=t*4;if(i>Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize,16777216*16))throw Error("2D hull geometry exceeds this GPU's storage capacity");return i}syncShapeGeometry(){let e=this.geometryBytes(),t=new Float32Array(e/4);this.packedShapes=new Map;let i=this.bodyCapacity;for(let[r,o]of ei(this.topology)??[]){let 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=vs;return this.shapeBuffer&&(e=e.replace(`@compute @workgroup_size(64)
|
|
4995
|
+
fn narrowphase`,Uc+`
|
|
4779
4996
|
@compute @workgroup_size(64)
|
|
4780
|
-
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=
|
|
4997
|
+
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=ys;if(this.shapeBuffer&&(e.push("read-only-storage"),t=t.replace(`fn radius(i: u32) -> f32 {
|
|
4781
4998
|
return 0.5 * length(bodies[i].shape.xy);
|
|
4782
4999
|
}`,`@group(0) @binding(7) var<storage,read> geometry:array<vec4f>;
|
|
4783
5000
|
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.) {
|
|
@@ -4785,13 +5002,13 @@ let plane=select(b,a,geometry[a].x==3.);let other=select(a,b,geometry[a].x==3.);
|
|
|
4785
5002
|
if(dot(bodies[other].pose.xy-bodies[plane].pose.xy,normal)>radius(other)){return;}
|
|
4786
5003
|
} 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>;
|
|
4787
5004
|
fn ignored(hi:u32,lo:u32)->bool{
|
|
4788
|
-
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=Er(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],p=e.info[u*4+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),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 Nt(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 Nt(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 sr(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(js(e),t)}addHull(e,t={}){return this.addShape(Os(e),t)}addCapsule(e,t,i={}){return this.addShape(Ts(e,t),i)}addSegment(e,t,i={}){return this.addShape(Ds(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Gs(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(h=>Number.isFinite(h)&&h>0)||!Number.isFinite(i)||i<0||!Number.isFinite(r)||r<0||!Number.isFinite(a))throw Error("Body dimensions must be positive, density and friction nonnegative, and angle finite");if(n.length!==3||!Array.from(n).every(Number.isFinite))throw Error("Velocity requires [x, y, angular] finite components");if(typeof c!="boolean"||!Number.isFinite(l)||l<0||l>1)throw Error("isTrigger must be boolean and restitution between zero and one");if([f,u].some(h=>!Number.isInteger(h)||h<0||h>4294967295))throw Error("Collision masks must be unsigned 32-bit integers");let p=xt(s,"position");e.geometry&&this.geometryBytes(e.geometry.vertices?.length??0);let m=super.addBody(e.size,i,r,[...p,a],Array.from(n));return e.geometry&&(Fs(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 Es(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,ks),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+ye]=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,Is);let i=ir(t);this.topology.data[e*T+Ee]=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,Qt),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=Pr.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) {
|
|
5005
|
+
if((filters[hi].x&filters[lo].y)==0u || (filters[lo].x&filters[hi].y)==0u){return true;}`)}this.filterLayout=this.makeLayout(e),this.compile(t,[this.filterLayout],["beginFrame","gridCount","gridScatter","findPairs"]),this.bindFilters()}uploadBodies(e=0,t=this.topology.bodyCount-e){this.flushPropertyEdits(),super.uploadBodies(e,t);for(let o=e;o<e+t;o++)this.sleeping?.register(o,this.sleepEligible?.get(o)!==!1);if(!this.shapeBuffer&&!this.triggers?.size)return;let i=this.shapeBuffer&&t>0?new Float32Array(t*4):null,r=this.packedShapes;for(let o=e;o<e+t;o++){let n=r?.get(o);i&&n&&i.set(n,(o-e)*4),this.triggers?.has(o)&&this.device.queue.writeBuffer(this.bodyBuffer,o*96+28,new Float32Array([1]))}i&&this.device.queue.writeBuffer(this.shapeBuffer,e*16,i)}uploadStatics(){if(!this.shapeBuffer)return super.uploadStatics();let e=this.topology,t=ei(e),i=[];for(let f=0;f<this.bodyCount;f++)t?.get(f)?.[0]!==3&&i.push(e.props[f*4+1]);i.sort((f,d)=>f-d);let r=i[i.length>>1]??1,o=0,n=1/0;for(let f of i)f<=4*r&&(o=Math.max(o,f));for(let f of i)f>o&&(n=Math.min(n,f));let a=n===1/0?o*1.5:(o+n)/2;this.maxSmallRadius=a,this.cellSize=Math.max(2*o*(1+1e-4),.001);let l=[],c=[];for(let f=0;f<this.bodyCount;f++)(t?.get(f)?.[0]===3||e.props[f*4+1]>a)&&l.push(f);for(let f=0;f<e.jointCount;f++){let d=e.info[f*4+1],p=e.info[f*4+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),f])}for(let f of e.ignoredPairs()){let d=Math.floor(f/2097152);c.push([d,f-d*2097152,4294967295])}c.sort((f,d)=>f[0]-d[0]||f[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let u=new Uint32Array(Math.max(l.length+3*c.length,4));u.set(l),c.forEach((f,d)=>u.set(f,l.length+3*d)),this.staticBuffer.size<u.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"2D boundaries",size:u.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,u),this.staticsDirty=!1}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new pi(this,2),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async circleCast(e,t,i,r={}){let[o]=await this.circleCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return o}circleCastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new pi(this,2),this.shapeQueries.cast(e,t)}liveIndex(e){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 2D body index is required")}queueBodyCommand(e,t,i,r,o=!1){return this.liveIndex(e),this.prepareBodyCommands().enqueue(e,t,i,r,o),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands||(this.bodyCommands=new Ur(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(qo(e),t)}addHull(e,t={}){return this.addShape(No(e),t)}addCapsule(e,t,i={}){return this.addShape(Vo(e,t),i)}addSegment(e,t,i={}){return this.addShape(Wo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Ho(e,t,i))}addShape(e,t={}){t=e.options??t;let{density:i=1,friction:r=.6,position:o=[0,0],angle:n=0,velocity:a=[0,0,0],restitution:l=0,isTrigger:c=!1,group:u=4294967295,collidesWith:f=4294967295,allowSleep:d}=t;if(this.destroyed)throw Error("The 2D solver has been destroyed");if(d!==void 0&&typeof d!="boolean")throw Error("allowSleep must be boolean");if(this.bodyCount>=this.bodyCapacity)throw Error("2D body capacity reached; reserve bodyCapacity when creating the solver");if(e.size.length!==2||!e.size.every(h=>Number.isFinite(h)&&h>0)||!Number.isFinite(i)||i<0||!Number.isFinite(r)||r<0||!Number.isFinite(n))throw Error("Body dimensions must be positive, density and friction nonnegative, and angle finite");if(a.length!==3||!Array.from(a).every(Number.isFinite))throw Error("Velocity requires [x, y, angular] finite components");if(typeof c!="boolean"||!Number.isFinite(l)||l<0||l>1)throw Error("isTrigger must be boolean and restitution between zero and one");if([u,f].some(h=>!Number.isInteger(h)||h<0||h>4294967295))throw Error("Collision masks must be unsigned 32-bit integers");let p=Vt(o,"position");e.geometry&&this.geometryBytes(e.geometry.vertices?.length??0);let m=super.addBody(e.size,i,r,[...p,n],Array.from(a));return e.geometry&&(Fr(this.topology,m,e,i),this.shapeBuffer?this.appendShapeGeometry(m,e.geometry):this.enableShapes(),this.uploadBodies(m,1)),l&&this.setRestitution(m,l),c&&this.setSensor(m,!0),(u!==4294967295||f!==4294967295)&&this.setFilters([m],[u],[f]),this.contactWatch?.setWatched([m],[!this.watchedIndices||this.watchedIndices.has(m)]),this.sleepEligible.set(m,d??!0),d===!0&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(m,d!==!1),this.wakeAll(),m}setRestitution(e,t){if(this.liveIndex(e),!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a coefficient in 0..1");t>0&&!this.restitution&&(this.restitution=new jo(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,Ro),t!==void 0&&!Number.isFinite(t)||i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("Motor speed must be finite and maximum torque nonnegative");let r=e*O,o=e*36;t!==void 0&&(this.topology.data[r+re]=t,le(this).float(this.jointBuffer,o+32,t)),i!==void 0&&(this.topology.data[r+_e]=-i,this.topology.data[r+Ae]=i,le(this).float(this.jointBuffer,o+12,-i),le(this).float(this.jointBuffer,o+16,i)),this.wakeAll()}setAngleLimits(e,t){this.constraintSlot(e,Fo);let i=ki(t);this.topology.data[e*O+be]=i.minAngle,this.topology.data[e*O+Ue]=i.maxAngle,le(this).float(this.jointBuffer,e*36+32+1,i.minAngle),le(this).float(this.jointBuffer,e*36+32+2,i.maxAngle);for(let r=4;r<8;r++)le(this).float(this.jointBuffer,e*36+r,0);this.wakeAll()}setWorldAnchor(e,t,i){if(this.constraintSlot(e,Bi),this.topology.info[e*j+1]!==-1||!Number.isFinite(t)||!Number.isFinite(i))throw Error("A world joint and finite xy anchor are required");le(this).float(this.jointBuffer,e*36+28,t),le(this).float(this.jointBuffer,e*36+28+1,i),this.wakeAll()}disableConstraint(e){if(this.topology.handles[e]?.disposed)return;this.constraintSlot(e),this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll();let t=this.topology.handles[e];t&&(t.alive=!1,t.disposed=!0)}appendJoint(e,t,i,r,o,n=1/0){if(e!==-1&&this.liveIndex(e),this.liveIndex(t),e===t)throw Error("Constraint endpoints must differ");let a=Vt(i,"anchorA"),l=Vt(r,"anchorB");if(!o||o.length!==3||Array.from(o).some(u=>u!==1/0&&(!Number.isFinite(u)||u<0))||n!==1/0&&(!Number.isFinite(n)||n<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");this.flushPropertyEdits();let c=super.appendJoint(e,t,a,l,Array.from(o),n);return this.wakeAll(),c}setFilters(e,t,i){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");let r=a=>{if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return a},o=Array.from(e,(a,l)=>(this.liveIndex(a),[a,r(t[l]),r(i[l])]));if(this.wakeAll(),!this.filters){let a=this.device;this.filters=a.createBuffer({size:this.bodyCapacity*8,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),a.queue.writeBuffer(this.filters,0,new Uint32Array(this.bodyCapacity*2).fill(4294967295)),this.compileBroadphase()}let n=le(this);for(let[a,l,c]of o)n.uint(this.filters,a*2,l),n.uint(this.filters,a*2+1,c)}bindFilters(){this.filterLayout&&(this.groups.broad=this.device.createBindGroup({layout:this.filterLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer,...this.shapeBuffer?[this.shapeBuffer]:[],...this.filters?[this.filters]:[]].map((e,t)=>({binding:t,resource:{buffer:e}}))}))}rebuildBindings(){super.rebuildBindings(),this.bindFilters(),this.bindContacts()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sensor flag must be boolean");if(this.wakeAll(),t&&!this.sensorsEnabled){this.sensorsEnabled=!0;let i=this.device,r=i.createComputePipeline({layout:"auto",compute:{module:i.createShaderModule({code:"@group(0) @binding(0) var<storage,read_write> data:array<vec4f>; @compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){if(id.x*6u+1u<arrayLength(&data)){data[id.x*6u+1u].w=0.;}}"}),entryPoint:"clear"}}),o=i.createBindGroup({layout:r.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.bodyBuffer}}]}),n=i.createCommandEncoder(),a=n.beginComputePass();a.setPipeline(r),a.setBindGroup(0,o),a.dispatchWorkgroups(Math.ceil(this.bodyCount/64)),a.end(),i.queue.submit([n.finish()]),this.compileContacts();let l=xs.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) {
|
|
4789
5006
|
if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace("fn dualContact(c: u32) {",`fn dualContact(c: u32) {
|
|
4790
5007
|
if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace(` let k = contacts[c];
|
|
4791
5008
|
let bound`,` let k = contacts[c];
|
|
4792
5009
|
if((k.ids.w&0x80000000u)!=0u){return;}
|
|
4793
|
-
let bound`);this.compile(l,[this.layouts.solve,this.layouts.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]);let c=
|
|
4794
|
-
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=Hn(Xn(Zn(e)))),e.includes("fn dynamicBody(")&&(e=Yn(e)),e.includes("fn colorCount(")&&(e=Jn(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 $t(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===Xt?"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?Qn.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=Ms(e):this.sleeping=new zs(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 Ns=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function we(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 sl=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function Uc(o,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new xi:this.editStaging??=new xi;this.editInProgress=!0;try{return Nc.call(this,o,e,i)}finally{this.editInProgress=t}}function Nc(o,e,t){t.count=0;let i,r=(s,a)=>{if(e===3)return we(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&&Ns(c),f!==void 0&&Ns(f),(i??=[]).push({index:a,isTrigger:n,restitution:l,group:c,collidesWith:f??4294967295,allowSleep:u})}let d=s.worldPoint===void 0?void 0:we(s.worldPoint,e,"worldPoint");if(s.position!==void 0){let p=we(s.position,e,"position"),m;if(s.rotation!==void 0){if(m=e===3?we(s.rotation,4,"rotation"):[s.rotation],e===3){let h=Math.hypot(...m);if(!h)throw Error("rotation must be nonzero");m=m.map(b=>b/h)}else if(!Number.isFinite(s.rotation))throw Error("angle must be finite")}t.append(a,4,p,e===3?m:void 0,m!==void 0),e===2&&(t.words[(t.count-1)*12+6]=m?.[0]??0)}else if(s.rotation!==void 0)throw Error("A rotation edit requires position; use teleport for a full pose");for(let p=0;p<sl.length;p++){if(p===4)continue;let m=sl[p];if(s[m]===void 0)continue;let b=p===1||p>=5?r(s[m],m):we(s[m],e,m);t.append(a,p,b,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 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 qs=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],[Ns(e)],[Ns(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 ol(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 al(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}=ol(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 qc(o,e){return al(this,o,e)}function nl(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(ol(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(()=>{al(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 ll(){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 Vs(o,e,t){o.advance=qc;let i,r;o.bodyIndex=function(a){if(this.destroyed)throw Error("The solver has been destroyed");let n=a;if(a instanceof qs){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 qs(this,n)),i.get(n)},o.editBodies=function(a){return Uc.call(this,a,t)},o.setLinearVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return this.velocityBatch??=new Lt(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 Ht(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 Lt(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 Jt(this.device),this.bodyReadback.read(this.bodyBuffer,l,n,6)}),o}var Ws=class extends Us{constructor(e,t,i){super(e,t,i),Vs(this,null,2)}};var na=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
|
|
5010
|
+
let bound`);this.compile(l,[this.layouts.solve,this.layouts.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]);let c=Qr.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
|
|
5011
|
+
if((contacts[gid.x].ids.w&0x80000000u)!=0u){return;}`);this.compile(c,[this.layouts.topo],["degreeContacts","fillContacts"])}this.sensorsEnabled&&le(this).float(this.bodyBuffer,e*24+7,t?1:0),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}compile(e,t,i){e.includes("fn warmStartBodies(")&&(e=Rc(Tc(Fc(e)))),e.includes("fn dynamicBody(")&&(e=jc(e));let r=e.includes("fn colorCount(");r&&(this.colorBucketSource=Ra(lo(e)),e=Ra(e));let o=this.device,n=o.createShaderModule({code:e}),a=o.createPipelineLayout({bindGroupLayouts:t});for(let l of i)this.pipes[l]=o.createComputePipeline({layout:a,compute:{module:n,entryPoint:l}});r&&i.includes("colorCount")&&(this.colorBucketPipelines={scan:this.pipes.colorCount},this.bitsetColorBuckets=void 0)}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array of live body indices");for(let r of i)this.liveIndex(r)}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");return this.contactWatch||(this.contactWatch=new xi(this.device,this.bodyBuffer,this.bodyCapacity,e,t,2),i===void 0&&this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,o)=>o),Array(this.bodyCount).fill(!0))),i!==void 0&&(this.watchedIndices=new Set(i),this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,o)=>o),Array.from({length:this.bodyCount},(r,o)=>this.watchedIndices.has(o)))),this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Ai?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}step(){if(this.destroyed)throw Error("The 2D solver has been destroyed");co(this,this.colorBucketSource),this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep||this.externalAfterStep?Dc.call(this):super.step(),this.contactWatch&&this.contactWatch.run({manifolds:this.contactBuffers[1-this.parity],contacts:this.contactBuffers[1-this.parity],counters:this.counterBuffer},this.contactCapacity,this.eventStep=(this.eventStep??0)+1,this.params.dt)}adapt(e){super.adapt(e),this.colorCap=Math.max(this.colorCap,this.minimumColors)}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.restitution?.before(e),this.externalBeforeStep?.(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e),this.externalAfterStep?.(e)}destroy(){this.passConstantCache=null,this.propertyEdits?.destroy(),!this.destroyed&&(this.detachFromScene(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.filters?.destroy(),this.shapeBuffer?.destroy(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),this.sleeping?.destroy(),super.destroy())}enableSleeping(e={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping?this.sleeping.options=Go(e):this.sleeping=new Do(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 Jo=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 Wc=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function cd(s,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new ti:this.editStaging??=new ti;this.editInProgress=!0;try{return ud.call(this,s,e,i)}finally{this.editInProgress=t}}function ud(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&&Jo(c),u!==void 0&&Jo(u),(i??=[]).push({index:n,isTrigger:a,restitution:l,group:c,collidesWith:u??4294967295,allowSleep:f})}let d=o.worldPoint===void 0?void 0:me(o.worldPoint,e,"worldPoint");if(o.position!==void 0){let p=me(o.position,e,"position"),m;if(o.rotation!==void 0){if(m=e===3?me(o.rotation,4,"rotation"):[o.rotation],e===3){let h=Math.hypot(...m);if(!h)throw Error("rotation must be nonzero");m=m.map(g=>g/h)}else if(!Number.isFinite(o.rotation))throw Error("angle must be finite")}t.append(n,4,p,e===3?m:void 0,m!==void 0),e===2&&(t.words[(t.count-1)*12+6]=m?.[0]??0)}else if(o.rotation!==void 0)throw Error("A rotation edit requires position; use teleport for a full pose");for(let p=0;p<Wc.length;p++){if(p===4)continue;let m=Wc[p];if(o[m]===void 0)continue;let g=p===1||p>=5?r(o[m],m):me(o[m],e,m);t.append(n,p,g,p===2||p===3?d:void 0,(p===2||p===3)&&d!==void 0),(p===3||p===6)&&(t.words[(t.count-1)*12+7]=this.params.dt)}}if(this.destroyed)throw Error("The solver was destroyed while reading body edits");t.count&&this.prepareBodyCommands().enqueueValidated(t);for(let o of i??[])o.isTrigger!==void 0&&this.setSensor(o.index,o.isTrigger),o.restitution!==void 0&&this.setRestitution(o.index,o.restitution),o.group!==void 0&&this.setFilters([o.index],[o.group],[o.collidesWith]),o.allowSleep!==void 0&&this.setSleepEnabled(o.index,o.allowSleep);return this}var Xo=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],[Jo(e)],[Jo(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 Hc(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 $c(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}=Hc(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 fd(s,e){return $c(this,s,e)}function Yo(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(Hc(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(()=>{$c(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 Qo(){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 Nr(s,e,t,i={}){s.advance=fd;let r,o;s.bodyIndex=function(a){if(this.destroyed)throw Error("The solver has been destroyed");let l=a;if(a instanceof Xo){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 Xo(this,l)),r.get(l)},s.editBodies=function(a){return cd.call(this,a,t)},s.setLinearVelocities=function(a,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,a,{linear:l}):(this.velocityBatch??=new Kt(this,t),this.velocityBatch.set(a,l),this)},s.setVelocities=function(a,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,a,l):l?.angular===void 0&&l?.linear!==void 0?this.setLinearVelocities(a,l.linear):l?.linear===void 0&&l?.angular!==void 0?this.setAngularVelocities(a,l.angular):(this.motionBatch??=new yi(this,t),this.motionBatch.set(a,l),this)},s.setAngularVelocities=function(a,l){if(this.destroyed)throw Error("The solver has been destroyed");return i.setVelocities?i.setVelocities.call(this,a,{angular:l}):(this.angularVelocityBatch??=new Kt(this,t,"angular"),this.angularVelocityBatch.set(a,l),this)};let n=s.destroy;return s.destroy=function(){this.destroyed||(this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback?.destroy(),this.editStaging=null,n.call(this),r?.clear(),o=null,e=null)},i.readSelectedBodies?s.readSelectedBodies=i.readSelectedBodies:t===2&&(s.readSelectedBodies=function(a,{posesOnly:l=!1}={}){let c=a instanceof Uint32Array?a:Array.from(a);return c.forEach(u=>this.liveIndex(u)),this.flushEdits(),this.bodyReadback??=new vi(this.device),this.bodyReadback.read(this.bodyBuffer,c,l,6)}),s}var Ko=class extends $o{constructor(e,t,i){super(e,t,i),Nr(this,null,2)}};var Ta=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
|
|
4795
5012
|
struct Command { header:vec4u, value:vec4f, point:vec4f }
|
|
4796
5013
|
struct Manifold { ids:vec4u, geo:vec4f }
|
|
4797
5014
|
@group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
|
|
@@ -4844,26 +5061,26 @@ fn angularImpulse(b:Body,impulse:vec3f)->vec3f {
|
|
|
4844
5061
|
let index=commands[ranges[i].x].header.x;
|
|
4845
5062
|
if(bodies[index].initialPos.w<0.){bodies[index].initialPos.w=-bodies[index].initialPos.w-1.;}
|
|
4846
5063
|
}
|
|
4847
|
-
`;var
|
|
4848
|
-
${
|
|
5064
|
+
`;var ja=class extends Ur{constructor(e){super(e,Ta,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;
|
|
5065
|
+
${Ta}`});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()}},Zo=class extends gi{constructor(e,t,i){super(e,t,i),this.nativeRef=t,Nr(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 ja(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 vi(this.device),this.bodyReadback.read(this.bodyBuffer,i,t)}async readBodyState(e){let t=await this.readSelectedBodies([e]),i=this.rigidAt(e)?.mass??t[19];return{position:Array.from(t.subarray(0,3)),rotation:Array.from(t.subarray(4,8)),linearVelocity:Array.from(t.subarray(32,35)),angularVelocity:Array.from(t.subarray(36,39)),mass:i,effectiveMass:t[19],sleeping:i>0&&t[19]===0}}raycastAll(...e){return this.flushEdits(),super.raycastAll(...e)}sphereCastAll(...e){return this.flushEdits(),super.sphereCastAll(...e)}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array");i.forEach(r=>this.liveIndex(r))}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");if(this.contactWatch||(this.contactWatch=new xi(this.device,this.bodyBuffer,this.bodyCapacity,e,t),i===void 0&&(i=Array.from({length:this.bodyCount},(r,o)=>o))),i!==void 0){let r=new Set(i);this.contactWatch.setWatched(Array.from({length:this.bodyCount},(o,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===Ai?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}setSensor(e,t){super.setSensor(e,t),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");let i=this.rigidAt(e);if(!i)throw Error("Sleep eligibility requires a scene body");return i.allowSleep=t,t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,i.mass,t),this.wakeAll(),this}enableSleeping(e={},t={}){let i=e.bodies?e:this.nativeRef;return super.enableSleeping(i,e.bodies?t:e),this}disableSleeping(){return this.flushEdits(),this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.dispose(),this.sleeping=null),this}attachToScene(e,t){return Yo.call(this,e,t)}detachFromScene(){return Qo.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 Cs(s,e,t){if(s.split(e).length!==2)throw Error("Pinned solver changed; review the cloth kernel integration");return s.replace(e,t)}var ii=Cs(Ge," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if (j >= params.jointCount) { return; }
|
|
4849
5066
|
if (info[j].x == T_SPRING) {
|
|
4850
5067
|
// Finite material stiffness: Eq. 16, with no hard-constraint multiplier.
|
|
4851
5068
|
joints[j].penLin.x = min(joints[j].penLin.w, max(PENALTY_MIN, joints[j].penLin.x * params.gamma));
|
|
4852
5069
|
return;
|
|
4853
5070
|
}
|
|
4854
|
-
if (info[j].x != T_JOINT) { return; }`);
|
|
5071
|
+
if (info[j].x != T_JOINT) { return; }`);ii=Cs(ii," let stiffness = k.penLin.w;"," let stiffness = k.penLin.x;");ii=Cs(ii," addRow(acc, n * sg, cross(r, n) * sg, stiffness, stiffness * (len - k.rA.w));",` let force = stiffness * (len - k.rA.w);
|
|
4855
5072
|
addRow(acc, n * sg, cross(r, n) * sg, stiffness, force);
|
|
4856
5073
|
// Eq. 17: positive diagonal lumping of the geometric Hessian's column
|
|
4857
5074
|
// norms. For C=|xA-xB|-L, G=(force/len)(I-n*n^T).
|
|
4858
5075
|
let g = abs(force / len) * sqrt(max(vec3f(0.0), vec3f(1.0) - n*n));
|
|
4859
|
-
(*acc).lin += mat3x3f(vec3f(g.x,0.0,0.0), vec3f(0.0,g.y,0.0), vec3f(0.0,0.0,g.z));`);
|
|
5076
|
+
(*acc).lin += mat3x3f(vec3f(g.x,0.0,0.0), vec3f(0.0,g.y,0.0), vec3f(0.0,0.0,g.z));`);ii=Cs(ii," if (t != T_JOINT) { return; }",` if (t == T_SPRING) {
|
|
4860
5077
|
let a = info[j].y;
|
|
4861
5078
|
let b = info[j].z;
|
|
4862
5079
|
let C = length(anchorA(joints[j], a) - (qrotate(bodies[b].rot, joints[j].rB.xyz) + bodies[b].pos.xyz)) - joints[j].rA.w;
|
|
4863
5080
|
joints[j].penLin.x = min(joints[j].penLin.w, joints[j].penLin.x + params.betaLin * abs(C));
|
|
4864
5081
|
return;
|
|
4865
5082
|
}
|
|
4866
|
-
if (t != T_JOINT) { return; }`);
|
|
5083
|
+
if (t != T_JOINT) { return; }`);ii=Cs(ii,"fn finishBody(i: u32, acc: Acc) {",`fn finishBody(i: u32, acc: Acc) {
|
|
4867
5084
|
if (all(bodies[i].moment.xyz == vec3f(0.0))) {
|
|
4868
5085
|
// LDL^T of the point's 3x3 SPD system, using the same pivot floor as
|
|
4869
5086
|
// rigid bodies. Contact friction moves the point; it cannot spin it.
|
|
@@ -4881,4 +5098,251 @@ ${na}`});this.invalidate=r.createComputePipeline({layout:"auto",compute:{module:
|
|
|
4881
5098
|
let v1 = y1/d1 - l21*v2 - l31*v3;
|
|
4882
5099
|
bodies[i].pos = vec4f(bodies[i].pos.xyz - vec3f(v1,v2,v3), bodies[i].pos.w);
|
|
4883
5100
|
return;
|
|
4884
|
-
}`);var cl=Et;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},ul=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function Js(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");ul(o.group??4294967295),ul(o.collidesWith??4294967295)}function fl(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 $s=class{constructor(e={}){if(this.ref=new jt,fl(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(),Js(t);let i=J(e,3,"size");i.forEach(s=>V(s,"size"));let r=new De(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(),Js(t),this.configure(Mi(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(),Js(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=nr(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(qr(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(),Js(i),this.configure(Hi(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:p=!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 p!="boolean")throw Error("Fabric pin options must be boolean");let m=Pt({breakStrain:c,breakForce:f}),h=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 v=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");v.set(b(M,P),{scale:V(_.stiffnessScale??1,"materialAt.stiffnessScale"),material:Pt({...m,..._})})}let w=Array.from({length:t},(M,P)=>Array.from({length:e},(_,C)=>this.addPoint([h[0]+(C-(e-1)/2)*i,h[1],h[2]+(P-(t-1)/2)*i],{mass:p&&(C===0||P===0||C===e-1||P===t-1)||d&&(C===0||C===e-1)&&(P===0||P===t-1)?0:s,radius:i/Math.SQRT2}))),g=w.flat(),A=new Map(g.map((M,P)=>[M,P])),x=[],B=new Map,E=(M,P,_)=>{(M.mass===0||P.mass===0)&&(_*=n/a);let C=A.get(M),S=A.get(P),R=v.get(b(C,S)),I=this.addSpring(M,P,[0,0,0],[0,0,0],{stiffness:_*(R?.scale??1),...R?.material??m});return B.set(b(A.get(M),A.get(P)),x.length),x.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,C=_+1,S=M*e+P-1,R=S+1;k.push(_,S,C,C,S,R)}let z=[];for(let M=0;M<k.length;M+=3){let[P,_,C]=k.slice(M,M+3);z.push(B.get(b(P,_)),B.get(b(_,C)),B.get(b(C,P)))}return{points:g,grid:w,indices:Uint32Array.from(k),connections:x,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 Be(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=Pt({breakStrain:n,breakForce:l}),f=new ct(this.ref,e,t,J(i,3,"anchorA"),J(r,3,"anchorB"),s,a);return os(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 Be(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);return Wi(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,p)=>d+u*s*.5*r.axisA[p]),n.map((d,p)=>d+u*s*.5*r.axisB[p]))),c,f;if(i.minAngle!==void 0||i.maxAngle!==void 0){let u=new Be(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);Wi(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 Hs(e,this.ref,ps(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:cl}}:{}})),r=[];this.ref.forces.forEach((l,c)=>{this.handles.get(l).slot=c,l instanceof Be&&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}},Xs=class{constructor(e={}){if(this.topology=new Ps,Object.assign(this.topology.params,Bs()),fl(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 Fs(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(js(e),t)}addHull(e,t={}){return this.addShape(Os(e),t)}addCapsule(e,t,i={}){return this.addShape(Ts(e,t),i)}addSegment(e,t,i={}){return this.addShape(Ds(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Gs(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(h=>V(h,"size"));let p=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 h of[c,f])if(!Number.isInteger(h)||h<0||h>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),[...p,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=ir(i),s=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);oa(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):oa(this.topology,a.slot,ir(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?ir(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 Ws(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}};typeof globalThis.AVBD<"u"?Object.assign(globalThis.AVBD,Ys):globalThis.AVBD=Ys;})();
|
|
5101
|
+
}`);var Jc=ii;var Zc=8,Xc=16,Yi=1e-6,Ga=1e-5,hd=1e-6,Fa=34028234663852886e22,en=0,eu=1,tn=2;function Yc(s){let e=s.positionAng;return{center:s.positionLin,half:ne(S(),s.size,.5),axis:[ae(S(),e,[1,0,0]),ae(S(),e,[0,1,0]),ae(S(),e,[0,0,1])]}}var Xi=(s,e)=>Math.abs(V(s,e));function rn(s,e){let t=V(e,s.axis[0])>=0?1:-1,i=V(e,s.axis[1])>=0?1:-1,r=V(e,s.axis[2])>=0?1:-1,o=D(S(),s.center,s.axis[0],s.half[0]*t);return D(o,o,s.axis[1],s.half[1]*i),D(o,o,s.axis[2],s.half[2]*r)}function tu(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 pd(s,e,t){let i=V(t,s.axis[e])>=0?1:-1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]);return{normal:r,center:o,...tu(s,e)}}function md(s,e){let t=0,i=-Fa;for(let r=0;r<3;r++){let o=Xi(s.axis[r],e);o>i&&(i=o,t=r)}return t}function gd(s,e,t){let i=V(s.axis[e],t)>0?-1:1,r=ne(S(),s.axis[e],i),o=D(S(),s.center,r,s.half[e]),{u:n,v:a,extentU:l,extentV:c}=tu(s,e),u=(f,d)=>{let p=D(S(),o,n,f*l);return D(p,p,a,d*c)};return[u(1,1),u(-1,1),u(-1,-1),u(1,-1)]}function bd(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<=Ga,c=a<=Ga;if(l!==c){let u=0,f=o-a;Math.abs(f)>Yi&&(u=ce(o/f,0,1)),i.length<Xc&&i.push(D(S(),r,J(S(),n,r),u))}c&&i.length<Xc&&i.push(n),r=n,o=a}return i}var sn=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=ne(S(),Li(S(),e,t),.5);for(let a of this.midpoints)if(Ne(J(S(),r,a))<hd)return;if(this.contacts.length>=Zc)return;let{bodyA:o,bodyB:n}=this;this.contacts.push({feature:i,rA:Te(S(),o.positionAng,J(S(),e,o.positionLin)),rB:Te(S(),n.positionAng,J(S(),t,n.positionLin))}),this.midpoints.push(r)}};function Da(s,e,t,i,r,o,n,a){let l=Ne(i);if(l<Yi)return!0;let c=1/Math.sqrt(l),u=ne(S(),i,c);V(u,t)<0&&ee(u,u);let f=Math.abs(V(t,u)),d=s.half[0]*Xi(u,s.axis[0])+s.half[1]*Xi(u,s.axis[1])+s.half[2]*Xi(u,s.axis[2]),p=e.half[0]*Xi(u,e.axis[0])+e.half[1]*Xi(u,e.axis[1])+e.half[2]*Xi(u,e.axis[2]),m=f-(d+p);return m>0?!1:((!a.valid||m>a.separation)&&(a.valid=!0,a.type=r,a.indexA=o,a.indexB=n,a.separation=m,a.normalAB=u),!0)}function Qc(s,e,t){let i=(e+1)%3,r=(e+2)%3,o=V(t,s.axis[i])>=0?1:-1,n=V(t,s.axis[r])>=0?1:-1,a=D(S(),s.center,s.axis[i],s.half[i]*o);D(a,a,s.axis[r],s.half[r]*n);let l=ne(S(),s.axis[e],s.half[e]);return[J(S(),a,l),Li(S(),a,l)]}function yd(s,e,t,i){let r=J(S(),e,s),o=J(S(),i,t),n=J(S(),s,t),a=V(r,r),l=V(o,o),c=V(o,n),u=0,f=0;if(a<=Yi&&l<=Yi)return[s,t];if(a<=Yi)f=ce(c/l,0,1);else{let d=V(r,n);if(l<=Yi)u=ce(-d/a,0,1);else{let p=V(r,o),m=a*l-p*p;Math.abs(m)>Yi&&(u=ce((p*c-d*l)/m,0,1)),f=(p*u+c)/l,f<0?(f=0,u=ce(-d/a,0,1)):f>1&&(f=1,u=ce((p-d)/a,0,1))}}return[D(S(),s,r,u),D(S(),t,o,f)]}function Kc(s,e,t,i,r,o,n){let a=r?t:i,l=r?i:t,c=r?n:ee(S(),n),u=pd(a,o,c),f=md(l,u.normal),d=gd(l,f,u.normal),p=[[u.u,u.extentU],[ee(S(),u.u),u.extentU],[u.v,u.extentV],[ee(S(),u.v),u.extentV]];for(let[g,b]of p)if(d=bd(d,g,V(g,u.center)+b),!d.length)return[];let m=new sn(s,e),h=(r?en:eu)<<24|(o&255)<<16|(f&255)<<8;for(let g=0;g<d.length&&m.contacts.length<Zc;g++){let b=d[g],y=V(J(S(),b,u.center),u.normal);if(y>Ga)continue;let A=D(S(),b,u.normal,-y),v=r?A:b,x=r?b:A;m.add(v,x,h|g&255)}return m.contacts.length||m.add(rn(t,n),rn(i,ee(S(),n)),h),m.contacts}function vd(s,e,t,i,r,o,n){let[a,l]=Qc(t,r,n),[c,u]=Qc(i,o,ee(S(),n)),[f,d]=yd(a,l,c,u),p=new sn(s,e),m=tn<<24|(r&255)<<8|o&255;return p.add(f,d,m),p.contacts.length||p.add(rn(t,n),rn(i,ee(S(),n)),m),p.contacts}function iu(s,e,t,i=!0){let r=Yc(s),o=Yc(e),n=J(S(),o.center,r.center),a={type:en,indexA:0,indexB:0,separation:-Fa,normalAB:S(),valid:!1},l={type:tn,indexA:0,indexB:0,separation:-Fa,normalAB:S(),valid:!1};for(let f=0;f<3;f++)if(!Da(r,o,n,r.axis[f],en,f,-1,a))return[];for(let f=0;f<3;f++)if(!Da(r,o,n,o.axis[f],eu,-1,f,a))return[];let c=S();for(let f=0;f<3;f++)for(let d=0;d<3;d++)if(Dt(c,r.axis[f],o.axis[d]),!Da(r,o,n,c,tn,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),zs(t,ee(S(),u.normalAB)),u.type===tn?vd(s,e,r,o,u.indexA,u.indexB,u.normalAB):u.type===en?Kc(s,e,r,o,!0,u.indexA,u.normalAB):Kc(s,e,r,o,!1,u.indexB,u.normalAB)}var de=(s,e)=>s.map((t,i)=>t+e[i]),T=(s,e)=>s.map((t,i)=>t-e[i]),q=(s,e)=>s.map(t=>t*e),F=(s,e)=>s.reduce((t,i,r)=>t+i*e[r],0),xt=(s,e)=>[s[1]*e[2]-s[2]*e[1],s[2]*e[0]-s[0]*e[2],s[0]*e[1]-s[1]*e[0]],Tt=s=>q(s,1/(Math.hypot(...s)||1)),Ee=(s,e)=>Array.from(ae(S(),s,e)),ru=(s,e)=>Te(S(),s.positionAng,T(e,s.positionLin)),Ot=(s,e)=>de(s.positionLin,Ee(s.positionAng,e)),xd=[[1,3,7,5],[0,4,6,2],[2,6,7,3],[0,1,5,4],[4,5,7,6],[0,2,3,1]],Ad=[[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]];function qr(s){if(De(s)||Fe(s)){let n=(De(s),s.size[0]/2),a=Fe(s)?Math.max(0,s.size[1]/2-n):0;return{body:s,radius:n,spine:[Ot(s,[0,-a,0]),Ot(s,[0,a,0])]}}let e=Ct(s),t=e?Array.from({length:e.vertices.length/3},(n,a)=>Ot(s,e.vertices.slice(a*3,a*3+3))):Array.from({length:8},(n,a)=>Ot(s,s.size.map((l,c)=>l/2*(a>>c&1?1:-1)))),i=e?e.faces.map(n=>({vertices:n.verts.map(a=>t[a]),normal:Ee(s.positionAng,n.normal)})):xd.map((n,a)=>({vertices:n.map(l=>t[l]),normal:Ee(s.positionAng,Ad[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 Ss(s,e){let t=s.spine??s.vertices,i=t[0];for(let r of t)F(r,e)>F(i,e)&&(i=r);return s.spine?de(i,q(e,s.radius)):i}function wd(s,e,t){let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,F(T(t,s),i)/(F(i,i)||1)))))}function on([s,e],[t,i]){let r=T(e,s),o=T(i,t),n=T(s,t),a=F(r,r),l=F(o,o),c=F(o,n),u=0,f=0,d=p=>Math.max(0,Math.min(1,p));if(a<1e-16)f=l>1e-16?d(c/l):0;else{let p=F(r,n);if(l<1e-16)u=d(-p/a);else{let m=F(r,o),h=a*l-m*m;u=h>1e-12*a*l?d((m*c-p*l)/h):0,f=(m*u+c)/l,f<0?(f=0,u=d(-p/a)):f>1&&(f=1,u=d((m-p)/a))}}return[de(s,q(r,u)),de(t,q(o,f))]}function Bd(s,e,t){let i=[];for(let r=0;r<s.length;r++){let o=s[r],n=s[(r+1)%s.length],a=F(o,e)-t,l=F(n,e)-t;a<=1e-8&&i.push(o),a<0!=l<0&&i.push(de(o,q(T(n,o),a/(a-l))))}return i}function Cd(s,e,t,i){let r=T(t,e),o=T(i,e),n=T(s,e),a=F(r,n),l=F(o,n);if(a<=0&&l<=0)return e;let c=T(s,t),u=F(r,c),f=F(o,c);if(u>=0&&f<=u)return t;let d=a*f-u*l;if(d<=0&&a>=0&&u<=0)return de(e,q(r,a/(a-u)));let p=T(s,i),m=F(r,p),h=F(o,p);if(h>=0&&m<=h)return i;let g=m*l-a*h;if(g<=0&&l>=0&&h<=0)return de(e,q(o,l/(l-h)));let b=u*h-m*f;if(b<=0&&f-u>=0&&m-h>=0)return de(t,q(T(i,t),(f-u)/(f-u+m-h)));let y=1/(b+g+d);return de(e,de(q(r,g*y),q(o,d*y)))}function Sd(s,e){let t=null,i=1/0;for(let r of s.faces)for(let o=1;o<r.vertices.length-1;o++){let n=Cd(e,r.vertices[0],r.vertices[o],r.vertices[o+1]),a=F(T(e,n),T(e,n));a<i&&(t=n,i=a)}return t}function su(s,e,t,i,r){if(!De(s)&&!De(e)&&!Fe(s)&&!Fe(e)&&!Ct(s)&&!Ct(e))return iu(s,e,t,s.solver.faceBias!==!1);i??=qr(s),r??=qr(e);let o=-1/0,n=[0,0,1],a=null,l=(f,d=null)=>{if(Math.hypot(...f)<1e-9)return;let p=Tt(f),m=F(T(Ss(i,q(p,-1)),Ss(r,p)),p),h=F(T(Ss(r,q(p,-1)),Ss(i,p)),p);h>m&&(m=h,p=q(p,-1)),m>o+1e-7&&(o=m,n=p,a=d)};for(let f of[i,r])for(let d of f.faces??[])l(d.normal);if(i.edges&&r.edges)for(let f of i.edges)for(let d of r.edges)l(xt(T(f[1],f[0]),T(d[1],d[0])),[f,d]);if(i.spine&&r.spine){let f=on(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,wd(...f.spine,p)));for(let p of d.edges){let m=on(f.spine,p);l(T(...m))}}if(o===-1/0&&l(T(s.positionLin,e.positionLin)),o>0)return[];o===-1/0&&(n=[0,0,1],o=-(i.radius+r.radius));let c=Tt(xt(n,Math.abs(n[0])<.7?[1,0,0]:[0,1,0]));t.set([...n,...c,...xt(n,c)]);let u=[];if(i.spine&&r.spine){let[f,d]=on(i.spine,r.spine);u=[[de(f,q(n,-i.radius)),de(d,q(n,r.radius))]]}else if(i.spine||r.spine){let f=i.spine?i:r,d=i.spine?r:i,p=i.spine?q(n,-1):n,m=Ss(f,p),h=Sd(d,m);u=[i.spine?[m,h]:[h,m]]}else if(a)u=[on(...a)];else{let f=(A,v)=>A.faces.reduce((x,w)=>F(w.normal,v)>F(x.normal,v)?w:x,A.faces[0]),d=f(i,q(n,-1)),p=f(r,n),m=F(d.normal,q(n,-1))>=F(p.normal,n),h=m?d:p,b=f(m?r:i,q(h.normal,-1)).vertices,y=q(h.vertices.reduce((A,v)=>de(A,v),[0,0,0]),1/h.vertices.length);for(let A=0;A<h.vertices.length&&b.length;A++){let v=h.vertices[A],x=h.vertices[(A+1)%h.vertices.length],w=Tt(xt(T(x,v),h.normal));F(T(y,v),w)>0&&(w=q(w,-1)),b=Bd(b,w,F(v,w))}for(let A of b){let v=F(T(A,h.vertices[0]),h.normal);if(v<=1e-6){let x=T(A,q(h.normal,v));u.push(m?[x,A]:[A,x])}}}return u.slice(0,8).map(([f,d],p)=>({feature:p+1,rA:ru(s,f),rB:ru(e,d)}))}var nn=(s,e,t)=>Math.max(e,Math.min(t,s)),kd=(s,e)=>[F(s.slice(0,3),e),F(s.slice(3,6),e),F(s.slice(6,9),e)];function qa(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=$r(jt(),kn(jt(),t),s.bodyB.positionAng),s.evaluateAng=i=>{let r=$r(jt(),s.bodyA?.positionAng??[0,0,0,1],s.cpuRest);ut(i,r,s.bodyB.positionAng);for(let o=0;o<3;o++)i[o]*=s.torqueArm;return i}}var Ua=class extends ni{initialize(){let e=this.bodyA,t=this.bodyB;if(!((e.group??4294967295)&(t.collidesWith??4294967295))||!((t.group??4294967295)&(e.collidesWith??4294967295)))return!1;let i=this.contacts,r=De(e)||De(t)||Fe(e)||Fe(t);this.friction=Math.sqrt(e.friction*t.friction),this.contacts=su(e,t,this.basis,this.solver.cpuGeometry.get(e),this.solver.cpuGeometry.get(t)).map(o=>{let n=i.find(a=>a.feature===o.feature);return{feature:o.feature,rA:n?.stick&&!r?n.rA:o.rA,rB:n?.stick&&!r?n.rB:o.rB,C0:S(),penalty:n?n.penalty.slice():S(),lambda:n?n.lambda.slice():S(),stick:n?.stick??!1}});for(let o of this.contacts){o.C0.set(kd(this.basis,T(Ot(e,o.rA),Ot(t,o.rB)))),o.C0[0]+=Rs;for(let n=0;n<3;n++)o.lambda[n]*=this.solver.alpha*this.solver.gamma,o.penalty[n]=nn(o.penalty[n]*this.solver.gamma,Et,Ve);if(this.solver.massPenalty!==!1){let n=Math.min(e.mass>0?e.mass:1/0,t.mass>0?t.mass:1/0);o.penalty[0]=Math.max(o.penalty[0],nn(n/this.solver.dt**2,Et,Ve))}}return this.contacts.length>0}updatePrimal(...e){!this.bodyA.isTrigger&&!this.bodyB.isTrigger&&super.updatePrimal(...e)}updateDual(...e){!this.bodyA.isTrigger&&!this.bodyB.isTrigger&&super.updateDual(...e)}},Na=class extends Ft{constructor(e,t,i){super(e,t.bodyA,t.bodyB),this.options={...i},this.pen=[Et,Et],this.lam=[0,0],this.source=t;let r=i.axisA;this.ta=Tt(Math.abs(r[0])>Math.abs(r[2])?[-r[1],r[0],0]:[0,-r[2],r[1]]);let o=this.bodyA?Ee(this.bodyA.positionAng,this.ta):this.ta,n=Array.from(Te(S(),this.bodyB.positionAng,o));n=T(n,q(i.axisB,F(n,i.axisB))),this.tb=Tt(n)}frame(){let e=this.bodyA?Ee(this.bodyA.positionAng,this.options.axisA):this.options.axisA,t=this.bodyA?Ee(this.bodyA.positionAng,this.ta):this.ta,i=Ee(this.bodyB.positionAng,this.tb);return{axis:e,angle:Math.atan2(F(e,xt(t,i)),F(t,i))}}initialize(){let e=this.frame().angle;this.c0=this.options.type==="motor"?[e,0]:[Math.min(0,e-this.options.minAngle),Math.max(0,e-this.options.maxAngle)];for(let t=0;t<2;t++)this.lam[t]*=this.solver.alpha*this.solver.gamma,this.pen[t]=nn(this.pen[t]*this.solver.gamma,Et,Ve);return!this.source.broken}evaluate(e){let{angle:t}=this.frame(),i=this.options,r=i.type==="motor"?[Math.atan2(Math.sin(t-this.c0[0]),Math.cos(t-this.c0[0]))-i.speed*this.solver.dt,0]:[t-i.minAngle-e*this.c0[0],t-i.maxAngle-e*this.c0[1]],o=r.map((n,a)=>this.pen[a]*n+this.lam[a]);return{c:r,f:i.type==="motor"?[nn(o[0],-i.maxTorque,i.maxTorque),0]:[Math.min(0,o[0]),Math.max(0,o[1])]}}updatePrimal(e,t,i){let{f:r}=this.evaluate(t),o=this.options;if(o.type==="motor"&&o.maxTorque===0)return;let n=q(this.frame().axis,e===this.bodyA?-1:1);for(let a=0;a<(o.type==="motor"?1:2);a++)if(!(o.type==="limit"&&r[a]===0))for(let l=0;l<3;l++){i.rhsAng[l]+=n[l]*r[a];for(let c=0;c<3;c++)i.lhsAng[l*3+c]+=n[l]*n[c]*this.pen[a]}}updateDual(e){let{c:t,f:i}=this.evaluate(e);this.lam=i;for(let r=0;r<2;r++)this.pen[r]=Math.min(Ve,this.pen[r]+this.solver.betaAng*(this.options.type==="motor"?Math.abs(t[r]):r===0?Math.max(0,-t[r]):Math.max(0,t[r])))}};function an(s){if(!s.cpuPrepared){s.cpuPrepared=!0;for(let e of[...s.forces]){let t=fs(e);if(t){e.unlinkFromBodies(),s.forces.splice(s.forces.indexOf(e),1),e.cpuAngular=new Na(s,e,t);continue}if(e instanceof ke&&e.stiffnessAng>0&&qa(e),e instanceof dt){let i=e.initialize.bind(e);e.initialize=()=>{if(e.broken)return!1;let r=Ui(e);if(r){let o=Math.max(0,Math.hypot(...T(Ot(e.bodyA,e.rA),Ot(e.bodyB,e.rB)))-e.rest);if(o/Math.max(e.rest,1e-6)>r.breakStrain||o*e.stiffness>r.breakForce)return e.broken=!0,!1}return i()}}}}}function ou(s){let e=s.bodies;s.cpuGeometry=new Map;for(let l of e)(De(l)||Fe(l)||Ct(l))&&s.cpuGeometry.set(l,qr(l));let t=e.filter(l=>l.mass>0).slice(0,1024).map(l=>l.radius).sort((l,c)=>l-c),i=Math.max(.1,(t[Math.floor(t.length/2)]??1)*2),r=new Map,o=[],n=new Set,a=(l,c)=>{if(l===c||e[l].mass<=0&&e[c].mass<=0)return;let u=Math.max(l,c),f=Math.min(l,c),d=u*e.length+f;if(n.has(d))return;n.add(d);let p=e[u],m=e[f];if(!((p.group??4294967295)&(m.collidesWith??4294967295))||!((m.group??4294967295)&(p.collidesWith??4294967295)))return;let h=p.radius+m.radius;F(T(p.positionLin,m.positionLin),T(p.positionLin,m.positionLin))<=h*h&&!p.constrainedTo(m)&&new Ua(s,p,m)};for(let l=0;l<e.length;l++){let c=e[l],u=c.positionLin.map(d=>Math.floor((d-c.radius)/i)),f=c.positionLin.map(d=>Math.floor((d+c.radius)/i));if((f[0]-u[0]+1)*(f[1]-u[1]+1)*(f[2]-u[2]+1)>128){o.push(l);continue}for(let d=u[0];d<=f[0];d++)for(let p=u[1];p<=f[1];p++)for(let m=u[2];m<=f[2];m++){let h=`${d},${p},${m}`,g=r.get(h)??[];for(let b of g)a(l,b);g.push(l),r.set(h,g)}}for(let l of o)for(let c=0;c<e.length;c++)a(l,c)}function ln(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 cn(s,e,t){if(!t)return;let i=e===3?s.bodies.length:s.bodyCount,r=e===3?6:4,o=e===3?new Float64Array(i*r):s.velocity;e===3&&s.bodies.forEach((h,g)=>{o.set(h.velocityLin,g*r),o.set(h.velocityAng,g*r+3)});let n=h=>e===3?s.bodies[h].mass>0&&!s.bodies[h].sleeping?1/s.bodies[h].mass:0:s.dynamic[h]?1/s.shape[h*4+2]:0,a=(h,g)=>{if(!n(h))return e===3?[0,0,0]:0;if(e===2)return s.shape[h*4+3]>0?g/s.shape[h*4+3]:0;let b=s.bodies[h],y=b.positionAng,A=Ee([-y[0],-y[1],-y[2],y[3]],g);return Ee(y,A.map((v,x)=>b.moment[x]>0?v/b.moment[x]:0))},l=(h,g,b)=>{let y=g*r;return e===3?T(h.subarray(y,y+3),xt(b,h.subarray(y+3,y+6))):[h[y]-h[y+2]*b[1],h[y+1]+h[y+2]*b[0]]},c=(h,g)=>e===3?xt(h,g):h[0]*g[1]-h[1]*g[0],u=(h,g)=>e===3?F(h,g):h*g,f;if(e===3){let h=new Map(s.bodies.map((g,b)=>[g,b]));f=s.forces.filter(g=>g.contacts?.length&&!g.bodyA.isTrigger&&!g.bodyB.isTrigger).map(g=>{let b=(y,A)=>{let v=[0,0,0];for(let x of g.contacts){let w=Ee(y.positionAng,x[A]);for(let B=0;B<3;B++)v[B]+=w[B]/g.contacts.length}return v};return{a:h.get(g.bodyA),b:h.get(g.bodyB),n:g.basis.slice(0,3),ra:b(g.bodyA,"rA"),rb:b(g.bodyB,"rB"),e:Math.max(g.bodyA.restitution??0,g.bodyB.restitution??0)}})}else{let h=(g,b)=>{let y=Math.cos(s.pose[g*4+2]),A=Math.sin(s.pose[g*4+2]);return[y*b[0]-A*b[1],A*b[0]+y*b[1]]};f=s.contactPairs.filter(g=>!s.sensors.has(g.a)&&!s.sensors.has(g.b)).map(g=>({...g,ra:h(g.a,g.ra),rb:h(g.b,g.rb),e:Math.max(s.restitution.get(g.a)??0,s.restitution.get(g.b)??0)}))}let d=new Uint32Array(i);if(e===3)s.bodies.forEach((h,g)=>{d[g]=h.forces.length});else for(let h=0;h<i;h++)d[h]=s.adjStart[h+1]-s.adjStart[h];let p=[];for(let h of f){if(h.e<=0)continue;let g=F(h.n,T(l(t,h.a,h.ra),l(t,h.b,h.rb)));if(g>=-1)continue;let b=c(h.ra,h.n),y=c(h.rb,h.n),A=a(h.a,b),v=a(h.b,y),x=n(h.a)+n(h.b)+u(b,A)+u(y,v);x>0&&p.push({...h,inertiaA:A,inertiaB:v,k:x,target:-h.e*g,impulse:0})}let m=new Float64Array(o.length);for(let h=0;h<12&&p.length;h++){m.fill(0);for(let g of p){let b=F(g.n,T(l(o,g.a,g.ra),l(o,g.b,g.rb))),y=Math.max(0,g.impulse+(g.target-b)/(g.k*Math.max(1,d[g.a],d[g.b]))),A=y-g.impulse;g.impulse=y;for(let[v,x,w]of[[g.a,1,g.inertiaA],[g.b,-1,g.inertiaB]]){let B=v*r,L=n(v);for(let M=0;M<e;M++)m[B+M]+=x*A*L*g.n[M];if(e===3)for(let M=0;M<3;M++)m[B+3+M]+=x*A*w[M];else m[B+2]+=x*A*w}}for(let g=0;g<o.length;g++)o[g]+=m[g]}e===3&&s.bodies.forEach((h,g)=>{h.velocityLin.set(o.subarray(g*r,g*r+3)),h.velocityAng.set(o.subarray(g*r+3,g*r+6))})}function nu(s,e,t){let i=e.windSpeed??0;if(!nr(s)||s.mass<=0||i===0)return null;let r=e.windAngle??0,o=[Math.cos(r),Math.sin(r),0],n=[-o[1],o[0],0],a=s.positionLin,l=t*e.dt,c=F(a,o),u=F(a,n),f=.5*Math.sin(1.1*l-.35*c+.2*u)+.3*Math.sin(2.9*l-.8*c+.6*a[2])+.2*Math.sin(6.1*l-1.7*c+1.3*a[2]+.9*u),d=.6*Math.sin(1.7*l-.6*c+.4*a[2])+.4*Math.sin(4.3*l-1.3*c-.7*a[2]),p=Math.sin(2.3*l-.9*c+1.1*u),m=e.windGust??.4,h=o.map((x,w)=>i*x*(1+m*f)+m*i*(n[w]*.25*d+(w===2?.1*p:0))-s.velocityLin[w]),g=Ee(s.positionAng,[0,0,1]),b=(e.windPressure??.72)*s.size[0]*s.size[1]/s.mass,y=F(h,g),A=h.map((x,w)=>x-g[w]*y),v=b*y*Math.abs(y)/(1+2*e.dt*b*Math.abs(y));return g.map((x,w)=>x*v+A[w]*.06*b*Math.hypot(...A))}var Pd=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),un=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;massPenalty=!0;faceBias=!0;up=[0,0,1];cpuStep=0;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,Pd())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,o=S(),n=S();for(let a=this.bodies.length-1;a>=0;a--){let l=this.bodies[a];if(l.mass<=0)continue;Te(o,l.positionAng,J(o,e,l.positionLin)),Te(n,l.positionAng,t);let c=0,u=1/0,f=!0;for(let p=0;p<3&&f;p++){let m=l.size[p]*.5;if(Math.abs(n[p])<1e-6){(o[p]<-m||o[p]>m)&&(f=!1);continue}let h=1/n[p],g=(-m-o[p])*h,b=(m-o[p])*h;g>b&&([g,b]=[b,g]),c=Math.max(c,g),u=Math.min(u,b),c>u&&(f=!1)}if(!f)continue;let d=c>=0?c:u;d<0||(!r||d<r.t)&&(r={body:l,local:D(S(),o,n,d),t:d})}return r}sys=Os();dxLin=S();dxAng=S();dp=S();accel=S();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:o}=this,n=ln(this,3);an(this),ou(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=nu(c,this,this.cpuStep);u&&D(c.inertialLin,c.inertialLin,u,e*e),si(c.inertialAng,c.positionAng,au(c.velocityAng,e)),_t(this.accel,J(this.accel,c.velocityLin,c.prevVelocityLin),e);let f=V(this.accel,this.up)*Es(t),d=ce(f/Math.abs(t),0,1);Number.isFinite(d)||(d=0),c.initialLin.set(c.positionLin),c.initialAng.set(c.positionAng),c.mass>0&&!c.sleeping&&(D(c.positionLin,c.positionLin,c.velocityLin,e),D(c.positionLin,c.positionLin,this.up,t*d*e*e),si(c.positionAng,c.positionAng,au(c.velocityAng,e)))}for(let l=0;l<this.iterations;l++){for(let c=i.length-1;c>=0;c--){let u=i[c];if(u.mass<=0||u.sleeping)continue;let f=u.mass,d=u.moment;Ce(r.lhsLin,f/(e*e),f/(e*e),f/(e*e)),Ce(r.lhsAng,d[0]/(e*e),d[1]/(e*e),d[2]/(e*e)),r.lhsCross.fill(0),ue(r.rhsLin,r.lhsLin,J(o,u.positionLin,u.inertialLin)),ue(r.rhsAng,r.lhsAng,ut(o,u.positionAng,u.inertialAng));let p=u.forces;for(let h=p.length-1;h>=0;h--)p[h].updatePrimal(u,this.alpha,r);let m=u.moment.every(h=>h===0);m&&(Ce(r.lhsAng,1,1,1),r.lhsCross.fill(0),r.rhsAng.fill(0)),Is(r.lhsLin,r.lhsAng,r.lhsCross,ee(r.rhsLin,r.rhsLin),ee(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),D(u.positionLin,u.positionLin,this.dxLin,1),m||si(u.positionAng,u.positionAng,this.dxAng)}for(let c of this.forces)c.updateDual(this.alpha)}for(let l of i)l.prevVelocityLin.set(l.velocityLin),l.mass>0&&!l.sleeping&&(_t(l.velocityLin,J(l.velocityLin,l.positionLin,l.initialLin),e),_t(l.velocityAng,ut(l.velocityAng,l.positionAng,l.initialAng),e));cn(this,3,n),this.cpuStep++}},Ld=S(),au=(s,e)=>ne(Ld,s,e);var Ps=(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),ot=(s,e)=>s[0]*e[0]+s[1]*e[1],ks=(s,e)=>[Math.cos(s)*e[0]-Math.sin(s)*e[1],Math.sin(s)*e[0]+Math.cos(s)*e[1]],cu=s=>Math.max(0,Math.min(1,s)),lu=(s,e,t)=>Ps(s,Lt(Re(e,s),cu(ot(Re(t,s),Re(e,s))/(ot(Re(e,s),Re(e,s))||1))));function fn(s,e){let t=ei(s)?.get(e)??[0,0,0,0],i=Array.from(s.pose.subarray(e*4,e*4+2)),r=s.pose[e*4+2],o=l=>Ps(i,ks(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 Qi(s,e){if(s.vertices){let r=-1/0,o=[];for(let n of s.vertices){let a=ot(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=Ps(s.spine[0],Lt(e,s.radius)),i=Ps(s.spine[1],Lt(e,s.radius));return Math.abs(ot(t,e)-ot(i,e))<1e-7?[t,i]:ot(t,e)>ot(i,e)?[t,t]:[i,i]}function uu(s,e,t,i){let r=fn(s,e),o=fn(s,t),n=s.pose,a=s.shape;if(r.kind===0&&o.kind===0)return _o(...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=ks(f.angle,[0,1]),m=Qi(d,Lt(p,-1));for(let h of m){let g=ot(Re(h,f.p),p);if(g<=0){let b=Re(h,Lt(p,g));l.push({a:u?b:h,b:u?h:b,n:u?Lt(p,-1):p})}}}else{let u=-1/0,f=[0,1],d=x=>{let w=Math.hypot(...x);if(w<1e-9)return;let B=Lt(x,1/w),L=ot(Re(Qi(r,Lt(B,-1))[0],Qi(o,B)[0]),B),M=ot(Re(Qi(o,Lt(B,-1))[0],Qi(r,B)[0]),B);Math.max(L,M)>u+1e-7&&(u=Math.max(L,M),f=L>=M?B:Lt(B,-1))};for(let[x,w]of[[r,o],[o,r]])if(x.vertices)for(let B=0;B<x.vertices.length;B++){let L=Re(x.vertices[(B+1)%x.vertices.length],x.vertices[B]);d([L[1],-L[0]]),w.vertices||d(Re(x.vertices[B],lu(...w.spine,x.vertices[B])))}else if(d(ks(x.angle,[0,1])),!w.vertices)for(let B of x.spine)d(Re(B,lu(...w.spine,B)));if(u===-1/0&&d([0,1]),u>0)return 0;let p=Qi(r,Lt(f,-1)),m=Qi(o,f),h=[-f[1],f[0]],g=p.map(x=>ot(x,h)),b=m.map(x=>ot(x,h)),y=Math.max(Math.min(...g),Math.min(...b)),A=Math.min(Math.max(...g),Math.max(...b)),v=(x,w)=>{let B=ot(Re(x[1],x[0]),h);return Math.abs(B)<1e-9?x[0]:Ps(x[0],Lt(Re(x[1],x[0]),cu((w-ot(x[0],h))/B)))};for(let x of A-y>1e-6?[y,A]:[(y+A)/2])l.push({a:v(p,x),b:v(m,x),n:f})}let c=0;for(let u of l){if(c&&Math.hypot(...Re(u.a,l[0].a))<1e-7)continue;let f=ks(-r.angle,Re(u.a,r.p)),d=ks(-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 fu=(s,e,t)=>[Math.cos(s)*e-Math.sin(s)*t,Math.sin(s)*e+Math.cos(s)*t],du=function(){let{pose:s,shape:e,props:t}=this,i=this.broadphase.pairs,r=this.contactOut;for(let o=0;o<this.broadphase.pairCount;o++){let n=i[o],a=Math.floor(n/2097152),l=n-a*2097152,c=this.collideBodies(a,l,r);if(c===0)continue;let u=0,f=this.prevCount;for(;u<f;){let m=u+f>>>1;this.prevKeys[m]<n?u=m+1:f=m}let d=u,p=Math.sqrt(t[a*4]*t[l*4]);for(let m=0;m<c;m++){let h=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(h+1)*j),this.data=this.grow(this.data,(h+1)*O);let g=this.data,b=h*O,y=m*7,A=r[y+0];this.info.set([bs,a,l,A],h*j),g.fill(0,b,b+O),g[b+Q]=g[b+Q+1]=1/0,g[b+_e]=-1/0,g[b+Ae]=0,g[b+Pt]=g[b+Pt+1]=1/0,g[b+N]=r[y+1],g[b+N+1]=r[y+1+1],g[b+Y]=r[y+3],g[b+Y+1]=r[y+3+1],g[b+re]=p,g[b+be]=r[y+5],g[b+Ue]=r[y+5+1];let v=-1;for(let P=d;P<this.prevCount&&this.prevKeys[P]===n;P++)this.prevInfo[P*j+3]===A&&(v=P);if(v<0&&this.params.matchNearest){let P=Ma*Math.min(e[a*4],e[a*4+1],e[l*4],e[l*4+1]);for(let C=d;C<this.prevCount&&this.prevKeys[C]===n;C++){let _=Math.hypot(this.prevData[C*O+N]-g[b+N],this.prevData[C*O+N+1]-g[b+N+1]);_<=P&&(P=_,v=C)}}if(v>=0){let P=v*O;g[b+se]=this.prevData[P+se],g[b+se+1]=this.prevData[P+se+1],g[b+te]=this.prevData[P+te],g[b+te+1]=this.prevData[P+te+1],this.prevData[P+Hi]&&(g[b+N]=this.prevData[P+N],g[b+N+1]=this.prevData[P+N+1],g[b+Y]=this.prevData[P+Y],g[b+Y+1]=this.prevData[P+Y+1])}let[x,w]=fu(s[a*4+2],g[b+N],g[b+N+1]),[B,L]=fu(s[l*4+2],g[b+Y],g[b+Y+1]),M=g[b+be],I=g[b+Ue],E=s[a*4]+x-s[l*4]-B,k=s[a*4+1]+w-s[l*4+1]-L;g[b+fe]=M*E+I*k+5e-4,g[b+fe+1]=I*E-M*k}}};var dn=class extends Dr{constructor(e,t,i){if(super({precision:i==="soa-colored"?"f32":"f64",order:i==="soa-colored"?"colored":"sequential"}),Object.assign(this,e),this.options={...this.options,precision:i==="soa-colored"?"f32":"f64",order:i==="soa-colored"?"colored":"sequential"},this.options.precision==="f64")for(let[r,o]of Object.entries(this))o instanceof Float32Array&&(this[r]=Float64Array.from(o));for(let[r,o]of ei(e)??[]){let n=this.shape[r*4+2];Fr(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=[],du.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=uu(this,e,t,i);if(n){let a=[i[5],i[6]],l=[0,0],c=[0,0];for(let p=0;p<n;p++)for(let m=0;m<2;m++)l[m]+=i[p*7+1+m]/n,c[m]+=i[p*7+3+m]/n;let u=this.pose[e*4+2],f=Math.cos(u),d=Math.sin(u);this.contactPairs.push({a:e,b:t,n:a,ra:l,rb:c,point:[this.pose[e*4]+f*l[0]-d*l[1],this.pose[e*4+1]+d*l[0]+f*l[1],0],impulse:0})}return this.sensors.has(e)||this.sensors.has(t)?0:n}initJoints(){for(let e=0;e<this.jointCount;e++){let t=e*O,i=this.info[e*j];if(this.data[t+Q]===0&&this.data[t+Q+1]===0&&this.data[t+Q+2]===0){i!==0&&(this.info[e*j]=0,this.noCollideDirty=!0,this.handles[e]&&(this.handles[e].alive=!1));continue}i===1&&this.jointC(this.info[e*j+1],this.info[e*j+2],t,this.pose,this.data,t+fe)}for(let e=0;e<this.jointCount;e++)if(this.info[e*j]===5){let t=e*O,i=this.info[e*j+1],r=this.info[e*j+2],o=(i<0?0:this.pose[i*4+2])-this.pose[r*4+2]-this.data[t+re];this.data[t+fe]=Math.min(0,o-this.data[t+be]),this.data[t+fe+1]=Math.max(0,o-this.data[t+Ue])}}warmStartConstraints(e,t){let i=[];for(let r=0;r<this.jointCount;r++)this.info[r*j]===5&&(i.push(r),this.info[r*j]=4);super.warmStartConstraints(e,t);for(let r of i)this.info[r*j]=5}evalConstraint(e,t,i){if(this.info[e*j]!==5)return super.evalConstraint(e,t,i);let r=e*O,o=this.info[e*j+1],n=this.info[e*j+2],a=this.data,l=(o<0?0:this.pose[o*4+2])-this.pose[n*4+2]-a[r+re];this.eC[0]=l-a[r+be]-t*a[r+fe],this.eC[1]=l-a[r+Ue]-t*a[r+fe+1],this.eG.fill(0),this.eJ.fill(0);for(let c=0;c<2;c++){this.eMin[c]=a[r+_e+c],this.eMax[c]=a[r+Ae+c];let u=Math.max(this.eMin[c],Math.min(this.eMax[c],a[r+se+c]*this.eC[c]+a[r+te+c]));i>=0&&u!==0&&(this.eJ[c*3+2]=i===o?1:-1)}return 2}step(){let e=ln(this,2);super.step(),cn(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 pn=(s,e,t)=>{let i=T(e,s);return de(s,q(i,Math.max(0,Math.min(1,F(T(t,s),i)/(F(i,i)||1)))))};function hn(s,e,t,i){let r=T(s,t),o=F(r,e),n=o*o-F(r,r)+i*i;if(n<0)return null;let a=-o-Math.sqrt(n);return a<0?null:{t:a,n:Tt(T(de(s,q(e,a)),t))}}function Vr(s,e){return e&&(!s||e.t<s.t)?e:s}function Va(s,e,t,i,r){let o=T(i,t),n=T(s,t),a=F(o,o);if(a<1e-16)return hn(s,e,t,r);let l=F(o,e),c=F(o,n),u=F(n,e),f=a-l*l,d=a*u-c*l,p=a*F(n,n)-c*c-r*r*a,m=d*d-f*p,h=null;if(f>1e-12*a&&m>=0){let g=(-d-Math.sqrt(m))/f,b=c+g*l;g>=0&&b>=0&&b<=a&&(h={t:g,n:Tt(T(de(s,q(e,g)),de(t,q(o,b/a))))})}return Vr(Vr(h,hn(s,e,t,r)),hn(s,e,i,r))}function hu(s,e){let t=q(e.vertices.reduce((i,r)=>de(i,r),[0,0,0]),1/e.vertices.length);for(let i=0;i<e.vertices.length;i++){let r=e.vertices[i],o=e.vertices[(i+1)%e.vertices.length],n=xt(T(o,r),e.normal);if(F(n,T(s,r))*(F(n,T(t,r))>=0?1:-1)<-1e-6)return!1}return!0}function _d(s,e,t,i){if(i===0){let a=-1/0,l=1/0,c=q(t,-1);for(let u of s.faces){let f=F(u.normal,t),d=F(u.normal,T(u.vertices[0],e));if(Math.abs(f)<1e-12){if(d<0)return null}else{let p=d/f;f<0?p>a&&(a=p,c=u.normal):l=Math.min(l,p)}if(a>l)return null}return l<0?null:{t:Math.max(0,a),n:a<=0?q(t,-1):c}}let r=!0,o=1/0,n=null;for(let a of s.faces){let l=F(a.normal,T(e,a.vertices[0]));l>0&&(r=!1),hu(T(e,q(a.normal,l)),a)&&(o=Math.min(o,l*l));let c=F(a.normal,t);if(c<-1e-12){let u=(i-l)/c;u>=0&&hu(T(de(e,q(t,u)),q(a.normal,i)),a)&&(n=Vr(n,{t:u,n:a.normal}))}}for(let[a,l]of s.edges){let c=T(e,pn(a,l,e));o=Math.min(o,F(c,c)),n=Vr(n,Va(e,t,a,l,i))}return r||o<=i*i?{t:0,n:q(t,-1)}:n}var At=(s,e)=>s[0]*e[0]+s[1]*e[1];function Ed(s,e,t,i){let r=!0,o=1/0,n=null;for(let a=0;a<s.vertices.length;a++){let l=s.vertices[a],c=s.vertices[(a+1)%s.vertices.length],u=T(c,l),f=Tt([u[1],-u[0]]),d=At(T(e,l),f);d>0&&(r=!1);let p=T(e,pn(l,c,e));o=Math.min(o,At(p,p));let m=At(t,f);if(m<-1e-12){let h=(i-d)/m,g=T(de(e,q(t,h)),q(f,i)),b=At(T(g,l),u),y=1e-6*At(u,u);h>=0&&b>=-y&&b<=At(u,u)+y&&(n=Vr(n,{t:h,n:f}))}i>0&&(n=Vr(n,hn(e,t,l,i)))}return r||o<=i*i?{t:0,n:q(t,-1)}:n}function Md(s,e,t,i){if(s.vertices)return Ed(s,e,t,i);if(s.kind===3){let p=[-Math.sin(s.angle),Math.cos(s.angle)],m=At(T(e,s.p),p)-i,h=At(t,p);return m<=0?{t:0,n:q(t,-1)}:h<-1e-12?{t:-m/h,n:p}:null}let r=s.kind===1?s.p:s.spine[0],o=s.kind===1?s.p:s.spine[1],n=i+s.radius,a=T(e,pn(r,o,e));if(At(a,a)<=n*n)return{t:0,n:q(t,-1)};if(n>0)return Va(e,t,r,o,n);let l=T(o,r),c=t[0]*l[1]-t[1]*l[0];if(Math.abs(c)<1e-12){let p=T(e,r);if(Math.abs(p[0]*l[1]-p[1]*l[0])/Math.max(Math.hypot(...l),1e-20)>1e-6)return null;let h=Math.min(At(T(r,e),t),At(T(o,e),t));return Math.max(At(T(r,e),t),At(T(o,e),t))<0?null:{t:Math.max(0,h),n:q(t,-1)}}let u=T(r,e),f=(u[0]*l[1]-u[1]*l[0])/c,d=(u[0]*t[1]-u[1]*t[0])/c;return f>=0&&d>=0&&d<=1?{t:f,n:q(Tt([l[1],-l[0]]),c>0?-1:1)}:null}function pu(s,e,t,i,{maxDistance:r=1e30,ignore:o=[],collidesWith:n=4294967295,includeTriggers:a=!0}={}){let l=s.dimension,c=me(e,l,"origin"),u=me(i,l,"direction"),f=Math.hypot(...u);if(!f||!Number.isFinite(t)||t<0||!Number.isFinite(r)||r<0)throw Error("Provide a nonzero direction, finite nonnegative radius and distance");if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let d=new Set(o);for(let h of d)s.liveIndex(h);let p=u.map(h=>h/f),m=null;for(let h=0;h<s.bodyCount;h++){if(d.has(h))continue;let g=l===3?s.solver.bodies[h]:null,b=g?g.group??4294967295:s.solver.filters.get(h)?.[0]??4294967295,y=g?g.isTrigger:s.solver.sensors.has(h);if(!(b&n)||!a&&y)continue;let A;if(l===2)A=Md(fn(s.solver,h),c,p,t);else{let x=qr(g);if(x.spine){let w=x.radius+t,B=T(c,pn(...x.spine,c));A=F(B,B)<=w*w?{t:0,n:q(p,-1)}:Va(c,p,...x.spine,w)}else A=_d(x,c,p,t)}if(!A||A.t>r||m&&A.t>=m.distance)continue;let v=de(c,q(p,A.t));m={index:h,distance:A.t,normal:A.n,center:v,point:T(v,q(A.n,t))}}return m}var mn=class{constructor(e,{speedThreshold:t=.08,timeThreshold:i=.6}={}){if(!Number.isFinite(t)||t<=0||!Number.isFinite(i)||i<=0)throw Error("Sleep thresholds must be positive and finite");this.world=e,this.speedThreshold=t,this.timeThreshold=i,this.time=new Float64Array(e.bodyCount),this.asleep=new Set}wake(){let e=this.world;for(let t of this.asleep)e.dimension===3?e.solver.bodies[t].sleeping=!1:e.solver.dynamic[t]=+(e.solver.shape[t*4+2]>0);this.asleep.clear(),this.time.fill(0)}after(){let e=this.world,t=e.solver,i=e.bodyCount,r=Int32Array.from({length:i},(d,p)=>p),o=new Set,n=[],a=d=>e.dimension===3?t.bodies[d].mass:t.shape[d*4+2],l=d=>{for(;r[d]!==d;)r[d]=r[r[d]],d=r[d];return d},c=(d,p)=>{if(d<0){o.add(p);return}a(d)<=0?o.add(p):a(p)<=0?o.add(d):r[l(d)]=l(p),n.push([d,p])};if(e.dimension===3){let d=new Map(t.bodies.map((p,m)=>[p,m]));for(let p of t.forces)!p.broken&&!p.bodyA?.isTrigger&&!p.bodyB?.isTrigger&&c(p.bodyA?d.get(p.bodyA):-1,d.get(p.bodyB))}else{for(let d of t.contactPairs)!t.sensors.has(d.a)&&!t.sensors.has(d.b)&&c(d.a,d.b);for(let d=0;d<t.jointCount;d++)(t.data[d*32+6]||t.data[d*32+7]||t.data[d*32+8])&&c(t.info[d*4+1],t.info[d*4+2])}let u=new Set([...o].map(l)),f=new Map;for(let d=0;d<i;d++)if(a(d)>0){let p=l(d),m=e.dimension===3?t.bodies[d]:null,h=m?m.allowSleep!==!1:t.avbdSleepEligible?.get(d)!==!1,g=m?Math.hypot(...m.velocityLin,...m.velocityAng):Math.hypot(...t.velocity.subarray(d*4,d*4+3));f.has(p)||f.set(p,u.has(p)),f.set(p,f.get(p)&&h&&g<=this.speedThreshold)}for(let d=0;d<i;d++)a(d)>0&&(this.time[d]=f.get(l(d))?this.time[d]+e.params.dt:0,this.time[d]>=this.timeThreshold&&(this.asleep.add(d),e.dimension===3?(t.bodies[d].sleeping=!0,t.bodies[d].velocityLin.fill(0),t.bodies[d].velocityAng.fill(0)):(t.dynamic[d]=0,t.velocity.fill(0,d*4,d*4+4))));n.some(([d,p])=>d>=0&&a(d)>0&&a(p)>0&&this.asleep.has(d)!==this.asleep.has(p))&&this.wake()}};var gn=class{constructor(e,t,i={}){if(this.backend="cpu",this.dimension=t,this.scene=e,this.stepCount=0,this.pending=new ti,this.events=[],this.previous=new Map,this.watched=null,t===3){this.solver=Object.assign(new un,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)}),an(this.solver)}else this.solver=new dn(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,Nr(this,t===3?{bodies:this.solver.bodies}:null,t,{setVelocities:this.setPackedVelocities,readSelectedBodies:this.readSelectedBodies}),(i.sleeping||e.sleepOptIn||[...e.topology?.avbdSleepEligible?.values()??[]].some(Boolean))&&this.enableSleeping(i.sleeping===!0?{}:i.sleeping)}get bodyCount(){return this.dimension===3?this.solver.bodies.length:this.solver.bodyCount}get jointCount(){return this.dimension===3?this.constraints.length:this.solver.jointCount}gpuIndex(e){return this.liveIndex(e),e}liveIndex(e){if(this.assertLive(),!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live body index is required")}assertLive(){if(this.destroyed)throw Error("The solver has been destroyed")}prepareBodyCommands(){return this.wakeAll(),{enqueueValidated:e=>{this.pending.reserve(this.pending.count+e.count),this.pending.u32.set(e.u32.subarray(0,e.count*12),this.pending.count*12),this.pending.count+=e.count}}}setLinearVelocity(e,t){return this.editBodies([{body:e,linearVelocity:t}])}setAngularVelocity(e,t){return this.editBodies([{body:e,angularVelocity:t}])}applyImpulse(e,t,i){return this.editBodies([{body:e,impulse:t,worldPoint:i}])}applyForce(e,t,i){return this.editBodies([{body:e,force:t,worldPoint:i}])}applyAngularImpulse(e,t){return this.editBodies([{body:e,angularImpulse:t}])}applyTorque(e,t){return this.editBodies([{body:e,torque:t}])}teleport(e,t,i){return this.editBodies([{body:e,position:t,rotation:i}])}setPackedVelocities(e,t){let i=Array.from(e),r=t?.linear,o=t?.angular,n=this.dimension,a=n===3?3:1;if(r===void 0&&o===void 0)throw Error("Provide linear or angular velocities");let l=-1;i.forEach(d=>{if(this.liveIndex(d),d<=l)throw Error("Velocity batch indices must be unique and ascending GPU slots");l=d});for(let[d,p]of[[r,n],[o,a]])if(d!==void 0&&(d.length!==i.length*p||!Array.from(d).every(m=>Number.isFinite(Math.fround(m)))))throw Error("Provide finite packed velocity components per body");let c=r===void 0?null:Array.from(r),u=o===void 0?null:Array.from(o),f=i.map((d,p)=>({body:d,...c===null?{}:{linearVelocity:c.slice(p*n,(p+1)*n)},...u===null?{}:{angularVelocity:n===3?u.slice(p*a,(p+1)*a):u[p]}}));return this.editBodies(f),this.flushEdits(),this}flushEdits(){if(this.destroyed)throw Error("The solver has been destroyed");let{words:e,u32:t,count:i}=this.pending,r=this.solver,o=this.dimension;for(let n=0;n<i;n++){let a=n*12,l=t[a],c=t[a+1],u=t[a+2]!==0,f=o===3?r.bodies[l]:null,d=f?.velocityLin??r.velocity.subarray(l*4,l*4+2),p=f?.mass??r.shape[l*4+2],m=f?.positionLin??r.pose.subarray(l*4,l*4+2),h=g=>{if(!(p<=0))if(o===2)r.velocity[l*4+2]+=g[0]/Math.max(1e-10,r.shape[l*4+3]);else{let b=f.positionAng,y=Ee([-b[0],-b[1],-b[2],b[3]],g),A=Ee(b,y.map((v,x)=>v/Math.max(1e-10,f.moment[x])));for(let v=0;v<3;v++)f.velocityAng[v]+=A[v]}};if(c===0)d.set(e.subarray(a+4,a+4+o));else if(c===1)f?f.velocityAng.set(e.subarray(a+4,a+7)):r.velocity[l*4+2]=e[a+4];else if(c===4)if(m.set(e.subarray(a+4,a+4+o)),f){u&&f.positionAng.set(e.subarray(a+8,a+12)),f.initialLin.set(m),f.inertialLin.set(m),f.initialAng.set(f.positionAng),f.inertialAng.set(f.positionAng);for(let g of[...f.forces])g.contacts&&g.destroy()}else u&&(r.pose[l*4+2]=e[a+6]),r.initial.set(r.pose.subarray(l*4,l*4+4),l*4),r.inertial.set(r.pose.subarray(l*4,l*4+4),l*4),r.prevCount=0;else if(c===2||c===3){if(p<=0)continue;let g=Array.from(e.subarray(a+4,a+4+o),b=>b*(c===3?e[a+7]:1));for(let b=0;b<o;b++)d[b]+=g[b]/p;if(u){let b=Array.from(e.subarray(a+8,a+8+o),(y,A)=>y-m[A]);h(o===3?xt(b,g):[b[0]*g[1]-b[1]*g[0]])}}else(c===5||c===6)&&h(Array.from(e.subarray(a+4,a+4+(o===3?3:1)),g=>g*(c===6?e[a+7]:1)))}this.pending.count=0}step(){this.flushEdits(),this.solver.step(),this.sleeping?.after(),this.stepCount++,this.captureEvents()}attachToScene(e,t){return Yo.call(this,e,t)}detachFromScene(){return Qo.call(this)}setFilters(e,t,i){let r=Array.from(e),o=Array.from(t),n=Array.from(i);if(o.length!==r.length||n.length!==r.length)throw Error("Provide one group and mask per body");r.forEach(a=>this.liveIndex(a));for(let a of[...o,...n])if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");r.forEach((a,l)=>{this.dimension===3?Object.assign(this.solver.bodies[a],{group:o[l],collidesWith:n[l]}):this.solver.filters.set(a,[o[l],n[l]])}),this.wakeAll()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Trigger flag must be boolean");this.dimension===3?this.solver.bodies[e].isTrigger=t:t?this.solver.sensors.add(e):this.solver.sensors.delete(e),this.wakeAll()}setRestitution(e,t){if(this.liveIndex(e),!Number.isFinite(t)||t<0||t>1)throw Error("restitution must be between 0 and 1");this.dimension===3?this.solver.bodies[e].restitution=t:this.solver.restitution.set(e,t),this.wakeAll()}setMotor(e,t){if(this.assertLive(),this.dimension===3){let i=this.constraints[e];if(!i?.cpuAngular)throw Error("A motor constraint is required");Object.assign(i.cpuAngular.options,kt({...i.cpuAngular.options,...t}))}else{let i=this.solver,r=e*O;if(!Number.isInteger(e)||e<0||e>=i.jointCount||i.info[e*4]!==3)throw Error("A motor constraint is required");let o=t.speed??i.data[r+re],n=t.maxTorque??i.data[r+Ae];if(!Number.isFinite(o)||!Number.isFinite(n)||n<0)throw Error("Provide finite motor speed and nonnegative torque");i.data[r+re]=o,i.data[r+_e]=-n,i.data[r+Ae]=n}this.wakeAll()}setAngleLimits(e,t){if(this.assertLive(),this.dimension!==2)throw Error("Use a 3D hinge constraint");if(this.solver.info[e*4]!==5)throw Error("An angular limit is required");Bs(this.solver,e,ki(t)),this.wakeAll()}releaseJoints(e){for(let t of e)this.disableConstraint(t)}disableConstraint(e){if(this.assertLive(),!Number.isInteger(e)||e<0||e>=this.jointCount)throw Error("A live constraint slot is required");if(this.dimension===3){let t=this.constraints[e];t.broken=!0,(t.cpuAngular??t).destroy()}else this.solver.disable(e),this.solver.info[e*4]=0,this.solver.noCollideDirty=!0;this.wakeAll()}setWorldAnchor(e,t,i){if(this.assertLive(),this.dimension===2){let r=this.solver;if(!Number.isInteger(e)||e<0||e>=r.jointCount||r.info[e*4+1]!==-1)throw Error("A world-anchor constraint is required");r.data.set(me([t,i],2,"point"),e*O+21)}else{let r=this.constraints[e];if(!r||r.bodyA)throw Error("A world-anchor constraint is required");r.rA.set(me(t,3,"point"))}this.wakeAll()}addBox(...e){return this.addDynamic("addBox",e)}addCircle(...e){return this.addDynamic("addCircle",e)}addCapsule(...e){return this.addDynamic("addCapsule",e)}addHull(...e){return this.addDynamic("addHull",e)}addSegment(...e){return this.addDynamic("addSegment",e)}addPlane(...e){return this.addDynamic("addPlane",e)}addDynamic(e,t){if(this.destroyed)throw Error("The solver has been destroyed");if(this.dimension!==2)throw Error("Build 3D bodies before creating the solver");this.wakeAll();let i=this.sleeping?{speedThreshold:this.sleeping.speedThreshold,timeThreshold:this.sleeping.timeThreshold}:null,r=this.builder[e](...t);return i&&this.enableSleeping(i),r}appendJoint(e,t,i,r,o,n=1/0){if(this.assertLive(),this.dimension!==2)throw Error("Build 3D constraints before creating the solver");return this.wakeAll(),this.builder.addJoint(e,t,i,r,{stiffness:o,breakForce:n}).slot}captureConstraintFrames(e){if(this.assertLive(),this.dimension!==3)throw Error("Constraint frame capture requires a 3D world");let t=Array.from(e);for(let{slot:i,spring:r}of t){let o=this.constraints[i];if(!Number.isInteger(i)||!o||o.broken||o.cpuAngular||(r?o.rest===void 0:o.stiffnessAng===void 0))throw Error("A live spring or joint slot is required")}this.flushEdits();for(let{slot:i,spring:r}of t)qa(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");xr(i,It(t)),this.wakeAll()}packBodyRecords(e,t=!1){let i=this.solver,r=this.dimension,o=t?r===3?8:4:r===3?40:24,n=e?.length??this.bodyCount,a=new Float32Array(n*o);for(let l=0;l<n;l++){let c=e?.[l]??l,u=l*o;if(t){if(r===3){let f=i.bodies[c];a.set(f.positionLin,u),a[u+3]=f.friction,a.set(f.positionAng,u+4)}else a.set(i.pose.subarray(c*4,c*4+4),u),a[u+3]=i.props[c*4];continue}if(r===3){let f=i.bodies[c];a.set(f.positionLin,u),a[u+3]=f.friction,a.set(f.positionAng,u+4),a.set(f.initialLin,u+8),a[u+11]=+!!f.isTrigger,a.set(f.initialAng,u+12),a.set(f.size,u+16),a[u+19]=f.sleeping?0:f.mass,a.set(f.moment,u+20),a[u+23]=f.radius,a.set(f.inertialLin,u+24),a.set(f.inertialAng,u+28),a.set(f.velocityLin,u+32),a[u+35]=f.prevVelocityLin.reduce((d,p,m)=>d+p*i.up[m],0),a.set(f.velocityAng,u+36),a[u+39]=De(f)?1:Fe(f)?-1:Ct(f)?3:nr(f)?2:0}else a.set(i.pose.subarray(c*4,c*4+4),u),a[u+3]=i.props[c*4],a.set(i.initial.subarray(c*4,c*4+4),u+4),a.set(i.inertial.subarray(c*4,c*4+4),u+8),a.set(i.velocity.subarray(c*4,c*4+4),u+12),a.set(i.prevVelocity.subarray(c*4,c*4+4),u+16),a.set(i.shape.subarray(c*4,c*4+4),u+20)}return a}async readBodies(){return this.flushEdits(),this.packBodyRecords()}async readSelectedBodies(e,{posesOnly:t=!1}={}){let i=Array.from(e);return i.forEach(r=>this.liveIndex(r)),this.flushEdits(),this.packBodyRecords(i,t)}async readBodyState(e){this.liveIndex(e),this.flushEdits();let t=this.solver;if(this.dimension===3){let i=t.bodies[e],r=this.sleeping?.asleep.has(e)??!1;return{position:Array.from(i.positionLin),rotation:Array.from(i.positionAng),linearVelocity:Array.from(i.velocityLin),angularVelocity:Array.from(i.velocityAng),mass:i.mass,effectiveMass:r?0:i.mass,sleeping:r}}return{position:Array.from(t.pose.subarray(e*4,e*4+2)),angle:t.pose[e*4+2],linearVelocity:Array.from(t.velocity.subarray(e*4,e*4+2)),angularVelocity:t.velocity[e*4+2],mass:t.shape[e*4+2],moment:t.shape[e*4+3],sleeping:this.sleeping?.asleep.has(e)??!1}}async readJoints(){return this.flushEdits(),this.packJointRecords()}packJointRecords(){let e=this.dimension===3?32:36,t=new Float32Array(this.jointCount*e),i=r=>r===1/0?3e38:r===-1/0?-3e38:r;if(this.dimension===2){let r=this.solver.data;for(let o=0;o<this.jointCount;o++){let n=o*36,a=o*O;[se,te,Q,_e,Ae,Pt,fe].forEach((l,c)=>{for(let u=0;u<3;u++)t[n+c*4+u]=i(r[a+l+u])}),t.set([r[a+N],r[a+N+1],r[a+Y],r[a+Y+1]],n+28),t.set([r[a+re],r[a+re+1],r[a+re+2],0],n+32)}}else this.constraints.forEach((r,o)=>{let n=o*32;if(r.cpuAngular){let a=r.cpuAngular,l=a.options;t.set(a.pen,n),t[n+3]=r.broken?0:1,t.set(a.lam,n+8),t.set(a.ta,n+16),t.set(a.tb,n+20),t.set(l.axisA,n+24),t.set(l.axisB,n+28),t[n+19]=a.c0?.[0]??0,t[n+23]=a.c0?.[1]??0,t[n+27]=l.type==="motor"?l.speed:l.minAngle,t[n+31]=l.type==="motor"?l.maxTorque:l.maxAngle;return}for(let[a,l]of[["penaltyLin",0],["penaltyAng",4],["lambdaLin",8],["lambdaAng",12],["C0Lin",16],["C0Ang",20],["rA",24],["rB",28]])r[a]&&t.set(r[a],n+l);if(t[n+3]=r.broken?0:i(r.stiffnessLin??r.stiffness??0),t[n+7]=r.broken?0:i(r.stiffnessAng??0),t[n+11]=i(r.fracture??0),t[n+15]=r.torqueArm??0,t[n+27]=r.rest??0,r.cpuRest&&(t[n+19]=r.cpuRest[0],t[n+23]=r.cpuRest[1],t[n+27]=r.cpuRest[2],t[n+31]=r.cpuRest[3]),r.rest!==void 0){let a=Ui(r);t[n+11]=Number.isFinite(a?.breakStrain)?a.breakStrain:0,t[n+15]=Number.isFinite(a?.breakForce)?a.breakForce:0}});return t}async readCounters(){this.assertLive();let e=this.dimension===2?this.solver.contactCount:this.solver.forces.reduce((i,r)=>i+(r.contacts?.length??0),0),t=this.dimension===2?this.solver.contactPairs.length:this.solver.forces.filter(i=>i.contacts?.length).length;return{pairs:t,contacts:e,manifolds:t,joints:this.jointCount,overflow:0,clashes:0,colors:0,sleeping:this.sleeping?.asleep.size??0}}adapt(){}watchContacts({indices:e,maxPairs:t=this.maxPairs??8192,maxEvents:i=this.maxEvents??4096}={}){if(this.assertLive(),!Number.isInteger(i)||i<1||!Number.isInteger(t)||t<1)throw Error("Contact capacities must be positive integers");return e!==void 0?(e.forEach(r=>this.liveIndex(r)),this.watched=new Set(e)):this.watched??=new Set(Array.from({length:this.bodyCount},(r,o)=>o)),this.maxEvents=i,this.maxPairs=t,this}captureEvents(){if(!this.watched)return;let e=this.dimension===3?new Map(this.solver.bodies.map((i,r)=>[i,r])):null,t=this.dimension===2?this.solver.contactPairs:this.solver.forces.filter(i=>i.contacts?.length).map(i=>{let r=Array.from(i.bodyA.positionLin);for(let o of i.contacts){let n=Ee(i.bodyA.positionAng,o.rA);for(let a=0;a<3;a++)r[a]+=n[a]/i.contacts.length}return{a:e.get(i.bodyA),b:e.get(i.bodyB),point:r,normal:Array.from(i.basis.slice(0,3)),impulse:i.bodyA.isTrigger||i.bodyB.isTrigger?0:i.contacts.reduce((o,n)=>o+Math.abs(n.lambda[0])*this.params.dt,0)}});this.trackContactPairs(t)}trackContactPairs(e){let t=new Map,i=(n,a)=>{this.events.length<(this.maxEvents??4096)?this.events.push({...n,normal:n.normal??[...n.n,0],type:a,kind:a==="begin"?1:2,step:this.stepCount,isTrigger:n.isTrigger??(this.dimension===3?!!(this.solver.bodies[n.a].isTrigger||this.solver.bodies[n.b].isTrigger):this.solver.sensors.has(n.a)||this.solver.sensors.has(n.b))}):this.dropped=(this.dropped??0)+1},r=new Set,o=[];for(let n of e){if(!this.watched.has(n.a)&&!this.watched.has(n.b))continue;let a=`${Math.max(n.a,n.b)},${Math.min(n.a,n.b)}`;r.add(a),this.previous.has(a)&&t.size<this.maxPairs?t.set(a,n):o.push([a,n])}for(let[n,a]of o)if(!t.has(n)){if(t.size>=this.maxPairs){this.pairFull=!0;continue}t.set(n,a),this.previous.has(n)||i(a,"begin")}for(let[n,a]of this.previous)r.has(n)||i({...a,impulse:0},"end");this.previous=t}async readContactEvents(){this.watchContacts();let e={events:this.events,dropped:this.dropped??0,full:!!this.dropped||!!this.pairFull};return this.events=[],this.dropped=0,this.pairFull=!1,e}async raycast(e,t,i){return this.sphereCast(e,0,t,i)}async circleCast(e,t,i,r){return this.sphereCast(e,t,i,r)}async sphereCast(e,t,i,r){return this.flushEdits(),pu(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 mn(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 mu=new WeakSet,bn=s=>s!==null&&typeof s=="object"&&mu.has(s),gu=s=>mu.add(s);async function bu({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 Ni(e):({device:e,adapter:o}=await Sr({...r,preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024,...r.preferredLimits},validate3D:i}),n=!0));let l=new Set,c={backend:s,device:e,adapter:o,createSolver(u,f={}){if(a)throw Error("The solver pipeline has been destroyed");if(!u||typeof u.assertEditable!="function"||!u.ref&&!u.topology)throw Error("An AVBD scene builder is required");u.assertEditable();let d;if(s==="webgpu")d=u.createSolver(e,f);else{let m=u.ref?3:u.topology?2:0;if(f.bodyBuffer!==void 0)throw Error("A GPU body buffer requires the WebGPU backend");d=new gn(u,m,f),u.gpu=d,u.created=!0}Object.defineProperty(d,"backend",{value:s,configurable:!0}),l.add(d);let p=d.destroy;return d.destroy=function(){p.call(this),l.delete(this)},d},destroy(){if(!a){for(let u of l)u.destroy();l.clear(),n&&e.destroy(),a=!0}}};return gu(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},yu=s=>{if(!Number.isInteger(s)||s<0||s>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return s};function yn(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");yu(s.group??4294967295),yu(s.collidesWith??4294967295)}function vu(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 vn=class{constructor(e={}){if(this.ref=new ai,vu(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(),yn(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(),yn(t),this.configure(ar(this.ref,K(e,"radius"),K(t.density??1,"density",!0),K(t.friction??.6,"friction",!0),ie(t.position??[0,0,0],3,"position")),t)}addHull(e,t={}){if(this.assertEditable(),yn(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=Xr(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(lr(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(),yn(i),this.configure(Cr(this.ref,e,t,K(i.density??1,"density",!0),K(i.friction??.6,"friction",!0),ie(i.position??[0,0,0],3,"position")),i)}addHeightfield(e){return this.addTerrain(Mr(e),e)}addStaticMesh(e){return this.addTerrain(zr(e),e)}addTerrain(e,t={}){this.assertEditable();let i=this.ref.bodies.length,r=vo(this.ref,e,t);r.forEach(n=>this.bodySet.add(n)),this.hulls=!0;let o=!1;return{bodies:r,triangleCount:e.triangleCount,kind:e.kind,dispose:()=>{if(!o&&(o=!0,r.forEach(n=>{n.group=0,n.collidesWith=0}),this.gpu&&!this.gpu.destroyed)){let n=r.map((a,l)=>this.gpu.gpuIndex(i+l));this.gpu.setFilters(n,n.map(()=>0),n.map(()=>0)),this.gpu.wakeAll?.()}}}}configure(e,t){if(t.allowSleep!==void 0&&typeof t.allowSleep!="boolean")throw Error("allowSleep must be a boolean");e.allowSleep=t.allowSleep??!0,this.sleepOptIn||=t.allowSleep===!0;let i=t.restitution??0;if(!Number.isFinite(i)||i<0||i>1)throw Error("restitution must be between 0 and 1");if(e.restitution=i,t.isTrigger!==void 0&&typeof t.isTrigger!="boolean")throw Error("isTrigger must be a boolean");if(e.isTrigger=t.isTrigger??!1,e.group=t.group??4294967295,e.collidesWith=t.collidesWith??4294967295,t.mass!==void 0){let r=K(t.mass,"mass",!0);if(e.mass===0&&r>0)throw Error("A positive mass needs positive density");let o=e.mass>0?r/e.mass:0;e.mass=r,e.moment.forEach((n,a)=>e.moment[a]=n*o)}if(t.rotation){let r=ie(t.rotation,4,"rotation"),o=Math.hypot(...r);if(!o)throw Error("rotation must be nonzero");e.positionAng.set(r.map(n=>n/o))}return t.velocity&&e.velocityLin.set(ie(t.velocity,3,"velocity")),t.angularVelocity&&e.velocityAng.set(ie(t.angularVelocity,3,"angularVelocity")),this.bodySet.add(e),e}addPoint(e,{mass:t=.02,radius:i=.05,friction:r=.5}={}){let o=this.addSphere(i,{position:e,mass:t,friction:r});return o.moment.fill(0),this.points=!0,o}addFabric({columns:e=16,rows:t=16,spacing:i=.2,origin:r=[0,3,0],mass:o=.02,stiffness:n=1e3,anchorStiffness:a=n,materialAt:l,breakStrain:c=1/0,breakForce:u=1/0,bendingStiffness:f=Number.isFinite(c)||Number.isFinite(u)?0:n/50,pinCorners:d=!1,pinEdges:p=!d}={}){for(let[E,k]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(k)||k<2)throw Error(`${E} must be an integer >= 2`);if(K(i,"spacing"),K(o,"mass"),K(n,"stiffness"),K(a,"anchorStiffness"),l!==void 0&&typeof l!="function")throw Error("materialAt must be a function");if(K(f,"bendingStiffness",!0),typeof d!="boolean"||typeof p!="boolean")throw Error("Fabric pin options must be boolean");let m=It({breakStrain:c,breakForce:u}),h=ie(r,3,"origin"),g=(E,k)=>`${Math.min(E,k)},${Math.max(E,k)}`;function*b(){for(let E=0;E<t;E++)for(let k=0;k<e;k++){let P=E*e+k;k&&(yield[P,P-1,n]),E&&(yield[P,P-e,n]),k&&E&&(yield[P,P-e-1,n/2],yield[P-1,P-e,n/2]),k>1&&f&&(yield[P,P-2,f]),E>1&&f&&(yield[P,P-2*e,f])}}let y=new Map;if(l)for(let[E,k]of b()){let P=l((E%e+k%e)/(2*(e-1)),(Math.floor(E/e)+Math.floor(k/e))/(2*(t-1)))??{};if(typeof P!="object"||Array.isArray(P))throw Error("materialAt must return a material object");y.set(g(E,k),{scale:K(P.stiffnessScale??1,"materialAt.stiffnessScale"),material:It({...m,...P})})}let A=Array.from({length:t},(E,k)=>Array.from({length:e},(P,C)=>this.addPoint([h[0]+(C-(e-1)/2)*i,h[1],h[2]+(k-(t-1)/2)*i],{mass:p&&(C===0||k===0||C===e-1||k===t-1)||d&&(C===0||C===e-1)&&(k===0||k===t-1)?0:o,radius:i/Math.SQRT2}))),v=A.flat(),x=new Map(v.map((E,k)=>[E,k])),w=[],B=new Map,L=(E,k,P)=>{(E.mass===0||k.mass===0)&&(P*=a/n);let C=x.get(E),_=x.get(k),z=y.get(g(C,_)),R=this.addSpring(E,k,[0,0,0],[0,0,0],{stiffness:P*(z?.scale??1),...z?.material??m});return B.set(g(x.get(E),x.get(k)),w.length),w.push(R),R};for(let[E,k,P]of b())L(v[E],v[k],P);let M=[];for(let E=0;E<t;E++)for(let k=0;k<e;k++)if(k&&E){let P=(E-1)*e+k-1,C=P+1,_=E*e+k-1,z=_+1;M.push(P,_,C,C,_,z)}let I=[];for(let E=0;E<M.length;E+=3){let[k,P,C]=M.slice(E,E+3);I.push(B.get(g(k,P)),B.get(g(P,C)),B.get(g(C,k)))}return{points:v,grid:A,indices:Uint32Array.from(M),connections:w,triangleConnections:Uint32Array.from(I)}}addRope({segments:e=12,length:t=12,origin:i=[0,12,0],width:r=.15,endMass:o=20}={}){if(!Number.isInteger(e)||e<1)throw Error("segments must be a positive integer");K(t,"length"),K(r,"width"),K(o,"endMass",!0);let n=ie(i,3,"origin"),a=t/e,l=this.addBox([r,r,r],{mass:0,position:n}),c=[];for(let u=0;u<e;u++){let f=this.addBox([r,a,r],{mass:1,position:[n[0],n[1]-(u+.5)*a,n[2]]});this.addJoint(u?c[u-1]:l,f,[0,u?-a/2:0,0],[0,a/2,0]),c.push(f)}if(o){let u=this.addBox([.6,.6,.6],{mass:o,position:[n[0],n[1]-t-.3,n[2]]});this.addJoint(c.at(-1),u,[0,-a/2,0],[0,.3,0]),c.push(u)}return{anchor:l,links:c}}addRagdoll({origin:e=[0,2,0],mass:t=10}={}){let i=ie(e,3,"origin");K(t,"mass");let r=(l,c)=>this.addBox(l,{mass:t/10,position:c.map((u,f)=>u+i[f])}),o=r([.7,1.2,.35],[0,2,0]),n=this.addSphere(.25,{mass:t/10,position:[i[0],i[1]+2.9,i[2]]}),a=[o,n];this.addJoint(o,n,[0,.6,0],[0,-.3,0]);for(let l of[-1,1]){let c=r([.3,.8,.3],[l*.5,1.95,0]),u=r([.26,.7,.26],[l*.5,1.2,0]);this.addJoint(o,c,[l*.35,.35,0],[-l*.15,.4,0]),this.addJoint(c,u,[0,-.4,0],[0,.35,0]);let f=r([.28,.8,.3],[l*.2,1,0]),d=r([.26,.75,.28],[l*.2,.225,0]);this.addJoint(o,f,[l*.2,-.6,0],[0,.4,0]),this.addJoint(f,d,[0,-.4,0],[0,.375,0]),a.push(c,u,f,d)}return{torso:o,head:n,bodies:a}}addJoint(e,t,i=[0,0,0],r=[0,0,0],{linearStiffness:o=1/0,angularStiffness:n=0,breakForce:a=1/0,breakOnPull:l=!1}={}){this.assertEditable(),this.checkBodies(e,t);for(let c of[o,n,a])c!==1/0&&K(c,"joint stiffness/breakForce",!0);return this.handle(new ke(this.ref,e,t,ie(i,3,"anchorA"),ie(r,3,"anchorB"),o,n,l&&a!==1/0?-a:a))}addSpring(e,t,i=[0,0,0],r=[0,0,0],{stiffness:o=1e3,rest:n=-1,breakStrain:a=1/0,breakForce:l=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!e)throw Error("A spring requires two bodies");K(o,"stiffness",!0),n!==-1&&K(n,"rest",!0);let c=It({breakStrain:a,breakForce:l}),u=new dt(this.ref,e,t,ie(i,3,"anchorA"),ie(r,3,"anchorB"),o,n);return xr(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 Br(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);Br(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(bn(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 Zo(e,this.ref,go(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:Jc}}:{}})),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}},xn=class{constructor(e={}){if(this.topology=new Dr,Object.assign(this.topology.params,ko()),vu(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 Fr(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(qo(e),t)}addHull(e,t={}){return this.addShape(No(e),t)}addCapsule(e,t,i={}){return this.addShape(Vo(e,t),i)}addSegment(e,t,i={}){return this.addShape(Wo(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Ho(e,t,i))}addBox(e,{density:t=1,friction:i=.6,position:r=[0,0],angle:o=0,velocity:n=[0,0,0],restitution:a=0,isTrigger:l=!1,group:c=4294967295,collidesWith:u=4294967295,allowSleep:f}={}){if(this.assertEditable(),f!==void 0&&typeof f!="boolean")throw Error("allowSleep must be boolean");let d=ie(e,2,"size");d.forEach(h=>K(h,"size"));let p=ie(r,2,"position");if(!Number.isFinite(o))throw Error("angle must be finite");if(!Number.isFinite(a)||a<0||a>1)throw Error("restitution must be between 0 and 1");if(typeof l!="boolean")throw Error("isTrigger must be boolean");for(let h of[c,u])if(!Number.isInteger(h)||h<0||h>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let m=this.topology.addBody(d,K(t,"density",!0),K(i,"friction",!0),[...p,o],ie(n,3,"velocity"));return this.restitution??=new Map,a>0&&this.restitution.set(m,a),this.sensors??=new Set,l&&this.sensors.add(m),this.filters??=new Map,(c!==4294967295||u!==4294967295)&&this.filters.set(m,[c,u]),this.topology.avbdSleepEligible??=new Map,f!==void 0&&this.topology.avbdSleepEligible.set(m,f),m}checkBodies(e,t){if(!Number.isInteger(e)||!Number.isInteger(t)||e<-1||e>=this.bodyCount||t<0||t>=this.bodyCount||e===t)throw Error("Invalid 2D constraint endpoints")}addJoint(e,t,i=[0,0],r=[0,0],{stiffness:o=[1/0,1/0,0],breakForce:n=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!o||o.length!==3||Array.from(o).some(l=>l!==1/0&&(!Number.isFinite(l)||l<0))||n!==1/0&&(!Number.isFinite(n)||n<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");let a=this.topology.addJoint(e,t,ie(i,2,"anchorA"),ie(r,2,"anchorB"),o,n);return this.constraintHandle(a.slot,a)}constraintHandle(e,t={}){return Object.assign(t,{slot:e,alive:!0,disposed:!1}),t.dispose=()=>{t.disposed||(this.gpu&&!this.gpu.destroyed?this.gpu.disableConstraint(e):this.gpu||this.topology.data.fill(0,e*O+Q,e*O+Q+3),t.alive=!1,t.disposed=!0)},t.destroy=t.dispose,this.topology.handles[e]=t,t}addSpring(e,t,i=[0,0],r=[0,0],{stiffness:o=1e3,rest:n=1}={}){if(this.assertEditable(),this.checkBodies(e,t),e<0)throw Error("A spring requires two bodies");let a=this.topology.jointCount;return this.topology.addSpring(e,t,ie(i,2,"anchorA"),ie(r,2,"anchorB"),K(o,"stiffness",!0),K(n,"rest",!0)),this.constraintHandle(a)}addMotor(e,t,{speed:i=1,maxTorque:r=100}={}){if(this.assertEditable(),this.checkBodies(e,t),!Number.isFinite(i))throw Error("speed must be finite");let o=this.topology.jointCount;return this.topology.addMotor(e,t,i,K(r,"maxTorque",!0)),this.constraintHandle(o)}addAngularLimit(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=ki(i),o=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);Bs(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):Bs(this.topology,n.slot,ki(a)),n},n}addHinge(e,t,i=[0,0],r=[0,0],o={}){this.assertEditable(),this.checkBodies(e,t);let n=ie(i,2,"anchorA"),a=ie(r,2,"anchorB"),l=o.minAngle!==void 0||o.maxAngle!==void 0?ki(o):null,c=o.motor;if(c){if(!Number.isFinite(c.speed??1))throw Error("speed must be finite");K(c.maxTorque??100,"maxTorque",!0)}let u=this.addJoint(e,t,n,a),f=l?this.addAngularLimit(e,t,l):null,d=c?this.addMotor(e,t,c):null;return{joint:u,limits:f,motor:d,disposed:!1,dispose(){this.disposed||(this.joint.dispose(),this.limits?.dispose(),this.motor?.dispose(),this.disposed=!0)},destroy(){this.dispose()}}}createSolver(e,t={}){if(bn(e))return e.createSolver(this,t);this.assertEditable();let i=new Ko(e,this.topology,t);for(let[r,o]of this.restitution??[])i.setRestitution(r,o);for(let r of this.sensors??[])i.setSensor(r,!0);for(let[r,o]of this.filters??[])i.setFilters([r],[o[0]],[o[1]]);return this.gpu=i,this.created=!0,i}};var Wr=class{constructor(e){if(this.device=e,this.latestMs=null,this.samples=0,this.slots=[],!!e.features.has("timestamp-query"))for(let t=0;t<3;t++)this.slots.push({query:e.createQuerySet({type:"timestamp",count:2}),resolve:e.createBuffer({size:256,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:16,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ}),busy:!1})}begin(){let e=this.slots.find(t=>!t.busy);return!e||this.disposed?null:(e.busy=!0,e)}descriptor(e){return e?{timestampWrites:{querySet:e.query,beginningOfPassWriteIndex:0,endOfPassWriteIndex:1}}:{}}encode(e,t){t&&(e.resolveQuerySet(t.query,0,2,t.resolve,0),e.copyBufferToBuffer(t.resolve,0,t.read,0,16))}resolve(e){if(e)return e.pending=e.read.mapAsync(GPUMapMode.READ).then(()=>{let t=new BigUint64Array(e.read.getMappedRange());this.latestMs=Number(t[1]-t[0])/1e6,this.samples++,e.read.unmap()}).catch(()=>{}).finally(()=>{e.busy=!1,e.pending=null}),e.pending}settled(){return Promise.all(this.slots.map(e=>e.pending))}destroy(){this.disposed=!0;for(let e of this.slots)e.query.destroy(),e.resolve.destroy(),e.read.destroy()}};var xu=(s,e)=>Math.ceil(s/e)*e,Au=s=>String(s>>>0),Hr=class{constructor(e,t={}){this.device=e,this.maxFreeBytes=t.maxFreeBytes??201326592,this.freeBytes=0,this.free=new Map,this.all=new Set}acquire(e,t,i="recycled-buffer"){let r=xu(Math.max(4,e),4),o=Au(t),n=this.free.get(o);if(n){let l=-1,c=1/0;for(let u=0;u<n.length;u+=1){let f=n[u];f.size>=r&&f.size<c&&(l=u,c=f.size)}if(l>=0){let[u]=n.splice(l,1);return this.freeBytes-=u.size,u.buffer}}let a=this.device.createBuffer({label:i,size:r,usage:t,mappedAtCreation:!1});return this.all.add(a),a}release(e,t,i){if(!e)return;let r=xu(Math.max(4,t),4);if(this.freeBytes+r>this.maxFreeBytes){e.destroy(),this.all.delete(e);return}let o=Au(i),n=this.free.get(o);n||(n=[],this.free.set(o,n)),n.push({buffer:e,size:r,usage:i}),this.freeBytes+=r}trim(e=this.maxFreeBytes){this.maxFreeBytes=e;for(let[t,i]of this.free.entries()){for(;this.freeBytes>this.maxFreeBytes&&i.length>0;){let r=i.pop();r.buffer.destroy(),this.all.delete(r.buffer),this.freeBytes-=r.size}i.length===0&&this.free.delete(t)}}destroy(){for(let e of this.all)e.destroy();this.free.clear(),this.all.clear(),this.freeBytes=0}};var wu=`const EPS : f32 = 1.0e-7;
|
|
5102
|
+
|
|
5103
|
+
struct Bullet {
|
|
5104
|
+
posLife: vec4<f32>,
|
|
5105
|
+
velRadius: vec4<f32>,
|
|
5106
|
+
seed: vec4<u32>,
|
|
5107
|
+
}
|
|
5108
|
+
|
|
5109
|
+
struct Triangle {
|
|
5110
|
+
a: vec4<f32>,
|
|
5111
|
+
b: vec4<f32>,
|
|
5112
|
+
c: vec4<f32>,
|
|
5113
|
+
}
|
|
5114
|
+
|
|
5115
|
+
struct Counters {
|
|
5116
|
+
hits: atomic<u32>,
|
|
5117
|
+
alive: atomic<u32>,
|
|
5118
|
+
_pad0: atomic<u32>,
|
|
5119
|
+
_pad1: atomic<u32>,
|
|
5120
|
+
}
|
|
5121
|
+
|
|
5122
|
+
struct Params {
|
|
5123
|
+
dt: f32,
|
|
5124
|
+
speed: f32,
|
|
5125
|
+
life: f32,
|
|
5126
|
+
bulletRadius: f32,
|
|
5127
|
+
|
|
5128
|
+
spawnRadius: f32,
|
|
5129
|
+
jitter: f32,
|
|
5130
|
+
targetCount: u32,
|
|
5131
|
+
triCount: u32,
|
|
5132
|
+
|
|
5133
|
+
frame: u32,
|
|
5134
|
+
dispatchWidth: u32,
|
|
5135
|
+
_pad1: u32,
|
|
5136
|
+
_pad2: u32,
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5139
|
+
@group(0) @binding(0) var<storage, read_write> bullets : array<Bullet>;
|
|
5140
|
+
@group(0) @binding(1) var<storage, read> triangles : array<Triangle>;
|
|
5141
|
+
@group(0) @binding(2) var<storage, read_write> counters : Counters;
|
|
5142
|
+
@group(0) @binding(3) var<uniform> params : Params;
|
|
5143
|
+
|
|
5144
|
+
fn hashU32(xIn: u32) -> u32 {
|
|
5145
|
+
var x = xIn;
|
|
5146
|
+
x ^= x >> 16u;
|
|
5147
|
+
x *= 0x7feb352du;
|
|
5148
|
+
x ^= x >> 15u;
|
|
5149
|
+
x *= 0x846ca68bu;
|
|
5150
|
+
x ^= x >> 16u;
|
|
5151
|
+
return x;
|
|
5152
|
+
}
|
|
5153
|
+
|
|
5154
|
+
fn rand01(state: ptr<function, u32>) -> f32 {
|
|
5155
|
+
let h = hashU32((*state) + 0x9e3779b9u);
|
|
5156
|
+
*state = h;
|
|
5157
|
+
return f32(h) * 2.3283064365386963e-10;
|
|
5158
|
+
}
|
|
5159
|
+
|
|
5160
|
+
fn randSigned(state: ptr<function, u32>) -> f32 {
|
|
5161
|
+
return rand01(state) * 2.0 - 1.0;
|
|
5162
|
+
}
|
|
5163
|
+
|
|
5164
|
+
fn randomUnit(state: ptr<function, u32>) -> vec3<f32> {
|
|
5165
|
+
var v = vec3<f32>(1.0, 0.0, 0.0);
|
|
5166
|
+
var ok = false;
|
|
5167
|
+
for (var i = 0u; i < 12u; i += 1u) {
|
|
5168
|
+
let p = vec3<f32>(randSigned(state), randSigned(state), randSigned(state));
|
|
5169
|
+
let l2 = dot(p, p);
|
|
5170
|
+
if (!ok && l2 > 1.0e-5 && l2 <= 1.0) {
|
|
5171
|
+
v = p * inverseSqrt(l2);
|
|
5172
|
+
ok = true;
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
return v;
|
|
5176
|
+
}
|
|
5177
|
+
|
|
5178
|
+
fn safeNormal(v: vec3<f32>) -> vec3<f32> {
|
|
5179
|
+
let l2 = dot(v, v);
|
|
5180
|
+
if (l2 > EPS) {
|
|
5181
|
+
return v * inverseSqrt(l2);
|
|
5182
|
+
}
|
|
5183
|
+
return vec3<f32>(1.0, 0.0, 0.0);
|
|
5184
|
+
}
|
|
5185
|
+
|
|
5186
|
+
fn makeBasis(dir: vec3<f32>) -> mat3x3<f32> {
|
|
5187
|
+
var helper = vec3<f32>(0.0, 1.0, 0.0);
|
|
5188
|
+
if (abs(dir.y) > 0.85) {
|
|
5189
|
+
helper = vec3<f32>(1.0, 0.0, 0.0);
|
|
5190
|
+
}
|
|
5191
|
+
let u = safeNormal(cross(helper, dir));
|
|
5192
|
+
let v = safeNormal(cross(dir, u));
|
|
5193
|
+
return mat3x3<f32>(u, v, dir);
|
|
5194
|
+
}
|
|
5195
|
+
|
|
5196
|
+
fn respawn(id: u32, frameSalt: u32) {
|
|
5197
|
+
var state = hashU32(id * 747796405u + frameSalt * 2891336453u + 0x68bc21ebu);
|
|
5198
|
+
let spawnDir = randomUnit(&state);
|
|
5199
|
+
let pos = spawnDir * params.spawnRadius;
|
|
5200
|
+
let jitter = randomUnit(&state) * params.jitter;
|
|
5201
|
+
let velDir = safeNormal(-pos + jitter);
|
|
5202
|
+
bullets[id].posLife = vec4<f32>(pos, params.life);
|
|
5203
|
+
bullets[id].velRadius = vec4<f32>(velDir * params.speed, params.bulletRadius);
|
|
5204
|
+
bullets[id].seed = vec4<u32>(state, frameSalt, id, 0u);
|
|
5205
|
+
}
|
|
5206
|
+
|
|
5207
|
+
fn rayTriHit(
|
|
5208
|
+
origin: vec3<f32>,
|
|
5209
|
+
dir: vec3<f32>,
|
|
5210
|
+
maxT: f32,
|
|
5211
|
+
a: vec3<f32>,
|
|
5212
|
+
b: vec3<f32>,
|
|
5213
|
+
c: vec3<f32>
|
|
5214
|
+
) -> bool {
|
|
5215
|
+
let ab = b - a;
|
|
5216
|
+
let ac = c - a;
|
|
5217
|
+
let p = cross(dir, ac);
|
|
5218
|
+
let det = dot(ab, p);
|
|
5219
|
+
if (abs(det) < EPS) {
|
|
5220
|
+
return false;
|
|
5221
|
+
}
|
|
5222
|
+
|
|
5223
|
+
let invDet = 1.0 / det;
|
|
5224
|
+
let tvec = origin - a;
|
|
5225
|
+
let u = dot(tvec, p) * invDet;
|
|
5226
|
+
if (u < 0.0 || u > 1.0) {
|
|
5227
|
+
return false;
|
|
5228
|
+
}
|
|
5229
|
+
|
|
5230
|
+
let q = cross(tvec, ab);
|
|
5231
|
+
let v = dot(dir, q) * invDet;
|
|
5232
|
+
if (v < 0.0 || u + v > 1.0) {
|
|
5233
|
+
return false;
|
|
5234
|
+
}
|
|
5235
|
+
|
|
5236
|
+
let t = dot(ac, q) * invDet;
|
|
5237
|
+
return t >= 0.0 && t <= maxT;
|
|
5238
|
+
}
|
|
5239
|
+
|
|
5240
|
+
fn sweepTriangle(p0: vec3<f32>, p1: vec3<f32>, radius: f32, tri: Triangle) -> bool {
|
|
5241
|
+
let delta = p1 - p0;
|
|
5242
|
+
let len = length(delta);
|
|
5243
|
+
if (len < EPS) {
|
|
5244
|
+
return false;
|
|
5245
|
+
}
|
|
5246
|
+
|
|
5247
|
+
let dir = delta / len;
|
|
5248
|
+
let basis = makeBasis(dir);
|
|
5249
|
+
let u = basis[0] * radius * 0.85;
|
|
5250
|
+
let v = basis[1] * radius * 0.85;
|
|
5251
|
+
let maxT = len + radius * 2.75;
|
|
5252
|
+
let a = tri.a.xyz;
|
|
5253
|
+
let b = tri.b.xyz;
|
|
5254
|
+
let c = tri.c.xyz;
|
|
5255
|
+
|
|
5256
|
+
if (rayTriHit(p0, dir, maxT, a, b, c)) { return true; }
|
|
5257
|
+
if (rayTriHit(p0 + u, dir, maxT, a, b, c)) { return true; }
|
|
5258
|
+
if (rayTriHit(p0 - u, dir, maxT, a, b, c)) { return true; }
|
|
5259
|
+
if (rayTriHit(p0 + v, dir, maxT, a, b, c)) { return true; }
|
|
5260
|
+
if (rayTriHit(p0 - v, dir, maxT, a, b, c)) { return true; }
|
|
5261
|
+
return false;
|
|
5262
|
+
}
|
|
5263
|
+
|
|
5264
|
+
@compute @workgroup_size(128)
|
|
5265
|
+
fn collisionBenchmark(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
5266
|
+
let id = gid.x + gid.y * params.dispatchWidth;
|
|
5267
|
+
if (id >= params.targetCount) {
|
|
5268
|
+
if (id < arrayLength(&bullets)) {
|
|
5269
|
+
bullets[id].posLife.w = -1.0;
|
|
5270
|
+
}
|
|
5271
|
+
return;
|
|
5272
|
+
}
|
|
5273
|
+
|
|
5274
|
+
var b = bullets[id];
|
|
5275
|
+
if (b.posLife.w <= 0.0 || b.velRadius.w <= 0.0) {
|
|
5276
|
+
respawn(id, params.frame);
|
|
5277
|
+
atomicAdd(&counters.alive, 1u);
|
|
5278
|
+
return;
|
|
5279
|
+
}
|
|
5280
|
+
|
|
5281
|
+
let p0 = b.posLife.xyz;
|
|
5282
|
+
let p1 = p0 + b.velRadius.xyz * params.dt;
|
|
5283
|
+
var hit = false;
|
|
5284
|
+
|
|
5285
|
+
for (var triId = 0u; triId < params.triCount; triId += 1u) {
|
|
5286
|
+
if (!hit && sweepTriangle(p0, p1, b.velRadius.w, triangles[triId])) {
|
|
5287
|
+
hit = true;
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
|
|
5291
|
+
if (hit) {
|
|
5292
|
+
atomicAdd(&counters.hits, 1u);
|
|
5293
|
+
respawn(id, params.frame + 97u);
|
|
5294
|
+
atomicAdd(&counters.alive, 1u);
|
|
5295
|
+
return;
|
|
5296
|
+
}
|
|
5297
|
+
|
|
5298
|
+
b.posLife = vec4<f32>(p1, b.posLife.w - params.dt);
|
|
5299
|
+
bullets[id] = b;
|
|
5300
|
+
atomicAdd(&counters.alive, 1u);
|
|
5301
|
+
}
|
|
5302
|
+
`;var Ls=48,Wa=48,_s=16,Ha=48,$a=class{constructor(e,t,i=3){this.device=e,this.recycler=t,this.slotCount=Math.max(2,i|0),this.usage=GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST,this.slots=[],this.cursor=0,this.latest={hits:0,alive:0};for(let r=0;r<this.slotCount;r+=1)this.slots.push({buffer:t.acquire(_s,this.usage,`collision-counter-readback-${r}`),pending:!1})}encodeCopy(e,t){let i=null;for(let r=0;r<this.slots.length;r+=1){let o=(this.cursor+r)%this.slots.length;if(!this.slots[o].pending){i=this.slots[o],this.cursor=(o+1)%this.slots.length;break}}return i?(i.pending=!0,e.copyBufferToBuffer(t,0,i.buffer,0,_s),i):null}resolve(e){return e?(e.buffer.mapAsync(GPUMapMode.READ).then(()=>{let t=new Uint32Array(e.buffer.getMappedRange(),0,4);this.latest={hits:t[0],alive:t[1]},e.buffer.unmap(),e.pending=!1}).catch(t=>{this.disposed||console.warn("Particle counter readback failed:",t),e.pending=!1}),this.latest):this.latest}destroy(){this.disposed=!0;for(let e of this.slots)e.pending||this.recycler.release(e.buffer,_s,this.usage);this.slots.length=0}},An=class{constructor(e,{capacity:t,triangleCount:i,readback:r=!0}={}){let o=e.limits.maxComputeWorkgroupsPerDimension**2*128,n=Math.min(e.limits.maxStorageBufferBindingSize,e.limits.maxBufferSize);for(let[a,l,c]of[["capacity",t,Ls],["triangleCount",i,Wa]])if(!Number.isSafeInteger(l)||l<1||l*c>n||a==="capacity"&&l>o)throw Error(`Invalid particle ${a}: exceeds GPU capacity or is not a positive integer`);this.device=e,this.timer=new Wr(e),this.capacity=t,this.triangleCount=i,this.frame=0,this.recycler=new Hr(e),this.storageUsage=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,this.uniformUsage=GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST,this.module=e.createShaderModule({label:"collision-benchmark-module",code:wu}),this.pipeline=e.createComputePipeline({label:"collision-benchmark-pipeline",layout:"auto",compute:{module:this.module,entryPoint:"collisionBenchmark"}}),this.particleBuffer=this.recycler.acquire(this.capacity*Ls,this.storageUsage,"collision-bullets"),this.triangleBuffer=this.recycler.acquire(this.triangleCount*Wa,this.storageUsage,"collision-triangles"),this.counterBuffer=this.recycler.acquire(_s,this.storageUsage,"collision-counters"),this.paramBuffer=this.recycler.acquire(Ha,this.uniformUsage,"collision-params"),this.params=new ArrayBuffer(Ha),this.zeroCounters=new Uint32Array(4),this.readback=r?new $a(e,this.recycler,3):null,this.bindGroup=e.createBindGroup({label:"collision-benchmark-bg",layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.particleBuffer}},{binding:1,resource:{buffer:this.triangleBuffer}},{binding:2,resource:{buffer:this.counterBuffer}},{binding:3,resource:{buffer:this.paramBuffer}}]}),this.clear()}clear(){if(this.disposed)throw Error("Particle system has been destroyed");let e=this.device.createCommandEncoder({label:"Clear surface particles"});e.clearBuffer(this.particleBuffer),this.device.queue.submit([e.finish()]),this.frame=0,this.readback&&(this.readback.latest={hits:0,alive:0})}writeTriangles(e){if(this.disposed)throw Error("Particle system has been destroyed");if(!(e instanceof Float32Array)||e.length!==this.triangleCount*12||!e.every(Number.isFinite))throw Error("Triangles require 12 finite float32 values per triangle: padded a, b, c vertices");this.device.queue.writeBuffer(this.triangleBuffer,0,e)}step(e){if(this.disposed)throw Error("Particle system has been destroyed");let t={speed:80,life:3,bulletRadius:.09,spawnRadius:32,jitter:4.8,...e,target:e.count??this.capacity,frame:e.frame??this.frame++};if(!Number.isSafeInteger(t.target)||t.target<1||t.target>this.capacity)throw Error("Particle count exceeds allocated capacity");for(let d of["dt","speed","life","bulletRadius","spawnRadius"])if(!Number.isFinite(t[d])||t[d]<=0)throw Error(`Particle ${d} must be positive and finite`);if(!Number.isFinite(t.jitter)||t.jitter<0)throw Error("Particle jitter must be nonnegative and finite");let i=new Float32Array(this.params),r=new Uint32Array(this.params);i.fill(0),i[0]=t.dt,i[1]=t.speed,i[2]=t.life,i[3]=t.bulletRadius,i[4]=t.spawnRadius,i[5]=t.jitter,r[6]=t.target>>>0,r[7]=this.triangleCount>>>0,r[8]=t.frame>>>0;let o=Math.ceil(t.target/128),n=Math.ceil(o/this.device.limits.maxComputeWorkgroupsPerDimension),a=Math.ceil(o/n);r[9]=a*128,this.device.queue.writeBuffer(this.paramBuffer,0,this.params),this.device.queue.writeBuffer(this.counterBuffer,0,this.zeroCounters);let l=this.device.createCommandEncoder({label:"collision-benchmark-encoder"}),c=this.timer.begin(),u=l.beginComputePass({label:"collision-benchmark-pass",...this.timer.descriptor(c)});u.setPipeline(this.pipeline),u.setBindGroup(0,this.bindGroup),u.dispatchWorkgroups(a,n),u.end(),this.timer.encode(l,c);let f=this.readback?.encodeCopy(l,this.counterBuffer);return this.device.queue.submit([l.finish()]),this.timer.resolve(c),this.readback?.resolve(f)??null}destroy(){this.disposed||(this.disposed=!0,this.timer.destroy(),this.readback?.destroy(),this.recycler.release(this.particleBuffer,this.capacity*Ls,this.storageUsage),this.recycler.release(this.triangleBuffer,this.triangleCount*Wa,this.storageUsage),this.recycler.release(this.counterBuffer,_s,this.storageUsage),this.recycler.release(this.paramBuffer,Ha,this.uniformUsage),this.recycler.destroy())}};var Bu=`// Read the canonical solver's completed contact buffers directly. These layouts
|
|
5303
|
+
// match avbd3d/gpu/layout.ts; no contact data is copied to the CPU for this view.
|
|
5304
|
+
struct Body {pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f}
|
|
5305
|
+
struct Camera {vp:mat4x4f,eye:vec4f,light:vec4f}
|
|
5306
|
+
struct Manifold {ids:vec4u,geo:vec4f}
|
|
5307
|
+
struct Contact {rA:vec4f,rB:vec4f,pen:vec4f,lambda:vec4f}
|
|
5308
|
+
@group(0) @binding(0) var<storage,read> bodies:array<Body>;
|
|
5309
|
+
@group(0) @binding(1) var<uniform> camera:Camera;
|
|
5310
|
+
@group(0) @binding(2) var<storage,read> manifolds:array<Manifold>;
|
|
5311
|
+
@group(0) @binding(3) var<storage,read> contacts:array<Contact>;
|
|
5312
|
+
@group(0) @binding(4) var<storage,read> counters:array<u32>;
|
|
5313
|
+
const CORNERS=array<vec2f,6>(vec2f(-1,-1),vec2f(1,-1),vec2f(1,1),vec2f(-1,-1),vec2f(1,1),vec2f(-1,1));
|
|
5314
|
+
const MAX_MANIFOLDS=512u;
|
|
5315
|
+
fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(q.xyz,t);}
|
|
5316
|
+
@vertex fn vertexMain(@builtin(vertex_index) v:u32,@builtin(instance_index) instance:u32)->@builtin(position) vec4f {
|
|
5317
|
+
let hidden=vec4f(2,2,2,1);
|
|
5318
|
+
let count=min(counters[6],arrayLength(&manifolds));
|
|
5319
|
+
let slot=instance/4u;
|
|
5320
|
+
if(slot>=min(count,MAX_MANIFOLDS)){return hidden;}
|
|
5321
|
+
// Sample across the entire contact region when it exceeds the display budget.
|
|
5322
|
+
let index=select(slot,slot*count/MAX_MANIFOLDS,count>MAX_MANIFOLDS);
|
|
5323
|
+
let m=manifolds[index];let point=instance%4u;let k=m.ids.z+point;
|
|
5324
|
+
if(point>=(m.ids.w&15u)||k>=min(counters[1],arrayLength(&contacts))||m.ids.x>=arrayLength(&bodies)){return hidden;}
|
|
5325
|
+
let b=bodies[m.ids.x];let p=b.pos.xyz+rotate(b.rot,contacts[k].rA.xyz);
|
|
5326
|
+
var clip=camera.vp*vec4f(p.x,p.z,p.y,1);
|
|
5327
|
+
clip.x+=CORNERS[v].x*3./camera.light.x*clip.w;
|
|
5328
|
+
clip.y+=CORNERS[v].y*3./camera.light.y*clip.w;
|
|
5329
|
+
return clip;
|
|
5330
|
+
}
|
|
5331
|
+
@fragment fn fragmentMain()->@location(0) vec4f {return vec4f(.83,.25,.39,1);}
|
|
5332
|
+
`;var Rd=Bu.replace("@vertex fn vertexMain","fn samplePoint").replaceAll("@builtin(vertex_index) ","").replaceAll("@builtin(instance_index) ","").replace("->@builtin(position) vec4f","->vec4f").replace(/clip\.x\+=.*?;/,"").replace(/clip\.y\+=.*?;/,"")+`
|
|
5333
|
+
@group(0) @binding(5) var<storage,read_write> sampled:array<vec4f>;
|
|
5334
|
+
@compute @workgroup_size(64) fn gather(@builtin(global_invocation_id) id:vec3u) {
|
|
5335
|
+
if(id.x<arrayLength(&sampled)){sampled[id.x]=samplePoint(0u,id.x);}
|
|
5336
|
+
}`,Cu=`
|
|
5337
|
+
struct Camera {vp:mat4x4f,eye:vec4f,light:vec4f}
|
|
5338
|
+
@group(0) @binding(0) var<uniform> camera:Camera;
|
|
5339
|
+
@group(0) @binding(1) var<storage,read> points:array<vec4f>;
|
|
5340
|
+
const CORNERS=array<vec2f,6>(vec2f(-1,-1),vec2f(1,-1),vec2f(1,1),vec2f(-1,-1),vec2f(1,1),vec2f(-1,1));
|
|
5341
|
+
@vertex fn vertexMain(@builtin(vertex_index) v:u32,@builtin(instance_index) instance:u32)->@builtin(position) vec4f {
|
|
5342
|
+
var clip=points[instance];
|
|
5343
|
+
clip.x+=CORNERS[v].x*3./camera.light.x*clip.w;
|
|
5344
|
+
clip.y+=CORNERS[v].y*3./camera.light.y*clip.w;
|
|
5345
|
+
return clip;
|
|
5346
|
+
}
|
|
5347
|
+
@fragment fn fragmentMain()->@location(0) vec4f {return vec4f(.83,.25,.39,1);}
|
|
5348
|
+
`,wn=class{constructor(e,{count:t=2048}={}){if(!Number.isSafeInteger(t)||t<1||Math.ceil(t/64)>e.limits.maxComputeWorkgroupsPerDimension)throw Error("Contact sample count exceeds GPU dispatch capacity");this.device=e,this.count=t,this.buffer=e.createBuffer({label:"AVBD sampled contact dots",size:this.count*16,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC}),this.pipeline=e.createComputePipeline({label:"AVBD contact overlay sampling",layout:"auto",compute:{module:e.createShaderModule({code:Rd}),entryPoint:"gather"}}),this.bindings=[]}encode(e,{bodyBuffer:t,cameraBuffer:i,contactStorage:r}){let o=[t,i,r.manifolds,r.contacts,r.counters,this.buffer],n=this.bindings.find(l=>l.buffers.every((c,u)=>c===o[u]));n||(n={buffers:o,bind:this.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:o.map((l,c)=>({binding:c,resource:{buffer:l}}))})},this.bindings.push(n),this.bindings.length>2&&this.bindings.shift());let a=e.beginComputePass();a.setPipeline(this.pipeline),a.setBindGroup(0,n.bind),a.dispatchWorkgroups(Math.ceil(this.count/64)),a.end()}dispose(){this.buffer.destroy(),this.bindings.length=0}};function Su(s,e,t=8){let i=new Map(e.bodies.map((c,u)=>[c,u])),r=e.bodies.map(()=>new Set);for(let c of e.forces){let u=i.get(c.bodyA),f=i.get(c.bodyB);u===void 0||f===void 0||e.bodies[u].mass===0||e.bodies[f].mass===0||(r[u].add(f),r[f].add(u))}let o=e.bodies.map((c,u)=>u).filter(c=>e.bodies[c].mass>0).sort((c,u)=>r[u].size-r[c].size||c-u),n=new Uint32Array(s.bodyCapacity).fill(255),a=new Map,l=0;for(let c of o){let u=new Set([...r[c]].map(d=>a.get(d))),f=0;for(;u.has(f);)f++;if(f>=64)return;a.set(c,f),n[s.gpuIndex(c)]=f,l=Math.max(l,f+1)}s.device.queue.writeBuffer(s.colorBuffer,0,n),s.colorCap=Math.min(64,Math.max(t,l+3))}var Ja=new WeakMap;function ku(s,{before:e,after:t}={}){if(!s.bodyBuffer||typeof s.step!="function")throw Error("GPU step passes require a WebGPU solver");if(!e&&!t||e!==void 0&&typeof e!="function"||t!==void 0&&typeof t!="function")throw Error("GPU step passes require synchronous before and/or after callbacks");let i=Ja.get(s);i||(i={hooks:new Set,previousBefore:s.externalBeforeStep,previousAfter:s.externalAfterStep},i.before=o=>{i.previousBefore?.call(s,o);for(let n of i.hooks)n.before?.(o,s)},i.after=o=>{i.previousAfter?.call(s,o);for(let n of i.hooks)n.after?.(o,s)},s.externalBeforeStep=i.before,s.externalAfterStep=i.after,Ja.set(s,i));let r={before:e,after:t};return i.hooks.add(r),()=>{i.hooks.delete(r)&&(i.hooks.size||(s.externalBeforeStep===i.before&&(s.externalBeforeStep=i.previousBefore),s.externalAfterStep===i.after&&(s.externalAfterStep=i.previousAfter),Ja.delete(s)))}}var Bn=class{constructor(e,t,i){this.offscreenCanvas=e,this.events=new Map,this.resize(t,i)}resize(e,t){let i=Math.max(1,Math.floor(e)),r=Math.max(1,Math.floor(t));(i!==this.clientWidth||r!==this.clientHeight)&&(this.revision=(this.revision??0)+1),this.clientWidth=i,this.clientHeight=r}get width(){return this.offscreenCanvas.width}set width(e){this.offscreenCanvas.width=e}get height(){return this.offscreenCanvas.height}set height(e){this.offscreenCanvas.height=e}getContext(...e){return this.offscreenCanvas.getContext(...e)}getBoundingClientRect(){return{left:0,top:0,width:this.clientWidth,height:this.clientHeight}}addEventListener(e,t){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(t)}removeEventListener(e,t){this.events.get(e)?.delete(t)}setPointerCapture(){}dispatch(e,t){let i={...t,preventDefault(){}};for(let r of this.events.get(e)??[])r(i)}};var Pu=9;function Lu(){return globalThis.crossOriginIsolated&&typeof SharedArrayBuffer=="function"?new Int32Array(new SharedArrayBuffer(36)):null}function _u(s,e,t=0){Atomics.add(s,0,1),[Math.round(e.clientX*1024),Math.round(e.clientY*1024),e.pointerId??1,e.buttons??0,e.altKey?1:0,e.shiftKey?1:0,e.ctrlKey?1:0,Math.round(t*1024)].forEach((r,o)=>Atomics.store(s,o+1,r)),Atomics.add(s,0,1)}function Eu(s,e=-1){let t=Atomics.load(s,0);if(t&1||t===e)return null;let i=Array.from({length:8},(r,o)=>Atomics.load(s,o+1));return Atomics.load(s,0)!==t?null:{sequence:t,event:{clientX:i[0]/1024,clientY:i[1]/1024,pointerId:i[2],buttons:i[3],altKey:!!i[4],shiftKey:!!i[5],ctrlKey:!!i[6]},wheel:i[7]/1024}}return Du(Od);})();
|