@hology/core 0.0.212 → 0.0.213
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/sequence/index.d.ts +1 -0
- package/dist/effects/sequence/index.js +1 -1
- package/dist/effects/sequence/sequence-action.d.ts +5 -0
- package/dist/effects/sequence/sequence-actor.d.ts +7 -0
- package/dist/effects/sequence/sequence-actor.js +1 -1
- package/dist/effects/sequence/sequence-animation-retiming.js +1 -1
- package/dist/effects/sequence/sequence-data.d.ts +9 -0
- package/dist/effects/sequence/sequence-data.js +1 -1
- package/dist/effects/sequence/sequence-player.d.ts +12 -1
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/effects/sequence/sequence-transform.d.ts +10 -0
- package/dist/effects/sequence/sequence-transform.js +4 -0
- package/dist/effects/sequence/sequence-value-lane.d.ts +2 -0
- package/dist/effects/sequence/sequence-value-lane.js +1 -1
- package/dist/effects/vfx/vfx-actor.d.ts +5 -0
- package/dist/effects/vfx/vfx-actor.js +1 -1
- package/dist/effects/vfx/vfx-asset.d.ts +2 -1
- package/dist/effects/vfx/vfx-asset.js +1 -1
- package/dist/effects/vfx/vfx-materializer.js +1 -1
- package/dist/effects/vfx/vfx-renderers.d.ts +1 -0
- package/dist/effects/vfx/vfx-renderers.js +1 -1
- package/dist/gameplay/actors/actor.d.ts +3 -5
- package/dist/gameplay/actors/actor.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +3 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-movement-like.d.ts +2 -0
- package/dist/gameplay/actors/builtin/components/character/character-movement.d.ts +13 -0
- package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/net-character-movement-protocol.d.ts +4 -1
- package/dist/gameplay/actors/builtin/components/character/net-character-movement-protocol.js +1 -1
- package/dist/gameplay/actors/builtin/components/mesh-component.d.ts +1 -0
- package/dist/gameplay/actors/builtin/components/mesh-component.js +1 -1
- package/dist/gameplay/actors/builtin/components/tween-component.js +1 -1
- package/dist/gameplay/actors/builtin/navmesh-actor.d.ts +2 -0
- package/dist/gameplay/actors/builtin/navmesh-actor.js +1 -1
- package/dist/gameplay/actors/builtin/trigger-volume.d.ts +3 -3
- package/dist/gameplay/actors/builtin/trigger-volume.js +1 -1
- package/dist/gameplay/actors/camera/camera-component.d.ts +1 -3
- package/dist/gameplay/actors/camera/camera-component.js +1 -1
- package/dist/gameplay/actors/component.d.ts +3 -0
- package/dist/gameplay/actors/component.js +1 -1
- package/dist/gameplay/actors/type-registry.d.ts +10 -0
- package/dist/gameplay/actors/type-registry.js +4 -0
- package/dist/gameplay/animation/retarget.d.ts +50 -0
- package/dist/gameplay/animation/retarget.js +4 -0
- package/dist/gameplay/animation/root-motion.js +1 -1
- package/dist/gameplay/event-graph/actor-transform-properties.d.ts +6 -0
- package/dist/gameplay/event-graph/actor-transform-properties.js +4 -0
- package/dist/gameplay/event-graph/asset-actor-types.d.ts +3 -0
- package/dist/gameplay/event-graph/asset-actor-types.js +4 -0
- package/dist/gameplay/event-graph/asset-references.d.ts +12 -0
- package/dist/gameplay/event-graph/asset-references.js +4 -0
- package/dist/gameplay/event-graph/compiler.d.ts +107 -0
- package/dist/gameplay/event-graph/compiler.js +4 -0
- package/dist/gameplay/event-graph/decorators.d.ts +74 -0
- package/dist/gameplay/event-graph/decorators.js +4 -0
- package/dist/gameplay/event-graph/document-compiler.d.ts +107 -0
- package/dist/gameplay/event-graph/document-compiler.js +4 -0
- package/dist/gameplay/event-graph/event-graph-demo.d.ts +2 -0
- package/dist/gameplay/event-graph/event-graph-demo.js +4 -0
- package/dist/gameplay/event-graph/graph.d.ts +106 -0
- package/dist/gameplay/event-graph/graph.js +4 -0
- package/dist/gameplay/event-graph/index.d.ts +13 -0
- package/dist/gameplay/event-graph/index.js +4 -0
- package/dist/gameplay/event-graph/model.d.ts +71 -0
- package/dist/gameplay/event-graph/model.js +4 -0
- package/dist/gameplay/event-graph/nodes/execution-nodes.d.ts +115 -0
- package/dist/gameplay/event-graph/nodes/execution-nodes.js +4 -0
- package/dist/gameplay/event-graph/nodes/gameplay-nodes.d.ts +208 -0
- package/dist/gameplay/event-graph/nodes/gameplay-nodes.js +4 -0
- package/dist/gameplay/event-graph/nodes/index.d.ts +4 -0
- package/dist/gameplay/event-graph/nodes/index.js +4 -0
- package/dist/gameplay/event-graph/nodes/value-nodes.d.ts +53 -0
- package/dist/gameplay/event-graph/nodes/value-nodes.js +4 -0
- package/dist/gameplay/event-graph/registry.d.ts +33 -0
- package/dist/gameplay/event-graph/registry.js +4 -0
- package/dist/gameplay/event-graph/runtime-assets.d.ts +21 -0
- package/dist/gameplay/event-graph/runtime-assets.js +4 -0
- package/dist/gameplay/event-graph/runtime.d.ts +160 -0
- package/dist/gameplay/event-graph/runtime.js +4 -0
- package/dist/gameplay/event-graph/type-inference.d.ts +41 -0
- package/dist/gameplay/event-graph/type-inference.js +4 -0
- package/dist/gameplay/event-graph/types.d.ts +77 -0
- package/dist/gameplay/event-graph/types.js +4 -0
- package/dist/gameplay/index.d.ts +15 -2
- package/dist/gameplay/index.js +1 -1
- package/dist/gameplay/initiate.js +1 -1
- package/dist/gameplay/input/input.d.ts +22 -17
- package/dist/gameplay/input/input.js +1 -1
- package/dist/gameplay/net/service/net-decorator.d.ts +1 -1
- package/dist/gameplay/net/service/net-decorator.js +1 -1
- package/dist/gameplay/net/service/net-serializer.js +1 -1
- package/dist/gameplay/net/service/net-service.d.ts +6 -6
- package/dist/gameplay/net/service/net-service.js +1 -1
- package/dist/gameplay/net/service/rpc-decorator.js +1 -1
- package/dist/gameplay/services/asset-loader.d.ts +7 -0
- package/dist/gameplay/services/asset-loader.js +1 -1
- package/dist/gameplay/services/physics/physics-system.d.ts +14 -0
- package/dist/gameplay/services/physics/physics-system.js +1 -1
- package/dist/gameplay/services/world.d.ts +12 -0
- package/dist/gameplay/services/world.js +1 -1
- package/dist/rendering.js +1 -1
- package/dist/scene/asset-resource-loader.d.ts +7 -1
- package/dist/scene/asset-resource-loader.js +1 -1
- package/dist/scene/custom-param-deserialize.js +1 -1
- package/dist/scene/custom-param-runtime-types.js +1 -1
- package/dist/scene/materializer.d.ts +25 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/scene/model.d.ts +114 -2
- package/dist/scene/model.js +1 -1
- package/dist/scene/objects/prefab.js +1 -1
- package/dist/shader/graph/compiler.d.ts +3 -0
- package/dist/shader/graph/compiler.js +1 -1
- package/dist/shader/graph/model.d.ts +1 -1
- package/dist/shader/graph/registry.js +1 -1
- package/dist/test/animation-retarget.test.d.ts +2 -0
- package/dist/test/animation-retarget.test.js +4 -0
- package/dist/test/event-graph-generated-code.test.d.ts +2 -0
- package/dist/test/event-graph-generated-code.test.js +4 -0
- package/dist/test/event-graph-types.test.d.ts +2 -0
- package/dist/test/event-graph-types.test.js +4 -0
- package/dist/test/event-graph.test.d.ts +2 -0
- package/dist/test/event-graph.test.js +4 -0
- package/dist/test/input.test.d.ts +2 -0
- package/dist/test/input.test.js +4 -0
- package/dist/test/net-character-movement.test.js +1 -1
- package/dist/test/prefab-instance-params.test.js +1 -1
- package/dist/test/sequence-camera-control.test.js +1 -1
- package/dist/test/sequence-scene-binding.test.d.ts +2 -0
- package/dist/test/sequence-scene-binding.test.js +4 -0
- package/dist/test/sequence-transform.test.d.ts +2 -0
- package/dist/test/sequence-transform.test.js +4 -0
- package/dist/test/shader-graph.test.js +1 -1
- package/package.json +10 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{SerializedParamType as e}from"../../scene/model.js";import{Color as t}from"three";export function sortTimedSequenceItems(e){return[...e].sort((e,t)=>e.time-t.time)}export function findKeyframeSpan(e,t){if(0===e.length)return{prev:null,next:null,t:0};let r=null,n=null;for(let
|
|
1
|
+
import{SerializedParamType as e}from"../../scene/model.js";import{Color as t}from"three";export function sortTimedSequenceItems(e){return[...e].sort((e,t)=>e.time-t.time)}export function findKeyframeSpan(e,t){if(0===e.length)return{prev:null,next:null,prevIndex:-1,nextIndex:-1,t:0};let r=null,n=null,u=-1,l=-1;for(let i=0;i<e.length;i++)if(e[i].time<=t&&(r=e[i],u=i),e[i].time>t&&!n){n=e[i],l=i;break}if(r||(r=e[0],u=0),n||(n=e[e.length-1],l=e.length-1),r===n||r.time===n.time)return{prev:r,next:r,prevIndex:u,nextIndex:u,t:0};return{prev:r,next:n,prevIndex:u,nextIndex:l,t:(t-r.time)/(n.time-r.time)}}export function applyInterpolation(e,t){switch(t){case"linear":case"catmullRom":default:return e;case"step":return 0;case"cubic":return e<.5?4*e*e*e:1-Math.pow(-2*e+2,3)/2}}export function clampNumericValue(e,t={}){const r=t.min,n=t.max;let u=e;return"number"==typeof r&&(u=Math.max(r,u)),"number"==typeof n&&(u=Math.min(n,u)),u}export function evaluateNumericKeyframes(e,t,n={}){if(0===e.length)return;const{prev:u,next:l,t:i}=findKeyframeSpan(e,t);if(!u)return;const a=r(u.value);if("number"!=typeof a)return;let o=a;if(l&&l!==u){const e=r(l.value);"number"==typeof e&&(o="step"===u.interpolation?a:a+(e-a)*applyInterpolation(i,u.interpolation))}return clampNumericValue(o,n)}export function evaluateCustomParamValueKeyframes(t,r,f={}){if(0===t.length)return null;const{prev:m,next:s,t:y}=findKeyframeSpan(t,r);if(!m)return null;const v=m.value;if(!s||s===m)return n(v,f);if(!f.stepOnly&&"step"!==m.interpolation){const t=function(t,r,n,f){if(t?.type!==r?.type)return null;if(t.type===e.Number&&r.type===e.Number){const u=i(t.value),l=i(r.value);return null==u||null==l?null:{type:e.Number,value:clampNumericValue(u+(l-u)*n,f)}}const m=u(t.type);if(m>0){const u=l(t.value,m),i=l(r.value,m);if(!u||!i)return null;const o=new Array(m);for(let e=0;e<m;e++)o[e]=clampNumericValue(u[e]+(i[e]-u[e])*n,f);return t.type===e.Euler&&(o[3]=a(t.value)??a(r.value)??"XYZ"),{type:t.type,value:o}}if(t.type===e.Color){const u=function(e,t,r){try{return o(c,e),o(p,t),c.lerp(p,r),`#${c.getHexString()}`}catch{return null}}(t.value,r.value,n);return null==u?null:{type:e.Color,value:u}}return null}(v,s.value,applyInterpolation(y,m.interpolation),f);if(t)return t}return n(v,f)}function r(t){if(t?.type===e.Number)return i(t.value)??void 0}function n(t,r){if(t?.type===e.Number&&"number"==typeof t.value)return{...t,value:clampNumericValue(t.value,r)};if(t?.type===e.Number){const e=i(t.value);if(null!=e)return{...t,value:clampNumericValue(e,r)}}const n=u(t?.type);if(n>0){const e=t.value;if(!Array.isArray(e))return t;const u=e.slice();for(let e=0;e<Math.min(n,u.length);e++){const t=i(u[e]);null!=t&&(u[e]=clampNumericValue(t,r))}return{...t,value:u}}return t}function u(t){switch(t){case e.Vector2:return 2;case e.Vector3:return 3;case e.Vector4:return 4;case e.Euler:return 3;default:return 0}}function l(e,t){if(!Array.isArray(e)||e.length<t)return null;const r=new Array(t);for(let n=0;n<t;n++){const t=i(e[n]);if(null==t)return null;r[n]=t}return r}function i(e){if("number"==typeof e)return Number.isFinite(e)?e:null;if("string"==typeof e){const t=parseFloat(e);return Number.isFinite(t)?t:null}return null}function a(e){return Array.isArray(e)&&"string"==typeof e[3]?e[3]:void 0}function o(e,r){if(r instanceof t||"string"==typeof r||"number"==typeof r)e.set(r);else{if(!(Array.isArray(r)&&r.length>=3))throw new Error("Unsupported color value");e.setRGB(Number(r[0])||0,Number(r[1])||0,Number(r[2])||0)}}const c=new t,p=new t;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { BaseActor } from '../../gameplay/actors/actor.js';
|
|
2
2
|
import { Asset } from '../../scene/model.js';
|
|
3
3
|
import type { VfxClipEndBehavior } from '../sequence/sequence-data.js';
|
|
4
|
+
import { Subject } from 'rxjs';
|
|
4
5
|
export declare class VfxActor extends BaseActor {
|
|
6
|
+
readonly onComplete: Subject<void>;
|
|
5
7
|
/**
|
|
6
8
|
* A multiplier for the speed of the visual effect.
|
|
7
9
|
*/
|
|
@@ -20,6 +22,8 @@ export declare class VfxActor extends BaseActor {
|
|
|
20
22
|
private shaderProvider;
|
|
21
23
|
private inputRuntime;
|
|
22
24
|
private inputOverrides;
|
|
25
|
+
private finishRequested;
|
|
26
|
+
private completionEmitted;
|
|
23
27
|
/**
|
|
24
28
|
* Replace visual effect with its definition in an asset
|
|
25
29
|
*/
|
|
@@ -51,6 +55,7 @@ export declare class VfxActor extends BaseActor {
|
|
|
51
55
|
*/
|
|
52
56
|
getParticleCount(): number;
|
|
53
57
|
onEndPlay(): void;
|
|
58
|
+
private updateCompletionState;
|
|
54
59
|
private updateSystem;
|
|
55
60
|
private applyEmitterClipEndBehavior;
|
|
56
61
|
private stopEmitterEmission;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t}from"tslib";import*as s from"three";import{Rate as
|
|
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 o}from"../../gameplay/actors/actor.js";import{inject as a}from"../../gameplay/inject.js";import{AssetLoader as n}from"../../gameplay/services/asset-loader.js";import{ViewController as m}from"../../gameplay/services/render.js";import{World as p}from"../../gameplay/services/world.js";import{DelayRate as h}from"./rates.js";import{materializeVfx as l}from"./vfx-materializer.js";import{PhysicsSystem as d}from"../../gameplay/services/physics/physics-system.js";import{ShaderProvider as c}from"../../gameplay/services/shader-provider.js";import{AssetResourceLoader as u}from"../../scene/asset-resource-loader.js";import{AssetsProvider as y}from"../../scene/assets-provider.js";import{VfxInputRuntime as f}from"./vfx-input-runtime.js";import{Subject as v}from"rxjs";import{EventGraphCallable as g}from"../../gameplay/event-graph/decorators.js";import{EVENT_GRAPH_VOID_TYPE as E}from"../../gameplay/event-graph/types.js";import{VFX_ACTOR_TYPE_ID as j}from"../../gameplay/event-graph/asset-actor-types.js";let w=class extends o{constructor(){super(...arguments),this.onComplete=new v,this.timescale=1,this.paused=!0,this.assetLoader=a(n),this.assetManagerService=a(u),this.assetService=a(y),this.world=a(p),this.view=a(m),this.physics=a(d),this.shaderProvider=a(c),this.inputRuntime=new f,this.inputOverrides=new Map,this.finishRequested=!1,this.completionEmitted=!1,this._worldPos=new s.Vector3,this._worldRot=new s.Quaternion,this._worldEul=new s.Euler,this.max=0}async fromAsset(t,e){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.sourceAsset=t,this.finishRequested=!1,this.completionEmitted=!1,this.disposeSystem&&this.disposeSystem(),this.world.scene.add(this.object),this.inputRuntime.setDefinitions(this.sourceAsset.vfx.inputs??[]);for(const[t,e]of this.inputOverrides)this.inputRuntime.setInput(t,e);null!=e&&this.setInputs(e);const{system:s,dispose:i,container:r}=await l(this.sourceAsset,this.object,{getAsset:t=>this.assetLoader.getAsset(t),getMaterial:t=>this.assetLoader.getMaterialByAssetId(t),getTexture:t=>this.assetLoader.getTextureByAssetId(t),getMesh:t=>this.assetLoader.getModelByAssetId(t).then(t=>t.scene)},this.view,this.physics,this.shaderProvider,this.assetService,this.assetManagerService,this.inputRuntime);this.system=s,this.disposeSystem=i,this.particleSystemContainer=r,this.object.visible=!1,this.object.matrixAutoUpdate=!1,this.object.matrixWorldAutoUpdate=!1,this.particleSystemContainer.matrixAutoUpdate=!1,this.particleSystemContainer.matrixWorldAutoUpdate=!1}setInput(t,e){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)}setInputs(t){for(const[e,s]of Object.entries(t))this.setInput(e,s)}play(){null==this.particleSystemContainer.parent&&this.world.scene.add(this.particleSystemContainer),this.object.matrixAutoUpdate=!0,this.object.matrixWorldAutoUpdate=!0,this.particleSystemContainer.matrixAutoUpdate=!0,this.particleSystemContainer.matrixWorldAutoUpdate=!0,this.object.visible=!0,this.paused=!1;this.system.emitters.every(t=>t.dead)&&this.restart()}pause(){this.paused=!0}stop(){this.finishRequested=!0,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.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&&null!=this.system.emitters){this.finishRequested=!1,this.completionEmitted=!1;for(const t of this.system.emitters)this.restartEmitter(t)}}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||(this.object.getWorldPosition(this._worldPos),this.object.getWorldQuaternion(this._worldRot),this._worldEul.setFromQuaternion(this._worldRot),this.system?.emitters.forEach(t=>{"world"===t._space&&(t.setPosition(this._worldPos),t.setRotation(this._worldEul))}),this.updateSystem(t*this.timescale),this.updateCompletionState())}getParticleCount(){return this.system?.getCount()??0}onEndPlay(){this.stop(),null!=this.disposeSystem&&this.disposeSystem(),this.onComplete.complete()}updateCompletionState(){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){this.system?.update(t)}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 o=t.childEmitters??[];for(const t of o)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([g({parameters:[],returns:E}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],w.prototype,"play",null),t([g({parameters:[],returns:E}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],w.prototype,"pause",null),t([g({parameters:[],returns:E}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],w.prototype,"stop",null),t([g({parameters:[],returns:E}),e("design:type",Function),e("design:paramtypes",[]),e("design:returntype",void 0)],w.prototype,"restart",null),w=t([r({typeId:j})],w);export{w as VfxActor};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -73,6 +73,7 @@ export type OutputTextureSetting = {
|
|
|
73
73
|
};
|
|
74
74
|
export type CommonOutput = {
|
|
75
75
|
space: 'world' | 'local';
|
|
76
|
+
renderOrder?: number;
|
|
76
77
|
};
|
|
77
78
|
export type SpriteOutput = {
|
|
78
79
|
type: 'sprite';
|
|
@@ -94,7 +95,6 @@ export type DecalOutput = {
|
|
|
94
95
|
intensity: number;
|
|
95
96
|
blendingMode: BlendingMode;
|
|
96
97
|
bloom: boolean;
|
|
97
|
-
renderOrder?: number;
|
|
98
98
|
} & CommonOutput & OutputTextureSetting & OutputMaterialSourceSettings;
|
|
99
99
|
export type StretchedSpriteOutput = {
|
|
100
100
|
type: 'stretchedSprite';
|
|
@@ -201,6 +201,7 @@ export declare function EmitterDataConstructor(): {
|
|
|
201
201
|
color: string;
|
|
202
202
|
blendingMode: "normal";
|
|
203
203
|
space: "world";
|
|
204
|
+
renderOrder: number;
|
|
204
205
|
softness: number;
|
|
205
206
|
intensity: number;
|
|
206
207
|
bloom: false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{SerializedParamType as e}from"../../scene/model.js";import*as t from"three";import{randomString as n}from"../../utils/math.js";export const BlendingModes=["additive","subtractive","multiply","normal"];export const ThreeBlendingMode={normal:t.NormalBlending,additive:t.AdditiveBlending,subtractive:t.SubtractiveBlending,multiply:t.MultiplyBlending};export const FlipbookModes=["clamp","loop"];export const VfxEffectInputTypes=["number","vec3","color"];export const ParticleChildSpawnEvents=["collision","start"];export function defaultVfxEffectInputValue(e){switch(e){case"number":return 0;case"vec3":return[0,0,0];case"color":return"#ffffff"}}export function createVfxEffectInputDefinition(e="input",t="number"){return{id:n(),name:e,type:t,defaultValue:defaultVfxEffectInputValue(t)}}export function EmitterDataConstructor(){return{id:n(),name:"Emitter",rate:{type:"continuous",count:1,time:.5,delay:0},position:[0,0,0],rotation:[0,0,0],initializers:[{id:n(),enabled:!0,type:"lifetime",params:{duration:{type:e.Number,value:1}}}],behaviours:[],children:[],output:{type:"sprite",texture:null,opacityChannel:"alpha",color:"#ffffff",blendingMode:"normal",space:"world",softness:0,intensity:1,bloom:!1}}}/*
|
|
1
|
+
import{SerializedParamType as e}from"../../scene/model.js";import*as t from"three";import{randomString as n}from"../../utils/math.js";export const BlendingModes=["additive","subtractive","multiply","normal"];export const ThreeBlendingMode={normal:t.NormalBlending,additive:t.AdditiveBlending,subtractive:t.SubtractiveBlending,multiply:t.MultiplyBlending};export const FlipbookModes=["clamp","loop"];export const VfxEffectInputTypes=["number","vec3","color"];export const ParticleChildSpawnEvents=["collision","start"];export function defaultVfxEffectInputValue(e){switch(e){case"number":return 0;case"vec3":return[0,0,0];case"color":return"#ffffff"}}export function createVfxEffectInputDefinition(e="input",t="number"){return{id:n(),name:e,type:t,defaultValue:defaultVfxEffectInputValue(t)}}export function EmitterDataConstructor(){return{id:n(),name:"Emitter",rate:{type:"continuous",count:1,time:.5,delay:0},position:[0,0,0],rotation:[0,0,0],initializers:[{id:n(),enabled:!0,type:"lifetime",params:{duration:{type:e.Number,value:1}}}],behaviours:[],children:[],output:{type:"sprite",texture:null,opacityChannel:"alpha",color:"#ffffff",blendingMode:"normal",space:"world",renderOrder:0,softness:0,intensity:1,bloom:!1}}}/*
|
|
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 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 c,RgbNode as p,UniformFloatNode as u,UniformVec3Node as h,Vec3ExpressionNode as d,Vec4Node as m,attributeFloat as f,attributeVec3 as y,attributeVec4 as g,attributes as w,clamp as b,float as v,glslFunction as E,lambertMaterial as A,log as M,pow as x,rgb as P,rgba as S,smoothstep as I,standardMaterial as T,textureSampler2d as C,uniformFloat as R,uniforms as B,varying as k,varyingAttributes as j,varyingVec3 as D,varyingVec4 as G}from"three-shader-graph";import{prepareClassParameters as V,prepareShaderGraphParameters as F,prepareShapeParameters as z}from"../../scene/materializer.js";import{SerializedParamType as L}from"../../scene/model.js";import{ShapeLibrary as O}from"../../scene/objects/shapes.js";import{fragmentLinearEyeDepth as W,linearEyeDepth as Q}from"../../shader-nodes/depth.js";import{particleEnergyUniformName as H,particleTimeUniformName as U,particleUniforms as q,particleVelcoityUniformName as J}from"../../shader-nodes/particle.js";import{sampleFlipbook as N}from"../../shader-nodes/texture-sequence.js";import{DefaultInitializer as X}from"./initializsers.js";import{DelayRate as _,OnceRate as K}from"./rates.js";import{StretchedSprite as Y}from"./stretched-sprite.js";import{ThreeBlendingMode as Z}from"./vfx-asset.js";import{VfxBehaviourLibrary as $,VfxInitializserLibrary as ee}from"./vfx-defs.js";import{MultiRenderer as te}from"./vfx-renderers.js";import{WorldCollisionBehaviour as ae}from"./vfx-collision-behaviour.js";import{getSpritePosition as re,SpriteNodeShaderMaterial as se}from"../../shader/sprite-shader.js";import{DecalUnlitShader as ie}from"../../shader/builtin/decal-unlit-shader.js";import{DecalStandardShader as ne}from"../../shader/builtin/decal-standard-shader.js";import{createBoundBehaviour as oe,createBoundInitializer as le}from"./vfx-binding-runtime.js";import{buildShaderGraphMaterial as ce}from"../../shader/graph/index.js";export async function materializeVfx(t,a,r,s,o,l,c,p,u){let h=a;for(;null!=h.parent;)h=h.parent;const d=new n;d.name="particle system local",a.add(d);const m=new n;m.name="particle system world";const f=new te(m,d,i,s),y=new e;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 pe(e,r,c,p,y,o,l,u,a);return t.setParentRecursive(y),t.emit()}))).forEach(e=>y.addEmitter(e)),y.addRenderer(f).emit({onEnd:()=>{}}),{container:m,system:y,dispose:()=>{m.removeFromParent(),d.removeFromParent(),f.dispose()}}}async function pe(e,t,r,p,v,x,P,T,D){const V=function(e){let t;switch(e.rate.type){case"continuous":t=new _(e.rate.delay??0,e.rate.count,e.rate.time,e.rate.duration);break;case"once":t=new K(e.rate.delay??0,e.rate.count);break;default:console.warn(`Failed to configure rate for emitter: ${JSON.stringify(e)}`),t=new s(0,1/0)}return t}(e);let F;switch(e.output.type){case"decal":F=new a(await async function(e,t,a,r,s){if("shaderGraph"===me(e)){const t=await de(e,"decal",a,r,s);if(null!=t){const a=new i.Mesh(new i.BoxGeometry(1,1,1),t);return null!=e.renderOrder&&(a.renderOrder=e.renderOrder),a}}const n=!1!==e.unlit?new ie:new ne;n.color=new i.Color(e.color),n instanceof ie?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=Z[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),c=new i.Mesh(l,o);null!=e.renderOrder&&(c.renderOrder=e.renderOrder);return c}(e.output,t,r,p,P));break;case"sprite":F=new a(await async function(e,t,a,r,s){let n=null;const o=me(e);"shaderGraph"===o&&(n=await de(e,"sprite",a,r,s));null==n&&"shader"===o&&null!=e.shader&&(n=await he(e,t,s));null==n&&(n=await async function(e,t){const a=null!=e.texture?await t.getTexture(e.texture):ue,r=C(a);let s=r.sample(j.uv);e.flipbook?.enabled&&(s=N(r,w.uv,e.flipbook.columns,e.flipbook.rows,q.time,e.flipbook.fps,e.flipbook.mode));const n=new u("rotation",0),o=new h("color").rgb,l=R("opacity",1),c=Q.subtract(W).divide(Q);let p=l;switch(e.opacityChannel??"red"){case"none":break;case"red":p=p.multiply(s.r);break;case"alpha":p=p.multiply(s.a)}if("number"==typeof e.softness&&e.softness>0){const t=b(c,0,1e3);p=p.multiply(I(0,.2*e.softness,t))}const d=re(n),m=S(o.multiply(s.rgb).multiplyScalar(e.intensity??1),p);var f=new se({color:m,emissive:m.rgb.multiplyScalar(p),transparent:!0,position:d,alphaTest:1e-4,uniforms:{color:{value:new i.Color(e.color)}}});f.alphaHash=!0,null!=f&&!0===e.bloom&&(f.userData.hasBloom=!0);return f.blending=Z[e.blendingMode]??i.NormalBlending,f}(e,t));fe(n,e);const l=new i.Mesh(new i.PlaneGeometry(1,1),n);return l.name="sprite",l}(e.output,t,r,p,P));break;case"stretchedSprite":F=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):ue,r=C(a).sample(j.uv),s=G(new l("color")),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=Q.subtract(W).divide(M(Q)),a=b(t,0,1e3);o=o.multiply(I(0,.2*e.softness,a))}const c=E(m,{position:w.position,offset:y("offset"),modelViewMatrix:B.modelViewMatrix,velocity:g("velocity"),size:y("size"),rotation:f("rotation")},"\n float lengthFactor = velocity.w;\n float avgSize = (size.x + size.y) * 0.5;\n\n vec4 mvPosition = modelViewMatrix * vec4( offset , 1.0 );\n vec3 viewVelocity = normalMatrix * velocity.xyz;\n float vlength = length(viewVelocity); \n mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * avgSize; \n mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * avgSize;\n return projectionMatrix * mvPosition;\n ");var p=new se({color:S(n.multiplyScalar(e.intensity??1),o),alphaTest:.1,transparent:!0,position:c,uniforms:{color:{value:new i.Color(e.color)}}});null!=p&&!0===e.bloom&&(p.userData.hasBloom=!0);p.blending=Z[e.blendingMode]??i.NormalBlending;const u=new Y(new i.PlaneGeometry(1,1),p);return u.scaleFactor=e.scale,u}(e.output,t));break;case"shape":F=new a(await async function(e,t,a,r,s){if(null==e.shape)return console.log("Shape is null"),new n;const l=O[e.shape];if(null==l)return console.error(`No shape with type ${e.shape}`),new n;const p=z(e.params??{}),u=l.geometry(p);let h=null;const m=me(e);"shaderGraph"===m&&(h=await de(e,"surface",a,r,s));null==h&&"shader"===m&&null!=e.shader&&(h=await he(e,t,s));null==h&&(h="material"===m&&null!=e.material?ye(await t.getMaterial(e.material)):function(){const e=k(new d("instanceColor")).rgb,t=k(new o("particleData")).x;return new c({color:A({color:e}).rgb.rgba(t),opacity:t,transparent:!0})}());return fe(h,e),new i.Mesh(u,h)}(e.output,t,r,p,P));break;case"mesh":F=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),c=me(e);if("shaderGraph"===c||"shader"===c||"material"===c){let n;"shaderGraph"===c?n=await de(e,"surface",a,r,s):"shader"===c&&null!=e.shader?n=await he(e,t,s):"material"===c&&null!=e.material&&(n=ye(await t.getMaterial(e.material))),fe(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)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 p=[];if(o.traverse(e=>{e instanceof i.Mesh&&p.push(e)}),1===p.length){const e=p[0];return e.updateWorldMatrix(!0,!0),e.updateMatrixWorld(),e.matrix.copy(e.matrixWorld),e.matrixWorld.decompose(e.position,e.quaternion,e.scale),e.removeFromParent(),e}return o}(e.output,t,r,p,P));break;case"trail":{const s="shaderGraph"===me(e.output)?await de(e.output,"trail",r,p,P,{trailBillboard:e.output.billboard??!1}):null;F=new a({type:"trail",taper:e.output.taper,headGeometry:null,material:s instanceof i.ShaderMaterial?s:void 0,dragTexture:!1,texture:null!=e.output.texture?await t.getTexture(e.output.texture):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,width:e.output.width,billboard:e.output.billboard??!1});break}default:console.error("Failed to create particly system body: "+JSON.stringify(e))}const H=new we(D);H.parent=v,H.setRate(V),H._space=e.output.space;const U=await Promise.all(e.initializers.filter(e=>!1!==e.enabled).filter(e=>null!=ee[e.type]).map(async e=>{const t=ee[e.type],a=await z(e.params??{});return le(t,e.params??{},a,T)}));U.push(F,new X),H.addInitializers(U);const J=await Promise.all(e.behaviours.filter(e=>!1!==e.enabled).filter(e=>null!=$[e.type]).map(async e=>{const t=$[e.type];for(const[a,r]of Object.entries(e.params))t.parameters&&null!=t.parameters[a]&&"curve"===t.parameters[a].type&&r.type!==L.Curve&&(r.type=L.Curve);const a=await z(e.params??{},r,p);return oe(t,e.params??{},a,T)}).sort((e,t)=>e instanceof ae?1:0));J.push(new ge);for(const e of J)e instanceof ae&&(e.physics=x);H.addBehaviours(J);for(const a of e.children??[]){const e=await pe(a,t,r,p,v,x,P,T,D),s=new EmitterPool(()=>{const t=e.clone();return t.onExpired=()=>{const e=H.childEmitters.findIndex(e=>e.id===t.id);-1!=e&&H.childEmitters.splice(e,1),null!=t.parentParticle&&(n.delete(t.parentParticle.id),t.parentParticle=null),s.release(t)},t}),i=H.eventDispatcher,n=new Map;H.bindEmitterEvent=!0,i.addEventListener("PARTICLE_DEAD",e=>{const t=n.get(e.id);null!=t&&(H.detachChildEmitterFromParticle(t,e),n.delete(e.id))});let o="PARTICLE_CREATED";if("spawnEvent"in a)switch(a.spawnEvent){case"collision":o="PARTICLE_COLLISION";break;case"start":o="PARTICLE_CREATED"}i.addEventListener(o,e=>{const t=s.get();t.age=0,t.totalEmitTimes=-1,t.particles.length=0,t.currentEmitTime=0,t.cID=0,t.eventDispatcher.removeAllEventListeners(),H.childEmitters.push(t),n.set(e.id,t),t.parentParticle=e,t.system=H.system,t.emit()})}return H}const ue=(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=");async function he(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,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);return Object.assign(s,n),s.build()}async function de(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;try{const t=await F(e.shaderGraphParams??{},l,a,r,void 0,s.shaders),o=ce(l,{parameters:t,trailBillboard:i.trailBillboard});return o.userData.customShaderName=`shaderGraph:${n.assetId}`,fe(o,e),o}catch(e){return console.log("Shader graph VFX runtime error: "+e,e),null}}function me(e){return null!=e.materialSource?e.materialSource:"asset"===e.shaderGraph?.source?"shaderGraph":"shader"in e&&null!=e.shader?"shader":"mesh"!==e.type&&"shape"!==e.type||null==e.material?"default":"material"}function fe(e,t){null!=e&&("bloom"in t&&!0===t.bloom&&(e.userData.hasBloom=!0),"sprite"===t.type?(e.blending=Z[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=Z[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 ye(e){const t=k(new d("instanceColor")).rgb;let a,r=k(new o("particleData")).x;if(e instanceof i.MeshStandardMaterial||e instanceof i.MeshLambertMaterial||e instanceof i.MeshBasicMaterial){let s=t.multiply(P(e.color));null!=e.map&&(s=s.multiply(C(e.map).sample(j.uv).rgb)),null!=e.alphaMap&&(r=r.multiply(C(e.alphaMap).sample(j.uv).r)),e instanceof i.MeshStandardMaterial?a=new c({color:T({color:s,emissive:P(e.emissive),emissiveIntensity:v(e.emissiveIntensity),roughness:e.roughness,metalness:e.metalness}).rgb.rgba(r),emissive:P(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshLambertMaterial?a=new c({color:A({color:s}).rgb.rgba(r),emissive:P(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshBasicMaterial&&(a=new c({color:s.rgb.rgba(r),emissive:s.rgb,transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}))}else e instanceof c&&(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 ge extends t{initialize(e){e.body instanceof i.Object3D&&e.body.traverse(e=>{if(e instanceof i.Mesh){const t=e.material;t instanceof c&&(null!=t.uniforms[H]||null!=t.uniforms[U]||t.uniforms[J])}})}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;a instanceof c&&(null!=a.uniforms[H]&&(a.uniforms[H].value=this.energy),null!=a.uniforms[U]&&(a.uniforms[U].value=e.age),null!=a.uniforms[J]&&(a.uniforms[J].value=e.velocity))}})}}class we 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?(be.copy(t),"local"===a&&"world"===r?be.applyMatrix4(this.transformRoot.matrixWorld):"world"===a&&"local"===r&&(ve.copy(this.transformRoot.matrixWorld).invert(),be.applyMatrix4(ve)),e.position.copy(be)):e.position.copy(t)}clone(){const e=new we(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 be=new i.Vector3,ve=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 c,RgbNode as p,UniformFloatNode as u,UniformVec3Node as h,Vec3ExpressionNode as d,Vec4Node as m,attributeFloat as f,attributeVec3 as y,attributeVec4 as g,attributes as w,clamp as b,float as v,glslFunction as E,lambertMaterial as A,log as M,pow as x,rgb as P,rgba as S,smoothstep as I,standardMaterial as T,textureSampler2d as C,uniformFloat as R,uniforms as B,varying as k,varyingAttributes as j,varyingVec3 as D,varyingVec4 as G}from"three-shader-graph";import{prepareClassParameters as V,prepareShaderGraphParameters as F,prepareShapeParameters as z}from"../../scene/materializer.js";import{SerializedParamType as L}from"../../scene/model.js";import{ShapeLibrary as O}from"../../scene/objects/shapes.js";import{fragmentLinearEyeDepth as W,linearEyeDepth as Q}from"../../shader-nodes/depth.js";import{particleEnergyUniformName as H,particleTimeUniformName as U,particleUniforms as q,particleVelcoityUniformName as J}from"../../shader-nodes/particle.js";import{sampleFlipbook as N}from"../../shader-nodes/texture-sequence.js";import{DefaultInitializer as X}from"./initializsers.js";import{DelayRate as _,OnceRate as K}from"./rates.js";import{StretchedSprite as Y}from"./stretched-sprite.js";import{ThreeBlendingMode as Z}from"./vfx-asset.js";import{VfxBehaviourLibrary as $,VfxInitializserLibrary as ee}from"./vfx-defs.js";import{MultiRenderer as te}from"./vfx-renderers.js";import{WorldCollisionBehaviour as ae}from"./vfx-collision-behaviour.js";import{getSpritePosition as re,SpriteNodeShaderMaterial as se}from"../../shader/sprite-shader.js";import{DecalUnlitShader as ie}from"../../shader/builtin/decal-unlit-shader.js";import{DecalStandardShader as ne}from"../../shader/builtin/decal-standard-shader.js";import{createBoundBehaviour as oe,createBoundInitializer as le}from"./vfx-binding-runtime.js";import{buildShaderGraphMaterial as ce}from"../../shader/graph/index.js";export async function materializeVfx(t,a,r,s,o,l,c,p,u){let h=a;for(;null!=h.parent;)h=h.parent;const d=new n;d.name="particle system local",a.add(d);const m=new n;m.name="particle system world";const f=new te(m,d,i,s),y=new e;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 pe(e,r,c,p,y,o,l,u,a);return t.setParentRecursive(y),t.emit()}))).forEach(e=>y.addEmitter(e)),y.addRenderer(f).emit({onEnd:()=>{}}),{container:m,system:y,dispose:()=>{m.removeFromParent(),d.removeFromParent(),f.dispose()}}}async function pe(e,t,r,p,v,x,P,T,D){const V=function(e){let t;switch(e.rate.type){case"continuous":t=new _(e.rate.delay??0,e.rate.count,e.rate.time,e.rate.duration);break;case"once":t=new K(e.rate.delay??0,e.rate.count);break;default:console.warn(`Failed to configure rate for emitter: ${JSON.stringify(e)}`),t=new s(0,1/0)}return t}(e);let F;switch(e.output.type){case"decal":F=new a(await async function(e,t,a,r,s){if("shaderGraph"===me(e)){const t=await de(e,"decal",a,r,s);if(null!=t){const a=new i.Mesh(new i.BoxGeometry(1,1,1),t);return ye(a,e),a}}const n=!1!==e.unlit?new ie:new ne;n.color=new i.Color(e.color),n instanceof ie?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=Z[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),c=new i.Mesh(l,o);return ye(c,e),c}(e.output,t,r,p,P));break;case"sprite":F=new a(await async function(e,t,a,r,s){let n=null;const o=me(e);"shaderGraph"===o&&(n=await de(e,"sprite",a,r,s));null==n&&"shader"===o&&null!=e.shader&&(n=await he(e,t,s));null==n&&(n=await async function(e,t){const a=null!=e.texture?await t.getTexture(e.texture):ue,r=C(a);let s=r.sample(j.uv);e.flipbook?.enabled&&(s=N(r,w.uv,e.flipbook.columns,e.flipbook.rows,q.time,e.flipbook.fps,e.flipbook.mode));const n=new u("rotation",0),o=new h("color").rgb,l=R("opacity",1),c=Q.subtract(W).divide(Q);let p=l;switch(e.opacityChannel??"red"){case"none":break;case"red":p=p.multiply(s.r);break;case"alpha":p=p.multiply(s.a)}if("number"==typeof e.softness&&e.softness>0){const t=b(c,0,1e3);p=p.multiply(I(0,.2*e.softness,t))}const d=re(n),m=S(o.multiply(s.rgb).multiplyScalar(e.intensity??1),p);var f=new se({color:m,emissive:m.rgb.multiplyScalar(p),transparent:!0,position:d,alphaTest:1e-4,uniforms:{color:{value:new i.Color(e.color)}}});f.alphaHash=!0,null!=f&&!0===e.bloom&&(f.userData.hasBloom=!0);return f.blending=Z[e.blendingMode]??i.NormalBlending,f}(e,t));fe(n,e);const l=new i.Mesh(new i.PlaneGeometry(1,1),n);return l.name="sprite",ye(l,e),l}(e.output,t,r,p,P));break;case"stretchedSprite":F=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):ue,r=C(a).sample(j.uv),s=G(new l("color")),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=Q.subtract(W).divide(M(Q)),a=b(t,0,1e3);o=o.multiply(I(0,.2*e.softness,a))}const c=E(m,{position:w.position,offset:y("offset"),modelViewMatrix:B.modelViewMatrix,velocity:g("velocity"),size:y("size"),rotation:f("rotation")},"\n float lengthFactor = velocity.w;\n float avgSize = (size.x + size.y) * 0.5;\n\n vec4 mvPosition = modelViewMatrix * vec4( offset , 1.0 );\n vec3 viewVelocity = normalMatrix * velocity.xyz;\n float vlength = length(viewVelocity); \n mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * avgSize; \n mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * avgSize;\n return projectionMatrix * mvPosition;\n ");var p=new se({color:S(n.multiplyScalar(e.intensity??1),o),alphaTest:.1,transparent:!0,position:c,uniforms:{color:{value:new i.Color(e.color)}}});null!=p&&!0===e.bloom&&(p.userData.hasBloom=!0);p.blending=Z[e.blendingMode]??i.NormalBlending;const u=new Y(new i.PlaneGeometry(1,1),p);return u.scaleFactor=e.scale,ye(u,e),u}(e.output,t));break;case"shape":F=new a(await async function(e,t,a,r,s){if(null==e.shape)return console.log("Shape is null"),new n;const l=O[e.shape];if(null==l)return console.error(`No shape with type ${e.shape}`),new n;const p=z(e.params??{}),u=l.geometry(p);let h=null;const m=me(e);"shaderGraph"===m&&(h=await de(e,"surface",a,r,s));null==h&&"shader"===m&&null!=e.shader&&(h=await he(e,t,s));null==h&&(h="material"===m&&null!=e.material?ge(await t.getMaterial(e.material)):function(){const e=k(new d("instanceColor")).rgb,t=k(new o("particleData")).x;return new c({color:A({color:e}).rgb.rgba(t),opacity:t,transparent:!0})}());fe(h,e);const f=new i.Mesh(u,h);return ye(f,e),f}(e.output,t,r,p,P));break;case"mesh":F=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),c=me(e);if("shaderGraph"===c||"shader"===c||"material"===c){let n;"shaderGraph"===c?n=await de(e,"surface",a,r,s):"shader"===c&&null!=e.shader?n=await he(e,t,s):"material"===c&&null!=e.material&&(n=ge(await t.getMaterial(e.material))),fe(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)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 p=[];if(o.traverse(e=>{e instanceof i.Mesh&&p.push(e)}),ye(o,e),1===p.length){const e=p[0];return e.updateWorldMatrix(!0,!0),e.updateMatrixWorld(),e.matrix.copy(e.matrixWorld),e.matrixWorld.decompose(e.position,e.quaternion,e.scale),e.removeFromParent(),e}return o}(e.output,t,r,p,P));break;case"trail":{const s="shaderGraph"===me(e.output)?await de(e.output,"trail",r,p,P,{trailBillboard:e.output.billboard??!1}):null;F=new a({type:"trail",taper:e.output.taper,headGeometry:null,material:s instanceof i.ShaderMaterial?s:void 0,dragTexture:!1,texture:null!=e.output.texture?await t.getTexture(e.output.texture):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,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 H=new be(D);H.parent=v,H.setRate(V),H._space=e.output.space;const U=await Promise.all(e.initializers.filter(e=>!1!==e.enabled).filter(e=>null!=ee[e.type]).map(async e=>{const t=ee[e.type],a=await z(e.params??{});return le(t,e.params??{},a,T)}));U.push(F,new X),H.addInitializers(U);const J=await Promise.all(e.behaviours.filter(e=>!1!==e.enabled).filter(e=>null!=$[e.type]).map(async e=>{const t=$[e.type];for(const[a,r]of Object.entries(e.params))t.parameters&&null!=t.parameters[a]&&"curve"===t.parameters[a].type&&r.type!==L.Curve&&(r.type=L.Curve);const a=await z(e.params??{},r,p);return oe(t,e.params??{},a,T)}).sort((e,t)=>e instanceof ae?1:0));J.push(new we);for(const e of J)e instanceof ae&&(e.physics=x);H.addBehaviours(J);for(const a of e.children??[]){const e=await pe(a,t,r,p,v,x,P,T,D),s=new EmitterPool(()=>{const t=e.clone();return t.onExpired=()=>{const e=H.childEmitters.findIndex(e=>e.id===t.id);-1!=e&&H.childEmitters.splice(e,1),null!=t.parentParticle&&(n.delete(t.parentParticle.id),t.parentParticle=null),s.release(t)},t}),i=H.eventDispatcher,n=new Map;H.bindEmitterEvent=!0,i.addEventListener("PARTICLE_DEAD",e=>{const t=n.get(e.id);null!=t&&(H.detachChildEmitterFromParticle(t,e),n.delete(e.id))});let o="PARTICLE_CREATED";if("spawnEvent"in a)switch(a.spawnEvent){case"collision":o="PARTICLE_COLLISION";break;case"start":o="PARTICLE_CREATED"}i.addEventListener(o,e=>{const t=s.get();t.age=0,t.totalEmitTimes=-1,t.particles.length=0,t.currentEmitTime=0,t.cID=0,t.eventDispatcher.removeAllEventListeners(),H.childEmitters.push(t),n.set(e.id,t),t.parentParticle=e,t.system=H.system,t.emit()})}return H}const ue=(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=");async function he(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,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);return Object.assign(s,n),s.build()}async function de(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;try{const t=await F(e.shaderGraphParams??{},l,a,r,void 0,s.shaders),o=ce(l,{parameters:t,trailBillboard:i.trailBillboard});return o.userData.customShaderName=`shaderGraph:${n.assetId}`,fe(o,e),o}catch(e){return console.log("Shader graph VFX runtime error: "+e,e),null}}function me(e){return null!=e.materialSource?e.materialSource:"asset"===e.shaderGraph?.source?"shaderGraph":"shader"in e&&null!=e.shader?"shader":"mesh"!==e.type&&"shape"!==e.type||null==e.material?"default":"material"}function fe(e,t){null!=e&&("bloom"in t&&!0===t.bloom&&(e.userData.hasBloom=!0),"sprite"===t.type?(e.blending=Z[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=Z[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 ye(e,t){const a=t.renderOrder;null!=a&&e.traverse(e=>e.renderOrder=a)}function ge(e){const t=k(new d("instanceColor")).rgb;let a,r=k(new o("particleData")).x;if(e instanceof i.MeshStandardMaterial||e instanceof i.MeshLambertMaterial||e instanceof i.MeshBasicMaterial){let s=t.multiply(P(e.color));null!=e.map&&(s=s.multiply(C(e.map).sample(j.uv).rgb)),null!=e.alphaMap&&(r=r.multiply(C(e.alphaMap).sample(j.uv).r)),e instanceof i.MeshStandardMaterial?a=new c({color:T({color:s,emissive:P(e.emissive),emissiveIntensity:v(e.emissiveIntensity),roughness:e.roughness,metalness:e.metalness}).rgb.rgba(r),emissive:P(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshLambertMaterial?a=new c({color:A({color:s}).rgb.rgba(r),emissive:P(e.emissive).multiplyScalar(e.emissiveIntensity),transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}):e instanceof i.MeshBasicMaterial&&(a=new c({color:s.rgb.rgba(r),emissive:s.rgb,transparent:e.transparent,opacity:r,alphaTest:e.alphaTest}))}else e instanceof c&&(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 we extends t{initialize(e){e.body instanceof i.Object3D&&e.body.traverse(e=>{if(e instanceof i.Mesh){const t=e.material;t instanceof c&&(null!=t.uniforms[H]||null!=t.uniforms[U]||t.uniforms[J])}})}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;a instanceof c&&(null!=a.uniforms[H]&&(a.uniforms[H].value=this.energy),null!=a.uniforms[U]&&(a.uniforms[U].value=e.age),null!=a.uniforms[J]&&(a.uniforms[J].value=e.velocity))}})}}class be 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?(ve.copy(t),"local"===a&&"world"===r?ve.applyMatrix4(this.transformRoot.matrixWorld):"world"===a&&"local"===r&&(Ee.copy(this.transformRoot.matrixWorld).invert(),ve.applyMatrix4(Ee)),e.position.copy(ve)):e.position.copy(t)}clone(){const e=new be(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 ve=new i.Vector3,Ee=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{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";import{particleEnergyUniformName as d}from"../../shader-nodes/particle";import{AssetMeshInstance as m}from"../../scene/asset-resource-loader.js";var u;!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"}(u||(u={}));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[u.trail]&&(o[u.trail]=new TrailRenderer(r,this.three)),e._renderer=o[u.trail]):e.body instanceof s?(null==o[u.stretchedBillboard]&&(o[u.stretchedBillboard]=new StretchedSpriteInstancedRenderer(r,this.three,this.view)),e._renderer=o[u.stretchedBillboard]):e.body instanceof a.Sprite||e.body instanceof a.Mesh&&"sprite"===e.body.name?(null==o[u.sprite]&&(o[u.sprite]=new CombinedRenderer(r,this.three)),e._renderer=o[u.sprite]):e.body instanceof a.Mesh&&0==e.body.children.length&&e.body.material instanceof n?(null==o[u.instanced]&&(o[u.instanced]=new InstancedRenderer(r,this.three,this.view)),e._renderer=o[u.instanced]):e.body instanceof a.Object3D?(null==o[u.mesh]&&(o[u.mesh]=new CombinedRenderer(r,this.three)),e._renderer=o[u.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 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.taper&&(n.uniforms.taper.value=t.taper??!1);const s=new a.Color(t.color??0).multiplyScalar(t.intensity),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);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=U;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).normalize():a.copy(e.velocity).normalize();const o=A;o.setFromUnitVectors(a,x),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*e.alpha);t.material.uniforms.tailColor.value.setW(r.opacityEnd*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)}}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=p.set(e.velocity.x,e.velocity.y,e.velocity.z).normalize(),a=t.getWorldDirection(h);a.applyAxisAngle(g,Math.PI/-2),f.setFromUnitVectors(a,r),t.quaternion.copy(f)}(r||o)&&this.applyUniforms(t,e)}}applyUniforms(e,t){e instanceof m&&e.traverse(r=>r!==e&&this.applyUniforms(r,t)),e instanceof a.Mesh&&(t.useAlpha&&(e.material instanceof c&&null!=e.material.uniforms.opacity&&(e.material.uniforms.opacity.value=t.alpha),e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.opacity?e.material.uniforms.opacity.value=t.alpha:e.material.opacity=t.alpha,e.material.transparent=!0),t.useColor&&(null!=e.material.color?e.material.color.copy(t.color):e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.color&&(e.material.uniforms.color.value=t.color)))}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("color");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=P.copy(l.color).getRGB(v);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("color",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:[]},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 p=new a.Vector3,h=new a.Vector3,f=new a.Quaternion,g=new a.Vector3(1,0,0);export class InstancedRenderer extends t{constructor(e,t,r){super(e,t),this.view=r,this.meshes=[]}dispose(){this.meshes.forEach(e=>e.mesh.dispose())}onSystemUpdate(){for(const e of this.meshes);}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 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:[]},r.mesh.renderOrder=t.renderOrder,r.mesh.setColorAt(0,P.setScalar(1)),r.mesh.instanceColor.needsUpdate=!0,r.mesh.material.defines.USE_INSTANCING="";const i=new Float32Array(3*o);w.makeScale(0,0,0);for(let e=0;e<o;e++)i[3*e+0]=1,i[3*e+1]=1,r.mesh.setMatrixAt(e,w);r.mesh.instanceMatrix.needsUpdate=!0,r.mesh.geometry.setAttribute("particleData",new a.InstancedBufferAttribute(i,3)),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))),r.indices[o]=performance.now(),e.target=o,r.particles[o]=e,"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);b(e,r.mesh),e.useAlpha&&(r.mesh.material.transparent=!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;n.mesh.frustumCulled=!1,b(e,n.mesh),a&&(n.mesh.setColorAt(e.target,P.copy(e.color)),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}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;r.indices[e.target]=null,e.scale=0,b(e,r.mesh),r.mesh.instanceMatrix.needsUpdate=!0,e.target=null}}}const y=new i;function b(e,t){if(U.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,y.matrix);const a=y.getWorldDirection(C);a.applyAxisAngle(g,Math.PI/-2),A.setFromUnitVectors(a,r)}else M.set(e.rotation.x,e.rotation.y,e.rotation.z),A.setFromEuler(M);const r=e.transform.initialScale;null!=r?C.set(r.x*e.scale,r.y*e.scale,r.z*e.scale):C.set(e.scale,e.scale,e.scale),w.compose(U,A,C),t.setMatrixAt(e.target,w),t.instanceMatrix.needsUpdate=!0}const x=new a.Vector3(0,0,-1),w=new a.Matrix4,A=new a.Quaternion,U=new a.Vector3,C=new a.Vector3,M=new a.Euler,P=new a.Color(0),v={r:0,g:0,b:0};function R(e,t){const r=e.mesh,o=r.count,i=[],n=r.geometry.getAttribute("particleData"),s=new a.Vector3;for(let l=0;l<o;l++){const o=new a.Matrix4,c=new a.Color;r.getMatrixAt(l,o),r.getColorAt(l,c);const d=n.getX(l);s.setFromMatrixPosition(o);const m=s.distanceTo(t.position);i.push({index:l,distance:m,matrix:o,particle:e.particles[l],color:c,pdx:d})}i.sort((e,t)=>t.distance-e.distance);for(let e=0;e<o;e++){const t=i[e].matrix;null==i[e].particle&&t.makeScale(0,0,0),r.setMatrixAt(e,t),r.setColorAt(e,i[e].color),n.setX(e,i[e].pdx)}r.instanceMatrix.needsUpdate=!0}export{R as sortInstancedMeshByDistance};/*
|
|
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";import{particleEnergyUniformName as d}from"../../shader-nodes/particle";import{AssetMeshInstance as m}from"../../scene/asset-resource-loader.js";var u;!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"}(u||(u={}));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[u.trail]&&(o[u.trail]=new TrailRenderer(r,this.three)),e._renderer=o[u.trail]):e.body instanceof s?(null==o[u.stretchedBillboard]&&(o[u.stretchedBillboard]=new StretchedSpriteInstancedRenderer(r,this.three,this.view)),e._renderer=o[u.stretchedBillboard]):e.body instanceof a.Sprite||e.body instanceof a.Mesh&&"sprite"===e.body.name?(null==o[u.sprite]&&(o[u.sprite]=new CombinedRenderer(r,this.three)),e._renderer=o[u.sprite]):e.body instanceof a.Mesh&&0==e.body.children.length&&e.body.material instanceof n?(null==o[u.instanced]&&(o[u.instanced]=new InstancedRenderer(r,this.three,this.view)),e._renderer=o[u.instanced]):e.body instanceof a.Object3D?(null==o[u.mesh]&&(o[u.mesh]=new CombinedRenderer(r,this.three)),e._renderer=o[u.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 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.taper&&(n.uniforms.taper.value=t.taper??!1);const s=new a.Color(t.color??0).multiplyScalar(t.intensity),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=U;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).normalize():a.copy(e.velocity).normalize();const o=A;o.setFromUnitVectors(a,x),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*e.alpha);t.material.uniforms.tailColor.value.setW(r.opacityEnd*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)}}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=p.set(e.velocity.x,e.velocity.y,e.velocity.z).normalize(),a=t.getWorldDirection(h);a.applyAxisAngle(g,Math.PI/-2),f.setFromUnitVectors(a,r),t.quaternion.copy(f)}(r||o)&&this.applyUniforms(t,e)}}applyUniforms(e,t){e instanceof m&&e.traverse(r=>r!==e&&this.applyUniforms(r,t)),e instanceof a.Mesh&&(t.useAlpha&&(e.material instanceof c&&null!=e.material.uniforms.opacity&&(e.material.uniforms.opacity.value=t.alpha),e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.opacity?e.material.uniforms.opacity.value=t.alpha:e.material.opacity=t.alpha,e.material.transparent=!0),t.useColor&&(null!=e.material.color?e.material.color.copy(t.color):e.material instanceof a.ShaderMaterial&&null!=e.material.uniforms.color&&(e.material.uniforms.color.value=t.color)))}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("color");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=P.copy(l.color).getRGB(v);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("color",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 p=new a.Vector3,h=new a.Vector3,f=new a.Quaternion,g=new a.Vector3(1,0,0);export class InstancedRenderer extends t{constructor(e,t,r){super(e,t),this.view=r,this.meshes=[]}dispose(){this.meshes.forEach(e=>e.mesh.dispose())}onSystemUpdate(){for(const e of this.meshes);}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 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:[]},r.mesh.renderOrder=t.renderOrder,r.mesh.setColorAt(0,P.setScalar(1)),r.mesh.instanceColor.needsUpdate=!0,r.mesh.material.defines.USE_INSTANCING="";const i=new Float32Array(3*o);w.makeScale(0,0,0);for(let e=0;e<o;e++)i[3*e+0]=1,i[3*e+1]=1,r.mesh.setMatrixAt(e,w);r.mesh.instanceMatrix.needsUpdate=!0,r.mesh.geometry.setAttribute("particleData",new a.InstancedBufferAttribute(i,3)),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))),r.indices[o]=performance.now(),e.target=o,r.particles[o]=e,"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);b(e,r.mesh),e.useAlpha&&(r.mesh.material.transparent=!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;n.mesh.frustumCulled=!1,b(e,n.mesh),a&&(n.mesh.setColorAt(e.target,P.copy(e.color)),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}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;r.indices[e.target]=null,e.scale=0,b(e,r.mesh),r.mesh.instanceMatrix.needsUpdate=!0,e.target=null}}}const y=new i;function b(e,t){if(U.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,y.matrix);const a=y.getWorldDirection(C);a.applyAxisAngle(g,Math.PI/-2),A.setFromUnitVectors(a,r)}else M.set(e.rotation.x,e.rotation.y,e.rotation.z),A.setFromEuler(M);const r=e.transform.initialScale;null!=r?C.set(r.x*e.scale,r.y*e.scale,r.z*e.scale):C.set(e.scale,e.scale,e.scale),w.compose(U,A,C),t.setMatrixAt(e.target,w),t.instanceMatrix.needsUpdate=!0}const x=new a.Vector3(0,0,-1),w=new a.Matrix4,A=new a.Quaternion,U=new a.Vector3,C=new a.Vector3,M=new a.Euler,P=new a.Color(0),v={r:0,g:0,b:0};function R(e,t){const r=e.mesh,o=r.count,i=[],n=r.geometry.getAttribute("particleData"),s=new a.Vector3;for(let l=0;l<o;l++){const o=new a.Matrix4,c=new a.Color;r.getMatrixAt(l,o),r.getColorAt(l,c);const d=n.getX(l);s.setFromMatrixPosition(o);const m=s.distanceTo(t.position);i.push({index:l,distance:m,matrix:o,particle:e.particles[l],color:c,pdx:d})}i.sort((e,t)=>t.distance-e.distance);for(let e=0;e<o;e++){const t=i[e].matrix;null==i[e].particle&&t.makeScale(0,0,0),r.setMatrixAt(e,t),r.setColorAt(e,i[e].color),n.setX(e,i[e].pdx)}r.instanceMatrix.needsUpdate=!0}export{R as sortInstancedMeshByDistance};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -2,13 +2,11 @@ import { Subject } from "rxjs";
|
|
|
2
2
|
import { Object3D } from "three";
|
|
3
3
|
import { Constructable } from "typedi";
|
|
4
4
|
import { ActorComponent, ComponentAttachProps } from './component.js';
|
|
5
|
-
import { Type } from "../../utils/type.js";
|
|
6
5
|
import { NetRole } from "../net/service/net-actor-role.js";
|
|
7
|
-
|
|
8
|
-
* Get actor by ID found using ActorClass.__actorId or actor.constructor.__actorId
|
|
9
|
-
*/
|
|
10
|
-
export declare function getActorClassById(id: string): Type<BaseActor> | undefined;
|
|
6
|
+
export { getActorClassById, getActorClassId, getRegisteredActorClasses, } from './type-registry.js';
|
|
11
7
|
export type ActorOptions = {
|
|
8
|
+
/** Stable serialized identity. Defaults to the class name for backwards compatibility. */
|
|
9
|
+
typeId?: string;
|
|
12
10
|
replicate: boolean;
|
|
13
11
|
relevancy?: {
|
|
14
12
|
ownerOnly?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t,__metadata as e}from"tslib";import o,{ignore as i}from"@plumier/reflect";import{Subject as
|
|
1
|
+
import{__decorate as t,__metadata as e}from"tslib";import o,{ignore as i}from"@plumier/reflect";import{Subject as s,skipWhile as r}from"rxjs";import{Group as n}from"three";import a,{Service as p}from"typedi";import{ActorComponent as c}from"./component.js";import{initComponentsSync as d}from"./internal/component-init.js";import{activeContainerInstance as m,containerRefMap as l}from"./internal/container-map.js";import{randomString as h}from"../../utils/math.js";import{inject as u}from"../inject.js";import{ViewController as f}from"../services/render.js";import{actorConstructContext as y}from"./factory.js";import{NetRole as j}from"../net/service/net-actor-role.js";import{registerActorClass as b}from"./type-registry.js";export{getActorClassById,getActorClassId,getRegisteredActorClasses}from"./type-registry.js";export const $actorOptions=Symbol("actor-options");const g={replicate:!1};export function Actor(t=g){const e=p({transient:!0});return function(i){const s=arguments[1],r=t.typeId??s?.name??i.name,n=b(r,i);null!=n&&n!==i&&console.warn(`Duplicate actor class name ${r}`),i.__actorId=r,i.__isActor=!0,e(i),o.noop()(i),o.parameterProperties()(i),i[$actorOptions]=t}}let U=0;export class BaseActor{get position(){return this.object?.position}get quaternion(){return this.object?.quaternion}get rotation(){return this.object?.rotation}onInit(){}onBeginPlay(){}onEndPlay(){}applySceneParameters(t,e){return!1}onUpdate(t){}onLateUpdate(t){}constructor(){this.id=++U,this.netRole=j.authority,this.__isInitialised=!1,this.object=new n,this.disposed=new s,this.attachedComponents=[],y.actor=this,this.onUpdate!==BaseActor.prototype.onUpdate&&u(f).onUpdate(this).pipe(r(()=>!this.__isInitialised)).subscribe(t=>this.onUpdate(t)),this.onLateUpdate!==BaseActor.prototype.onLateUpdate&&u(f).onLateUpdate(this).pipe(r(()=>!this.__isInitialised)).subscribe(t=>this.onLateUpdate(t))}attach(t,e){const o=m.value,i=l.get(this)??m.value??a.of("default");m.value=i;const s=h();i.set({id:s,type:t,transient:!0});const r=i.get(s);if(m.value=o,null!=e)for(const t of Object.keys(e))r[t]=e[t];return this.__isInitialised&&(r.actor=this,r.onInit(),d(r,this)),this.attachedComponents.push(r),r}getComponent(t){for(const e of Object.values(this))if(e instanceof t)return e;for(const e of this.attachedComponents)if(e instanceof t)return e}}t([i(),e("design:type",Object),e("design:paramtypes",[])],BaseActor.prototype,"position",null),t([i(),e("design:type",Object),e("design:paramtypes",[])],BaseActor.prototype,"quaternion",null),t([i(),e("design:type",Object),e("design:paramtypes",[])],BaseActor.prototype,"rotation",null);export function _setupActorUpdateEventHandlers(){this.onUpdate!==c.prototype.onUpdate&&u(f).onUpdate(this.actor).pipe(r(()=>!this.actor?.__isInitialised)).subscribe(t=>this.onUpdate(t)),this.onLateUpdate!==c.prototype.onLateUpdate&&u(f).onLateUpdate(this.actor).pipe(r(()=>!this.actor?.__isInitialised)).subscribe(t=>this.onLateUpdate(t))}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -28,6 +28,8 @@ export type CharacterAnimationPoseProcessor = {
|
|
|
28
28
|
export declare class CharacterAnimationComponent extends ActorComponent {
|
|
29
29
|
private viewController;
|
|
30
30
|
private mixer;
|
|
31
|
+
private animationRigId?;
|
|
32
|
+
private animationRigData?;
|
|
31
33
|
private poseProcessors;
|
|
32
34
|
private poseProcessorContext?;
|
|
33
35
|
private stateMachines;
|
|
@@ -224,6 +226,7 @@ export declare class CharacterAnimationComponent extends ActorComponent {
|
|
|
224
226
|
private getFullBodyClip;
|
|
225
227
|
private getUpperBodyClip;
|
|
226
228
|
play(clip: AnimationClip, options?: PlayOptions): void;
|
|
229
|
+
private assertClipRigCompatible;
|
|
227
230
|
private onActionDone;
|
|
228
231
|
/**
|
|
229
232
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t}from"tslib";import{ActorComponent as e,Component as o}from"../../../component.js";import{RootMotionClip as i}from"../../../../animation/root-motion.js";import{inject as s}from"../../../../inject.js";import{ViewController as r}from"../../../../services/render.js";import{AnimationMixer as n,Bone as l,Object3D as a,LoopOnce as h,Raycaster as c,Vector3 as p,SkinnedMesh as d,Skeleton as u,Quaternion as f,Matrix4 as g,MathUtils as y,ArrowHelper as m,Group as k}from"three";import{getCharacterMovementLike as S}from"./character-movement-like.js";import{CCDIKSolver as w}from"three/addons/animation/CCDIKSolver.js";import{World as b}from"../../../../services/world.js";import{PhysicsSystem as L,RayTestResult as v}from"../../../../services/physics/physics-system.js";import{ArrayMap as B}from"../../../../../utils/collections.js";const I=new p(0,-1,0),P=new p(0,1,0),x=new p(1,1,1),F=new f,D=new p,R=new p,A=new p,M=new p,T=new p,W=new f,O=new f,V=new f,C=new f,E=new g,q=new g,H=new p(1,0,0),z=new p(0,1,0),N=new p(0,0,1);function U(){return{point:new p,normal:new p,hasNormal:!1}}const j=["mixamorigHips","Hips","hips","Pelvis","pelvis"],Q={upper:["LeftUpLeg","leftUpLeg","left_up_leg"],lower:["LeftLeg","leftLeg","left_leg"],foot:["LeftFoot","leftFoot","left_foot"]},G={upper:["RightUpLeg","rightUpLeg","right_up_leg"],lower:["RightLeg","rightLeg","right_leg"],foot:["RightFoot","rightFoot","right_foot"]},_=["left","right"];let K=class extends e{constructor(){super(...arguments),this.viewController=s(r),this.poseProcessors=new B,this.stateMachines=[],this.upperStateMachines=[],this.fadeTime=.2,this.movementSpeed=null,this.upperBodyTimer=0,this.upperBodyOverride=!1,this.fullBodyTimer=0,this.currentFullBodyPriority=-1,this.currentUpperBodyPriority=-1,this.externalControlActive=!1,this.world=s(b),this.physicsSystem=s(L),this.footIkEnabled=!1,this.footIkMoving=!1,this.footIkRayStartHeight=.5,this.footIkRayLength=1.5,this.footIkRaycastMode="physics",this.footIkFootOffset=.08,this.footIkAutoContactOffset=!1,this.footIkPositionLerpSpeed=28,this.footIkRotationLerpSpeed=14,this.footIkWeightInSpeed=34,this.footIkWeightOutSpeed=30,this.footIkMaxSlopeAngleDeg=40,this.footIkForwardSampleDistance=.14,this.footIkSideSampleDistance=.08,this.footIkMaxHorizontalOffset=.22,this.footIkMaxVerticalOffsetDown=.75,this.footIkMaxVerticalOffsetUp=.2,this.footIkPelvisEnabled=!0,this.footIkPelvisBone="mixamorigHips",this.footIkPelvisLerpSpeed=24,this.footIkPelvisMaxOffsetDown=.4,this.footIkPelvisMaxOffsetUp=.08,this.footIkRequireGrounded=!0,this.footIkSimpleTiltOnly=!1,this.footIkDebug=!1,this.footIkDebugNormalLength=.25,this.footIkDebugAxisLength=.2,this.footIkExtraClearance=.01,this.footIkPlantReleaseUpSpeed=.08,this.footIkPlantReleaseExtension=.9,this.footIkPlantAttachExtension=.96,this.footIkPlantReleaseLift=.055,this.footIkPlantAttachLift=.02,this.leftLegIkBones={upperLeg:"mixamorigLeftUpLeg",lowerLeg:"mixamorigLeftLeg",foot:"mixamorigLeftFoot"},this.rightLegIkBones={upperLeg:"mixamorigRightUpLeg",lowerLeg:"mixamorigRightLeg",foot:"mixamorigRightFoot"},this.ikPelvisOffsetY=0,this.ikLegState={left:{targetPosition:new p,normal:new p(0,1,0),forward:new p(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new p,hasLastFootSample:!1,weight:0,top:!1},right:{targetPosition:new p,normal:new p(0,1,0),forward:new p(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new p,hasLastFootSample:!1,weight:0,top:!1}},this.ikWarnedMissingBones=!1,this.raycaster=new c,this.raycastIntersections=[],this.footIkRayTo=new p,this.footIkRayTestResult=new v,this.footIkRayTestOptions={excludeTriggers:!0,resolveActor:!1,collisionFilter:-2},this.footIkHits={center:U(),toe:U(),heel:U(),side:U()},this.footIkAxesScratch={up:new p,forward:new p,right:new p},this.footIkSampleAxesScratch={forward:new p,right:new p},this.footIkScratch={footWorldPos:new p,centerRayStart:new p,toeRayStart:new p,heelRayStart:new p,sideRayStart:new p,desiredTargetPos:new p,desiredNormal:new p,desiredForward:new p,debugRayEndA:new p,debugRayEndB:new p,debugRayEndC:new p,debugRayEndD:new p,debugRayEndE:new p,debugRayEndF:new p},this.ikPreSolve={left:{upper:new f,lower:new f,foot:new f},right:{upper:new f,lower:new f,foot:new f}},this.getFullBodyClip=X(t=>t.uuid,t=>Y(this.fullBodyMask,t)),this.getUpperBodyClip=X(t=>t.uuid,t=>Y(this.upperBodyMask,t))}onInit(){this.viewController.onUpdate(this.actor).subscribe(t=>this.updateInternal(t)),this.characterMovement=S(this.actor),this.footIkRayTestOptions.excludeActor=this.actor}onEndPlay(){this.disposeFootIkDebug()}raycastDownFirstValid(t,e,o){const i=this.world.scene;this.raycaster.set(t,I),this.raycaster.near=0,this.raycaster.far=e,this.raycastIntersections.length=0,this.raycaster.intersectObject(i,!0,this.raycastIntersections);for(const t of this.raycastIntersections)if(!(this.isFootIkDebugObject(t.object)||this.isRelatedToActorHierarchy(t.object)||this.isSkinnedMeshObject(t.object)))return o.point.copy(t.point),null!=t.face?(o.normal.copy(t.face.normal).transformDirection(t.object.matrixWorld).normalize(),o.hasNormal=!0):o.hasNormal=!1,this.raycastIntersections.length=0,!0;return this.raycastIntersections.length=0,!1}rayTestDown(t,e,o){const i=this.footIkRayTo.copy(t).addScaledVector(I,e),s=this.physicsSystem.rayTest(t,i,this.footIkRayTestResult,this.footIkRayTestOptions);return!!s.hasHit&&(o.point.copy(s.hitPoint),o.normal.copy(s.hitNormal),o.normal.lengthSq()>1e-8?(o.normal.normalize(),o.hasNormal=!0):o.hasNormal=!1,!0)}isRelatedToActorHierarchy(t){const e=this.actor?.object;return null!=e&&null!=t&&(tt(t,e)||tt(e,t))}isFootIkDebugObject(t){let e=t;for(;null!=e;){if(!0===e.userData?.footIkDebug)return!0;e=e.parent}return!1}isSkinnedMeshObject(t){let e=t;for(;null!=e;){if(e instanceof d||!0===e.isSkinnedMesh)return!0;e=e.parent}return!1}getRootMotionAction(){if(this.fullBodyAction.getClip()instanceof i)return this.fullBodyAction}getFullBodyAction(){return this.fullBodyAction}setup(t,e,o){null!=e&&(this.upperBodyMask=$(e),this.fullBodyMask=function(t,e){const o=new Set($(e).map(t=>t.uuid)),i=[];return t.traverse(t=>{(t instanceof l||t.isBone)&&!o.has(t.uuid)&&i.push(t)}),i}(Z(t),e)),null!=this.mixer&&this.mixer.stopAllAction(),this.mixer=new n(t),this.ikRoot=t,this.disposeFootIkDebug(),this.initializeFootIk(t,o),this.syncFootIkDebug()}updateStateMachines(t){this.stateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o&&this.play(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0})}),this.upperStateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o?this.playUpper(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0}):this.play(this.fullBodyClip)})}addPoseProcessor(t){const e={id:t.id,phase:t.phase??"afterMixer",order:t.order??0,update:t.update};this.removePoseProcessor(e.id);const o=this.poseProcessors.get(e.phase);let i=o.findIndex(t=>t.order>e.order);return-1===i&&(i=o.length),o.splice(i,0,e),()=>{const t=o.indexOf(e);-1!==t&&(o.splice(t,1),0===o.length&&this.poseProcessors.delete(e.phase))}}removePoseProcessor(t){for(const[e,o]of this.poseProcessors.entries()){const i=o.findIndex(e=>e.id===t);if(-1!==i)return o.splice(i,1),void(0===o.length&&this.poseProcessors.delete(e))}}runPoseProcessors(t,e){if(0===this.poseProcessors.size||null==this.mixer||!this.poseProcessors.present(t))return;const o=this.mixer.getRoot();if(!(o instanceof a))return;const i=this.poseProcessorContext??(this.poseProcessorContext={deltaTime:e,phase:t,animation:this,mixer:this.mixer,root:o});i.deltaTime=e,i.phase=t,i.mixer=this.mixer,i.root=o;const s=this.poseProcessors.get(t);for(let t=0;t<s.length;t++)s[t].update(i)}updateInternal(t){null!=this.mixer&&(this.externalControlActive||(this.upperBodyTimer+=t*(this.upperBodyAction?.timeScale??1),this.fullBodyTimer+=t*(this.fullBodyAction?.timeScale??1),this.upperBodyAction&&this.upperBodyOverride&&this.upperBodyAction.getClip().duration-2*(this.overrideFadeTimeUpper??this.fadeTime)<this.upperBodyTimer&&(this.upperBodyOverride=!1,null!=this.fullBodyClip&&this.transition(this.upperBodyAction,this.getUpperBodyClip(this.fullBodyClip)),this.upperBodyAction=null,this.overrideFadeTimeUpper=null),this.fullBodyAction&&this.fullBodyAction.loop===h&&this.fullBodyAction.getClip().duration-2*(this.overrideFadeTime??this.fadeTime)<this.fullBodyTimer&&(this.currentFullBodyPriority=-1,this.overrideFadeTime=null),null!=this.characterMovement&&(this.movementSpeed=this.characterMovement.horizontalSpeed),this.updateStateMachines(t),this.syncMovementSpeed(this.fullBodyAction),this.upperBodyOverride||this.syncMovementSpeed(this.upperBodyAction),this.runPoseProcessors("beforeMixer",t),this.mixer.update(t),this.runPoseProcessors("afterMixer",t),this.runPoseProcessors("beforeIk",t),this.updateFootIk(t),this.runPoseProcessors("afterIk",t)))}initializeFootIk(t,e){if(this.ikSolver=null,this.ikSkinnedMesh=null,this.ikBoneRefs=null,this.ikLegLengths=null,this.ikPelvisBone=null,this.ikPelvisOffsetY=0,this.ikFootAxes=null,this.ikTargetBones=null,this.ikWarnedMissingBones=!1,this.ikLegState.left.weight=0,this.ikLegState.right.weight=0,!this.footIkEnabled)return;const o=this.findFirstSkinnedMesh(t);if(null==o||null==o.skeleton)return;this.ikSkinnedMesh=o,t.updateWorldMatrix(!0,!0);const i=o.skeleton,s=this.resolveLegBoneRefs(i,this.leftLegIkBones,Q),r=this.resolveLegBoneRefs(i,this.rightLegIkBones,G);if(null==s||null==r)return void(this.ikWarnedMissingBones||(this.ikWarnedMissingBones=!0,console.warn(`[CharacterAnimationComponent] Foot IK disabled: missing leg bones. Left=${this.leftLegIkBones.upperLeg}/${this.leftLegIkBones.lowerLeg}/${this.leftLegIkBones.foot}, Right=${this.rightLegIkBones.upperLeg}/${this.rightLegIkBones.lowerLeg}/${this.rightLegIkBones.foot}`)));this.ikBoneRefs={left:s,right:r},this.ikLegLengths={left:this.computeLegLengths(s),right:this.computeLegLengths(r)},this.ikFootAxes={left:this.detectFootAxisBasis(s.foot),right:this.detectFootAxisBasis(r.foot)},this.ikPelvisBone=this.resolvePelvisBone(i,s,r),this.ikLegState.left.targetPosition.copy(s.foot.getWorldPosition(D)),this.ikLegState.right.targetPosition.copy(r.foot.getWorldPosition(D)),this.ikLegState.left.normal.set(0,1,0),this.ikLegState.right.normal.set(0,1,0),this.ikLegState.left.pitch=0,this.ikLegState.right.pitch=0,this.ikLegState.left.contactOffset=this.estimateFootContactOffset(i,"left",s.foot),this.ikLegState.right.contactOffset=this.estimateFootContactOffset(i,"right",r.foot),this.ikLegState.left.planted=!0,this.ikLegState.right.planted=!0,this.ikLegState.left.lastFootWorldPos.copy(s.foot.getWorldPosition(D)),this.ikLegState.right.lastFootWorldPos.copy(r.foot.getWorldPosition(D)),this.ikLegState.left.hasLastFootSample=!0,this.ikLegState.right.hasLastFootSample=!0;const n=this.resolveFootAxes("left",s.foot),a=this.resolveFootAxes("right",r.foot);this.ikLegState.left.forward.copy(n.forward),this.ikLegState.right.forward.copy(a.forward);const h=e??Z(t);if(null==h)return;let c=i.getBoneByName("_IKTargetLeftFoot"),p=i.getBoneByName("_IKTargetRightFoot"),d=!1;if(null==c&&(c=new l,c.name="_IKTargetLeftFoot",h.add(c),d=!0),null==p&&(p=new l,p.name="_IKTargetRightFoot",h.add(p),d=!0),this.setBoneWorldPosition(c,s.foot.getWorldPosition(D)),this.setBoneWorldPosition(p,r.foot.getWorldPosition(D)),d||i.bones.indexOf(c)<0||i.bones.indexOf(p)<0){const t=i.bones.slice(),e=i.boneInverses.map(t=>t.clone());t.indexOf(c)<0&&(t.push(c),e.push((new g).copy(c.matrixWorld).invert())),t.indexOf(p)<0&&(t.push(p),e.push((new g).copy(p.matrixWorld).invert())),o.bind(new u(t,e),o.bindMatrix)}this.ikTargetBones={left:c,right:p};const f=o.skeleton.bones,y={target:f.indexOf(c),effector:f.indexOf(s.foot),links:[{index:f.indexOf(s.lower)},{index:f.indexOf(s.upper)}],iteration:8},m={target:f.indexOf(p),effector:f.indexOf(r.foot),links:[{index:f.indexOf(r.lower)},{index:f.indexOf(r.upper)}],iteration:8};if(y.target<0||y.effector<0||y.links.some(t=>t.index<0)||m.target<0||m.effector<0||m.links.some(t=>t.index<0))return console.warn("[CharacterAnimationComponent] Foot IK disabled: failed to resolve IK indexes in skeleton."),void(this.ikSolver=null);this.ikSolver=new w(o,[y,m])}findFirstSkinnedMesh(t){let e;return t.traverse(t=>{null==e&&(t instanceof d||!0===t.isSkinnedMesh)&&(e=t)}),e}resolveLegBoneRefs(t,e,o){const i=this.findBoneByNames(t,[e.upperLeg,...o.upper]),s=this.findBoneByNames(t,[e.lowerLeg,...o.lower]),r=this.findBoneByNames(t,[e.foot,...o.foot]);if(null!=i&&null!=s&&null!=r)return{upper:i,lower:s,foot:r}}findBoneByNames(t,e){for(const o of e){const e=t.getBoneByName(o);if(null!=e)return e}}resolvePelvisBone(t,e,o){const i=this.findBoneByNames(t,[this.footIkPelvisBone,...j]);if(null!=i)return i;const s=e.upper.parent,r=o.upper.parent;return null!=s&&s===r&&(s instanceof l||!0===s.isBone)||null!=s&&(s instanceof l||!0===s.isBone)?s:void 0}computeLegLengths(t){const e=t.upper.getWorldPosition(D),o=t.lower.getWorldPosition(R),i=t.foot.getWorldPosition(A),s=e.distanceTo(o),r=o.distanceTo(i);return{upper:s,lower:r,total:s+r}}setBoneWorldPosition(t,e){const o=t.parent;null==o?t.position.copy(e):t.position.copy(o.worldToLocal(D.copy(e))),t.quaternion.copy(F),t.scale.copy(x),t.updateMatrixWorld(!0)}syncFootIkDebug(){this.footIkDebug&&null!=this.ikRoot?(null!=this.ikDebugRoot&&null!=this.ikDebugLegs||(this.ikDebugRoot=new k,this.ikDebugRoot.name="FootIKDebug",this.ikDebugRoot.userData.footIkDebug=!0,this.ikDebugLegs={left:this.createDebugLeg("LeftFootIK",53503),right:this.createDebugLeg("RightFootIK",16755200)},this.ikDebugRoot.add(this.ikDebugLegs.left.root,this.ikDebugLegs.right.root),this.world.scene.add(this.ikDebugRoot)),this.ikDebugRoot.visible=!0):null!=this.ikDebugRoot&&(this.ikDebugRoot.visible=!1)}disposeFootIkDebug(){null!=this.ikDebugRoot&&(this.ikDebugRoot.removeFromParent(),this.ikDebugRoot.clear()),this.ikDebugRoot=null,this.ikDebugLegs=null}createDebugLeg(t,e){const o=new k;o.name=t,o.userData.footIkDebug=!0;const i=this.createDebugArrow(e),s=this.createDebugArrow(e),r=this.createDebugArrow(e),n=this.createDebugArrow(e),l=this.createDebugArrow(65280),a=this.createDebugArrow(6750054),h=this.createDebugArrow(16711935),c=this.createDebugArrow(16776960),p=this.createDebugArrow(16737792);return o.add(i,s,r,n,l,a,h,c,p),{root:o,rayCenter:i,rayToe:s,rayHeel:r,raySide:n,hitNormal:l,footUp:a,footToTarget:h,solveUpper:c,solveLower:p}}createDebugArrow(t){const e=new m(P,new p,.001,t);return e.userData.footIkDebug=!0,e.traverse(t=>{t.userData.footIkDebug=!0,t.raycast=()=>{}}),e.visible=!1,e}setDebugArrow(t,e,o){D.subVectors(o,e);const i=D.length();i<1e-6?t.visible=!1:(t.visible=!0,t.position.copy(e),t.setDirection(D.multiplyScalar(1/i)),t.setLength(i,Math.min(.08,.25*i),Math.min(.05,.2*i)))}resetDebugVisibility(){null!=this.ikDebugLegs&&(this.ikDebugLegs.left.rayCenter.visible=!1,this.ikDebugLegs.left.rayToe.visible=!1,this.ikDebugLegs.left.rayHeel.visible=!1,this.ikDebugLegs.left.raySide.visible=!1,this.ikDebugLegs.left.hitNormal.visible=!1,this.ikDebugLegs.left.footUp.visible=!1,this.ikDebugLegs.left.footToTarget.visible=!1,this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.rayCenter.visible=!1,this.ikDebugLegs.right.rayToe.visible=!1,this.ikDebugLegs.right.rayHeel.visible=!1,this.ikDebugLegs.right.raySide.visible=!1,this.ikDebugLegs.right.hitNormal.visible=!1,this.ikDebugLegs.right.footUp.visible=!1,this.ikDebugLegs.right.footToTarget.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1)}updateFootIk(t){if(this.syncFootIkDebug(),!this.footIkEnabled||null==this.ikBoneRefs||null==this.ikRoot)return void this.resetDebugVisibility();if(!this.footIkMoving&&(this.movementSpeed??0)>.01)return this.resetFootIkState(),void this.resetDebugVisibility();this.ikRoot.updateWorldMatrix(!0,!0);const e=this.characterMovement?.isGrounded??!0;return this.footIkSimpleTiltOnly?(this.updatePelvisOffset(t,!1),this.updateLegTiltSimple("left",this.ikBoneRefs.left.foot,this.ikLegState.left,e,t),void this.updateLegTiltSimple("right",this.ikBoneRefs.right.foot,this.ikLegState.right,e,t)):null==this.ikLegLengths||null==this.ikTargetBones||null==this.ikSkinnedMesh||null==this.ikSolver?(this.updatePelvisOffset(t,!1),void this.resetDebugVisibility()):(this.updateLegIkTarget("left",this.ikBoneRefs.left,this.ikLegLengths.left,this.ikTargetBones.left,this.ikLegState.left,e,t),this.updateLegIkTarget("right",this.ikBoneRefs.right,this.ikLegLengths.right,this.ikTargetBones.right,this.ikLegState.right,e,t),this.updatePelvisOffset(t,!0),this.setBoneWorldPosition(this.ikTargetBones.left,this.ikLegState.left.targetPosition),this.setBoneWorldPosition(this.ikTargetBones.right,this.ikLegState.right.targetPosition),this.ikPreSolve.left.upper.copy(this.ikBoneRefs.left.upper.quaternion),this.ikPreSolve.left.lower.copy(this.ikBoneRefs.left.lower.quaternion),this.ikPreSolve.left.foot.copy(this.ikBoneRefs.left.foot.quaternion),this.ikPreSolve.right.upper.copy(this.ikBoneRefs.right.upper.quaternion),this.ikPreSolve.right.lower.copy(this.ikBoneRefs.right.lower.quaternion),this.ikPreSolve.right.foot.copy(this.ikBoneRefs.right.foot.quaternion),null!=this.ikDebugLegs&&(this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1),this.ikSolver.update(),this.blendLegAfterSolve(this.ikBoneRefs.left,this.ikPreSolve.left,this.ikLegState.left.weight),this.blendLegAfterSolve(this.ikBoneRefs.right,this.ikPreSolve.right,this.ikLegState.right.weight),this.applySimpleFootPitch("left",this.ikBoneRefs.left.foot,this.ikLegState.left),void this.applySimpleFootPitch("right",this.ikBoneRefs.right.foot,this.ikLegState.right))}resetFootIkState(){if(null!=this.ikBoneRefs){for(const t of _){const e=this.ikBoneRefs[t],o=this.ikLegState[t],i=e.foot.getWorldPosition(D);o.targetPosition.copy(i),o.lastFootWorldPos.copy(i),o.hasLastFootSample=!0,o.weight=0,o.normal.set(0,1,0);const s=this.resolveFootAxes(t,e.foot,this.footIkAxesScratch);o.forward.copy(s.forward),o.pitch=0,this.footIkAutoContactOffset?o.contactOffset=Math.max(o.contactOffset,this.footIkFootOffset+this.footIkExtraClearance):o.contactOffset=this.footIkFootOffset+this.footIkExtraClearance,o.planted=!1}this.ikPelvisOffsetY=0}}updatePelvisOffset(t,e){if(null==this.ikPelvisBone)return;let o=0;if(e&&this.footIkPelvisEnabled&&null!=this.ikBoneRefs&&null!=this.ikLegLengths){this.ikLegState.left.top=!1,this.ikLegState.right.top=!1;for(const t of _){const e=this.ikBoneRefs[t],i=this.ikLegLengths[t],s=this.ikLegState[t];if(!s.planted)continue;const r=e.upper.getWorldPosition(D),n=.998*i.total,l=this.computeRequiredPelvisDrop(r,s.targetPosition,n);if(l>0){const e="left"===t?"right":"left";this.ikLegState[e].top=!0}o=Math.min(o,-l)}o=y.clamp(o,-this.footIkPelvisMaxOffsetDown,this.footIkPelvisMaxOffsetUp)}const i=1-Math.exp(-this.footIkPelvisLerpSpeed*t);this.ikPelvisOffsetY=y.lerp(this.ikPelvisOffsetY,o,i);const s=this.ikPelvisBone.getWorldPosition(R),r=A.copy(s).addScaledVector(P,this.ikPelvisOffsetY);null==this.ikPelvisBone.parent?this.ikPelvisBone.position.copy(r):this.ikPelvisBone.position.copy(this.ikPelvisBone.parent.worldToLocal(r)),this.ikPelvisBone.updateMatrixWorld(!0)}computeFootVerticalSpeed(t,e,o){return!t.hasLastFootSample||o<=1e-6?0:(e.y-t.lastFootWorldPos.y)/o}updateFootSample(t,e){t.lastFootWorldPos.copy(e),t.hasLastFootSample=!0}estimateFootContactOffset(t,e,o){const i=this.footIkFootOffset+this.footIkExtraClearance;if(!this.footIkAutoContactOffset)return i;const s=t.bones.indexOf(o);if(s<0)return i;const r=t.boneInverses[s];if(null==r)return i;const n=this.ikFootAxes?.[e]??this.detectFootAxisBasis(o),a=o.getWorldPosition(R),h=A.copy(n.upLocal).applyQuaternion(o.getWorldQuaternion(W)).normalize();let c=0;return o.traverse(e=>{if(!(e instanceof l)||e===o)return;const i=t.bones.indexOf(e);if(i<0)return;const s=E.copy(t.boneInverses[i]).invert(),n=q.copy(r).multiply(s),p=D.setFromMatrixPosition(n),d=M.copy(p).applyMatrix4(o.matrixWorld).sub(a);c=Math.min(c,d.dot(h))}),Math.max(i,-c+this.footIkExtraClearance)}shouldPlantFoot(t,e,o,i,s,r){if(!e||!o)return!1;const n=this.footIkPlantReleaseExtension,l=this.footIkPlantAttachExtension,a=this.footIkPlantReleaseLift,h=this.footIkPlantAttachLift;return t.planted?!(i>this.footIkPlantReleaseUpSpeed&&(s<n||r>a)):s>=l&&r<=h||i<=0&&s>=n}computeLegExtensionRatio(t,e){const o=t.upper.getWorldPosition(D),i=t.foot.getWorldPosition(R),s=o.distanceTo(i);return e.total<=1e-6?1:y.clamp(s/e.total,0,1.2)}computeRequiredPelvisDrop(t,e,o){const i=M.subVectors(t,e),s=i.length();if(s<=o+1e-6)return 0;const r=i.dot(P),n=o*o-Math.max(s*s-r*r,0);if(n<=0)return s-o;const l=Math.sqrt(n),a=r-l,h=r+l;return a>=0?a:h>=0?h:0}updateLegTiltSimple(t,e,o,i,s){const r=this.footIkScratch,n=this.footIkHits,l=e.getWorldPosition(r.footWorldPos),a=this.computeFootVerticalSpeed(o,l,s),h=this.resolveFootAxes(t,e,this.footIkAxesScratch),c=this.resolveGroundSampleAxes(t,e,h,this.footIkSampleAxesScratch),p=r.centerRayStart.copy(l).addScaledVector(P,this.footIkRayStartHeight),d=r.toeRayStart.copy(p).addScaledVector(c.forward,this.footIkForwardSampleDistance),u=r.heelRayStart.copy(p).addScaledVector(c.forward,-this.footIkForwardSampleDistance),f=r.sideRayStart.copy(p).addScaledVector(c.right,this.footIkSideSampleDistance),g=this.getFirstGroundHit(p,n.center),m=this.getFirstGroundHit(d,n.toe),k=this.getFirstGroundHit(u,n.heel),S=this.getFirstGroundHit(f,n.side),w=r.desiredNormal.set(0,1,0),b=r.desiredForward.copy(c.forward);let L=0,v=0,B=this.footIkWeightOutSpeed;const I=null!=g||null!=m||null!=k;let x=l.y;null!=g?x=g.point.y:null!=m&&null!=k?x=.5*(m.point.y+k.point.y):null!=m?x=m.point.y:null!=k&&(x=k.point.y);const F=this.ikBoneRefs?.[t],D=this.ikLegLengths?.[t],R=null!=F&&null!=D?this.computeLegExtensionRatio(F,D):1,A=Math.max(0,l.y-x,l.y-o.targetPosition.y),M=this.shouldPlantFoot(o,!this.footIkRequireGrounded||i,I,a,R,A);M?this.computeDesiredGroundNormal(c,g,m,k,S,w)&&(this.computeDesiredFootForward(c.forward,c.right,w,m,k,b),L=this.computeDesiredPitchAngle(m,k,c.forward),v=1,B=this.footIkWeightInSpeed):this.computeDesiredFootForward(c.forward,c.right,w,null,null,b);const T=1-Math.exp(-this.footIkPositionLerpSpeed*s),W=1-Math.exp(-B*s);o.normal.lerp(w,T).normalize(),o.forward.lerp(b,T).normalize(),o.pitch=y.lerp(o.pitch,L,T),o.weight=y.lerp(o.weight,v,W),o.planted=M&&v>.001,this.updateFootSample(o,l),this.applySimpleFootPitch(t,e,o),this.updateLegDebug(t,{footWorldPos:l,centerRayStart:p,toeRayStart:d,heelRayStart:u,sideRayStart:f,centerHit:g,toeHit:m,heelHit:k,sideHit:S,desiredNormal:o.normal,desiredTargetPos:r.footWorldPos,weight:o.weight})}updateLegIkTarget(t,e,o,i,s,r,n){const l=this.footIkScratch,a=this.footIkHits,h=e.foot,c=h.getWorldPosition(l.footWorldPos),p=this.computeFootVerticalSpeed(s,c,n),d=this.resolveFootAxes(t,h,this.footIkAxesScratch),u=this.resolveGroundSampleAxes(t,h,d,this.footIkSampleAxesScratch),f=l.centerRayStart.copy(c).addScaledVector(P,this.footIkRayStartHeight),g=l.toeRayStart.copy(f).addScaledVector(u.forward,this.footIkForwardSampleDistance),m=l.heelRayStart.copy(f).addScaledVector(u.forward,-this.footIkForwardSampleDistance),k=l.sideRayStart.copy(f).addScaledVector(u.right,this.footIkSideSampleDistance),S=this.getFirstGroundHit(f,a.center),w=this.getFirstGroundHit(g,a.toe),b=this.getFirstGroundHit(m,a.heel),L=this.getFirstGroundHit(k,a.side),v=l.desiredTargetPos.copy(c),B=l.desiredNormal.set(0,1,0),I=l.desiredForward.copy(u.forward);let x=0,F=0,D=this.footIkWeightOutSpeed;const R=null!=S||null!=w||null!=b;let A=c.y;null!=S?A=S.point.y:null!=w&&null!=b?A=.5*(w.point.y+b.point.y):null!=w?A=w.point.y:null!=b&&(A=b.point.y);const M=this.computeLegExtensionRatio(e,o),T=Math.max(0,c.y-A,c.y-s.targetPosition.y),W=this.shouldPlantFoot(s,!this.footIkRequireGrounded||r,R,p,M,T);if(W){if(v.copy(c),this.computeDesiredGroundNormal(u,S,w,b,L,B)){this.computeDesiredFootForward(u.forward,u.right,B,w,b,I),x=this.computeDesiredPitchAngle(w,b,u.forward);const t=Math.max(0,Math.sin(x))*this.footIkForwardSampleDistance*.45,e=Math.max(this.footIkFootOffset+this.footIkExtraClearance,s.contactOffset);v.y=A+e+t,s.top&&(v.y-=this.ikPelvisOffsetY/2),F=1,D=this.footIkWeightInSpeed}}else this.computeDesiredFootForward(u.forward,u.right,B,null,null,I),v.copy(c),v.y+=this.footIkFootOffset;this.clampDesiredFootTarget(e,o,c,v);const O=1-Math.exp(-this.footIkPositionLerpSpeed*n),V=1-Math.exp(-D*n);s.targetPosition.lerp(v,O),s.normal.lerp(B,O).normalize(),s.forward.lerp(I,O).normalize(),s.pitch=y.lerp(s.pitch,x,O),s.weight=y.lerp(s.weight,F,V),s.planted=W&&F>.001,this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),this.updateLegDebug(t,{footWorldPos:c,centerRayStart:f,toeRayStart:g,heelRayStart:m,sideRayStart:k,centerHit:S,toeHit:w,heelHit:b,sideHit:L,desiredNormal:B,desiredTargetPos:s.targetPosition,weight:s.weight})}updateLegDebug(t,e){if(!this.footIkDebug||null==this.ikDebugLegs||null==this.ikBoneRefs)return;const o=this.ikDebugLegs[t],i=this.ikBoneRefs[t],s=this.resolveFootAxes(t,i.foot,this.footIkAxesScratch),r=this.footIkScratch,n=e.centerHit?.point??r.debugRayEndA.copy(e.centerRayStart).addScaledVector(I,this.footIkRayLength),l=e.toeHit?.point??r.debugRayEndB.copy(e.toeRayStart).addScaledVector(I,this.footIkRayLength),a=e.heelHit?.point??r.debugRayEndC.copy(e.heelRayStart).addScaledVector(I,this.footIkRayLength),h=e.sideHit?.point??r.debugRayEndD.copy(e.sideRayStart).addScaledVector(I,this.footIkRayLength);this.setDebugArrow(o.rayCenter,e.centerRayStart,n),this.setDebugArrow(o.rayToe,e.toeRayStart,l),this.setDebugArrow(o.rayHeel,e.heelRayStart,a),this.setDebugArrow(o.raySide,e.sideRayStart,h);const c=e.centerHit?.point??e.toeHit?.point??e.heelHit?.point??e.sideHit?.point??e.footWorldPos;this.setDebugArrow(o.hitNormal,c,r.debugRayEndE.copy(c).addScaledVector(e.desiredNormal,this.footIkDebugNormalLength)),this.setDebugArrow(o.footUp,e.footWorldPos,r.debugRayEndF.copy(e.footWorldPos).addScaledVector(s.up,this.footIkDebugAxisLength*(.15+e.weight))),this.setDebugArrow(o.footToTarget,e.footWorldPos,e.desiredTargetPos)}getFirstGroundHit(t,e){return"physics"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)?e:null:"physicsThenRender"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)||this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null:this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null}resolveGroundSampleAxes(t,e,o,i){const s=o??this.resolveFootAxes(t,e),r=i??{forward:new p,right:new p},n=this.actor.object.getWorldDirection(M);n.addScaledVector(P,-n.dot(P)),n.lengthSq()<1e-8&&n.copy(s.forward).addScaledVector(P,-s.forward.dot(P)),n.lengthSq()<1e-8&&n.set(0,0,1),n.normalize();const l=T.crossVectors(P,n);return l.lengthSq()<1e-8?l.copy(s.right):l.normalize(),l.dot(s.right)<0&&l.multiplyScalar(-1),r.forward.copy(n),r.right.copy(l),r}computeDesiredGroundNormal(t,e,o,i,s,r){const n=this.computeAverageHitNormal(e,o,i,s,M);let l=!1,a=0,h=0;if(null!=o&&null!=i){const e=R.subVectors(o.point,i.point).dot(t.forward);Math.abs(e)>1e-5&&(a=(o.point.y-i.point.y)/e,l=!0)}if(null!=s&&null!=e){const o=A.subVectors(s.point,e.point).dot(t.right);Math.abs(o)>1e-5&&(h=(s.point.y-e.point.y)/o,l=!0)}if(l&&(r.copy(P),r.addScaledVector(t.forward,-a),r.addScaledVector(t.right,-h),r.lengthSq()>1e-8?r.normalize():l=!1),l)null!=n&&r.dot(n)<0&&r.multiplyScalar(-1);else{if(null==n)return!1;r.copy(n),l=!0}return r.dot(P)<0&&r.multiplyScalar(-1),this.clampSlopeNormal(r),!0}computeAverageHitNormal(t,e,o,i,s){s.set(0,0,0);let r=0;return null!=t&&t.hasNormal&&(s.add(t.normal),r++),null!=e&&e.hasNormal&&(s.add(e.normal),r++),null!=o&&o.hasNormal&&(s.add(o.normal),r++),null!=i&&i.hasNormal&&(s.add(i.normal),r++),0===r||s.lengthSq()<1e-8?null:(s.multiplyScalar(1/r).normalize(),s)}computeDesiredFootForward(t,e,o,i,s,r){null!=i&&null!=s?r.subVectors(i.point,s.point):r.copy(t),r.addScaledVector(o,-r.dot(o)),r.lengthSq()<1e-8&&(r.copy(this.actor.object.getWorldDirection(M)),r.addScaledVector(o,-r.dot(o))),r.lengthSq()<1e-8&&r.crossVectors(o,e),r.lengthSq()<1e-8&&r.set(0,0,1),r.normalize();const n=M.copy(this.actor.object.getWorldDirection(M));n.addScaledVector(o,-n.dot(o)),n.lengthSq()<1e-8&&(n.copy(t),n.addScaledVector(o,-n.dot(o))),n.lengthSq()>1e-8&&n.normalize(),r.dot(n)<0&&r.multiplyScalar(-1)}computeDesiredPitchAngle(t,e,o){if(null==t||null==e)return 0;const i=R.subVectors(t.point,e.point),s=Math.abs(i.dot(o));if(s<1e-5)return 0;const r=t.point.y-e.point.y,n=y.degToRad(this.footIkMaxSlopeAngleDeg);return y.clamp(-Math.atan2(r,s),-n,n)}applySimpleFootPitch(t,e,o){if(null==e.parent||o.weight<=.001)return;const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o.pitch*o.weight;if(Math.abs(s)<=1e-5)return;const r=this.resolveLocalPitchSign(i),n=W.copy(e.quaternion),l=O.setFromAxisAngle(i.rightLocal,s*r);e.quaternion.copy(n.multiply(l))}resolveLocalPitchSign(t){const e=V.setFromAxisAngle(t.rightLocal,.15);return A.copy(t.upLocal).applyQuaternion(e).sub(t.upLocal).dot(t.forwardLocal)>=0?1:-1}clampDesiredFootTarget(t,e,o,i){const s=R.subVectors(i,o),r=s.dot(P),n=A.copy(s).addScaledVector(P,-r),l=n.length();l>this.footIkMaxHorizontalOffset&&l>1e-6&&n.multiplyScalar(this.footIkMaxHorizontalOffset/l);const a=y.clamp(r,-this.footIkMaxVerticalOffsetDown,this.footIkMaxVerticalOffsetUp);if(i.copy(o),i.add(n),i.addScaledVector(P,a),this.footIkPelvisEnabled)return;const h=t.upper.getWorldPosition(D).clone(),c=R.subVectors(i,h),p=c.length(),d=.995*e.total;p>d&&p>1e-6&&i.copy(h).addScaledVector(c,d/p)}solveTwoBoneLeg(t,e,o){const i=e.upper.getWorldPosition(D).clone(),s=e.lower.getWorldPosition(R).clone(),r=e.foot.getWorldPosition(A).clone(),n=i.distanceTo(s),l=s.distanceTo(r);if(n<1e-5||l<1e-5)return;const a=(new p).subVectors(o,i),h=a.length();if(h<1e-5)return;const c=Math.abs(n-l)+1e-4,d=n+l-1e-4,u=y.clamp(h,c,d),f=a.multiplyScalar(1/h),g=(new p).subVectors(s,i).normalize(),m=(new p).subVectors(r,s).normalize(),k=(new p).crossVectors(g,m);k.lengthSq()<1e-8&&(k.copy(this.actor.object.getWorldDirection(D).cross(P)),k.lengthSq()<1e-8&&k.set(1,0,0)),k.normalize();const S=(u*u+n*n-l*l)/(2*u),w=Math.max(n*n-S*S,0),b=Math.sqrt(w),L=(new p).copy(i).addScaledVector(f,S);let v=(new p).crossVectors(k,f);v.lengthSq()<1e-8&&(v=(new p).crossVectors(f,P),v.lengthSq()<1e-8&&(v=new p(1,0,0))),v.normalize();const B=(new p).copy(L).addScaledVector(v,b),I=(new p).copy(L).addScaledVector(v,-b),x=(new p).subVectors(s,L).dot(v)>=0?B:I;this.rotateBoneToward(e.upper,i,s,x),e.upper.updateMatrixWorld(!0);const F=e.lower.getWorldPosition(R),M=e.foot.getWorldPosition(A);this.rotateBoneToward(e.lower,F,M,o),e.lower.updateMatrixWorld(!0),this.updateSolveDebug(t,i,x,o)}updateSolveDebug(t,e,o,i){if(!this.footIkDebug||null==this.ikDebugLegs)return;const s=this.ikDebugLegs[t];this.setDebugArrow(s.solveUpper,e,o),this.setDebugArrow(s.solveLower,o,i)}rotateBoneToward(t,e,o,i){const s=(new p).subVectors(o,e),r=(new p).subVectors(i,e);if(s.lengthSq()<1e-10||r.lengthSq()<1e-10)return;if(s.normalize(),r.normalize(),s.dot(r)>.9999)return;const n=(new f).setFromUnitVectors(s,r),l=t.getWorldQuaternion(W),a=O.copy(n).multiply(l);if(null==t.parent)return void t.quaternion.copy(a);const h=t.parent.getWorldQuaternion(V),c=C.copy(h).invert().multiply(a);t.quaternion.copy(c)}detectFootAxisBasis(t){const e=t.getWorldQuaternion(W),o=this.actor.object.getWorldDirection(D);o.lengthSq()<1e-6&&o.set(0,0,1),o.normalize();const i=[{local:H.clone(),world:H.clone().applyQuaternion(e)},{local:z.clone(),world:z.clone().applyQuaternion(e)},{local:N.clone(),world:N.clone().applyQuaternion(e)}];i.push({local:H.clone().multiplyScalar(-1),world:H.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:z.clone().multiplyScalar(-1),world:z.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:N.clone().multiplyScalar(-1),world:N.clone().multiplyScalar(-1).applyQuaternion(e)});let s=i[0],r=-1/0;for(const t of i){const e=t.world.dot(P);e>r&&(r=e,s=t)}let n=i[0],l=-1/0;for(const t of i){if(Math.abs(t.world.dot(s.world))>.6)continue;const e=Math.abs(t.world.dot(o));e>l&&(l=e,n=t)}n.world.dot(o)<0&&(n={local:n.local.clone().multiplyScalar(-1),world:n.world.clone().multiplyScalar(-1)});const a=s.local.clone().normalize(),h=n.local.clone().normalize(),c=a.clone().cross(h).normalize();return{upLocal:a,forwardLocal:c.clone().cross(a).normalize(),rightLocal:c}}resolveFootAxes(t,e,o){const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o??{up:new p,forward:new p,right:new p},r=e.getWorldQuaternion(W);return s.up.copy(i.upLocal).applyQuaternion(r).normalize(),s.forward.copy(i.forwardLocal).applyQuaternion(r).normalize(),s.right.copy(i.rightLocal).applyQuaternion(r).normalize(),s}clampSlopeNormal(t){const e=y.clamp(t.dot(P),-1,1),o=Math.acos(e),i=y.degToRad(this.footIkMaxSlopeAngleDeg);if(o<=i||o<1e-5)return;const s=i/o;t.lerpVectors(P,t,s).normalize()}blendLegAfterSolve(t,e,o){if(!(o>=.999)){if(o<=.001)return t.upper.quaternion.copy(e.upper),t.lower.quaternion.copy(e.lower),void t.foot.quaternion.copy(e.foot);this.blendBoneQuaternion(t.upper,e.upper,o),this.blendBoneQuaternion(t.lower,e.lower,o),this.blendBoneQuaternion(t.foot,e.foot,o)}}blendBoneQuaternion(t,e,o){W.copy(t.quaternion),t.quaternion.copy(e).slerp(W,o)}alignFootToGroundNormal(t,e,o,i){if(o.weight<=.001||null==e.parent)return;const s=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),r=D.copy(o.normal).normalize(),n=R.copy(o.forward);if(n.addScaledVector(r,-n.dot(r)),n.lengthSq()<1e-8){const o=this.resolveFootAxes(t,e);n.copy(o.forward).addScaledVector(r,-o.forward.dot(r))}if(n.lengthSq()<1e-8)return;n.normalize();const l=A.crossVectors(r,n);if(l.lengthSq()<1e-8)return;l.normalize(),n.copy(M.crossVectors(l,r)).normalize(),E.makeBasis(s.rightLocal,s.upLocal,s.forwardLocal),W.setFromRotationMatrix(E),q.makeBasis(l,r,n),O.setFromRotationMatrix(q);const a=V.copy(O).multiply(C.copy(W).invert()),h=e.parent.getWorldQuaternion(C),c=O.copy(h).invert().multiply(a),p=(1-Math.exp(-this.footIkRotationLerpSpeed*i))*o.weight;e.quaternion.slerp(c,y.clamp(p,0,1))}getMixer(){return this.mixer}beginExternalControl(){this.externalControlActive=!0}endExternalControl(){this.externalControlActive=!1}stopSequenceAnimation(){this.currentFullBodyPriority=-1,this.externalControlActive=!1,null==this.fullBodyAction||this.fullBodyAction.isRunning()||(this.fullBodyAction=null),null==this.upperBodyAction||this.upperBodyAction.isRunning()||(this.upperBodyAction=null),this.fullBodyTimer=0}cancelRootMotionAction(t){null!=t&&this.fullBodyAction!==t||(this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.fullBodyAction?.stop(),this.fullBodyAction=null,this.fullBodyTimer=0)}beginExternalAnimationControl(t,e={}){J(null!=this.mixer,"Can't begin external control before setup is called"),this.mixer.stopAllAction(),this.currentFullBodyPriority=99,this.fullBodyTimer=0;const o=this.mixer.clipAction(t);return o.setLoop(h,1),o.clampWhenFinished=!0,o.timeScale=e.timeScale??1,o.reset(),o.play(),this.fullBodyAction=o,this.externalControlActive=!0,o}syncMovementSpeed(t){if(null!=t){const e=t.getClip();if(e instanceof i&&e.fixedInPlace&&null!=this.movementSpeed){t.timeScale=e.duration/e.displacement*this.movementSpeed;const o=this.mixer.getRoot();o instanceof a&&(t.timeScale/=o.scale.x)}}}playStateMachine(t){this.stateMachines.push(t)}playUpperStateMachine(t){this.upperStateMachines.push(t)}removeStateMachine(t){const e=this.stateMachines.indexOf(t);e>=0&&this.stateMachines.splice(e,1)}removeUpperStateMachine(t){const e=this.upperStateMachines.indexOf(t);e>=0&&this.upperStateMachines.splice(e,1)}playUpper(t,e={}){const o=e?.priority??1;o<this.currentUpperBodyPriority||(this.currentUpperBodyPriority=o,this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1,this.upperBodyTimer=0,this.upperBodyOverride=!0,this.overrideFadeTimeUpper=e.fadeTime)}play(t,e={}){J(null!=this.mixer,"Can't play animation before setup is called");const o=e.priority??1;o<this.currentFullBodyPriority||(this.currentFullBodyPriority=o,this.fullBodyTimer=0,this.upperBodyOverride||(this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1),this.fullBodyClip=t,this.fullBodyAction=this.transition(this.fullBodyAction,this.getFullBodyClip(t),e),this.fullBodyAction.timeScale=e?.timeScale??1,this.fullBodyAction.getClip().uuid==this.upperBodyAction.getClip().uuid&&this.upperBodyAction.syncWith(this.fullBodyAction),this.overrideFadeTime=e.fadeTime)}onActionDone(t){return new Promise(e=>{const o=i=>{i.action===t&&(e(i.action),this.mixer.removeEventListener("finished",o))};this.mixer.addEventListener("finished",o)})}transition(t,e,o={}){const i=o?.fadeTime??this.fadeTime;if(null!=t&&t.getClip().uuid===e.uuid)return!t.isRunning()&&!1===o.loop&&t.clampWhenFinished||t.isRunning()||(t.reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play(),!1===o.loop&&(t.setLoop(h,1),t.clampWhenFinished=!0)),t;if(t&&t.isRunning()){const s=t,r=this.mixer.clipAction(e);r.reset(),null!=o.offset&&(r.time=o.offset),r.play(),r.enabled=!0,r.setEffectiveTimeScale(1),r.weight=1,s.crossFadeTo(r,i,!0),t=r}else(t=this.mixer.clipAction(e)).reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play();return!1===o.loop&&(t.setLoop(h,1),t.clampWhenFinished=!0),t}};K=t([o({inEditor:!0})],K);export{K as CharacterAnimationComponent};function Y(t,e){if(null==t)return e;const o=e.clone(),i=new Set(t.map(t=>t.name));return o.tracks=o.tracks.filter(t=>i.has(t.name.split(".")[0])),o}function $(t){return t.flatMap(t=>function(t){const e=[];return t.traverse(t=>{e.push(t)}),e}(t)).filter(t=>t instanceof l)}function J(t,e){if(!1===t||"function"==typeof t&&!1===t())throw new Error(e)}function X(t,e){const o=new Map;return(i,...s)=>{const r=t(i);return o.has(r)||o.set(r,e(i,...s)),o.get(r)}}function Z(t){let e;return t.traverse(t=>{(t instanceof l||t.isBone)&&null==e&&(e=t)}),e}function tt(t,e){let o=e;for(;null!=o;){if(o===t)return!0;o=o.parent}return!1}/*
|
|
1
|
+
import{__decorate as t}from"tslib";import{ActorComponent as e,Component as o}from"../../../component.js";import{RootMotionClip as i}from"../../../../animation/root-motion.js";import{inject as s}from"../../../../inject.js";import{ViewController as r}from"../../../../services/render.js";import{AnimationMixer as n,Bone as l,Object3D as a,LoopOnce as h,Raycaster as c,Vector3 as d,SkinnedMesh as p,Skeleton as u,Quaternion as f,Matrix4 as g,MathUtils as y,ArrowHelper as m,Group as k}from"three";import{getCharacterMovementLike as S}from"./character-movement-like.js";import{CCDIKSolver as w}from"three/addons/animation/CCDIKSolver.js";import{World as b}from"../../../../services/world.js";import{PhysicsSystem as L,RayTestResult as v}from"../../../../services/physics/physics-system.js";import{ArrayMap as I}from"../../../../../utils/collections.js";import{getAnimationClipRigId as B,getAnimationRigChainBoneNames as P,getAnimationRigDataFromObject as x,getAnimationRigIdFromObject as R,setAnimationClipRigId as D}from"../../../../animation/retarget.js";const F=new d(0,-1,0),A=new d(0,1,0),M=new d(1,1,1),T=new f,W=new d,O=new d,V=new d,C=new d,E=new d,q=new f,H=new f,z=new f,N=new f,U=new g,j=new g,G=new d(1,0,0),Q=new d(0,1,0),$=new d(0,0,1);function _(){return{point:new d,normal:new d,hasNormal:!1}}const K=["mixamorigHips","Hips","hips","Pelvis","pelvis"],Y={upper:["LeftUpLeg","leftUpLeg","left_up_leg"],lower:["LeftLeg","leftLeg","left_leg"],foot:["LeftFoot","leftFoot","left_foot"]},J={upper:["RightUpLeg","rightUpLeg","right_up_leg"],lower:["RightLeg","rightLeg","right_leg"],foot:["RightFoot","rightFoot","right_foot"]},X=["left","right"];let Z=class extends e{constructor(){super(...arguments),this.viewController=s(r),this.poseProcessors=new I,this.stateMachines=[],this.upperStateMachines=[],this.fadeTime=.2,this.movementSpeed=null,this.upperBodyTimer=0,this.upperBodyOverride=!1,this.fullBodyTimer=0,this.currentFullBodyPriority=-1,this.currentUpperBodyPriority=-1,this.externalControlActive=!1,this.world=s(b),this.physicsSystem=s(L),this.footIkEnabled=!1,this.footIkMoving=!1,this.footIkRayStartHeight=.5,this.footIkRayLength=1.5,this.footIkRaycastMode="physics",this.footIkFootOffset=.08,this.footIkAutoContactOffset=!1,this.footIkPositionLerpSpeed=28,this.footIkRotationLerpSpeed=14,this.footIkWeightInSpeed=34,this.footIkWeightOutSpeed=30,this.footIkMaxSlopeAngleDeg=40,this.footIkForwardSampleDistance=.14,this.footIkSideSampleDistance=.08,this.footIkMaxHorizontalOffset=.22,this.footIkMaxVerticalOffsetDown=.75,this.footIkMaxVerticalOffsetUp=.2,this.footIkPelvisEnabled=!0,this.footIkPelvisBone="mixamorigHips",this.footIkPelvisLerpSpeed=24,this.footIkPelvisMaxOffsetDown=.4,this.footIkPelvisMaxOffsetUp=.08,this.footIkRequireGrounded=!0,this.footIkSimpleTiltOnly=!1,this.footIkDebug=!1,this.footIkDebugNormalLength=.25,this.footIkDebugAxisLength=.2,this.footIkExtraClearance=.01,this.footIkPlantReleaseUpSpeed=.08,this.footIkPlantReleaseExtension=.9,this.footIkPlantAttachExtension=.96,this.footIkPlantReleaseLift=.055,this.footIkPlantAttachLift=.02,this.leftLegIkBones={upperLeg:"mixamorigLeftUpLeg",lowerLeg:"mixamorigLeftLeg",foot:"mixamorigLeftFoot"},this.rightLegIkBones={upperLeg:"mixamorigRightUpLeg",lowerLeg:"mixamorigRightLeg",foot:"mixamorigRightFoot"},this.ikPelvisOffsetY=0,this.ikLegState={left:{targetPosition:new d,normal:new d(0,1,0),forward:new d(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new d,hasLastFootSample:!1,weight:0,top:!1},right:{targetPosition:new d,normal:new d(0,1,0),forward:new d(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new d,hasLastFootSample:!1,weight:0,top:!1}},this.ikWarnedMissingBones=!1,this.raycaster=new c,this.raycastIntersections=[],this.footIkRayTo=new d,this.footIkRayTestResult=new v,this.footIkRayTestOptions={excludeTriggers:!0,resolveActor:!1,collisionFilter:-2},this.footIkHits={center:_(),toe:_(),heel:_(),side:_()},this.footIkAxesScratch={up:new d,forward:new d,right:new d},this.footIkSampleAxesScratch={forward:new d,right:new d},this.footIkScratch={footWorldPos:new d,centerRayStart:new d,toeRayStart:new d,heelRayStart:new d,sideRayStart:new d,desiredTargetPos:new d,desiredNormal:new d,desiredForward:new d,debugRayEndA:new d,debugRayEndB:new d,debugRayEndC:new d,debugRayEndD:new d,debugRayEndE:new d,debugRayEndF:new d},this.ikPreSolve={left:{upper:new f,lower:new f,foot:new f},right:{upper:new f,lower:new f,foot:new f}},this.getFullBodyClip=it(t=>t.uuid,t=>tt(this.fullBodyMask,t)),this.getUpperBodyClip=it(t=>t.uuid,t=>tt(this.upperBodyMask,t))}onInit(){this.viewController.onUpdate(this.actor).subscribe(t=>this.updateInternal(t)),this.characterMovement=S(this.actor),this.footIkRayTestOptions.excludeActor=this.actor}onEndPlay(){this.disposeFootIkDebug()}raycastDownFirstValid(t,e,o){const i=this.world.scene;this.raycaster.set(t,F),this.raycaster.near=0,this.raycaster.far=e,this.raycastIntersections.length=0,this.raycaster.intersectObject(i,!0,this.raycastIntersections);for(const t of this.raycastIntersections)if(!(this.isFootIkDebugObject(t.object)||this.isRelatedToActorHierarchy(t.object)||this.isSkinnedMeshObject(t.object)))return o.point.copy(t.point),null!=t.face?(o.normal.copy(t.face.normal).transformDirection(t.object.matrixWorld).normalize(),o.hasNormal=!0):o.hasNormal=!1,this.raycastIntersections.length=0,!0;return this.raycastIntersections.length=0,!1}rayTestDown(t,e,o){const i=this.footIkRayTo.copy(t).addScaledVector(F,e),s=this.physicsSystem.rayTest(t,i,this.footIkRayTestResult,this.footIkRayTestOptions);return!!s.hasHit&&(o.point.copy(s.hitPoint),o.normal.copy(s.hitNormal),o.normal.lengthSq()>1e-8?(o.normal.normalize(),o.hasNormal=!0):o.hasNormal=!1,!0)}isRelatedToActorHierarchy(t){const e=this.actor?.object;return null!=e&&null!=t&&(rt(t,e)||rt(e,t))}isFootIkDebugObject(t){let e=t;for(;null!=e;){if(!0===e.userData?.footIkDebug)return!0;e=e.parent}return!1}isSkinnedMeshObject(t){let e=t;for(;null!=e;){if(e instanceof p||!0===e.isSkinnedMesh)return!0;e=e.parent}return!1}getRootMotionAction(){if(this.fullBodyAction.getClip()instanceof i)return this.fullBodyAction}getFullBodyAction(){return this.fullBodyAction}setup(t,e,o){null!=e&&(this.upperBodyMask=et(e),this.fullBodyMask=function(t,e){const o=new Set(et(e).map(t=>t.uuid)),i=[];return t.traverse(t=>{(t instanceof l||t.isBone)&&!o.has(t.uuid)&&i.push(t)}),i}(st(t),e)),null!=this.mixer&&this.mixer.stopAllAction(),this.mixer=new n(t),this.animationRigId=R(t),this.animationRigData=x(t),this.ikRoot=t,this.disposeFootIkDebug(),this.initializeFootIk(t,o),this.syncFootIkDebug()}updateStateMachines(t){this.stateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o&&this.play(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0})}),this.upperStateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o?this.playUpper(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0}):this.play(this.fullBodyClip)})}addPoseProcessor(t){const e={id:t.id,phase:t.phase??"afterMixer",order:t.order??0,update:t.update};this.removePoseProcessor(e.id);const o=this.poseProcessors.get(e.phase);let i=o.findIndex(t=>t.order>e.order);return-1===i&&(i=o.length),o.splice(i,0,e),()=>{const t=o.indexOf(e);-1!==t&&(o.splice(t,1),0===o.length&&this.poseProcessors.delete(e.phase))}}removePoseProcessor(t){for(const[e,o]of this.poseProcessors.entries()){const i=o.findIndex(e=>e.id===t);if(-1!==i)return o.splice(i,1),void(0===o.length&&this.poseProcessors.delete(e))}}runPoseProcessors(t,e){if(0===this.poseProcessors.size||null==this.mixer||!this.poseProcessors.present(t))return;const o=this.mixer.getRoot();if(!(o instanceof a))return;const i=this.poseProcessorContext??(this.poseProcessorContext={deltaTime:e,phase:t,animation:this,mixer:this.mixer,root:o});i.deltaTime=e,i.phase=t,i.mixer=this.mixer,i.root=o;const s=this.poseProcessors.get(t);for(let t=0;t<s.length;t++)s[t].update(i)}updateInternal(t){null!=this.mixer&&(this.externalControlActive||(this.upperBodyTimer+=t*(this.upperBodyAction?.timeScale??1),this.fullBodyTimer+=t*(this.fullBodyAction?.timeScale??1),this.upperBodyAction&&this.upperBodyOverride&&this.upperBodyAction.getClip().duration-2*(this.overrideFadeTimeUpper??this.fadeTime)<this.upperBodyTimer&&(this.upperBodyOverride=!1,null!=this.fullBodyClip&&this.transition(this.upperBodyAction,this.getUpperBodyClip(this.fullBodyClip)),this.upperBodyAction=null,this.overrideFadeTimeUpper=null),this.fullBodyAction&&this.fullBodyAction.loop===h&&this.fullBodyAction.getClip().duration-2*(this.overrideFadeTime??this.fadeTime)<this.fullBodyTimer&&(this.currentFullBodyPriority=-1,this.overrideFadeTime=null),null!=this.characterMovement&&(this.movementSpeed=this.characterMovement.horizontalSpeed),this.updateStateMachines(t),this.syncMovementSpeed(this.fullBodyAction),this.upperBodyOverride||this.syncMovementSpeed(this.upperBodyAction),this.runPoseProcessors("beforeMixer",t),this.mixer.update(t),this.runPoseProcessors("afterMixer",t),this.runPoseProcessors("beforeIk",t),this.updateFootIk(t),this.runPoseProcessors("afterIk",t)))}initializeFootIk(t,e){if(this.ikSolver=null,this.ikSkinnedMesh=null,this.ikBoneRefs=null,this.ikLegLengths=null,this.ikPelvisBone=null,this.ikPelvisOffsetY=0,this.ikFootAxes=null,this.ikTargetBones=null,this.ikWarnedMissingBones=!1,this.ikLegState.left.weight=0,this.ikLegState.right.weight=0,!this.footIkEnabled)return;const o=this.findFirstSkinnedMesh(t);if(null==o||null==o.skeleton)return;this.ikSkinnedMesh=o,t.updateWorldMatrix(!0,!0);const i=o.skeleton,s=P(this.animationRigData,"leftLeg"),r=P(this.animationRigData,"rightLeg"),n=this.resolveLegBoneRefs(i,this.leftLegIkBones,Y,s),a=this.resolveLegBoneRefs(i,this.rightLegIkBones,J,r);if(null==n||null==a)return void(this.ikWarnedMissingBones||(this.ikWarnedMissingBones=!0,console.warn(`[CharacterAnimationComponent] Foot IK disabled: missing leg bones. Left=${this.leftLegIkBones.upperLeg}/${this.leftLegIkBones.lowerLeg}/${this.leftLegIkBones.foot}, Right=${this.rightLegIkBones.upperLeg}/${this.rightLegIkBones.lowerLeg}/${this.rightLegIkBones.foot}`)));this.ikBoneRefs={left:n,right:a},this.ikLegLengths={left:this.computeLegLengths(n),right:this.computeLegLengths(a)},this.ikFootAxes={left:this.detectFootAxisBasis(n.foot),right:this.detectFootAxisBasis(a.foot)};const h=this.animationRigData?.bones.find(t=>t.id===this.animationRigData?.pelvisBoneId)?.name;this.ikPelvisBone=this.resolvePelvisBone(i,n,a,h),this.ikLegState.left.targetPosition.copy(n.foot.getWorldPosition(W)),this.ikLegState.right.targetPosition.copy(a.foot.getWorldPosition(W)),this.ikLegState.left.normal.set(0,1,0),this.ikLegState.right.normal.set(0,1,0),this.ikLegState.left.pitch=0,this.ikLegState.right.pitch=0,this.ikLegState.left.contactOffset=this.estimateFootContactOffset(i,"left",n.foot),this.ikLegState.right.contactOffset=this.estimateFootContactOffset(i,"right",a.foot),this.ikLegState.left.planted=!0,this.ikLegState.right.planted=!0,this.ikLegState.left.lastFootWorldPos.copy(n.foot.getWorldPosition(W)),this.ikLegState.right.lastFootWorldPos.copy(a.foot.getWorldPosition(W)),this.ikLegState.left.hasLastFootSample=!0,this.ikLegState.right.hasLastFootSample=!0;const c=this.resolveFootAxes("left",n.foot),d=this.resolveFootAxes("right",a.foot);this.ikLegState.left.forward.copy(c.forward),this.ikLegState.right.forward.copy(d.forward);const p=e??st(t);if(null==p)return;let f=i.getBoneByName("_IKTargetLeftFoot"),y=i.getBoneByName("_IKTargetRightFoot"),m=!1;if(null==f&&(f=new l,f.name="_IKTargetLeftFoot",p.add(f),m=!0),null==y&&(y=new l,y.name="_IKTargetRightFoot",p.add(y),m=!0),this.setBoneWorldPosition(f,n.foot.getWorldPosition(W)),this.setBoneWorldPosition(y,a.foot.getWorldPosition(W)),m||i.bones.indexOf(f)<0||i.bones.indexOf(y)<0){const t=i.bones.slice(),e=i.boneInverses.map(t=>t.clone());t.indexOf(f)<0&&(t.push(f),e.push((new g).copy(f.matrixWorld).invert())),t.indexOf(y)<0&&(t.push(y),e.push((new g).copy(y.matrixWorld).invert())),o.bind(new u(t,e),o.bindMatrix)}this.ikTargetBones={left:f,right:y};const k=o.skeleton.bones,S={target:k.indexOf(f),effector:k.indexOf(n.foot),links:[{index:k.indexOf(n.lower)},{index:k.indexOf(n.upper)}],iteration:8},b={target:k.indexOf(y),effector:k.indexOf(a.foot),links:[{index:k.indexOf(a.lower)},{index:k.indexOf(a.upper)}],iteration:8};if(S.target<0||S.effector<0||S.links.some(t=>t.index<0)||b.target<0||b.effector<0||b.links.some(t=>t.index<0))return console.warn("[CharacterAnimationComponent] Foot IK disabled: failed to resolve IK indexes in skeleton."),void(this.ikSolver=null);this.ikSolver=new w(o,[S,b])}findFirstSkinnedMesh(t){let e;return t.traverse(t=>{null==e&&(t instanceof p||!0===t.isSkinnedMesh)&&(e=t)}),e}resolveLegBoneRefs(t,e,o,i=[]){const s=this.findBoneByNames(t,[i[0],e.upperLeg,...o.upper].filter(t=>null!=t)),r=this.findBoneByNames(t,[i.at(-2),e.lowerLeg,...o.lower].filter(t=>null!=t)),n=this.findBoneByNames(t,[i.at(-1),e.foot,...o.foot].filter(t=>null!=t));if(null!=s&&null!=r&&null!=n)return{upper:s,lower:r,foot:n}}findBoneByNames(t,e){for(const o of e){const e=t.getBoneByName(o);if(null!=e)return e}}resolvePelvisBone(t,e,o,i){const s=this.findBoneByNames(t,[i,this.footIkPelvisBone,...K].filter(t=>null!=t));if(null!=s)return s;const r=e.upper.parent,n=o.upper.parent;return null!=r&&r===n&&(r instanceof l||!0===r.isBone)||null!=r&&(r instanceof l||!0===r.isBone)?r:void 0}computeLegLengths(t){const e=t.upper.getWorldPosition(W),o=t.lower.getWorldPosition(O),i=t.foot.getWorldPosition(V),s=e.distanceTo(o),r=o.distanceTo(i);return{upper:s,lower:r,total:s+r}}setBoneWorldPosition(t,e){const o=t.parent;null==o?t.position.copy(e):t.position.copy(o.worldToLocal(W.copy(e))),t.quaternion.copy(T),t.scale.copy(M),t.updateMatrixWorld(!0)}syncFootIkDebug(){this.footIkDebug&&null!=this.ikRoot?(null!=this.ikDebugRoot&&null!=this.ikDebugLegs||(this.ikDebugRoot=new k,this.ikDebugRoot.name="FootIKDebug",this.ikDebugRoot.userData.footIkDebug=!0,this.ikDebugLegs={left:this.createDebugLeg("LeftFootIK",53503),right:this.createDebugLeg("RightFootIK",16755200)},this.ikDebugRoot.add(this.ikDebugLegs.left.root,this.ikDebugLegs.right.root),this.world.scene.add(this.ikDebugRoot)),this.ikDebugRoot.visible=!0):null!=this.ikDebugRoot&&(this.ikDebugRoot.visible=!1)}disposeFootIkDebug(){null!=this.ikDebugRoot&&(this.ikDebugRoot.removeFromParent(),this.ikDebugRoot.clear()),this.ikDebugRoot=null,this.ikDebugLegs=null}createDebugLeg(t,e){const o=new k;o.name=t,o.userData.footIkDebug=!0;const i=this.createDebugArrow(e),s=this.createDebugArrow(e),r=this.createDebugArrow(e),n=this.createDebugArrow(e),l=this.createDebugArrow(65280),a=this.createDebugArrow(6750054),h=this.createDebugArrow(16711935),c=this.createDebugArrow(16776960),d=this.createDebugArrow(16737792);return o.add(i,s,r,n,l,a,h,c,d),{root:o,rayCenter:i,rayToe:s,rayHeel:r,raySide:n,hitNormal:l,footUp:a,footToTarget:h,solveUpper:c,solveLower:d}}createDebugArrow(t){const e=new m(A,new d,.001,t);return e.userData.footIkDebug=!0,e.traverse(t=>{t.userData.footIkDebug=!0,t.raycast=()=>{}}),e.visible=!1,e}setDebugArrow(t,e,o){W.subVectors(o,e);const i=W.length();i<1e-6?t.visible=!1:(t.visible=!0,t.position.copy(e),t.setDirection(W.multiplyScalar(1/i)),t.setLength(i,Math.min(.08,.25*i),Math.min(.05,.2*i)))}resetDebugVisibility(){null!=this.ikDebugLegs&&(this.ikDebugLegs.left.rayCenter.visible=!1,this.ikDebugLegs.left.rayToe.visible=!1,this.ikDebugLegs.left.rayHeel.visible=!1,this.ikDebugLegs.left.raySide.visible=!1,this.ikDebugLegs.left.hitNormal.visible=!1,this.ikDebugLegs.left.footUp.visible=!1,this.ikDebugLegs.left.footToTarget.visible=!1,this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.rayCenter.visible=!1,this.ikDebugLegs.right.rayToe.visible=!1,this.ikDebugLegs.right.rayHeel.visible=!1,this.ikDebugLegs.right.raySide.visible=!1,this.ikDebugLegs.right.hitNormal.visible=!1,this.ikDebugLegs.right.footUp.visible=!1,this.ikDebugLegs.right.footToTarget.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1)}updateFootIk(t){if(this.syncFootIkDebug(),!this.footIkEnabled||null==this.ikBoneRefs||null==this.ikRoot)return void this.resetDebugVisibility();if(!this.footIkMoving&&(this.movementSpeed??0)>.01)return this.resetFootIkState(),void this.resetDebugVisibility();this.ikRoot.updateWorldMatrix(!0,!0);const e=this.characterMovement?.isGrounded??!0;return this.footIkSimpleTiltOnly?(this.updatePelvisOffset(t,!1),this.updateLegTiltSimple("left",this.ikBoneRefs.left.foot,this.ikLegState.left,e,t),void this.updateLegTiltSimple("right",this.ikBoneRefs.right.foot,this.ikLegState.right,e,t)):null==this.ikLegLengths||null==this.ikTargetBones||null==this.ikSkinnedMesh||null==this.ikSolver?(this.updatePelvisOffset(t,!1),void this.resetDebugVisibility()):(this.updateLegIkTarget("left",this.ikBoneRefs.left,this.ikLegLengths.left,this.ikTargetBones.left,this.ikLegState.left,e,t),this.updateLegIkTarget("right",this.ikBoneRefs.right,this.ikLegLengths.right,this.ikTargetBones.right,this.ikLegState.right,e,t),this.updatePelvisOffset(t,!0),this.setBoneWorldPosition(this.ikTargetBones.left,this.ikLegState.left.targetPosition),this.setBoneWorldPosition(this.ikTargetBones.right,this.ikLegState.right.targetPosition),this.ikPreSolve.left.upper.copy(this.ikBoneRefs.left.upper.quaternion),this.ikPreSolve.left.lower.copy(this.ikBoneRefs.left.lower.quaternion),this.ikPreSolve.left.foot.copy(this.ikBoneRefs.left.foot.quaternion),this.ikPreSolve.right.upper.copy(this.ikBoneRefs.right.upper.quaternion),this.ikPreSolve.right.lower.copy(this.ikBoneRefs.right.lower.quaternion),this.ikPreSolve.right.foot.copy(this.ikBoneRefs.right.foot.quaternion),null!=this.ikDebugLegs&&(this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1),this.ikSolver.update(),this.blendLegAfterSolve(this.ikBoneRefs.left,this.ikPreSolve.left,this.ikLegState.left.weight),this.blendLegAfterSolve(this.ikBoneRefs.right,this.ikPreSolve.right,this.ikLegState.right.weight),this.applySimpleFootPitch("left",this.ikBoneRefs.left.foot,this.ikLegState.left),void this.applySimpleFootPitch("right",this.ikBoneRefs.right.foot,this.ikLegState.right))}resetFootIkState(){if(null!=this.ikBoneRefs){for(const t of X){const e=this.ikBoneRefs[t],o=this.ikLegState[t],i=e.foot.getWorldPosition(W);o.targetPosition.copy(i),o.lastFootWorldPos.copy(i),o.hasLastFootSample=!0,o.weight=0,o.normal.set(0,1,0);const s=this.resolveFootAxes(t,e.foot,this.footIkAxesScratch);o.forward.copy(s.forward),o.pitch=0,this.footIkAutoContactOffset?o.contactOffset=Math.max(o.contactOffset,this.footIkFootOffset+this.footIkExtraClearance):o.contactOffset=this.footIkFootOffset+this.footIkExtraClearance,o.planted=!1}this.ikPelvisOffsetY=0}}updatePelvisOffset(t,e){if(null==this.ikPelvisBone)return;let o=0;if(e&&this.footIkPelvisEnabled&&null!=this.ikBoneRefs&&null!=this.ikLegLengths){this.ikLegState.left.top=!1,this.ikLegState.right.top=!1;for(const t of X){const e=this.ikBoneRefs[t],i=this.ikLegLengths[t],s=this.ikLegState[t];if(!s.planted)continue;const r=e.upper.getWorldPosition(W),n=.998*i.total,l=this.computeRequiredPelvisDrop(r,s.targetPosition,n);if(l>0){const e="left"===t?"right":"left";this.ikLegState[e].top=!0}o=Math.min(o,-l)}o=y.clamp(o,-this.footIkPelvisMaxOffsetDown,this.footIkPelvisMaxOffsetUp)}const i=1-Math.exp(-this.footIkPelvisLerpSpeed*t);this.ikPelvisOffsetY=y.lerp(this.ikPelvisOffsetY,o,i);const s=this.ikPelvisBone.getWorldPosition(O),r=V.copy(s).addScaledVector(A,this.ikPelvisOffsetY);null==this.ikPelvisBone.parent?this.ikPelvisBone.position.copy(r):this.ikPelvisBone.position.copy(this.ikPelvisBone.parent.worldToLocal(r)),this.ikPelvisBone.updateMatrixWorld(!0)}computeFootVerticalSpeed(t,e,o){return!t.hasLastFootSample||o<=1e-6?0:(e.y-t.lastFootWorldPos.y)/o}updateFootSample(t,e){t.lastFootWorldPos.copy(e),t.hasLastFootSample=!0}estimateFootContactOffset(t,e,o){const i=this.footIkFootOffset+this.footIkExtraClearance;if(!this.footIkAutoContactOffset)return i;const s=t.bones.indexOf(o);if(s<0)return i;const r=t.boneInverses[s];if(null==r)return i;const n=this.ikFootAxes?.[e]??this.detectFootAxisBasis(o),a=o.getWorldPosition(O),h=V.copy(n.upLocal).applyQuaternion(o.getWorldQuaternion(q)).normalize();let c=0;return o.traverse(e=>{if(!(e instanceof l)||e===o)return;const i=t.bones.indexOf(e);if(i<0)return;const s=U.copy(t.boneInverses[i]).invert(),n=j.copy(r).multiply(s),d=W.setFromMatrixPosition(n),p=C.copy(d).applyMatrix4(o.matrixWorld).sub(a);c=Math.min(c,p.dot(h))}),Math.max(i,-c+this.footIkExtraClearance)}shouldPlantFoot(t,e,o,i,s,r){if(!e||!o)return!1;const n=this.footIkPlantReleaseExtension,l=this.footIkPlantAttachExtension,a=this.footIkPlantReleaseLift,h=this.footIkPlantAttachLift;return t.planted?!(i>this.footIkPlantReleaseUpSpeed&&(s<n||r>a)):s>=l&&r<=h||i<=0&&s>=n}computeLegExtensionRatio(t,e){const o=t.upper.getWorldPosition(W),i=t.foot.getWorldPosition(O),s=o.distanceTo(i);return e.total<=1e-6?1:y.clamp(s/e.total,0,1.2)}computeRequiredPelvisDrop(t,e,o){const i=C.subVectors(t,e),s=i.length();if(s<=o+1e-6)return 0;const r=i.dot(A),n=o*o-Math.max(s*s-r*r,0);if(n<=0)return s-o;const l=Math.sqrt(n),a=r-l,h=r+l;return a>=0?a:h>=0?h:0}updateLegTiltSimple(t,e,o,i,s){const r=this.footIkScratch,n=this.footIkHits,l=e.getWorldPosition(r.footWorldPos),a=this.computeFootVerticalSpeed(o,l,s),h=this.resolveFootAxes(t,e,this.footIkAxesScratch),c=this.resolveGroundSampleAxes(t,e,h,this.footIkSampleAxesScratch),d=r.centerRayStart.copy(l).addScaledVector(A,this.footIkRayStartHeight),p=r.toeRayStart.copy(d).addScaledVector(c.forward,this.footIkForwardSampleDistance),u=r.heelRayStart.copy(d).addScaledVector(c.forward,-this.footIkForwardSampleDistance),f=r.sideRayStart.copy(d).addScaledVector(c.right,this.footIkSideSampleDistance),g=this.getFirstGroundHit(d,n.center),m=this.getFirstGroundHit(p,n.toe),k=this.getFirstGroundHit(u,n.heel),S=this.getFirstGroundHit(f,n.side),w=r.desiredNormal.set(0,1,0),b=r.desiredForward.copy(c.forward);let L=0,v=0,I=this.footIkWeightOutSpeed;const B=null!=g||null!=m||null!=k;let P=l.y;null!=g?P=g.point.y:null!=m&&null!=k?P=.5*(m.point.y+k.point.y):null!=m?P=m.point.y:null!=k&&(P=k.point.y);const x=this.ikBoneRefs?.[t],R=this.ikLegLengths?.[t],D=null!=x&&null!=R?this.computeLegExtensionRatio(x,R):1,F=Math.max(0,l.y-P,l.y-o.targetPosition.y),M=this.shouldPlantFoot(o,!this.footIkRequireGrounded||i,B,a,D,F);M?this.computeDesiredGroundNormal(c,g,m,k,S,w)&&(this.computeDesiredFootForward(c.forward,c.right,w,m,k,b),L=this.computeDesiredPitchAngle(m,k,c.forward),v=1,I=this.footIkWeightInSpeed):this.computeDesiredFootForward(c.forward,c.right,w,null,null,b);const T=1-Math.exp(-this.footIkPositionLerpSpeed*s),W=1-Math.exp(-I*s);o.normal.lerp(w,T).normalize(),o.forward.lerp(b,T).normalize(),o.pitch=y.lerp(o.pitch,L,T),o.weight=y.lerp(o.weight,v,W),o.planted=M&&v>.001,this.updateFootSample(o,l),this.applySimpleFootPitch(t,e,o),this.updateLegDebug(t,{footWorldPos:l,centerRayStart:d,toeRayStart:p,heelRayStart:u,sideRayStart:f,centerHit:g,toeHit:m,heelHit:k,sideHit:S,desiredNormal:o.normal,desiredTargetPos:r.footWorldPos,weight:o.weight})}updateLegIkTarget(t,e,o,i,s,r,n){const l=this.footIkScratch,a=this.footIkHits,h=e.foot,c=h.getWorldPosition(l.footWorldPos),d=this.computeFootVerticalSpeed(s,c,n),p=this.resolveFootAxes(t,h,this.footIkAxesScratch),u=this.resolveGroundSampleAxes(t,h,p,this.footIkSampleAxesScratch),f=l.centerRayStart.copy(c).addScaledVector(A,this.footIkRayStartHeight),g=l.toeRayStart.copy(f).addScaledVector(u.forward,this.footIkForwardSampleDistance),m=l.heelRayStart.copy(f).addScaledVector(u.forward,-this.footIkForwardSampleDistance),k=l.sideRayStart.copy(f).addScaledVector(u.right,this.footIkSideSampleDistance),S=this.getFirstGroundHit(f,a.center),w=this.getFirstGroundHit(g,a.toe),b=this.getFirstGroundHit(m,a.heel),L=this.getFirstGroundHit(k,a.side),v=l.desiredTargetPos.copy(c),I=l.desiredNormal.set(0,1,0),B=l.desiredForward.copy(u.forward);let P=0,x=0,R=this.footIkWeightOutSpeed;const D=null!=S||null!=w||null!=b;let F=c.y;null!=S?F=S.point.y:null!=w&&null!=b?F=.5*(w.point.y+b.point.y):null!=w?F=w.point.y:null!=b&&(F=b.point.y);const M=this.computeLegExtensionRatio(e,o),T=Math.max(0,c.y-F,c.y-s.targetPosition.y),W=this.shouldPlantFoot(s,!this.footIkRequireGrounded||r,D,d,M,T);if(W){if(v.copy(c),this.computeDesiredGroundNormal(u,S,w,b,L,I)){this.computeDesiredFootForward(u.forward,u.right,I,w,b,B),P=this.computeDesiredPitchAngle(w,b,u.forward);const t=Math.max(0,Math.sin(P))*this.footIkForwardSampleDistance*.45,e=Math.max(this.footIkFootOffset+this.footIkExtraClearance,s.contactOffset);v.y=F+e+t,s.top&&(v.y-=this.ikPelvisOffsetY/2),x=1,R=this.footIkWeightInSpeed}}else this.computeDesiredFootForward(u.forward,u.right,I,null,null,B),v.copy(c),v.y+=this.footIkFootOffset;this.clampDesiredFootTarget(e,o,c,v);const O=1-Math.exp(-this.footIkPositionLerpSpeed*n),V=1-Math.exp(-R*n);s.targetPosition.lerp(v,O),s.normal.lerp(I,O).normalize(),s.forward.lerp(B,O).normalize(),s.pitch=y.lerp(s.pitch,P,O),s.weight=y.lerp(s.weight,x,V),s.planted=W&&x>.001,this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),this.updateLegDebug(t,{footWorldPos:c,centerRayStart:f,toeRayStart:g,heelRayStart:m,sideRayStart:k,centerHit:S,toeHit:w,heelHit:b,sideHit:L,desiredNormal:I,desiredTargetPos:s.targetPosition,weight:s.weight})}updateLegDebug(t,e){if(!this.footIkDebug||null==this.ikDebugLegs||null==this.ikBoneRefs)return;const o=this.ikDebugLegs[t],i=this.ikBoneRefs[t],s=this.resolveFootAxes(t,i.foot,this.footIkAxesScratch),r=this.footIkScratch,n=e.centerHit?.point??r.debugRayEndA.copy(e.centerRayStart).addScaledVector(F,this.footIkRayLength),l=e.toeHit?.point??r.debugRayEndB.copy(e.toeRayStart).addScaledVector(F,this.footIkRayLength),a=e.heelHit?.point??r.debugRayEndC.copy(e.heelRayStart).addScaledVector(F,this.footIkRayLength),h=e.sideHit?.point??r.debugRayEndD.copy(e.sideRayStart).addScaledVector(F,this.footIkRayLength);this.setDebugArrow(o.rayCenter,e.centerRayStart,n),this.setDebugArrow(o.rayToe,e.toeRayStart,l),this.setDebugArrow(o.rayHeel,e.heelRayStart,a),this.setDebugArrow(o.raySide,e.sideRayStart,h);const c=e.centerHit?.point??e.toeHit?.point??e.heelHit?.point??e.sideHit?.point??e.footWorldPos;this.setDebugArrow(o.hitNormal,c,r.debugRayEndE.copy(c).addScaledVector(e.desiredNormal,this.footIkDebugNormalLength)),this.setDebugArrow(o.footUp,e.footWorldPos,r.debugRayEndF.copy(e.footWorldPos).addScaledVector(s.up,this.footIkDebugAxisLength*(.15+e.weight))),this.setDebugArrow(o.footToTarget,e.footWorldPos,e.desiredTargetPos)}getFirstGroundHit(t,e){return"physics"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)?e:null:"physicsThenRender"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)||this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null:this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null}resolveGroundSampleAxes(t,e,o,i){const s=o??this.resolveFootAxes(t,e),r=i??{forward:new d,right:new d},n=this.actor.object.getWorldDirection(C);n.addScaledVector(A,-n.dot(A)),n.lengthSq()<1e-8&&n.copy(s.forward).addScaledVector(A,-s.forward.dot(A)),n.lengthSq()<1e-8&&n.set(0,0,1),n.normalize();const l=E.crossVectors(A,n);return l.lengthSq()<1e-8?l.copy(s.right):l.normalize(),l.dot(s.right)<0&&l.multiplyScalar(-1),r.forward.copy(n),r.right.copy(l),r}computeDesiredGroundNormal(t,e,o,i,s,r){const n=this.computeAverageHitNormal(e,o,i,s,C);let l=!1,a=0,h=0;if(null!=o&&null!=i){const e=O.subVectors(o.point,i.point).dot(t.forward);Math.abs(e)>1e-5&&(a=(o.point.y-i.point.y)/e,l=!0)}if(null!=s&&null!=e){const o=V.subVectors(s.point,e.point).dot(t.right);Math.abs(o)>1e-5&&(h=(s.point.y-e.point.y)/o,l=!0)}if(l&&(r.copy(A),r.addScaledVector(t.forward,-a),r.addScaledVector(t.right,-h),r.lengthSq()>1e-8?r.normalize():l=!1),l)null!=n&&r.dot(n)<0&&r.multiplyScalar(-1);else{if(null==n)return!1;r.copy(n),l=!0}return r.dot(A)<0&&r.multiplyScalar(-1),this.clampSlopeNormal(r),!0}computeAverageHitNormal(t,e,o,i,s){s.set(0,0,0);let r=0;return null!=t&&t.hasNormal&&(s.add(t.normal),r++),null!=e&&e.hasNormal&&(s.add(e.normal),r++),null!=o&&o.hasNormal&&(s.add(o.normal),r++),null!=i&&i.hasNormal&&(s.add(i.normal),r++),0===r||s.lengthSq()<1e-8?null:(s.multiplyScalar(1/r).normalize(),s)}computeDesiredFootForward(t,e,o,i,s,r){null!=i&&null!=s?r.subVectors(i.point,s.point):r.copy(t),r.addScaledVector(o,-r.dot(o)),r.lengthSq()<1e-8&&(r.copy(this.actor.object.getWorldDirection(C)),r.addScaledVector(o,-r.dot(o))),r.lengthSq()<1e-8&&r.crossVectors(o,e),r.lengthSq()<1e-8&&r.set(0,0,1),r.normalize();const n=C.copy(this.actor.object.getWorldDirection(C));n.addScaledVector(o,-n.dot(o)),n.lengthSq()<1e-8&&(n.copy(t),n.addScaledVector(o,-n.dot(o))),n.lengthSq()>1e-8&&n.normalize(),r.dot(n)<0&&r.multiplyScalar(-1)}computeDesiredPitchAngle(t,e,o){if(null==t||null==e)return 0;const i=O.subVectors(t.point,e.point),s=Math.abs(i.dot(o));if(s<1e-5)return 0;const r=t.point.y-e.point.y,n=y.degToRad(this.footIkMaxSlopeAngleDeg);return y.clamp(-Math.atan2(r,s),-n,n)}applySimpleFootPitch(t,e,o){if(null==e.parent||o.weight<=.001)return;const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o.pitch*o.weight;if(Math.abs(s)<=1e-5)return;const r=this.resolveLocalPitchSign(i),n=q.copy(e.quaternion),l=H.setFromAxisAngle(i.rightLocal,s*r);e.quaternion.copy(n.multiply(l))}resolveLocalPitchSign(t){const e=z.setFromAxisAngle(t.rightLocal,.15);return V.copy(t.upLocal).applyQuaternion(e).sub(t.upLocal).dot(t.forwardLocal)>=0?1:-1}clampDesiredFootTarget(t,e,o,i){const s=O.subVectors(i,o),r=s.dot(A),n=V.copy(s).addScaledVector(A,-r),l=n.length();l>this.footIkMaxHorizontalOffset&&l>1e-6&&n.multiplyScalar(this.footIkMaxHorizontalOffset/l);const a=y.clamp(r,-this.footIkMaxVerticalOffsetDown,this.footIkMaxVerticalOffsetUp);if(i.copy(o),i.add(n),i.addScaledVector(A,a),this.footIkPelvisEnabled)return;const h=t.upper.getWorldPosition(W).clone(),c=O.subVectors(i,h),d=c.length(),p=.995*e.total;d>p&&d>1e-6&&i.copy(h).addScaledVector(c,p/d)}solveTwoBoneLeg(t,e,o){const i=e.upper.getWorldPosition(W).clone(),s=e.lower.getWorldPosition(O).clone(),r=e.foot.getWorldPosition(V).clone(),n=i.distanceTo(s),l=s.distanceTo(r);if(n<1e-5||l<1e-5)return;const a=(new d).subVectors(o,i),h=a.length();if(h<1e-5)return;const c=Math.abs(n-l)+1e-4,p=n+l-1e-4,u=y.clamp(h,c,p),f=a.multiplyScalar(1/h),g=(new d).subVectors(s,i).normalize(),m=(new d).subVectors(r,s).normalize(),k=(new d).crossVectors(g,m);k.lengthSq()<1e-8&&(k.copy(this.actor.object.getWorldDirection(W).cross(A)),k.lengthSq()<1e-8&&k.set(1,0,0)),k.normalize();const S=(u*u+n*n-l*l)/(2*u),w=Math.max(n*n-S*S,0),b=Math.sqrt(w),L=(new d).copy(i).addScaledVector(f,S);let v=(new d).crossVectors(k,f);v.lengthSq()<1e-8&&(v=(new d).crossVectors(f,A),v.lengthSq()<1e-8&&(v=new d(1,0,0))),v.normalize();const I=(new d).copy(L).addScaledVector(v,b),B=(new d).copy(L).addScaledVector(v,-b),P=(new d).subVectors(s,L).dot(v)>=0?I:B;this.rotateBoneToward(e.upper,i,s,P),e.upper.updateMatrixWorld(!0);const x=e.lower.getWorldPosition(O),R=e.foot.getWorldPosition(V);this.rotateBoneToward(e.lower,x,R,o),e.lower.updateMatrixWorld(!0),this.updateSolveDebug(t,i,P,o)}updateSolveDebug(t,e,o,i){if(!this.footIkDebug||null==this.ikDebugLegs)return;const s=this.ikDebugLegs[t];this.setDebugArrow(s.solveUpper,e,o),this.setDebugArrow(s.solveLower,o,i)}rotateBoneToward(t,e,o,i){const s=(new d).subVectors(o,e),r=(new d).subVectors(i,e);if(s.lengthSq()<1e-10||r.lengthSq()<1e-10)return;if(s.normalize(),r.normalize(),s.dot(r)>.9999)return;const n=(new f).setFromUnitVectors(s,r),l=t.getWorldQuaternion(q),a=H.copy(n).multiply(l);if(null==t.parent)return void t.quaternion.copy(a);const h=t.parent.getWorldQuaternion(z),c=N.copy(h).invert().multiply(a);t.quaternion.copy(c)}detectFootAxisBasis(t){const e=t.getWorldQuaternion(q),o=this.actor.object.getWorldDirection(W);o.lengthSq()<1e-6&&o.set(0,0,1),o.normalize();const i=[{local:G.clone(),world:G.clone().applyQuaternion(e)},{local:Q.clone(),world:Q.clone().applyQuaternion(e)},{local:$.clone(),world:$.clone().applyQuaternion(e)}];i.push({local:G.clone().multiplyScalar(-1),world:G.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:Q.clone().multiplyScalar(-1),world:Q.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:$.clone().multiplyScalar(-1),world:$.clone().multiplyScalar(-1).applyQuaternion(e)});let s=i[0],r=-1/0;for(const t of i){const e=t.world.dot(A);e>r&&(r=e,s=t)}let n=i[0],l=-1/0;for(const t of i){if(Math.abs(t.world.dot(s.world))>.6)continue;const e=Math.abs(t.world.dot(o));e>l&&(l=e,n=t)}n.world.dot(o)<0&&(n={local:n.local.clone().multiplyScalar(-1),world:n.world.clone().multiplyScalar(-1)});const a=s.local.clone().normalize(),h=n.local.clone().normalize(),c=a.clone().cross(h).normalize();return{upLocal:a,forwardLocal:c.clone().cross(a).normalize(),rightLocal:c}}resolveFootAxes(t,e,o){const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o??{up:new d,forward:new d,right:new d},r=e.getWorldQuaternion(q);return s.up.copy(i.upLocal).applyQuaternion(r).normalize(),s.forward.copy(i.forwardLocal).applyQuaternion(r).normalize(),s.right.copy(i.rightLocal).applyQuaternion(r).normalize(),s}clampSlopeNormal(t){const e=y.clamp(t.dot(A),-1,1),o=Math.acos(e),i=y.degToRad(this.footIkMaxSlopeAngleDeg);if(o<=i||o<1e-5)return;const s=i/o;t.lerpVectors(A,t,s).normalize()}blendLegAfterSolve(t,e,o){if(!(o>=.999)){if(o<=.001)return t.upper.quaternion.copy(e.upper),t.lower.quaternion.copy(e.lower),void t.foot.quaternion.copy(e.foot);this.blendBoneQuaternion(t.upper,e.upper,o),this.blendBoneQuaternion(t.lower,e.lower,o),this.blendBoneQuaternion(t.foot,e.foot,o)}}blendBoneQuaternion(t,e,o){q.copy(t.quaternion),t.quaternion.copy(e).slerp(q,o)}alignFootToGroundNormal(t,e,o,i){if(o.weight<=.001||null==e.parent)return;const s=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),r=W.copy(o.normal).normalize(),n=O.copy(o.forward);if(n.addScaledVector(r,-n.dot(r)),n.lengthSq()<1e-8){const o=this.resolveFootAxes(t,e);n.copy(o.forward).addScaledVector(r,-o.forward.dot(r))}if(n.lengthSq()<1e-8)return;n.normalize();const l=V.crossVectors(r,n);if(l.lengthSq()<1e-8)return;l.normalize(),n.copy(C.crossVectors(l,r)).normalize(),U.makeBasis(s.rightLocal,s.upLocal,s.forwardLocal),q.setFromRotationMatrix(U),j.makeBasis(l,r,n),H.setFromRotationMatrix(j);const a=z.copy(H).multiply(N.copy(q).invert()),h=e.parent.getWorldQuaternion(N),c=H.copy(h).invert().multiply(a),d=(1-Math.exp(-this.footIkRotationLerpSpeed*i))*o.weight;e.quaternion.slerp(c,y.clamp(d,0,1))}getMixer(){return this.mixer}beginExternalControl(){this.externalControlActive=!0}endExternalControl(){this.externalControlActive=!1}stopSequenceAnimation(){this.currentFullBodyPriority=-1,this.externalControlActive=!1,null==this.fullBodyAction||this.fullBodyAction.isRunning()||(this.fullBodyAction=null),null==this.upperBodyAction||this.upperBodyAction.isRunning()||(this.upperBodyAction=null),this.fullBodyTimer=0}cancelRootMotionAction(t){null!=t&&this.fullBodyAction!==t||(this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.fullBodyAction?.stop(),this.fullBodyAction=null,this.fullBodyTimer=0)}beginExternalAnimationControl(t,e={}){ot(null!=this.mixer,"Can't begin external control before setup is called"),this.assertClipRigCompatible(t),this.mixer.stopAllAction(),this.currentFullBodyPriority=99,this.fullBodyTimer=0;const o=this.mixer.clipAction(t);return o.setLoop(h,1),o.clampWhenFinished=!0,o.timeScale=e.timeScale??1,o.reset(),o.play(),this.fullBodyAction=o,this.externalControlActive=!0,o}syncMovementSpeed(t){if(null!=t){const e=t.getClip();if(e instanceof i&&e.fixedInPlace&&null!=this.movementSpeed){t.timeScale=e.duration/e.displacement*this.movementSpeed;const o=this.mixer.getRoot();o instanceof a&&(t.timeScale/=o.scale.x)}}}playStateMachine(t){this.stateMachines.push(t)}playUpperStateMachine(t){this.upperStateMachines.push(t)}removeStateMachine(t){const e=this.stateMachines.indexOf(t);e>=0&&this.stateMachines.splice(e,1)}removeUpperStateMachine(t){const e=this.upperStateMachines.indexOf(t);e>=0&&this.upperStateMachines.splice(e,1)}playUpper(t,e={}){this.assertClipRigCompatible(t);const o=e?.priority??1;o<this.currentUpperBodyPriority||(this.currentUpperBodyPriority=o,this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1,this.upperBodyTimer=0,this.upperBodyOverride=!0,this.overrideFadeTimeUpper=e.fadeTime)}play(t,e={}){ot(null!=this.mixer,"Can't play animation before setup is called"),this.assertClipRigCompatible(t);const o=e.priority??1;o<this.currentFullBodyPriority||(this.currentFullBodyPriority=o,this.fullBodyTimer=0,this.upperBodyOverride||(this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1),this.fullBodyClip=t,this.fullBodyAction=this.transition(this.fullBodyAction,this.getFullBodyClip(t),e),this.fullBodyAction.timeScale=e?.timeScale??1,this.fullBodyAction.getClip().uuid==this.upperBodyAction.getClip().uuid&&this.upperBodyAction.syncWith(this.fullBodyAction),this.overrideFadeTime=e.fadeTime)}assertClipRigCompatible(t){const e=B(t);if(e===this.animationRigId||null==e&&null==this.animationRigId)return;const o=`${t.uuid}|${e??""}|${this.animationRigId??""}`;if(!nt.has(o)){if(nt.add(o),null==e||null==this.animationRigId)return void console.warn(`Animation clip "${t.name}" or its character has no rig assigned. Playing without rig compatibility validation for legacy compatibility.`);console.warn(`Animation clip "${t.name}" targets rig ${e??"no rig"}, but this character uses rig ${this.animationRigId??"no rig"}. Generate and assign a retargeted clip for the character rig.`)}}onActionDone(t){return new Promise(e=>{const o=i=>{i.action===t&&(e(i.action),this.mixer.removeEventListener("finished",o))};this.mixer.addEventListener("finished",o)})}transition(t,e,o={}){const i=o?.fadeTime??this.fadeTime;if(null!=t&&t.getClip().uuid===e.uuid)return!t.isRunning()&&!1===o.loop&&t.clampWhenFinished||t.isRunning()||(t.reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play(),!1===o.loop&&(t.setLoop(h,1),t.clampWhenFinished=!0)),t;if(t&&t.isRunning()){const s=t,r=this.mixer.clipAction(e);r.reset(),null!=o.offset&&(r.time=o.offset),r.play(),r.enabled=!0,r.setEffectiveTimeScale(1),r.weight=1,s.crossFadeTo(r,i,!0),t=r}else(t=this.mixer.clipAction(e)).reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play();return!1===o.loop&&(t.setLoop(h,1),t.clampWhenFinished=!0),t}};Z=t([o({inEditor:!0})],Z);export{Z as CharacterAnimationComponent};function tt(t,e){if(null==t)return e;const o=e.clone();D(o,B(e));const i=new Set(t.map(t=>t.name));return o.tracks=o.tracks.filter(t=>i.has(t.name.split(".")[0])),o}function et(t){return t.flatMap(t=>function(t){const e=[];return t.traverse(t=>{e.push(t)}),e}(t)).filter(t=>t instanceof l)}function ot(t,e){if(!1===t||"function"==typeof t&&!1===t())throw new Error(e)}function it(t,e){const o=new Map;return(i,...s)=>{const r=t(i);return o.has(r)||o.set(r,e(i,...s)),o.get(r)}}function st(t){let e;return t.traverse(t=>{(t instanceof l||t.isBone)&&null==e&&(e=t)}),e}function rt(t,e){let o=e;for(;null!=o;){if(o===t)return!0;o=o.parent}return!1}const nt=new Set;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -8,6 +8,7 @@ export type CharacterMovementLike = {
|
|
|
8
8
|
readonly directionInput: AxisInput;
|
|
9
9
|
readonly jumpInput: ActionInput;
|
|
10
10
|
readonly sprintInput: ActionInput;
|
|
11
|
+
readonly crouchInput?: ActionInput;
|
|
11
12
|
readonly rotationInput: RotationInput;
|
|
12
13
|
horizontalSpeed: number;
|
|
13
14
|
readonly velocity: Vector3;
|
|
@@ -15,6 +16,7 @@ export type CharacterMovementLike = {
|
|
|
15
16
|
enabled: boolean;
|
|
16
17
|
allowRootMotionJumpCancel: boolean;
|
|
17
18
|
isGrounded: boolean;
|
|
19
|
+
isCrouching?: boolean;
|
|
18
20
|
applyImpulse(impulse: Vector3): void;
|
|
19
21
|
setRootMotionAction(action: AnimationAction, options?: RootMotionActionOptions): void;
|
|
20
22
|
readonly isDefaultCharacterMovementComponent?: boolean;
|