@hology/core 0.0.258 → 0.0.260

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.
@@ -1,4 +1,4 @@
1
- import*as e from"three";import{applyMeshWorldTransformToGeometry as t}from"./vfx-mesh-transform.js";import{attributes as a,clamp as r,lambertMaterial as s,NodeShaderMaterial as o,rgba as n,smoothstep as i,textureSampler2d as l,varyingAttributes as p}from"three-shader-graph";import{SerializedParamType as c}from"../../scene/model.js";import{particleUniforms as u}from"../../shader-nodes/particle.js";import{sampleFlipbook as m}from"../../shader-nodes/texture-sequence.js";import{fragmentLinearEyeDepth as d,linearEyeDepth as h}from"../../shader-nodes/depth.js";import{ThreeBlendingMode as y}from"./vfx-asset.js";import{getDefaultVfxSpriteTexture as f}from"./vfx-default-sprite-texture.js";import{ShapeLibrary as b}from"../../scene/objects/shapes.js";export class GpuSpriteRenderClassCache{constructor(){this.entries=new Map}resolve(e,t){let a=this.entries.get(e);return null==a&&(a=t().catch(t=>{throw this.entries.delete(e),t}),this.entries.set(e,a)),a}clear(){this.entries.clear()}}export function hasGpuUnsupportedVfxInputBindings(e){const t=new Map((e.vfx.inputs??[]).map(e=>[e.id,e.type])),a=e=>{for(const a of[...e.initializers,...e.behaviours])if(!1!==a.enabled)for(const[r,s]of Object.entries(a.params)){if("vfxInput"!==s?.binding?.type)continue;const o=s.type===c.Number?"number":s.type===c.Vector3?"vec3":s.type===c.Color?"color":s.type===c.Object3D?"mesh":void 0;if(null==o||t.get(s.binding.inputId)!==o)return!0;if("lifetime"===a.type&&"duration"===r&&"continuous"===e.rate.type)return!0}return(e.children??[]).some(a)};return e.vfx.emitters.some(a)}export function selectVfxBackend(e,t="auto"){if("cpu"===t)return{backend:"cpu",reasons:["CPU backend explicitly requested"]};const a=e.issues.filter(e=>"error"===e.severity).map(e=>e.message);return a.length>0&&"gpu"!==t?{backend:"cpu",reasons:a}:a.length>0?{backend:"gpu",reasons:["GPU backend explicitly forced despite incompatibilities",...a]}:{backend:"gpu",reasons:e.issues.filter(e=>"warning"===e.severity).map(e=>e.message)}}export function compileGpuSpriteEmitters(t,a,r="emitter-major"){const s=[],o=[],n=new Map,i=[],l=[],p=new Map((t.vfx.inputs??[]).map(e=>[e.id,e.type])),u=new Map((t.vfx.inputs??[]).filter(e=>"number"===e.type).map(e=>[e.id,"number"==typeof e.defaultValue?e.defaultValue:0]));hasGpuUnsupportedVfxInputBindings(t)&&i.push({severity:"error",code:"vfx-input-binding",emitter:"effect",message:"this VFX input binding target is not supported on GPU yet"});const m=t.vfx.emitters.slice().sort((e,t)=>(e.output.renderOrder??0)-(t.output.renderOrder??0)),d=(t,r,m)=>{const h="vfxInput"===t.rate.delayBinding?.type?u.get(t.rate.delayBinding.inputId):void 0,y=null==h?t:{...t,rate:{...t.rate,delay:Math.max(0,h)}};if("sprite"!==t.output.type&&"mesh"!==t.output.type&&"stretchedSprite"!==t.output.type&&"decal"!==t.output.type&&"shape"!==t.output.type&&"trail"!==t.output.type)return void w(i,t,"error","output-type","this output type is not supported by the GPU sprite backend");if("mesh"===t.output.type&&null==t.output.assetId)return;if(t.rate.count<=0)return;const f="trail"===(b=t.output).type?JSON.stringify(b):"mesh"===b.type?JSON.stringify({type:b.type,assetId:b.assetId,renderOrder:b.renderOrder??0,useLegacyTransform:!0===b.useLegacyTransform,materialSource:b.materialSource??null,material:b.material??null,shader:b.shader??null,shaderParams:b.shaderParams??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space}):"decal"===b.type?JSON.stringify({type:b.type,colorMap:b.colorMap,alphaMap:b.alphaMap,color:b.color,unlit:b.unlit,intensity:b.intensity,opacityChannel:b.opacityChannel??"red",blendingMode:b.blendingMode,renderOrder:b.renderOrder??0,materialSource:b.materialSource??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space}):"shape"===b.type?JSON.stringify({type:b.type,shape:b.shape,params:b.params,material:b.material,renderOrder:b.renderOrder??0,materialSource:b.materialSource??null,shader:b.shader??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space}):JSON.stringify({type:b.type,texture:b.texture,opacityChannel:b.opacityChannel??"red",blendingMode:b.blendingMode,softness:b.softness,renderOrder:b.renderOrder??0,bloom:b.bloom,materialSource:b.materialSource??null,shader:b.shader??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space,lockY:"sprite"===b.type&&!0===b.lockY,stretchScale:"stretchedSprite"===b.type?b.scale:null});var b;let x=n.get(f);null==x&&(x=o.length,n.set(f,x),o.push({key:f,output:t.output}));const v=[];for(let o=0;o<a.length;o++){const n=g(y,null==r?a[o]:new e.Vector3,x,i);if(null!=n){if(null!=r){const e=s[r]?.instances[o];if(null==e)continue;const a=n.capacity,l=e.capacity*a;if(l>25e4){w(i,t,"error","child-particle-capacity",`child emitter expansion requires ${l} particles, exceeding the GPU per-emitter limit`);continue}n.capacity=l,n.parentEmitter=r,n.childParticlesPerParent=a,n.childSpawnEvent="collision"===t.spawnEvent?2:1,n.childLevel=m,!0===n.localSpace&&!0!==e.localSpace&&w(i,t,"error","child-emitter-local-space","GPU child emitters do not yet support a world-space parent with local-space child output"),"trail"===t.output.type&&w(i,t,"error","child-emitter-trail","GPU child emitters do not yet support trail output")}v.push(n)}}if(v.length>0){const e=s.length;s.push({instances:v,parentSource:r});for(const a of[...t.initializers,...t.behaviours])if(!1!==a.enabled)for(const[r,s]of Object.entries(a.params)){const o=s?.binding;if("vfxInput"!==o?.type)continue;const n=s.type===c.Number?"number":s.type===c.Vector3?"vec3":s.type===c.Color?"color":s.type===c.Object3D?"mesh":void 0;null!=n&&p.get(o.inputId)===n&&("mesh"!==n&&("lifetime"===a.type&&"duration"===r&&"continuous"===t.rate.type||l.push({inputId:o.inputId,emitterIndex:e,target:`${t.initializers.includes(a)?"initializer":"behaviour"}:${a.type}:${r}`,authoredScalar:"number"==typeof s.value?s.value:void 0,baseScaleStart:[...v[0].scaleStart],baseScaleEnd:[...v[0].scaleEnd]})))}for(const a of t.children??[])d(a,e,m+1)}};for(const e of m)d(e,null,0);const h="instance-major"===r?a.flatMap((e,t)=>s.flatMap(e=>null==e.instances[t]?[]:[e.instances[t]])):s.flatMap(e=>e.instances);for(let e=0;e<s.length;e++){const t=s[e];if(null!=t.parentSource)for(let e=0;e<t.instances.length;e++)t.instances[e].parentEmitter="instance-major"===r?e*s.length+t.parentSource:t.parentSource*a.length+e}const y=o.map((e,t)=>({renderClass:e,oldIndex:t})).sort((e,t)=>(e.renderClass.output.renderOrder??0)-(t.renderClass.output.renderOrder??0)),f=new Array(o.length);for(let e=0;e<y.length;e++)f[y[e].oldIndex]=e;for(const e of h)e.renderClass=f[e.renderClass??0]??0;const b={compatible:!i.some(e=>"error"===e.severity),issues:i};return{emitters:h,inputBindings:l,renderClasses:y.map(e=>e.renderClass),compatibility:b,unsupported:[...new Set(i.map(e=>`${e.emitter}: ${e.message}`))]}}export async function materializeGpuSpriteRenderClasses(c,g,w,x){const v=async c=>{const x=c.output;if("trail"===x.type){const t=x.materialSource??("asset"===x.shaderGraph?.source?"shaderGraph":"default"),a="default"===t?null:await(w?.(x))??null;if(null==a&&"default"!==t)throw new Error(`${t} trail material requires a compatible GPU material resolver`);return{texture:null!=x.texture?await g.getTexture(x.texture):f(),material:a??void 0,localSpace:"local"===x.space,trail:{historyLength:Math.max(2,Math.round(x.length)+1),width:x.width??1,taper:x.taper,colorStart:new e.Color(x.color).multiplyScalar(x.intensity),colorEnd:new e.Color(x.colorEnd).multiplyScalar(x.intensityEnd),opacityStart:x.opacityStart,opacityEnd:x.opacityEnd,scrollSpeed:x.scrollSpeed??0,opacityChannel:x.opacityChannel??"red",blendingMode:x.blendingMode??"normal",billboard:x.billboard??!1}}}if("mesh"===x.type){if(null==g.getMesh)throw new Error("GPU mesh particles require a mesh asset provider");const a=await g.getMesh(x.assetId);a.updateWorldMatrix(!0,!0);const r=[];if(a.traverse(t=>{t instanceof e.Mesh&&r.push(t)}),1!==r.length)throw new Error(`GPU mesh particles require exactly one mesh geometry; asset ${x.assetId} resolved to ${r.length}`);const s=null==r[0].geometry.index?r[0].geometry.clone():r[0].geometry.toNonIndexed();!0!==x.useLegacyTransform&&t(r[0],s);const n=s.getAttribute("position");if(null==n)throw new Error(`GPU mesh particle asset ${x.assetId} has no position attribute`);const i=s.getAttribute("normal"),l=s.getAttribute("uv"),p=new Float32Array(8*n.count);for(let e=0;e<n.count;e++){const t=8*e;p[t]=n.getX(e),p[t+1]=n.getY(e),p[t+2]=n.getZ(e),p[t+3]=i?.getX(e)??0,p[t+4]=i?.getY(e)??1,p[t+5]=i?.getZ(e)??0,p[t+6]=l?.getX(e)??0,p[t+7]=l?.getY(e)??0}s.dispose();const c=Array.isArray(r[0].material)?r[0].material[0]:r[0].material,m="material"===x.materialSource&&null==x.material||"shaderGraph"===x.materialSource&&("asset"!==x.shaderGraph?.source||null==x.shaderGraph.assetId)||"shader"===x.materialSource&&null==x.shader?"default":x.materialSource??("asset"===x.shaderGraph?.source?"shaderGraph":null!=x.shader?"shader":null!=x.material?"material":"default"),d="default"===m?await(w?.(x))??function(){const e=u.opacity;return new o({color:u.color.rgb.rgba(e),opacity:e,transparent:!0})}():await(w?.(x))??null;if(null==d&&"default"!==m)throw new Error(`${m} mesh material requires a compatible GPU material resolver`);const h=null!=c&&"color"in c&&c.color instanceof e.Color?c.color.clone():new e.Color(16777215);return{texture:f(),meshVertices:p,meshColor:h,meshOpacity:c?.opacity??1,localSpace:"local"===x.space,material:d??void 0}}if("decal"===x.type){const t=await(w?.(x))??null;if(null==t)throw new Error("GPU decal output requires a compatible GPU decal material resolver");const a=new e.BoxGeometry(1,1,1).toNonIndexed(),r=a.getAttribute("position"),s=a.getAttribute("normal"),o=a.getAttribute("uv"),n=new Float32Array(8*r.count);for(let e=0;e<r.count;e++){const t=8*e;n[t]=r.getX(e),n[t+1]=r.getY(e),n[t+2]=r.getZ(e),n[t+3]=s.getX(e),n[t+4]=s.getY(e),n[t+5]=s.getZ(e),n[t+6]=o.getX(e),n[t+7]=o.getY(e)}return a.dispose(),{texture:f(),meshVertices:n,meshColor:new e.Color(16777215),meshOpacity:1,localSpace:"local"===x.space,material:t}}if("shape"===x.type){const t=b[x.shape];if(null==t)throw new Error(`GPU shape output has unknown shape ${x.shape}`);const a=x.materialSource??("asset"===x.shaderGraph?.source?"shaderGraph":null!=x.shader?"shader":null!=x.material?"material":"default"),r="default"===a?function(){const e=u.opacity;return new o({color:s({color:u.color.rgb}).rgb.rgba(e),opacity:e,transparent:!0})}():await(w?.(x))??null;if(null==r&&"default"!==a)throw new Error(`${a} shape material requires a compatible GPU material resolver`);const n=Object.fromEntries(Object.entries(x.params??{}).map(([e,t])=>[e,t.value])),i=t.geometry(n),l=null==i.index?i:i.toNonIndexed(),p=l.getAttribute("position"),c=l.getAttribute("normal"),m=l.getAttribute("uv"),d=new Float32Array(8*p.count);for(let e=0;e<p.count;e++){const t=8*e;d[t]=p.getX(e),d[t+1]=p.getY(e),d[t+2]=p.getZ(e),d[t+3]=c?.getX(e)??0,d[t+4]=c?.getY(e)??1,d[t+5]=c?.getZ(e)??0,d[t+6]=m?.getX(e)??0,d[t+7]=m?.getY(e)??0}return l!==i&&l.dispose(),i.dispose(),{texture:f(),meshVertices:d,meshColor:new e.Color(16777215),meshOpacity:1,localSpace:"local"===x.space,material:r??void 0}}const v=null!=x.texture?await g.getTexture(x.texture):f(),S="stretchedSprite"===x.type?"default":x.materialSource??("asset"===x.shaderGraph?.source?"shaderGraph":"default");let M="default"===S?null:await(w?.(x))??null;if(null==M&&"default"!==S)throw new Error(`${S} material requires a compatible GPU material resolver`);return M??(M=function(t,s){const c=l(s),f="sprite"===t.type&&!0===t.flipbook?.enabled?m(c,a.uv,t.flipbook.columns,t.flipbook.rows,u.time,t.flipbook.fps,t.flipbook.mode):c.sample(p.uv);let b=u.opacity;"red"===(t.opacityChannel??"red")?b=b.multiply(f.r):"alpha"===t.opacityChannel&&(b=b.multiply(f.a));if(t.softness>0){const e=h.subtract(d).divide(h);b=b.multiply(i(0,.2*t.softness,r(e,0,1e3)))}const g=new o({color:n(u.color.multiply(f.rgb),b),transparent:!0,lights:!1,fog:!1,alphaTest:1e-4});return g.blending=y[t.blendingMode]??e.NormalBlending,g.depthWrite=!1,g}(x,v)),M.transparent&&(M.depthWrite=!1),{texture:v,material:M,lockY:"sprite"===x.type&&!0===x.lockY,localSpace:"local"===x.space,stretchScale:"stretchedSprite"===x.type?0===x.scale?.001:x.scale:void 0}};return{renderClasses:await Promise.all(c.map(e=>x?.resolve(e.key,()=>v(e))??v(e))),unsupported:[]}}function g(t,a,r,s){if("sprite"!==t.output.type&&"mesh"!==t.output.type&&"decal"!==t.output.type&&"shape"!==t.output.type&&"trail"!==t.output.type&&"stretchedSprite"!==t.output.type)return null;const o="mesh"===t.output.type||"decal"===t.output.type||"shape"===t.output.type;let n=99999,i=99999,l=0,p=0;const c=new e.Vector4,u=new e.Vector4,m=new e.Vector3,d=new e.Vector3,h=new e.Vector3;let y=0,f=1;const b=new e.Vector3;let g=0,k=0,G=!1,V=0,C=!1;const I=new e.Vector3;let P,O=0,E=!1,U=!1,A=0,R=0,N=0,B=!1,T=[1,1],z=1,$=1,j=new e.Color("sprite"===t.output.type||"stretchedSprite"===t.output.type?t.output.color:"#ffffff"),F=j.clone(),Y=!1,X=1,Z=1,D=0,q=0;const L=new e.Quaternion,Q=new e.Vector3,J=new e.Vector3;let W=0;const H=new e.Vector3(0,1,0);let K=0,_=0,ee=!1;const te=new e.Vector3;let ae=0,re=0,se=!1;const oe=new e.Vector3(0,1,0);let ne=0;const ie=new e.Vector3,le=new e.Vector3,pe=new e.Vector3(0,1,0);let ce=0,ue=!1,me=0,de=0,he=0,ye=!1;const fe=new e.Vector4(0,0,0,0),be=new e.Vector3(1,1,1),ge=new e.Vector3(1,1,1),we=new e.Vector3(1,1,1);let xe,ve,Se,Me,ke,Ge="surface";for(const a of t.initializers.filter(e=>!1!==e.enabled))switch(a.type){case"lifetime":i=n=x(a.params.duration,1);break;case"randomLifetime":{const e=x(a.params.minDuration,1),t=x(a.params.maxDuration,2);i=Math.min(e,t),n=Math.max(e,t);break}case"velocity":{const e=S(a.params.velocity);m.copy(e),d.copy(e);break}case"randomVelocity":m.copy(S(a.params.min)),d.copy(S(a.params.max));break;case"randomDirection":{const e=x(a.params.speed,1);m.setScalar(-e),d.setScalar(e);break}case"radialVelocity":H.copy(S(a.params.direction)).normalize(),K=x(a.params.speed,1),_=e.MathUtils.degToRad(x(a.params.spread,0)),ee=!0;break;case"mass":f=Math.max(1e-6,x(a.params.min,1));break;case"positionPoint":c.copy(new e.Vector4(...S(a.params.position).toArray(),0));break;case"positionSphere":{l=x(a.params.radius,1);const e=S(a.params.position);p=1,c.set(e.x,e.y,e.z,!0===a.params.surface?.value?1:0);break}case"positionBox":{const e=S(a.params.position),t=S(a.params.dimensions);p=2,c.set(e.x,e.y,e.z,0),u.set(t.x,t.y,t.z,0);break}case"positionLine":{const e=S(a.params.a),t=S(a.params.b);p=3,c.set(e.x,e.y,e.z,0),u.set(t.x,t.y,t.z,0);break}case"positionCircle":case"positionRing":{const t=S(a.params.position),r=S(a.params.rotation).multiplyScalar(Math.PI/180);l=x(a.params.radius,1);const s="positionRing"===a.type?Math.max(x(a.params.thickness,.2),1e-4):0;p=4,c.set(t.x,t.y,t.z,s),u.set(r.x,r.y,r.z,e.MathUtils.degToRad(Math.max(0,Math.min(360,x(a.params.angle,360)))));break}case"positionMesh":{const r=a.params.mesh?.value,o=a.params.mesh?.binding;if("vfxInput"===o?.type&&(ve=o.inputId),("string"!=typeof r||0===r.length)&&null==ve){w(s,t,"error","mesh-sampler-source","GPU mesh sampling requires a selected fallback mesh asset");break}if(xe="string"==typeof r&&r.length>0?r:void 0,Ge="vertices"===a.params.sampling?.value?"vertices":"surface",Se=(new e.Matrix4).compose(S(a.params.position),(new e.Quaternion).setFromEuler((new e.Euler).setFromVector3(S(a.params.rotation).multiplyScalar(Math.PI/180))),null==a.params.scale?new e.Vector3(1,1,1):S(a.params.scale)),!0===a.params.alignNormal?.value){const t=a.params.alignmentAxis?.value;Me="+X"===t?new e.Vector3(1,0,0):"-X"===t?new e.Vector3(-1,0,0):"-Y"===t?new e.Vector3(0,-1,0):"+Z"===t?new e.Vector3(0,0,1):"-Z"===t?new e.Vector3(0,0,-1):new e.Vector3(0,1,0)}!0===a.params.normalVelocity?.value&&(ke=new e.Vector4(x(a.params.normalVelocityMin,1),x(a.params.normalVelocityMax,1),e.MathUtils.degToRad(e.MathUtils.clamp(x(a.params.normalVelocitySpread,0),0,180)),1)),p=5;break}case"scale":{const e=x(a.params.scale,1);T=[e,e];break}case"scaleComponents":be.copy(S(a.params.scale));break;case"randomScale":T=[x(a.params.min,1),x(a.params.max,1)];break;case"randomColor":j=M(a.params.a,j),F=M(a.params.b,j),Y=!0;break;case"rotation":{const t=S(a.params.rotation);D=q=e.MathUtils.degToRad(t.z),Q.set(e.MathUtils.degToRad(t.x),e.MathUtils.degToRad(t.y),e.MathUtils.degToRad(t.z)),L.setFromEuler(new e.Euler(Q.x,Q.y,Q.z));break}case"randomRotation":{if(o){const e=a.params.axis?.value;W="X"===e?2:"Y"===e?3:"Z"===e?4:1;break}const e=a.params.axis?.value;"XYZ"!==e&&"Z"!==e&&null!=e||(q=2*Math.PI);break}case"alignRotation":{if(!o)break;const e=a.params.axis?.value;"+X"===e?oe.set(1,0,0):"-X"===e?oe.set(-1,0,0):"-Y"===e?oe.set(0,-1,0):"+Z"===e?oe.set(0,0,1):"-Z"===e?oe.set(0,0,-1):oe.set(0,1,0);const t=a.params.mode?.value;ne="TowardsCenter"===t?2:"AlongVelocity"===t?3:1,ie.copy(S(a.params.center)),le.copy(S(a.params.offset)).multiplyScalar(Math.PI/180);break}default:w(s,t,"error",`initializer:${a.type}`,`initializer ${a.type} is not supported on GPU`)}for(const e of t.behaviours.filter(e=>!1!==e.enabled))switch(e.type){case"force":h.add(S(e.params.force));break;case"gravity":h.y-=x(e.params.gravity,8);break;case"linearDamping":y+=x(e.params.factor,.1);break;case"randomDrift":b.copy(S(e.params.drift)),g=Math.max(0,x(e.params.delay,0)),k=v(e.params.ease?.value),G=!0;break;case"followParent":V=x(e.params.speed,0),C=!0;break;case"moveTo":I.copy(S(e.params.target)),O=v(e.params.ease?.value),E=!0;break;case"attraction":case"repulsion":if(se){w(s,t,"error","multiple-attraction-behaviours","multiple attraction or repulsion behaviours are not supported in one GPU emitter");break}te.copy(S(e.params.target)),ae=x(e.params.force,1)*("repulsion"===e.type?-1:1),re=Math.max(0,x(e.params.radius,1)),se=!0;break;case"vortex":if(ue){w(s,t,"error","multiple-vortex-behaviours","multiple vortex behaviours are not supported in one GPU emitter");break}pe.copy(S(e.params.axis)),ce=x(e.params.amount,1),ue=!0;break;case"disperse":{if(ye){w(s,t,"error","multiple-disperse-behaviours","multiple disperse behaviours are not supported in one GPU emitter");break}me=x(e.params.distance,1);const a=e.params.axis?.value;de="XY"===a?1:"XZ"===a?2:"YZ"===a?3:0,he=v(e.params.ease?.value),ye=!0;break}case"worldCollision":U=!0,A=Math.max(0,x(e.params.bounce,0)),R=Math.max(0,Math.min(1,x(e.params.friction,0))),N=Math.max(0,x(e.params.lifeLoss,0)),B=!0===e.params.debug?.value;break;case"scale":z=x(e.params.a,1),$=x(e.params.b,1),fe.x=v(e.params.ease?.value);break;case"scaleComponents":ge.copy(be).multiply(S(e.params.a)),we.copy(be).multiply(S(e.params.b)),fe.w=v(e.params.ease?.value);break;case"changeColor":j=M(e.params.a,j),F=M(e.params.b,F),fe.y=v(e.params.ease?.value),Y=!1;break;case"changeOpacity":X=x(e.params.a,1),Z=x(e.params.b,0),fe.z=v(e.params.ease?.value);break;case"rotate":case"rotateAdd":{const t=S(e.params.rotation);t.multiplyScalar(Math.PI/180*Math.PI/180),o?J.add(t):P=(P??0)+t.z;break}default:w(s,t,"error",`behaviour:${e.type}`,`behaviour ${e.type} is not supported on GPU`)}const Ve=Math.max(0,Math.floor(t.rate.count)),Ce="sprite"!==t.output.type&&"stretchedSprite"!==t.output.type||!Number.isFinite(t.output.intensity)?1:t.output.intensity;j.multiplyScalar(Ce),F.multiplyScalar(Ce);const Ie="once"===t.rate.type?Ve:Math.max(Ve,Math.ceil(n/Math.max(t.rate.time,1e-4))*Ve),Pe=Math.min(25e4,Ie);return Pe!==Ie&&w(s,t,"warning","particle-capacity-clamped",`particle capacity ${Ie} exceeds the current GPU per-emitter limit of ${Pe}`),t.behaviours.some(e=>!1!==e.enabled&&"scaleComponents"===e.type)||(ge.copy(be),we.copy(be)),U&&"local"===t.output.space&&w(s,t,"error","world-collision-local-space","GPU screen-space world collision currently requires world-space output"),{position:(new e.Vector3).fromArray(t.position).add("local"===t.output.space?new e.Vector3:a),localSpace:"local"===t.output.space,capacity:Pe,rate:"once"===t.rate.type?{type:"once",count:Ve,delay:Math.max(0,t.rate.delay??0)}:{type:"continuous",count:Ve,interval:Math.max(t.rate.time,1e-4),delay:Math.max(0,t.rate.delay??0),duration:(t.rate.duration??0)>0?t.rate.duration:-1},life:n,lifeMin:i,positionRadius:l,spawnZoneType:p,spawnZoneA:c,spawnZoneB:u,meshSamplerAssetId:xe,meshSamplerInputId:ve,meshSamplerMode:Ge,meshSamplerTransform:Se,meshSamplerAlignAxis:Me,meshNormalVelocity:ke,velocityMin:m,velocityMax:d,force:h,damping:y,mass:f,randomDrift:b,randomDriftDelay:g,randomDriftEasing:k,hasRandomDrift:G,followParentSpeed:V,hasFollowParent:C,moveToTarget:I,moveToEasing:O,hasMoveTo:E,hasWorldCollision:U,collisionBounce:A,collisionFriction:R,collisionLifeLoss:N,collisionDebug:B,orientAlongVelocity:!1,scaleStart:[T[0]*z,T[1]*z],scaleEnd:[T[0]*$,T[1]*$],colorStart:j,colorEnd:F,randomColor:Y,alphaStart:X,alphaEnd:Z,rotationMin:D,rotationMax:q,spriteRotationStep:P,meshRotation:L,meshRotationEuler:Q,meshRotationStep:J,randomMeshRotationAxis:W,radialVelocityDirection:H,radialVelocitySpeed:K,radialVelocitySpread:_,hasRadialVelocity:ee,attractionTarget:te,attractionForce:ae,attractionRadius:re,hasAttraction:se,alignRotationAxis:oe,alignRotationMode:ne,alignRotationCenter:ie,alignRotationOffset:le,vortexAxis:pe,vortexAmount:ce,hasVortex:ue,disperseDistance:me,dispersePlane:de,disperseEasing:he,hasDisperse:ye,curveEasing:fe,meshScaleStart:ge,meshScaleEnd:we,renderClass:r}}function w(e,t,a,r,s){e.some(e=>e.emitter===t.name&&e.code===r)||e.push({severity:a,code:r,emitter:t.name||t.id,message:s})}function x(e,t){const a=e?.value;if("number"==typeof a)return Number.isFinite(a)?a:t;if("string"==typeof a&&""!==a.trim()){const e=Number(a);if(Number.isFinite(e))return e}return t}function v(e){switch(e){case"InQuad":return 1;case"OutQuad":return 2;case"InOutQuad":return 3;case"InCubic":return 4;case"OutCubic":return 5;case"InOutCubic":return 6;case"InQuart":return 7;case"OutQuart":return 8;case"InOutQuart":return 9;case"InSine":return 10;case"OutSine":return 11;case"InOutSine":return 12;case"InExpo":return 13;case"OutExpo":return 14;case"InOutExpo":return 15;case"InCirc":return 16;case"InBack":return 17;case"OutBack":return 18;case"Bump":return 19;case"BumpSharp":return 20;case"BumpFlat":return 21;case"SpikeFade":return 22;case"OutCirc":return 23;case"InOutCirc":return 24;case"InOutBack":return 25;case"BumpFade":return 26;case"BumpFall":return 27;default:return 0}}function S(t){const a=t?.value;return Array.isArray(a)?new e.Vector3(Number(a[0]),Number(a[1]),Number(a[2])):new e.Vector3}function M(t,a){const r=t?.value;if("string"==typeof r){if(null!=/^#([A-Fa-f0-9]{6})$/.exec(r))return new e.Color(r);const t=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(r);if(null!=t)return(new e.Color).setRGB(Math.min(255,Number.parseInt(t[1],10))/255,Math.min(255,Number.parseInt(t[2],10))/255,Math.min(255,Number.parseInt(t[3],10))/255,e.SRGBColorSpace)}if("number"==typeof r&&Number.isFinite(r))return new e.Color(Math.floor(r));if(null!=r&&"object"==typeof r&&"r"in r&&"g"in r&&"b"in r){const t=r;return(new e.Color).setRGB(t.r,t.g,t.b)}return a.clone()}/*
1
+ import*as e from"three";import{applyMeshWorldTransformToGeometry as t}from"./vfx-mesh-transform.js";import{attributes as a,clamp as r,lambertMaterial as s,NodeShaderMaterial as o,rgba as n,smoothstep as i,textureSampler2d as l,varyingAttributes as p}from"three-shader-graph";import{SerializedParamType as c}from"../../scene/model.js";import{particleUniforms as u}from"../../shader-nodes/particle.js";import{sampleFlipbook as m}from"../../shader-nodes/texture-sequence.js";import{fragmentLinearEyeDepth as d,linearEyeDepth as h}from"../../shader-nodes/depth.js";import{ThreeBlendingMode as y,WorldCollisionMethod as f}from"./vfx-asset.js";import{getDefaultVfxSpriteTexture as b}from"./vfx-default-sprite-texture.js";import{ShapeLibrary as g}from"../../scene/objects/shapes.js";export class GpuSpriteRenderClassCache{constructor(){this.entries=new Map}resolve(e,t){let a=this.entries.get(e);return null==a&&(a=t().catch(t=>{throw this.entries.delete(e),t}),this.entries.set(e,a)),a}clear(){this.entries.clear()}}export function hasGpuUnsupportedVfxInputBindings(e){const t=new Map((e.vfx.inputs??[]).map(e=>[e.id,e.type])),a=e=>{for(const a of[...e.initializers,...e.behaviours])if(!1!==a.enabled)for(const[r,s]of Object.entries(a.params)){if("vfxInput"!==s?.binding?.type)continue;const o=s.type===c.Number?"number":s.type===c.Vector3?"vec3":s.type===c.Color?"color":s.type===c.Object3D?"mesh":void 0;if(null==o||t.get(s.binding.inputId)!==o)return!0;if("lifetime"===a.type&&"duration"===r&&"continuous"===e.rate.type)return!0}return(e.children??[]).some(a)};return e.vfx.emitters.some(a)}export function selectVfxBackend(e,t="auto"){if("cpu"===t)return{backend:"cpu",reasons:["CPU backend explicitly requested"]};const a=e.issues.filter(e=>"requirement"===e.severity).map(e=>e.message);if(a.length>0)return{backend:"cpu",reasons:a};const r=e.issues.filter(e=>"error"===e.severity).map(e=>e.message);return r.length>0&&"gpu"!==t?{backend:"cpu",reasons:r}:r.length>0?{backend:"gpu",reasons:["GPU backend explicitly forced despite incompatibilities",...r]}:{backend:"gpu",reasons:e.issues.filter(e=>"warning"===e.severity).map(e=>e.message)}}export function compileGpuSpriteEmitters(t,a,r="emitter-major"){const s=[],o=[],n=new Map,i=[],l=[],p=new Map((t.vfx.inputs??[]).map(e=>[e.id,e.type])),u=new Map((t.vfx.inputs??[]).filter(e=>"number"===e.type).map(e=>[e.id,"number"==typeof e.defaultValue?e.defaultValue:0]));hasGpuUnsupportedVfxInputBindings(t)&&i.push({severity:"error",code:"vfx-input-binding",emitter:"effect",message:"this VFX input binding target is not supported on GPU yet"});const m=t.vfx.emitters.slice().sort((e,t)=>(e.output.renderOrder??0)-(t.output.renderOrder??0)),d=(t,r,m)=>{const h="vfxInput"===t.rate.delayBinding?.type?u.get(t.rate.delayBinding.inputId):void 0,y=null==h?t:{...t,rate:{...t.rate,delay:Math.max(0,h)}};if("sprite"!==t.output.type&&"mesh"!==t.output.type&&"stretchedSprite"!==t.output.type&&"decal"!==t.output.type&&"shape"!==t.output.type&&"trail"!==t.output.type)return void v(i,t,"error","output-type","this output type is not supported by the GPU sprite backend");if("mesh"===t.output.type&&null==t.output.assetId)return;if(t.rate.count<=0)return;const f="trail"===(b=t.output).type?JSON.stringify(b):"mesh"===b.type?JSON.stringify({type:b.type,assetId:b.assetId,renderOrder:b.renderOrder??0,useLegacyTransform:!0===b.useLegacyTransform,materialSource:b.materialSource??null,material:b.material??null,shader:b.shader??null,shaderParams:b.shaderParams??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space}):"decal"===b.type?JSON.stringify({type:b.type,colorMap:b.colorMap,alphaMap:b.alphaMap,color:b.color,unlit:b.unlit,intensity:b.intensity,opacityChannel:b.opacityChannel??"red",blendingMode:b.blendingMode,renderOrder:b.renderOrder??0,materialSource:b.materialSource??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space}):"shape"===b.type?JSON.stringify({type:b.type,shape:b.shape,params:b.params,material:b.material,renderOrder:b.renderOrder??0,materialSource:b.materialSource??null,shader:b.shader??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space}):JSON.stringify({type:b.type,texture:b.texture,opacityChannel:b.opacityChannel??"red",blendingMode:b.blendingMode,softness:b.softness,renderOrder:b.renderOrder??0,bloom:b.bloom,materialSource:b.materialSource??null,shader:b.shader??null,shaderGraph:b.shaderGraph??null,shaderGraphParams:b.shaderGraphParams??null,space:b.space,lockY:"sprite"===b.type&&!0===b.lockY,stretchScale:"stretchedSprite"===b.type?b.scale:null});var b;let g=n.get(f);null==g&&(g=o.length,n.set(f,g),o.push({key:f,output:t.output}));const x=[];for(let o=0;o<a.length;o++){const n=w(y,null==r?a[o]:new e.Vector3,g,i);if(null!=n){if(null!=r){const e=s[r]?.instances[o];if(null==e)continue;const a=n.capacity,l=e.capacity*a;if(l>25e4){v(i,t,"error","child-particle-capacity",`child emitter expansion requires ${l} particles, exceeding the GPU per-emitter limit`);continue}n.capacity=l,n.parentEmitter=r,n.childParticlesPerParent=a,n.childSpawnEvent="collision"===t.spawnEvent?2:1,n.childLevel=m,!0===n.localSpace&&!0!==e.localSpace&&v(i,t,"error","child-emitter-local-space","GPU child emitters do not yet support a world-space parent with local-space child output"),"trail"===t.output.type&&v(i,t,"error","child-emitter-trail","GPU child emitters do not yet support trail output")}x.push(n)}}if(x.length>0){const e=s.length;s.push({instances:x,parentSource:r});for(const a of[...t.initializers,...t.behaviours])if(!1!==a.enabled)for(const[r,s]of Object.entries(a.params)){const o=s?.binding;if("vfxInput"!==o?.type)continue;const n=s.type===c.Number?"number":s.type===c.Vector3?"vec3":s.type===c.Color?"color":s.type===c.Object3D?"mesh":void 0;null!=n&&p.get(o.inputId)===n&&("mesh"!==n&&("lifetime"===a.type&&"duration"===r&&"continuous"===t.rate.type||l.push({inputId:o.inputId,emitterIndex:e,target:`${t.initializers.includes(a)?"initializer":"behaviour"}:${a.type}:${r}`,authoredScalar:"number"==typeof s.value?s.value:void 0,baseScaleStart:[...x[0].scaleStart],baseScaleEnd:[...x[0].scaleEnd]})))}for(const a of t.children??[])d(a,e,m+1)}};for(const e of m)d(e,null,0);const h="instance-major"===r?a.flatMap((e,t)=>s.flatMap(e=>null==e.instances[t]?[]:[e.instances[t]])):s.flatMap(e=>e.instances);for(let e=0;e<s.length;e++){const t=s[e];if(null!=t.parentSource)for(let e=0;e<t.instances.length;e++)t.instances[e].parentEmitter="instance-major"===r?e*s.length+t.parentSource:t.parentSource*a.length+e}const y=o.map((e,t)=>({renderClass:e,oldIndex:t})).sort((e,t)=>(e.renderClass.output.renderOrder??0)-(t.renderClass.output.renderOrder??0)),f=new Array(o.length);for(let e=0;e<y.length;e++)f[y[e].oldIndex]=e;for(const e of h)e.renderClass=f[e.renderClass??0]??0;const b={compatible:!i.some(e=>"warning"!==e.severity),issues:i};return{emitters:h,inputBindings:l,renderClasses:y.map(e=>e.renderClass),compatibility:b,unsupported:[...new Set(i.map(e=>`${e.emitter}: ${e.message}`))]}}export async function materializeGpuSpriteRenderClasses(c,f,w,v){const x=async c=>{const v=c.output;if("trail"===v.type){const t=v.materialSource??("asset"===v.shaderGraph?.source?"shaderGraph":"default"),a="default"===t?null:await(w?.(v))??null;if(null==a&&"default"!==t)throw new Error(`${t} trail material requires a compatible GPU material resolver`);const r=null!=v.texture?await f.getTexture(v.texture):b(),s="repeat"===v.uvMode?r.clone():r;return"repeat"===v.uvMode&&(s.wrapS=e.RepeatWrapping,s.needsUpdate=!0),{texture:s,material:a??void 0,localSpace:"local"===v.space,trail:{historyLength:Math.max(2,Math.round(v.length)+1),width:v.width??1,taper:v.taper,colorStart:new e.Color(v.color).multiplyScalar(v.intensity),colorEnd:new e.Color(v.colorEnd).multiplyScalar(v.intensityEnd),opacityStart:v.opacityStart,opacityEnd:v.opacityEnd,scrollSpeed:v.scrollSpeed??0,uvMode:v.uvMode??"stretch",unitsPerRepeat:Math.max(.001,v.unitsPerRepeat??1),opacityChannel:v.opacityChannel??"red",blendingMode:v.blendingMode??"normal",billboard:v.billboard??!1}}}if("mesh"===v.type){if(null==f.getMesh)throw new Error("GPU mesh particles require a mesh asset provider");const a=await f.getMesh(v.assetId);a.updateWorldMatrix(!0,!0);const r=[];if(a.traverse(t=>{t instanceof e.Mesh&&r.push(t)}),1!==r.length)throw new Error(`GPU mesh particles require exactly one mesh geometry; asset ${v.assetId} resolved to ${r.length}`);const s=null==r[0].geometry.index?r[0].geometry.clone():r[0].geometry.toNonIndexed();!0!==v.useLegacyTransform&&t(r[0],s);const n=s.getAttribute("position");if(null==n)throw new Error(`GPU mesh particle asset ${v.assetId} has no position attribute`);const i=s.getAttribute("normal"),l=s.getAttribute("uv"),p=new Float32Array(8*n.count);for(let e=0;e<n.count;e++){const t=8*e;p[t]=n.getX(e),p[t+1]=n.getY(e),p[t+2]=n.getZ(e),p[t+3]=i?.getX(e)??0,p[t+4]=i?.getY(e)??1,p[t+5]=i?.getZ(e)??0,p[t+6]=l?.getX(e)??0,p[t+7]=l?.getY(e)??0}s.dispose();const c=Array.isArray(r[0].material)?r[0].material[0]:r[0].material,m="material"===v.materialSource&&null==v.material||"shaderGraph"===v.materialSource&&("asset"!==v.shaderGraph?.source||null==v.shaderGraph.assetId)||"shader"===v.materialSource&&null==v.shader?"default":v.materialSource??("asset"===v.shaderGraph?.source?"shaderGraph":null!=v.shader?"shader":null!=v.material?"material":"default"),d="default"===m?await(w?.(v))??function(){const e=u.opacity;return new o({color:u.color.rgb.rgba(e),opacity:e,transparent:!0})}():await(w?.(v))??null;if(null==d&&"default"!==m)throw new Error(`${m} mesh material requires a compatible GPU material resolver`);const h=null!=c&&"color"in c&&c.color instanceof e.Color?c.color.clone():new e.Color(16777215);return{texture:b(),meshVertices:p,meshColor:h,meshOpacity:c?.opacity??1,localSpace:"local"===v.space,material:d??void 0}}if("decal"===v.type){const t=await(w?.(v))??null;if(null==t)throw new Error("GPU decal output requires a compatible GPU decal material resolver");const a=new e.BoxGeometry(1,1,1).toNonIndexed(),r=a.getAttribute("position"),s=a.getAttribute("normal"),o=a.getAttribute("uv"),n=new Float32Array(8*r.count);for(let e=0;e<r.count;e++){const t=8*e;n[t]=r.getX(e),n[t+1]=r.getY(e),n[t+2]=r.getZ(e),n[t+3]=s.getX(e),n[t+4]=s.getY(e),n[t+5]=s.getZ(e),n[t+6]=o.getX(e),n[t+7]=o.getY(e)}return a.dispose(),{texture:b(),meshVertices:n,meshColor:new e.Color(16777215),meshOpacity:1,localSpace:"local"===v.space,material:t}}if("shape"===v.type){const t=g[v.shape];if(null==t)throw new Error(`GPU shape output has unknown shape ${v.shape}`);const a=v.materialSource??("asset"===v.shaderGraph?.source?"shaderGraph":null!=v.shader?"shader":null!=v.material?"material":"default"),r="default"===a?function(){const e=u.opacity;return new o({color:s({color:u.color.rgb}).rgb.rgba(e),opacity:e,transparent:!0})}():await(w?.(v))??null;if(null==r&&"default"!==a)throw new Error(`${a} shape material requires a compatible GPU material resolver`);const n=Object.fromEntries(Object.entries(v.params??{}).map(([e,t])=>[e,t.value])),i=t.geometry(n),l=null==i.index?i:i.toNonIndexed(),p=l.getAttribute("position"),c=l.getAttribute("normal"),m=l.getAttribute("uv"),d=new Float32Array(8*p.count);for(let e=0;e<p.count;e++){const t=8*e;d[t]=p.getX(e),d[t+1]=p.getY(e),d[t+2]=p.getZ(e),d[t+3]=c?.getX(e)??0,d[t+4]=c?.getY(e)??1,d[t+5]=c?.getZ(e)??0,d[t+6]=m?.getX(e)??0,d[t+7]=m?.getY(e)??0}return l!==i&&l.dispose(),i.dispose(),{texture:b(),meshVertices:d,meshColor:new e.Color(16777215),meshOpacity:1,localSpace:"local"===v.space,material:r??void 0}}const x=null!=v.texture?await f.getTexture(v.texture):b(),S="stretchedSprite"===v.type?"default":v.materialSource??("asset"===v.shaderGraph?.source?"shaderGraph":"default");let M="default"===S?null:await(w?.(v))??null;if(null==M&&"default"!==S)throw new Error(`${S} material requires a compatible GPU material resolver`);return M??(M=function(t,s){const c=l(s),f="sprite"===t.type&&!0===t.flipbook?.enabled?m(c,a.uv,t.flipbook.columns,t.flipbook.rows,u.time,t.flipbook.fps,t.flipbook.mode):c.sample(p.uv);let b=u.opacity;"red"===(t.opacityChannel??"red")?b=b.multiply(f.r):"alpha"===t.opacityChannel&&(b=b.multiply(f.a));if(t.softness>0){const e=h.subtract(d).divide(h);b=b.multiply(i(0,.2*t.softness,r(e,0,1e3)))}const g=new o({color:n(u.color.multiply(f.rgb),b),transparent:!0,lights:!1,fog:!1,alphaTest:1e-4});return g.blending=y[t.blendingMode]??e.NormalBlending,g.depthWrite=!1,g}(v,x)),M.transparent&&(M.depthWrite=!1),{texture:x,material:M,lockY:"sprite"===v.type&&!0===v.lockY,localSpace:"local"===v.space,stretchScale:"stretchedSprite"===v.type?0===v.scale?.001:v.scale:void 0}};return{renderClasses:await Promise.all(c.map(e=>v?.resolve(e.key,()=>x(e))??x(e))),unsupported:[]}}function w(t,a,r,s){if("sprite"!==t.output.type&&"mesh"!==t.output.type&&"decal"!==t.output.type&&"shape"!==t.output.type&&"trail"!==t.output.type&&"stretchedSprite"!==t.output.type)return null;const o="mesh"===t.output.type||"decal"===t.output.type||"shape"===t.output.type;let n=99999,i=99999,l=0,p=0;const c=new e.Vector4,u=new e.Vector4,m=new e.Vector3,d=new e.Vector3,h=new e.Vector3;let y=0,b=1;const g=new e.Vector3;let w=0,G=0,V=!1,C=0,P=!1;const I=new e.Vector3;let O,E=0,U=!1,R=!1,A=0,N=0,B=0,T=!1,z=[1,1],$=1,j=1,F=new e.Color("sprite"===t.output.type||"stretchedSprite"===t.output.type?t.output.color:"#ffffff"),Y=F.clone(),X=!1,Z=1,q=1,D=0,L=0;const Q=new e.Quaternion,J=new e.Vector3,W=new e.Vector3;let H=0;const K=new e.Vector3(0,1,0);let _=0,ee=0,te=!1;const ae=new e.Vector3;let re=0,se=0,oe=!1;const ne=new e.Vector3(0,1,0);let ie=0;const le=new e.Vector3,pe=new e.Vector3,ce=new e.Vector3(0,1,0);let ue=0,me=!1,de=0,he=0,ye=0,fe=!1;const be=new e.Vector4(0,0,0,0),ge=new e.Vector3(1,1,1),we=new e.Vector3(1,1,1),ve=new e.Vector3(1,1,1);let xe,Se,Me,ke,Ge,Ve="surface";for(const a of t.initializers.filter(e=>!1!==e.enabled))switch(a.type){case"lifetime":i=n=x(a.params.duration,1);break;case"randomLifetime":{const e=x(a.params.minDuration,1),t=x(a.params.maxDuration,2);i=Math.min(e,t),n=Math.max(e,t);break}case"velocity":{const e=M(a.params.velocity);m.copy(e),d.copy(e);break}case"randomVelocity":m.copy(M(a.params.min)),d.copy(M(a.params.max));break;case"randomDirection":{const e=x(a.params.speed,1);m.setScalar(-e),d.setScalar(e);break}case"radialVelocity":K.copy(M(a.params.direction)).normalize(),_=x(a.params.speed,1),ee=e.MathUtils.degToRad(x(a.params.spread,0)),te=!0;break;case"mass":b=Math.max(1e-6,x(a.params.min,1));break;case"positionPoint":c.copy(new e.Vector4(...M(a.params.position).toArray(),0));break;case"positionSphere":{l=x(a.params.radius,1);const e=M(a.params.position);p=1,c.set(e.x,e.y,e.z,!0===a.params.surface?.value?1:0);break}case"positionBox":{const e=M(a.params.position),t=M(a.params.dimensions);p=2,c.set(e.x,e.y,e.z,0),u.set(t.x,t.y,t.z,0);break}case"positionLine":{const e=M(a.params.a),t=M(a.params.b);p=3,c.set(e.x,e.y,e.z,0),u.set(t.x,t.y,t.z,0);break}case"positionCircle":case"positionRing":{const t=M(a.params.position),r=M(a.params.rotation).multiplyScalar(Math.PI/180);l=x(a.params.radius,1);const s="positionRing"===a.type?Math.max(x(a.params.thickness,.2),1e-4):0;p=4,c.set(t.x,t.y,t.z,s),u.set(r.x,r.y,r.z,e.MathUtils.degToRad(Math.max(0,Math.min(360,x(a.params.angle,360)))));break}case"positionMesh":{const r=a.params.mesh?.value,o=a.params.mesh?.binding;if("vfxInput"===o?.type&&(Se=o.inputId),("string"!=typeof r||0===r.length)&&null==Se){v(s,t,"error","mesh-sampler-source","GPU mesh sampling requires a selected fallback mesh asset");break}if(xe="string"==typeof r&&r.length>0?r:void 0,Ve="vertices"===a.params.sampling?.value?"vertices":"surface",Me=(new e.Matrix4).compose(M(a.params.position),(new e.Quaternion).setFromEuler((new e.Euler).setFromVector3(M(a.params.rotation).multiplyScalar(Math.PI/180))),null==a.params.scale?new e.Vector3(1,1,1):M(a.params.scale)),!0===a.params.alignNormal?.value){const t=a.params.alignmentAxis?.value;ke="+X"===t?new e.Vector3(1,0,0):"-X"===t?new e.Vector3(-1,0,0):"-Y"===t?new e.Vector3(0,-1,0):"+Z"===t?new e.Vector3(0,0,1):"-Z"===t?new e.Vector3(0,0,-1):new e.Vector3(0,1,0)}!0===a.params.normalVelocity?.value&&(Ge=new e.Vector4(x(a.params.normalVelocityMin,1),x(a.params.normalVelocityMax,1),e.MathUtils.degToRad(e.MathUtils.clamp(x(a.params.normalVelocitySpread,0),0,180)),1)),p=5;break}case"scale":{const e=x(a.params.scale,1);z=[e,e];break}case"scaleComponents":ge.copy(M(a.params.scale));break;case"randomScale":z=[x(a.params.min,1),x(a.params.max,1)];break;case"randomColor":F=k(a.params.a,F),Y=k(a.params.b,F),X=!0;break;case"rotation":{const t=M(a.params.rotation);D=L=e.MathUtils.degToRad(t.z),J.set(e.MathUtils.degToRad(t.x),e.MathUtils.degToRad(t.y),e.MathUtils.degToRad(t.z)),Q.setFromEuler(new e.Euler(J.x,J.y,J.z));break}case"randomRotation":{if(o){const e=a.params.axis?.value;H="X"===e?2:"Y"===e?3:"Z"===e?4:1;break}const e=a.params.axis?.value;"XYZ"!==e&&"Z"!==e&&null!=e||(L=2*Math.PI);break}case"alignRotation":{if(!o)break;const e=a.params.axis?.value;"+X"===e?ne.set(1,0,0):"-X"===e?ne.set(-1,0,0):"-Y"===e?ne.set(0,-1,0):"+Z"===e?ne.set(0,0,1):"-Z"===e?ne.set(0,0,-1):ne.set(0,1,0);const t=a.params.mode?.value;ie="TowardsCenter"===t?2:"AlongVelocity"===t?3:1,le.copy(M(a.params.center)),pe.copy(M(a.params.offset)).multiplyScalar(Math.PI/180);break}default:v(s,t,"error",`initializer:${a.type}`,`initializer ${a.type} is not supported on GPU`)}for(const e of t.behaviours.filter(e=>!1!==e.enabled))switch(e.type){case"force":h.add(M(e.params.force));break;case"gravity":h.y-=x(e.params.gravity,8);break;case"linearDamping":y+=x(e.params.factor,.1);break;case"randomDrift":g.copy(M(e.params.drift)),w=Math.max(0,x(e.params.delay,0)),G=S(e.params.ease?.value),V=!0;break;case"followParent":C=x(e.params.speed,0),P=!0;break;case"moveTo":I.copy(M(e.params.target)),E=S(e.params.ease?.value),U=!0;break;case"attraction":case"repulsion":if(oe){v(s,t,"error","multiple-attraction-behaviours","multiple attraction or repulsion behaviours are not supported in one GPU emitter");break}ae.copy(M(e.params.target)),re=x(e.params.force,1)*("repulsion"===e.type?-1:1),se=Math.max(0,x(e.params.radius,1)),oe=!0;break;case"vortex":if(me){v(s,t,"error","multiple-vortex-behaviours","multiple vortex behaviours are not supported in one GPU emitter");break}ce.copy(M(e.params.axis)),ue=x(e.params.amount,1),me=!0;break;case"disperse":{if(fe){v(s,t,"error","multiple-disperse-behaviours","multiple disperse behaviours are not supported in one GPU emitter");break}de=x(e.params.distance,1);const a=e.params.axis?.value;he="XY"===a?1:"XZ"===a?2:"YZ"===a?3:0,ye=S(e.params.ease?.value),fe=!0;break}case"worldCollision":if(e.params.method?.value===f.PhysicsRaycast){v(s,t,"requirement","physics-raycast-collision","Physics raycast world collision requests CPU particle simulation");break}R=!0,A=Math.max(0,x(e.params.bounce,0)),N=Math.max(0,Math.min(1,x(e.params.friction,0))),B=Math.max(0,x(e.params.lifeLoss,0)),T=!0===e.params.debug?.value;break;case"scale":$=x(e.params.a,1),j=x(e.params.b,1),be.x=S(e.params.ease?.value);break;case"scaleComponents":we.copy(ge).multiply(M(e.params.a)),ve.copy(ge).multiply(M(e.params.b)),be.w=S(e.params.ease?.value);break;case"changeColor":F=k(e.params.a,F),Y=k(e.params.b,Y),be.y=S(e.params.ease?.value),X=!1;break;case"changeOpacity":Z=x(e.params.a,1),q=x(e.params.b,0),be.z=S(e.params.ease?.value);break;case"rotate":case"rotateAdd":{const t=M(e.params.rotation);t.multiplyScalar(Math.PI/180*Math.PI/180),o?W.add(t):O=(O??0)+t.z;break}default:v(s,t,"error",`behaviour:${e.type}`,`behaviour ${e.type} is not supported on GPU`)}const Ce=Math.max(0,Math.floor(t.rate.count)),Pe="sprite"!==t.output.type&&"stretchedSprite"!==t.output.type||!Number.isFinite(t.output.intensity)?1:t.output.intensity;F.multiplyScalar(Pe),Y.multiplyScalar(Pe);const Ie="once"===t.rate.type?Ce:Math.max(Ce,Math.ceil(n/Math.max(t.rate.time,1e-4))*Ce),Oe=Math.min(25e4,Ie);return Oe!==Ie&&v(s,t,"warning","particle-capacity-clamped",`particle capacity ${Ie} exceeds the current GPU per-emitter limit of ${Oe}`),t.behaviours.some(e=>!1!==e.enabled&&"scaleComponents"===e.type)||(we.copy(ge),ve.copy(ge)),R&&"local"===t.output.space&&v(s,t,"error","world-collision-local-space","GPU screen-space world collision currently requires world-space output"),{position:(new e.Vector3).fromArray(t.position).add("local"===t.output.space?new e.Vector3:a),localSpace:"local"===t.output.space,capacity:Oe,rate:"once"===t.rate.type?{type:"once",count:Ce,delay:Math.max(0,t.rate.delay??0)}:{type:"continuous",count:Ce,interval:Math.max(t.rate.time,1e-4),delay:Math.max(0,t.rate.delay??0),duration:(t.rate.duration??0)>0?t.rate.duration:-1},life:n,lifeMin:i,positionRadius:l,spawnZoneType:p,spawnZoneA:c,spawnZoneB:u,meshSamplerAssetId:xe,meshSamplerInputId:Se,meshSamplerMode:Ve,meshSamplerTransform:Me,meshSamplerAlignAxis:ke,meshNormalVelocity:Ge,velocityMin:m,velocityMax:d,force:h,damping:y,mass:b,randomDrift:g,randomDriftDelay:w,randomDriftEasing:G,hasRandomDrift:V,followParentSpeed:C,hasFollowParent:P,moveToTarget:I,moveToEasing:E,hasMoveTo:U,hasWorldCollision:R,collisionBounce:A,collisionFriction:N,collisionLifeLoss:B,collisionDebug:T,orientAlongVelocity:!1,scaleStart:[z[0]*$,z[1]*$],scaleEnd:[z[0]*j,z[1]*j],colorStart:F,colorEnd:Y,randomColor:X,alphaStart:Z,alphaEnd:q,rotationMin:D,rotationMax:L,spriteRotationStep:O,meshRotation:Q,meshRotationEuler:J,meshRotationStep:W,randomMeshRotationAxis:H,radialVelocityDirection:K,radialVelocitySpeed:_,radialVelocitySpread:ee,hasRadialVelocity:te,attractionTarget:ae,attractionForce:re,attractionRadius:se,hasAttraction:oe,alignRotationAxis:ne,alignRotationMode:ie,alignRotationCenter:le,alignRotationOffset:pe,vortexAxis:ce,vortexAmount:ue,hasVortex:me,disperseDistance:de,dispersePlane:he,disperseEasing:ye,hasDisperse:fe,curveEasing:be,meshScaleStart:we,meshScaleEnd:ve,renderClass:r}}export function requiresCpuVfxBackend(e){const t=e=>e.behaviours.some(e=>!1!==e.enabled&&"worldCollision"===e.type&&e.params.method?.value===f.PhysicsRaycast)||(e.children??[]).some(t);return e.vfx.emitters.some(t)}function v(e,t,a,r,s){e.some(e=>e.emitter===t.name&&e.code===r)||e.push({severity:a,code:r,emitter:t.name||t.id,message:s})}function x(e,t){const a=e?.value;if("number"==typeof a)return Number.isFinite(a)?a:t;if("string"==typeof a&&""!==a.trim()){const e=Number(a);if(Number.isFinite(e))return e}return t}function S(e){switch(e){case"InQuad":return 1;case"OutQuad":return 2;case"InOutQuad":return 3;case"InCubic":return 4;case"OutCubic":return 5;case"InOutCubic":return 6;case"InQuart":return 7;case"OutQuart":return 8;case"InOutQuart":return 9;case"InSine":return 10;case"OutSine":return 11;case"InOutSine":return 12;case"InExpo":return 13;case"OutExpo":return 14;case"InOutExpo":return 15;case"InCirc":return 16;case"InBack":return 17;case"OutBack":return 18;case"Bump":return 19;case"BumpSharp":return 20;case"BumpFlat":return 21;case"SpikeFade":return 22;case"OutCirc":return 23;case"InOutCirc":return 24;case"InOutBack":return 25;case"BumpFade":return 26;case"BumpFall":return 27;default:return 0}}function M(t){const a=t?.value;return Array.isArray(a)?new e.Vector3(Number(a[0]),Number(a[1]),Number(a[2])):new e.Vector3}function k(t,a){const r=t?.value;if("string"==typeof r){if(null!=/^#([A-Fa-f0-9]{6})$/.exec(r))return new e.Color(r);const t=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(r);if(null!=t)return(new e.Color).setRGB(Math.min(255,Number.parseInt(t[1],10))/255,Math.min(255,Number.parseInt(t[2],10))/255,Math.min(255,Number.parseInt(t[3],10))/255,e.SRGBColorSpace)}if("number"==typeof r&&Number.isFinite(r))return new e.Color(Math.floor(r));if(null!=r&&"object"==typeof r&&"r"in r&&"g"in r&&"b"in r){const t=r;return(new e.Color).setRGB(t.r,t.g,t.b)}return a.clone()}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -141,6 +141,8 @@ export type GpuSpriteRenderClass = {
141
141
  opacityStart: number;
142
142
  opacityEnd: number;
143
143
  scrollSpeed: number;
144
+ uvMode: 'stretch' | 'repeat';
145
+ unitsPerRepeat: number;
144
146
  opacityChannel: 'red' | 'alpha' | 'none';
145
147
  blendingMode: string;
146
148
  /** Camera-facing ribbons store positions only; oriented ribbons also store a side vector per sample. */
@@ -175,7 +177,7 @@ export declare const GPU_SPRITE_EMITTER_BYTES: number;
175
177
  export declare const GPU_SPRITE_BASE_COMMAND_BYTES: number;
176
178
  export declare const GPU_SPRITE_TRANSFORM_COMMAND_BYTES: number;
177
179
  /** WGSL RenderClass: five 16-byte vectors. Exported to guard its CPU packing ABI. */
178
- export declare const GPU_TRAIL_RENDER_CLASS_BYTES = 80;
180
+ export declare const GPU_TRAIL_RENDER_CLASS_BYTES = 96;
179
181
  export type GpuSpriteOptionalStateLayout = Readonly<{
180
182
  vortexOffset: number;
181
183
  disperseOffset: number;