@hology/core 0.0.187 → 0.0.190
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/audio-parameters.d.ts +25 -0
- package/dist/effects/sequence/audio-parameters.js +4 -0
- package/dist/effects/sequence/index.d.ts +8 -0
- package/dist/effects/sequence/index.js +4 -0
- package/dist/effects/sequence/sequence-action.d.ts +17 -0
- package/dist/effects/sequence/sequence-action.js +4 -0
- package/dist/effects/sequence/sequence-actor.d.ts +135 -0
- package/dist/effects/sequence/sequence-actor.js +4 -0
- package/dist/effects/sequence/sequence-data.d.ts +458 -0
- package/dist/effects/sequence/sequence-data.js +4 -0
- package/dist/effects/sequence/sequence-event.d.ts +37 -0
- package/dist/effects/sequence/sequence-event.js +4 -0
- package/dist/effects/sequence/sequence-player.d.ts +323 -0
- package/dist/effects/sequence/sequence-player.js +4 -0
- package/dist/effects/sequence/sequencer.d.ts +7 -0
- package/dist/effects/sequence/sequencer.js +4 -0
- package/dist/effects/vfx/trail-renderer.d.ts +19 -15
- package/dist/effects/vfx/trail-renderer.js +1 -1
- package/dist/gameplay/actors/actor.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +151 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-movement.d.ts +4 -0
- package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
- package/dist/gameplay/actors/builtin/components/mesh-component.js +1 -1
- package/dist/gameplay/actors/builtin/index.d.ts +5 -0
- package/dist/gameplay/actors/builtin/index.js +1 -1
- package/dist/gameplay/actors/camera/third-person-camera-component.d.ts +6 -0
- package/dist/gameplay/actors/camera/third-person-camera-component.js +1 -1
- package/dist/gameplay/actors/factory.d.ts +3 -0
- package/dist/gameplay/actors/factory.js +1 -1
- package/dist/gameplay/actors/internal/component-init.d.ts +1 -0
- package/dist/gameplay/actors/internal/component-init.js +1 -1
- package/dist/gameplay/index.d.ts +1 -1
- package/dist/gameplay/index.js +1 -1
- package/dist/gameplay/initiate.js +1 -1
- package/dist/gameplay/input/input-service.d.ts +16 -2
- package/dist/gameplay/input/input-service.js +1 -1
- package/dist/gameplay/input/input.d.ts +3 -0
- package/dist/gameplay/input/input.js +1 -1
- package/dist/gameplay/input/keybind.d.ts +37 -0
- package/dist/gameplay/input/keybind.js +1 -1
- package/dist/gameplay/services/asset-loader.d.ts +4 -1
- package/dist/gameplay/services/asset-loader.js +1 -1
- package/dist/gameplay/services/physics/physics-system.d.ts +7 -1
- package/dist/gameplay/services/physics/physics-system.js +1 -1
- package/dist/gameplay/services/render.js +1 -1
- package/dist/gameplay/services/world.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/rendering/fog/fog-volume-actor.d.ts +1 -1
- package/dist/rendering/fog/fog-volume-actor.js +1 -1
- package/dist/rendering/fog/volumetric-fog-pass.d.ts +1 -0
- package/dist/rendering/fog/volumetric-fog-pass.js +1 -1
- package/dist/rendering/light-probes/light-probe-volume-actor.d.ts +14 -0
- package/dist/rendering/light-probes/light-probe-volume-actor.js +4 -0
- package/dist/rendering/light-probes/light-volume-capture.d.ts +16 -0
- package/dist/rendering/light-probes/light-volume-capture.js +4 -0
- package/dist/rendering.d.ts +5 -1
- package/dist/rendering.js +1 -1
- package/dist/scene/asset-resource-loader.d.ts +11 -1
- package/dist/scene/asset-resource-loader.js +1 -1
- package/dist/scene/bootstrap.d.ts +1 -2
- package/dist/scene/collision/collision-shape-import.js +1 -1
- package/dist/scene/landscape/landscape.d.ts +1 -1
- package/dist/scene/landscape/landscape.js +1 -1
- package/dist/scene/materializer.d.ts +4 -1
- package/dist/scene/materializer.js +1 -1
- package/dist/scene/model.d.ts +21 -3
- package/dist/scene/model.js +1 -1
- package/dist/scene/runtime-backend-service.js +1 -1
- package/dist/scene/storage/storage.d.ts +3 -2
- package/dist/scene/storage/storage.js +1 -1
- package/dist/shader/builtin/index.js +1 -1
- package/dist/shader/color-layer.d.ts +7 -2
- package/dist/shader/color-layer.js +1 -1
- package/dist/shader/parameter.d.ts +3 -2
- package/dist/shader/shader.d.ts +4 -2
- package/dist/shader/sprite-shader.d.ts +13 -3
- package/dist/shader/sprite-shader.js +1 -1
- package/dist/shader-nodes/dither.d.ts +8 -0
- package/dist/shader-nodes/dither.js +4 -0
- package/dist/shader-nodes/glsl-node.d.ts +1 -1
- package/dist/shader-nodes/index.d.ts +1 -0
- package/dist/shader-nodes/index.js +1 -1
- package/dist/test/injection.test.js +1 -1
- package/dist/utils/three/outline-pass.js +1 -1
- package/package.json +4 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { Type } from '../../utils/type.js';
|
|
3
|
+
/**
|
|
4
|
+
* Metadata about a sequence event method on an actor
|
|
5
|
+
*/
|
|
6
|
+
export interface SequenceEventInfo {
|
|
7
|
+
/** Display name shown in the editor */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Actual method name on the actor class */
|
|
10
|
+
methodName: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Decorator to mark an actor method as callable from sequences.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* class Character extends BaseActor {
|
|
17
|
+
* @SequenceEvent('Attack End')
|
|
18
|
+
* onAttackEnd() {
|
|
19
|
+
* // Handle attack end
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* @SequenceEvent('Hit')
|
|
23
|
+
* onSequenceHit() {
|
|
24
|
+
* // Handle hit event
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
export declare function SequenceEvent(displayName?: string): (target: Object, methodName: string) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Extract all sequence events from an actor class and its parent classes.
|
|
31
|
+
* This is used by the editor to populate the event function dropdown.
|
|
32
|
+
*
|
|
33
|
+
* @param actorType - The actor class to extract events from
|
|
34
|
+
* @returns Array of sequence event info objects
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractSequenceEvents(actorType: Type<any>): SequenceEventInfo[];
|
|
37
|
+
//# sourceMappingURL=sequence-event.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import"reflect-metadata";import{ArrayMap as e}from"../../utils/collections.js";const t=new e;export function SequenceEvent(e){return function(n,o){null!=n&&t.push(n.constructor,{name:e??o?.replace(/^on/,""),methodName:o})}}export function extractSequenceEvents(e){if(null==e)return[];const n=t.get(e)||[];return[...extractSequenceEvents(Object.getPrototypeOf(e)).filter(e=>n.every(t=>t.methodName!==e.methodName)),...n]}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { Subject } from 'rxjs';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { AnimationAction, AnimationMixer, Euler, Object3D, Vector3 } from 'three';
|
|
4
|
+
import { VfxService } from '../../effects/vfx/vfx-service.js';
|
|
5
|
+
import { BaseActor } from '../../gameplay/actors/actor.js';
|
|
6
|
+
import { ActorFactory } from '../../gameplay/actors/factory.js';
|
|
7
|
+
import { AssetLoader } from '../../gameplay/services/asset-loader.js';
|
|
8
|
+
import { World } from '../../gameplay/services/world.js';
|
|
9
|
+
import { PrefabInstance } from '../../scene/objects/prefab.js';
|
|
10
|
+
import { SequenceData, SequenceRole, SequenceTrack } from './sequence-data.js';
|
|
11
|
+
import { CharacterAnimationComponent } from '../../gameplay/actors/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* Playback state of the sequence
|
|
14
|
+
*/
|
|
15
|
+
export declare enum SequencePlaybackState {
|
|
16
|
+
Stopped = "stopped",
|
|
17
|
+
Playing = "playing",
|
|
18
|
+
Paused = "paused"
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Interface for targets that can play animations
|
|
22
|
+
*/
|
|
23
|
+
export interface AnimationPlayable {
|
|
24
|
+
/** The target object (actor, object3d, etc.) */
|
|
25
|
+
target: Object3D | BaseActor | null;
|
|
26
|
+
/** Animation mixer for skeletal animations on this target */
|
|
27
|
+
mixer?: AnimationMixer;
|
|
28
|
+
/** Currently active animation actions */
|
|
29
|
+
activeActions: Map<string, AnimationAction>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Binding between a track and its runtime target
|
|
33
|
+
*/
|
|
34
|
+
export interface TrackBinding extends AnimationPlayable {
|
|
35
|
+
track: SequenceTrack;
|
|
36
|
+
/** Original transform before sequence started (for restoration) */
|
|
37
|
+
originalTransform?: {
|
|
38
|
+
position: Vector3;
|
|
39
|
+
rotation: Euler;
|
|
40
|
+
scale: Vector3;
|
|
41
|
+
};
|
|
42
|
+
/** The parent object before any parenting sequences applied */
|
|
43
|
+
originalParent?: Object3D | null;
|
|
44
|
+
/** The current parent object assigned by sequence parenting */
|
|
45
|
+
currentParent?: Object3D;
|
|
46
|
+
/**
|
|
47
|
+
* The CharacterAnimationComponent (if any) associated with this binding's target.
|
|
48
|
+
* Stored so we can call lifecycle methods (beginExternalControl / stopSequenceAnimation)
|
|
49
|
+
* without re-querying the actor every frame.
|
|
50
|
+
*/
|
|
51
|
+
charAnimComponent?: CharacterAnimationComponent;
|
|
52
|
+
/**
|
|
53
|
+
* Set of actionKeys (from activeActions) whose AnimationAction lives inside
|
|
54
|
+
* charAnimComponent's mixer rather than our own binding.mixer.
|
|
55
|
+
*/
|
|
56
|
+
charAnimActionKeys?: Set<string>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Role bindings map - binds predefined roles to runtime targets
|
|
60
|
+
*/
|
|
61
|
+
export type RoleBindings = Map<SequenceRole, BaseActor | Object3D>;
|
|
62
|
+
/**
|
|
63
|
+
* Spawned instance tracking
|
|
64
|
+
*/
|
|
65
|
+
export type SpawnedInstance = ({
|
|
66
|
+
type: 'actor';
|
|
67
|
+
instance: BaseActor;
|
|
68
|
+
} | {
|
|
69
|
+
type: 'prefab';
|
|
70
|
+
instance: PrefabInstance;
|
|
71
|
+
} | {
|
|
72
|
+
type: 'mesh';
|
|
73
|
+
instance: Object3D;
|
|
74
|
+
} | {
|
|
75
|
+
type: 'proxy';
|
|
76
|
+
target: BaseActor | Object3D;
|
|
77
|
+
}) & Partial<AnimationPlayable>;
|
|
78
|
+
/**
|
|
79
|
+
* SequencePlayer is the runtime system that plays a sequence.
|
|
80
|
+
* It manages track bindings, playback state, and updates.
|
|
81
|
+
*/
|
|
82
|
+
export declare class SequencePlayer {
|
|
83
|
+
private _state;
|
|
84
|
+
private _time;
|
|
85
|
+
private _duration;
|
|
86
|
+
private _timescale;
|
|
87
|
+
private _loop;
|
|
88
|
+
private _externalTimeControl;
|
|
89
|
+
private bindings;
|
|
90
|
+
private roleBindings;
|
|
91
|
+
private sequenceData;
|
|
92
|
+
readonly onComplete: Subject<void>;
|
|
93
|
+
readonly onLoop: Subject<number>;
|
|
94
|
+
readonly onTimeUpdate: import("rxjs").Observable<number>;
|
|
95
|
+
readonly onStateChange: import("rxjs").Observable<SequencePlaybackState>;
|
|
96
|
+
private loopCount;
|
|
97
|
+
private sequenceCamera;
|
|
98
|
+
private originalCamera;
|
|
99
|
+
private audioListener;
|
|
100
|
+
private audioBuffers;
|
|
101
|
+
private loadingAudioBuffers;
|
|
102
|
+
private animationClips;
|
|
103
|
+
private loadingAnimationClips;
|
|
104
|
+
private audioByClip;
|
|
105
|
+
private activeAudioClips;
|
|
106
|
+
private audioGainNodes;
|
|
107
|
+
private audioStartInFlight;
|
|
108
|
+
private lastAudioStartAttemptTime;
|
|
109
|
+
private readonly audioStartRetryIntervalSeconds;
|
|
110
|
+
private world;
|
|
111
|
+
private assetLoader;
|
|
112
|
+
private actorFactory;
|
|
113
|
+
private vfxService;
|
|
114
|
+
private spawnedInstances;
|
|
115
|
+
private activeClips;
|
|
116
|
+
private pendingSpawns;
|
|
117
|
+
private vfxActors;
|
|
118
|
+
private activeVfxClips;
|
|
119
|
+
private clearCounter;
|
|
120
|
+
private audioFilters;
|
|
121
|
+
private audioPannerNodes;
|
|
122
|
+
private firedEvents;
|
|
123
|
+
constructor();
|
|
124
|
+
/**
|
|
125
|
+
* Current playback state
|
|
126
|
+
*/
|
|
127
|
+
get state(): SequencePlaybackState;
|
|
128
|
+
/**
|
|
129
|
+
* Current playback time in seconds
|
|
130
|
+
*/
|
|
131
|
+
get time(): number;
|
|
132
|
+
/**
|
|
133
|
+
* Total duration of the sequence
|
|
134
|
+
*/
|
|
135
|
+
get duration(): number;
|
|
136
|
+
/**
|
|
137
|
+
* Playback rate multiplier
|
|
138
|
+
*/
|
|
139
|
+
get timescale(): number;
|
|
140
|
+
set timescale(value: number);
|
|
141
|
+
/**
|
|
142
|
+
* Whether the sequence loops
|
|
143
|
+
*/
|
|
144
|
+
get loop(): boolean;
|
|
145
|
+
set loop(value: boolean);
|
|
146
|
+
/**
|
|
147
|
+
* When true, the update() method will not advance time internally.
|
|
148
|
+
* Time must be controlled externally via seek().
|
|
149
|
+
* This is useful for editor preview where the UI controls playback position.
|
|
150
|
+
*/
|
|
151
|
+
get externalTimeControl(): boolean;
|
|
152
|
+
set externalTimeControl(value: boolean);
|
|
153
|
+
/**
|
|
154
|
+
* Set world dependency
|
|
155
|
+
*/
|
|
156
|
+
setWorld(world: World): void;
|
|
157
|
+
setAudioListener(audioListener: THREE.AudioListener): void;
|
|
158
|
+
/**
|
|
159
|
+
* Set asset loader dependency
|
|
160
|
+
*/
|
|
161
|
+
setAssetLoader(assetLoader: AssetLoader): void;
|
|
162
|
+
/**
|
|
163
|
+
* Set actor factory dependency
|
|
164
|
+
*/
|
|
165
|
+
setActorFactory(actorFactory: ActorFactory): void;
|
|
166
|
+
/**
|
|
167
|
+
* Set VFX service dependency
|
|
168
|
+
*/
|
|
169
|
+
setVfxService(vfxService: VfxService): void;
|
|
170
|
+
/**
|
|
171
|
+
* Load sequence data and prepare for playback
|
|
172
|
+
*/
|
|
173
|
+
load(data: SequenceData): void;
|
|
174
|
+
private getAudioAssetIdsForSequence;
|
|
175
|
+
private preloadAudioBuffersForSequence;
|
|
176
|
+
/**
|
|
177
|
+
* Bind a role to a runtime target.
|
|
178
|
+
* This allows tracks with a role to target a different actor at runtime.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* player.bindRole(SequenceRole.Self, playerActor)
|
|
182
|
+
* player.bindRole(SequenceRole.Target, enemyActor)
|
|
183
|
+
*/
|
|
184
|
+
bindRole(role: SequenceRole, target: BaseActor | Object3D): void;
|
|
185
|
+
/**
|
|
186
|
+
* Bind a scene object by ID to a target
|
|
187
|
+
*/
|
|
188
|
+
bindObject(objectId: string, target: Object3D | BaseActor): void;
|
|
189
|
+
/**
|
|
190
|
+
* Resolve all track bindings based on current proxy bindings and scene references
|
|
191
|
+
*/
|
|
192
|
+
private resolveBindings;
|
|
193
|
+
private resolveTrackBinding;
|
|
194
|
+
private createBinding;
|
|
195
|
+
/**
|
|
196
|
+
* Start or resume playback
|
|
197
|
+
*/
|
|
198
|
+
play(): void;
|
|
199
|
+
/**
|
|
200
|
+
* Pause playback
|
|
201
|
+
*/
|
|
202
|
+
pause(): void;
|
|
203
|
+
private pauseAudio;
|
|
204
|
+
private pauseVfx;
|
|
205
|
+
private resumeVfx;
|
|
206
|
+
/**
|
|
207
|
+
* Stop playback and reset to beginning
|
|
208
|
+
*/
|
|
209
|
+
stop(): void;
|
|
210
|
+
/**
|
|
211
|
+
* Restart playback from the beginning
|
|
212
|
+
*/
|
|
213
|
+
restart(): void;
|
|
214
|
+
/**
|
|
215
|
+
* Seek to a specific time
|
|
216
|
+
*/
|
|
217
|
+
seek(time: number): void;
|
|
218
|
+
/**
|
|
219
|
+
* Update the sequence player - should be called every frame
|
|
220
|
+
*/
|
|
221
|
+
update(deltaTime: number): void;
|
|
222
|
+
/**
|
|
223
|
+
* Evaluate all tracks at the given time
|
|
224
|
+
*/
|
|
225
|
+
private evaluate;
|
|
226
|
+
private evaluateTrack;
|
|
227
|
+
private evaluateObjectTrack;
|
|
228
|
+
private evaluateSubTracks;
|
|
229
|
+
private evaluateTransformSubTrack;
|
|
230
|
+
private evaluateAnimationSubTrack;
|
|
231
|
+
private evaluateVisibilitySubTrack;
|
|
232
|
+
private evaluatePropertySubTrack;
|
|
233
|
+
private evaluateEventSubTrack;
|
|
234
|
+
private evaluateCameraTrack;
|
|
235
|
+
private evaluateAudioTrack;
|
|
236
|
+
private tryStartAudioClip;
|
|
237
|
+
private applyAudioParameters;
|
|
238
|
+
private updatePositionalAudioTransform;
|
|
239
|
+
/**
|
|
240
|
+
* Play an audio clip
|
|
241
|
+
*/
|
|
242
|
+
private playAudioClip;
|
|
243
|
+
/**
|
|
244
|
+
* Stop an audio clip
|
|
245
|
+
*/
|
|
246
|
+
private stopAudioClip;
|
|
247
|
+
/**
|
|
248
|
+
* Stop all audio clips
|
|
249
|
+
*/
|
|
250
|
+
private stopAllAudio;
|
|
251
|
+
private evaluateVfxTrack;
|
|
252
|
+
/**
|
|
253
|
+
* Ensure VFX actor exists for a clip (create if needed, reuse if exists)
|
|
254
|
+
*/
|
|
255
|
+
private ensureVfxActor;
|
|
256
|
+
/**
|
|
257
|
+
* Get transform for VFX actor from track or transform sub-track
|
|
258
|
+
*/
|
|
259
|
+
private getVfxTransform;
|
|
260
|
+
/**
|
|
261
|
+
* Update VFX actor transform during playback
|
|
262
|
+
*/
|
|
263
|
+
private updateVfxTransform;
|
|
264
|
+
/**
|
|
265
|
+
* Resolve actor type from string name
|
|
266
|
+
*/
|
|
267
|
+
private resolveActorType;
|
|
268
|
+
/**
|
|
269
|
+
* Spawn an actor for a clip
|
|
270
|
+
*/
|
|
271
|
+
private spawnActorForClip;
|
|
272
|
+
/**
|
|
273
|
+
* Spawn a prefab for a clip
|
|
274
|
+
*/
|
|
275
|
+
private spawnPrefabForClip;
|
|
276
|
+
/**
|
|
277
|
+
* Spawn a mesh for a clip
|
|
278
|
+
*/
|
|
279
|
+
private spawnMeshForClip;
|
|
280
|
+
/**
|
|
281
|
+
* Despawn an instance
|
|
282
|
+
*/
|
|
283
|
+
private despawnInstance;
|
|
284
|
+
/**
|
|
285
|
+
* Despawn all instances
|
|
286
|
+
*/
|
|
287
|
+
private despawnAll;
|
|
288
|
+
/**
|
|
289
|
+
* Get initial transform from transform sub-track or clip defaults
|
|
290
|
+
*/
|
|
291
|
+
private getInitialTransform;
|
|
292
|
+
private evaluateSpawnTrack;
|
|
293
|
+
/**
|
|
294
|
+
* Get Object3D from spawned instance
|
|
295
|
+
*/
|
|
296
|
+
private getInstanceObject;
|
|
297
|
+
/**
|
|
298
|
+
* Spawn for clip (async wrapper)
|
|
299
|
+
*/
|
|
300
|
+
private spawnForClip;
|
|
301
|
+
private findKeyframes;
|
|
302
|
+
private applyInterpolation;
|
|
303
|
+
private ensureAnimationClip;
|
|
304
|
+
private captureOriginalTransforms;
|
|
305
|
+
private restoreOriginalTransforms;
|
|
306
|
+
private stopAllAnimations;
|
|
307
|
+
/**
|
|
308
|
+
* Stop all VFX actors (but don't remove from world - they're reused)
|
|
309
|
+
*/
|
|
310
|
+
private stopAllVfx;
|
|
311
|
+
/**
|
|
312
|
+
* Dispose of all resources
|
|
313
|
+
*/
|
|
314
|
+
dispose(): void;
|
|
315
|
+
private updateTrackParenting;
|
|
316
|
+
private findSocket;
|
|
317
|
+
private getActiveObjectsForTrack;
|
|
318
|
+
private restoreObjectParent;
|
|
319
|
+
private restoreOriginalParents;
|
|
320
|
+
private evaluateAudioSubTracks;
|
|
321
|
+
private stringToRandom;
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=sequence-player.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{BehaviorSubject as t,Subject as e}from"rxjs";import*as i from"three";import{AnimationMixer as s,Euler as a,Quaternion as o,Vector3 as n}from"three";import{BaseActor as r}from"../../gameplay/actors/actor.js";import c from"../../gameplay/actors/builtin/index.js";import{getAudioParameterDefinition as l}from"./audio-parameters.js";import{SkeletonUtils as u}from"three-stdlib";import{CharacterAnimationComponent as h,CharacterMovementComponent as d}from"../../gameplay/actors/index.js";import{RootMotionClip as p}from"../../gameplay/animation/root-motion.js";export var SequencePlaybackState;!function(t){t.Stopped="stopped",t.Playing="playing",t.Paused="paused"}(SequencePlaybackState||(SequencePlaybackState={}));export class SequencePlayer{constructor(){this._state=new t(SequencePlaybackState.Stopped),this._time=new t(0),this._duration=0,this._timescale=1,this._loop=!1,this._externalTimeControl=!1,this.bindings=new Map,this.roleBindings=new Map,this.sequenceData=null,this.onComplete=new e,this.onLoop=new e,this.onTimeUpdate=this._time.asObservable(),this.onStateChange=this._state.asObservable(),this.loopCount=0,this.sequenceCamera=null,this.originalCamera=null,this.audioListener=null,this.audioBuffers=new Map,this.loadingAudioBuffers=new Map,this.animationClips=new Map,this.loadingAnimationClips=new Set,this.audioByClip=new Map,this.activeAudioClips=new Set,this.audioGainNodes=new Map,this.audioStartInFlight=new Map,this.lastAudioStartAttemptTime=new Map,this.audioStartRetryIntervalSeconds=.25,this.world=null,this.assetLoader=null,this.actorFactory=null,this.vfxService=null,this.spawnedInstances=new Map,this.activeClips=new Set,this.pendingSpawns=new Set,this.vfxActors=new Map,this.activeVfxClips=new Set,this.clearCounter=0,this.audioFilters=new Map,this.audioPannerNodes=new Map,this.firedEvents=new Set}get state(){return this._state.value}get time(){return this._time.value}get duration(){return this._duration}get timescale(){return this._timescale}set timescale(t){this._timescale=Math.max(.01,t)}get loop(){return this._loop}set loop(t){this._loop=t}get externalTimeControl(){return this._externalTimeControl}set externalTimeControl(t){this._externalTimeControl=t}setWorld(t){this.world!==t&&(this.world=t,this.clearCounter++,this.vfxActors.clear(),this.spawnedInstances.clear(),this.bindings.clear(),this.activeClips.clear(),this.activeVfxClips.clear(),this.activeAudioClips.clear(),this.audioStartInFlight.clear(),this.lastAudioStartAttemptTime.clear(),this.loadingAudioBuffers.clear(),this.audioFilters.clear(),this.audioPannerNodes.clear(),this.pendingSpawns.clear(),this._state.next(SequencePlaybackState.Stopped))}setAudioListener(t){this.audioListener=t}setAssetLoader(t){if(this.assetLoader=t,this.sequenceData){const t=this.clearCounter;this.preloadAudioBuffersForSequence(this.sequenceData,t)}}setActorFactory(t){this.actorFactory=t}setVfxService(t){this.vfxService=t}load(t){if(this.stop(),this.clearCounter++,this.sequenceData=t,this._duration=t.duration,this._loop=t.loop,this._timescale=t.playbackRate,this.bindings.clear(),this.loopCount=0,this.assetLoader){const e=this.clearCounter;this.preloadAudioBuffersForSequence(t,e)}}getAudioAssetIdsForSequence(t){const e=new Set;for(const i of t.tracks){if("audio"!==i.type)continue;const t=i;for(const i of t.clips??[])i.audioAssetId&&e.add(i.audioAssetId)}return[...e]}async preloadAudioBuffersForSequence(t,e){if(!this.assetLoader)return;const i=this.getAudioAssetIdsForSequence(t);0!==i.length&&await Promise.all(i.map(async t=>{if(e!==this.clearCounter)return;if(this.audioBuffers.has(t))return;let i=this.loadingAudioBuffers.get(t);i||(i=this.assetLoader.getAudioByAssetId(t),this.loadingAudioBuffers.set(t,i));try{const s=await i;if(e!==this.clearCounter)return;this.audioBuffers.set(t,s)}catch(t){}finally{this.loadingAudioBuffers.get(t)===i&&this.loadingAudioBuffers.delete(t)}}))}bindRole(t,e){this.roleBindings.set(t,e),this.sequenceData&&this.resolveBindings()}bindObject(t,e){if(this.sequenceData)for(const i of this.sequenceData.tracks)"object"===i.type&&i.targetId===t&&this.createBinding(i,e)}resolveBindings(){if(this.sequenceData)for(const t of this.sequenceData.tracks)this.resolveTrackBinding(t)}resolveTrackBinding(t){if("object"===t.type){const e=t;e.role&&this.roleBindings.has(e.role)&&this.createBinding(t,this.roleBindings.get(e.role))}else if("spawn"===t.type){const e=t;e.role&&this.roleBindings.has(e.role)&&this.createBinding(t,this.roleBindings.get(e.role))}else if("group"===t.type)for(const e of t.childTracks)this.resolveTrackBinding(e)}createBinding(t,e){const i=e instanceof r?e.object:e,a={track:t,target:e,activeActions:new Map,originalTransform:{position:i.position.clone(),rotation:i.rotation.clone(),scale:i.scale.clone()},originalParent:i.parent};t.subTracks.some(t=>"animation"===t.type)&&(a.mixer=new s(i)),this.bindings.set(t.id,a)}play(){this._state.value!==SequencePlaybackState.Playing&&this.sequenceData&&(this._state.value===SequencePlaybackState.Stopped&&(this._time.next(0),this.loopCount=0,this.resolveBindings(),this.captureOriginalTransforms()),this._state.next(SequencePlaybackState.Playing),this.resumeVfx())}pause(){this._state.value===SequencePlaybackState.Playing&&(this._state.next(SequencePlaybackState.Paused),this.pauseAudio(),this.pauseVfx())}pauseAudio(){for(const t of this.audioByClip.values())t.pause()}pauseVfx(){for(const t of this.vfxActors.values())t.pause()}resumeVfx(){for(const[t,e]of this.vfxActors.entries())(this.activeVfxClips.has(t)||e.getParticleCount()>0)&&e.play()}stop(){this._state.next(SequencePlaybackState.Stopped),this._time.next(0),this.clearCounter++,this.loopCount=0,this.pauseAudio(),this.restoreOriginalTransforms(),this.restoreOriginalParents(),this.stopAllAudio(),this.stopAllAnimations(),this.despawnAll(),this.stopAllVfx(),this.firedEvents.clear()}restart(){this.stop(),this.play()}seek(t){const e=Math.max(0,Math.min(t,this._duration));this._time.next(e),this.evaluate(e)}update(t){if(this._state.value!==SequencePlaybackState.Playing)return;if(!this.sequenceData)return;if(this._externalTimeControl){for(const e of this.bindings.values())e.mixer&&e.mixer.update(t*this._timescale);for(const e of this.spawnedInstances.values())e.mixer&&e.mixer.update(t*this._timescale);return}const e=this._time.value+t*this._timescale;if(e>=this._duration){if(!this._loop)return this._time.next(this._duration),this.evaluate(this._duration),this._state.next(SequencePlaybackState.Stopped),void this.onComplete.next();this.loopCount++,this._time.next(e%this._duration),this.firedEvents.clear(),this.onLoop.next(this.loopCount)}else this._time.next(e);this.evaluate(this._time.value);for(const e of this.bindings.values())e.mixer&&e.mixer.update(t*this._timescale);for(const e of this.spawnedInstances.values())e.mixer&&e.mixer.update(t*this._timescale)}evaluate(t){if(this.sequenceData)for(const e of this.sequenceData.tracks){if(!e.muted&&("object"===e.type||"spawn"===e.type||"vfx"===e.type)){const t=this.bindings.get(e.id);this.updateTrackParenting(e,t)}this.evaluateTrack(e,t)}}evaluateTrack(t,e){if(t.muted)return;const i=this.bindings.get(t.id);switch(t.type){case"object":i&&this.evaluateObjectTrack(t,i,e);break;case"camera":this.evaluateCameraTrack(t,e);break;case"audio":this.evaluateAudioTrack(t,e);break;case"vfx":this.evaluateVfxTrack(t,e);break;case"spawn":this.evaluateSpawnTrack(t,i,e);break;case"group":for(const i of t.childTracks)this.evaluateTrack(i,e)}}evaluateObjectTrack(t,e,i){this.evaluateSubTracks(t.subTracks,e,i)}evaluateSubTracks(t,e,i){const s=e.target instanceof r?e.target.object:e.target;if(s)for(const a of t)if(!a.muted)switch(a.type){case"transform":this.evaluateTransformSubTrack(a,s,i);break;case"animation":this.evaluateAnimationSubTrack(a,e,i);break;case"visibility":this.evaluateVisibilitySubTrack(a,s,i);break;case"property":this.evaluatePropertySubTrack(a,e.target||null,i);break;case"event":this.evaluateEventSubTrack(a,e.target||null,i)}}evaluateTransformSubTrack(t,e,i){const s=t.keyframes;if(0===s.length)return;const{prev:a,next:o,t:n}=this.findKeyframes(s,i);if(a)if(o&&a!==o){const i=this.applyInterpolation(n,a.interpolation);t.components.position&&a.position&&o.position&&e.position.lerpVectors(f.fromArray(a.position),m.fromArray(o.position),i),t.components.rotation&&a.rotation&&o.rotation&&(g.setFromEuler(v.fromArray([...a.rotation,"XYZ"])),A.setFromEuler(b.fromArray([...o.rotation,"XYZ"])),e.quaternion.slerpQuaternions(g,A,i)),t.components.scale&&a.scale&&o.scale&&e.scale.lerpVectors(f.fromArray(a.scale),m.fromArray(o.scale),i)}else t.components.position&&a.position&&e.position.fromArray(a.position),t.components.rotation&&a.rotation&&e.rotation.fromArray([...a.rotation,"XYZ"]),t.components.scale&&a.scale&&e.scale.fromArray(a.scale)}evaluateAnimationSubTrack(t,e,s){if(!e.mixer||!e.activeActions)return;if(e.target instanceof r?e.target.object:e.target)for(const a of t.clips){const o=a.startTime+a.duration,n=s>=a.startTime&&s<o,c=`${t.id}-${a.id}`;let l=e.activeActions.get(c),u=!1;if(n){if(!l){u=!0;const t=a.animationClipAssetId;if(null==t)continue;const s=this.animationClips.get(t);if(null==s){this.ensureAnimationClip(t);continue}const o=e;let n=!1;if(e.target instanceof r){o.charAnimComponent||(o.charAnimComponent=e.target.getComponent(h)??void 0);let t=s;a.clipEndOffset>0&&(t=t.clone(),t.duration=Math.max(0,t.duration-a.clipEndOffset));const i=o.charAnimComponent;if(null!=i){const s=a.rootMotion?p.fromClip(t,!1):t;if(this._externalTimeControl)l=i.beginExternalAnimationControl(s,{timeScale:a.playbackRate});else if(i.play(s,{inPlace:!a.rootMotion,loop:!1,priority:20,timeScale:a.playbackRate,fadeTime:a.fadeInDuration??.2,offset:a.clipStartOffset}),l=i.getFullBodyAction(),a.rootMotion){const t=e.target.getComponent(d);t&&t.setRootMotionAction(l)}e.activeActions.set(c,l),o.charAnimActionKeys||(o.charAnimActionKeys=new Set),o.charAnimActionKeys.add(c),n=!0}}if(!n){let t=s;a.clipEndOffset>0&&(t=t.clone(),t.duration=Math.max(0,t.duration-a.clipEndOffset)),l=e.mixer.clipAction(t),l.setLoop(i.LoopOnce,1),l.clampWhenFinished=!0,l.timeScale=a.playbackRate,a.clipStartOffset>0&&(l.time=a.clipStartOffset),e.activeActions.set(c,l),l.play()}}if(l){const t=e;if(this._externalTimeControl){t.charAnimComponent?.beginExternalControl();const i=(s-a.startTime)*a.playbackRate+a.clipStartOffset,o=Math.min(i,l.getClip().duration);(u||Math.abs(l.time-o)>.001)&&(l.time=o,l.paused=!1,l.play(),t.charAnimComponent?t.charAnimComponent.getMixer()?.update(0):e.mixer&&e.mixer.update(0))}else t.charAnimComponent?.endExternalControl()}}else if(l){e.activeActions.delete(c);const t=e;t.charAnimActionKeys?.has(c)?(t.charAnimActionKeys.delete(c),0===t.charAnimActionKeys.size&&t.charAnimComponent&&(t.charAnimComponent.stopSequenceAnimation(),t.charAnimComponent=void 0)):(l.stop(),this._externalTimeControl&&e.mixer&&e.mixer.update(0))}}}evaluateVisibilitySubTrack(t,e,i){const s=t.keyframes;if(0===s.length)return;let a=s[0];for(const t of s){if(!(t.time<=i))break;a=t}e.visible=a.visible}evaluatePropertySubTrack(t,e,i){}evaluateEventSubTrack(t,e,i){if(e instanceof r&&this._state.value===SequencePlaybackState.Playing)for(const s of t.events){const a=`${t.id}-${s.id}`;if(i>=s.time&&!this.firedEvents.has(a)){if(this.firedEvents.add(a),!s.functionName)continue;const t=e[s.functionName];if("function"==typeof t)try{const i=s.arguments.map(t=>t.value);t.call(e,...i)}catch(t){console.error(`Failed to call sequence event ${s.functionName}:`,t)}else console.warn(`Sequence event method '${s.functionName}' not found on actor`)}else s.time>i&&this.firedEvents.delete(a)}}evaluateCameraTrack(t,e){}evaluateAudioTrack(t,e){if(!this.assetLoader||!t.clips||0===t.clips.length)return;const i=this._state.value===SequencePlaybackState.Playing,s=this.evaluateAudioSubTracks(t,e);t.volume;for(const a of t.clips){const o=a.startTime+a.duration,n=e>=a.startTime&&e<o,r=`${t.id}-${a.id}`;if(n){if(this.activeAudioClips.has(r)||this.activeAudioClips.add(r),i){const i=this.audioByClip.get(r);i&&i.isPlaying||this.tryStartAudioClip(t,a,r,e,s)}const o=this.audioByClip.get(r);o&&this.applyAudioParameters(o,t,a,r,e,s)}else this.activeAudioClips.has(r)&&(this.activeAudioClips.delete(r),this.lastAudioStartAttemptTime.delete(r),this.audioStartInFlight.delete(r),this.stopAudioClip(r))}}tryStartAudioClip(t,e,i,s,a){if(this.audioStartInFlight.has(i))return;const o=this.lastAudioStartAttemptTime.get(i);if(void 0!==o&&s-o<this.audioStartRetryIntervalSeconds)return;this.lastAudioStartAttemptTime.set(i,s);const n=this.playAudioClip(t,e,i,s,a).catch(()=>{}).finally(()=>{this.audioStartInFlight.get(i)===n&&this.audioStartInFlight.delete(i)});this.audioStartInFlight.set(i,n)}applyAudioParameters(t,e,s,a,o,n){const r=n.volume??1,c=this.stringToRandom(a+e.id),l=s.volumeRandomization?(2*c-1)*s.volumeRandomization:0;let u=1;const h=o-s.startTime;h<s.fadeInDuration&&s.fadeInDuration>0?u=h/s.fadeInDuration:h>s.duration-s.fadeOutDuration&&s.fadeOutDuration>0&&(u=(s.duration-h)/s.fadeOutDuration);const d=Math.max(0,(e.volume??1)*r*(s.volume+l)*u);t.setVolume(d);let p=n.detune??0;if(s.pitchRandomization){p+=(2*this.stringToRandom(a+"_pitch")-1)*s.pitchRandomization}if(t.setDetune(p),t.hasPlaybackControl&&t.setPlaybackRate(s.playbackRate*this.timescale),this.audioListener&&this.audioListener.context){const s=this.audioListener.context,o=[],r=n.lowpass,c=n.highpass;let l=this.audioFilters.get(a);if(void 0!==r||void 0!==c)l||(l=s.createBiquadFilter(),this.audioFilters.set(a,l)),void 0!==r?(l.type="lowpass",l.frequency.value=r):void 0!==c&&(l.type="highpass",l.frequency.value=c),o.push(l);else if(l){try{l.disconnect()}catch{}this.audioFilters.delete(a)}const u=n.pan;if(!e.spatial&&t instanceof i.Audio&&void 0!==u&&"function"==typeof s.createStereoPanner){let t=this.audioPannerNodes.get(a);t||(t=s.createStereoPanner(),this.audioPannerNodes.set(a,t)),t.pan.value=u,o.push(t)}else{const t=this.audioPannerNodes.get(a);if(t){try{t.disconnect()}catch{}this.audioPannerNodes.delete(a)}}t.setFilters(o)}e.spatial&&t instanceof i.PositionalAudio?this.updatePositionalAudioTransform(t,e,s,n.spatialBlend):void 0!==n.pan&&this.audioListener}updatePositionalAudioTransform(t,e,s,a){let o=null,n=null;const r=s.attachedToTrackId||e.parent?.trackId,c=s.attachmentSocketName||e.parent?.socketName;if(r){const t=this.sequenceData?.tracks.find(t=>t.id===r);if(t){const e=this.getActiveObjectsForTrack(t);if(e.length>0){const t=e[0];if(o=f,n=g,t.getWorldPosition(o),t.getWorldQuaternion(n),c){const e=this.findSocket(t,c);e&&(e.getWorldPosition(o),e.getWorldQuaternion(n))}}}}if(o&&n){const e="number"==typeof a?i.MathUtils.clamp(a,0,1):1;if(e<1&&this.audioListener){const t=y;this.audioListener.getWorldPosition(t),o=o.clone().lerp(t,1-e)}t.position.copy(o),t.quaternion.copy(n),t.updateMatrixWorld()}}async playAudioClip(t,e,s,a,o){if(!this.assetLoader||!e.audioAssetId||null==this.audioListener)return;if("suspended"===this.audioListener.context.state)try{await this.audioListener.context.resume()}catch(t){return void console.warn("Failed to resume audio context:",t)}this.stopAudioClip(s);const n=this.clearCounter;try{let a=this.audioBuffers.get(e.audioAssetId);if(!a){let t=this.loadingAudioBuffers.get(e.audioAssetId);t||(t=this.assetLoader.getAudioByAssetId(e.audioAssetId),this.loadingAudioBuffers.set(e.audioAssetId,t));try{a=await t,this.audioBuffers.set(e.audioAssetId,a)}finally{this.loadingAudioBuffers.delete(e.audioAssetId)}}if(n!==this.clearCounter)return;const r=this._time.value,c=e.startTime+e.duration;if(!(r>=e.startTime&&r<c))return;const l=Math.max(0,r-e.startTime),u=e.clipStartOffset+l*e.playbackRate;if(u>=a.duration||l>=e.duration)return;let h=this.audioByClip.get(s);const d=!0===t.spatial,p=h&&!0===h.isPositionalAudio;if(h&&d!==p&&(h.isPlaying&&h.stop(),h.disconnect(),this.audioByClip.delete(s),h=void 0),!h){if(d){const t=new i.PositionalAudio(this.audioListener);t.setRefDistance(10),t.setRolloffFactor(1),h=t}else h=new i.Audio(this.audioListener);this.world&&this.world.scene.add(h),this.audioByClip.set(s,h)}Math.min((e.duration-l)/e.playbackRate,(a.duration-u)/e.playbackRate);h.setLoopStart(e.clipStartOffset),h.setLoopEnd(Math.min(e.clipStartOffset+e.duration,a.duration)),h.setLoop(!0),h.setBuffer(a),this.applyAudioParameters(h,t,e,s,r,o),h.isPlaying||h.play()}catch(t){console.error(`Failed to play audio clip ${e.id}:`,t)}}stopAudioClip(t){const e=this.audioByClip.get(t);if(e){try{e.isPlaying&&e.stop()}catch(t){}e.parent&&e.parent.remove(e),this.audioByClip.delete(t)}const i=this.audioGainNodes.get(t);i&&(i.disconnect(),this.audioGainNodes.delete(t));const s=this.audioFilters.get(t);if(s){try{s.disconnect()}catch{}this.audioFilters.delete(t)}const a=this.audioPannerNodes.get(t);if(a){try{a.disconnect()}catch{}this.audioPannerNodes.delete(t)}}stopAllAudio(){for(const t of this.audioByClip.keys())this.stopAudioClip(t);this.activeAudioClips.clear(),this.audioStartInFlight.clear(),this.lastAudioStartAttemptTime.clear()}evaluateVfxTrack(t,e){if(!this.vfxService||!this.world||!t.clips||0===t.clips.length)return;const i=new Set,s=new Map;for(const a of t.clips){const o=a.startTime+a.duration;if(e>=a.startTime&&e<o){const e=`${t.id}-${a.id}-${a.vfxAssetId}`;i.add(e),s.has(e)||s.set(e,a)}}const a=[];for(const e of this.activeVfxClips)e.startsWith(`${t.id}-`)&&(i.has(e)||a.push(e));for(const t of a){this.activeVfxClips.delete(t);const e=this.vfxActors.get(t);e&&!e.paused&&e.stop()}for(const a of i){const i=this.vfxActors.get(a);if(this.activeVfxClips.has(a))i&&(this.updateVfxTransform(t,i,e),this._state.value===SequencePlaybackState.Playing&&i.play());else{this.activeVfxClips.add(a);const e=s.get(a);this.ensureVfxActor(t,e,a)}}}async ensureVfxActor(t,e,i){if(!this.vfxService||!e.vfxAssetId)return;if(this.vfxActors.has(i)){const t=this.vfxActors.get(i);return t.restart(),void t.play()}const s=this.clearCounter;try{const a=await this.vfxService.createFromAssetId(e.vfxAssetId,this.world.scene);if(s!==this.clearCounter)return void(this.world&&this.world.removeActor(a));const{position:o,rotation:n,scale:r}=this.getVfxTransform(t,e.startTime);a.object.position.copy(o),a.object.rotation.copy(n),a.object.scale.copy(r),this.vfxActors.set(i,a),this._state.value===SequencePlaybackState.Playing&&a.play()}catch(t){console.error(`Failed to create VFX actor for clip ${e.id}:`,t)}}getVfxTransform(t,e){const i=new n,s=new a,r=new n(1,1,1),c=t.subTracks.find(t=>"transform"===t.type);if(c&&c.keyframes.length>0){const{prev:t,next:a,t:n}=this.findKeyframes(c.keyframes,e);if(t&&(t.position&&i.fromArray(t.position),t.rotation&&s.fromArray([...t.rotation,"XYZ"]),t.scale&&r.fromArray(t.scale)),a&&t!==a&&t.time!==a.time){const e=this.applyInterpolation(n,t.interpolation);if(t.position&&a.position&&i.lerpVectors(f.fromArray(t.position),m.fromArray(a.position),e),t.rotation&&a.rotation){g.setFromEuler(v.fromArray([...t.rotation,"XYZ"])),A.setFromEuler(b.fromArray([...a.rotation,"XYZ"]));const i=new o;i.slerpQuaternions(g,A,e),s.setFromQuaternion(i)}t.scale&&a.scale&&r.lerpVectors(f.fromArray(t.scale),m.fromArray(a.scale),e)}}else i.fromArray(t.position),s.fromArray([...t.rotation,"XYZ"]),r.fromArray(t.scale);return{position:i,rotation:s,scale:r}}updateVfxTransform(t,e,i){const{position:s,rotation:a,scale:o}=this.getVfxTransform(t,i);e.object.position.copy(s),e.object.rotation.copy(a),e.object.scale.copy(o)}resolveActorType(t){const e=this.actorFactory.classes[t];if(null!=e)return e;const i=c[t];return i||(console.warn(`Could not resolve actor type: ${t}`),null)}async spawnActorForClip(t,e,i,s){if(!this.world||!t.actorType)return null;const a=this.resolveActorType(t.actorType);if(!a)return null;try{return await this.world.spawnActor(a,i,s)}catch(e){return console.error(`Failed to spawn actor ${t.actorType}:`,e),null}}async spawnPrefabForClip(t,e,i,s){if(!this.world||!this.assetLoader||!t.prefabId)return null;try{const e=await this.assetLoader.getPrefabById(t.prefabId);if(!e)return null;return await this.world.spawnPrefab(e,i,s)}catch(e){return console.error(`Failed to spawn prefab ${t.prefabId}:`,e),null}}async spawnMeshForClip(t,e,i,s){if(!this.world||!this.assetLoader||!t.meshId)return null;try{const e=await this.assetLoader.getAsset(t.meshId),a=await this.assetLoader.getModelByAssetId(t.meshId),o=u.clone(a.scene);if(o.position.copy(i),o.rotation.copy(s),e){await this.assetLoader.applyMaterials(e,o);const t=e.mesh?.rescale;null!=t&&1!==t&&o.scale.multiplyScalar(t)}return this.world.scene.add(o),o}catch(e){return console.error(`Failed to spawn mesh ${t.meshId}:`,e),null}}despawnInstance(t){if(this.world)switch(t?.type){case"actor":this.world.removeActor(t.instance);break;case"prefab":this.world.removePrefab(t.instance);break;case"mesh":this.world.scene.remove(t.instance)}}despawnAll(){for(const t of this.spawnedInstances.values())this.despawnInstance(t);this.spawnedInstances.clear(),this.activeClips.clear(),this.pendingSpawns.clear()}getInitialTransform(t,e,i){const s=new n,o=new a,r=t.subTracks.find(t=>"transform"===t.type);if(r&&r.keyframes.length>0){const{prev:t}=this.findKeyframes(r.keyframes,i);t&&(t.position&&s.fromArray(t.position),t.rotation&&o.fromArray([...t.rotation,"XYZ"]))}else e.initialPosition&&s.fromArray(e.initialPosition),e.initialRotation&&o.fromArray([...e.initialRotation,"XYZ"]);return{position:s,rotation:o}}evaluateSpawnTrack(t,e,i){if(!t.clips||0===t.clips.length)return;const s=e||this.bindings.get(t.id);if(s&&s.target)for(const e of t.clips){const a=e.startTime+e.duration,o=i>=e.startTime&&i<a,n=`${t.id}-${e.id}`;if(o){this.activeClips.has(n)||(this.activeClips.add(n),this.spawnedInstances.set(n,{type:"proxy",target:s.target}));(s.target instanceof r?s.target.object:s.target)&&this.evaluateSubTracks(t.subTracks,s,i)}else this.activeClips.has(n)&&(this.activeClips.delete(n),this.spawnedInstances.delete(n))}else for(const e of t.clips){const s=e.startTime+e.duration,a=i>=e.startTime&&i<s,o=`${t.id}-${e.id}`,n=this.spawnedInstances.has(o),r=this.pendingSpawns.has(o);if(!a||n||r){if(a&&n){const e=this.spawnedInstances.get(o);e&&"proxy"!==e.type&&this.evaluateSubTracks(t.subTracks,e,i)}else if(!a&&n){const t=this.spawnedInstances.get(o);t&&(this.despawnInstance(t),this.spawnedInstances.delete(o),this.activeClips.delete(o))}}else{const{position:i,rotation:s}=this.getInitialTransform(t,e,e.startTime);this.pendingSpawns.add(o),this.spawnForClip(t,e,i,s,o)}}}getInstanceObject(t){switch(t.type){case"actor":return t.instance.object;case"prefab":return t.instance.mainActor?.object||t.instance.object;case"mesh":return t.instance;case"proxy":return t.target instanceof r?t.target.object:t.target;default:return null}}async spawnForClip(t,e,i,a,o){const n=this.clearCounter;let r=null;try{switch(t.spawnType){case"actor":const s=await this.spawnActorForClip(t,e,i,a);s&&(r={type:"actor",instance:s});break;case"prefab":const o=await this.spawnPrefabForClip(t,e,i,a);o&&(r={type:"prefab",instance:o});break;case"mesh":const n=await this.spawnMeshForClip(t,e,i,a);n&&(r={type:"mesh",instance:n})}if(n!==this.clearCounter)return void(r&&this.despawnInstance(r));const c=this._time.value,l=c>=e.startTime&&c<e.startTime+e.duration;if(r&&l){const e=this.getInstanceObject(r);"proxy"===r.type||("actor"===r.type?r.target=r.instance:"prefab"===r.type?r.target=r.instance.mainActor||r.instance.object:r.target=e);t.subTracks.some(t=>"animation"===t.type)&&e&&(r.mixer=new s(e),r.activeActions=new Map),this.spawnedInstances.set(o,r),this.activeClips.add(o)}else r&&this.despawnInstance(r)}finally{this.pendingSpawns.delete(o)}}findKeyframes(t,e){if(0===t.length)return{prev:null,next:null,t:0};let i=null,s=null;for(let a=0;a<t.length;a++)if(t[a].time<=e&&(i=t[a]),t[a].time>e&&!s){s=t[a];break}if(i||(i=t[0]),s||(s=t[t.length-1]),i===s||i.time===s.time)return{prev:i,next:i,t:0};return{prev:i,next:s,t:(e-i.time)/(s.time-i.time)}}applyInterpolation(t,e){switch(e){case"linear":case"catmullRom":default:return t;case"step":return 0;case"cubic":return t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2}}async ensureAnimationClip(t){if(this.assetLoader&&!this.animationClips.has(t)&&!this.loadingAnimationClips.has(t)){this.loadingAnimationClips.add(t);try{const e=await this.assetLoader.getAnimationClipByAssetId(t);e&&this.animationClips.set(t,e)}catch(e){console.error(`Failed to load animation clip for asset ${t}:`,e)}finally{this.loadingAnimationClips.delete(t)}}}captureOriginalTransforms(){for(const t of this.bindings.values()){const e=t.target instanceof r?t.target.object:t.target;e&&(t.originalTransform={position:e.position.clone(),rotation:e.rotation.clone(),scale:e.scale.clone()})}}restoreOriginalTransforms(){for(const t of this.bindings.values())if(t.originalTransform){const e=t.target instanceof r?t.target.object:t.target;e&&(e.position.copy(t.originalTransform.position),e.rotation.copy(t.originalTransform.rotation),e.scale.copy(t.originalTransform.scale))}}stopAllAnimations(){for(const t of this.bindings.values()){for(const e of t.activeActions.values())e.stop();t.activeActions.clear(),t.mixer&&t.mixer.stopAllAction(),t.charAnimComponent&&(t.charAnimComponent.stopSequenceAnimation(),t.charAnimComponent=void 0),t.charAnimActionKeys?.clear()}}stopAllVfx(){for(const t of this.vfxActors.values())t.paused||t.stop();this.activeVfxClips.clear()}dispose(){if(this.stop(),this.bindings.clear(),this.roleBindings.clear(),this.sequenceData=null,this.world)for(const t of this.vfxActors.values())this.world.removeActor(t);this.vfxActors.clear(),this.stopAllAudio(),this.audioBuffers.clear(),this.loadingAudioBuffers.clear(),this.animationClips.clear(),this.audioByClip.clear()}updateTrackParenting(t,e){const i=this.getActiveObjectsForTrack(t);if(0===i.length)return;let s=null;const a=t.parent?.trackId;if(a){const e=this.sequenceData?.tracks.find(t=>t.id===a);if(e){const i=this.getActiveObjectsForTrack(e);if(i.length>0&&(s=i[0],t.parent?.socketName)){const e=s.getObjectByName(t.parent.socketName);e&&(s=e)}}}for(const a of i)s?a.parent!==s&&(s.add(a),e&&"object"===t.type&&(e.currentParent=s)):e?.currentParent&&e.currentParent===a.parent?this.restoreObjectParent(e,a):a.parent&&a.parent!==this.world?.scene&&"object"!==t.type&&this.world?.scene.add(a)}findSocket(t,e){if(t.name===e)return t;if(t instanceof i.SkinnedMesh){const i=t.skeleton.bones.find(t=>t.name===e);if(i)return i}return t.getObjectByName(e)||null}getActiveObjectsForTrack(t){if("object"===t.type){const e=this.bindings.get(t.id);if(e&&e.target)return[e.target instanceof r?e.target.object:e.target]}else{if("spawn"===t.type){const e=[];for(const[i,s]of this.spawnedInstances)if(i.startsWith(t.id+"-")&&this.activeClips.has(i)){const t=this.getInstanceObject(s);t&&e.push(t)}return e}if("vfx"===t.type){const e=[];for(const i of this.activeVfxClips)if(i.startsWith(t.id+"-")){const t=this.vfxActors.get(i);t&&e.push(t.object)}return e}}return[]}restoreObjectParent(t,e){t.originalParent?t.originalParent.add(e):this.world&&this.world.scene.add(e),t.currentParent=void 0}restoreOriginalParents(){for(const t of this.bindings.values())if(t.currentParent){const e=t.target instanceof r?t.target.object:t.target;e&&this.restoreObjectParent(t,e)}}evaluateAudioSubTracks(t,e){const i={};for(const s of t.subTracks)if("audioParameter"===s.type){const t=s,a=s.keyframes;if(0===a.length)continue;const{prev:o,next:n,t:r}=this.findKeyframes(a,e);if(!o)continue;let c;if(n&&o!==n){const t=o.value.value,e=n.value.value;if("step"===o.interpolation)c=t;else if("cubic"===o.interpolation){c=t+(e-t)*(r*r*(3-2*r))}else c=t+(e-t)*r}else c=o.value.value;if("number"==typeof c){const e=l(t.parameter),i=e.min,s=e.max;"number"==typeof i&&(c=Math.max(i,c)),"number"==typeof s&&(c=Math.min(s,c))}void 0!==c&&(i[t.parameter]=c)}return i}stringToRandom(t){let e=0;for(let i=0;i<t.length;i++){e=(e<<5)-e+t.charCodeAt(i),e&=e}const i=1e4*Math.sin(e);return i-Math.floor(i)}}const f=new n,m=new n,y=new n,g=new o,A=new o,v=new a,b=new a;/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{__decorate as t}from"tslib";import{Service as r}from"typedi";import{inject as o}from"../../gameplay/inject.js";import{World as e}from"../../gameplay/services/world.js";import{SequenceActor as s}from"./sequence-actor.js";let a=class{constructor(){this.world=o(e)}action(t){const r=this.world.spawnActorSync(s);return r.setSequenceData(t.data),r}};a=t([r()],a);export{a as Sequencer};/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -10,13 +10,16 @@ export declare class Trail extends THREE.Object3D {
|
|
|
10
10
|
scene: any;
|
|
11
11
|
geometry: THREE.BufferGeometry | null;
|
|
12
12
|
mesh: THREE.Mesh | null;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
historyCount: number;
|
|
14
|
+
historyHeadIndex: number;
|
|
15
|
+
history: Array<{
|
|
16
|
+
position: THREE.Vector3;
|
|
17
|
+
tangent: THREE.Vector3;
|
|
18
|
+
transformMatrix: THREE.Matrix4;
|
|
19
|
+
id: number;
|
|
20
|
+
}>;
|
|
21
|
+
renderNodeCount: number;
|
|
22
|
+
subdivisions: number;
|
|
20
23
|
currentNodeID: number;
|
|
21
24
|
advanceFrequency: number;
|
|
22
25
|
advancePeriod: number;
|
|
@@ -47,18 +50,19 @@ export declare class Trail extends THREE.Object3D {
|
|
|
47
50
|
setAllNodesToCurrentTargetPosition(): void;
|
|
48
51
|
updateUniforms(): void;
|
|
49
52
|
advance: () => void;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
advanceWithTransform(transformMatrix: THREE.Matrix4): void;
|
|
54
|
+
updateHead(): void;
|
|
55
|
+
getControlPoint(index: number): {
|
|
56
|
+
position: THREE.Vector3;
|
|
57
|
+
tangent: THREE.Vector3;
|
|
58
|
+
transformMatrix: THREE.Matrix4;
|
|
59
|
+
id: number;
|
|
60
|
+
};
|
|
61
|
+
rebuildGeometry(): void;
|
|
53
62
|
currentTime(): number;
|
|
54
63
|
pause(): void;
|
|
55
64
|
resume(): void;
|
|
56
65
|
update(): void;
|
|
57
|
-
updateHead: () => void;
|
|
58
|
-
updateNodeID(nodeIndex: any, id: any): void;
|
|
59
|
-
updateNodeCenter(nodeIndex: any, nodeCenter: any): void;
|
|
60
|
-
updateNodePositionsFromOrientationTangent: (nodeIndex: any, nodeCenter: any, orientationTangent: any) => void;
|
|
61
|
-
updateNodePositionsFromTransformMatrix: (nodeIndex: any, transformMatrix: any) => void;
|
|
62
66
|
connectNodes: (srcNodeIndex: number, destNodeIndex: number) => {
|
|
63
67
|
attribute: any;
|
|
64
68
|
offset: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{timeUniforms as e}from"../../shader-nodes";import*as t from"three";import{NodeShaderMaterial as r,uniformBool as i,uniformFloat as n,uniformVec2 as a,mix as s,uniformVec4 as o,attributeFloat as h,attributeVec3 as l,select as c,float as d,textureSampler2d as u,attributes as m,uniforms as g,rgba as y,vec4 as p,varying as f,vec2 as x,normalize as v,vec3 as T,cross as V,max as C,length as P}from"three-shader-graph";n("trailLength");const N=n("verticesPerNode"),b=n("minID"),D=n("maxID"),w=(n("dragTexture"),n("maxTrailLength")),F=a("textureTileFactor",new t.Vector2(1,1)),I=o("headColor",new t.Vector4),U=o("tailColor",new t.Vector4),A=i("taper",!1),L=(n("scale",1),h("nodeID")),O=h("nodeVertexID"),M=l("nodeCenter"),B=D.subtract(L).divide(D.subtract(b)),S=c(A,B,d(0)),z=f(d(1).subtract(B).multiplyVec4(I).add(B.multiplyVec4(U))),H=v(g.cameraPosition.subtract(M)),G=v(l("nodeTangent")),j=n("width",1),E=v(V(c(P(G).gt(d(.1)),G,T(1,0,0)),H)).multiplyScalar(O.subtract(d(.5))).multiplyScalar(j),R=p(d(1).subtract(S).multiplyVec3(E).add(C(S,d(1)).multiplyVec3(M)),1),W=p(d(1).subtract(S).multiplyVec3(m.position).add(S.multiplyVec3(M)),1),q=g.projectionMatrix.multiply(g.viewMatrix).multiplyVec(R),_=g.projectionMatrix.multiply(g.viewMatrix).multiplyVec(W);export const trailUV=f(x(B.multiply(F.x),O.divide(N).multiply(F.y).multiply(2)));export const trailDragUV=f(x(L.divide(w).multiply(F.x),O.divide(N).multiply(F.y).multiply(2)));export class Trail extends t.Object3D{constructor(e,r){super(),this.nodeCenters=[],this.nodeIDs=[],this.advance=function(){const e=new t.Matrix4;return function(){this.targetObject.updateMatrixWorld(),e.copy(this.targetObject.matrixWorld),this.advanceWithTransform(e),this.updateUniforms()}}(),this.advanceGeometry=function(e,t){const r=this.currentEnd+1>=this.length?0:this.currentEnd+1;if(t?this.updateNodePositionsFromTransformMatrix(r,t):this.updateNodePositionsFromOrientationTangent(r,e.position,e.tangent),this.currentLength>=1&&(this.connectNodes(this.currentEnd,r),this.currentLength>=this.length)){const e=this.currentEnd+1>=this.length?0:this.currentEnd+1;this.disconnectNodes(e)}this.currentLength<this.length&&this.currentLength++,this.currentEnd++,this.currentEnd>=this.length&&(this.currentEnd=0),this.currentLength>=1?(this.currentLength<this.length?this.geometry.setDrawRange(0,(this.currentLength-1)*this.FaceIndicesPerNode):this.geometry.setDrawRange(0,this.currentLength*this.FaceIndicesPerNode),this.mesh&&(this.mesh.visible=!0)):(this.geometry.setDrawRange(0,0),this.mesh&&(this.mesh.visible=!1)),this.updateNodeID(this.currentEnd,this.currentNodeID),this.currentNodeID++},this.updateHead=function(){const e=new t.Matrix4;return function(){this.currentEnd<0||(this.targetObject.updateMatrixWorld(),e.copy(this.targetObject.matrixWorld),this.updateNodePositionsFromTransformMatrix(this.currentEnd,e))}}(),this.updateNodePositionsFromOrientationTangent=function(){const e=new t.Quaternion,r=new t.Vector3,i=[];for(let e=0;e<Trail.MaxHeadVertices;e++){const e=new t.Vector3;i.push(e)}return function(t,n,a){const s=this.geometry.getAttribute("position");this.updateNodeCenter(t,n),r.copy(n),r.sub(Trail.LocalHeadOrigin),e.setFromUnitVectors(Trail.LocalOrientationTangent,a);for(let t=0;t<this.localHeadGeometry.length;t++){const n=i[t];n.copy(this.localHeadGeometry[t]),n.applyQuaternion(e),n.add(r)}for(let e=0;e<this.localHeadGeometry.length;e++){const r=(this.VerticesPerNode*t+e)*Trail.PositionComponentCount,n=i[e];s.array[r]=n.x,s.array[r+1]=n.y,s.array[r+2]=n.z}s.needsUpdate=!0}}(),this.updateNodePositionsFromTransformMatrix=function(){const e=new t.Matrix3,r=new t.Quaternion,i=new t.Vector3,n=new t.Vector3,a=new t.Vector3,s=new t.Vector3,o=new t.Box3,h=new t.Vector3,l=[];for(let e=0;e<Trail.MaxHeadVertices;e++){const e=new t.Vector3;l.push(e)}const c=new t.Vector3(1,0,0);return function(d,u){const m=this.geometry.getAttribute("position");i.set(0,0,0),i.applyMatrix4(u),this.updateNodeCenter(d,i);for(let e=0;e<this.localHeadGeometry.length;e++){l[e].copy(this.localHeadGeometry[e])}for(let e=0;e<this.localHeadGeometry.length;e++){l[e].applyMatrix4(u)}if(this.lastNodeCenter&&this.orientToMovement&&(function(e,t){const r=t.elements;e.set(r[0],r[1],r[2],r[4],r[5],r[6],r[8],r[9],r[10])}(e,u),a.set(0,0,-1),a.applyMatrix3(e),s.copy(this.currentNodeCenter),s.sub(this.lastNodeCenter),s.normalize(),s.lengthSq()<=1e-4&&this.lastOrientationDir&&s.copy(this.lastOrientationDir),s.lengthSq()>1e-4)){this.lastOrientationDir||(this.lastOrientationDir=new t.Vector3),r.setFromUnitVectors(a,s),n.copy(this.currentNodeCenter);for(let e=0;e<this.localHeadGeometry.length;e++){const t=l[e];t.sub(n),t.applyQuaternion(r),t.add(n)}}this.billboard&&(this.lastNodeCenter&&this.currentNodeCenter&&!this.currentNodeCenter.equals(this.lastNodeCenter)?(c.copy(this.currentNodeCenter).sub(this.lastNodeCenter),c.normalize(),this.lastOrientationDir||(this.lastOrientationDir=new t.Vector3),this.lastOrientationDir.copy(c)):this.lastOrientationDir?c.copy(this.lastOrientationDir):(c.set(1,0,0),c.applyMatrix4(u),c.normalize(),this.lastOrientationDir||(this.lastOrientationDir=new t.Vector3),this.lastOrientationDir.copy(c)));const g=this.geometry.getAttribute("nodeTangent");for(let e=0;e<this.localHeadGeometry.length;e++){const t=(this.VerticesPerNode*d+e)*Trail.PositionComponentCount,r=l[e];this.billboard?(g.array[t]=c.x,g.array[t+1]=c.y,g.array[t+2]=c.z):(m.array[t]=r.x,m.array[t+1]=r.y,m.array[t+2]=r.z)}if(this.billboard?(this.mesh.frustumCulled=!1,g.needsUpdate=!0,g.addUpdateRange(d*this.VerticesPerNode*Trail.PositionComponentCount,this.VerticesPerNode*Trail.PositionComponentCount)):(m.needsUpdate=!0,m.addUpdateRange(d*this.VerticesPerNode*Trail.PositionComponentCount,this.VerticesPerNode*Trail.PositionComponentCount)),o.makeEmpty(),this.billboard)for(let e=0;e<this.currentLength;e++){const r=(this.currentEnd-e+this.length)%this.length,i=this.nodeCenters[r];if(i){const e=this.material.uniforms.width?.5*this.material.uniforms.width.value:.5;o.expandByPoint(i.clone().add(new t.Vector3(e,e,e))),o.expandByPoint(i.clone().sub(new t.Vector3(e,e,e)))}}else for(let e=0;e<this.currentLength*this.VerticesPerNode;e++){const t=3*e;h.set(m.array[t],m.array[t+1],m.array[t+2]),o.expandByPoint(h)}this.mesh.visible=!o.isEmpty(),null==this.geometry.boundingBox&&(this.geometry.boundingBox=new t.Box3),this.geometry.boundingBox.copy(o),null==this.geometry.boundingSphere&&(this.geometry.boundingSphere=new t.Sphere),this.geometry.boundingBox.getBoundingSphere(this.geometry.boundingSphere)}}(),this.connectNodes=(()=>{const e={attribute:null,offset:0,count:-1};return(t,r)=>{const i=this.geometry.getIndex();for(let e=0;e<this.localHeadGeometry.length-1;e++){const n=this.VerticesPerNode*t+e,a=this.VerticesPerNode*r+e,s=(t*this.FacesPerNode+e*Trail.FacesPerQuad)*Trail.IndicesPerFace;i.array[s]=n,i.array[s+1]=a,i.array[s+2]=n+1,i.array[s+3]=a,i.array[s+4]=a+1,i.array[s+5]=n+1}return i.needsUpdate=!0,i.clearUpdateRanges(),e.attribute=i,e.offset=t*this.FacesPerNode*Trail.IndicesPerFace,e.count=this.FacesPerNode*Trail.IndicesPerFace,e}})(),this.disconnectNodes=(()=>{const e={attribute:null,offset:0,count:-1};return t=>{const r=this.geometry.getIndex();for(let e=0;e<this.localHeadGeometry.length-1;e++){const i=(t*this.FacesPerNode+e*Trail.FacesPerQuad)*Trail.IndicesPerFace;r.array[i]=0,r.array[i+1]=0,r.array[i+2]=0,r.array[i+3]=0,r.array[i+4]=0,r.array[i+5]=0}return r.needsUpdate=!0,r.clearUpdateRanges(),e.attribute=r,e.offset=t*this.FacesPerNode*Trail.IndicesPerFace,e.count=this.FacesPerNode*Trail.IndicesPerFace,e}})(),this.active=!1,this.orientToMovement=!1,r&&(this.orientToMovement=!0),this.scene=e,this.geometry=null,this.mesh=null,this.nodeCenters=[],this.lastNodeCenter=null,this.currentNodeCenter=null,this.lastOrientationDir=null,this.nodeIDs=[],this.currentLength=0,this.currentEnd=0,this.currentNodeID=0,this.advanceFrequency=60,this.advancePeriod=1/this.advanceFrequency,this.lastAdvanceTime=0,this.paused=!1,this.pauseAdvanceUpdateTimeDiff=0}setAdvanceFrequency(e){this.advanceFrequency=e,this.advancePeriod=1/this.advanceFrequency}initialize(e,r,i,n,a,s,o=!1){this.deactivate(),this.destroyMesh(),this.length=r>0?r+1:0,this.dragTexture=i?1:0,this.targetObject=s,this.billboard=o,this.initializeLocalHeadGeometry(n,a),null!=e.uniforms.width&&(e.uniforms.width.value=n),this.nodeIDs=[],this.nodeCenters=[];for(let e=0;e<this.length;e++)this.nodeIDs[e]=-1,this.nodeCenters[e]=new t.Vector3;this.setAllNodesToCurrentTargetPosition(),this.material=e,this.initializeGeometry(),this.initializeMesh(),this.material.uniforms.dragTexture&&(this.material.uniforms.trailLength.value=0),this.material.uniforms.minID.value=0,this.material.uniforms.maxID.value=0,this.material.uniforms.dragTexture&&(this.material.uniforms.dragTexture.value=this.dragTexture),this.material.uniforms.maxTrailLength&&(this.material.uniforms.maxTrailLength.value=this.length),this.material.uniforms.verticesPerNode&&(this.material.uniforms.verticesPerNode.value=this.VerticesPerNode),this.material.uniforms.textureTileFactor&&(this.material.uniforms.textureTileFactor.value=new t.Vector2(1,1)),this.material.uniforms.scale&&(this.material.uniforms.scale.value=1),this.reset()}initializeLocalHeadGeometry(e,r){if(this.localHeadGeometry=[],r){this.VerticesPerNode=0;for(let e=0;e<r.length&&e<Trail.MaxHeadVertices;e++){const i=r[e];if(i&&i instanceof t.Vector3){const e=new t.Vector3;e.copy(i),this.localHeadGeometry.push(e),this.VerticesPerNode++}}}else{const r=(e||1)/2;this.localHeadGeometry.push(new t.Vector3(-r,0,0)),this.localHeadGeometry.push(new t.Vector3(r,0,0)),this.VerticesPerNode=2}this.FacesPerNode=2*(this.VerticesPerNode-1),this.FaceIndicesPerNode=3*this.FacesPerNode}initializeGeometry(){this.vertexCount=this.length*this.VerticesPerNode,this.faceCount=this.length*this.FacesPerNode;const e=new t.BufferGeometry,r=new Float32Array(this.vertexCount),i=new Float32Array(this.vertexCount*this.VerticesPerNode),n=new Float32Array(this.vertexCount*Trail.PositionComponentCount),a=new Float32Array(this.vertexCount*Trail.PositionComponentCount),s=new Float32Array(this.vertexCount*Trail.UVComponentCount),o=new Uint32Array(this.faceCount*Trail.IndicesPerFace),h=new Float32Array(3*this.vertexCount),l=new t.BufferAttribute(h,3);l.setUsage(t.DynamicDrawUsage),e.setAttribute("nodeTangent",l);const c=new t.BufferAttribute(r,1);c.setUsage(t.DynamicDrawUsage),e.setAttribute("nodeID",c);const d=new t.BufferAttribute(i,1);d.setUsage(t.DynamicDrawUsage),e.setAttribute("nodeVertexID",d);const u=new t.BufferAttribute(a,Trail.PositionComponentCount);u.setUsage(t.DynamicDrawUsage),e.setAttribute("nodeCenter",u);const m=new t.BufferAttribute(n,Trail.PositionComponentCount);m.setUsage(t.DynamicDrawUsage),e.setAttribute("position",m);const g=new t.BufferAttribute(s,Trail.UVComponentCount);g.setUsage(t.DynamicDrawUsage),e.setAttribute("uv",g);const y=new t.BufferAttribute(o,1);y.setUsage(t.DynamicDrawUsage),e.setIndex(y),this.geometry=e}zeroVertices(){const e=this.geometry.getAttribute("position");for(let t=0;t<this.vertexCount;t++){const r=3*t;e.array[r]=0,e.array[r+1]=0,e.array[r+2]=0}e.needsUpdate=!0}zeroIndices(){if(null==this.geometry)throw"Geometry not initialized";const e=this.geometry.getIndex();for(let t=0;t<this.faceCount;t++){const r=3*t;e.array[r]=0,e.array[r+1]=0,e.array[r+2]=0}e.needsUpdate=!0,e.clearUpdateRanges()}formInitialFaces(){this.zeroIndices();const e=this.geometry.getIndex();for(let e=0;e<this.length-1;e++)this.connectNodes(e,e+1);e.needsUpdate=!0,e.clearUpdateRanges()}initializeMesh(){if(null==this.geometry)throw"Geometry not initialized";this.mesh=new t.Mesh(this.geometry,this.material),this.mesh.matrixAutoUpdate=!1,this.mesh.visible=!1}destroyMesh(){this.mesh&&(this.scene.remove(this.mesh),this.mesh=null)}reset(){this.currentLength=0,this.currentEnd=-1,this.lastNodeCenter=null,this.currentNodeCenter=null,this.lastOrientationDir=null,this.currentNodeID=0,this.formInitialFaces(),this.zeroVertices(),this.geometry.setDrawRange(0,0),this.setAllNodesToCurrentTargetPosition(),this.mesh&&(this.mesh.visible=!0)}setAllNodesToCurrentTargetPosition(){if(!this.targetObject)return;const e=new t.Vector3;this.targetObject.getWorldPosition(e);for(let t=0;t<this.length;t++)this.nodeCenters[t].copy(e);if(this.billboard&&this.geometry){const e=this.geometry.getAttribute("nodeTangent"),r=new t.Vector3(1,0,0);this.targetObject.matrixWorld&&(r.applyMatrix4(this.targetObject.matrixWorld),r.normalize());for(let t=0;t<this.vertexCount;t++){const i=3*t;e.array[i]=r.x,e.array[i+1]=r.y,e.array[i+2]=r.z}e.needsUpdate=!0}}updateUniforms(){this.currentLength<this.length?this.material.uniforms.minID.value=0:this.material.uniforms.minID.value=this.currentNodeID-this.length,this.material.uniforms.maxID.value=this.currentNodeID,this.material.uniforms.trailLength&&(this.material.uniforms.trailLength.value=this.currentLength),this.material.uniforms.maxTrailLength&&(this.material.uniforms.maxTrailLength.value=this.length),this.material.uniforms.verticesPerNode&&(this.material.uniforms.verticesPerNode.value=this.VerticesPerNode)}advanceWithPositionAndOrientation(e,t){this.advanceGeometry({position:e,tangent:t},null)}advanceWithTransform(e){this.advanceGeometry(null,e)}currentTime(){return performance.now()/1e3}pause(){this.paused||(this.paused=!0,this.pauseAdvanceUpdateTimeDiff=this.currentTime()-this.lastAdvanceTime)}resume(){this.paused&&(this.paused=!1,this.lastAdvanceTime=this.currentTime()-this.pauseAdvanceUpdateTimeDiff)}update(){if(!this.paused){const e=this.currentTime();this.lastAdvanceTime||(this.lastAdvanceTime=e),e-this.lastAdvanceTime>this.advancePeriod?(this.advance(),this.lastAdvanceTime=e):this.updateHead()}}updateNodeID(e,t){this.nodeIDs[e]=t;const r=this.geometry.getAttribute("nodeID"),i=this.geometry.getAttribute("nodeVertexID");for(let n=0;n<this.VerticesPerNode;n++){const a=e*this.VerticesPerNode+n;r.array[a]=t,i.array[a]=n}r.needsUpdate=!0,i.needsUpdate=!0,r.addUpdateRange(e*this.VerticesPerNode,this.VerticesPerNode),i.addUpdateRange(e*this.VerticesPerNode,this.VerticesPerNode)}updateNodeCenter(e,t){this.lastNodeCenter=this.currentNodeCenter,this.currentNodeCenter=this.nodeCenters[e],this.currentNodeCenter.copy(t);const r=this.geometry.getAttribute("nodeCenter");for(let i=0;i<this.VerticesPerNode;i++){const n=3*(e*this.VerticesPerNode+i);r.array[n]=t.x,r.array[n+1]=t.y,r.array[n+2]=t.z}r.needsUpdate=!0,r.updateRanges=[{start:e*this.VerticesPerNode*Trail.PositionComponentCount,count:this.VerticesPerNode*Trail.PositionComponentCount}]}deactivate(){this.active&&(this.scene.remove(this.mesh),this.active=!1)}activate(){this.active||(this.scene.add(this.mesh),this.active=!0)}static getTrailShaderNodes(e){return{position:e?q:_,color:z,fraction:B}}static applyTrailShaderParameters(e){e.transparent=!0,e.alphaTest=.5,e.blending=t.CustomBlending,e.blendSrc=t.SrcAlphaFactor,e.blendDst=t.OneMinusSrcAlphaFactor,e.blendEquation=t.AddEquation,e.depthTest=!0,e.depthWrite=!1,e.side=t.DoubleSide}static createBaseMaterial(t,i,n,a=!1){let{position:o}=Trail.getTrailShaderNodes(a),h=s(I,U,B).rgba,l=h.a;if(null!=t){let r=trailUV;null!=i&&(r=r.add(x(e.elapsed.multiply(-i),0)));const a=u(t).sample(r);switch(n??"red"){case"none":break;case"red":l=l.multiply(a.r);break;case"alpha":l=l.multiply(a.a)}h=y(h.rgb.multiply(a.rgb),l)}const c=new r({position:o,opacity:l,color:h});return Trail.applyTrailShaderParameters(c),c}static get MaxHeadVertices(){return 128}static get LocalOrientationTangent(){return Trail._LocalOrientationTangent}static get LocalHeadOrigin(){return Trail._LocalHeadOrigin}static get PositionComponentCount(){return 3}static get UVComponentCount(){return 2}static get IndicesPerFace(){return 3}static get FacesPerQuad(){return 2}}Trail._LocalOrientationTangent=new t.Vector3(1,0,0),Trail._LocalHeadOrigin=new t.Vector3(0,0,0),Trail.Shader={get BaseVertexVars(){return["attribute float nodeID;","attribute float nodeVertexID;","attribute vec3 nodeCenter;","uniform float minID;","uniform float maxID;","uniform float trailLength;","uniform float maxTrailLength;","uniform float verticesPerNode;","uniform vec2 textureTileFactor;","uniform bool taper;","uniform vec4 headColor;","uniform vec4 tailColor;","varying vec4 vColor;"].join("\n")},get TexturedVertexVars(){return[this.BaseVertexVars,"varying vec2 vUV;","uniform float dragTexture;"].join("\n")},BaseFragmentVars:["varying vec4 vColor;","uniform sampler2D trailTexture;"].join("\n"),get TexturedFragmentVars(){return[this.BaseFragmentVars,"varying vec2 vUV;"].join("\n")},get VertexShaderCore(){return["float fraction = (maxID - nodeID) / (maxID - minID);","float fractionSize = taper ? fraction : 0.0;","vColor = (1.0 - fraction) * headColor + fraction * tailColor;","vec4 realPosition = vec4((1.0 - fractionSize) * position.xyz + fractionSize * nodeCenter.xyz, 1.0); "].join("\n")},get BaseVertexShader(){return[this.BaseVertexVars,"void main() { ",this.VertexShaderCore,"gl_Position = projectionMatrix * viewMatrix * realPosition;","}"].join("\n")},get BaseFragmentShader(){return[this.BaseFragmentVars,"void main() { ","gl_FragColor = vColor;","}"].join("\n")},get TexturedVertexShader(){return[this.TexturedVertexVars,"void main() { ",this.VertexShaderCore,"float s = 0.0;","float t = 0.0;","if (dragTexture == 1.0) { "," s = fraction * textureTileFactor.s; "," t = (nodeVertexID / verticesPerNode) * textureTileFactor.t;","} else { "," s = nodeID / maxTrailLength * textureTileFactor.s;"," t = (nodeVertexID / verticesPerNode) * textureTileFactor.t;","}","vUV = vec2(s, t); ","gl_Position = projectionMatrix * viewMatrix * realPosition;","}"].join("\n")},get TexturedFragmentShader(){return[this.TexturedFragmentVars,"void main() { ","vec4 textureColor = texture2D(trailTexture, vUV);","gl_FragColor = vColor * textureColor;","}"].join("\n")}};/*
|
|
1
|
+
import{timeUniforms as t}from"../../shader-nodes/index.js";import*as e from"three";import{NodeShaderMaterial as i,uniformBool as r,uniformFloat as o,uniformVec2 as s,mix as a,uniformVec4 as n,attributeFloat as h,attributeVec3 as l,select as d,float as u,textureSampler2d as c,attributes as m,uniforms as y,rgba as g,vec4 as p,varying as x,vec2 as f,normalize as v,vec3 as b,cross as T,max as C,length as V}from"three-shader-graph";o("trailLength");const P=o("verticesPerNode"),w=o("minID"),F=o("maxID"),I=(o("dragTexture"),o("maxTrailLength")),D=s("textureTileFactor",new e.Vector2(1,1)),N=n("headColor",new e.Vector4),U=n("tailColor",new e.Vector4),A=r("taper",!1),M=(o("scale",1),h("nodeID")),z=h("nodeVertexID"),B=l("nodeCenter"),H=F.subtract(M).divide(F.subtract(w)),S=d(A,H,u(0)),j=x(u(1).subtract(H).multiplyVec4(N).add(H.multiplyVec4(U))),q=v(y.cameraPosition.subtract(B)),L=v(l("nodeTangent")),G=o("width",1),O=v(T(d(V(L).gt(u(.1)),L,b(1,0,0)),q)).multiplyScalar(z.subtract(u(.5))).multiplyScalar(G),Q=p(u(1).subtract(S).multiplyVec3(O).add(C(S,u(1)).multiplyVec3(B)),1),R=p(u(1).subtract(S).multiplyVec3(m.position).add(S.multiplyVec3(B)),1),W=y.projectionMatrix.multiply(y.viewMatrix).multiplyVec(Q),_=y.projectionMatrix.multiply(y.viewMatrix).multiplyVec(R);export const trailUV=x(f(H.multiply(D.x),z.divide(P).multiply(D.y).multiply(2)));export const trailDragUV=x(f(M.divide(I).multiply(D.x),z.divide(P).multiply(D.y).multiply(2)));const k={position:new e.Vector3,direction:new e.Vector3,offset:new e.Vector3,worldOrientation:new e.Vector3,matrix4:new e.Matrix4,quat1:new e.Quaternion,quat2:new e.Quaternion,quaternion:new e.Quaternion,quaternion2:new e.Quaternion,boundingBox:new e.Box3,localHeadGeometry:Array.from({length:128},()=>new e.Vector3)};export class Trail extends e.Object3D{constructor(t,i){super(),this.historyCount=0,this.historyHeadIndex=-1,this.history=[],this.renderNodeCount=0,this.subdivisions=4,this.currentNodeID=0,this.advance=function(){const t=new e.Matrix4;return function(){this.targetObject.updateMatrixWorld(),t.copy(this.targetObject.matrixWorld),this.advanceWithTransform(t),this.updateUniforms()}}(),this.connectNodes=(()=>{const t={attribute:null,offset:0,count:-1};return(e,i)=>{const r=this.geometry.getIndex();for(let t=0;t<this.localHeadGeometry.length-1;t++){const o=this.VerticesPerNode*e+t,s=this.VerticesPerNode*i+t,a=(e*this.FacesPerNode+t*Trail.FacesPerQuad)*Trail.IndicesPerFace;r.array[a]=o,r.array[a+1]=s,r.array[a+2]=o+1,r.array[a+3]=s,r.array[a+4]=s+1,r.array[a+5]=o+1}return r.needsUpdate=!0,r.clearUpdateRanges(),t.attribute=r,t.offset=e*this.FacesPerNode*Trail.IndicesPerFace,t.count=this.FacesPerNode*Trail.IndicesPerFace,t}})(),this.disconnectNodes=(()=>{const t={attribute:null,offset:0,count:-1};return e=>{const i=this.geometry.getIndex();for(let t=0;t<this.localHeadGeometry.length-1;t++){const r=(e*this.FacesPerNode+t*Trail.FacesPerQuad)*Trail.IndicesPerFace;i.array[r]=0,i.array[r+1]=0,i.array[r+2]=0,i.array[r+3]=0,i.array[r+4]=0,i.array[r+5]=0}return i.needsUpdate=!0,i.clearUpdateRanges(),t.attribute=i,t.offset=e*this.FacesPerNode*Trail.IndicesPerFace,t.count=this.FacesPerNode*Trail.IndicesPerFace,t}})(),this.active=!1,this.orientToMovement=!1,i&&(this.orientToMovement=!0),this.scene=t,this.geometry=null,this.mesh=null,this.historyCount=0,this.historyHeadIndex=-1,this.history=[],this.renderNodeCount=0,this.currentNodeID=0,this.advanceFrequency=60,this.advancePeriod=1/this.advanceFrequency,this.lastAdvanceTime=0,this.paused=!1,this.pauseAdvanceUpdateTimeDiff=0}setAdvanceFrequency(t){this.advanceFrequency=t,this.advancePeriod=1/this.advanceFrequency}initialize(t,i,r,o,s,a,n=!1){this.deactivate(),this.destroyMesh(),this.length=i>0?i+1:0,this.dragTexture=r?1:0,this.targetObject=a,this.billboard=n,this.initializeLocalHeadGeometry(o,s),null!=t.uniforms.width&&(t.uniforms.width.value=o),this.renderNodeCount=this.length*this.subdivisions,this.historyCount=0,this.historyHeadIndex=-1,this.history=[];for(let t=0;t<this.length;t++)this.history.push({position:new e.Vector3,tangent:new e.Vector3(1,0,0),transformMatrix:new e.Matrix4,id:-1});this.setAllNodesToCurrentTargetPosition(),this.material=t,this.initializeGeometry(),this.initializeMesh(),this.material.uniforms.dragTexture&&(this.material.uniforms.trailLength.value=0),this.material.uniforms.minID.value=0,this.material.uniforms.maxID.value=0,this.material.uniforms.dragTexture&&(this.material.uniforms.dragTexture.value=this.dragTexture),this.material.uniforms.maxTrailLength&&(this.material.uniforms.maxTrailLength.value=this.length),this.material.uniforms.verticesPerNode&&(this.material.uniforms.verticesPerNode.value=this.VerticesPerNode),this.material.uniforms.textureTileFactor&&(this.material.uniforms.textureTileFactor.value=new e.Vector2(1,1)),this.material.uniforms.scale&&(this.material.uniforms.scale.value=1),this.reset()}initializeLocalHeadGeometry(t,i){if(this.localHeadGeometry=[],i){this.VerticesPerNode=0;for(let t=0;t<i.length&&t<Trail.MaxHeadVertices;t++){const r=i[t];if(r&&r instanceof e.Vector3){const t=new e.Vector3;t.copy(r),this.localHeadGeometry.push(t),this.VerticesPerNode++}}}else{const i=(t||1)/2;this.localHeadGeometry.push(new e.Vector3(-i,0,0)),this.localHeadGeometry.push(new e.Vector3(i,0,0)),this.VerticesPerNode=2}this.FacesPerNode=2*(this.VerticesPerNode-1),this.FaceIndicesPerNode=3*this.FacesPerNode}initializeGeometry(){this.vertexCount=this.renderNodeCount*this.VerticesPerNode,this.faceCount=this.renderNodeCount*this.FacesPerNode;const t=new e.BufferGeometry,i=new Float32Array(this.vertexCount),r=new Float32Array(this.vertexCount*this.VerticesPerNode),o=new Float32Array(this.vertexCount*Trail.PositionComponentCount),s=new Float32Array(this.vertexCount*Trail.PositionComponentCount),a=new Float32Array(this.vertexCount*Trail.UVComponentCount),n=new Uint32Array(this.faceCount*Trail.IndicesPerFace),h=new Float32Array(3*this.vertexCount),l=new e.BufferAttribute(h,3);l.setUsage(e.DynamicDrawUsage),t.setAttribute("nodeTangent",l);const d=new e.BufferAttribute(i,1);d.setUsage(e.DynamicDrawUsage),t.setAttribute("nodeID",d);const u=new e.BufferAttribute(r,1);u.setUsage(e.DynamicDrawUsage),t.setAttribute("nodeVertexID",u);const c=new e.BufferAttribute(s,Trail.PositionComponentCount);c.setUsage(e.DynamicDrawUsage),t.setAttribute("nodeCenter",c);const m=new e.BufferAttribute(o,Trail.PositionComponentCount);m.setUsage(e.DynamicDrawUsage),t.setAttribute("position",m);const y=new e.BufferAttribute(a,Trail.UVComponentCount);y.setUsage(e.DynamicDrawUsage),t.setAttribute("uv",y);const g=new e.BufferAttribute(n,1);g.setUsage(e.DynamicDrawUsage),t.setIndex(g),this.geometry=t}zeroVertices(){const t=this.geometry.getAttribute("position");for(let e=0;e<this.vertexCount;e++){const i=3*e;t.array[i]=0,t.array[i+1]=0,t.array[i+2]=0}t.needsUpdate=!0}zeroIndices(){if(null==this.geometry)throw"Geometry not initialized";const t=this.geometry.getIndex();for(let e=0;e<this.faceCount;e++){const i=3*e;t.array[i]=0,t.array[i+1]=0,t.array[i+2]=0}t.needsUpdate=!0,t.clearUpdateRanges()}formInitialFaces(){this.zeroIndices();const t=this.geometry.getIndex();for(let t=0;t<this.renderNodeCount-1;t++)this.connectNodes(t,t+1);t.needsUpdate=!0,t.clearUpdateRanges()}initializeMesh(){if(null==this.geometry)throw"Geometry not initialized";this.mesh=new e.Mesh(this.geometry,this.material),this.mesh.matrixAutoUpdate=!1,this.mesh.visible=!1}destroyMesh(){this.mesh&&(this.scene.remove(this.mesh),this.mesh=null)}reset(){this.historyCount=0,this.historyHeadIndex=-1,this.currentNodeID=0,this.formInitialFaces(),this.zeroVertices(),this.geometry.setDrawRange(0,0),this.setAllNodesToCurrentTargetPosition(),this.mesh&&(this.mesh.visible=!0)}setAllNodesToCurrentTargetPosition(){if(!this.targetObject)return;this.targetObject.updateMatrixWorld();const t=this.targetObject.matrixWorld,i=(new e.Vector3).setFromMatrixPosition(t),r=new e.Vector3(1,0,0).applyMatrix4(t).normalize();for(let e=0;e<this.length;e++)this.history[e].position.copy(i),this.history[e].tangent.copy(r),this.history[e].transformMatrix.copy(t),this.history[e].id=-1;this.historyCount=0,this.historyHeadIndex=-1}updateUniforms(){if(0===this.historyCount)return;const t=this.getControlPoint(0).id,e=this.getControlPoint(this.historyCount-1).id;this.material.uniforms.minID.value=t,this.material.uniforms.maxID.value=e,this.material.uniforms.trailLength&&(this.material.uniforms.trailLength.value=this.historyCount),this.material.uniforms.maxTrailLength&&(this.material.uniforms.maxTrailLength.value=this.length),this.material.uniforms.verticesPerNode&&(this.material.uniforms.verticesPerNode.value=this.VerticesPerNode)}advanceWithTransform(t){this.historyCount<this.length&&this.historyCount++,this.historyHeadIndex=(this.historyHeadIndex+1)%this.length;const e=this.history[this.historyHeadIndex];if(e.id=this.currentNodeID++,k.position.set(0,0,0).applyMatrix4(t),e.position.copy(k.position),e.transformMatrix.copy(t),this.billboard){const i=this.getControlPoint(this.historyCount-2);i&&!i.position.equals(k.position)?e.tangent.copy(k.position).sub(i.position).normalize():e.tangent.set(1,0,0).applyMatrix4(t).normalize()}this.rebuildGeometry()}updateHead(){if(0===this.historyCount)return;this.targetObject.updateMatrixWorld();const t=this.targetObject.matrixWorld,e=this.history[this.historyHeadIndex];if(k.position.set(0,0,0).applyMatrix4(t),e.position.copy(k.position),e.transformMatrix.copy(t),this.billboard){const i=this.getControlPoint(this.historyCount-2);i&&!i.position.equals(k.position)?e.tangent.copy(k.position).sub(i.position).normalize():e.tangent.set(1,0,0).applyMatrix4(t).normalize()}this.rebuildGeometry()}getControlPoint(t){if(t<0)return this.history[(this.historyHeadIndex-this.historyCount+1+this.length)%this.length];t>=this.historyCount&&(t=this.historyCount-1);const e=(this.historyHeadIndex-this.historyCount+1+this.length)%this.length;return this.history[(e+t)%this.length]}rebuildGeometry(){if(this.historyCount<2)return this.geometry.setDrawRange(0,0),void(this.mesh&&(this.mesh.visible=!1));const t=this.geometry.getAttribute("position"),i=this.geometry.getAttribute("nodeID"),r=this.geometry.getAttribute("nodeVertexID"),o=this.geometry.getAttribute("nodeCenter"),s=this.geometry.getAttribute("nodeTangent"),a=this.historyCount-1,n=a*this.subdivisions+1;let h=0;k.boundingBox.makeEmpty();for(let e=0;e<a;e++){const n=this.getControlPoint(e-1),l=this.getControlPoint(e),d=this.getControlPoint(e+1),u=this.getControlPoint(e+2),c=e===a-1?this.subdivisions+1:this.subdivisions;for(let e=0;e<c;e++){const a=e/this.subdivisions,c=n.position,m=l.position,y=d.position,g=u.position,p=a*a,x=p*a,f=.5*(y.x-c.x),v=.5*(g.x-m.x),b=(2*(m.x-y.x)+f+v)*x+(-3*(m.x-y.x)-2*f-v)*p+f*a+m.x,T=.5*(y.y-c.y),C=.5*(g.y-m.y),V=(2*(m.y-y.y)+T+C)*x+(-3*(m.y-y.y)-2*T-C)*p+T*a+m.y,P=.5*(y.z-c.z),w=.5*(g.z-m.z),F=(2*(m.z-y.z)+P+w)*x+(-3*(m.z-y.z)-2*P-w)*p+P*a+m.z;k.position.set(b,V,F);const I=l.id+(d.id-l.id)*a,D=k.direction.copy(l.tangent).lerp(d.tangent,a).normalize();k.matrix4.copy(l.transformMatrix),k.quat1.setFromRotationMatrix(l.transformMatrix),k.quat2.setFromRotationMatrix(d.transformMatrix),k.quaternion.copy(k.quat1).slerp(k.quat2,a),k.matrix4.makeRotationFromQuaternion(k.quaternion),k.matrix4.setPosition(k.position);for(let e=0;e<this.VerticesPerNode;e++){const n=h*this.VerticesPerNode+e;i.array[n]=I,r.array[n]=e,o.array[3*n]=k.position.x,o.array[3*n+1]=k.position.y,o.array[3*n+2]=k.position.z;const l=k.localHeadGeometry[e].copy(this.localHeadGeometry[e]);if(this.billboard)s.array[3*n]=D.x,s.array[3*n+1]=D.y,s.array[3*n+2]=D.z,k.boundingBox.expandByPoint(k.position);else{if(l.applyMatrix4(k.matrix4),this.orientToMovement){k.worldOrientation.set(0,0,-1).applyQuaternion(k.quaternion);const t=(3*p-2*a)*(m.x-y.x)+(x-p)*v+(3*p-4*a+1)*f,e=(3*p-2*a)*(m.y-y.y)+(x-p)*C+(3*p-4*a+1)*T,i=(3*p-2*a)*(m.z-y.z)+(x-p)*w+(3*p-4*a+1)*P;k.direction.set(t,e,i).normalize(),k.direction.lengthSq()>1e-4&&(k.quaternion2.setFromUnitVectors(k.worldOrientation,k.direction),l.sub(k.position).applyQuaternion(k.quaternion2).add(k.position))}t.array[3*n]=l.x,t.array[3*n+1]=l.y,t.array[3*n+2]=l.z,k.boundingBox.expandByPoint(l)}}h++}}if(i.needsUpdate=!0,r.needsUpdate=!0,o.needsUpdate=!0,this.billboard){s.needsUpdate=!0,this.mesh.frustumCulled=!1;const t=this.material.uniforms.width?.5*this.material.uniforms.width.value:.5;k.boundingBox.min.subScalar(t),k.boundingBox.max.addScalar(t)}else t.needsUpdate=!0;this.geometry.boundingBox=this.geometry.boundingBox||new e.Box3,this.geometry.boundingBox.copy(k.boundingBox),this.geometry.boundingSphere=this.geometry.boundingSphere||new e.Sphere,this.geometry.boundingBox.getBoundingSphere(this.geometry.boundingSphere),this.mesh.visible=!k.boundingBox.isEmpty(),this.geometry.setDrawRange(0,(n-1)*this.FaceIndicesPerNode)}currentTime(){return performance.now()/1e3}pause(){this.paused||(this.paused=!0,this.pauseAdvanceUpdateTimeDiff=this.currentTime()-this.lastAdvanceTime)}resume(){this.paused&&(this.paused=!1,this.lastAdvanceTime=this.currentTime()-this.pauseAdvanceUpdateTimeDiff)}update(){if(!this.paused){const t=this.currentTime();this.lastAdvanceTime||(this.lastAdvanceTime=t),t-this.lastAdvanceTime>this.advancePeriod?(this.advance(),this.lastAdvanceTime=t):this.updateHead()}}deactivate(){this.active&&(this.scene.remove(this.mesh),this.active=!1)}activate(){this.active||(this.scene.add(this.mesh),this.active=!0)}static getTrailShaderNodes(t){return{position:t?W:_,color:j,fraction:H}}static applyTrailShaderParameters(t){t.transparent=!0,t.alphaTest=.5,t.blending=e.CustomBlending,t.blendSrc=e.SrcAlphaFactor,t.blendDst=e.OneMinusSrcAlphaFactor,t.blendEquation=e.AddEquation,t.depthTest=!0,t.depthWrite=!1,t.side=e.DoubleSide}static createBaseMaterial(e,r,o,s=!1){let{position:n}=Trail.getTrailShaderNodes(s),h=a(N,U,H).rgba,l=h.a;if(null!=e){let i=trailUV;null!=r&&(i=i.add(f(t.elapsed.multiply(-r),0)));const s=c(e).sample(i);switch(o??"red"){case"none":break;case"red":l=l.multiply(s.r);break;case"alpha":l=l.multiply(s.a)}h=g(h.rgb.multiply(s.rgb),l)}const d=new i({position:n,opacity:l,color:h});return Trail.applyTrailShaderParameters(d),d}static get MaxHeadVertices(){return 128}static get LocalOrientationTangent(){return Trail._LocalOrientationTangent}static get LocalHeadOrigin(){return Trail._LocalHeadOrigin}static get PositionComponentCount(){return 3}static get UVComponentCount(){return 2}static get IndicesPerFace(){return 3}static get FacesPerQuad(){return 2}}Trail._LocalOrientationTangent=new e.Vector3(1,0,0),Trail._LocalHeadOrigin=new e.Vector3(0,0,0),Trail.Shader={get BaseVertexVars(){return["attribute float nodeID;","attribute float nodeVertexID;","attribute vec3 nodeCenter;","uniform float minID;","uniform float maxID;","uniform float trailLength;","uniform float maxTrailLength;","uniform float verticesPerNode;","uniform vec2 textureTileFactor;","uniform bool taper;","uniform vec4 headColor;","uniform vec4 tailColor;","varying vec4 vColor;"].join("\n")},get TexturedVertexVars(){return[this.BaseVertexVars,"varying vec2 vUV;","uniform float dragTexture;"].join("\n")},BaseFragmentVars:["varying vec4 vColor;","uniform sampler2D trailTexture;"].join("\n"),get TexturedFragmentVars(){return[this.BaseFragmentVars,"varying vec2 vUV;"].join("\n")},get VertexShaderCore(){return["float fraction = (maxID - nodeID) / (maxID - minID);","float fractionSize = taper ? fraction : 0.0;","vColor = (1.0 - fraction) * headColor + fraction * tailColor;","vec4 realPosition = vec4((1.0 - fractionSize) * position.xyz + fractionSize * nodeCenter.xyz, 1.0); "].join("\n")},get BaseVertexShader(){return[this.BaseVertexVars,"void main() { ",this.VertexShaderCore,"gl_Position = projectionMatrix * viewMatrix * realPosition;","}"].join("\n")},get BaseFragmentShader(){return[this.BaseFragmentVars,"void main() { ","gl_FragColor = vColor;","}"].join("\n")},get TexturedVertexShader(){return[this.TexturedVertexVars,"void main() { ",this.VertexShaderCore,"float s = 0.0;","float t = 0.0;","if (dragTexture == 1.0) { "," s = fraction * textureTileFactor.s; "," t = (nodeVertexID / verticesPerNode) * textureTileFactor.t;","} else { "," s = nodeID / maxTrailLength * textureTileFactor.s;"," t = (nodeVertexID / verticesPerNode) * textureTileFactor.t;","}","vUV = vec2(s, t); ","gl_Position = projectionMatrix * viewMatrix * realPosition;","}"].join("\n")},get TexturedFragmentShader(){return[this.TexturedFragmentVars,"void main() { ","vec4 textureColor = texture2D(trailTexture, vUV);","gl_FragColor = vColor * textureColor;","}"].join("\n")}};/*
|
|
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{__decorate as t,__metadata as e}from"tslib";import o,{ignore as i}from"@plumier/reflect";import{Subject as n,skipWhile as s}from"rxjs";import{Group as r}from"three";import a,{Service as p}from"typedi";import{ActorComponent as c}from"./component.js";import{
|
|
1
|
+
import{__decorate as t,__metadata as e}from"tslib";import o,{ignore as i}from"@plumier/reflect";import{Subject as n,skipWhile as s}from"rxjs";import{Group as r}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 h,containerRefMap as m}from"./internal/container-map.js";import{randomString as u}from"../../utils/math.js";import{inject as l}from"../inject.js";import{ViewController as f}from"../services/render.js";export function Actor(){const t=p({transient:!0});return function(e){e.__isActor=!0,t(e),o.noop()(e),o.parameterProperties()(e)}}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(){}onUpdate(t){}onLateUpdate(t){}constructor(){this.id=++U,this.__isInitialised=!1,this.object=new r,this.disposed=new n,this.attachedComponents=[],this.onUpdate!==BaseActor.prototype.onUpdate&&l(f).onUpdate(this).pipe(s(()=>!this.__isInitialised)).subscribe(t=>this.onUpdate(t)),this.onLateUpdate!==BaseActor.prototype.onLateUpdate&&l(f).onLateUpdate(this).pipe(s(()=>!this.__isInitialised)).subscribe(t=>this.onLateUpdate(t))}attach(t,e){const o=h.value,i=m.get(this)??h.value??a.of("default");h.value=i;const n=u();i.set({id:n,type:t,transient:!0});const s=i.get(n);if(h.value=o,null!=e)for(const t of Object.keys(e))s[t]=e[t];return this.__isInitialised&&(s.actor=this,s.onInit(),d(s,this)),this.attachedComponents.push(s),s}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&&l(f).onUpdate(this.actor).pipe(s(()=>!this.actor?.__isInitialised)).subscribe(t=>this.onUpdate(t)),this.onLateUpdate!==c.prototype.onLateUpdate&&l(f).onLateUpdate(this.actor).pipe(s(()=>!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
|
*/
|