@hology/core 0.0.272 → 0.0.273
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-vfx-world-service.js +1 -1
- package/dist/effects/vfx/rates.d.ts +2 -0
- package/dist/effects/vfx/rates.js +1 -1
- package/dist/effects/vfx/vfx-actor.d.ts +2 -1
- package/dist/effects/vfx/vfx-actor.js +1 -1
- package/dist/effects/vfx/vfx-asset.d.ts +10 -1
- package/dist/effects/vfx/vfx-backend-partition.d.ts +8 -0
- package/dist/effects/vfx/vfx-backend-partition.js +4 -0
- package/dist/effects/vfx/vfx-materializer.js +1 -1
- package/dist/effects/vfx/vfx-render-preparation.js +1 -1
- package/dist/effects/vfx/vfx-renderers.d.ts +27 -2
- package/dist/effects/vfx/vfx-renderers.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +2 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/test/vfx-backend-partition.test.d.ts +2 -0
- package/dist/test/vfx-backend-partition.test.js +4 -0
- package/dist/test/vfx-playback-transform.test.js +1 -1
- package/dist/test/vfx-render-preparation.test.js +1 -1
- package/dist/utils/mesh.d.ts +1 -1
- package/dist/utils/mesh.js +1 -1
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as e,__metadata as t}from"tslib";import*as s from"three";import{Service as r}from"typedi";import{inject as i}from"../../gameplay/inject.js";import{AssetLoader as n}from"../../gameplay/services/asset-loader.js";import{ViewController as a}from"../../gameplay/services/render.js";import{AssetResourceLoader as o}from"../../scene/asset-resource-loader.js";import{AssetsProvider as l}from"../../scene/assets-provider.js";import{ShaderProvider as h}from"../../gameplay/services/shader-provider.js";import{ensureWebGpuRuntimeBindings as u}from"../../rendering/webgpu-runtime-bridge.js";import{createGpuSpriteShaderGraphResolver as c}from"./vfx-materializer.js";import{GpuSpriteRenderClassCache as d,hasGpuUnsupportedVfxInputBindings as p}from"./gpu-sprite-compiler.js";import{addGpuSpriteRenderGraphPasses as m}from"./gpu-sprite-render-graph.js";import{GpuSpriteWorldRuntime as g}from"./gpu-sprite-world-runtime.js";import{buildGpuSpriteTemplate as f,GpuSpriteTemplateCache as y}from"./gpu-sprite-template.js";import{gpuVfxCapacityClass as v}from"./gpu-vfx-capacity.js";export class GpuVfxEffectHandle{constructor(e,t){this.epoch=e,this.instance=t,this.released=!1}setPosition(e){return!this.released&&this.epoch.runtime.setPosition(this.instance,e)}setTransform(e){return!this.released&&this.epoch.runtime.setTransform(this.instance,e)}pause(){return!this.released&&this.epoch.runtime.pause(this.instance)}play(){return!this.released&&this.epoch.runtime.play(this.instance)}restart(){return!this.released&&this.epoch.runtime.restart(this.instance)}stop(){return!this.released&&this.epoch.runtime.stop(this.instance)}expire(e){return!this.released&&this.epoch.runtime.stop(this.instance,Math.max(0,e))}kill(){return!this.released&&this.epoch.runtime.stop(this.instance,0)}setTimescale(e){return!this.released&&this.epoch.runtime.setTimescale(this.instance,e)}setInput(e,t){return!this.released&&this.epoch.runtime.setInput(this.instance,e,t)}release(){return!this.released&&(this.released=!0,this.epoch.runtime.destroy(this.instance))}get active(){return!this.released&&this.epoch.runtime.has(this.instance)}}export class PreparedGpuVfxEffect{constructor(e,t,s){this.entry=e,this.spawnEntry=t,this.releaseEntry=s,this.released=!1}spawn(e){return this.released?null:this.spawnEntry(this.entry,e)}release(){this.released||(this.released=!0,this.releaseEntry(this.entry))}}let w=class{constructor(){this.view=i(a),this.assetLoader=i(n),this.assetsProvider=i(l),this.assetResources=i(o),this.shaderProvider=i(h),this.templateCache=new y,this.renderClassCache=new d,this.entriesByAssetId=new Map,this.entries=new Set,this.epochs=new Set,this.reportedCpuFallbacks=new Set,this.cpuFallbackReasons=new Map,this.currentEpoch=null,this.nextGeneration=1,this.rebuildTail=Promise.resolve(!0),this.pendingRebuild=null,this.pendingEntryBuilds=new Set,this.unregisterRenderGraph=null,this.estimatedWorldBytes=0,this.profilingEnabled=!1,this.view.onLateUpdate().subscribe(e=>this.update(e))}setProfilingEnabled(e){this.profilingEnabled=e}getStats(){const e=this.currentEpoch;return null==e?null:{particles:e.runtime.runtime.getStats(),scheduler:e.runtime.scheduler.getStats()}}beginPipelineWarmup(){const e=[],t=new s.Vector3;for(const s of this.epochs){for(const r of s.templateIndices.values()){const i=s.runtime.spawn(r,t);null!=i&&e.push({epoch:s,instance:i})}s.runtime.flushActivePages()}return()=>{for(const{epoch:t,instance:s}of e)t.runtime.destroy(s);for(const e of this.epochs)e.runtime.flushActivePages()}}async prepare(e,t,s){if(this.cpuFallbackReasons.delete(this.fallbackKey(e,t)),"webgpu"!==this.view.requestedBackend)return this.reportCpuFallback(e,t,[`GPU VFX requires the WebGPU renderer; the requested renderer is "${this.view.requestedBackend}".`]),null;if(await u(),p(e))return this.reportCpuFallback(e,t,["one or more authored VFX input bindings are not supported on GPU"]),null;let r=null==s||0===s.size?this.entriesByAssetId.get(e.id):void 0;if(null==r||r.asset!==e||r.revision!==t){const i=r;r={asset:e,revision:t,pending:(async()=>(null!=i&&await this.retireEntry(i.pending),this.createEntry(e,t,s)))()};const n=r.pending;this.pendingEntryBuilds.add(n),n.then(()=>this.pendingEntryBuilds.delete(n),()=>this.pendingEntryBuilds.delete(n)),null!=s&&0!==s.size||this.entriesByAssetId.set(e.id,r)}const i=await r.pending;if(null==i)return this.reportCpuFallback(e,t,["GPU template preparation rejected this asset without a diagnostic"]),null;i.references+=1;const n=v(i.references),a=this.currentEpoch?.reservations.get(i)??0,o=i.reservedInstances;return n>i.reservedInstances&&(i.reservedInstances=n),a<n&&!await this.queueRebuild()?(i.references-=1,i.reservedInstances=o,this.reportCpuFallback(e,t,["the shared GPU VFX world could not grow within its memory budget or WebGPU device limits"]),null):new PreparedGpuVfxEffect(i,(e,t)=>this.spawn(e,t),e=>this.releaseEntry(e))}getFallbackReasons(e,t){return this.cpuFallbackReasons.get(this.fallbackKey(e,t))??[]}async createEntry(e,t,s){const r={assets:this.assets,renderClassCache:this.renderClassCache,resolveCustomMaterial:this.resolveCustomMaterial,revision:t,meshInputs:s},i=null==s||0===s.size?await this.templateCache.resolve(e,r):await f(e,r);if(null==i.template){const s=i.compatibility.issues.filter(e=>"warning"!==e.severity).map(e=>`${e.emitter}: ${e.message}`);return this.reportCpuFallback(e,t,s.length>0?s:i.reasons),null}const n={asset:e,revision:t,references:0,reservedInstances:1,retired:!1,runtimeMeshInput:null!=s&&s.size>0,template:i.template};return this.entries.add(n),n}spawn(e,t){let s=this.currentEpoch,r=s?.templateIndices.get(e);if(null==r&&e.retired)for(const t of this.epochs){const i=t.templateIndices.get(e);t.disposalQueued||null==i||(s=t,r=i)}if(null==s||null==r)return null;const i=s.runtime.spawn(r,t);return null==i?null:new GpuVfxEffectHandle(s,i)}releaseEntry(e){e.references=Math.max(0,e.references-1),!e.runtimeMeshInput||e.references>0||e.retired||(e.retired=!0,this.entries.delete(e),this.queueRebuild())}async retireEntry(e){const t=await e;null!=t&&(t.retired=!0,this.entries.delete(t))}queueRebuild(){if(null!=this.pendingRebuild)return this.pendingRebuild;const e=this.rebuildTail.then(async()=>{for(;this.pendingEntryBuilds.size>0;)await Promise.allSettled([...this.pendingEntryBuilds]);return this.pendingRebuild=null,this.rebuildWorld()}).catch(t=>(this.pendingRebuild===e&&(this.pendingRebuild=null),console.error("[Hology GPU VFX] Failed to rebuild the shared world generation.",t),!1));return this.pendingRebuild=e,this.rebuildTail=e,e}async rebuildWorld(){const e=[...this.entries].filter(e=>!e.retired);if(0===e.length)return null!=this.currentEpoch&&(this.currentEpoch.retired=!0,this.currentEpoch=null,this.disposeRetiredEpochs()),!0;if(null!=this.currentEpoch&&this.currentEpoch.templateIndices.size===e.length&&e.every(e=>(this.currentEpoch.reservations.get(e)??0)>=e.reservedInstances))return!0;const t=[...this.epochs].reduce((e,t)=>t===this.currentEpoch&&0===t.runtime.scheduler.getStats().activeInstances?e:e+t.estimatedBytes,0),s=Math.max(0,this.view.webgpuVfxWorldMemoryBudgetBytes-t),r=await g.create({templates:e.map(e=>({asset:e.asset,revision:e.revision,maximumInstances:e.reservedInstances,preparedTemplate:e.template})),assets:this.assets,templateCache:this.templateCache,renderClassCache:this.renderClassCache,resolveCustomMaterial:this.resolveCustomMaterial,particlePageSize:256,aliveCountReadback:this.profilingEnabled,device:this.view.experimentalWebGpuDevice??void 0,maximumStorageBytes:s});if(null==r)return!1;const i=r.runtime.getStats().storageBytes;if(i>this.view.webgpuVfxWorldMemoryBudgetBytes||t+i>this.view.webgpuVfxWorldMemoryBudgetBytes)return r.dispose(),console.warn(`[Hology GPU VFX] Refusing shared world growth: retained storage would become ${b(t+i)} MiB, exceeding the ${b(this.view.webgpuVfxWorldMemoryBudgetBytes)} MiB budget.`),!1;const n={generation:this.nextGeneration++,runtime:r,templateIndices:new Map(e.map((e,t)=>[e,t])),reservations:new Map(e.map(e=>[e,e.reservedInstances])),estimatedBytes:i,retired:!1,disposalQueued:!1};return null!=this.currentEpoch&&(this.currentEpoch.retired=!0),this.currentEpoch=n,this.epochs.add(n),this.estimatedWorldBytes+=i,this.ensureRenderGraph(),console.info(`[Hology GPU VFX] Shared world generation ${n.generation}: ${e.length} template(s), ${e.reduce((e,t)=>e+t.reservedInstances,0)} reserved instance(s), ${r.runtime.emitters.length} emitter slots, ${r.runtime.getStats().capacity.toLocaleString()} particle slots, ${b(i)} MiB estimated storage.`),this.disposeRetiredEpochs(),!0}ensureRenderGraph(){if(null!=this.unregisterRenderGraph)return;const e=(e,t)=>m(e,this,t,"WorldGpuVfx");e.getSceneTextureReads=()=>this.getSceneTextureReads(),this.unregisterRenderGraph=this.view.registerExperimentalWebGpuRenderGraphExtension(e)}update(e){for(const t of this.epochs)t.runtime.update(e);this.disposeRetiredEpochs()}disposeRetiredEpochs(){for(const e of this.epochs){if(!e.retired||e.disposalQueued||0!==e.runtime.scheduler.getStats().activeInstances||[...e.templateIndices.keys()].some(e=>e.retired&&e.references>0))continue;e.disposalQueued=!0,this.epochs.delete(e),this.estimatedWorldBytes=Math.max(0,this.estimatedWorldBytes-e.estimatedBytes);const t=()=>{e.runtime.dispose(),console.info(`[Hology GPU VFX] Retired shared world generation ${e.generation}, releasing ${b(e.estimatedBytes)} MiB estimated storage.`)},s=this.view.experimentalWebGpuDevice;null==s?t():s.queue.onSubmittedWorkDone().then(t,t)}}reportCpuFallback(e,t,s){const r=this.fallbackKey(e,t);this.cpuFallbackReasons.set(r,[...s]),this.reportedCpuFallbacks.has(r)||(this.reportedCpuFallbacks.add(r),console.warn(`[Hology GPU VFX] Falling back to CPU for "${e.name}".`,s))}fallbackKey(e,t){return`${e.id}:${t??""}`}executeCompute(e){for(const t of this.epochs)t.runtime.runtime.executeCompute(e)}executeRender(e,t){for(const s of this.epochs)s.runtime.runtime.executeRender(e,t)}getSceneTextureReads(){const e=new Set;for(const t of this.epochs)if(t.runtime.runtime.hasActiveParticles)for(const s of t.runtime.runtime.getSceneTextureReads())e.add(s);return[...e]}getComputeTextureReads(){const e=new Set;for(const t of this.epochs)if(t.runtime.runtime.hasActiveParticles)for(const s of t.runtime.runtime.getComputeTextureReads())e.add(s);return[...e]}get assets(){return{getTexture:e=>this.assetLoader.getTextureByAssetId(e),getMesh:async e=>(await this.assetResources.getMesh(await this.assetsProvider.getAsset(e),{applyMaterials:!0,rescale:!0})).scene}}get resolveCustomMaterial(){return c(this.assetsProvider,this.assetResources,this.shaderProvider,{getAsset:e=>this.assetLoader.getAsset(e),getTexture:e=>this.assetLoader.getTextureByAssetId(e),getMaterial:e=>this.assetLoader.getMaterialByAssetId(e),getMesh:e=>this.assetLoader.getModelByAssetId(e).then(e=>e.scene)})}};w=e([r(),t("design:paramtypes",[])],w);export{w as GpuVfxWorldService};function b(e){return(e/1048576).toFixed(1)}/*
|
|
1
|
+
import{__decorate as e,__metadata as t}from"tslib";import{partitionVfxBackends as s}from"./vfx-backend-partition.js";import*as r from"three";import{Service as i}from"typedi";import{inject as n}from"../../gameplay/inject.js";import{AssetLoader as a}from"../../gameplay/services/asset-loader.js";import{ViewController as o}from"../../gameplay/services/render.js";import{AssetResourceLoader as l}from"../../scene/asset-resource-loader.js";import{AssetsProvider as h}from"../../scene/assets-provider.js";import{ShaderProvider as u}from"../../gameplay/services/shader-provider.js";import{ensureWebGpuRuntimeBindings as c}from"../../rendering/webgpu-runtime-bridge.js";import{createGpuSpriteShaderGraphResolver as d}from"./vfx-materializer.js";import{GpuSpriteRenderClassCache as p,hasGpuUnsupportedVfxInputBindings as m}from"./gpu-sprite-compiler.js";import{addGpuSpriteRenderGraphPasses as g}from"./gpu-sprite-render-graph.js";import{GpuSpriteWorldRuntime as f}from"./gpu-sprite-world-runtime.js";import{buildGpuSpriteTemplate as y,GpuSpriteTemplateCache as v}from"./gpu-sprite-template.js";import{gpuVfxCapacityClass as w}from"./gpu-vfx-capacity.js";export class GpuVfxEffectHandle{constructor(e,t){this.epoch=e,this.instance=t,this.released=!1}setPosition(e){return!this.released&&this.epoch.runtime.setPosition(this.instance,e)}setTransform(e){return!this.released&&this.epoch.runtime.setTransform(this.instance,e)}pause(){return!this.released&&this.epoch.runtime.pause(this.instance)}play(){return!this.released&&this.epoch.runtime.play(this.instance)}restart(){return!this.released&&this.epoch.runtime.restart(this.instance)}stop(){return!this.released&&this.epoch.runtime.stop(this.instance)}expire(e){return!this.released&&this.epoch.runtime.stop(this.instance,Math.max(0,e))}kill(){return!this.released&&this.epoch.runtime.stop(this.instance,0)}setTimescale(e){return!this.released&&this.epoch.runtime.setTimescale(this.instance,e)}setInput(e,t){return!this.released&&this.epoch.runtime.setInput(this.instance,e,t)}release(){return!this.released&&(this.released=!0,this.epoch.runtime.destroy(this.instance))}get active(){return!this.released&&this.epoch.runtime.has(this.instance)}}export class PreparedGpuVfxEffect{constructor(e,t,s){this.entry=e,this.spawnEntry=t,this.releaseEntry=s,this.released=!1}spawn(e){return this.released?null:this.spawnEntry(this.entry,e)}release(){this.released||(this.released=!0,this.releaseEntry(this.entry))}}let b=class{constructor(){this.view=n(o),this.assetLoader=n(a),this.assetsProvider=n(h),this.assetResources=n(l),this.shaderProvider=n(u),this.templateCache=new v,this.renderClassCache=new p,this.entriesByAssetId=new Map,this.entries=new Set,this.epochs=new Set,this.reportedCpuFallbacks=new Set,this.cpuFallbackReasons=new Map,this.currentEpoch=null,this.nextGeneration=1,this.rebuildTail=Promise.resolve(!0),this.pendingRebuild=null,this.pendingEntryBuilds=new Set,this.unregisterRenderGraph=null,this.estimatedWorldBytes=0,this.profilingEnabled=!1,this.view.onLateUpdate().subscribe(e=>this.update(e))}setProfilingEnabled(e){this.profilingEnabled=e}getStats(){const e=this.currentEpoch;return null==e?null:{particles:e.runtime.runtime.getStats(),scheduler:e.runtime.scheduler.getStats()}}beginPipelineWarmup(){const e=[],t=new r.Vector3;for(const s of this.epochs){for(const r of s.templateIndices.values()){const i=s.runtime.spawn(r,t);null!=i&&e.push({epoch:s,instance:i})}s.runtime.flushActivePages()}return()=>{for(const{epoch:t,instance:s}of e)t.runtime.destroy(s);for(const e of this.epochs)e.runtime.flushActivePages()}}async prepare(e,t,r){if(e=s(e,t).gpu,this.cpuFallbackReasons.delete(this.fallbackKey(e,t)),"webgpu"!==this.view.requestedBackend)return this.reportCpuFallback(e,t,[`GPU VFX requires the WebGPU renderer; the requested renderer is "${this.view.requestedBackend}".`]),null;if(await c(),m(e))return this.reportCpuFallback(e,t,["one or more authored VFX input bindings are not supported on GPU"]),null;let i=null==r||0===r.size?this.entriesByAssetId.get(e.id):void 0;if(null==i||i.asset!==e||i.revision!==t){const s=i;i={asset:e,revision:t,pending:(async()=>(null!=s&&await this.retireEntry(s.pending),this.createEntry(e,t,r)))()};const n=i.pending;this.pendingEntryBuilds.add(n),n.then(()=>this.pendingEntryBuilds.delete(n),()=>this.pendingEntryBuilds.delete(n)),null!=r&&0!==r.size||this.entriesByAssetId.set(e.id,i)}const n=await i.pending;if(null==n)return this.reportCpuFallback(e,t,["GPU template preparation rejected this asset without a diagnostic"]),null;n.references+=1;const a=w(n.references),o=this.currentEpoch?.reservations.get(n)??0,l=n.reservedInstances;return a>n.reservedInstances&&(n.reservedInstances=a),o<a&&!await this.queueRebuild()?(n.references-=1,n.reservedInstances=l,this.reportCpuFallback(e,t,["the shared GPU VFX world could not grow within its memory budget or WebGPU device limits"]),null):new PreparedGpuVfxEffect(n,(e,t)=>this.spawn(e,t),e=>this.releaseEntry(e))}getFallbackReasons(e,t){return this.cpuFallbackReasons.get(this.fallbackKey(e,t))??[]}async createEntry(e,t,s){const r={assets:this.assets,renderClassCache:this.renderClassCache,resolveCustomMaterial:this.resolveCustomMaterial,revision:t,meshInputs:s},i=null==s||0===s.size?await this.templateCache.resolve(e,r):await y(e,r);if(null==i.template){const s=i.compatibility.issues.filter(e=>"warning"!==e.severity).map(e=>`${e.emitter}: ${e.message}`);return this.reportCpuFallback(e,t,s.length>0?s:i.reasons),null}const n={asset:e,revision:t,references:0,reservedInstances:1,retired:!1,runtimeMeshInput:null!=s&&s.size>0,template:i.template};return this.entries.add(n),n}spawn(e,t){let s=this.currentEpoch,r=s?.templateIndices.get(e);if(null==r&&e.retired)for(const t of this.epochs){const i=t.templateIndices.get(e);t.disposalQueued||null==i||(s=t,r=i)}if(null==s||null==r)return null;const i=s.runtime.spawn(r,t);return null==i?null:new GpuVfxEffectHandle(s,i)}releaseEntry(e){e.references=Math.max(0,e.references-1),!e.runtimeMeshInput||e.references>0||e.retired||(e.retired=!0,this.entries.delete(e),this.queueRebuild())}async retireEntry(e){const t=await e;null!=t&&(t.retired=!0,this.entries.delete(t))}queueRebuild(){if(null!=this.pendingRebuild)return this.pendingRebuild;const e=this.rebuildTail.then(async()=>{for(;this.pendingEntryBuilds.size>0;)await Promise.allSettled([...this.pendingEntryBuilds]);return this.pendingRebuild=null,this.rebuildWorld()}).catch(t=>(this.pendingRebuild===e&&(this.pendingRebuild=null),console.error("[Hology GPU VFX] Failed to rebuild the shared world generation.",t),!1));return this.pendingRebuild=e,this.rebuildTail=e,e}async rebuildWorld(){const e=[...this.entries].filter(e=>!e.retired);if(0===e.length)return null!=this.currentEpoch&&(this.currentEpoch.retired=!0,this.currentEpoch=null,this.disposeRetiredEpochs()),!0;if(null!=this.currentEpoch&&this.currentEpoch.templateIndices.size===e.length&&e.every(e=>(this.currentEpoch.reservations.get(e)??0)>=e.reservedInstances))return!0;const t=[...this.epochs].reduce((e,t)=>t===this.currentEpoch&&0===t.runtime.scheduler.getStats().activeInstances?e:e+t.estimatedBytes,0),s=Math.max(0,this.view.webgpuVfxWorldMemoryBudgetBytes-t),r=await f.create({templates:e.map(e=>({asset:e.asset,revision:e.revision,maximumInstances:e.reservedInstances,preparedTemplate:e.template})),assets:this.assets,templateCache:this.templateCache,renderClassCache:this.renderClassCache,resolveCustomMaterial:this.resolveCustomMaterial,particlePageSize:256,aliveCountReadback:this.profilingEnabled,device:this.view.experimentalWebGpuDevice??void 0,maximumStorageBytes:s});if(null==r)return!1;const i=r.runtime.getStats().storageBytes;if(i>this.view.webgpuVfxWorldMemoryBudgetBytes||t+i>this.view.webgpuVfxWorldMemoryBudgetBytes)return r.dispose(),console.warn(`[Hology GPU VFX] Refusing shared world growth: retained storage would become ${x(t+i)} MiB, exceeding the ${x(this.view.webgpuVfxWorldMemoryBudgetBytes)} MiB budget.`),!1;const n={generation:this.nextGeneration++,runtime:r,templateIndices:new Map(e.map((e,t)=>[e,t])),reservations:new Map(e.map(e=>[e,e.reservedInstances])),estimatedBytes:i,retired:!1,disposalQueued:!1};return null!=this.currentEpoch&&(this.currentEpoch.retired=!0),this.currentEpoch=n,this.epochs.add(n),this.estimatedWorldBytes+=i,this.ensureRenderGraph(),console.info(`[Hology GPU VFX] Shared world generation ${n.generation}: ${e.length} template(s), ${e.reduce((e,t)=>e+t.reservedInstances,0)} reserved instance(s), ${r.runtime.emitters.length} emitter slots, ${r.runtime.getStats().capacity.toLocaleString()} particle slots, ${x(i)} MiB estimated storage.`),this.disposeRetiredEpochs(),!0}ensureRenderGraph(){if(null!=this.unregisterRenderGraph)return;const e=(e,t)=>g(e,this,t,"WorldGpuVfx");e.getSceneTextureReads=()=>this.getSceneTextureReads(),this.unregisterRenderGraph=this.view.registerExperimentalWebGpuRenderGraphExtension(e)}update(e){for(const t of this.epochs)t.runtime.update(e);this.disposeRetiredEpochs()}disposeRetiredEpochs(){for(const e of this.epochs){if(!e.retired||e.disposalQueued||0!==e.runtime.scheduler.getStats().activeInstances||[...e.templateIndices.keys()].some(e=>e.retired&&e.references>0))continue;e.disposalQueued=!0,this.epochs.delete(e),this.estimatedWorldBytes=Math.max(0,this.estimatedWorldBytes-e.estimatedBytes);const t=()=>{e.runtime.dispose(),console.info(`[Hology GPU VFX] Retired shared world generation ${e.generation}, releasing ${x(e.estimatedBytes)} MiB estimated storage.`)},s=this.view.experimentalWebGpuDevice;null==s?t():s.queue.onSubmittedWorkDone().then(t,t)}}reportCpuFallback(e,t,s){const r=this.fallbackKey(e,t);this.cpuFallbackReasons.set(r,[...s]),this.reportedCpuFallbacks.has(r)||(this.reportedCpuFallbacks.add(r),console.warn(`[Hology GPU VFX] Falling back to CPU for "${e.name}".`,s))}fallbackKey(e,t){return`${e.id}:${t??""}`}executeCompute(e){for(const t of this.epochs)t.runtime.runtime.executeCompute(e)}executeRender(e,t){for(const s of this.epochs)s.runtime.runtime.executeRender(e,t)}getSceneTextureReads(){const e=new Set;for(const t of this.epochs)if(t.runtime.runtime.hasActiveParticles)for(const s of t.runtime.runtime.getSceneTextureReads())e.add(s);return[...e]}getComputeTextureReads(){const e=new Set;for(const t of this.epochs)if(t.runtime.runtime.hasActiveParticles)for(const s of t.runtime.runtime.getComputeTextureReads())e.add(s);return[...e]}get assets(){return{getTexture:e=>this.assetLoader.getTextureByAssetId(e),getMesh:async e=>(await this.assetResources.getMesh(await this.assetsProvider.getAsset(e),{applyMaterials:!0,rescale:!0})).scene}}get resolveCustomMaterial(){return d(this.assetsProvider,this.assetResources,this.shaderProvider,{getAsset:e=>this.assetLoader.getAsset(e),getTexture:e=>this.assetLoader.getTextureByAssetId(e),getMaterial:e=>this.assetLoader.getMaterialByAssetId(e),getMesh:e=>this.assetLoader.getModelByAssetId(e).then(e=>e.scene)})}};b=e([i(),t("design:paramtypes",[])],b);export{b as GpuVfxWorldService};function x(e){return(e/1048576).toFixed(1)}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -5,6 +5,8 @@ export declare class DelayRate extends Rate {
|
|
|
5
5
|
readonly delay: number;
|
|
6
6
|
duration?: number;
|
|
7
7
|
constructor(delay?: number, numPan?: number | SpanValue[] | Span, timePan?: number | SpanValue[] | Span, duration?: number);
|
|
8
|
+
/** True once this schedule can no longer spawn, including one-shot and stopped rates. */
|
|
9
|
+
get emissionComplete(): boolean;
|
|
8
10
|
private durationPlayed;
|
|
9
11
|
restart(): void;
|
|
10
12
|
getValue(time: number): number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Rate as t}from"@hology/nebula";export{Rate}from"@hology/nebula";export class DelayRate extends t{constructor(t=0,e,i,s){super(e,i),this.delay=t,this.duration=s,this.durationPlayed=0,this.restart()}restart(){this.durationPlayed=0,this.nextTime=0,null!=this.delay&&this.delay>0?this.startTime=-this.delay:this.startTime=0}getValue(t){return null!=this.duration&&this.duration>0&&(this.durationPlayed+=t,this.durationPlayed>=this.duration)?0:super.getValue(t)}clone(){return new DelayRate(this.delay,this.numPan,this.timePan,this.duration)}}export class OnceRate extends DelayRate{constructor(t,e=1){super(t,e,1/0),this.count=e,this.calls=0,this.nextTime=0}clone(){return new OnceRate(this.delay,this.count)}init(){this.restart()}getValue(t){return this.calls++,this.startTime+=t,this.startTime>=this.nextTime?(this.nextTime=1/0,this.count):0}}export class ConstantRate extends DelayRate{constructor(t,e=1){super(t,e,1),this.perSecond=e,this.emitted=0,this.nextTime=0}clone(){return new ConstantRate(this.delay,this.perSecond)}init(){this.restart(),this.emitted=0}getValue(t){if(this.startTime+=t,this.startTime>=this.nextTime){let t=0;for(;this.emitted+t<this.startTime*this.perSecond;)t++;return this.emitted+=t,t}return 0}}/*
|
|
1
|
+
import{Rate as t}from"@hology/nebula";export{Rate}from"@hology/nebula";export class DelayRate extends t{constructor(t=0,e,i,s){super(e,i),this.delay=t,this.duration=s,this.durationPlayed=0,this.restart()}get emissionComplete(){return this.nextTime===1/0||null!=this.duration&&this.duration>0&&this.durationPlayed>=this.duration}restart(){this.durationPlayed=0,this.nextTime=0,null!=this.delay&&this.delay>0?this.startTime=-this.delay:this.startTime=0}getValue(t){return null!=this.duration&&this.duration>0&&(this.durationPlayed+=t,this.durationPlayed>=this.duration)?0:super.getValue(t)}clone(){return new DelayRate(this.delay,this.numPan,this.timePan,this.duration)}}export class OnceRate extends DelayRate{constructor(t,e=1){super(t,e,1/0),this.count=e,this.calls=0,this.nextTime=0}clone(){return new OnceRate(this.delay,this.count)}init(){this.restart()}getValue(t){return this.calls++,this.startTime+=t,this.startTime>=this.nextTime?(this.nextTime=1/0,this.count):0}}export class ConstantRate extends DelayRate{constructor(t,e=1){super(t,e,1),this.perSecond=e,this.emitted=0,this.nextTime=0}clone(){return new ConstantRate(this.delay,this.perSecond)}init(){this.restart(),this.emitted=0}getValue(t){if(this.startTime+=t,this.startTime>=this.nextTime){let t=0;for(;this.emitted+t<this.startTime*this.perSecond;)t++;return this.emitted+=t,t}return 0}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -24,7 +24,7 @@ export declare class VfxActor extends BaseActor {
|
|
|
24
24
|
set timescale(value: number);
|
|
25
25
|
paused: boolean;
|
|
26
26
|
/** Backend selected for the currently materialized effect. */
|
|
27
|
-
runtimeBackend: 'cpu' | 'gpu';
|
|
27
|
+
runtimeBackend: 'cpu' | 'gpu' | 'mixed';
|
|
28
28
|
backendFallbackReasons: readonly string[];
|
|
29
29
|
profilingEnabled: boolean;
|
|
30
30
|
private simulationCpuMs;
|
|
@@ -104,6 +104,7 @@ export declare class VfxActor extends BaseActor {
|
|
|
104
104
|
cpuTimeMs: number;
|
|
105
105
|
};
|
|
106
106
|
onEndPlay(): void;
|
|
107
|
+
private cpuSimulationComplete;
|
|
107
108
|
private updateCompletionState;
|
|
108
109
|
private updateSystem;
|
|
109
110
|
private applyEmitterClipEndBehavior;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t,__metadata as e}from"tslib";import*as s from"three";import{Rate as i}from"@hology/nebula";import{Actor as r,BaseActor as n}from"../../gameplay/actors/actor.js";import{inject as o}from"../../gameplay/inject.js";import{AssetLoader as a}from"../../gameplay/services/asset-loader.js";import{ViewController as l}from"../../gameplay/services/render.js";import{World as p}from"../../gameplay/services/world.js";import{DelayRate as h}from"./rates.js";import{materializeVfx as u}from"./vfx-materializer.js";import{PhysicsSystem as m}from"../../gameplay/services/physics/physics-system.js";import{ShaderProvider as d}from"../../gameplay/services/shader-provider.js";import{AssetResourceLoader as c}from"../../scene/asset-resource-loader.js";import{AssetsProvider as g}from"../../scene/assets-provider.js";import{VfxInputRuntime as f}from"./vfx-input-runtime.js";import{Subject as y}from"rxjs";import{EventGraphCallable as v}from"../../gameplay/event-graph/decorators.js";import{EVENT_GRAPH_VOID_TYPE as w}from"../../gameplay/event-graph/types.js";import{VFX_ACTOR_TYPE_ID as P}from"../../gameplay/event-graph/asset-actor-types.js";import{GpuVfxWorldService as b}from"./gpu-vfx-world-service.js";import{compileGpuSpriteEmitters as S,requiresCpuVfxBackend as j}from"./gpu-sprite-compiler.js";import{withSeededRandom as x}from"./seeded-random.js";import{RenderingView as E}from"../../rendering.js";import{prepareVfxRendering as R}from"./vfx-render-preparation.js";let C=class extends n{constructor(){super(...arguments),this.onComplete=new y,this.collisionEvent={emitterId:"",emitterName:"",position:new s.Vector3,normal:new s.Vector3},this._timescale=1,this.paused=!0,this.runtimeBackend="cpu",this.backendFallbackReasons=[],this.profilingEnabled=!1,this.simulationCpuMs=0,this.system=null,this.disposeSystem=null,this.renderBodies=[],this.renderingView=o(E),this.assetLoader=o(a),this.assetManagerService=o(c),this.assetService=o(g),this.world=o(p),this.view=o(l),this.gpuWorld=o(b),this.particleSystemContainer=null,this.gpuPrepared=null,this.gpuHandle=null,this.physics=o(m),this.shaderProvider=o(d),this.inputRuntime=new f,this.inputOverrides=new Map,this.finishRequested=!1,this.completionEmitted=!1,this.inputsMaterialized=!1,this._worldPos=new s.Vector3,this._worldRot=new s.Quaternion,this._worldEul=new s.Euler,this._worldScale=new s.Vector3,this.previewStepSeconds=1/60,this.maxPreviewSteps=240,this.max=0}get timescale(){return this._timescale}set timescale(t){this._timescale=Math.max(0,t),this.gpuHandle?.setTimescale(this._timescale)}async fromAsset(t,e,i,r){if("vfx"!==t.type)throw"Asset must be a VFX asset but is "+t.type;null!=this.system&&(this.system.destroy(),this.system.emitters.forEach(t=>t.reset())),this.gpuHandle?.release(),this.gpuPrepared?.release(),this.gpuHandle=null,this.gpuPrepared=null,this.runtimeBackend="cpu",this.renderBodies=[],this.renderPreparation=void 0,this.backendFallbackReasons=[],this.simulationCpuMs=0,this.sourceAsset=t,this.finishRequested=!1,this.completionEmitted=!1,this.inputsMaterialized=!1,this.disposeSystem&&this.disposeSystem(),null==this.object.parent&&this.world.scene.add(this.object),this.inputRuntime.setDefinitions(this.sourceAsset.vfx.inputs??[]);for(const t of this.inputRuntime.getDefinitions()){if("mesh"!==t.type||"string"!=typeof t.defaultValue||0===t.defaultValue.length)continue;const e=await this.assetService.getAsset(t.defaultValue);if(null==e)continue;const s=await this.assetManagerService.getMesh(e,{applyMaterials:!0,rescale:!0});this.inputRuntime.setInput(t.id,s.scene)}for(const[t,e]of this.inputOverrides)this.inputRuntime.setInput(t,e);null!=e&&this.setInputs(e);const n=i??this.view.vfxBackendPreference,o=j(this.sourceAsset);if("cpu"===n&&(this.backendFallbackReasons=["GPU VFX was not requested; the selected VFX backend preference is CPU."]),"cpu"!==n&&!o){const t=new Map;for(const e of this.inputRuntime.getDefinitions()){if("mesh"!==e.type)continue;const s=this.inputRuntime.getMesh(e.id);null!=s&&t.set(e.id,s)}if(this.gpuPrepared=await this.gpuWorld.prepare(this.sourceAsset,r,t),null!=this.gpuPrepared)return this.runtimeBackend="gpu",this.system=null,this.disposeSystem=null,this.particleSystemContainer=null,this.object.visible=!1,this.object.matrixAutoUpdate=!0,this.object.matrixWorldAutoUpdate=!0,void(this.inputsMaterialized=!0);if(this.backendFallbackReasons=this.gpuWorld.getFallbackReasons(this.sourceAsset,r),"gpu"===n){const t=S(this.sourceAsset,[new s.Vector3]).compatibility;this.backendFallbackReasons=t.issues.filter(t=>"warning"!==t.severity).map(t=>`${t.emitter}: ${t.message}`),0===this.backendFallbackReasons.length&&(this.backendFallbackReasons=["GPU template or material preparation failed; inspect earlier shader diagnostics."])}}const{system:a,dispose:l,container:p,renderBodies:h}=await u(this.sourceAsset,this.object,{getAsset:t=>this.assetLoader.getAsset(t),getMaterial:t=>this.assetLoader.getMaterialByAssetId(t),getTexture:t=>this.assetLoader.getTextureByAssetId(t),getMesh:async t=>(await this.assetManagerService.getMesh(await this.assetService.getAsset(t),{applyMaterials:!0,rescale:!0})).scene},this.view,this.physics,this.shaderProvider,this.assetService,this.assetManagerService,this.inputRuntime,(t,e,s,i,r)=>{if(null==this.onCollision)return;const n=this.collisionEvent;n.emitterId=t,n.emitterName=e,n.position.copy(s),n.normal.copy(i),r&&(n.position.applyMatrix4(this.object.matrixWorld),n.normal.transformDirection(this.object.matrixWorld)),this.onCollision(n)});this.system=a,this.renderBodies=h,this.disposeSystem=l,this.particleSystemContainer=p,this.inputsMaterialized=!0,this.object.visible=!1,this.object.matrixAutoUpdate=!1,this.object.matrixWorldAutoUpdate=!1,this.particleSystemContainer.matrixAutoUpdate=!1,this.particleSystemContainer.matrixWorldAutoUpdate=!1}prepareRendering(){return this.renderPreparation??(this.renderPreparation=this.prepareRenderResources())}async prepareRenderResources(){null==this.gpuPrepared&&0!==this.renderBodies.length&&await R(this.renderBodies,this.view,this.renderingView)}setInput(t,e){const i=this.inputRuntime.getDefinition(t);if(this.inputsMaterialized&&"mesh"===i?.type)return void console.warn(`VFX mesh input "${i.name}" is creation-time only; recreate the VFX to replace it.`);this.inputOverrides.set(t,function(t){if(t instanceof s.Vector3||t instanceof s.Color)return t.clone();if(Array.isArray(t))return[...t];return t}(e)),this.inputRuntime.setInput(t,e);const r=this.inputRuntime.getDefinition(t);null!=r&&this.gpuHandle?.setInput(r.id,this.inputRuntime.getValue(r.id))}setInputs(t){for(const[e,s]of Object.entries(t))this.setInput(e,s)}play(){if(this.prepareForPlayback(),this.paused=!1,null!=this.gpuPrepared){if(this.object.getWorldPosition(this._worldPos),null!=this.gpuHandle&&this.gpuHandle.active)this.gpuHandle.setPosition(this._worldPos),this.gpuHandle.setTransform(this.object.matrixWorld),this.gpuHandle.play();else{this.gpuHandle=this.gpuPrepared.spawn(this._worldPos),this.gpuHandle?.setTransform(this.object.matrixWorld),this.gpuHandle?.setTimescale(this._timescale);for(const t of this.inputRuntime.getDefinitions())this.gpuHandle?.setInput(t.id,this.inputRuntime.getValue(t.id))}return}this.system.emitters.every(t=>t.dead)&&this.restart()}pause(){this.paused=!0,this.gpuHandle?.pause()}previewAt(t,e,s,i,r=0){null!=this.system&&(this.prepareForPlayback(),x(r,()=>{this.restart(),this.paused=!0;const r=Math.max(0,t),n=Math.min(r,Math.max(0,e)),o=r-n,a=Number(n>0)+Number(o>0),l=Math.max(a,Math.min(this.maxPreviewSteps,Math.max(1,Math.ceil(r/this.previewStepSeconds)))),p=n<=0?0:o<=0?l:Math.min(l-1,Math.max(1,Math.round(l*n/r)));this.simulatePreview(n,p),o>0&&(this.applyClipEndBehavior(s,i),this.simulatePreview(o,l-p))}))}clearPreview(){this.restart(),this.pause(),this.object.visible=!1,null!=this.particleSystemContainer&&(this.particleSystemContainer.visible=!1)}stop(){this.finishRequested=!0,null==this.gpuPrepared?null!=this.system?.emitters&&this.system.emitters.forEach(t=>{this.stopEmitterEmission(t)}):this.gpuHandle?.stop()}applyClipEndBehavior(t,e){const s=t??"finish";if("finish"!==s){if(null!=this.gpuPrepared)return this.finishRequested=!0,void("kill"===s?this.gpuHandle?.kill():this.gpuHandle?.expire(Math.max(0,e??0)));if(null!=this.system&&null!=this.system.emitters)for(const t of this.system.emitters)this.applyEmitterClipEndBehavior(t,s,e)}else this.stop()}restart(){if(null==this.gpuPrepared){if(null!=this.system&&null!=this.system.emitters){this.finishRequested=!1,this.completionEmitted=!1;for(const t of this.system.emitters)this.restartEmitter(t)}}else if(this.finishRequested=!1,this.completionEmitted=!1,null!=this.gpuHandle){if(!this.gpuHandle.active&&!this.paused)return void this.play();this.object.getWorldPosition(this._worldPos),this.gpuHandle.setPosition(this._worldPos),this.gpuHandle.setTransform(this.object.matrixWorld),this.gpuHandle.restart()}}restartEmitter(t){const e=t.rate;e instanceof h?e.restart():e instanceof i&&(e.nextTime=0),null==t.clearParticlesRecursive?this.removeEmitterParticlesRecursive(t):t.clearParticlesRecursive()}removeEmitterParticlesRecursive(t){const e=t.childEmitters??[];for(const t of e)this.removeEmitterParticlesRecursive(t);t.removeAllParticles?.()}onUpdate(t){if(this.paused)null!=this.gpuHandle&&(this.object.getWorldPosition(this._worldPos),this.gpuHandle.setPosition(this._worldPos),this.gpuHandle.setTransform(this.object.matrixWorld));else{if(null!=this.gpuPrepared)return this.object.getWorldPosition(this._worldPos),this.gpuHandle?.setPosition(this._worldPos),this.gpuHandle?.setTransform(this.object.matrixWorld),void this.updateCompletionState();this.updateWorldSpaceEmitterTransforms(),this.updateSystem(t*this.timescale),this.updateCompletionState()}}prepareForPlayback(){null==this.gpuPrepared?(null!=this.particleSystemContainer&&null==this.particleSystemContainer.parent&&this.world.scene.add(this.particleSystemContainer),this.object.matrixAutoUpdate=!0,this.object.matrixWorldAutoUpdate=!0,null!=this.particleSystemContainer&&(this.particleSystemContainer.matrixAutoUpdate=!0,this.particleSystemContainer.matrixWorldAutoUpdate=!0),this.object.visible=!0,null!=this.particleSystemContainer&&(this.particleSystemContainer.visible=!0)):this.object.visible=!0}simulatePreview(t,e){if(t<=0)return;const s=t/e;for(let t=0;t<e;t++)this.updateWorldSpaceEmitterTransforms(),this.updateSystem(s)}updateWorldSpaceEmitterTransforms(){this.object.getWorldPosition(this._worldPos),this.object.getWorldQuaternion(this._worldRot),this.object.getWorldScale(this._worldScale),this._worldEul.setFromQuaternion(this._worldRot),this.system?.emitters.forEach(t=>{if("world"===t._space){t.setPosition(this._worldPos),t.setRotation(this._worldEul);(t._worldScale??(t._worldScale=new s.Vector3)).copy(this._worldScale)}})}getParticleCount(){return null!=this.gpuPrepared?0:this.system?.getCount()??0}getProfilingStats(){let t=0,e=0;return this.particleSystemContainer?.traverse(i=>{const r=i,n=r.geometry;if(null==n||!1===r.visible)return;const o=r instanceof s.InstancedMesh?r.count:1,a=n.index?.count??n.getAttribute("position")?.count??0;o<=0||a<=0||(t++,e+=Math.floor(a/3)*o)}),{particles:this.system?.getCount()??0,drawCalls:t,triangles:e,cpuTimeMs:this.simulationCpuMs}}onEndPlay(){this.stop(),null!=this.disposeSystem&&this.disposeSystem(),this.gpuHandle?.release(),this.gpuPrepared?.release(),this.gpuHandle=null,this.gpuPrepared=null,this.renderBodies=[],this.renderPreparation=void 0,this.onComplete.complete()}updateCompletionState(){if(null!=this.gpuPrepared){if(this.completionEmitted||!1!==this.gpuHandle?.active)return;return this.completionEmitted=!0,void this.onComplete.next()}if(this.completionEmitted||null==this.system||!this.onComplete.observed)return;const t=this.system.emitters.every(t=>t.dead),e=this.finishRequested&&0===this.system.getCount();(t||e)&&(this.completionEmitted=!0,this.onComplete.next())}updateSystem(t){if(null==this.system)return;if(!this.profilingEnabled)return void this.system.update(t);const e=performance.now();this.system.update(t),this.simulationCpuMs=performance.now()-e}applyEmitterClipEndBehavior(t,e,s,i=!1){this.stopEmitterEmission(t,i);const r=t.particles??[];if("kill"===e)t.removeAllParticles?.();else{const t=Math.max(0,s??0);for(const e of r)e.life=Math.min(e.life,e.age+t)}const n=t.childEmitters??[];for(const t of n)this.applyEmitterClipEndBehavior(t,e,s,!0)}stopEmitterEmission(t,e=!1){const s=t.rate;s instanceof i&&(s.nextTime=1/0),e&&t.stopEmit?.()}};t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],C.prototype,"play",null),t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],C.prototype,"pause",null),t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],C.prototype,"stop",null),t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],C.prototype,"restart",null),C=t([r({typeId:P})],C);export{C as VfxActor};/*
|
|
1
|
+
import{__decorate as t,__metadata as e}from"tslib";import*as s from"three";import{Rate as i}from"@hology/nebula";import{Actor as r,BaseActor as n}from"../../gameplay/actors/actor.js";import{inject as a}from"../../gameplay/inject.js";import{AssetLoader as o}from"../../gameplay/services/asset-loader.js";import{ViewController as l}from"../../gameplay/services/render.js";import{World as p}from"../../gameplay/services/world.js";import{DelayRate as h}from"./rates.js";import{materializeVfx as u}from"./vfx-materializer.js";import{PhysicsSystem as m}from"../../gameplay/services/physics/physics-system.js";import{ShaderProvider as d}from"../../gameplay/services/shader-provider.js";import{AssetResourceLoader as c}from"../../scene/asset-resource-loader.js";import{AssetsProvider as g}from"../../scene/assets-provider.js";import{VfxInputRuntime as f}from"./vfx-input-runtime.js";import{Subject as y}from"rxjs";import{EventGraphCallable as v}from"../../gameplay/event-graph/decorators.js";import{EVENT_GRAPH_VOID_TYPE as w}from"../../gameplay/event-graph/types.js";import{VFX_ACTOR_TYPE_ID as P}from"../../gameplay/event-graph/asset-actor-types.js";import{GpuVfxWorldService as b}from"./gpu-vfx-world-service.js";import{compileGpuSpriteEmitters as S,requiresCpuVfxBackend as j}from"./gpu-sprite-compiler.js";import{withSeededRandom as x}from"./seeded-random.js";import{RenderingView as E}from"../../rendering.js";import{partitionVfxBackends as C}from"./vfx-backend-partition.js";import{prepareVfxRendering as R}from"./vfx-render-preparation.js";let M=class extends n{constructor(){super(...arguments),this.onComplete=new y,this.collisionEvent={emitterId:"",emitterName:"",position:new s.Vector3,normal:new s.Vector3},this._timescale=1,this.paused=!0,this.runtimeBackend="cpu",this.backendFallbackReasons=[],this.profilingEnabled=!1,this.simulationCpuMs=0,this.system=null,this.disposeSystem=null,this.renderBodies=[],this.renderingView=a(E),this.assetLoader=a(o),this.assetManagerService=a(c),this.assetService=a(g),this.world=a(p),this.view=a(l),this.gpuWorld=a(b),this.particleSystemContainer=null,this.gpuPrepared=null,this.gpuHandle=null,this.physics=a(m),this.shaderProvider=a(d),this.inputRuntime=new f,this.inputOverrides=new Map,this.finishRequested=!1,this.completionEmitted=!1,this.inputsMaterialized=!1,this._worldPos=new s.Vector3,this._worldRot=new s.Quaternion,this._worldEul=new s.Euler,this._worldScale=new s.Vector3,this.previewStepSeconds=1/60,this.maxPreviewSteps=240,this.max=0}get timescale(){return this._timescale}set timescale(t){this._timescale=Math.max(0,t),this.gpuHandle?.setTimescale(this._timescale)}async fromAsset(t,e,i,r){if("vfx"!==t.type)throw"Asset must be a VFX asset but is "+t.type;null!=this.system&&(this.system.destroy(),this.system.emitters.forEach(t=>t.reset())),this.gpuHandle?.release(),this.gpuPrepared?.release(),this.gpuHandle=null,this.gpuPrepared=null,this.runtimeBackend="cpu",this.renderBodies=[],this.renderPreparation=void 0,this.backendFallbackReasons=[],this.simulationCpuMs=0,this.sourceAsset=t,this.finishRequested=!1,this.completionEmitted=!1,this.inputsMaterialized=!1,this.disposeSystem&&this.disposeSystem(),null==this.object.parent&&this.world.scene.add(this.object),this.inputRuntime.setDefinitions(this.sourceAsset.vfx.inputs??[]);for(const t of this.inputRuntime.getDefinitions()){if("mesh"!==t.type||"string"!=typeof t.defaultValue||0===t.defaultValue.length)continue;const e=await this.assetService.getAsset(t.defaultValue);if(null==e)continue;const s=await this.assetManagerService.getMesh(e,{applyMaterials:!0,rescale:!0});this.inputRuntime.setInput(t.id,s.scene)}for(const[t,e]of this.inputOverrides)this.inputRuntime.setInput(t,e);null!=e&&this.setInputs(e);const n=i??this.view.vfxBackendPreference,a=C(this.sourceAsset,r),o=a.gpu,l=j(o);let p=this.sourceAsset;if("cpu"===n&&(this.backendFallbackReasons=["GPU VFX was not requested; the selected VFX backend preference is CPU."]),"cpu"!==n&&!l&&o.vfx.emitters.length>0){const t=new Map;for(const e of this.inputRuntime.getDefinitions()){if("mesh"!==e.type)continue;const s=this.inputRuntime.getMesh(e.id);null!=s&&t.set(e.id,s)}if(this.gpuPrepared=await this.gpuWorld.prepare(o,r,t),null!=this.gpuPrepared){if(this.runtimeBackend=a.cpu.vfx.emitters.length>0?"mixed":"gpu",this.system=null,this.disposeSystem=null,this.particleSystemContainer=null,this.object.visible=!1,this.object.matrixAutoUpdate=!0,this.object.matrixWorldAutoUpdate=!0,this.inputsMaterialized=!0,0===a.cpu.vfx.emitters.length)return;p=a.cpu}if(null==this.gpuPrepared&&(this.backendFallbackReasons=this.gpuWorld.getFallbackReasons(o,r)),"gpu"===n&&null==this.gpuPrepared){const t=S(o,[new s.Vector3]).compatibility;this.backendFallbackReasons=t.issues.filter(t=>"warning"!==t.severity).map(t=>`${t.emitter}: ${t.message}`),0===this.backendFallbackReasons.length&&(this.backendFallbackReasons=["GPU template or material preparation failed; inspect earlier shader diagnostics."])}}const{system:h,dispose:m,container:d,renderBodies:c}=await u(p,this.object,{getAsset:t=>this.assetLoader.getAsset(t),getMaterial:t=>this.assetLoader.getMaterialByAssetId(t),getTexture:t=>this.assetLoader.getTextureByAssetId(t),getMesh:async t=>(await this.assetManagerService.getMesh(await this.assetService.getAsset(t),{applyMaterials:!0,rescale:!0})).scene},this.view,this.physics,this.shaderProvider,this.assetService,this.assetManagerService,this.inputRuntime,(t,e,s,i,r)=>{if(null==this.onCollision)return;const n=this.collisionEvent;n.emitterId=t,n.emitterName=e,n.position.copy(s),n.normal.copy(i),r&&(n.position.applyMatrix4(this.object.matrixWorld),n.normal.transformDirection(this.object.matrixWorld)),this.onCollision(n)});this.system=h,this.renderBodies=c,this.disposeSystem=m,this.particleSystemContainer=d,this.inputsMaterialized=!0,this.object.visible=!1,this.object.matrixAutoUpdate=!1,this.object.matrixWorldAutoUpdate=!1,this.particleSystemContainer.matrixAutoUpdate=!1,this.particleSystemContainer.matrixWorldAutoUpdate=!1}prepareRendering(){return this.renderPreparation??(this.renderPreparation=this.prepareRenderResources())}async prepareRenderResources(){0===this.renderBodies.length||this.renderBodies.every(t=>"pointLight"===t.type)||await R(this.renderBodies,this.view,this.renderingView)}setInput(t,e){const i=this.inputRuntime.getDefinition(t);if(this.inputsMaterialized&&"mesh"===i?.type)return void console.warn(`VFX mesh input "${i.name}" is creation-time only; recreate the VFX to replace it.`);this.inputOverrides.set(t,function(t){if(t instanceof s.Vector3||t instanceof s.Color)return t.clone();if(Array.isArray(t))return[...t];return t}(e)),this.inputRuntime.setInput(t,e);const r=this.inputRuntime.getDefinition(t);null!=r&&this.gpuHandle?.setInput(r.id,this.inputRuntime.getValue(r.id))}setInputs(t){for(const[e,s]of Object.entries(t))this.setInput(e,s)}play(){if(this.prepareForPlayback(),this.paused=!1,null!=this.gpuPrepared){if(this.object.getWorldPosition(this._worldPos),!1===this.gpuHandle?.active&&null!=this.system&&!this.cpuSimulationComplete())return;if(null!=this.gpuHandle&&this.gpuHandle.active)this.gpuHandle.setPosition(this._worldPos),this.gpuHandle.setTransform(this.object.matrixWorld),this.gpuHandle.play();else{if(null!=this.system)for(const t of this.system.emitters)this.restartEmitter(t);this.finishRequested=!1,this.completionEmitted=!1,this.gpuHandle=this.gpuPrepared.spawn(this._worldPos),this.gpuHandle?.setTransform(this.object.matrixWorld),this.gpuHandle?.setTimescale(this._timescale);for(const t of this.inputRuntime.getDefinitions())this.gpuHandle?.setInput(t.id,this.inputRuntime.getValue(t.id))}return}(this.system?.emitters.every(t=>t.dead)??!1)&&this.restart()}pause(){this.paused=!0,this.gpuHandle?.pause()}previewAt(t,e,s,i,r=0){null!=this.system&&(this.prepareForPlayback(),x(r,()=>{this.restart(),this.paused=!0;const r=Math.max(0,t),n=Math.min(r,Math.max(0,e)),a=r-n,o=Number(n>0)+Number(a>0),l=Math.max(o,Math.min(this.maxPreviewSteps,Math.max(1,Math.ceil(r/this.previewStepSeconds)))),p=n<=0?0:a<=0?l:Math.min(l-1,Math.max(1,Math.round(l*n/r)));this.simulatePreview(n,p),a>0&&(this.applyClipEndBehavior(s,i),this.simulatePreview(a,l-p))}))}clearPreview(){this.restart(),this.pause(),this.object.visible=!1,null!=this.particleSystemContainer&&(this.particleSystemContainer.visible=!1)}stop(){this.finishRequested=!0,null!=this.gpuPrepared&&this.gpuHandle?.stop(),null!=this.system?.emitters&&this.system.emitters.forEach(t=>{this.stopEmitterEmission(t)})}applyClipEndBehavior(t,e){const s=t??"finish";if("finish"!==s){if(null!=this.gpuPrepared&&(this.finishRequested=!0,"kill"===s?this.gpuHandle?.kill():this.gpuHandle?.expire(Math.max(0,e??0))),null!=this.system&&null!=this.system.emitters)for(const t of this.system.emitters)this.applyEmitterClipEndBehavior(t,s,e)}else this.stop()}restart(){if(null!=this.system)for(const t of this.system.emitters)this.restartEmitter(t);if(null==this.gpuPrepared)null!=this.system&&null!=this.system.emitters&&(this.finishRequested=!1,this.completionEmitted=!1);else if(this.finishRequested=!1,this.completionEmitted=!1,null!=this.gpuHandle){if(!this.gpuHandle.active)return this.gpuHandle.release(),this.gpuHandle=null,void(this.paused||this.play());this.object.getWorldPosition(this._worldPos),this.gpuHandle.setPosition(this._worldPos),this.gpuHandle.setTransform(this.object.matrixWorld),this.gpuHandle.restart()}}restartEmitter(t){const e=t.rate;e instanceof h?e.restart():e instanceof i&&(e.nextTime=0),null==t.clearParticlesRecursive?this.removeEmitterParticlesRecursive(t):t.clearParticlesRecursive()}removeEmitterParticlesRecursive(t){const e=t.childEmitters??[];for(const t of e)this.removeEmitterParticlesRecursive(t);t.removeAllParticles?.()}onUpdate(t){this.paused?null!=this.gpuHandle&&(this.object.getWorldPosition(this._worldPos),this.gpuHandle.setPosition(this._worldPos),this.gpuHandle.setTransform(this.object.matrixWorld)):null==this.gpuPrepared||(this.object.getWorldPosition(this._worldPos),this.gpuHandle?.setPosition(this._worldPos),this.gpuHandle?.setTransform(this.object.matrixWorld),null!=this.system)?(this.updateWorldSpaceEmitterTransforms(),this.updateSystem(t*this.timescale),this.updateCompletionState()):this.updateCompletionState()}prepareForPlayback(){null!=this.particleSystemContainer&&null==this.particleSystemContainer.parent&&this.world.scene.add(this.particleSystemContainer),this.object.matrixAutoUpdate=!0,this.object.matrixWorldAutoUpdate=!0,null!=this.particleSystemContainer&&(this.particleSystemContainer.matrixAutoUpdate=!0,this.particleSystemContainer.matrixWorldAutoUpdate=!0),this.object.visible=!0,null!=this.particleSystemContainer&&(this.particleSystemContainer.visible=!0)}simulatePreview(t,e){if(t<=0)return;const s=t/e;for(let t=0;t<e;t++)this.updateWorldSpaceEmitterTransforms(),this.updateSystem(s)}updateWorldSpaceEmitterTransforms(){this.object.getWorldPosition(this._worldPos),this.object.getWorldQuaternion(this._worldRot),this.object.getWorldScale(this._worldScale),this._worldEul.setFromQuaternion(this._worldRot),this.system?.emitters.forEach(t=>{if("world"===t._space){t.setPosition(this._worldPos),t.setRotation(this._worldEul);(t._worldScale??(t._worldScale=new s.Vector3)).copy(this._worldScale)}})}getParticleCount(){return this.system?.getCount()??0}getProfilingStats(){let t=0,e=0;return this.particleSystemContainer?.traverse(i=>{const r=i,n=r.geometry;if(null==n||!1===r.visible)return;const a=r instanceof s.InstancedMesh?r.count:1,o=n.index?.count??n.getAttribute("position")?.count??0;a<=0||o<=0||(t++,e+=Math.floor(o/3)*a)}),{particles:this.system?.getCount()??0,drawCalls:t,triangles:e,cpuTimeMs:this.simulationCpuMs}}onEndPlay(){this.stop(),null!=this.disposeSystem&&this.disposeSystem(),this.gpuHandle?.release(),this.gpuPrepared?.release(),this.gpuHandle=null,this.gpuPrepared=null,this.renderBodies=[],this.renderPreparation=void 0,this.onComplete.complete()}cpuSimulationComplete(){return null==this.system||this.system.emitters.every(t=>{if(t.dead)return!0;const e=t;return!((e.particles?.length??0)>0||(e.childEmitters?.length??0)>0)&&(this.finishRequested||e.rate instanceof h&&e.rate.emissionComplete)})}updateCompletionState(){if(this.completionEmitted||!this.onComplete.observed)return;const t=this.cpuSimulationComplete(),e=null==this.gpuPrepared||!1===this.gpuHandle?.active;t&&e&&(this.completionEmitted=!0,this.onComplete.next())}updateSystem(t){if(null==this.system)return;if(!this.profilingEnabled)return void this.system.update(t);const e=performance.now();this.system.update(t),this.simulationCpuMs=performance.now()-e}applyEmitterClipEndBehavior(t,e,s,i=!1){this.stopEmitterEmission(t,i);const r=t.particles??[];if("kill"===e)t.removeAllParticles?.();else{const t=Math.max(0,s??0);for(const e of r)e.life=Math.min(e.life,e.age+t)}const n=t.childEmitters??[];for(const t of n)this.applyEmitterClipEndBehavior(t,e,s,!0)}stopEmitterEmission(t,e=!1){const s=t.rate;s instanceof i&&(s.nextTime=1/0),e&&t.stopEmit?.()}};t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],M.prototype,"play",null),t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],M.prototype,"pause",null),t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],M.prototype,"stop",null),t([v({parameters:[],returns:w}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],M.prototype,"restart",null),M=t([r({typeId:P})],M);export{M as VfxActor};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -150,7 +150,16 @@ export type ShapeOutput = {
|
|
|
150
150
|
material: AssetId;
|
|
151
151
|
bloom?: boolean;
|
|
152
152
|
} & CommonOutput & Partial<OutputCustomShader> & OutputMaterialSourceSettings;
|
|
153
|
-
|
|
153
|
+
/** Unshadowed transient lights. Alpha controls intensity; particle scale controls radius. */
|
|
154
|
+
export type PointLightOutput = {
|
|
155
|
+
type: 'pointLight';
|
|
156
|
+
color: string;
|
|
157
|
+
intensity: number;
|
|
158
|
+
radius: number;
|
|
159
|
+
/** Per-emitter live light cap; clamped to 0..32 at materialization. */
|
|
160
|
+
maxLights: number;
|
|
161
|
+
} & CommonOutput & OutputMaterialSourceSettings;
|
|
162
|
+
export type EmitterOutput = PointLightOutput | SpriteOutput | MeshOutput | ShapeOutput | StretchedSpriteOutput | TrailOutput | DecalOutput;
|
|
154
163
|
export type EmitterData = {
|
|
155
164
|
id: string;
|
|
156
165
|
name: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { VfxAsset } from '../../scene/model.js';
|
|
2
|
+
/** Never split parent/child simulation dependencies across backends. */
|
|
3
|
+
export declare function partitionVfxBackends(asset: VfxAsset, revision?: string | number): {
|
|
4
|
+
revision?: string | number;
|
|
5
|
+
gpu: VfxAsset;
|
|
6
|
+
cpu: VfxAsset;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=vfx-backend-partition.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const t=new WeakMap;export function partitionVfxBackends(n,o){const i=t.get(n);if(null!=i&&i.revision===o)return i;const r=[],s=[];for(const t of n.vfx.emitters)(e(t)?r:s).push(t);const f={revision:o,gpu:0===r.length?n:{...n,vfx:{...n.vfx,emitters:s}},cpu:{...n,vfx:{...n.vfx,emitters:r}}};return t.set(n,f),f}function e(t){return"pointLight"===t.output.type||(t.children?.some(e)??!1)}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e,{Behaviour as t,Body as a,Emitter as r,Rate as s}from"@hology/nebula";import*as i from"three";import{Object3D as n}from"three";import{AttributeVec3Node as o,AttributeVec4Node as l,NodeShaderMaterial as p,RgbNode as c,UniformFloatNode as u,UniformVec3Node as d,Vec3ExpressionNode as h,attributeVec3 as m,attributeVec4 as f,attributes as y,clamp as w,cross as g,float as b,lambertMaterial as v,length as M,log as E,normalize as A,pow as x,rgb as S,rgba as I,smoothstep as P,standardMaterial as T,textureSampler2d as C,uniforms as R,varying as G,varyingAttributes as B,varyingVec3 as k,varyingVec4 as j,vec2 as D,vec3 as F,vec4 as V}from"three-shader-graph";import{prepareClassParameters as L,prepareShaderGraphParameters as O,prepareShapeParameters as W}from"../../scene/materializer.js";import{SerializedParamType as U}from"../../scene/model.js";import{ShapeLibrary as N}from"../../scene/objects/shapes.js";import{fragmentLinearEyeDepth as Q,linearEyeDepth as z}from"../../shader-nodes/depth.js";import{particleEnergyUniformName as H,particleTimeUniformName as q,particleUniforms as J,particleVelcoityUniformName as X}from"../../shader-nodes/particle.js";import{sampleFlipbook as _}from"../../shader-nodes/texture-sequence.js";import{DefaultInitializer as K,WorldSpaceEmitterTransformInitializer as Y}from"./initializsers.js";import{DelayRate as Z,OnceRate as $}from"./rates.js";import{StretchedSprite as ee}from"./stretched-sprite.js";import{ThreeBlendingMode as te}from"./vfx-asset.js";import{VfxBehaviourLibrary as ae,VfxInitializserLibrary as re}from"./vfx-defs.js";import{MultiRenderer as se}from"./vfx-renderers.js";import{WorldCollisionBehaviour as ie}from"./vfx-collision-behaviour.js";import{getSpritePosition as ne,SpriteNodeShader as oe,SpriteNodeShaderMaterial as le}from"../../shader/sprite-shader.js";import{DecalUnlitShader as pe}from"../../shader/builtin/decal-unlit-shader.js";import{DecalStandardShader as ce}from"../../shader/builtin/decal-standard-shader.js";import{createBoundBehaviour as ue,createBoundInitializer as de}from"./vfx-binding-runtime.js";import{buildShaderGraphMaterial as he}from"../../shader/graph/index.js";import{markMaterialUniformValuesNeedUpdate as me,setMaterialUniformValuesAutoUpdate as fe}from"../../rendering/node-material-webgpu-resolver.js";import{applyMeshWorldTransformToGeometry as ye}from"./vfx-mesh-transform.js";import{createGpuMeshAssetMaterial as we}from"./vfx-gpu-material.js";export async function materializeVfx(t,a,r,s,o,l,p,c,u,d){let h=a;for(;null!=h.parent;)h=h.parent;const m=new n;m.name="particle system local",a.add(m);const f=new n;f.name="particle system world";const y=new se(f,m,i,s),w=new e,g=[];return(await Promise.all(t.vfx.emitters.slice().sort((e,t)=>(t.output.renderOrder??0)-(e.output.renderOrder??0)).map(async e=>{const t=await be(e,r,p,c,w,o,l,u,a,d,g);return t.setParentRecursive(w),t.emit()}))).forEach(e=>w.addEmitter(e)),w.addRenderer(y).emit({onEnd:()=>{}}),{renderBodies:g,container:f,system:w,dispose:()=>{f.removeFromParent(),m.removeFromParent(),y.dispose()}}}function ge(e,t){let a;const r=function(e,t){const a=Math.max(0,e.rate.delay??0),r=e.rate.delayBinding;if("vfxInput"!==r?.type||null==t)return a;const s=t.getDefinition(r.inputId);return"number"!==s?.type?a:Math.max(0,t.getNumber(r.inputId,a))}(e,t);switch(e.rate.type){case"continuous":a=new Z(r,e.rate.count,e.rate.time,e.rate.duration);break;case"once":a=new $(r,e.rate.count);break;default:console.warn(`Failed to configure rate for emitter: ${JSON.stringify(e)}`),a=new s(0,1/0)}return a}async function be(e,t,r,s,c,x,S,T,D,F,L){const O=ge(e,T);let H;switch(e.output.type){case"decal":H=new a(await async function(e,t,a,r,s){if("shaderGraph"===Me(e)){const t=await createShaderGraphMaterial(e,"decal",a,r,s);if(null!=t){const a=new i.Mesh(new i.BoxGeometry(1,1,1),t);return Ae(a,e),a}}const n=!1!==e.unlit?new pe:new ce;n.color=new i.Color(e.color),n instanceof pe?n.intensity=e.intensity??1:n.emissiveIntensity=e.intensity??1;if(e.colorMap){const a=await t.getTexture(e.colorMap);n.colorMap=a}if(e.alphaMap){const a=await t.getTexture(e.alphaMap);n.alphaMap=a}const o=n.build();o.blending=te[e.blendingMode]??i.NormalBlending,o.transparent=!0,o.side=i.BackSide,o.depthTest=!1,null!=o&&!0===e.bloom&&(o.userData.hasBloom=!0);o.defines.IS_PARTICLE="";const l=new i.BoxGeometry(1,1,1),p=new i.Mesh(l,o);return Ae(p,e),p}(e.output,t,r,s,S));break;case"sprite":H=new a(await async function(e,t,a,r,s){let n=null;const o=Me(e);"shaderGraph"===o&&(n=await createShaderGraphMaterial(e,"sprite",a,r,s));null==n&&"shader"===o&&null!=e.shader&&(n=await createShaderMaterial(e,t,s));null==n&&(n=await async function(e,t){const a=null!=e.texture?await t.getTexture(e.texture):ve,r=C(a);let s=r.sample(B.uv);e.flipbook?.enabled&&(s=_(r,y.uv,new u("vfxFlipbookColumns",e.flipbook.columns,void 0,!1),new u("vfxFlipbookRows",e.flipbook.rows,void 0,!1),J.time,new u("vfxFlipbookFps",e.flipbook.fps,void 0,!1),e.flipbook.mode));const n=new u("rotation",0),o=new d("color").rgb,l=k(new h("instanceColor")).rgb,p=z.subtract(Q).divide(z);let c=J.opacity;switch(e.opacityChannel??"red"){case"none":break;case"red":c=c.multiply(s.r);break;case"alpha":c=c.multiply(s.a)}if("number"==typeof e.softness&&e.softness>0){const t=w(p,0,1e3);c=c.multiply(P(b(0),new u("vfxSoftness",.2*e.softness,void 0,!1),t))}const m=ne(n,void 0,void 0,!0),f=I(o.multiply(l).multiply(s.rgb).multiplyScalar(new u("vfxIntensity",e.intensity??1,void 0,!1)),c);var g=new le({color:f,emissive:f.rgb.multiplyScalar(c),transparent:!0,position:m,alphaTest:1e-4,uniforms:{color:{value:new i.Color(e.color)}}});g.alphaHash=!0,null!=g&&!0===e.bloom&&(g.userData.hasBloom=!0);return g.blending=te[e.blendingMode]??i.NormalBlending,g}(e,t));n.depthWrite=!1,Ee(n,e);const l=new i.Mesh(new i.PlaneGeometry(1,1),n);return l.name="sprite",Ae(l,e),l}(e.output,t,r,s,S));break;case"stretchedSprite":H=new a(await async function(e,t){"shaderGraph"===Me(e)&&console.warn("Shader graph materials are not supported for stretched sprite VFX outputs yet. Falling back to the default stretched sprite material.");const a=null!=e.texture?await t.getTexture(e.texture):ve,r=C(a).sample(B.uv),s=j(new l("particleColor")),n=s.rgb.multiply(r.rgb);let o=s.w;switch(e.opacityChannel??"red"){case"none":break;case"red":o=o.multiply(r.r);break;case"alpha":o=o.multiply(r.a)}if("number"==typeof e.softness&&e.softness>0){const t=z.subtract(Q).divide(E(z)),a=w(t,0,1e3);o=o.multiply(P(b(0),new u("vfxSoftness",.2*e.softness,void 0,!1),a))}const p=m("offset"),c=f("velocity"),d=m("size"),h=c.w,v=d.x.add(d.y).multiply(.5),x=R.modelViewMatrix.multiplyVec(V(p,1)),S=R.normalMatrix.multiplyVec(c.xyz),T=A(g(x.xyz,S)).multiplyScalar(y.position.y.multiply(v)),G=S.multiplyScalar(y.position.x.add(.5).multiply(b(1).add(h.divide(M(S)))).multiply(v)),k=R.projectionMatrix.multiplyVec(V(x.xyz.add(T).subtract(G),x.w));var D=new le({color:I(n.multiplyScalar(new u("vfxIntensity",e.intensity??1,void 0,!1)),o),alphaTest:.1,transparent:!0,position:k,uniforms:{color:{value:new i.Color(e.color)}}});null!=D&&!0===e.bloom&&(D.userData.hasBloom=!0);D.blending=te[e.blendingMode]??i.NormalBlending;const F=new ee(new i.PlaneGeometry(1,1),D);return F.scaleFactor=e.scale,Ae(F,e),F}(e.output,t));break;case"shape":H=new a(await async function(e,t,a,r,s){if(null==e.shape)return console.log("Shape is null"),new n;const l=N[e.shape];if(null==l)return console.error(`No shape with type ${e.shape}`),new n;const c=W(e.params??{}),u=l.geometry(c);let d=null;const m=Me(e);"shaderGraph"===m&&(d=await createShaderGraphMaterial(e,"surface",a,r,s));null==d&&"shader"===m&&null!=e.shader&&(d=await createShaderMaterial(e,t,s));null==d&&(d="material"===m&&null!=e.material?xe(await t.getMaterial(e.material)):function(){const e=G(new h("instanceColor")).rgb,t=G(new o("particleData")).x;return new p({color:v({color:e}).rgb.rgba(t),opacity:t,transparent:!0})}());Ee(d,e);const f=new i.Mesh(u,d);return Ae(f,e),f}(e.output,t,r,s,S));break;case"mesh":H=new a(await async function(e,t,a,r,s){if(null==e.assetId)return console.warn("Can't use mesh as particle without asset id"),new n;const o=await t.getMesh(e.assetId),l=await t.getAsset(e.assetId),p=Me(e);if("shaderGraph"===p||"shader"===p&&null!=e.shader||"material"===p&&null!=e.material){let n;"shaderGraph"===p?n=await createShaderGraphMaterial(e,"surface",a,r,s):"shader"===p&&null!=e.shader?n=await createShaderMaterial(e,t,s):"material"===p&&null!=e.material&&(n=xe(await t.getMaterial(e.material))),n?.transparent&&(n.depthWrite=!1),null!=n&&Ee(n,e),o.traverse(e=>{e instanceof i.Mesh&&null!=n&&(e.material=n)})}else{const e=[];if(null!=l.materialAssignments)for(const a of l.materialAssignments)null!=a.materialId&&o.traverse(r=>{r instanceof i.Mesh&&r.material instanceof i.Material&&r.material.color instanceof i.Color&&(r.material.name!=a.name&&null!=a.name||"#"+r.material.color.getHexString()!==a.color||e.push(t.getMaterial(a.materialId).then(e=>r.material=e)))});await Promise.all(e)}const c=[];if(o.traverse(e=>{e instanceof i.Mesh&&c.push(e)}),Ae(o,e),1===c.length){const t=c[0];return!0!==e.useLegacyTransform&&function(e){e.geometry=e.geometry.clone(),ye(e,e.geometry),e.position.set(0,0,0),e.quaternion.identity(),e.scale.set(1,1,1),e.updateMatrix(),e.matrixWorld.identity()}(t),t.removeFromParent(),t}return o}(e.output,t,r,s,S));break;case"trail":{const n=e.output,o="shaderGraph"===Me(e.output)?await createShaderGraphMaterial(e.output,"trail",r,s,S,{trailBillboard:e.output.billboard??!1}):null;H=new a({type:"trail",taper:e.output.taper,headGeometry:null,material:o instanceof i.ShaderMaterial?o:void 0,dragTexture:!1,texture:null!=e.output.texture?await t.getTexture(e.output.texture).then(e=>{if("repeat"!==n.uvMode)return e;const t=e.clone();return t.wrapS=i.RepeatWrapping,t.needsUpdate=!0,t}):null,opacityChannel:e.output.opacityChannel,color:e.output.color,colorEnd:e.output.colorEnd,intensity:e.output.intensity??1,intensityEnd:e.output.intensityEnd??1,length:e.output.length,opacityStart:e.output.opacityStart,opacityEnd:e.output.opacityEnd,bloom:e.output.bloom,scrollSpeed:e.output.scrollSpeed,uvMode:e.output.uvMode,unitsPerRepeat:e.output.unitsPerRepeat,width:e.output.width,billboard:e.output.billboard??!1,renderOrder:e.output.renderOrder});break}default:console.error("Failed to create particly system body: "+JSON.stringify(e))}const q=new Ie(D);null!=H&&L?.push(H.body.getValue()),q.parent=c,q.setRate(O),q._space=e.output.space;const X=await Promise.all(e.initializers.filter(e=>!1!==e.enabled).filter(e=>null!=re[e.type]).map(async e=>{const t=re[e.type],a=await W(e.params??{},r,s);return de(t,e.params??{},a,T)}));"world"===e.output.space&&X.push(new Y("shape"===e.output.type||"mesh"===e.output.type)),X.push(H,new K),q.addInitializers(X);const Z=await Promise.all(e.behaviours.filter(e=>!1!==e.enabled).filter(e=>null!=ae[e.type]).map(async e=>{const t=ae[e.type];for(const[a,r]of Object.entries(e.params))t.parameters&&null!=t.parameters[a]&&"curve"===t.parameters[a].type&&r.type!==U.Curve&&(r.type=U.Curve);const a=await W(e.params??{},r,s);return ue(t,e.params??{},a,T)}));Z.sort((e,t)=>Number(t instanceof ie)-Number(e instanceof ie)),Z.push(new Se);for(const t of Z)t instanceof ie&&(t.physics=x,t.emitEvents&&null!=F&&(t.onCollision=(t,a)=>F(e.id,e.name,t,a,"local"===e.output.space)));q.addBehaviours(Z);for(const a of e.children??[]){const e=await be(a,t,r,s,c,x,S,T,D,F,L),i=new EmitterPool(()=>{const t=e.clone();return t.onExpired=()=>{const e=q.childEmitters.findIndex(e=>e.id===t.id);-1!=e&&q.childEmitters.splice(e,1),null!=t.parentParticle&&(o.delete(t.parentParticle.id),t.parentParticle=null),i.release(t)},t}),n=q.eventDispatcher,o=new Map;q.bindEmitterEvent=!0,n.addEventListener("PARTICLE_DEAD",e=>{const t=o.get(e.id);null!=t&&(q.detachChildEmitterFromParticle(t,e),o.delete(e.id))});let l="PARTICLE_CREATED";if("spawnEvent"in a)switch(a.spawnEvent){case"collision":l="PARTICLE_COLLISION";break;case"start":l="PARTICLE_CREATED"}n.addEventListener(l,e=>{const t=i.get();t.age=0,t.totalEmitTimes=-1,t.particles.length=0,t.currentEmitTime=0,t.cID=0,t.eventDispatcher.removeAllEventListeners(),q.childEmitters.push(t),o.set(e.id,t),t.parentParticle=e,t.system=q.system,t.emit()})}return q}const ve=(new i.TextureLoader).load("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJkSURBVHjaxJeJbusgEEW94S1L//83X18M2MSuLd2pbqc4wZGqRLrKBsyZhQHny7Jk73xVL8xpVhWrcmiB5lX+6GJ5YgQ2owbAm8oIwH1VgKZUmGcRqKGGPgtEQQAzGR8hQ59fAmhJHSAagigJ4E7GPWRXOYC6owAd1JM6wDQPADyMWUqZRMqmAojHp1Vn6EQQEgUNMJLnUjMyJsM49wygBkAPw9dVFwXRkncCIIW3GRgoTQUZn6HxCMAFEFd8TwEQ78X4rHbILoAUmeT+RFG4UhQ6MiIAE4W/UsYFjuVjAIa2nIY4q1R0GFtQWG3E84lqw2GO2QOoCKBVu0BAPgDSU0eUDjjQenNkV/AW/pWChhpMTelo1a64AOKM30vk18GzTHXCNtI/Knz3DFBgsUqBGIjTInXRY1yA9xkVoqW5tVq3pDR9A0hfF5BSARmVnh7RMDCaIdcNgbPBkgzn1Bu+SfIEFSpSBmkxyrMicb0fAEuCZrWnN89veA/4XcakrPcjBWzkTuLjlbfTQPOlBhz+HwkqqPXmPQDdrQItxE1moGof1S74j/8txk8EHhTQrAE8qlwfqS5yukm1x/rAJ9Jiaa6nyATqD78aUVBhFo8b1V4DdTXdCW+IxA1zB4JhiOhZMEWO1HqnvdoHZ4FAMIhV9REF8FiUm0jsYPEJx/Fm/N8OhH90HI9YRHesWbXXZwAShU8qThe7H8YAuJmw5yOd989uRINKRTJAhoF8jbqrHKfeCYdIISZfSq26bk/K+yO3YvfKrVgiwQBHnwt8ynPB25+M8hceTt/ybPhnryJ78+tLgAEAuCFyiQgQB30AAAAASUVORK5CYII=");export async function createShaderMaterial(e,t,a){const r=a.get(e.shader);if(null==r)return console.error("No shader exists with name "+e.shader),new i.Material;const s=new r.type;"sprite"===e.type&&s instanceof oe&&(s.instanced=!0);const n=await L(e.shaderParams??{},r.type,t,{getTexture:e=>t.getTexture(e.id),getMaterial:e=>t.getMaterial(e.id),getMesh:e=>t.getMesh(e.id)},void 0,void 0,void 0,void 0,void 0,s);Object.assign(s,n);const o=s.build();return o.userData.vfxShader={key:"shader:"+e.shader,name:e.shader},o}export async function createShaderGraphMaterial(e,t,a,r,s,i={}){const n=e.shaderGraph;if("asset"!==n?.source||null==n.assetId)return console.warn(`Missing shader graph asset reference for ${e.type} VFX output`),null;const o=await a.getAsset(n.assetId),l=o?.shaderGraph;if(null==l)return console.warn(`Missing shader graph asset "${n.assetId}" for ${e.type} VFX output`),null;if(l.target!==t)return console.warn(`Shader graph "${o.name}" targets "${l.target}" but ${e.type} VFX output expects "${t}"`),null;const p="sprite"===t||"decal"===t||"surface"===t&&"unlit"===l.shadingModel;try{const t=await O(e.shaderGraphParams??{},l,a,r,void 0,s.shaders,p),c=he(l,{parameters:t,trailBillboard:i.trailBillboard});return c.userData.customShaderName=`shaderGraph:${n.assetId}`,c.userData.vfxShader={key:"shaderGraph:"+n.assetId,name:o.name,target:l.target,shadingModel:l.shadingModel,animationLayout:p?"shared":"specialized",parameters:Object.fromEntries((l.parameters??[]).map(t=>{const a=e.shaderGraphParams?.[t.name],r=!1!==a?.override&&null!=a?.value?a.value:t.defaultValue,s=null!=r&&"object"==typeof r&&"a"in r&&"b"in r&&null!=r.b;return[t.name,s?{mode:"animated",easing:"easing"in r?r.easing:void 0}:{mode:"static"}]}))},Ee(c,e),c}catch(e){return console.log("Shader graph VFX runtime error: "+e,e),null}}export function createGpuSpriteShaderGraphResolver(e,t,a,r){return async s=>{const n=Me(s);let o=null;if("shaderGraph"===n)o=await createShaderGraphMaterial(s,"sprite"===s.type?"sprite":"decal"===s.type?"decal":"trail"===s.type?"trail":"surface",e,t,a,"trail"===s.type?{trailBillboard:s.billboard??!1}:void 0);else if("shader"===n&&"decal"!==s.type&&null!=r)o=await createShaderMaterial(s,r,a);else if("material"!==n||"mesh"!==s.type&&"shape"!==s.type||null==s.material||null==r){if("default"===n&&"mesh"===s.type&&null!=s.assetId&&null!=r){const e=await r.getAsset(s.assetId),t=e.materialAssignments?.find(e=>null!=e.materialId);null!=t&&(o=await we(t.materialId,r))}else if("default"===n&&"decal"===s.type&&null!=r){const e=!1!==s.unlit?new pe:new ce;e.color=new i.Color(s.color),e instanceof pe?e.intensity=s.intensity??1:e.emissiveIntensity=s.intensity??1,s.colorMap&&(e.colorMap=await r.getTexture(s.colorMap)),s.alphaMap&&(e.alphaMap=await r.getTexture(s.alphaMap)),o=e.build(),o.transparent=!0}}else o="mesh"===s.type?await we(s.material,r):xe(await r.getMaterial(s.material));return null==o?null:("mesh"!==s.type&&"shape"!==s.type||!o.transparent||(o.depthWrite=!1),o instanceof p?o:(console.warn(`GPU ${s.type} materials must compile to NodeShaderMaterial; using CPU fallback`),null))}}function Me(e){return null!=e.materialSource?("material"!==e.materialSource||"mesh"!==e.type&&"shape"!==e.type||null!=e.material)&&("shaderGraph"!==e.materialSource||"asset"===e.shaderGraph?.source&&null!=e.shaderGraph.assetId)&&("shader"!==e.materialSource||"shader"in e&&null!=e.shader)?e.materialSource:"default":"asset"===e.shaderGraph?.source?"shaderGraph":"shader"in e&&null!=e.shader?"shader":"mesh"!==e.type&&"shape"!==e.type||null==e.material?"default":"material"}function Ee(e,t){null!=e&&(fe(e,!1),"bloom"in t&&!0===t.bloom&&(e.userData.hasBloom=!0),"sprite"===t.type?(e.blending=te[t.blendingMode]??i.NormalBlending,!0===t.lockY&&e instanceof i.ShaderMaterial&&(e.defines??(e.defines={}),e.defines.LOCK_Y_AXIS="")):"decal"===t.type?(e.blending=te[t.blendingMode]??i.NormalBlending,e.transparent=!0,e.side=i.BackSide,e.depthTest=!1,e instanceof i.ShaderMaterial&&(e.defines??(e.defines={}),e.defines.IS_PARTICLE="")):"shape"!==t.type&&"mesh"!==t.type||e instanceof i.ShaderMaterial&&(e.defines??(e.defines={}),e.defines.IS_PARTICLE=""))}function Ae(e,t){const a=t.renderOrder;null!=a&&e.traverse(e=>e.renderOrder=a)}function xe(e){const t=G(new h("instanceColor")).rgb;let a,r=G(new o("particleData")).x;if(e instanceof i.MeshStandardMaterial||e instanceof i.MeshLambertMaterial||e instanceof i.MeshBasicMaterial){let s=t.multiply(S(e.color));null!=e.map&&(s=s.multiply(C(e.map).sample(B.uv).rgb)),null!=e.alphaMap&&(r=r.multiply(C(e.alphaMap).sample(B.uv).r)),e instanceof i.MeshStandardMaterial?a=new p({color:T({color:s,emissive:S(e.emissive),emissiveIntensity:b(e.emissiveIntensity),roughness:e.roughness,metalness:e.metalness}).rgb.rgba(r),emissive:S(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshLambertMaterial?a=new p({color:v({color:s}).rgb.rgba(r),emissive:S(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshBasicMaterial&&(a=new p({color:s.rgb.rgba(r),emissive:s.rgb,transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}))}else e instanceof p&&(a=e.clone(),a.defines.IS_PARTICLE="");return null!=a&&(!0===e.userData?.hasBloom&&null!=a&&(a.userData.hasBloom=!0),a.side=e.side,a.transparent=e.transparent),a??e}class Se extends t{initialize(e){e.body instanceof i.Object3D&&e.body.traverse(e=>{if(e instanceof i.Mesh){const t=e.material;t instanceof p&&(null!=t.uniforms[H]||null!=t.uniforms[q]||t.uniforms[X])}})}mutate(e,t,a){this.energize(e,t),e.target instanceof n&&e.target.traverse(t=>{if(t instanceof i.Mesh){const a=t.material;if(a instanceof p){let t=!1;null!=a.uniforms[H]&&(a.uniforms[H].value=this.energy,t=!0),null!=a.uniforms[q]&&(a.uniforms[q].value=e.age,t=!0),null!=a.uniforms[X]&&(a.uniforms[X].value=e.velocity,t=!0),t&&me(a)}}})}}class Ie extends r{constructor(e){super(),this.transformRoot=e,this.childEmitters=[],this.bindEmitterEvent=!1,this.onExpired=()=>{}}clearParticlesRecursive(){let e=this.childEmitters.length;for(;e--;){const t=this.childEmitters[e];t.stopEmit(),t.clearParticlesRecursive(),t.onExpired()}this.childEmitters.length=0;let t=this.particles.length;for(;t--;){const e=this.particles[t];this.system?.dispatch("PARTICLE_DEAD",e),this.bindEmitterEvent&&this.dispatch("PARTICLE_DEAD",e),null!=this.system?this.system.pool.expire(e.reset()):e.reset()}this.particles.length=0}detachChildEmitterFromParticle(e,t){this.copyPositionToChildSpace(e,t.position),e.parentParticle=null,e.stopEmit()}update(e){if(!this.isEmitting&&0===this.particles.length&&0===this.childEmitters.length)return;this.age+=e,(this.dead||this.age>=this.life)&&this.destroy(),this.isEmitting&&this.generate(e),this.integrate(e);let t=this.particles.length;for(;t--;){const e=this.particles[t];e.dead&&(this.system&&this.system.dispatch("PARTICLE_DEAD",e),this.bindEmitterEvent&&this.dispatch("PARTICLE_DEAD",e),this.system.pool.expire(e.reset()),this.particles.splice(t,1))}this.updateEmitterBehaviours(e),this.updateChildren(e),this.isEmitting||0!==this.particles.length||0!==this.childEmitters.length||this.onExpired()}updateChildren(e){for(const t of this.childEmitters)null!=t.parentParticle&&this.copyPositionToChildSpace(t,t.parentParticle.position),t.update(e)}copyPositionToChildSpace(e,t){const a=this._space??"world",r=e._space??"world";a!==r&&null!=this.transformRoot?(Pe.copy(t),"local"===a&&"world"===r?Pe.applyMatrix4(this.transformRoot.matrixWorld):"world"===a&&"local"===r&&(Te.copy(this.transformRoot.matrixWorld).invert(),Pe.applyMatrix4(Te)),e.position.copy(Pe)):e.position.copy(t)}clone(){const e=new Ie(this.transformRoot);return e.setRate(this.rate.clone()),e.behaviours=this.behaviours,e.initializers=this.initializers,e._space=this._space,e.body=this.body,e.parent=this.parent,e.system=this.system,e}setParentRecursive(e){this.system=e,this.childEmitters.forEach(t=>t.setParentRecursive(e))}}const Pe=new i.Vector3,Te=new i.Matrix4;export class EmitterPool{constructor(e){this.creator=e,this.instances=[]}get(){0==this.instances.length&&this.instances.push(this.creator());return this.instances.pop()}release(e){this.instances.push(e)}dispose(){this.instances.length=0}}/*
|
|
1
|
+
import e,{Behaviour as t,Body as a,Emitter as r,Rate as s}from"@hology/nebula";import*as i from"three";import{Object3D as n}from"three";import{AttributeVec3Node as o,AttributeVec4Node as l,NodeShaderMaterial as p,RgbNode as c,UniformFloatNode as u,UniformVec3Node as d,Vec3ExpressionNode as h,attributeVec3 as m,attributeVec4 as f,attributes as y,clamp as g,cross as w,float as b,lambertMaterial as v,length as M,log as E,normalize as A,pow as x,rgb as S,rgba as I,smoothstep as P,standardMaterial as T,textureSampler2d as C,uniforms as R,varying as G,varyingAttributes as B,varyingVec3 as k,varyingVec4 as j,vec2 as D,vec3 as F,vec4 as L}from"three-shader-graph";import{prepareClassParameters as V,prepareShaderGraphParameters as O,prepareShapeParameters as W}from"../../scene/materializer.js";import{SerializedParamType as U}from"../../scene/model.js";import{ShapeLibrary as N}from"../../scene/objects/shapes.js";import{fragmentLinearEyeDepth as Q,linearEyeDepth as z}from"../../shader-nodes/depth.js";import{particleEnergyUniformName as H,particleTimeUniformName as q,particleUniforms as J,particleVelcoityUniformName as X}from"../../shader-nodes/particle.js";import{sampleFlipbook as _}from"../../shader-nodes/texture-sequence.js";import{DefaultInitializer as K,WorldSpaceEmitterTransformInitializer as Y}from"./initializsers.js";import{DelayRate as Z,OnceRate as $}from"./rates.js";import{StretchedSprite as ee}from"./stretched-sprite.js";import{ThreeBlendingMode as te}from"./vfx-asset.js";import{VfxBehaviourLibrary as ae,VfxInitializserLibrary as re}from"./vfx-defs.js";import{MultiRenderer as se}from"./vfx-renderers.js";import{WorldCollisionBehaviour as ie}from"./vfx-collision-behaviour.js";import{getSpritePosition as ne,SpriteNodeShader as oe,SpriteNodeShaderMaterial as le}from"../../shader/sprite-shader.js";import{DecalUnlitShader as pe}from"../../shader/builtin/decal-unlit-shader.js";import{DecalStandardShader as ce}from"../../shader/builtin/decal-standard-shader.js";import{createBoundBehaviour as ue,createBoundInitializer as de}from"./vfx-binding-runtime.js";import{buildShaderGraphMaterial as he}from"../../shader/graph/index.js";import{markMaterialUniformValuesNeedUpdate as me,setMaterialUniformValuesAutoUpdate as fe}from"../../rendering/node-material-webgpu-resolver.js";import{applyMeshWorldTransformToGeometry as ye}from"./vfx-mesh-transform.js";import{createGpuMeshAssetMaterial as ge}from"./vfx-gpu-material.js";export async function materializeVfx(t,a,r,s,o,l,p,c,u,d){let h=a;for(;null!=h.parent;)h=h.parent;const m=new n;m.name="particle system local",a.add(m);const f=new n;f.name="particle system world";const y=new se(f,m,i,s),g=new e,w=[];return(await Promise.all(t.vfx.emitters.slice().sort((e,t)=>(t.output.renderOrder??0)-(e.output.renderOrder??0)).map(async e=>{const t=await be(e,r,p,c,g,o,l,u,a,d,w);return t.setParentRecursive(g),t.emit()}))).forEach(e=>g.addEmitter(e)),y.prepareLightOutputs(w),g.addRenderer(y).emit({onEnd:()=>{}}),{renderBodies:w,container:f,system:g,dispose:()=>{f.removeFromParent(),m.removeFromParent(),y.dispose()}}}function we(e,t){let a;const r=function(e,t){const a=Math.max(0,e.rate.delay??0),r=e.rate.delayBinding;if("vfxInput"!==r?.type||null==t)return a;const s=t.getDefinition(r.inputId);return"number"!==s?.type?a:Math.max(0,t.getNumber(r.inputId,a))}(e,t);switch(e.rate.type){case"continuous":a=new Z(r,e.rate.count,e.rate.time,e.rate.duration);break;case"once":a=new $(r,e.rate.count);break;default:console.warn(`Failed to configure rate for emitter: ${JSON.stringify(e)}`),a=new s(0,1/0)}return a}async function be(e,t,r,s,c,x,S,T,D,F,V){const O=we(e,T);let H;switch(e.output.type){case"pointLight":H=new a({type:"pointLight",output:e.output});break;case"decal":H=new a(await async function(e,t,a,r,s){if("shaderGraph"===Me(e)){const t=await createShaderGraphMaterial(e,"decal",a,r,s);if(null!=t){const a=new i.Mesh(new i.BoxGeometry(1,1,1),t);return Ae(a,e),a}}const n=!1!==e.unlit?new pe:new ce;n.color=new i.Color(e.color),n instanceof pe?n.intensity=e.intensity??1:n.emissiveIntensity=e.intensity??1;if(e.colorMap){const a=await t.getTexture(e.colorMap);n.colorMap=a}if(e.alphaMap){const a=await t.getTexture(e.alphaMap);n.alphaMap=a}const o=n.build();o.blending=te[e.blendingMode]??i.NormalBlending,o.transparent=!0,o.side=i.BackSide,o.depthTest=!1,null!=o&&!0===e.bloom&&(o.userData.hasBloom=!0);o.defines.IS_PARTICLE="";const l=new i.BoxGeometry(1,1,1),p=new i.Mesh(l,o);return Ae(p,e),p}(e.output,t,r,s,S));break;case"sprite":H=new a(await async function(e,t,a,r,s){let n=null;const o=Me(e);"shaderGraph"===o&&(n=await createShaderGraphMaterial(e,"sprite",a,r,s));null==n&&"shader"===o&&null!=e.shader&&(n=await createShaderMaterial(e,t,s));null==n&&(n=await async function(e,t){const a=null!=e.texture?await t.getTexture(e.texture):ve,r=C(a);let s=r.sample(B.uv);e.flipbook?.enabled&&(s=_(r,y.uv,new u("vfxFlipbookColumns",e.flipbook.columns,void 0,!1),new u("vfxFlipbookRows",e.flipbook.rows,void 0,!1),J.time,new u("vfxFlipbookFps",e.flipbook.fps,void 0,!1),e.flipbook.mode));const n=new u("rotation",0),o=new d("color").rgb,l=k(new h("instanceColor")).rgb,p=z.subtract(Q).divide(z);let c=J.opacity;switch(e.opacityChannel??"red"){case"none":break;case"red":c=c.multiply(s.r);break;case"alpha":c=c.multiply(s.a)}if("number"==typeof e.softness&&e.softness>0){const t=g(p,0,1e3);c=c.multiply(P(b(0),new u("vfxSoftness",.2*e.softness,void 0,!1),t))}const m=ne(n,void 0,void 0,!0),f=I(o.multiply(l).multiply(s.rgb).multiplyScalar(new u("vfxIntensity",e.intensity??1,void 0,!1)),c);var w=new le({color:f,emissive:f.rgb.multiplyScalar(c),transparent:!0,position:m,alphaTest:1e-4,uniforms:{color:{value:new i.Color(e.color)}}});w.alphaHash=!0,null!=w&&!0===e.bloom&&(w.userData.hasBloom=!0);return w.blending=te[e.blendingMode]??i.NormalBlending,w}(e,t));n.depthWrite=!1,Ee(n,e);const l=new i.Mesh(new i.PlaneGeometry(1,1),n);return l.name="sprite",Ae(l,e),l}(e.output,t,r,s,S));break;case"stretchedSprite":H=new a(await async function(e,t){"shaderGraph"===Me(e)&&console.warn("Shader graph materials are not supported for stretched sprite VFX outputs yet. Falling back to the default stretched sprite material.");const a=null!=e.texture?await t.getTexture(e.texture):ve,r=C(a).sample(B.uv),s=j(new l("particleColor")),n=s.rgb.multiply(r.rgb);let o=s.w;switch(e.opacityChannel??"red"){case"none":break;case"red":o=o.multiply(r.r);break;case"alpha":o=o.multiply(r.a)}if("number"==typeof e.softness&&e.softness>0){const t=z.subtract(Q).divide(E(z)),a=g(t,0,1e3);o=o.multiply(P(b(0),new u("vfxSoftness",.2*e.softness,void 0,!1),a))}const p=m("offset"),c=f("velocity"),d=m("size"),h=c.w,v=d.x.add(d.y).multiply(.5),x=R.modelViewMatrix.multiplyVec(L(p,1)),S=R.normalMatrix.multiplyVec(c.xyz),T=A(w(x.xyz,S)).multiplyScalar(y.position.y.multiply(v)),G=S.multiplyScalar(y.position.x.add(.5).multiply(b(1).add(h.divide(M(S)))).multiply(v)),k=R.projectionMatrix.multiplyVec(L(x.xyz.add(T).subtract(G),x.w));var D=new le({color:I(n.multiplyScalar(new u("vfxIntensity",e.intensity??1,void 0,!1)),o),alphaTest:.1,transparent:!0,position:k,uniforms:{color:{value:new i.Color(e.color)}}});null!=D&&!0===e.bloom&&(D.userData.hasBloom=!0);D.blending=te[e.blendingMode]??i.NormalBlending;const F=new ee(new i.PlaneGeometry(1,1),D);return F.scaleFactor=e.scale,Ae(F,e),F}(e.output,t));break;case"shape":H=new a(await async function(e,t,a,r,s){if(null==e.shape)return console.log("Shape is null"),new n;const l=N[e.shape];if(null==l)return console.error(`No shape with type ${e.shape}`),new n;const c=W(e.params??{}),u=l.geometry(c);let d=null;const m=Me(e);"shaderGraph"===m&&(d=await createShaderGraphMaterial(e,"surface",a,r,s));null==d&&"shader"===m&&null!=e.shader&&(d=await createShaderMaterial(e,t,s));null==d&&(d="material"===m&&null!=e.material?xe(await t.getMaterial(e.material)):function(){const e=G(new h("instanceColor")).rgb,t=G(new o("particleData")).x;return new p({color:v({color:e}).rgb.rgba(t),opacity:t,transparent:!0})}());Ee(d,e);const f=new i.Mesh(u,d);return Ae(f,e),f}(e.output,t,r,s,S));break;case"mesh":H=new a(await async function(e,t,a,r,s){if(null==e.assetId)return console.warn("Can't use mesh as particle without asset id"),new n;const o=await t.getMesh(e.assetId),l=await t.getAsset(e.assetId),p=Me(e);if("shaderGraph"===p||"shader"===p&&null!=e.shader||"material"===p&&null!=e.material){let n;"shaderGraph"===p?n=await createShaderGraphMaterial(e,"surface",a,r,s):"shader"===p&&null!=e.shader?n=await createShaderMaterial(e,t,s):"material"===p&&null!=e.material&&(n=xe(await t.getMaterial(e.material))),n?.transparent&&(n.depthWrite=!1),null!=n&&Ee(n,e),o.traverse(e=>{e instanceof i.Mesh&&null!=n&&(e.material=n)})}else{const e=[];if(null!=l.materialAssignments)for(const a of l.materialAssignments)null!=a.materialId&&o.traverse(r=>{r instanceof i.Mesh&&r.material instanceof i.Material&&r.material.color instanceof i.Color&&(r.material.name!=a.name&&null!=a.name||"#"+r.material.color.getHexString()!==a.color||e.push(t.getMaterial(a.materialId).then(e=>r.material=e)))});await Promise.all(e)}const c=[];if(o.traverse(e=>{e instanceof i.Mesh&&c.push(e)}),Ae(o,e),1===c.length){const t=c[0];return!0!==e.useLegacyTransform&&function(e){e.geometry=e.geometry.clone(),ye(e,e.geometry),e.position.set(0,0,0),e.quaternion.identity(),e.scale.set(1,1,1),e.updateMatrix(),e.matrixWorld.identity()}(t),t.removeFromParent(),t}return o}(e.output,t,r,s,S));break;case"trail":{const n=e.output,o="shaderGraph"===Me(e.output)?await createShaderGraphMaterial(e.output,"trail",r,s,S,{trailBillboard:e.output.billboard??!1}):null;H=new a({type:"trail",taper:e.output.taper,headGeometry:null,material:o instanceof i.ShaderMaterial?o:void 0,dragTexture:!1,texture:null!=e.output.texture?await t.getTexture(e.output.texture).then(e=>{if("repeat"!==n.uvMode)return e;const t=e.clone();return t.wrapS=i.RepeatWrapping,t.needsUpdate=!0,t}):null,opacityChannel:e.output.opacityChannel,color:e.output.color,colorEnd:e.output.colorEnd,intensity:e.output.intensity??1,intensityEnd:e.output.intensityEnd??1,length:e.output.length,opacityStart:e.output.opacityStart,opacityEnd:e.output.opacityEnd,bloom:e.output.bloom,scrollSpeed:e.output.scrollSpeed,uvMode:e.output.uvMode,unitsPerRepeat:e.output.unitsPerRepeat,width:e.output.width,billboard:e.output.billboard??!1,renderOrder:e.output.renderOrder});break}default:console.error("Failed to create particly system body: "+JSON.stringify(e))}const q=new Ie(D);null!=H&&V?.push(H.body.getValue()),q.parent=c,q.setRate(O),q._space=e.output.space;const X=await Promise.all(e.initializers.filter(e=>!1!==e.enabled).filter(e=>null!=re[e.type]).map(async e=>{const t=re[e.type],a=await W(e.params??{},r,s);return de(t,e.params??{},a,T)}));"world"===e.output.space&&X.push(new Y("shape"===e.output.type||"mesh"===e.output.type)),X.push(H,new K),q.addInitializers(X);const Z=await Promise.all(e.behaviours.filter(e=>!1!==e.enabled).filter(e=>null!=ae[e.type]).map(async e=>{const t=ae[e.type];for(const[a,r]of Object.entries(e.params))t.parameters&&null!=t.parameters[a]&&"curve"===t.parameters[a].type&&r.type!==U.Curve&&(r.type=U.Curve);const a=await W(e.params??{},r,s);return ue(t,e.params??{},a,T)}));Z.sort((e,t)=>Number(t instanceof ie)-Number(e instanceof ie)),Z.push(new Se);for(const t of Z)t instanceof ie&&(t.physics=x,t.emitEvents&&null!=F&&(t.onCollision=(t,a)=>F(e.id,e.name,t,a,"local"===e.output.space)));q.addBehaviours(Z);for(const a of e.children??[]){const e=await be(a,t,r,s,c,x,S,T,D,F,V),i=new EmitterPool(()=>{const t=e.clone();return t.onExpired=()=>{const e=q.childEmitters.findIndex(e=>e.id===t.id);-1!=e&&q.childEmitters.splice(e,1),null!=t.parentParticle&&(o.delete(t.parentParticle.id),t.parentParticle=null),i.release(t)},t}),n=q.eventDispatcher,o=new Map;q.bindEmitterEvent=!0,n.addEventListener("PARTICLE_DEAD",e=>{const t=o.get(e.id);null!=t&&(q.detachChildEmitterFromParticle(t,e),o.delete(e.id))});let l="PARTICLE_CREATED";if("spawnEvent"in a)switch(a.spawnEvent){case"collision":l="PARTICLE_COLLISION";break;case"start":l="PARTICLE_CREATED"}n.addEventListener(l,e=>{const t=i.get();t.age=0,t.totalEmitTimes=-1,t.particles.length=0,t.currentEmitTime=0,t.cID=0,t.eventDispatcher.removeAllEventListeners(),q.childEmitters.push(t),o.set(e.id,t),t.parentParticle=e,t.system=q.system,t.emit()})}return q}const ve=(new i.TextureLoader).load("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJkSURBVHjaxJeJbusgEEW94S1L//83X18M2MSuLd2pbqc4wZGqRLrKBsyZhQHny7Jk73xVL8xpVhWrcmiB5lX+6GJ5YgQ2owbAm8oIwH1VgKZUmGcRqKGGPgtEQQAzGR8hQ59fAmhJHSAagigJ4E7GPWRXOYC6owAd1JM6wDQPADyMWUqZRMqmAojHp1Vn6EQQEgUNMJLnUjMyJsM49wygBkAPw9dVFwXRkncCIIW3GRgoTQUZn6HxCMAFEFd8TwEQ78X4rHbILoAUmeT+RFG4UhQ6MiIAE4W/UsYFjuVjAIa2nIY4q1R0GFtQWG3E84lqw2GO2QOoCKBVu0BAPgDSU0eUDjjQenNkV/AW/pWChhpMTelo1a64AOKM30vk18GzTHXCNtI/Knz3DFBgsUqBGIjTInXRY1yA9xkVoqW5tVq3pDR9A0hfF5BSARmVnh7RMDCaIdcNgbPBkgzn1Bu+SfIEFSpSBmkxyrMicb0fAEuCZrWnN89veA/4XcakrPcjBWzkTuLjlbfTQPOlBhz+HwkqqPXmPQDdrQItxE1moGof1S74j/8txk8EHhTQrAE8qlwfqS5yukm1x/rAJ9Jiaa6nyATqD78aUVBhFo8b1V4DdTXdCW+IxA1zB4JhiOhZMEWO1HqnvdoHZ4FAMIhV9REF8FiUm0jsYPEJx/Fm/N8OhH90HI9YRHesWbXXZwAShU8qThe7H8YAuJmw5yOd989uRINKRTJAhoF8jbqrHKfeCYdIISZfSq26bk/K+yO3YvfKrVgiwQBHnwt8ynPB25+M8hceTt/ybPhnryJ78+tLgAEAuCFyiQgQB30AAAAASUVORK5CYII=");export async function createShaderMaterial(e,t,a){const r=a.get(e.shader);if(null==r)return console.error("No shader exists with name "+e.shader),new i.Material;const s=new r.type;"sprite"===e.type&&s instanceof oe&&(s.instanced=!0);const n=await V(e.shaderParams??{},r.type,t,{getTexture:e=>t.getTexture(e.id),getMaterial:e=>t.getMaterial(e.id),getMesh:e=>t.getMesh(e.id)},void 0,void 0,void 0,void 0,void 0,s);Object.assign(s,n);const o=s.build();return o.userData.vfxShader={key:"shader:"+e.shader,name:e.shader},o}export async function createShaderGraphMaterial(e,t,a,r,s,i={}){const n=e.shaderGraph;if("asset"!==n?.source||null==n.assetId)return console.warn(`Missing shader graph asset reference for ${e.type} VFX output`),null;const o=await a.getAsset(n.assetId),l=o?.shaderGraph;if(null==l)return console.warn(`Missing shader graph asset "${n.assetId}" for ${e.type} VFX output`),null;if(l.target!==t)return console.warn(`Shader graph "${o.name}" targets "${l.target}" but ${e.type} VFX output expects "${t}"`),null;const p="sprite"===t||"decal"===t||"surface"===t&&"unlit"===l.shadingModel;try{const t=await O(e.shaderGraphParams??{},l,a,r,void 0,s.shaders,p),c=he(l,{parameters:t,trailBillboard:i.trailBillboard});return c.userData.customShaderName=`shaderGraph:${n.assetId}`,c.userData.vfxShader={key:"shaderGraph:"+n.assetId,name:o.name,target:l.target,shadingModel:l.shadingModel,animationLayout:p?"shared":"specialized",parameters:Object.fromEntries((l.parameters??[]).map(t=>{const a=e.shaderGraphParams?.[t.name],r=!1!==a?.override&&null!=a?.value?a.value:t.defaultValue,s=null!=r&&"object"==typeof r&&"a"in r&&"b"in r&&null!=r.b;return[t.name,s?{mode:"animated",easing:"easing"in r?r.easing:void 0}:{mode:"static"}]}))},Ee(c,e),c}catch(e){return console.log("Shader graph VFX runtime error: "+e,e),null}}export function createGpuSpriteShaderGraphResolver(e,t,a,r){return async s=>{const n=Me(s);let o=null;if("shaderGraph"===n)o=await createShaderGraphMaterial(s,"sprite"===s.type?"sprite":"decal"===s.type?"decal":"trail"===s.type?"trail":"surface",e,t,a,"trail"===s.type?{trailBillboard:s.billboard??!1}:void 0);else if("shader"===n&&"decal"!==s.type&&null!=r)o=await createShaderMaterial(s,r,a);else if("material"!==n||"mesh"!==s.type&&"shape"!==s.type||null==s.material||null==r){if("default"===n&&"mesh"===s.type&&null!=s.assetId&&null!=r){const e=await r.getAsset(s.assetId),t=e.materialAssignments?.find(e=>null!=e.materialId);null!=t&&(o=await ge(t.materialId,r))}else if("default"===n&&"decal"===s.type&&null!=r){const e=!1!==s.unlit?new pe:new ce;e.color=new i.Color(s.color),e instanceof pe?e.intensity=s.intensity??1:e.emissiveIntensity=s.intensity??1,s.colorMap&&(e.colorMap=await r.getTexture(s.colorMap)),s.alphaMap&&(e.alphaMap=await r.getTexture(s.alphaMap)),o=e.build(),o.transparent=!0}}else o="mesh"===s.type?await ge(s.material,r):xe(await r.getMaterial(s.material));return null==o?null:("mesh"!==s.type&&"shape"!==s.type||!o.transparent||(o.depthWrite=!1),o instanceof p?o:(console.warn(`GPU ${s.type} materials must compile to NodeShaderMaterial; using CPU fallback`),null))}}function Me(e){return null!=e.materialSource?("material"!==e.materialSource||"mesh"!==e.type&&"shape"!==e.type||null!=e.material)&&("shaderGraph"!==e.materialSource||"asset"===e.shaderGraph?.source&&null!=e.shaderGraph.assetId)&&("shader"!==e.materialSource||"shader"in e&&null!=e.shader)?e.materialSource:"default":"asset"===e.shaderGraph?.source?"shaderGraph":"shader"in e&&null!=e.shader?"shader":"mesh"!==e.type&&"shape"!==e.type||null==e.material?"default":"material"}function Ee(e,t){null!=e&&(fe(e,!1),"bloom"in t&&!0===t.bloom&&(e.userData.hasBloom=!0),"sprite"===t.type?(e.blending=te[t.blendingMode]??i.NormalBlending,!0===t.lockY&&e instanceof i.ShaderMaterial&&(e.defines??(e.defines={}),e.defines.LOCK_Y_AXIS="")):"decal"===t.type?(e.blending=te[t.blendingMode]??i.NormalBlending,e.transparent=!0,e.side=i.BackSide,e.depthTest=!1,e instanceof i.ShaderMaterial&&(e.defines??(e.defines={}),e.defines.IS_PARTICLE="")):"shape"!==t.type&&"mesh"!==t.type||e instanceof i.ShaderMaterial&&(e.defines??(e.defines={}),e.defines.IS_PARTICLE=""))}function Ae(e,t){const a=t.renderOrder;null!=a&&e.traverse(e=>e.renderOrder=a)}function xe(e){const t=G(new h("instanceColor")).rgb;let a,r=G(new o("particleData")).x;if(e instanceof i.MeshStandardMaterial||e instanceof i.MeshLambertMaterial||e instanceof i.MeshBasicMaterial){let s=t.multiply(S(e.color));null!=e.map&&(s=s.multiply(C(e.map).sample(B.uv).rgb)),null!=e.alphaMap&&(r=r.multiply(C(e.alphaMap).sample(B.uv).r)),e instanceof i.MeshStandardMaterial?a=new p({color:T({color:s,emissive:S(e.emissive),emissiveIntensity:b(e.emissiveIntensity),roughness:e.roughness,metalness:e.metalness}).rgb.rgba(r),emissive:S(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshLambertMaterial?a=new p({color:v({color:s}).rgb.rgba(r),emissive:S(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshBasicMaterial&&(a=new p({color:s.rgb.rgba(r),emissive:s.rgb,transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}))}else e instanceof p&&(a=e.clone(),a.defines.IS_PARTICLE="");return null!=a&&(!0===e.userData?.hasBloom&&null!=a&&(a.userData.hasBloom=!0),a.side=e.side,a.transparent=e.transparent),a??e}class Se extends t{initialize(e){e.body instanceof i.Object3D&&e.body.traverse(e=>{if(e instanceof i.Mesh){const t=e.material;t instanceof p&&(null!=t.uniforms[H]||null!=t.uniforms[q]||t.uniforms[X])}})}mutate(e,t,a){this.energize(e,t),e.target instanceof n&&e.target.traverse(t=>{if(t instanceof i.Mesh){const a=t.material;if(a instanceof p){let t=!1;null!=a.uniforms[H]&&(a.uniforms[H].value=this.energy,t=!0),null!=a.uniforms[q]&&(a.uniforms[q].value=e.age,t=!0),null!=a.uniforms[X]&&(a.uniforms[X].value=e.velocity,t=!0),t&&me(a)}}})}}class Ie extends r{constructor(e){super(),this.transformRoot=e,this.childEmitters=[],this.bindEmitterEvent=!1,this.onExpired=()=>{}}clearParticlesRecursive(){let e=this.childEmitters.length;for(;e--;){const t=this.childEmitters[e];t.stopEmit(),t.clearParticlesRecursive(),t.onExpired()}this.childEmitters.length=0;let t=this.particles.length;for(;t--;){const e=this.particles[t];this.system?.dispatch("PARTICLE_DEAD",e),this.bindEmitterEvent&&this.dispatch("PARTICLE_DEAD",e),null!=this.system?this.system.pool.expire(e.reset()):e.reset()}this.particles.length=0}detachChildEmitterFromParticle(e,t){this.copyPositionToChildSpace(e,t.position),e.parentParticle=null,e.stopEmit()}update(e){if(!this.isEmitting&&0===this.particles.length&&0===this.childEmitters.length)return;this.age+=e,(this.dead||this.age>=this.life)&&this.destroy(),this.isEmitting&&this.generate(e),this.integrate(e);let t=this.particles.length;for(;t--;){const e=this.particles[t];e.dead&&(this.system&&this.system.dispatch("PARTICLE_DEAD",e),this.bindEmitterEvent&&this.dispatch("PARTICLE_DEAD",e),this.system.pool.expire(e.reset()),this.particles.splice(t,1))}this.updateEmitterBehaviours(e),this.updateChildren(e),this.isEmitting||0!==this.particles.length||0!==this.childEmitters.length||this.onExpired()}updateChildren(e){for(const t of this.childEmitters)null!=t.parentParticle&&this.copyPositionToChildSpace(t,t.parentParticle.position),t.update(e)}copyPositionToChildSpace(e,t){const a=this._space??"world",r=e._space??"world";a!==r&&null!=this.transformRoot?(Pe.copy(t),"local"===a&&"world"===r?Pe.applyMatrix4(this.transformRoot.matrixWorld):"world"===a&&"local"===r&&(Te.copy(this.transformRoot.matrixWorld).invert(),Pe.applyMatrix4(Te)),e.position.copy(Pe)):e.position.copy(t)}clone(){const e=new Ie(this.transformRoot);return e.setRate(this.rate.clone()),e.behaviours=this.behaviours,e.initializers=this.initializers,e._space=this._space,e.body=this.body,e.parent=this.parent,e.system=this.system,e}setParentRecursive(e){this.system=e,this.childEmitters.forEach(t=>t.setParentRecursive(e))}}const Pe=new i.Vector3,Te=new i.Matrix4;export class EmitterPool{constructor(e){this.creator=e,this.instances=[]}get(){0==this.instances.length&&this.instances.push(this.creator());return this.instances.pop()}release(e){this.instances.push(e)}dispose(){this.instances.length=0}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Particle as e,Emitter as t}from"@hology/nebula";import*as r from"three";import{OnceRate as o}from"./rates.js";import{MultiRenderer as n}from"./vfx-renderers.js";const a=new WeakMap;export function prepareVfxRendering(e,t,o){const n=createVfxRenderPreparation(e,t);return new Promise((e,t)=>{let s=a.get(o);null==s&&(s=[],a.set(o,s),setTimeout(()=>{!async function(e,t){a.delete(e);const o=new r.Scene;for(const e of t)o.add(e.preparation.scene);try{await e.compileAsync(o),"webgpu"!==e.activeBackend&&e.initTextures(o);for(const e of t)e.resolve()}catch(e){for(const r of t)r.reject(e)}finally{o.clear()}}(o,s)},0)),s.push({preparation:n,resolve:e,reject:t})}).finally(()=>n.dispose())}export function createVfxRenderPreparation(a,c){const i=new r.Scene,f=new n(i,i,r,c),l=new Set,p=new Set,d=new t;d.setRate(new o(0,1));const m=[],u=()=>{i.traverse(e=>{if(!(e instanceof r.Mesh))return;l.add(e.geometry);const t=Array.isArray(e.material)?e.material:[e.material];for(const e of t)a.some(t=>t instanceof r.Object3D&&s(t,e))||p.add(e)});for(const e of m)f.onParticleDead(e);for(const e of l)e.dispose();for(const e of p)e.dispose();i.clear()};try{for(const t of a){const o=new e;if(o.parent=d,o.life=1,o.useAlpha=!0,o.useColor=!0,t instanceof r.Object3D){const e=t.clone();e.traverse(e=>{e instanceof r.Mesh&&(e.geometry=e.geometry.clone(),l.add(e.geometry))}),o.body=e}else o.body=t;f.onParticleCreated(o),m.push(o)}return f.onSystemUpdate(),{scene:i,dispose:u}}catch(e){throw u(),e}}function s(e,t){let o=!1;return e.traverse(e=>{(e instanceof r.Mesh||e instanceof r.Sprite)&&(o||(o=Array.isArray(e.material)?e.material.includes(t):e.material===t))}),o}/*
|
|
1
|
+
import{Particle as e,Emitter as t}from"@hology/nebula";import*as r from"three";import{OnceRate as o}from"./rates.js";import{MultiRenderer as n}from"./vfx-renderers.js";const a=new WeakMap;export function prepareVfxRendering(e,t,o){const n=createVfxRenderPreparation(e,t);return new Promise((e,t)=>{let s=a.get(o);null==s&&(s=[],a.set(o,s),setTimeout(()=>{!async function(e,t){a.delete(e);const o=new r.Scene;for(const e of t)o.add(e.preparation.scene);try{await e.compileAsync(o),"webgpu"!==e.activeBackend&&e.initTextures(o);for(const e of t)e.resolve()}catch(e){for(const r of t)r.reject(e)}finally{o.clear()}}(o,s)},0)),s.push({preparation:n,resolve:e,reject:t})}).finally(()=>n.dispose())}export function createVfxRenderPreparation(a,c){const i=new r.Scene,f=new n(i,i,r,c),l=new Set,p=new Set,d=new t;d.setRate(new o(0,1));const m=[],u=()=>{i.traverse(e=>{if(!(e instanceof r.Mesh))return;l.add(e.geometry);const t=Array.isArray(e.material)?e.material:[e.material];for(const e of t)a.some(t=>t instanceof r.Object3D&&s(t,e))||p.add(e)});for(const e of m)f.onParticleDead(e);for(const e of l)e.dispose();for(const e of p)e.dispose();i.clear()};try{for(const t of a){if("pointLight"===t.type)continue;const o=new e;if(o.parent=d,o.life=1,o.useAlpha=!0,o.useColor=!0,t instanceof r.Object3D){const e=t.clone();e.traverse(e=>{e instanceof r.Mesh&&(e.geometry=e.geometry.clone(),l.add(e.geometry))}),o.body=e}else o.body=t;f.onParticleCreated(o),m.push(o)}return f.onSystemUpdate(),{scene:i,dispose:u}}catch(e){throw u(),e}}function s(e,t){let o=!1;return e.traverse(e=>{(e instanceof r.Mesh||e instanceof r.Sprite)&&(o||(o=Array.isArray(e.material)?e.material.includes(t):e.material===t))}),o}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -3,7 +3,7 @@ import { CustomRenderer, MeshRenderer } from '@hology/nebula';
|
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { Object3D } from 'three';
|
|
5
5
|
import type { ViewController } from '../../gameplay/services/render.js';
|
|
6
|
-
import type { OutputTextureSetting } from './vfx-asset.js';
|
|
6
|
+
import type { PointLightOutput, OutputTextureSetting } from './vfx-asset.js';
|
|
7
7
|
export declare class MultiRenderer extends CustomRenderer {
|
|
8
8
|
private worldContainer;
|
|
9
9
|
private localContainer;
|
|
@@ -12,6 +12,8 @@ export declare class MultiRenderer extends CustomRenderer {
|
|
|
12
12
|
private localRenderers;
|
|
13
13
|
private worldRenderers;
|
|
14
14
|
constructor(worldContainer: Object3D, localContainer: Object3D, three: typeof THREE, view: ViewController);
|
|
15
|
+
/** Allocate bounded light slots while loading, including delayed and child outputs. */
|
|
16
|
+
prepareLightOutputs(bodies: readonly unknown[]): void;
|
|
15
17
|
onSystemUpdate(): void;
|
|
16
18
|
onParticleCreated(particle: Particle): void;
|
|
17
19
|
onParticleUpdate(particle: Particle): void;
|
|
@@ -19,7 +21,30 @@ export declare class MultiRenderer extends CustomRenderer {
|
|
|
19
21
|
private getRenderer;
|
|
20
22
|
dispose(): void;
|
|
21
23
|
}
|
|
22
|
-
export type ParticleTemplate = TrailTemplate;
|
|
24
|
+
export type ParticleTemplate = TrailTemplate | PointLightTemplate;
|
|
25
|
+
type LightParticle = Particle & {
|
|
26
|
+
target?: THREE.PointLight;
|
|
27
|
+
};
|
|
28
|
+
type PointLightTemplate = {
|
|
29
|
+
type: 'pointLight';
|
|
30
|
+
output: PointLightOutput;
|
|
31
|
+
};
|
|
32
|
+
/** Fixed slots per authored emitter, reused across particle deaths and effect restarts. */
|
|
33
|
+
export declare class PointLightRenderer {
|
|
34
|
+
private readonly container;
|
|
35
|
+
private readonly pools;
|
|
36
|
+
constructor(container: Object3D);
|
|
37
|
+
onSystemUpdate(): void;
|
|
38
|
+
prepare(template: PointLightTemplate): {
|
|
39
|
+
lights: THREE.PointLight[];
|
|
40
|
+
free: THREE.PointLight[];
|
|
41
|
+
color: THREE.Color;
|
|
42
|
+
};
|
|
43
|
+
onParticleCreated(particle: LightParticle): void;
|
|
44
|
+
onParticleUpdate(particle: LightParticle): void;
|
|
45
|
+
onParticleDead(particle: LightParticle): void;
|
|
46
|
+
dispose(): void;
|
|
47
|
+
}
|
|
23
48
|
export type TrailTemplate = {
|
|
24
49
|
type: 'trail';
|
|
25
50
|
headGeometry?: THREE.BufferGeometry;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{CustomRenderer as e,MeshRenderer as t,PUID as r}from"@hology/nebula";import*as a from"three";import{InstancedBufferAttribute as o,Object3D as n}from"three";import{NodeShaderMaterial as i}from"three-shader-graph";import{StretchedSprite as s}from"./stretched-sprite.js";import{Trail as l}from"./trail-renderer.js";import{SpriteNodeShaderMaterial as c}from"../../shader/sprite-shader.js";import{particleEnergyUniformName as d}from"../../shader-nodes/particle.js";import{AssetMeshInstance as m}from"../../scene/asset-resource-loader.js";import{markMaterialUniformValuesNeedUpdate as u}from"../../rendering/node-material-webgpu-resolver.js";var h;!function(e){e[e.mesh=0]="mesh",e[e.instanced=1]="instanced",e[e.sprite=2]="sprite",e[e.stretchedBillboard=3]="stretchedBillboard",e[e.trail=4]="trail"}(h||(h={}));export class MultiRenderer extends e{constructor(e,t,r,a){super(),this.worldContainer=e,this.localContainer=t,this.three=r,this.view=a,this.localRenderers=[],this.worldRenderers=[]}onSystemUpdate(){for(const e of this.worldRenderers)e?.onSystemUpdate();for(const e of this.localRenderers)e?.onSystemUpdate()}onParticleCreated(e){e.target=null,this.getRenderer(e)?.onParticleCreated(e)}onParticleUpdate(e){this.getRenderer(e)?.onParticleUpdate(e)}onParticleDead(e){this.getRenderer(e)?.onParticleDead(e),e._renderer=null}getRenderer(e){if(e._renderer)return e._renderer;const t=(null!=e?.parent?e?.parent._space:null)??"world",r="world"===t?this.worldContainer:this.localContainer,o="world"===t?this.worldRenderers:this.localRenderers;return"trail"===e.body.type?(null==o[h.trail]&&(o[h.trail]=new TrailRenderer(r,this.three)),e._renderer=o[h.trail]):e.body instanceof s?(null==o[h.stretchedBillboard]&&(o[h.stretchedBillboard]=new StretchedSpriteInstancedRenderer(r,this.three,this.view)),e._renderer=o[h.stretchedBillboard]):e.body instanceof a.Sprite?(null==o[h.sprite]&&(o[h.sprite]=new CombinedRenderer(r,this.three)),e._renderer=o[h.sprite]):e.body instanceof a.Mesh&&0==e.body.children.length&&e.body.material instanceof i?(null==o[h.instanced]&&(o[h.instanced]=new InstancedRenderer(r,this.three,this.view)),e._renderer=o[h.instanced]):e.body instanceof a.Object3D?(null==o[h.mesh]&&(o[h.mesh]=new CombinedRenderer(r,this.three)),e._renderer=o[h.mesh]):void 0}dispose(){this.localRenderers.forEach(e=>{e instanceof InstancedRenderer&&e.dispose()}),this.worldRenderers.forEach(e=>{e instanceof InstancedRenderer&&e.dispose()})}}export class TrailRenderer extends t{constructor(){super(...arguments),this.trails=[]}dispose(){}onSystemUpdate(){for(const e of this.trails)e.update()}onParticleCreated(e){const t=e.body,r=new l(this.container,!1),o=new n;this.container.add(o),o.updateMatrixWorld(),o.updateWorldMatrix(!0,!0);const i=t.material?.clone()??l.createBaseMaterial(t.texture,t.scrollSpeed,t.opacityChannel,t.billboard??!1);i.uniforms.trailUvMode&&(i.uniforms.trailUvMode.value="repeat"===t.uvMode?1:0),i.uniforms.unitsPerRepeat&&(i.uniforms.unitsPerRepeat.value=Math.max(.001,t.unitsPerRepeat??1)),i.uniforms.taper&&(i.uniforms.taper.value=t.taper??!1);const s=new a.Color(t.color??0).multiplyScalar(t.intensity??1),c=new a.Color(t.colorEnd??0).multiplyScalar(t.intensityEnd??t.intensity??1);i.uniforms.headColor&&i.uniforms.tailColor&&(i.uniforms.headColor.value=new a.Vector4(s.r,s.g,s.b,t.opacityStart??1),i.uniforms.tailColor.value=new a.Vector4(c.r,c.g,c.b,t.opacityEnd??1)),i.uniforms.color&&null==i.uniforms.headColor&&(i.uniforms.color.value=s),i.uniforms.opacity&&null==i.uniforms.headColor&&(i.uniforms.opacity.value=t.opacityStart??1),t.bloom&&(i.userData.hasBloom=!0),r.initialize(i,Math.round(t.length??10),!1,t.width,null,o,t.billboard??!1),null!=t.renderOrder&&null!=r.mesh&&(r.mesh.renderOrder=t.renderOrder);const d=e.rotation;o.rotation.set(d.x,d.y,d.z);e.scale;r.activate(),this.trails.push(r),e.target=o}onParticleUpdate(e){const t=e.target;t.position.copy(e.position);const r=e.body;let a=v;e.old&&e.old.position?a.set(e.position.x-e.old.position.x,e.position.y-e.old.position.y,e.position.z-e.old.position.z):a.copy(e.velocity),a.lengthSq()>1e-8?a.normalize():a.copy(C);const o=P;o.setFromUnitVectors(a,C),t.rotation.set(0,0,0),t.rotation.set(e.rotation.x,e.rotation.y,e.rotation.z),t.applyQuaternion(o);const n=this.trails.findIndex(e=>e.targetObject.id===t.id);if(-1!=n){const t=this.trails[n];if(e.useAlpha){if(t.material.uniforms.headColor&&t.material.uniforms.tailColor){t.material.uniforms.headColor.value.setW((r.opacityStart??1)*e.alpha);t.material.uniforms.tailColor.value.setW((r.opacityEnd??1)*e.alpha)}null!=t.material.uniforms.opacity&&(t.material.uniforms.opacity.value=e.alpha)}e.useColor&&null!=t.material.uniforms.color&&t.material.uniforms.color.value.copy(e.color),null!=t.material.uniforms[d]&&(t.material.uniforms[d].value=e.energy),u(t.material)}}onParticleDead(e){const t=e.target,r=this.trails.findIndex(e=>e.targetObject.id===t.id);if(-1!=r){const e=this.trails[r];e.removeFromParent(),e.mesh.removeFromParent(),e.targetObject.removeFromParent(),t.removeFromParent(),this.trails.splice(r,1)}}}export class CombinedRenderer extends t{dispose(){}scale(e){const t=e.transform.initialScale;e.target instanceof a.Sprite?null!=t?e.target.scale.set(t.x*e.scale*e.radius,t.y*e.scale*e.radius,1):e.target.scale.set(e.scale*e.radius,e.scale*e.radius,1):null!=t?e.target.scale.set(t.x*e.scale,t.y*e.scale,t.z*e.scale):super.scale(e)}rotate(e){e.target.material.rotation=e.rotation.z}onParticleCreated(e){e.target||(e.target=this._targetPool.get(e.body),(e.useAlpha||e.useColor)&&(e.target instanceof a.Mesh||e.target instanceof a.Sprite&&e.target.material instanceof a.Material)&&(e.target.material.__puid=r.id(e.body.material),e.target.material=this._materialPool.get(e.target.material))),e.target&&e.target instanceof n&&(e.target.position.set(e.position.x,e.position.y,e.position.z),this.container.add(e.target))}onParticleUpdate(e){const{target:t,useAlpha:r,useColor:o,rotation:i}=e;if(t){if(t.position.copy(e.position),this.isThreeSprite(e)||(t instanceof a.Mesh&&t.material instanceof c?e.target.material.rotation=e.rotation.z:t.rotation.set(i.x,i.y,i.z)),this.scale(e),e.transform&&e.transform.orientAlongVelocity&&t instanceof n){const r=p.set(e.velocity.x,e.velocity.y,e.velocity.z).normalize(),a=t.getWorldDirection(f);a.applyAxisAngle(y,Math.PI/-2),g.setFromUnitVectors(a,r),t.quaternion.copy(g)}(r||o)&&this.applyUniforms(t,e)}}applyUniforms(e,t){if(e instanceof m&&e.traverse(r=>r!==e&&this.applyUniforms(r,t)),e instanceof a.Mesh){let r=!1;t.useAlpha&&(e.material instanceof c&&null!=e.material.uniforms.opacity&&(e.material.uniforms.opacity.value=t.alpha,r=!0),e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.opacity?(e.material.uniforms.opacity.value=t.alpha,r=!0):(e.material.opacity=t.alpha,r=!0),e.material.transparent=!0),t.useColor&&(null!=e.material.color?(e.material.color.copy(t.color),r=!0):e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.color&&(e.material.uniforms.color.value=t.color,r=!0)),r&&u(e.material)}}onParticleDead(e){e.target&&(e.target.material&&(e.useAlpha||e.useColor)&&this._materialPool.expire(e.target.material),this._targetPool.expire(e.target),this.container.remove(e.target),e.target=null)}}export class StretchedSpriteInstancedRenderer extends t{constructor(e,t,r){super(e,t),this.view=r,this.meshes=[]}dispose(){this.meshes.forEach(e=>e.mesh.geometry.dispose())}onSystemUpdate(){for(const e of this.meshes){const t=e.mesh.geometry.getAttribute("offset"),r=e.mesh.geometry.getAttribute("size"),o=e.mesh.geometry.getAttribute("velocity"),n=e.mesh.geometry.getAttribute("rotation"),i=e.mesh.geometry.getAttribute("particleColor");let s=0;for(const l of e.particles){if(null==l||l.dead)continue;if(l.useColor||l.color instanceof a.Color){const e=S.copy(l.color).getRGB(z);i.setXYZW(s,e.r,e.g,e.b,1),i.needsUpdate=!0}l.useAlpha&&(i.setW(s,l.alpha),i.needsUpdate=!0),"number"==typeof l.rotation?n.setX(s,l.rotation):n.setX(s,l.rotation.x);const e=l.position;if(t.setXYZ(s,e.x,e.y,e.z),"number"!=typeof l.scale)throw new Error("Particle scale is not a number");r.setXYZ(s,l.scale,l.scale,l.scale);let c=l.body.scaleFactor;0===c&&(c=.001);const d=1,m=l.velocity;o.setXYZW(s,m.x*c,m.y*c,m.z*c,d),s++}s>0&&(t.clearUpdateRanges(),t.addUpdateRange(0,3*s),t.needsUpdate=!0,r.clearUpdateRanges(),r.addUpdateRange(0,3*s),r.needsUpdate=!0,i.clearUpdateRanges(),i.addUpdateRange(0,4*s),i.needsUpdate=!0,o.clearUpdateRanges(),o.addUpdateRange(0,4*s),o.needsUpdate=!0,n.clearUpdateRanges(),n.addUpdateRange(0,s),n.needsUpdate=!0)}}calcMaxCount(e){if(null==e)return 1;const t=e.parent.rate,r=t.timePan.a;if(!Number.isFinite(r)||r<=0)return t.numPan.b*this.calcMaxCount(e.parent.parentParticle);return Math.min(t.numPan.b*Math.ceil(e.life/Math.min(Math.max(t.timePan.a,.01),1)),1e5)*this.calcMaxCount(e.parent.parentParticle)}onParticleCreated(e){if(e.body instanceof s&&null==e.target){const t=e.body;let r=this.meshes.find(e=>e.mesh.material.uuid===t.material.uuid);if(null==r){const n=this.calcMaxCount(e),i=new a.InstancedBufferGeometry;i.setIndex(t.geometry.getIndex()),i.setAttribute("position",t.geometry.getAttribute("position")),t.geometry.hasAttribute("normal")&&i.setAttribute("normal",t.geometry.getAttribute("normal")),i.setAttribute("uv",t.geometry.getAttribute("uv"));const s=new o(new Float32Array(3*n),3);s.setUsage(a.DynamicDrawUsage),i.setAttribute("offset",s);const l=new o(new Float32Array(4*n),4);if(l.setUsage(a.DynamicDrawUsage),i.setAttribute("particleColor",l),t.material instanceof c){const e=new a.Color(t.material.color);for(let t=0;t<l.count;t++)l.setXYZW(t,e.r,e.g,e.b,1)}const d=new o(new Float32Array(3*n),3);d.setUsage(a.DynamicDrawUsage),i.setAttribute("size",d);const m=new o(new Float32Array(4*n),4);m.setUsage(a.DynamicDrawUsage),i.setAttribute("velocity",m);const u=new o(new Float32Array(4*n),1);u.setUsage(a.DynamicDrawUsage),i.setAttribute("rotation",u),r={mesh:new a.Mesh(i,t.material),indices:new Float32Array(n),particles:[]},r.mesh.renderOrder=t.renderOrder,this.meshes.push(r),this.container.add(r.mesh)}let n=r.indices.findIndex(e=>0===e);n<0&&(n=function(e){let t=e[0],r=0;for(let a=1;a<e.length;a++)e[a]<t&&(t=e[a],r=a);return r}(r.indices)),r.indices[n]=performance.now(),e.target=n,r.particles[n]=e}}onParticleUpdate(e){}onParticleDead(e){const t=e.body;let r=this.meshes.find(e=>e.mesh.material.uuid===t.material.uuid);if(r){const t=r.mesh.geometry.getAttribute("size");t.setXYZ(e.target,0,0,0),t.needsUpdate=!0,r.particles[e.target]=null,r.indices[e.target]=0}e.target=null}}const p=new a.Vector3,f=new a.Vector3,g=new a.Quaternion,y=new a.Vector3(1,0,0),b=1e5;function w(e){return Number.isFinite(e)?Math.min(Math.max(Math.ceil(e),1),b):b}export class InstancedRenderer extends t{constructor(e,t,r){super(e,t),this.view=r,this.meshes=[]}dispose(){this.meshes.forEach(e=>{e.mesh.geometry.dispose(),e.mesh.dispose()})}onSystemUpdate(){for(const e of this.meshes)e.mesh.material.transparent&&e.mesh.material.blending===a.NormalBlending&&F(e,this.view.getCamera())}calcMaxCount(e){if(null==e)return 1;const t=e.parent.rate,r=t.timePan.a,a=t.numPan.b,o=e.parent.parentParticle;if(!Number.isFinite(r)||r<=0)return w(a*this.calcMaxCount(o));return w(a*Math.ceil(e.life/Math.min(Math.max(r,.01),1))*this.calcMaxCount(o))}onParticleCreated(e){if(e.body instanceof a.Sprite)return;const t=e.body;let r=this.meshes.find(e=>e.mesh.geometry.uuid===t.geometry.uuid&&e.mesh.material.uuid===t.material.uuid);if(null==r){const o=this.calcMaxCount(e);r={mesh:new a.InstancedMesh(t.geometry,t.material,o),indices:new Array(o).fill(null),particles:[],sortOrder:new Array(o),sortDistances:new Float32Array(o),sortedMatrices:new Float32Array(16*o),sortedColors:new Float32Array(4*o),sortedParticleData:new Float32Array(3*o),sortedRotations:new Float32Array(o),sortedParticles:new Array(o),sortedIndices:new Array(o)},r.mesh.renderOrder=t.renderOrder,r.mesh.userData.webGpuCacheMaterial=!0;const n=new Float32Array(4*o);n.fill(1),r.mesh.instanceColor=new a.InstancedBufferAttribute(n,4),r.mesh.setColorAt(0,S.setScalar(1)),r.mesh.instanceColor.needsUpdate=!0,r.mesh.material.defines.USE_INSTANCING="",r.mesh.material.needsUpdate=!0;const i=new Float32Array(3*o);M.makeScale(0,0,0);for(let e=0;e<o;e++)i[3*e+0]=1,i[3*e+1]=1,i[3*e+2]=1,r.mesh.instanceMatrix.array.set(M.elements,16*e);r.mesh.instanceMatrix.needsUpdate=!0,r.mesh.geometry.setAttribute("particleData",new a.InstancedBufferAttribute(i,3));const s=new a.InstancedBufferAttribute(new Float32Array(o),1);s.setUsage(a.DynamicDrawUsage),r.mesh.geometry.setAttribute("rotation",s),this.meshes.push(r),this.container.add(r.mesh)}let o=r.indices.findIndex(e=>null==e);if(o<0&&(o=r.indices.indexOf(Math.min(...r.indices))),o<0||o>=r.mesh.instanceMatrix.count)return void(e.target=null);const n=r.particles[o];if(null!=n&&n!==e&&(n.target=null),r.indices[o]=performance.now(),e.target=o,r.particles[o]=e,r.mesh.count<=o&&(r.mesh.count=o+1),"number"==typeof e.target){e.transform.initialScale;const t=e.body;let r=this.meshes.find(e=>e.mesh.geometry.uuid===t.geometry.uuid&&e.mesh.material.uuid===t.material.uuid);A(e,r.mesh),e.useAlpha&&(r.mesh.material.transparent||(r.mesh.material.transparent=!0,r.mesh.material.needsUpdate=!0))}}onParticleUpdate(e){const{target:t,useAlpha:r,useColor:a,rotation:o}=e;if(null==t)return;const n=e.body,i=this.meshes.find(e=>e.mesh.geometry.uuid===n.geometry.uuid&&e.mesh.material.uuid===n.material.uuid);if(null==i)return;if(!U(i,e))return void(e.target=null);if(i.mesh.frustumCulled=!1,A(e,i.mesh),a){S.copy(e.color);const t=i.mesh.instanceColor.array,r=e.target*i.mesh.instanceColor.itemSize;t[r+0]=S.r,t[r+1]=S.g,t[r+2]=S.b,i.mesh.instanceColor.itemSize>=4&&(t[r+3]=1),i.mesh.instanceColor.needsUpdate=!0}const s=i.mesh.geometry.getAttribute("particleData");r&&s.setX(e.target,e.alpha),s.setY(e.target,e.energy),s.needsUpdate=!0;const l=i.mesh.geometry.getAttribute("rotation");l.setX(e.target,o.z),l.needsUpdate=!0}onParticleDead(e){if(null!=e.target){const t=e.body,r=this.meshes.find(e=>e.mesh.geometry.uuid===t.geometry.uuid&&e.mesh.material.uuid===t.material.uuid);if(null==r)return;if(!U(r,e))return void(e.target=null);r.indices[e.target]=null,r.particles[e.target]=null,e.scale=0,A(e,r.mesh),r.mesh.instanceMatrix.needsUpdate=!0,e.target=null}}}const x=new n;function U(e,t){const r=t.target;return"number"==typeof r&&Number.isInteger(r)&&r>=0&&r<e.mesh.instanceMatrix.count&&e.particles[r]===t}function A(e,t){if(!Number.isInteger(e.target)||e.target<0||e.target>=t.instanceMatrix.count)return;if(v.set(e.position.x,e.position.y,e.position.z),e.transform&&e.transform.orientAlongVelocity){const r=p.set(e.position.x-e.old.position.x,e.position.y-e.old.position.y,e.position.z-e.old.position.z).normalize();t.getMatrixAt(e.target,x.matrix);const a=x.getWorldDirection(R);a.applyAxisAngle(y,Math.PI/-2),P.setFromUnitVectors(a,r)}else D.set(e.rotation.x,e.rotation.y,e.rotation.z),P.setFromEuler(D);const r=e.transform.initialScale;null!=r?R.set(r.x*e.scale,r.y*e.scale,r.z*e.scale):R.set(e.scale,e.scale,e.scale),M.compose(v,P,R),t.instanceMatrix.array.set(M.elements,16*e.target),t.instanceMatrix.needsUpdate=!0}const C=new a.Vector3(0,0,-1),M=new a.Matrix4,P=new a.Quaternion,v=new a.Vector3,R=new a.Vector3,D=new a.Euler,S=new a.Color(0),z={r:0,g:0,b:0};function F(e,t){const r=e.mesh,a=r.instanceMatrix.array,o=r.instanceColor.array,n=r.geometry.getAttribute("particleData"),i=n.array,s=r.geometry.getAttribute("rotation"),l=s.array,c=r.worldToLocal(t.getWorldPosition(I));let d=0;for(let t=0;t<e.particles.length;t++){if(null==e.particles[t])continue;const r=16*t,o=a[r+12]-c.x,n=a[r+13]-c.y,i=a[r+14]-c.z;e.sortDistances[t]=o*o+n*n+i*i,e.sortOrder[d++]=t}e.sortOrder.length=d,e.sortOrder.sort((t,r)=>e.sortDistances[r]-e.sortDistances[t]);for(let t=0;t<d;t++){const r=e.sortOrder[t];for(let o=0;o<16;o++)e.sortedMatrices[16*t+o]=a[16*r+o];for(let a=0;a<4;a++)e.sortedColors[4*t+a]=o[4*r+a];for(let a=0;a<3;a++)e.sortedParticleData[3*t+a]=i[3*r+a];e.sortedRotations[t]=l[r],e.sortedParticles[t]=e.particles[r],e.sortedIndices[t]=e.indices[r]}for(let t=0;t<d;t++){const r=e.sortedParticles[t];e.particles[t]=r,e.indices[t]=e.sortedIndices[t],l[t]=e.sortedRotations[t],r.target=t}for(let t=d;t<e.indices.length;t++)e.particles[t]=null,e.indices[t]=null;for(let t=0;t<16*d;t++)a[t]=e.sortedMatrices[t];for(let t=0;t<4*d;t++)o[t]=e.sortedColors[t];for(let t=0;t<3*d;t++)i[t]=e.sortedParticleData[t];r.count=d,r.instanceMatrix.needsUpdate=!0,r.instanceColor.needsUpdate=!0,n.needsUpdate=!0,s.needsUpdate=!0}export{F as sortInstancedMeshByDistance};const I=new a.Vector3;/*
|
|
1
|
+
import{CustomRenderer as e,MeshRenderer as t,PUID as r}from"@hology/nebula";import*as a from"three";import{InstancedBufferAttribute as o,Object3D as i}from"three";import{NodeShaderMaterial as n}from"three-shader-graph";import{StretchedSprite as s}from"./stretched-sprite.js";import{Trail as l}from"./trail-renderer.js";import{SpriteNodeShaderMaterial as c}from"../../shader/sprite-shader.js";import{particleEnergyUniformName as d}from"../../shader-nodes/particle.js";import{AssetMeshInstance as m}from"../../scene/asset-resource-loader.js";import{markMaterialUniformValuesNeedUpdate as u}from"../../rendering/node-material-webgpu-resolver.js";var p;!function(e){e[e.mesh=0]="mesh",e[e.instanced=1]="instanced",e[e.sprite=2]="sprite",e[e.stretchedBillboard=3]="stretchedBillboard",e[e.trail=4]="trail",e[e.pointLight=5]="pointLight"}(p||(p={}));export class MultiRenderer extends e{constructor(e,t,r,a){super(),this.worldContainer=e,this.localContainer=t,this.three=r,this.view=a,this.localRenderers=[],this.worldRenderers=[]}prepareLightOutputs(e){var t;for(const r of e){const e=r;if("pointLight"!==e.type)continue;const a="local"===e.output.space?this.localRenderers:this.worldRenderers,o="local"===e.output.space?this.localContainer:this.worldContainer;(a[t=p.pointLight]??(a[t]=new PointLightRenderer(o))).prepare(e)}}onSystemUpdate(){for(const e of this.worldRenderers)e?.onSystemUpdate();for(const e of this.localRenderers)e?.onSystemUpdate()}onParticleCreated(e){e.target=null,this.getRenderer(e)?.onParticleCreated(e)}onParticleUpdate(e){this.getRenderer(e)?.onParticleUpdate(e)}onParticleDead(e){this.getRenderer(e)?.onParticleDead(e),e._renderer=null}getRenderer(e){var t;if(e._renderer)return e._renderer;const r=(null!=e?.parent?e?.parent._space:null)??"world",o="world"===r?this.worldContainer:this.localContainer,i="world"===r?this.worldRenderers:this.localRenderers;return"pointLight"===e.body.type?e._renderer=i[t=p.pointLight]??(i[t]=new PointLightRenderer(o)):"trail"===e.body.type?(null==i[p.trail]&&(i[p.trail]=new TrailRenderer(o,this.three)),e._renderer=i[p.trail]):e.body instanceof s?(null==i[p.stretchedBillboard]&&(i[p.stretchedBillboard]=new StretchedSpriteInstancedRenderer(o,this.three,this.view)),e._renderer=i[p.stretchedBillboard]):e.body instanceof a.Sprite?(null==i[p.sprite]&&(i[p.sprite]=new CombinedRenderer(o,this.three)),e._renderer=i[p.sprite]):e.body instanceof a.Mesh&&0==e.body.children.length&&e.body.material instanceof n?(null==i[p.instanced]&&(i[p.instanced]=new InstancedRenderer(o,this.three,this.view)),e._renderer=i[p.instanced]):e.body instanceof a.Object3D?(null==i[p.mesh]&&(i[p.mesh]=new CombinedRenderer(o,this.three)),e._renderer=i[p.mesh]):void 0}dispose(){this.localRenderers.forEach(e=>{(e instanceof InstancedRenderer||e instanceof PointLightRenderer)&&e.dispose()}),this.worldRenderers.forEach(e=>{(e instanceof InstancedRenderer||e instanceof PointLightRenderer)&&e.dispose()})}}export class PointLightRenderer{constructor(e){this.container=e,this.pools=new Map}onSystemUpdate(){}prepare(e){let t=this.pools.get(e);if(null==t){const r=e.output.maxLights??4,o=Number.isFinite(r)?Math.min(32,Math.max(0,Math.floor(r))):4,i=[];for(let e=0;e<o;e++){const e=new a.PointLight;e.userData.vfxTransientLight=!0,e.castShadow=!1,i.push(e)}t={lights:i,free:i.slice(),color:new a.Color(e.output.color??"#ffffff")},this.pools.set(e,t)}return t}onParticleCreated(e){const t=this.prepare(e.body).free.pop();e.target=t,null!=t&&(this.onParticleUpdate(e),this.container.add(t))}onParticleUpdate(e){const t=e.target;if(null==t)return;const r=e.body.output;t.position.set(e.position.x,e.position.y,e.position.z),t.color.copy(this.pools.get(e.body).color),e.useColor&&t.color.multiply(e.color);const a=(r.intensity??1)*(e.useAlpha?e.alpha:1),o=(r.radius??3)*Math.abs(e.scale);t.intensity=Number.isFinite(a)?Math.max(0,a):0,t.distance=Number.isFinite(o)?Math.max(.001,o):.001,t.visible=t.intensity>0&&o>0&&Number.isFinite(o)}onParticleDead(e){const t=e.target;null!=t&&(t.removeFromParent(),this.pools.get(e.body)?.free.push(t),e.target=null)}dispose(){for(const e of this.pools.values())for(const t of e.lights)t.removeFromParent(),t.dispose();this.pools.clear()}}export class TrailRenderer extends t{constructor(){super(...arguments),this.trails=[]}dispose(){}onSystemUpdate(){for(const e of this.trails)e.update()}onParticleCreated(e){const t=e.body,r=new l(this.container,!1),o=new i;this.container.add(o),o.updateMatrixWorld(),o.updateWorldMatrix(!0,!0);const n=t.material?.clone()??l.createBaseMaterial(t.texture,t.scrollSpeed,t.opacityChannel,t.billboard??!1);n.uniforms.trailUvMode&&(n.uniforms.trailUvMode.value="repeat"===t.uvMode?1:0),n.uniforms.unitsPerRepeat&&(n.uniforms.unitsPerRepeat.value=Math.max(.001,t.unitsPerRepeat??1)),n.uniforms.taper&&(n.uniforms.taper.value=t.taper??!1);const s=new a.Color(t.color??0).multiplyScalar(t.intensity??1),c=new a.Color(t.colorEnd??0).multiplyScalar(t.intensityEnd??t.intensity??1);n.uniforms.headColor&&n.uniforms.tailColor&&(n.uniforms.headColor.value=new a.Vector4(s.r,s.g,s.b,t.opacityStart??1),n.uniforms.tailColor.value=new a.Vector4(c.r,c.g,c.b,t.opacityEnd??1)),n.uniforms.color&&null==n.uniforms.headColor&&(n.uniforms.color.value=s),n.uniforms.opacity&&null==n.uniforms.headColor&&(n.uniforms.opacity.value=t.opacityStart??1),t.bloom&&(n.userData.hasBloom=!0),r.initialize(n,Math.round(t.length??10),!1,t.width,null,o,t.billboard??!1),null!=t.renderOrder&&null!=r.mesh&&(r.mesh.renderOrder=t.renderOrder);const d=e.rotation;o.rotation.set(d.x,d.y,d.z);e.scale;r.activate(),this.trails.push(r),e.target=o}onParticleUpdate(e){const t=e.target;t.position.copy(e.position);const r=e.body;let a=v;e.old&&e.old.position?a.set(e.position.x-e.old.position.x,e.position.y-e.old.position.y,e.position.z-e.old.position.z):a.copy(e.velocity),a.lengthSq()>1e-8?a.normalize():a.copy(A);const o=P;o.setFromUnitVectors(a,A),t.rotation.set(0,0,0),t.rotation.set(e.rotation.x,e.rotation.y,e.rotation.z),t.applyQuaternion(o);const i=this.trails.findIndex(e=>e.targetObject.id===t.id);if(-1!=i){const t=this.trails[i];if(e.useAlpha){if(t.material.uniforms.headColor&&t.material.uniforms.tailColor){t.material.uniforms.headColor.value.setW((r.opacityStart??1)*e.alpha);t.material.uniforms.tailColor.value.setW((r.opacityEnd??1)*e.alpha)}null!=t.material.uniforms.opacity&&(t.material.uniforms.opacity.value=e.alpha)}e.useColor&&null!=t.material.uniforms.color&&t.material.uniforms.color.value.copy(e.color),null!=t.material.uniforms[d]&&(t.material.uniforms[d].value=e.energy),u(t.material)}}onParticleDead(e){const t=e.target,r=this.trails.findIndex(e=>e.targetObject.id===t.id);if(-1!=r){const e=this.trails[r];e.removeFromParent(),e.mesh.removeFromParent(),e.targetObject.removeFromParent(),t.removeFromParent(),this.trails.splice(r,1)}}}export class CombinedRenderer extends t{dispose(){}scale(e){const t=e.transform.initialScale;e.target instanceof a.Sprite?null!=t?e.target.scale.set(t.x*e.scale*e.radius,t.y*e.scale*e.radius,1):e.target.scale.set(e.scale*e.radius,e.scale*e.radius,1):null!=t?e.target.scale.set(t.x*e.scale,t.y*e.scale,t.z*e.scale):super.scale(e)}rotate(e){e.target.material.rotation=e.rotation.z}onParticleCreated(e){e.target||(e.target=this._targetPool.get(e.body),(e.useAlpha||e.useColor)&&(e.target instanceof a.Mesh||e.target instanceof a.Sprite&&e.target.material instanceof a.Material)&&(e.target.material.__puid=r.id(e.body.material),e.target.material=this._materialPool.get(e.target.material))),e.target&&e.target instanceof i&&(e.target.position.set(e.position.x,e.position.y,e.position.z),this.container.add(e.target))}onParticleUpdate(e){const{target:t,useAlpha:r,useColor:o,rotation:n}=e;if(t){if(t.position.copy(e.position),this.isThreeSprite(e)||(t instanceof a.Mesh&&t.material instanceof c?e.target.material.rotation=e.rotation.z:t.rotation.set(n.x,n.y,n.z)),this.scale(e),e.transform&&e.transform.orientAlongVelocity&&t instanceof i){const r=h.set(e.velocity.x,e.velocity.y,e.velocity.z).normalize(),a=t.getWorldDirection(f);a.applyAxisAngle(y,Math.PI/-2),g.setFromUnitVectors(a,r),t.quaternion.copy(g)}(r||o)&&this.applyUniforms(t,e)}}applyUniforms(e,t){if(e instanceof m&&e.traverse(r=>r!==e&&this.applyUniforms(r,t)),e instanceof a.Mesh){let r=!1;t.useAlpha&&(e.material instanceof c&&null!=e.material.uniforms.opacity&&(e.material.uniforms.opacity.value=t.alpha,r=!0),e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.opacity?(e.material.uniforms.opacity.value=t.alpha,r=!0):(e.material.opacity=t.alpha,r=!0),e.material.transparent=!0),t.useColor&&(null!=e.material.color?(e.material.color.copy(t.color),r=!0):e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.color&&(e.material.uniforms.color.value=t.color,r=!0)),r&&u(e.material)}}onParticleDead(e){e.target&&(e.target.material&&(e.useAlpha||e.useColor)&&this._materialPool.expire(e.target.material),this._targetPool.expire(e.target),this.container.remove(e.target),e.target=null)}}export class StretchedSpriteInstancedRenderer extends t{constructor(e,t,r){super(e,t),this.view=r,this.meshes=[]}dispose(){this.meshes.forEach(e=>e.mesh.geometry.dispose())}onSystemUpdate(){for(const e of this.meshes){const t=e.mesh.geometry.getAttribute("offset"),r=e.mesh.geometry.getAttribute("size"),o=e.mesh.geometry.getAttribute("velocity"),i=e.mesh.geometry.getAttribute("rotation"),n=e.mesh.geometry.getAttribute("particleColor");let s=0;for(const l of e.particles){if(null==l||l.dead)continue;if(l.useColor||l.color instanceof a.Color){const e=S.copy(l.color).getRGB(F);n.setXYZW(s,e.r,e.g,e.b,1),n.needsUpdate=!0}l.useAlpha&&(n.setW(s,l.alpha),n.needsUpdate=!0),"number"==typeof l.rotation?i.setX(s,l.rotation):i.setX(s,l.rotation.x);const e=l.position;if(t.setXYZ(s,e.x,e.y,e.z),"number"!=typeof l.scale)throw new Error("Particle scale is not a number");r.setXYZ(s,l.scale,l.scale,l.scale);let c=l.body.scaleFactor;0===c&&(c=.001);const d=1,m=l.velocity;o.setXYZW(s,m.x*c,m.y*c,m.z*c,d),s++}s>0&&(t.clearUpdateRanges(),t.addUpdateRange(0,3*s),t.needsUpdate=!0,r.clearUpdateRanges(),r.addUpdateRange(0,3*s),r.needsUpdate=!0,n.clearUpdateRanges(),n.addUpdateRange(0,4*s),n.needsUpdate=!0,o.clearUpdateRanges(),o.addUpdateRange(0,4*s),o.needsUpdate=!0,i.clearUpdateRanges(),i.addUpdateRange(0,s),i.needsUpdate=!0)}}calcMaxCount(e){if(null==e)return 1;const t=e.parent.rate,r=t.timePan.a;if(!Number.isFinite(r)||r<=0)return t.numPan.b*this.calcMaxCount(e.parent.parentParticle);return Math.min(t.numPan.b*Math.ceil(e.life/Math.min(Math.max(t.timePan.a,.01),1)),1e5)*this.calcMaxCount(e.parent.parentParticle)}onParticleCreated(e){if(e.body instanceof s&&null==e.target){const t=e.body;let r=this.meshes.find(e=>e.mesh.material.uuid===t.material.uuid);if(null==r){const i=this.calcMaxCount(e),n=new a.InstancedBufferGeometry;n.setIndex(t.geometry.getIndex()),n.setAttribute("position",t.geometry.getAttribute("position")),t.geometry.hasAttribute("normal")&&n.setAttribute("normal",t.geometry.getAttribute("normal")),n.setAttribute("uv",t.geometry.getAttribute("uv"));const s=new o(new Float32Array(3*i),3);s.setUsage(a.DynamicDrawUsage),n.setAttribute("offset",s);const l=new o(new Float32Array(4*i),4);if(l.setUsage(a.DynamicDrawUsage),n.setAttribute("particleColor",l),t.material instanceof c){const e=new a.Color(t.material.color);for(let t=0;t<l.count;t++)l.setXYZW(t,e.r,e.g,e.b,1)}const d=new o(new Float32Array(3*i),3);d.setUsage(a.DynamicDrawUsage),n.setAttribute("size",d);const m=new o(new Float32Array(4*i),4);m.setUsage(a.DynamicDrawUsage),n.setAttribute("velocity",m);const u=new o(new Float32Array(4*i),1);u.setUsage(a.DynamicDrawUsage),n.setAttribute("rotation",u),r={mesh:new a.Mesh(n,t.material),indices:new Float32Array(i),particles:[]},r.mesh.renderOrder=t.renderOrder,this.meshes.push(r),this.container.add(r.mesh)}let i=r.indices.findIndex(e=>0===e);i<0&&(i=function(e){let t=e[0],r=0;for(let a=1;a<e.length;a++)e[a]<t&&(t=e[a],r=a);return r}(r.indices)),r.indices[i]=performance.now(),e.target=i,r.particles[i]=e}}onParticleUpdate(e){}onParticleDead(e){const t=e.body;let r=this.meshes.find(e=>e.mesh.material.uuid===t.material.uuid);if(r){const t=r.mesh.geometry.getAttribute("size");t.setXYZ(e.target,0,0,0),t.needsUpdate=!0,r.particles[e.target]=null,r.indices[e.target]=0}e.target=null}}const h=new a.Vector3,f=new a.Vector3,g=new a.Quaternion,y=new a.Vector3(1,0,0),b=1e5;function w(e){return Number.isFinite(e)?Math.min(Math.max(Math.ceil(e),1),b):b}export class InstancedRenderer extends t{constructor(e,t,r){super(e,t),this.view=r,this.meshes=[]}dispose(){this.meshes.forEach(e=>{e.mesh.geometry.dispose(),e.mesh.dispose()})}onSystemUpdate(){for(const e of this.meshes)e.mesh.material.transparent&&e.mesh.material.blending===a.NormalBlending&&z(e,this.view.getCamera())}calcMaxCount(e){if(null==e)return 1;const t=e.parent.rate,r=t.timePan.a,a=t.numPan.b,o=e.parent.parentParticle;if(!Number.isFinite(r)||r<=0)return w(a*this.calcMaxCount(o));return w(a*Math.ceil(e.life/Math.min(Math.max(r,.01),1))*this.calcMaxCount(o))}onParticleCreated(e){if(e.body instanceof a.Sprite)return;const t=e.body;let r=this.meshes.find(e=>e.mesh.geometry.uuid===t.geometry.uuid&&e.mesh.material.uuid===t.material.uuid);if(null==r){const o=this.calcMaxCount(e);r={mesh:new a.InstancedMesh(t.geometry,t.material,o),indices:new Array(o).fill(null),particles:[],sortOrder:new Array(o),sortDistances:new Float32Array(o),sortedMatrices:new Float32Array(16*o),sortedColors:new Float32Array(4*o),sortedParticleData:new Float32Array(3*o),sortedRotations:new Float32Array(o),sortedParticles:new Array(o),sortedIndices:new Array(o)},r.mesh.renderOrder=t.renderOrder,r.mesh.userData.webGpuCacheMaterial=!0;const i=new Float32Array(4*o);i.fill(1),r.mesh.instanceColor=new a.InstancedBufferAttribute(i,4),r.mesh.setColorAt(0,S.setScalar(1)),r.mesh.instanceColor.needsUpdate=!0,r.mesh.material.defines.USE_INSTANCING="",r.mesh.material.needsUpdate=!0;const n=new Float32Array(3*o);M.makeScale(0,0,0);for(let e=0;e<o;e++)n[3*e+0]=1,n[3*e+1]=1,n[3*e+2]=1,r.mesh.instanceMatrix.array.set(M.elements,16*e);r.mesh.instanceMatrix.needsUpdate=!0,r.mesh.geometry.setAttribute("particleData",new a.InstancedBufferAttribute(n,3));const s=new a.InstancedBufferAttribute(new Float32Array(o),1);s.setUsage(a.DynamicDrawUsage),r.mesh.geometry.setAttribute("rotation",s),this.meshes.push(r),this.container.add(r.mesh)}let o=r.indices.findIndex(e=>null==e);if(o<0&&(o=r.indices.indexOf(Math.min(...r.indices))),o<0||o>=r.mesh.instanceMatrix.count)return void(e.target=null);const i=r.particles[o];if(null!=i&&i!==e&&(i.target=null),r.indices[o]=performance.now(),e.target=o,r.particles[o]=e,r.mesh.count<=o&&(r.mesh.count=o+1),"number"==typeof e.target){e.transform.initialScale;const t=e.body;let r=this.meshes.find(e=>e.mesh.geometry.uuid===t.geometry.uuid&&e.mesh.material.uuid===t.material.uuid);U(e,r.mesh),e.useAlpha&&(r.mesh.material.transparent||(r.mesh.material.transparent=!0,r.mesh.material.needsUpdate=!0))}}onParticleUpdate(e){const{target:t,useAlpha:r,useColor:a,rotation:o}=e;if(null==t)return;const i=e.body,n=this.meshes.find(e=>e.mesh.geometry.uuid===i.geometry.uuid&&e.mesh.material.uuid===i.material.uuid);if(null==n)return;if(!C(n,e))return void(e.target=null);if(n.mesh.frustumCulled=!1,U(e,n.mesh),a){S.copy(e.color);const t=n.mesh.instanceColor.array,r=e.target*n.mesh.instanceColor.itemSize;t[r+0]=S.r,t[r+1]=S.g,t[r+2]=S.b,n.mesh.instanceColor.itemSize>=4&&(t[r+3]=1),n.mesh.instanceColor.needsUpdate=!0}const s=n.mesh.geometry.getAttribute("particleData");r&&s.setX(e.target,e.alpha),s.setY(e.target,e.energy),s.needsUpdate=!0;const l=n.mesh.geometry.getAttribute("rotation");l.setX(e.target,o.z),l.needsUpdate=!0}onParticleDead(e){if(null!=e.target){const t=e.body,r=this.meshes.find(e=>e.mesh.geometry.uuid===t.geometry.uuid&&e.mesh.material.uuid===t.material.uuid);if(null==r)return;if(!C(r,e))return void(e.target=null);r.indices[e.target]=null,r.particles[e.target]=null,e.scale=0,U(e,r.mesh),r.mesh.instanceMatrix.needsUpdate=!0,e.target=null}}}const x=new i;function C(e,t){const r=t.target;return"number"==typeof r&&Number.isInteger(r)&&r>=0&&r<e.mesh.instanceMatrix.count&&e.particles[r]===t}function U(e,t){if(!Number.isInteger(e.target)||e.target<0||e.target>=t.instanceMatrix.count)return;if(v.set(e.position.x,e.position.y,e.position.z),e.transform&&e.transform.orientAlongVelocity){const r=h.set(e.position.x-e.old.position.x,e.position.y-e.old.position.y,e.position.z-e.old.position.z).normalize();t.getMatrixAt(e.target,x.matrix);const a=x.getWorldDirection(R);a.applyAxisAngle(y,Math.PI/-2),P.setFromUnitVectors(a,r)}else D.set(e.rotation.x,e.rotation.y,e.rotation.z),P.setFromEuler(D);const r=e.transform.initialScale;null!=r?R.set(r.x*e.scale,r.y*e.scale,r.z*e.scale):R.set(e.scale,e.scale,e.scale),M.compose(v,P,R),t.instanceMatrix.array.set(M.elements,16*e.target),t.instanceMatrix.needsUpdate=!0}const A=new a.Vector3(0,0,-1),M=new a.Matrix4,P=new a.Quaternion,v=new a.Vector3,R=new a.Vector3,D=new a.Euler,S=new a.Color(0),F={r:0,g:0,b:0};function z(e,t){const r=e.mesh,a=r.instanceMatrix.array,o=r.instanceColor.array,i=r.geometry.getAttribute("particleData"),n=i.array,s=r.geometry.getAttribute("rotation"),l=s.array,c=r.worldToLocal(t.getWorldPosition(I));let d=0;for(let t=0;t<e.particles.length;t++){if(null==e.particles[t])continue;const r=16*t,o=a[r+12]-c.x,i=a[r+13]-c.y,n=a[r+14]-c.z;e.sortDistances[t]=o*o+i*i+n*n,e.sortOrder[d++]=t}e.sortOrder.length=d,e.sortOrder.sort((t,r)=>e.sortDistances[r]-e.sortDistances[t]);for(let t=0;t<d;t++){const r=e.sortOrder[t];for(let o=0;o<16;o++)e.sortedMatrices[16*t+o]=a[16*r+o];for(let a=0;a<4;a++)e.sortedColors[4*t+a]=o[4*r+a];for(let a=0;a<3;a++)e.sortedParticleData[3*t+a]=n[3*r+a];e.sortedRotations[t]=l[r],e.sortedParticles[t]=e.particles[r],e.sortedIndices[t]=e.indices[r]}for(let t=0;t<d;t++){const r=e.sortedParticles[t];e.particles[t]=r,e.indices[t]=e.sortedIndices[t],l[t]=e.sortedRotations[t],r.target=t}for(let t=d;t<e.indices.length;t++)e.particles[t]=null,e.indices[t]=null;for(let t=0;t<16*d;t++)a[t]=e.sortedMatrices[t];for(let t=0;t<4*d;t++)o[t]=e.sortedColors[t];for(let t=0;t<3*d;t++)n[t]=e.sortedParticleData[t];r.count=d,r.instanceMatrix.needsUpdate=!0,r.instanceColor.needsUpdate=!0,i.needsUpdate=!0,s.needsUpdate=!0}export{z as sortInstancedMeshByDistance};const I=new a.Vector3;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -296,6 +296,8 @@ export declare class CharacterAnimationComponent extends ActorComponent {
|
|
|
296
296
|
removeStateMachine(sm: AnimationStateMachine): void;
|
|
297
297
|
removeUpperStateMachine(sm: AnimationStateMachine): void;
|
|
298
298
|
playUpper(clip: AnimationClip, options?: PlayOptions): void;
|
|
299
|
+
/** Release an upper-body override only if it still belongs to this clip. */
|
|
300
|
+
cancelUpper(clip: AnimationClip): void;
|
|
299
301
|
private getFullBodyClip;
|
|
300
302
|
private getUpperBodyClip;
|
|
301
303
|
play(clip: AnimationClip, options?: PlayOptions): void;
|