@needle-tools/engine 5.1.0-alpha.3 → 5.1.0-alpha.5
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/CHANGELOG.md +68 -0
- package/SKILL.md +4 -1
- package/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-DF01sSGQ.js → needle-engine.bundle-C-LG00ZZ.js} +10681 -10100
- package/dist/needle-engine.bundle-D7tzaiYE.min.js +1733 -0
- package/dist/{needle-engine.bundle-C-ixARur.umd.cjs → needle-engine.bundle-OPkPmdUM.umd.cjs} +161 -161
- package/dist/needle-engine.d.ts +1349 -317
- package/dist/needle-engine.js +556 -555
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/three.js +1 -0
- package/dist/three.min.js +21 -21
- package/dist/three.umd.cjs +16 -16
- package/lib/engine/api.d.ts +5 -0
- package/lib/engine/api.js +4 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/codegen/register_types.js +10 -18
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_camera.fit.js +16 -4
- package/lib/engine/engine_camera.fit.js.map +1 -1
- package/lib/engine/engine_context.d.ts +20 -7
- package/lib/engine/engine_context.js +31 -15
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_context_eventbus.d.ts +47 -0
- package/lib/engine/engine_context_eventbus.js +47 -0
- package/lib/engine/engine_context_eventbus.js.map +1 -0
- package/lib/engine/engine_disposable.d.ts +172 -0
- package/lib/engine/engine_disposable.js +136 -0
- package/lib/engine/engine_disposable.js.map +1 -0
- package/lib/engine/engine_gameobject.d.ts +1 -10
- package/lib/engine/engine_gameobject.js +20 -118
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_gltf_builtin_components.js +7 -69
- package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
- package/lib/engine/engine_input.d.ts +23 -4
- package/lib/engine/engine_input.js +2 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
- package/lib/engine/engine_instantiate_resolve.js +372 -0
- package/lib/engine/engine_instantiate_resolve.js.map +1 -0
- package/lib/engine/engine_mainloop_utils.js +2 -2
- package/lib/engine/engine_mainloop_utils.js.map +1 -1
- package/lib/engine/engine_networking.d.ts +51 -37
- package/lib/engine/engine_networking.js +132 -82
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_networking.transport.websocket.d.ts +15 -0
- package/lib/engine/engine_networking.transport.websocket.js +38 -0
- package/lib/engine/engine_networking.transport.websocket.js.map +1 -0
- package/lib/engine/engine_networking_instantiate.js +2 -2
- package/lib/engine/engine_networking_instantiate.js.map +1 -1
- package/lib/engine/engine_networking_types.d.ts +39 -1
- package/lib/engine/engine_networking_types.js +7 -0
- package/lib/engine/engine_networking_types.js.map +1 -1
- package/lib/engine/engine_physics_rapier.d.ts +21 -3
- package/lib/engine/engine_physics_rapier.js +94 -25
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.js +1 -5
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_serialization_core.d.ts +1 -0
- package/lib/engine/engine_serialization_core.js +7 -0
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_types.d.ts +29 -11
- package/lib/engine/engine_types.js +1 -1
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_util_decorator.js +7 -2
- package/lib/engine/engine_util_decorator.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +1 -1
- package/lib/engine/engine_utils.js +19 -5
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine-components/AnimationBuilder.d.ts +158 -0
- package/lib/engine-components/AnimationBuilder.js +305 -0
- package/lib/engine-components/AnimationBuilder.js.map +1 -0
- package/lib/engine-components/Animator.d.ts +6 -0
- package/lib/engine-components/Animator.js +23 -13
- package/lib/engine-components/Animator.js.map +1 -1
- package/lib/engine-components/AnimatorController.builder.d.ts +191 -0
- package/lib/engine-components/AnimatorController.builder.js +263 -0
- package/lib/engine-components/AnimatorController.builder.js.map +1 -0
- package/lib/engine-components/AnimatorController.d.ts +2 -119
- package/lib/engine-components/AnimatorController.js +33 -232
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/Collider.d.ts +18 -9
- package/lib/engine-components/Collider.js +61 -14
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.d.ts +72 -9
- package/lib/engine-components/Component.js +114 -10
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/ContactShadows.d.ts +1 -0
- package/lib/engine-components/ContactShadows.js +14 -1
- package/lib/engine-components/ContactShadows.js.map +1 -1
- package/lib/engine-components/DragControls.js +0 -7
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DropListener.js +3 -0
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/EventList.d.ts +31 -9
- package/lib/engine-components/EventList.js +37 -76
- package/lib/engine-components/EventList.js.map +1 -1
- package/lib/engine-components/Joints.d.ts +4 -2
- package/lib/engine-components/Joints.js +19 -3
- package/lib/engine-components/Joints.js.map +1 -1
- package/lib/engine-components/Light.js +9 -1
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +0 -2
- package/lib/engine-components/OrbitControls.js +14 -1
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/RigidBody.d.ts +12 -4
- package/lib/engine-components/RigidBody.js +18 -4
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +3 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/api.d.ts +2 -1
- package/lib/engine-components/api.js +2 -1
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +7 -13
- package/lib/engine-components/codegen/components.js +7 -13
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
- package/lib/engine-components/timeline/PlayableDirector.d.ts +21 -11
- package/lib/engine-components/timeline/PlayableDirector.js +75 -67
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
- package/lib/engine-components/timeline/SignalAsset.js +1 -0
- package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
- package/lib/engine-components/timeline/TimelineBuilder.d.ts +413 -0
- package/lib/engine-components/timeline/TimelineBuilder.js +506 -0
- package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
- package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
- package/lib/engine-components/timeline/TimelineModels.js +3 -0
- package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +37 -6
- package/lib/engine-components/timeline/TimelineTracks.js +92 -26
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/engine-components/timeline/index.d.ts +2 -1
- package/lib/engine-components/timeline/index.js +2 -0
- package/lib/engine-components/timeline/index.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +0 -1
- package/lib/engine-components/web/CursorFollow.js +0 -1
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/package.json +2 -83
- package/plugins/common/cloud.js +6 -1
- package/plugins/common/license.js +5 -2
- package/plugins/common/worker.js +9 -4
- package/plugins/vite/dependencies.js +1 -11
- package/plugins/vite/dependency-watcher.js +2 -2
- package/plugins/vite/editor-connection.js +3 -3
- package/plugins/vite/license.js +19 -1
- package/plugins/vite/reload.js +1 -1
- package/plugins/vite/server.js +2 -1
- package/src/engine/api.ts +7 -0
- package/src/engine/codegen/register_types.ts +10 -18
- package/src/engine/engine_camera.fit.ts +15 -4
- package/src/engine/engine_context.ts +32 -16
- package/src/engine/engine_context_eventbus.ts +73 -0
- package/src/engine/engine_disposable.ts +214 -0
- package/src/engine/engine_gameobject.ts +52 -157
- package/src/engine/engine_gltf_builtin_components.ts +7 -76
- package/src/engine/engine_input.ts +27 -6
- package/src/engine/engine_instantiate_resolve.ts +407 -0
- package/src/engine/engine_mainloop_utils.ts +2 -2
- package/src/engine/engine_networking.transport.websocket.ts +45 -0
- package/src/engine/engine_networking.ts +161 -137
- package/src/engine/engine_networking_instantiate.ts +2 -2
- package/src/engine/engine_networking_types.ts +41 -1
- package/src/engine/engine_physics_rapier.ts +102 -33
- package/src/engine/engine_serialization_builtin_serializer.ts +1 -6
- package/src/engine/engine_serialization_core.ts +9 -0
- package/src/engine/engine_types.ts +46 -27
- package/src/engine/engine_util_decorator.ts +7 -2
- package/src/engine/engine_utils.ts +16 -5
- package/src/engine-components/AnimationBuilder.ts +472 -0
- package/src/engine-components/Animator.ts +24 -12
- package/src/engine-components/AnimatorController.builder.ts +387 -0
- package/src/engine-components/AnimatorController.ts +20 -291
- package/src/engine-components/Collider.ts +66 -18
- package/src/engine-components/Component.ts +118 -20
- package/src/engine-components/ContactShadows.ts +15 -1
- package/src/engine-components/DragControls.ts +0 -9
- package/src/engine-components/DropListener.ts +3 -0
- package/src/engine-components/EventList.ts +45 -83
- package/src/engine-components/Joints.ts +20 -4
- package/src/engine-components/Light.ts +10 -2
- package/src/engine-components/OrbitControls.ts +16 -5
- package/src/engine-components/RigidBody.ts +18 -4
- package/src/engine-components/SceneSwitcher.ts +3 -0
- package/src/engine-components/api.ts +2 -1
- package/src/engine-components/codegen/components.ts +7 -13
- package/src/engine-components/timeline/PlayableDirector.ts +83 -81
- package/src/engine-components/timeline/SignalAsset.ts +4 -1
- package/src/engine-components/timeline/TimelineBuilder.ts +824 -0
- package/src/engine-components/timeline/TimelineModels.ts +5 -1
- package/src/engine-components/timeline/TimelineTracks.ts +96 -27
- package/src/engine-components/timeline/index.ts +2 -1
- package/src/engine-components/web/CursorFollow.ts +0 -1
- package/dist/needle-engine.bundle-CHmXdnE1.min.js +0 -1733
- package/lib/engine-components/AvatarLoader.d.ts +0 -80
- package/lib/engine-components/AvatarLoader.js +0 -232
- package/lib/engine-components/AvatarLoader.js.map +0 -1
- package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
- package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
- package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
- package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
- package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
- package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
- package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
- package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
- package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
- package/src/engine-components/AvatarLoader.ts +0 -264
- package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
- package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
- package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
- package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
- package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { AnimationClip, Object3D } from "three";
|
|
2
|
+
import type { Light, Material, PerspectiveCamera } from "three";
|
|
3
|
+
|
|
4
|
+
import { AnimatorConditionMode, AnimatorControllerParameterType } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
5
|
+
import type { AnimatorControllerModel, Condition, Parameter, State, Transition } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
6
|
+
import { InstantiateIdProvider } from "../engine/engine_networking_instantiate.js";
|
|
7
|
+
import { AnimatorController } from "./AnimatorController.js";
|
|
8
|
+
import { resolveClipSource, track as trackFn, type TrackDescriptor, type TrackOptions, type AnimationKeyframe, type Tween, type Vec3Value, type QuatValue, type EulerValue, type ColorValue } from "./AnimationBuilder.js";
|
|
9
|
+
|
|
10
|
+
/** Keyframe array or tween shorthand */
|
|
11
|
+
type KF<V> = AnimationKeyframe<V>[] | Tween<V>;
|
|
12
|
+
|
|
13
|
+
/** Extracts parameter names of a given type from the builder's tracked parameter map */
|
|
14
|
+
type ParamNamesOfType<TParams, PType extends string> = {
|
|
15
|
+
[K in keyof TParams & string]: TParams[K] extends PType ? K : never
|
|
16
|
+
}[keyof TParams & string];
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for an animation state in the builder
|
|
21
|
+
*/
|
|
22
|
+
export declare type StateOptions = {
|
|
23
|
+
/**
|
|
24
|
+
* The animation clip for this state. Accepts:
|
|
25
|
+
* - A pre-built `AnimationClip`
|
|
26
|
+
* - A single {@link TrackDescriptor} from {@link track}
|
|
27
|
+
* - An array of {@link TrackDescriptor}s (multiple tracks combined into one clip)
|
|
28
|
+
*
|
|
29
|
+
* When omitted, use {@link AnimatorControllerBuilder.track .track()} to define animation tracks inline.
|
|
30
|
+
*/
|
|
31
|
+
clip?: AnimationClip | TrackDescriptor | TrackDescriptor[];
|
|
32
|
+
/** Whether the animation should loop (default: false) */
|
|
33
|
+
loop?: boolean;
|
|
34
|
+
/** Base speed multiplier (default: 1) */
|
|
35
|
+
speed?: number;
|
|
36
|
+
/** Name of a float parameter to multiply with speed */
|
|
37
|
+
speedParameter?: string;
|
|
38
|
+
/** Normalized cycle offset 0-1 (default: 0) */
|
|
39
|
+
cycleOffset?: number;
|
|
40
|
+
/** Name of a float parameter to use as cycle offset */
|
|
41
|
+
cycleOffsetParameter?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Configuration for a transition in the builder
|
|
46
|
+
*/
|
|
47
|
+
export declare type TransitionOptions = {
|
|
48
|
+
/** Duration of the crossfade in seconds (default: 0) */
|
|
49
|
+
duration?: number;
|
|
50
|
+
/** Normalized exit time 0-1. When set, the transition waits until the source animation reaches this point before transitioning. */
|
|
51
|
+
exitTime?: number;
|
|
52
|
+
/** Normalized offset into the destination state's animation (default: 0) */
|
|
53
|
+
offset?: number;
|
|
54
|
+
/** Whether duration is in seconds (true) or normalized (false) (default: false) */
|
|
55
|
+
hasFixedDuration?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** String condition modes for the builder, mapped to {@link AnimatorConditionMode} */
|
|
59
|
+
export type ConditionMode = "if" | "ifNot" | "greater" | "less" | "equals" | "notEqual";
|
|
60
|
+
|
|
61
|
+
function conditionModeToEnum(mode: ConditionMode): AnimatorConditionMode {
|
|
62
|
+
switch (mode) {
|
|
63
|
+
case "if": return AnimatorConditionMode.If;
|
|
64
|
+
case "ifNot": return AnimatorConditionMode.IfNot;
|
|
65
|
+
case "greater": return AnimatorConditionMode.Greater;
|
|
66
|
+
case "less": return AnimatorConditionMode.Less;
|
|
67
|
+
case "equals": return AnimatorConditionMode.Equals;
|
|
68
|
+
case "notEqual": return AnimatorConditionMode.NotEqual;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type BuilderTransition = {
|
|
73
|
+
to: string;
|
|
74
|
+
options: TransitionOptions;
|
|
75
|
+
conditions: Array<{ parameter: string; mode: ConditionMode; threshold: number }>;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type BuilderState = {
|
|
79
|
+
name: string;
|
|
80
|
+
options: StateOptions;
|
|
81
|
+
inlineTracks: TrackDescriptor[];
|
|
82
|
+
transitions: BuilderTransition[];
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A fluent builder for creating {@link AnimatorController} instances from code.
|
|
87
|
+
*
|
|
88
|
+
* Use {@link AnimatorControllerBuilder.create} or {@link AnimatorController.build} to create a new builder.
|
|
89
|
+
*
|
|
90
|
+
* The builder tracks state names and parameter types through the fluent chain,
|
|
91
|
+
* providing autocomplete for state names in `.transition()` and type-aware
|
|
92
|
+
* `.condition()` calls (e.g., trigger parameters don't require a mode argument).
|
|
93
|
+
*
|
|
94
|
+
* @example With pre-built AnimationClips
|
|
95
|
+
* ```ts
|
|
96
|
+
* const controller = AnimatorControllerBuilder.create("CharacterController")
|
|
97
|
+
* .floatParameter("Speed", 0)
|
|
98
|
+
* .triggerParameter("Jump")
|
|
99
|
+
* .state("Idle", { clip: idleClip, loop: true })
|
|
100
|
+
* .state("Walk", { clip: walkClip, loop: true })
|
|
101
|
+
* .state("Jump", { clip: jumpClip })
|
|
102
|
+
* .transition("Idle", "Walk", { duration: 0.25 })
|
|
103
|
+
* .condition("Speed", "greater", 0.1)
|
|
104
|
+
* .transition("Walk", "Idle", { duration: 0.25 })
|
|
105
|
+
* .condition("Speed", "less", 0.1)
|
|
106
|
+
* .transition("*", "Jump", { duration: 0.1 })
|
|
107
|
+
* .condition("Jump")
|
|
108
|
+
* .transition("Jump", "Idle", { hasExitTime: true, exitTime: 0.9, duration: 0.25 })
|
|
109
|
+
* .build();
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @example With inline tracks (no pre-built clips needed)
|
|
113
|
+
* ```ts
|
|
114
|
+
* const controller = AnimatorControllerBuilder.create("Door")
|
|
115
|
+
* .boolParameter("Open", false)
|
|
116
|
+
* .state("Closed", { loop: true })
|
|
117
|
+
* .track(door, "position", { from: [0, 0, 0], to: [0, 0, 0], duration: 1 })
|
|
118
|
+
* .state("Open", { loop: true })
|
|
119
|
+
* .track(door, "position", { from: [0, 0, 0], to: [2, 0, 0], duration: 1 })
|
|
120
|
+
* .track(light, "intensity", { from: 0, to: 5, duration: 1 })
|
|
121
|
+
* .transition("Closed", "Open", { duration: 0.25 })
|
|
122
|
+
* .condition("Open", "if")
|
|
123
|
+
* .transition("Open", "Closed", { duration: 0.25 })
|
|
124
|
+
* .condition("Open", "ifNot")
|
|
125
|
+
* .build(room);
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @typeParam TStates - Union of state names added via `.state()`. Used for autocomplete and validation in `.transition()` and `.defaultState()`.
|
|
129
|
+
* @typeParam TParams - Record mapping parameter names to their types (`"trigger"`, `"bool"`, `"float"`, `"int"`). Used for type-aware `.condition()` overloads.
|
|
130
|
+
*
|
|
131
|
+
* @category Animation and Sequencing
|
|
132
|
+
* @group Utilities
|
|
133
|
+
*/
|
|
134
|
+
export class AnimatorControllerBuilder<
|
|
135
|
+
TStates extends string = never,
|
|
136
|
+
TParams extends Record<string, "trigger" | "bool" | "float" | "int"> = {},
|
|
137
|
+
> {
|
|
138
|
+
private _name: string;
|
|
139
|
+
private _parameters: Parameter[] = [];
|
|
140
|
+
private _states: BuilderState[] = [];
|
|
141
|
+
private _anyStateTransitions: BuilderTransition[] = [];
|
|
142
|
+
private _defaultStateName: string | null = null;
|
|
143
|
+
private _lastTransition: BuilderTransition | null = null;
|
|
144
|
+
private _lastState: BuilderState | null = null;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Creates a new AnimatorControllerBuilder instance.
|
|
148
|
+
* @param name - Optional name for the controller
|
|
149
|
+
*/
|
|
150
|
+
static create(name?: string): AnimatorControllerBuilder {
|
|
151
|
+
return new AnimatorControllerBuilder(name);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
constructor(name?: string) {
|
|
155
|
+
this._name = name ?? "AnimatorController";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Adds a float parameter */
|
|
159
|
+
floatParameter<N extends string>(name: N, defaultValue: number = 0): AnimatorControllerBuilder<TStates, TParams & Record<N, "float">> {
|
|
160
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Float, value: defaultValue });
|
|
161
|
+
return this as any;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Adds an integer parameter */
|
|
165
|
+
intParameter<N extends string>(name: N, defaultValue: number = 0): AnimatorControllerBuilder<TStates, TParams & Record<N, "int">> {
|
|
166
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Int, value: defaultValue });
|
|
167
|
+
return this as any;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Adds a boolean parameter */
|
|
171
|
+
boolParameter<N extends string>(name: N, defaultValue: boolean = false): AnimatorControllerBuilder<TStates, TParams & Record<N, "bool">> {
|
|
172
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Bool, value: defaultValue });
|
|
173
|
+
return this as any;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Adds a trigger parameter */
|
|
177
|
+
triggerParameter<N extends string>(name: N): AnimatorControllerBuilder<TStates, TParams & Record<N, "trigger">> {
|
|
178
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Trigger, value: false });
|
|
179
|
+
return this as any;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Adds a state to the controller. The first state added becomes the default state.
|
|
184
|
+
*
|
|
185
|
+
* When `options.clip` is provided, the state uses that clip directly.
|
|
186
|
+
* When omitted, chain `.track()` calls to define animation tracks inline:
|
|
187
|
+
* ```ts
|
|
188
|
+
* .state("Open", { loop: true })
|
|
189
|
+
* .track(door, "position", { from: [0,0,0], to: [2,0,0], duration: 1 })
|
|
190
|
+
* .track(light, "intensity", { from: 0, to: 5, duration: 1 })
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* @param name - Unique name for the state
|
|
194
|
+
* @param options - State configuration including clip, loop, speed. When omitted, use `.track()` to add animation data.
|
|
195
|
+
*/
|
|
196
|
+
state<N extends string>(name: N, options?: StateOptions): AnimatorControllerBuilder<TStates | N, TParams> {
|
|
197
|
+
const state: BuilderState = { name, options: options ?? {}, inlineTracks: [], transitions: [] };
|
|
198
|
+
this._states.push(state);
|
|
199
|
+
this._lastState = state;
|
|
200
|
+
return this as any;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Adds a transition between two states.
|
|
205
|
+
* Use `"*"` as the source to create a transition from any state.
|
|
206
|
+
* Chain `.condition()` calls after this to add conditions.
|
|
207
|
+
* @param from - Source state name, or `"*"` for any-state transition
|
|
208
|
+
* @param to - Destination state name
|
|
209
|
+
* @param options - Transition configuration
|
|
210
|
+
*/
|
|
211
|
+
transition(from: TStates | "*", to: TStates, options?: TransitionOptions): AnimatorControllerBuilder<TStates, TParams> {
|
|
212
|
+
this._lastState = null;
|
|
213
|
+
const t: BuilderTransition = { to: to as string, options: options ?? {}, conditions: [] };
|
|
214
|
+
if (from === "*") {
|
|
215
|
+
this._anyStateTransitions.push(t);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const state = this._states.find(s => s.name === from);
|
|
219
|
+
if (!state) throw new Error(`AnimatorControllerBuilder: source state "${from}" not found. Add it with .state() first.`);
|
|
220
|
+
state.transitions.push(t);
|
|
221
|
+
}
|
|
222
|
+
this._lastTransition = t;
|
|
223
|
+
return this as any;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Adds a condition to the most recently added transition.
|
|
228
|
+
* Multiple conditions on the same transition are AND-ed together.
|
|
229
|
+
*
|
|
230
|
+
* The required arguments depend on the parameter type:
|
|
231
|
+
* - **Trigger**: `.condition("Jump")` — mode defaults to `"if"`, no threshold needed
|
|
232
|
+
* - **Bool**: `.condition("Open", "if")` or `.condition("Open", "ifNot")`
|
|
233
|
+
* - **Float/Int**: `.condition("Speed", "greater", 0.1)`
|
|
234
|
+
*
|
|
235
|
+
* @param parameter - Name of the parameter to evaluate
|
|
236
|
+
*/
|
|
237
|
+
// Trigger parameters: mode is optional (defaults to "if")
|
|
238
|
+
condition(parameter: ParamNamesOfType<TParams, "trigger">, mode?: "if" | "ifNot"): AnimatorControllerBuilder<TStates, TParams>;
|
|
239
|
+
// Bool parameters: mode is required
|
|
240
|
+
condition(parameter: ParamNamesOfType<TParams, "bool">, mode: "if" | "ifNot"): AnimatorControllerBuilder<TStates, TParams>;
|
|
241
|
+
// Float/Int parameters: mode and optional threshold
|
|
242
|
+
condition(parameter: ParamNamesOfType<TParams, "float" | "int">, mode: "greater" | "less" | "equals" | "notEqual", threshold?: number): AnimatorControllerBuilder<TStates, TParams>;
|
|
243
|
+
condition(parameter: string, mode?: ConditionMode, threshold?: number): AnimatorControllerBuilder<TStates, TParams> {
|
|
244
|
+
if (!this._lastTransition) throw new Error("AnimatorControllerBuilder: .condition() must be called after .transition()");
|
|
245
|
+
this._lastTransition.conditions.push({ parameter, mode: mode ?? "if", threshold: threshold ?? 0 });
|
|
246
|
+
return this as any;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// --- Object3D ---
|
|
250
|
+
/** Adds an animation track for an Object3D's position or scale to the current state */
|
|
251
|
+
track(target: Object3D, property: "position" | "scale", keyframes: KF<Vec3Value>, options?: TrackOptions): this;
|
|
252
|
+
/** Adds an animation track for an Object3D's quaternion to the current state */
|
|
253
|
+
track(target: Object3D, property: "quaternion", keyframes: KF<QuatValue>, options?: TrackOptions): this;
|
|
254
|
+
/** Adds an animation track for an Object3D's rotation (Euler, converted to quaternion) to the current state */
|
|
255
|
+
track(target: Object3D, property: "rotation", keyframes: KF<EulerValue>, options?: TrackOptions): this;
|
|
256
|
+
/** Adds an animation track for an Object3D's visibility to the current state */
|
|
257
|
+
track(target: Object3D, property: "visible", keyframes: KF<boolean>, options?: TrackOptions): this;
|
|
258
|
+
// --- Material ---
|
|
259
|
+
/** Adds an animation track for a material's numeric property to the current state */
|
|
260
|
+
track(target: Material, property: "opacity" | "roughness" | "metalness" | "alphaTest" | "emissiveIntensity" | "envMapIntensity" | "bumpScale" | "displacementScale" | "displacementBias", keyframes: KF<number>, options?: TrackOptions): this;
|
|
261
|
+
/** Adds an animation track for a material's color property to the current state */
|
|
262
|
+
track(target: Material, property: "color" | "emissive", keyframes: KF<ColorValue>, options?: TrackOptions): this;
|
|
263
|
+
// --- Light ---
|
|
264
|
+
/** Adds an animation track for a light's numeric property to the current state */
|
|
265
|
+
track(target: Light, property: "intensity" | "distance" | "angle" | "penumbra" | "decay", keyframes: KF<number>, options?: TrackOptions): this;
|
|
266
|
+
/** Adds an animation track for a light's color to the current state */
|
|
267
|
+
track(target: Light, property: "color", keyframes: KF<ColorValue>, options?: TrackOptions): this;
|
|
268
|
+
// --- Camera ---
|
|
269
|
+
/** Adds an animation track for a camera's numeric property to the current state */
|
|
270
|
+
track(target: PerspectiveCamera, property: "fov" | "near" | "far" | "zoom", keyframes: KF<number>, options?: TrackOptions): this;
|
|
271
|
+
/**
|
|
272
|
+
* Adds an animation track to the most recently added state.
|
|
273
|
+
* Must be called after `.state()`. The track has the same type-safe overloads
|
|
274
|
+
* as the standalone {@link track} function.
|
|
275
|
+
*
|
|
276
|
+
* @param target - The object whose type determines valid properties and value types
|
|
277
|
+
* @param property - The property to animate
|
|
278
|
+
* @param keyframes - Keyframe array or {@link Tween} shorthand
|
|
279
|
+
* @param options - Optional {@link TrackOptions} with a `root` for named targeting
|
|
280
|
+
*/
|
|
281
|
+
track(target: object, property: string, keyframes: KF<any>, options?: TrackOptions): this {
|
|
282
|
+
if (!this._lastState) throw new Error("AnimatorControllerBuilder: .track() must be called after .state()");
|
|
283
|
+
if (this._lastState.options.clip) throw new Error(`AnimatorControllerBuilder: state "${this._lastState.name}" already has a clip. Use either .track() or { clip: ... }, not both.`);
|
|
284
|
+
this._lastState.inlineTracks.push(trackFn(target as Object3D, property as "position", keyframes, options));
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Sets which state is the default/entry state.
|
|
290
|
+
* If not called, the first added state is used.
|
|
291
|
+
* @param name - Name of the state
|
|
292
|
+
*/
|
|
293
|
+
defaultState(name: TStates): AnimatorControllerBuilder<TStates, TParams> {
|
|
294
|
+
this._defaultStateName = name as string;
|
|
295
|
+
return this as any;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Builds and returns the {@link AnimatorController}.
|
|
300
|
+
* Resolves all state name references to indices.
|
|
301
|
+
* @param root - Optional root Object3D for resolving {@link TrackDescriptor} track paths.
|
|
302
|
+
* When provided, tracks targeting a different object use `target.name` for named resolution.
|
|
303
|
+
*/
|
|
304
|
+
build(root?: Object3D): AnimatorController {
|
|
305
|
+
const stateIndexMap = new Map<string, number>();
|
|
306
|
+
this._states.forEach((s, i) => stateIndexMap.set(s.name, i));
|
|
307
|
+
|
|
308
|
+
let defaultStateIndex = 0;
|
|
309
|
+
if (this._defaultStateName !== null) {
|
|
310
|
+
const idx = stateIndexMap.get(this._defaultStateName);
|
|
311
|
+
if (idx === undefined) throw new Error(`AnimatorControllerBuilder: default state "${this._defaultStateName}" not found`);
|
|
312
|
+
defaultStateIndex = idx;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const resolveTransition = (t: BuilderTransition): Transition => {
|
|
316
|
+
const destIndex = stateIndexMap.get(t.to);
|
|
317
|
+
if (destIndex === undefined) throw new Error(`AnimatorControllerBuilder: transition target "${t.to}" not found`);
|
|
318
|
+
return {
|
|
319
|
+
exitTime: t.options.exitTime ?? 1,
|
|
320
|
+
hasExitTime: t.options.exitTime !== undefined,
|
|
321
|
+
duration: t.options.duration ?? 0,
|
|
322
|
+
offset: t.options.offset ?? 0,
|
|
323
|
+
hasFixedDuration: t.options.hasFixedDuration ?? false,
|
|
324
|
+
destinationState: destIndex,
|
|
325
|
+
conditions: t.conditions.map(c => ({
|
|
326
|
+
parameter: c.parameter,
|
|
327
|
+
mode: conditionModeToEnum(c.mode),
|
|
328
|
+
threshold: c.threshold,
|
|
329
|
+
})),
|
|
330
|
+
};
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const states: State[] = this._states.map((s, index) => {
|
|
334
|
+
const transitions: Transition[] = s.transitions.map(resolveTransition);
|
|
335
|
+
|
|
336
|
+
// Replicate any-state transitions onto every state (except self-targeting)
|
|
337
|
+
for (const anyT of this._anyStateTransitions) {
|
|
338
|
+
const destIndex = stateIndexMap.get(anyT.to);
|
|
339
|
+
if (destIndex === index) continue;
|
|
340
|
+
transitions.push(resolveTransition(anyT));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Resolve clip: from options.clip, inline .track() calls, or error
|
|
344
|
+
let clip: AnimationClip;
|
|
345
|
+
if (s.options.clip) {
|
|
346
|
+
clip = resolveClipSource(s.options.clip, root);
|
|
347
|
+
}
|
|
348
|
+
else if (s.inlineTracks.length > 0) {
|
|
349
|
+
clip = resolveClipSource(s.inlineTracks, root);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
throw new Error(`AnimatorControllerBuilder: state "${s.name}" has no clip and no inline tracks. Provide { clip } or chain .track() calls.`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
name: s.name,
|
|
357
|
+
hash: index,
|
|
358
|
+
motion: {
|
|
359
|
+
name: clip.name,
|
|
360
|
+
clip: clip,
|
|
361
|
+
isLooping: s.options.loop ?? false,
|
|
362
|
+
},
|
|
363
|
+
transitions,
|
|
364
|
+
behaviours: [],
|
|
365
|
+
speed: s.options.speed,
|
|
366
|
+
speedParameter: s.options.speedParameter,
|
|
367
|
+
cycleOffset: s.options.cycleOffset,
|
|
368
|
+
cycleOffsetParameter: s.options.cycleOffsetParameter,
|
|
369
|
+
};
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const model: AnimatorControllerModel = {
|
|
373
|
+
name: this._name,
|
|
374
|
+
guid: new InstantiateIdProvider(Date.now()).generateUUID(),
|
|
375
|
+
parameters: this._parameters,
|
|
376
|
+
layers: [{
|
|
377
|
+
name: "Base Layer",
|
|
378
|
+
stateMachine: {
|
|
379
|
+
defaultState: defaultStateIndex,
|
|
380
|
+
states,
|
|
381
|
+
}
|
|
382
|
+
}],
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
return new AnimatorController(model);
|
|
386
|
+
}
|
|
387
|
+
}
|