@needle-tools/engine 5.1.0-alpha → 5.1.0-alpha.2
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/.needle/generated/needle-bindings.gen.d.ts +5 -0
- package/CHANGELOG.md +27 -1
- package/SKILL.md +39 -21
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-DJBMx-zB.umd.cjs → gltf-progressive-BmblPzFj.umd.cjs} +4 -4
- package/dist/{gltf-progressive-BryRjllq.min.js → gltf-progressive-CN_mbb66.min.js} +2 -2
- package/dist/{gltf-progressive-Cl167Vjx.js → gltf-progressive-DUlhxdv4.js} +5 -2
- package/dist/{needle-engine.bundle-qDahLTqW.min.js → needle-engine.bundle-B-5Q2CpC.min.js} +249 -173
- package/dist/{needle-engine.bundle-CwhCzjep.js → needle-engine.bundle-dit3f1l5.js} +13238 -12724
- package/dist/{needle-engine.bundle-wM-BWPX9.umd.cjs → needle-engine.bundle-qZfVf_v-.umd.cjs} +250 -174
- package/dist/needle-engine.d.ts +295 -31
- package/dist/needle-engine.js +569 -563
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-B_9sKVU7.min.js → postprocessing-B571qGWR.min.js} +34 -34
- package/dist/{postprocessing-WDc9WwI3.js → postprocessing-CfrLAbLX.js} +0 -1
- package/dist/{postprocessing-B2wb6pzI.umd.cjs → postprocessing-CiGkAeM9.umd.cjs} +17 -17
- package/dist/{vendor-CAcsI0eU.js → vendor-BFrMaK9q.js} +8983 -9136
- package/dist/vendor-CJmyOrCq.min.js +1116 -0
- package/dist/vendor-DkMW3WY4.umd.cjs +1116 -0
- package/lib/engine/api.d.ts +12 -0
- package/lib/engine/api.js +2 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug_environment.js +1 -1
- package/lib/engine/debug/debug_environment.js.map +1 -1
- package/lib/engine/engine_application.js +8 -6
- package/lib/engine/engine_application.js.map +1 -1
- package/lib/engine/engine_components.js +5 -1
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_constants.js +6 -0
- package/lib/engine/engine_constants.js.map +1 -1
- package/lib/engine/engine_context.d.ts +33 -7
- package/lib/engine/engine_context.js +40 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_context_registry.js +1 -1
- package/lib/engine/engine_context_registry.js.map +1 -1
- package/lib/engine/engine_init.js +7 -0
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.d.ts +3 -2
- package/lib/engine/engine_input.js +3 -2
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +25 -15
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_networking_blob.d.ts +1 -1
- package/lib/engine/engine_networking_blob.js +5 -11
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_physics_rapier.d.ts +3 -0
- package/lib/engine/engine_physics_rapier.js +13 -10
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_pmrem.js +2 -2
- package/lib/engine/engine_pmrem.js.map +1 -1
- package/lib/engine/engine_scenedata.d.ts +30 -0
- package/lib/engine/engine_scenedata.js +136 -0
- package/lib/engine/engine_scenedata.js.map +1 -0
- package/lib/engine/engine_ssr.d.ts +18 -0
- package/lib/engine/engine_ssr.js +40 -0
- package/lib/engine/engine_ssr.js.map +1 -0
- package/lib/engine/engine_three_utils.d.ts +14 -7
- package/lib/engine/engine_three_utils.js +14 -7
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_utils.js +4 -2
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/engine_utils_hash.d.ts +9 -0
- package/lib/engine/engine_utils_hash.js +112 -0
- package/lib/engine/engine_utils_hash.js.map +1 -0
- package/lib/engine/webcomponents/jsx.d.ts +51 -0
- package/lib/engine/webcomponents/logo-element.d.ts +2 -1
- package/lib/engine/webcomponents/logo-element.js +2 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +4 -4
- package/lib/engine/webcomponents/needle menu/needle-menu.js +2 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +2 -1
- package/lib/engine/webcomponents/needle-button.js +2 -1
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +2 -1
- package/lib/engine/webcomponents/needle-engine.js +2 -1
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +1 -0
- package/lib/engine/xr/NeedleXRSession.js +5 -5
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/events.d.ts +30 -3
- package/lib/engine/xr/events.js +38 -0
- package/lib/engine/xr/events.js.map +1 -1
- package/lib/engine/xr/init.js +1 -7
- package/lib/engine/xr/init.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +135 -2
- package/lib/engine-components/AnimatorController.js +218 -2
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/GroundProjection.d.ts +1 -0
- package/lib/engine-components/GroundProjection.js +184 -48
- package/lib/engine-components/GroundProjection.js.map +1 -1
- package/lib/engine-components/Light.d.ts +6 -8
- package/lib/engine-components/Light.js +40 -27
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/RigidBody.js +3 -3
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +2 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -0
- package/lib/engine-components/api.js +1 -0
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -0
- package/lib/engine-components/codegen/components.js +1 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
- package/lib/engine-components/postprocessing/Effects/Sharpening.js +1 -2
- package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +5 -6
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +0 -1
- package/lib/engine-components/web/ScrollFollow.js +3 -2
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/needle-engine.d.ts +2 -0
- package/lib/needle-engine.js +2 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +6 -4
- package/plugins/common/logger.js +42 -19
- package/plugins/dts-generator/dts.codegen.js +334 -0
- package/plugins/dts-generator/dts.scan.js +99 -0
- package/plugins/dts-generator/dts.writer.js +59 -0
- package/plugins/dts-generator/glb.discovery.js +279 -0
- package/plugins/dts-generator/glb.extractor.js +215 -0
- package/plugins/dts-generator/glb.reader.js +167 -0
- package/plugins/dts-generator/index.js +36 -0
- package/plugins/dts-generator/manifest.types.js +174 -0
- package/plugins/types/index.d.ts +2 -1
- package/plugins/types/needle-bindings.d.ts +30 -0
- package/plugins/types/userconfig.d.ts +21 -2
- package/plugins/vite/asap.js +1 -1
- package/plugins/vite/dependency-watcher.d.ts +2 -2
- package/plugins/vite/dependency-watcher.js +3 -4
- package/plugins/vite/drop.d.ts +2 -2
- package/plugins/vite/drop.js +3 -4
- package/plugins/vite/dts-generator.d.ts +7 -0
- package/plugins/vite/dts-generator.js +191 -0
- package/plugins/vite/index.d.ts +10 -3
- package/plugins/vite/index.js +27 -10
- package/plugins/vite/logger.client.js +4 -3
- package/plugins/vite/logging.js +2 -2
- package/plugins/vite/meta.js +4 -2
- package/plugins/vite/poster.d.ts +2 -2
- package/plugins/vite/poster.js +3 -5
- package/plugins/vite/reload.d.ts +2 -2
- package/plugins/vite/reload.js +23 -22
- package/src/engine/api.ts +15 -1
- package/src/engine/debug/debug_environment.ts +1 -1
- package/src/engine/engine_application.ts +8 -6
- package/src/engine/engine_components.ts +7 -4
- package/src/engine/engine_constants.ts +11 -6
- package/src/engine/engine_context.ts +50 -7
- package/src/engine/engine_context_registry.ts +1 -1
- package/src/engine/engine_init.ts +6 -0
- package/src/engine/engine_input.ts +3 -2
- package/src/engine/engine_license.ts +23 -19
- package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
- package/src/engine/engine_networking_blob.ts +5 -11
- package/src/engine/engine_physics_rapier.ts +14 -12
- package/src/engine/engine_pmrem.ts +3 -3
- package/src/engine/engine_scenedata.ts +134 -0
- package/src/engine/engine_ssr.ts +48 -0
- package/src/engine/engine_three_utils.ts +15 -7
- package/src/engine/engine_types.ts +2 -0
- package/src/engine/engine_utils.ts +3 -2
- package/src/engine/engine_utils_hash.ts +65 -0
- package/src/engine/webcomponents/jsx.d.ts +51 -0
- package/src/engine/webcomponents/logo-element.ts +3 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +4 -2
- package/src/engine/webcomponents/needle-button.ts +3 -1
- package/src/engine/webcomponents/needle-engine.ts +3 -1
- package/src/engine/xr/NeedleXRSession.ts +6 -6
- package/src/engine/xr/events.ts +44 -1
- package/src/engine/xr/init.ts +0 -7
- package/src/engine-components/AnimatorController.ts +286 -4
- package/src/engine-components/GroundProjection.ts +226 -52
- package/src/engine-components/Light.ts +40 -26
- package/src/engine-components/RigidBody.ts +3 -3
- package/src/engine-components/SceneSwitcher.ts +1 -0
- package/src/engine-components/api.ts +1 -0
- package/src/engine-components/codegen/components.ts +1 -0
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +1 -1
- package/src/engine-components/postprocessing/Effects/Sharpening.ts +1 -2
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +4 -8
- package/src/engine-components/web/ScrollFollow.ts +2 -2
- package/src/needle-engine.ts +3 -0
- package/src/vite-env.d.ts +16 -0
- package/dist/vendor-CEM38hLE.umd.cjs +0 -1116
- package/dist/vendor-HRlxIBga.min.js +0 -1116
package/src/engine/xr/events.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { NeedleXRSession } from "./NeedleXRSession.js";
|
|
2
|
+
import type { Context } from "../engine_context.js";
|
|
2
3
|
|
|
3
4
|
export declare type XRSessionEventArgs = { session: NeedleXRSession };
|
|
4
5
|
|
|
@@ -14,11 +15,13 @@ const onXRSessionStartListeners: ((evt: XRSessionEventArgs) => void)[] = [];
|
|
|
14
15
|
* console.log("XR session started", evt);
|
|
15
16
|
* });
|
|
16
17
|
* ```
|
|
18
|
+
* @returns A function to remove the listener
|
|
17
19
|
*/
|
|
18
20
|
export function onXRSessionStart(fn: (evt: XRSessionEventArgs) => void) {
|
|
19
21
|
if (onXRSessionStartListeners.indexOf(fn) === -1) {
|
|
20
22
|
onXRSessionStartListeners.push(fn);
|
|
21
23
|
}
|
|
24
|
+
return () => offXRSessionStart(fn);
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
27
|
* Remove a listener for when an XR session starts
|
|
@@ -51,11 +54,13 @@ const onXRSessionEndListeners: ((evt: XRSessionEventArgs) => void)[] = [];
|
|
|
51
54
|
* console.log("XR session ended", evt);
|
|
52
55
|
* });
|
|
53
56
|
* ```
|
|
57
|
+
* @returns A function to remove the listener
|
|
54
58
|
*/
|
|
55
59
|
export function onXRSessionEnd(fn: (evt: XRSessionEventArgs) => void) {
|
|
56
60
|
if (onXRSessionEndListeners.indexOf(fn) === -1) {
|
|
57
61
|
onXRSessionEndListeners.push(fn);
|
|
58
62
|
}
|
|
63
|
+
return () => offXRSessionEnd(fn);
|
|
59
64
|
};
|
|
60
65
|
|
|
61
66
|
/**
|
|
@@ -78,6 +83,44 @@ export function offXRSessionEnd(fn: (evt: XRSessionEventArgs) => void) {
|
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
|
|
86
|
+
export declare type XRSessionRequestEventArgs = { readonly mode: XRSessionMode, readonly init: XRSessionInit, readonly context: Context };
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Add a listener that fires before an XR session is requested.
|
|
90
|
+
* Use this to modify the session init options, e.g. to add optional features like `camera-access`.
|
|
91
|
+
* @param fn The function to call before the XR session is requested
|
|
92
|
+
* @example
|
|
93
|
+
* ```js
|
|
94
|
+
* onBeforeXRSession((args) => {
|
|
95
|
+
* args.init.optionalFeatures ??= [];
|
|
96
|
+
* args.init.optionalFeatures.push("camera-access");
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
* @return A function to remove the listener
|
|
100
|
+
*/
|
|
101
|
+
export function onBeforeXRSession(fn: (args: XRSessionRequestEventArgs) => void) {
|
|
102
|
+
if (onBeforeXRSessionListeners.indexOf(fn) === -1) {
|
|
103
|
+
onBeforeXRSessionListeners.push(fn);
|
|
104
|
+
// Delegate to NeedleXRSession which owns the actual invocation
|
|
105
|
+
NeedleXRSession.onSessionRequestStart(fn);
|
|
106
|
+
}
|
|
107
|
+
return () => offBeforeXRSession(fn);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Remove a listener for before an XR session is requested
|
|
112
|
+
* @param fn The function to remove from the listeners
|
|
113
|
+
*/
|
|
114
|
+
export function offBeforeXRSession(fn: (args: XRSessionRequestEventArgs) => void) {
|
|
115
|
+
const index = onBeforeXRSessionListeners.indexOf(fn);
|
|
116
|
+
if (index !== -1) {
|
|
117
|
+
onBeforeXRSessionListeners.splice(index, 1);
|
|
118
|
+
NeedleXRSession.offSessionRequestStart(fn);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const onBeforeXRSessionListeners: ((args: XRSessionRequestEventArgs) => void)[] = [];
|
|
123
|
+
|
|
81
124
|
/**
|
|
82
125
|
* @internal
|
|
83
126
|
* Invoke the XRSessionStart event
|
package/src/engine/xr/init.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isDevEnvironment, showBalloonMessage } from "../debug/index.js";
|
|
2
1
|
import { DeviceUtilities, setParamWithoutReload } from "../engine_utils.js";
|
|
3
2
|
import { NeedleXRSession } from "./NeedleXRSession.js";
|
|
4
3
|
|
|
@@ -13,12 +12,6 @@ export function initXR() {
|
|
|
13
12
|
|
|
14
13
|
if (DeviceUtilities.isiOS()) {
|
|
15
14
|
|
|
16
|
-
if (isDevEnvironment()) {
|
|
17
|
-
const randomParameterValue = Date.now().toString();
|
|
18
|
-
setParamWithoutReload("debug_appclip", randomParameterValue);
|
|
19
|
-
showBalloonMessage("iOS appclip debug: " + randomParameterValue);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
15
|
// Prefetch
|
|
23
16
|
const url = new URL("https://appclip.apple.com/id?p=tools.needle.launch-app.Clip");
|
|
24
17
|
url.searchParams.set("url", location.href);
|
|
@@ -9,7 +9,7 @@ import { Context } from "../engine/engine_setup.js";
|
|
|
9
9
|
import { isAnimationAction } from "../engine/engine_three_utils.js";
|
|
10
10
|
import { TypeStore } from "../engine/engine_typestore.js";
|
|
11
11
|
import { deepClone, getParam } from "../engine/engine_utils.js";
|
|
12
|
-
import type { AnimatorControllerModel, Condition, State, Transition } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
12
|
+
import type { AnimatorControllerModel, Condition, Parameter, State, Transition } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
13
13
|
import { AnimatorConditionMode, AnimatorControllerParameterType, AnimatorStateInfo, createMotion, StateMachineBehaviour } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
14
14
|
import type { Animator } from "./Animator.js";
|
|
15
15
|
|
|
@@ -30,16 +30,274 @@ declare type CreateAnimatorControllerOptions = {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
// #region AnimatorControllerBuilder
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Configuration for an animation state in the builder
|
|
37
|
+
*/
|
|
38
|
+
export declare type StateOptions = {
|
|
39
|
+
/** The animation clip for this state */
|
|
40
|
+
clip: AnimationClip;
|
|
41
|
+
/** Whether the animation should loop (default: false) */
|
|
42
|
+
loop?: boolean;
|
|
43
|
+
/** Base speed multiplier (default: 1) */
|
|
44
|
+
speed?: number;
|
|
45
|
+
/** Name of a float parameter to multiply with speed */
|
|
46
|
+
speedParameter?: string;
|
|
47
|
+
/** Normalized cycle offset 0-1 (default: 0) */
|
|
48
|
+
cycleOffset?: number;
|
|
49
|
+
/** Name of a float parameter to use as cycle offset */
|
|
50
|
+
cycleOffsetParameter?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Configuration for a transition in the builder
|
|
55
|
+
*/
|
|
56
|
+
export declare type TransitionOptions = {
|
|
57
|
+
/** Duration of the crossfade in seconds (default: 0) */
|
|
58
|
+
duration?: number;
|
|
59
|
+
/** Normalized exit time 0-1 (default: 1). Only used when hasExitTime is true */
|
|
60
|
+
exitTime?: number;
|
|
61
|
+
/** Whether the transition waits for exitTime before transitioning (default: false) */
|
|
62
|
+
hasExitTime?: boolean;
|
|
63
|
+
/** Normalized offset into the destination state's animation (default: 0) */
|
|
64
|
+
offset?: number;
|
|
65
|
+
/** Whether duration is in seconds (true) or normalized (false) (default: false) */
|
|
66
|
+
hasFixedDuration?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** String condition modes for the builder, mapped to {@link AnimatorConditionMode} */
|
|
70
|
+
export type ConditionMode = "if" | "ifNot" | "greater" | "less" | "equals" | "notEqual";
|
|
71
|
+
|
|
72
|
+
function conditionModeToEnum(mode: ConditionMode): AnimatorConditionMode {
|
|
73
|
+
switch (mode) {
|
|
74
|
+
case "if": return AnimatorConditionMode.If;
|
|
75
|
+
case "ifNot": return AnimatorConditionMode.IfNot;
|
|
76
|
+
case "greater": return AnimatorConditionMode.Greater;
|
|
77
|
+
case "less": return AnimatorConditionMode.Less;
|
|
78
|
+
case "equals": return AnimatorConditionMode.Equals;
|
|
79
|
+
case "notEqual": return AnimatorConditionMode.NotEqual;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
type BuilderTransition = {
|
|
84
|
+
to: string;
|
|
85
|
+
options: TransitionOptions;
|
|
86
|
+
conditions: Array<{ parameter: string; mode: ConditionMode; threshold: number }>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
type BuilderState = {
|
|
90
|
+
name: string;
|
|
91
|
+
options: StateOptions;
|
|
92
|
+
transitions: BuilderTransition[];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A fluent builder for creating {@link AnimatorController} instances from code.
|
|
97
|
+
*
|
|
98
|
+
* Use {@link AnimatorController.build} to create a new builder.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const controller = AnimatorController.build("CharacterController")
|
|
103
|
+
* .floatParameter("Speed", 0)
|
|
104
|
+
* .triggerParameter("Jump")
|
|
105
|
+
* .state("Idle", { clip: idleClip, loop: true })
|
|
106
|
+
* .state("Walk", { clip: walkClip, loop: true })
|
|
107
|
+
* .state("Jump", { clip: jumpClip })
|
|
108
|
+
* .transition("Idle", "Walk", { duration: 0.25 })
|
|
109
|
+
* .condition("Speed", "greater", 0.1)
|
|
110
|
+
* .transition("Walk", "Idle", { duration: 0.25 })
|
|
111
|
+
* .condition("Speed", "less", 0.1)
|
|
112
|
+
* .transition("*", "Jump", { duration: 0.1 })
|
|
113
|
+
* .condition("Jump", "if")
|
|
114
|
+
* .transition("Jump", "Idle", { hasExitTime: true, exitTime: 0.9, duration: 0.25 })
|
|
115
|
+
* .build();
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @category Animation and Sequencing
|
|
119
|
+
* @group Utilities
|
|
120
|
+
*/
|
|
121
|
+
export class AnimatorControllerBuilder {
|
|
122
|
+
private _name: string;
|
|
123
|
+
private _parameters: Parameter[] = [];
|
|
124
|
+
private _states: BuilderState[] = [];
|
|
125
|
+
private _anyStateTransitions: BuilderTransition[] = [];
|
|
126
|
+
private _defaultStateName: string | null = null;
|
|
127
|
+
private _lastTransition: BuilderTransition | null = null;
|
|
128
|
+
|
|
129
|
+
constructor(name?: string) {
|
|
130
|
+
this._name = name ?? "AnimatorController";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Adds a float parameter */
|
|
134
|
+
floatParameter(name: string, defaultValue: number = 0): this {
|
|
135
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Float, value: defaultValue });
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Adds an integer parameter */
|
|
140
|
+
intParameter(name: string, defaultValue: number = 0): this {
|
|
141
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Int, value: defaultValue });
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Adds a boolean parameter */
|
|
146
|
+
boolParameter(name: string, defaultValue: boolean = false): this {
|
|
147
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Bool, value: defaultValue });
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Adds a trigger parameter */
|
|
152
|
+
triggerParameter(name: string): this {
|
|
153
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Trigger, value: false });
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Adds a state to the controller. The first state added becomes the default state.
|
|
159
|
+
* @param name - Unique name for the state
|
|
160
|
+
* @param options - State configuration including clip, loop, speed
|
|
161
|
+
*/
|
|
162
|
+
state(name: string, options: StateOptions): this {
|
|
163
|
+
this._states.push({ name, options, transitions: [] });
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Adds a transition between two states.
|
|
169
|
+
* Use `"*"` as the source to create a transition from any state.
|
|
170
|
+
* Chain `.condition()` calls after this to add conditions.
|
|
171
|
+
* @param from - Source state name, or `"*"` for any-state transition
|
|
172
|
+
* @param to - Destination state name
|
|
173
|
+
* @param options - Transition configuration
|
|
174
|
+
*/
|
|
175
|
+
transition(from: string, to: string, options?: TransitionOptions): this {
|
|
176
|
+
const t: BuilderTransition = { to, options: options ?? {}, conditions: [] };
|
|
177
|
+
if (from === "*") {
|
|
178
|
+
this._anyStateTransitions.push(t);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const state = this._states.find(s => s.name === from);
|
|
182
|
+
if (!state) throw new Error(`AnimatorControllerBuilder: source state "${from}" not found. Add it with .state() first.`);
|
|
183
|
+
state.transitions.push(t);
|
|
184
|
+
}
|
|
185
|
+
this._lastTransition = t;
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Adds a condition to the most recently added transition.
|
|
191
|
+
* Multiple conditions on the same transition are AND-ed together.
|
|
192
|
+
* @param parameter - Name of the parameter to evaluate
|
|
193
|
+
* @param mode - Condition mode: `"if"`, `"ifNot"`, `"greater"`, `"less"`, `"equals"`, `"notEqual"`
|
|
194
|
+
* @param threshold - Comparison threshold for numeric conditions (default: 0)
|
|
195
|
+
*/
|
|
196
|
+
condition(parameter: string, mode: ConditionMode, threshold: number = 0): this {
|
|
197
|
+
if (!this._lastTransition) throw new Error("AnimatorControllerBuilder: .condition() must be called after .transition()");
|
|
198
|
+
this._lastTransition.conditions.push({ parameter, mode, threshold });
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Sets which state is the default/entry state.
|
|
204
|
+
* If not called, the first added state is used.
|
|
205
|
+
* @param name - Name of the state
|
|
206
|
+
*/
|
|
207
|
+
defaultState(name: string): this {
|
|
208
|
+
this._defaultStateName = name;
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Builds and returns the {@link AnimatorController}.
|
|
214
|
+
* Resolves all state name references to indices.
|
|
215
|
+
*/
|
|
216
|
+
build(): AnimatorController {
|
|
217
|
+
const stateIndexMap = new Map<string, number>();
|
|
218
|
+
this._states.forEach((s, i) => stateIndexMap.set(s.name, i));
|
|
219
|
+
|
|
220
|
+
let defaultStateIndex = 0;
|
|
221
|
+
if (this._defaultStateName !== null) {
|
|
222
|
+
const idx = stateIndexMap.get(this._defaultStateName);
|
|
223
|
+
if (idx === undefined) throw new Error(`AnimatorControllerBuilder: default state "${this._defaultStateName}" not found`);
|
|
224
|
+
defaultStateIndex = idx;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const resolveTransition = (t: BuilderTransition): Transition => {
|
|
228
|
+
const destIndex = stateIndexMap.get(t.to);
|
|
229
|
+
if (destIndex === undefined) throw new Error(`AnimatorControllerBuilder: transition target "${t.to}" not found`);
|
|
230
|
+
return {
|
|
231
|
+
exitTime: t.options.exitTime ?? 1,
|
|
232
|
+
hasExitTime: t.options.hasExitTime ?? false,
|
|
233
|
+
duration: t.options.duration ?? 0,
|
|
234
|
+
offset: t.options.offset ?? 0,
|
|
235
|
+
hasFixedDuration: t.options.hasFixedDuration ?? false,
|
|
236
|
+
destinationState: destIndex,
|
|
237
|
+
conditions: t.conditions.map(c => ({
|
|
238
|
+
parameter: c.parameter,
|
|
239
|
+
mode: conditionModeToEnum(c.mode),
|
|
240
|
+
threshold: c.threshold,
|
|
241
|
+
})),
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const states: State[] = this._states.map((s, index) => {
|
|
246
|
+
const transitions: Transition[] = s.transitions.map(resolveTransition);
|
|
247
|
+
|
|
248
|
+
// Replicate any-state transitions onto every state (except self-targeting)
|
|
249
|
+
for (const anyT of this._anyStateTransitions) {
|
|
250
|
+
const destIndex = stateIndexMap.get(anyT.to);
|
|
251
|
+
if (destIndex === index) continue;
|
|
252
|
+
transitions.push(resolveTransition(anyT));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
name: s.name,
|
|
257
|
+
hash: index,
|
|
258
|
+
motion: {
|
|
259
|
+
name: s.options.clip.name,
|
|
260
|
+
clip: s.options.clip,
|
|
261
|
+
isLooping: s.options.loop ?? false,
|
|
262
|
+
},
|
|
263
|
+
transitions,
|
|
264
|
+
behaviours: [],
|
|
265
|
+
speed: s.options.speed,
|
|
266
|
+
speedParameter: s.options.speedParameter,
|
|
267
|
+
cycleOffset: s.options.cycleOffset,
|
|
268
|
+
cycleOffsetParameter: s.options.cycleOffsetParameter,
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const model: AnimatorControllerModel = {
|
|
273
|
+
name: this._name,
|
|
274
|
+
guid: new InstantiateIdProvider(Date.now()).generateUUID(),
|
|
275
|
+
parameters: this._parameters,
|
|
276
|
+
layers: [{
|
|
277
|
+
name: "Base Layer",
|
|
278
|
+
stateMachine: {
|
|
279
|
+
defaultState: defaultStateIndex,
|
|
280
|
+
states,
|
|
281
|
+
}
|
|
282
|
+
}],
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return new AnimatorController(model);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// #endregion
|
|
289
|
+
|
|
33
290
|
// #region AnimatorController
|
|
34
|
-
/**
|
|
291
|
+
/**
|
|
35
292
|
* Controls the playback of animations using a state machine architecture.
|
|
36
293
|
*
|
|
37
294
|
* The AnimatorController manages animation states, transitions between states,
|
|
38
295
|
* and parameters that affect those transitions. It is used by the {@link Animator}
|
|
39
296
|
* component to control animation behavior on 3D models.
|
|
40
297
|
*
|
|
41
|
-
* Use
|
|
42
|
-
*
|
|
298
|
+
* Use {@link AnimatorController.build} to fluently create a controller with parameters,
|
|
299
|
+
* states, transitions, and conditions. For simple sequential playback,
|
|
300
|
+
* use {@link AnimatorController.createFromClips}.
|
|
43
301
|
*
|
|
44
302
|
* @category Animation and Sequencing
|
|
45
303
|
* @group Utilities
|
|
@@ -107,6 +365,30 @@ export class AnimatorController {
|
|
|
107
365
|
return controller;
|
|
108
366
|
}
|
|
109
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Creates a new {@link AnimatorControllerBuilder} for fluently constructing a controller with
|
|
370
|
+
* parameters, states, transitions, and conditions.
|
|
371
|
+
*
|
|
372
|
+
* @param name - Optional name for the controller
|
|
373
|
+
* @returns A new builder instance
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts
|
|
377
|
+
* const ctrl = AnimatorController.build("MyController")
|
|
378
|
+
* .floatParameter("Speed")
|
|
379
|
+
* .state("Idle", { clip: idleClip, loop: true })
|
|
380
|
+
* .state("Walk", { clip: walkClip, loop: true })
|
|
381
|
+
* .transition("Idle", "Walk", { duration: 0.25 })
|
|
382
|
+
* .condition("Speed", "greater", 0.1)
|
|
383
|
+
* .transition("Walk", "Idle", { duration: 0.25 })
|
|
384
|
+
* .condition("Speed", "less", 0.1)
|
|
385
|
+
* .build();
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
static build(name?: string): AnimatorControllerBuilder {
|
|
389
|
+
return new AnimatorControllerBuilder(name);
|
|
390
|
+
}
|
|
391
|
+
|
|
110
392
|
/**
|
|
111
393
|
* Plays an animation state by name or hash.
|
|
112
394
|
*
|