UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

196 lines (175 loc) 6.08 kB
import { AnimationAction, AnimationClip, MathUtils, Object3D } from "three" import { InstantiateIdProvider } from "../../engine/engine_networking_instantiate.js"; import { Animator } from "../../engine-components/Animator.js"; import { Context } from "../engine_setup.js"; export declare type AnimatorControllerModel = { name: string, guid: string, parameters: Parameter[], layers: Layer[], } export declare type Parameter = { name: string; /** the animator string to hash result, test against this if a number is used to get a param value */ hash: number; type: AnimatorControllerParameterType; value: number | boolean | string; } export declare type Layer = { name: string, stateMachine: StateMachine } export declare type StateMachine = { defaultState: number; states: State[], } export declare type State = { name: string, hash: number; motion: Motion, transitions: Transition[], behaviours: StateMachineBehaviourModel[], /** The base speed of the animation */ speed?: number; /** Set to a animator controller float parameter name to multiply this ontop of the speed value */ speedParameter?: string; /** Cycle offset normalized 0-1, used when starting a animation */ cycleOffset?: number; /** If set to a parameter then this is used instead of the CycleOffset value to offset the animation start time */ cycleOffsetParameter?: string; } export declare type StateMachineBehaviourModel = { typeName: string; properties: object; instance?: StateMachineBehaviour; } export abstract class StateMachineBehaviour { _context?: Context; get context(): Context { return this._context ?? Context.Current; } get isStateMachineBehaviour() { return true; } onStateEnter?(animator: Animator, _animatorStateInfo: AnimatorStateInfo, layerIndex: number); onStateUpdate?(animator: Animator, animatorStateInfo: AnimatorStateInfo, _layerIndex: number); onStateExit?(animator: Animator, animatorStateInfo: AnimatorStateInfo, layerIndex: number); } export class AnimatorStateInfo { /** The name of the animation */ readonly name: string; /** The hash of the name */ readonly nameHash: number; /** The normalized time of the animation */ readonly normalizedTime: number; /** The length of the animation */ readonly length: number; /** The current speed of the animation */ readonly speed: number; /** The current action playing. It can be used to modify the action */ readonly action: AnimationAction | null; /** * If the state has any transitions */ readonly hasTransitions: boolean; constructor(state: State, normalizedTime: number, length: number, speed: number) { this.name = state.name; this.nameHash = state.hash; this.normalizedTime = normalizedTime; this.length = length; this.speed = speed; this.action = state.motion.action || null; this.hasTransitions = state.transitions?.length > 0 || false; } } export declare type Motion = { name: string, isLooping: boolean, guid?: string, /** clip index in gltf animations array */ index?: number, /** the resolved clip */ clip?: AnimationClip, /** the clip mapping -> which object has which animationclip */ clips?: ClipMapping[]; action?: AnimationAction, /** used when a transition points to the same state we need another action to blend */ action_loopback?: AnimationAction, } export function createMotion(name: string, id?: InstantiateIdProvider): Motion { return { name: "Empty", isLooping: false, guid: id?.generateUUID() ?? MathUtils.generateUUID(), index: -1, clip: new AnimationClip(name, 0, []), } } export declare type ClipMapping = { /** the object this clip is for */ node: Object3D; /** the animationclip we resolve from a json ptr */ clip: AnimationClip; } export declare type Transition = { isExit?: boolean; exitTime: number, hasFixedDuration?: boolean, offset: number, duration: number, hasExitTime: number | boolean, destinationState: number | State, conditions: Condition[], // isAny?: boolean } export declare type Condition = { parameter: string, mode: AnimatorConditionMode, threshold: number, } /// <summary> /// <para>The mode of the condition.</para> /// </summary> export enum AnimatorConditionMode { /// <summary> /// <para>The condition is true when the parameter value is true.</para> /// </summary> If = 1, /// <summary> /// <para>The condition is true when the parameter value is false.</para> /// </summary> IfNot = 2, /// <summary> /// <para>The condition is true when parameter value is greater than the threshold.</para> /// </summary> Greater = 3, /// <summary> /// <para>The condition is true when the parameter value is less than the threshold.</para> /// </summary> Less = 4, /// <summary> /// <para>The condition is true when parameter value is equal to the threshold.</para> /// </summary> Equals = 6, /// <summary> /// <para>The condition is true when the parameter value is not equal to the threshold.</para> /// </summary> NotEqual = 7, } /// <summary> /// <para>The type of the parameter.</para> /// </summary> export enum AnimatorControllerParameterType { /// <summary> /// <para>Float type parameter.</para> /// </summary> Float = 1, /// <summary> /// <para>Int type parameter.</para> /// </summary> Int = 3, /// <summary> /// <para>Boolean type parameter.</para> /// </summary> Bool = 4, /// <summary> /// <para>Trigger type parameter.</para> /// </summary> Trigger = 9, }