@hology/core 0.0.245 → 0.0.247
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/dist/effects/vfx/gpu-sprite-compiler.js +1 -1
- package/dist/effects/vfx/gpu-sprite-runtime.d.ts +18 -1
- package/dist/effects/vfx/gpu-sprite-runtime.js +1 -1
- package/dist/effects/vfx/gpu-sprite-template.d.ts +1 -0
- package/dist/effects/vfx/gpu-sprite-template.js +1 -1
- package/dist/effects/vfx/gpu-sprite-world-runtime.d.ts +2 -1
- package/dist/effects/vfx/gpu-sprite-world-runtime.js +1 -1
- package/dist/effects/vfx/gpu-vfx-world-service.d.ts +7 -1
- package/dist/effects/vfx/gpu-vfx-world-service.js +1 -1
- package/dist/effects/vfx/initializsers.d.ts +15 -0
- package/dist/effects/vfx/initializsers.js +1 -1
- package/dist/effects/vfx/vfx-actor.d.ts +2 -0
- package/dist/effects/vfx/vfx-actor.js +1 -1
- package/dist/effects/vfx/vfx-asset.d.ts +7 -3
- package/dist/effects/vfx/vfx-asset.js +1 -1
- package/dist/effects/vfx/vfx-binding-runtime.d.ts +1 -1
- package/dist/effects/vfx/vfx-binding-runtime.js +1 -1
- package/dist/effects/vfx/vfx-defs.d.ts +53 -3
- package/dist/effects/vfx/vfx-defs.js +1 -1
- package/dist/effects/vfx/vfx-input-runtime.d.ts +2 -2
- package/dist/effects/vfx/vfx-input-runtime.js +1 -1
- package/dist/effects/vfx/vfx-materializer.js +1 -1
- package/dist/effects/vfx/vfx-mesh-sampler.d.ts +25 -0
- package/dist/effects/vfx/vfx-mesh-sampler.js +4 -0
- package/dist/gameplay/actors/builtin/components/index.d.ts +2 -0
- package/dist/gameplay/actors/builtin/components/index.js +1 -1
- package/dist/gameplay/actors/builtin/components/spline-component.d.ts +65 -0
- package/dist/gameplay/actors/builtin/components/spline-component.js +4 -0
- package/dist/gameplay/actors/builtin/index.d.ts +6 -0
- package/dist/gameplay/actors/builtin/index.js +1 -1
- package/dist/gameplay/actors/builtin/navmesh-actor.js +1 -1
- package/dist/gameplay/actors/builtin/spline-actor.d.ts +6 -0
- package/dist/gameplay/actors/builtin/spline-actor.js +4 -0
- package/dist/gameplay/actors/builtin/water-spline-volume-actor.d.ts +13 -0
- package/dist/gameplay/actors/builtin/water-spline-volume-actor.js +4 -0
- package/dist/gameplay/actors/builtin/water-volume-actor.d.ts +8 -0
- package/dist/gameplay/actors/builtin/water-volume-actor.js +4 -0
- package/dist/gameplay/actors/index.d.ts +4 -0
- package/dist/gameplay/actors/index.js +1 -1
- package/dist/gameplay/ai/build-tile.d.ts +3 -1
- package/dist/gameplay/ai/build-tile.js +1 -1
- package/dist/gameplay/ai/dynamic-tiled-navmesh.d.ts +4 -21
- package/dist/gameplay/ai/dynamic-tiled-navmesh.js +1 -1
- package/dist/gameplay/ai/dynamic-tiled-navmesh.worker.d.ts +10 -1
- package/dist/gameplay/ai/dynamic-tiled-navmesh.worker.js +1 -1
- package/dist/gameplay/ai/index.d.ts +1 -0
- package/dist/gameplay/ai/index.js +1 -1
- package/dist/gameplay/ai/navigation-areas.d.ts +22 -0
- package/dist/gameplay/ai/navigation-areas.js +4 -0
- package/dist/gameplay/ai/navigation.d.ts +28 -4
- package/dist/gameplay/ai/navigation.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/scene/materializer.d.ts +3 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/shader/graph/compiler.js +1 -1
- package/dist/shader/graph/model.d.ts +1 -1
- package/dist/shader/graph/registry.js +1 -1
- package/dist/test/build-tile.test.js +1 -1
- package/dist/test/gpu-sprite-compiler.test.js +1 -1
- package/dist/test/navmesh-worker-pool.test.js +1 -1
- package/dist/test/node-material-webgpu-resolver.test.js +1 -1
- package/dist/test/shader-graph.test.js +1 -1
- package/dist/test/spline-component.test.d.ts +2 -0
- package/dist/test/spline-component.test.js +4 -0
- package/dist/test/vfx-mesh-sampler.test.d.ts +2 -0
- package/dist/test/vfx-mesh-sampler.test.js +4 -0
- package/dist/test/water-spline-volume-actor.test.d.ts +2 -0
- package/dist/test/water-spline-volume-actor.test.js +4 -0
- package/dist/test/worker-task-executor.test.d.ts +2 -0
- package/dist/test/worker-task-executor.test.js +4 -0
- package/dist/worker/index.d.ts +11 -2
- package/dist/worker/index.js +1 -1
- package/dist/worker/task-executor.d.ts +22 -0
- package/dist/worker/task-executor.js +4 -0
- package/dist/worker/task-worker.d.ts +3 -0
- package/dist/worker/task-worker.js +4 -0
- package/dist/worker/task.d.ts +39 -0
- package/dist/worker/task.js +4 -0
- package/package.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -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 o,NodeShaderMaterial as s,rgba as n,smoothstep as i,textureSampler2d as l,varyingAttributes as c}from"three-shader-graph";import{SerializedParamType as p}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,o]of Object.entries(a.params)){if("vfxInput"!==o?.binding?.type)continue;const s=o.type===p.Number?"number":o.type===p.Vector3?"vec3":o.type===p.Color?"color":void 0;if(null==s||t.get(o.binding.inputId)!==s)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 o=[],s=[],n=new Map,i=[],l=[],c=new Map((t.vfx.inputs??[]).map(e=>[e.id,e.type]));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 u=t.vfx.emitters.slice().sort((e,t)=>(e.output.renderOrder??0)-(t.output.renderOrder??0)),m=(t,r,u)=>{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 d="trail"===(h=t.output).type?JSON.stringify(h):"mesh"===h.type?JSON.stringify({type:h.type,assetId:h.assetId,renderOrder:h.renderOrder??0,useLegacyTransform:!0===h.useLegacyTransform,materialSource:h.materialSource??null,material:h.material??null,shader:h.shader??null,shaderParams:h.shaderParams??null,shaderGraph:h.shaderGraph??null,shaderGraphParams:h.shaderGraphParams??null,space:h.space}):"decal"===h.type?JSON.stringify({type:h.type,colorMap:h.colorMap,alphaMap:h.alphaMap,color:h.color,unlit:h.unlit,intensity:h.intensity,opacityChannel:h.opacityChannel??"red",blendingMode:h.blendingMode,renderOrder:h.renderOrder??0,materialSource:h.materialSource??null,shaderGraph:h.shaderGraph??null,shaderGraphParams:h.shaderGraphParams??null,space:h.space}):"shape"===h.type?JSON.stringify({type:h.type,shape:h.shape,params:h.params,material:h.material,renderOrder:h.renderOrder??0,materialSource:h.materialSource??null,shader:h.shader??null,shaderGraph:h.shaderGraph??null,shaderGraphParams:h.shaderGraphParams??null,space:h.space}):JSON.stringify({type:h.type,texture:h.texture,opacityChannel:h.opacityChannel??"red",blendingMode:h.blendingMode,softness:h.softness,renderOrder:h.renderOrder??0,bloom:h.bloom,materialSource:h.materialSource??null,shader:h.shader??null,shaderGraph:h.shaderGraph??null,shaderGraphParams:h.shaderGraphParams??null,space:h.space,lockY:"sprite"===h.type&&!0===h.lockY,stretchScale:"stretchedSprite"===h.type?h.scale:null});var h;let y=n.get(d);null==y&&(y=s.length,n.set(d,y),s.push({key:d,output:t.output}));const f=[];for(let s=0;s<a.length;s++){const n=g(t,null==r?a[s]:new e.Vector3,y,i);if(null!=n){if(null!=r){const e=o[r]?.instances[s];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=u,!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")}f.push(n)}}if(f.length>0){const e=o.length;o.push({instances:f,parentSource:r});for(const a of[...t.initializers,...t.behaviours])if(!1!==a.enabled)for(const[r,o]of Object.entries(a.params)){const s=o?.binding;if("vfxInput"!==s?.type)continue;const n=o.type===p.Number?"number":o.type===p.Vector3?"vec3":o.type===p.Color?"color":void 0;null!=n&&c.get(s.inputId)===n&&("lifetime"===a.type&&"duration"===r&&"continuous"===t.rate.type||l.push({inputId:s.inputId,emitterIndex:e,target:`${t.initializers.includes(a)?"initializer":"behaviour"}:${a.type}:${r}`,authoredScalar:"number"==typeof o.value?o.value:void 0,baseScaleStart:[...f[0].scaleStart],baseScaleEnd:[...f[0].scaleEnd]}))}for(const a of t.children??[])m(a,e,u+1)}};for(const e of u)m(e,null,0);const d="instance-major"===r?a.flatMap((e,t)=>o.flatMap(e=>null==e.instances[t]?[]:[e.instances[t]])):o.flatMap(e=>e.instances);for(let e=0;e<o.length;e++){const t=o[e];if(null!=t.parentSource)for(let e=0;e<t.instances.length;e++)t.instances[e].parentEmitter="instance-major"===r?e*o.length+t.parentSource:t.parentSource*a.length+e}const h=s.map((e,t)=>({renderClass:e,oldIndex:t})).sort((e,t)=>(e.renderClass.output.renderOrder??0)-(t.renderClass.output.renderOrder??0)),y=new Array(s.length);for(let e=0;e<h.length;e++)y[h[e].oldIndex]=e;for(const e of d)e.renderClass=y[e.renderClass??0]??0;const f={compatible:!i.some(e=>"error"===e.severity),issues:i};return{emitters:d,inputBindings:l,renderClasses:h.map(e=>e.renderClass),compatibility:f,unsupported:[...new Set(i.map(e=>`${e.emitter}: ${e.message}`))]}}export async function materializeGpuSpriteRenderClasses(p,g,w,x){const v=async p=>{const x=p.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 o=null==r[0].geometry.index?r[0].geometry.clone():r[0].geometry.toNonIndexed();!0!==x.useLegacyTransform&&t(r[0],o);const n=o.getAttribute("position");if(null==n)throw new Error(`GPU mesh particle asset ${x.assetId} has no position attribute`);const i=o.getAttribute("normal"),l=o.getAttribute("uv"),c=new Float32Array(8*n.count);for(let e=0;e<n.count;e++){const t=8*e;c[t]=n.getX(e),c[t+1]=n.getY(e),c[t+2]=n.getZ(e),c[t+3]=i?.getX(e)??0,c[t+4]=i?.getY(e)??1,c[t+5]=i?.getZ(e)??0,c[t+6]=l?.getX(e)??0,c[t+7]=l?.getY(e)??0}o.dispose();const p=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 s({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!=p&&"color"in p&&p.color instanceof e.Color?p.color.clone():new e.Color(16777215);return{texture:f(),meshVertices:c,meshColor:h,meshOpacity:p?.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"),o=a.getAttribute("normal"),s=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]=o.getX(e),n[t+4]=o.getY(e),n[t+5]=o.getZ(e),n[t+6]=s.getX(e),n[t+7]=s.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 s({color:o({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(),c=l.getAttribute("position"),p=l.getAttribute("normal"),m=l.getAttribute("uv"),d=new Float32Array(8*c.count);for(let e=0;e<c.count;e++){const t=8*e;d[t]=c.getX(e),d[t+1]=c.getY(e),d[t+2]=c.getZ(e),d[t+3]=p?.getX(e)??0,d[t+4]=p?.getY(e)??1,d[t+5]=p?.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,o){const p=l(o),f="sprite"===t.type&&!0===t.flipbook?.enabled?m(p,a.uv,t.flipbook.columns,t.flipbook.rows,u.time,t.flipbook.fps,t.flipbook.mode):p.sample(c.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 s({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(p.map(e=>x?.resolve(e.key,()=>v(e))??v(e))),unsupported:[]}}function g(t,a,r,o){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 s="mesh"===t.output.type||"decal"===t.output.type||"shape"===t.output.type;let n=99999,i=99999,l=0,c=0;const p=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,C=0,P=!1;const I=new e.Vector3;let V,O=0,E=!1,U=!1,A=0,R=0,N=0,z=!1,B=[1,1],$=1,T=1,Y=new e.Color("sprite"===t.output.type||"stretchedSprite"===t.output.type?t.output.color:"#ffffff"),j=Y.clone(),F=!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,oe=!1;const se=new e.Vector3(0,1,0);let ne=0;const ie=new e.Vector3,le=new e.Vector3,ce=new e.Vector3(0,1,0);let pe=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);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":p.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);c=1,p.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);c=2,p.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);c=3,p.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 o="positionRing"===a.type?Math.max(x(a.params.thickness,.2),1e-4):0;c=4,p.set(t.x,t.y,t.z,o),u.set(r.x,r.y,r.z,e.MathUtils.degToRad(Math.max(0,Math.min(360,x(a.params.angle,360)))));break}case"scale":{const e=x(a.params.scale,1);B=[e,e];break}case"scaleComponents":be.copy(S(a.params.scale));break;case"randomScale":B=[x(a.params.min,1),x(a.params.max,1)];break;case"randomColor":Y=M(a.params.a,Y),j=M(a.params.b,Y),F=!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(s){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(!s)break;const e=a.params.axis?.value;"+X"===e?se.set(1,0,0):"-X"===e?se.set(-1,0,0):"-Y"===e?se.set(0,-1,0):"+Z"===e?se.set(0,0,1):"-Z"===e?se.set(0,0,-1):se.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(o,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":C=x(e.params.speed,0),P=!0;break;case"moveTo":I.copy(S(e.params.target)),O=v(e.params.ease?.value),E=!0;break;case"attraction":case"repulsion":if(oe){w(o,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)),oe=!0;break;case"vortex":if(ue){w(o,t,"error","multiple-vortex-behaviours","multiple vortex behaviours are not supported in one GPU emitter");break}ce.copy(S(e.params.axis)),pe=x(e.params.amount,1),ue=!0;break;case"disperse":{if(ye){w(o,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)),z=!0===e.params.debug?.value;break;case"scale":$=x(e.params.a,1),T=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":Y=M(e.params.a,Y),j=M(e.params.b,j),fe.y=v(e.params.ease?.value),F=!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),s?J.add(t):V=(V??0)+t.z;break}default:w(o,t,"error",`behaviour:${e.type}`,`behaviour ${e.type} is not supported on GPU`)}const xe=Math.max(0,Math.floor(t.rate.count)),ve="sprite"!==t.output.type&&"stretchedSprite"!==t.output.type||!Number.isFinite(t.output.intensity)?1:t.output.intensity;Y.multiplyScalar(ve),j.multiplyScalar(ve);const Se="once"===t.rate.type?xe:Math.max(xe,Math.ceil(n/Math.max(t.rate.time,1e-4))*xe),Me=Math.min(25e4,Se);return Me!==Se&&w(o,t,"warning","particle-capacity-clamped",`particle capacity ${Se} exceeds the current GPU per-emitter limit of ${Me}`),t.behaviours.some(e=>!1!==e.enabled&&"scaleComponents"===e.type)||(ge.copy(be),we.copy(be)),U&&"local"===t.output.space&&w(o,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:Me,rate:"once"===t.rate.type?{type:"once",count:xe,delay:Math.max(0,t.rate.delay??0)}:{type:"continuous",count:xe,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:c,spawnZoneA:p,spawnZoneB:u,velocityMin:m,velocityMax:d,force:h,damping:y,mass:f,randomDrift:b,randomDriftDelay:g,randomDriftEasing:k,hasRandomDrift:G,followParentSpeed:C,hasFollowParent:P,moveToTarget:I,moveToEasing:O,hasMoveTo:E,hasWorldCollision:U,collisionBounce:A,collisionFriction:R,collisionLifeLoss:N,collisionDebug:z,orientAlongVelocity:!1,scaleStart:[B[0]*$,B[1]*$],scaleEnd:[B[0]*T,B[1]*T],colorStart:Y,colorEnd:j,randomColor:F,alphaStart:X,alphaEnd:Z,rotationMin:D,rotationMax:q,spriteRotationStep:V,meshRotation:L,meshRotationEuler:Q,meshRotationStep:J,randomMeshRotationAxis:W,radialVelocityDirection:H,radialVelocitySpeed:K,radialVelocitySpread:_,hasRadialVelocity:ee,attractionTarget:te,attractionForce:ae,attractionRadius:re,hasAttraction:oe,alignRotationAxis:se,alignRotationMode:ne,alignRotationCenter:ie,alignRotationOffset:le,vortexAxis:ce,vortexAmount:pe,hasVortex:ue,disperseDistance:me,dispersePlane:de,disperseEasing:he,hasDisperse:ye,curveEasing:fe,meshScaleStart:ge,meshScaleEnd:we,renderClass:r}}function w(e,t,a,r,o){e.some(e=>e.emitter===t.name&&e.code===r)||e.push({severity:a,code:r,emitter:t.name||t.id,message:o})}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){const t=/^#([A-Fa-f0-9]{6})$/.exec(r);if(null!=t){const a=Number.parseInt(t[1],16);return(new e.Color).setRGB((a>>16&255)/255,(a>>8&255)/255,(255&a)/255)}const a=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(r);if(null!=a)return(new e.Color).setRGB(Math.min(255,Number.parseInt(a[1],10))/255,Math.min(255,Number.parseInt(a[2],10))/255,Math.min(255,Number.parseInt(a[3],10))/255)}if("number"==typeof r&&Number.isFinite(r)){const t=Math.floor(r);return(new e.Color).setRGB((t>>16&255)/255,(t>>8&255)/255,(255&t)/255)}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}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){const t=/^#([A-Fa-f0-9]{6})$/.exec(r);if(null!=t){const a=Number.parseInt(t[1],16);return(new e.Color).setRGB((a>>16&255)/255,(a>>8&255)/255,(255&a)/255)}const a=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(r);if(null!=a)return(new e.Color).setRGB(Math.min(255,Number.parseInt(a[1],10))/255,Math.min(255,Number.parseInt(a[2],10))/255,Math.min(255,Number.parseInt(a[3],10))/255)}if("number"==typeof r&&Number.isFinite(r)){const t=Math.floor(r);return(new e.Color).setRGB((t>>16&255)/255,(t>>8&255)/255,(255&t)/255)}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
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import type { RendererCustomPassContext } from '@hology/webgpu-renderer';
|
|
3
3
|
import { type CompiledNodeWebGpuParticle, type NodeShaderMaterial, type WgslExternalBindings } from 'three-shader-graph';
|
|
4
|
+
import type { VfxMeshSampler } from './vfx-mesh-sampler.js';
|
|
4
5
|
/** Shader-graph semantic contract used while compiling a GPU sprite material. */
|
|
5
6
|
export declare const gpuSpriteParticleWgslBindings: WgslExternalBindings;
|
|
6
7
|
export type GpuSpriteEmitter = {
|
|
@@ -26,6 +27,14 @@ export type GpuSpriteEmitter = {
|
|
|
26
27
|
spawnZoneType?: number;
|
|
27
28
|
spawnZoneA?: THREE.Vector4;
|
|
28
29
|
spawnZoneB?: THREE.Vector4;
|
|
30
|
+
meshSampler?: VfxMeshSampler;
|
|
31
|
+
meshSamplerAssetId?: string;
|
|
32
|
+
meshSamplerInputId?: string;
|
|
33
|
+
meshSamplerMode?: 'surface' | 'vertices';
|
|
34
|
+
meshSamplerTransform?: THREE.Matrix4;
|
|
35
|
+
meshSamplerAlignAxis?: THREE.Vector3;
|
|
36
|
+
/** x/y = speed range, z = cone radians, w = enabled. */
|
|
37
|
+
meshNormalVelocity?: THREE.Vector4;
|
|
29
38
|
velocityMin: THREE.Vector3;
|
|
30
39
|
velocityMax: THREE.Vector3;
|
|
31
40
|
force: THREE.Vector3;
|
|
@@ -172,6 +181,7 @@ export type GpuSpriteOptionalStateLayout = Readonly<{
|
|
|
172
181
|
disperseOffset: number;
|
|
173
182
|
collisionEventOffset: number;
|
|
174
183
|
trailOffset: number;
|
|
184
|
+
meshSamplerOffset: number;
|
|
175
185
|
entries: number;
|
|
176
186
|
bytes: number;
|
|
177
187
|
}>;
|
|
@@ -183,6 +193,7 @@ export declare function calculateGpuSpriteOptionalStateLayout(options: {
|
|
|
183
193
|
disperse: boolean;
|
|
184
194
|
collisionEvents?: boolean;
|
|
185
195
|
trailHistoryEntries: number;
|
|
196
|
+
meshSamplerEntries?: number;
|
|
186
197
|
}): GpuSpriteOptionalStateLayout;
|
|
187
198
|
export declare class GpuSpriteDirtyRange {
|
|
188
199
|
start: number;
|
|
@@ -250,6 +261,10 @@ export declare class GpuSpriteRuntime {
|
|
|
250
261
|
private readonly optionalDisperseOffset;
|
|
251
262
|
private readonly optionalCollisionEventOffset;
|
|
252
263
|
private readonly optionalTrailOffset;
|
|
264
|
+
private readonly optionalMeshSamplerOffset;
|
|
265
|
+
private readonly meshSamplerOffsets;
|
|
266
|
+
private readonly meshSamplerData;
|
|
267
|
+
private readonly hasSkinnedMeshSamplers;
|
|
253
268
|
private readonly optionalStateEntries;
|
|
254
269
|
constructor(emitters: readonly GpuSpriteEmitter[], textureOrClasses: THREE.Texture | readonly GpuSpriteRenderClass[], material?: NodeShaderMaterial, options?: {
|
|
255
270
|
pageSize?: number;
|
|
@@ -291,6 +306,8 @@ export declare class GpuSpriteRuntime {
|
|
|
291
306
|
dispose(): void;
|
|
292
307
|
private readGpuCounts;
|
|
293
308
|
private writeEmitterData;
|
|
309
|
+
private writeMeshSamplerData;
|
|
310
|
+
private uploadMeshSamplerSkinning;
|
|
294
311
|
private writeInitialParticleOwners;
|
|
295
312
|
private rebuildActiveParticleSchedule;
|
|
296
313
|
private uploadDirtyParticleOwners;
|
|
@@ -300,7 +317,7 @@ export declare class GpuSpriteRuntime {
|
|
|
300
317
|
}
|
|
301
318
|
/** Returns the complete render shader used for a compiled particle material. */
|
|
302
319
|
export declare function getGpuSpriteMaterialWgsl(material: CompiledNodeWebGpuParticle, meshOutput?: boolean, localSpace?: boolean): string;
|
|
303
|
-
export declare function getGpuSpriteComputeWgsl(pageSize: number, meshTransforms?: boolean, vortexState?: boolean, disperseState?: boolean, trailState?: boolean, localTrailTransforms?: boolean, randomDrift?: boolean, vortexStateOffset?: number, disperseStateOffset?: number, collision?: boolean, collisionEvents?: boolean, collisionEventOffset?: number, maximumChildLevel?: number, localChildTransforms?: boolean): string;
|
|
320
|
+
export declare function getGpuSpriteComputeWgsl(pageSize: number, meshTransforms?: boolean, vortexState?: boolean, disperseState?: boolean, trailState?: boolean, localTrailTransforms?: boolean, randomDrift?: boolean, vortexStateOffset?: number, disperseStateOffset?: number, collision?: boolean, collisionEvents?: boolean, collisionEventOffset?: number, maximumChildLevel?: number, localChildTransforms?: boolean, meshSamplerState?: boolean): string;
|
|
304
321
|
/** Returns the procedural trail shader used for a compiled custom trail graph. */
|
|
305
322
|
export declare function getGpuTrailMaterialWgsl(material: NodeShaderMaterial, localSpace?: boolean): string;
|
|
306
323
|
//# sourceMappingURL=gpu-sprite-runtime.d.ts.map
|