@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.
414 lines (413 loc) • 20.7 kB
TypeScript
import { AnimationClip, Object3D } from "three";
import type { Light, Material, PerspectiveCamera } from "three";
import type { Animator } from "../Animator.js";
import type { AudioSource } from "../AudioSource.js";
import { type TrackDescriptor, type TrackOptions, type AnimationKeyframe, type Tween, type Vec3Value, type QuatValue, type EulerValue, type ColorValue } from "../AnimationBuilder.js";
/** Keyframe array or tween shorthand */
type KF<V> = AnimationKeyframe<V>[] | Tween<V>;
import { SignalReceiver } from "./SignalAsset.js";
import type { PlayableDirector } from "./PlayableDirector.js";
import { ClipExtrapolation } from "./TimelineModels.js";
import type { TimelineAssetModel } from "./TimelineModels.js";
/**
* Options for an animation clip in the timeline builder
*/
export declare type AnimationClipOptions = {
/** Start time of the clip in seconds. If omitted, placed after the previous clip on this track. */
start?: number;
/** Duration of the clip in seconds. Defaults to the animation clip duration. */
duration?: number;
/** Playback speed multiplier (default: 1) */
speed?: number;
/** Whether the animation should loop within the clip (default: false) */
loop?: boolean;
/** Ease-in duration in seconds (default: 0) */
easeIn?: number;
/** Ease-out duration in seconds (default: 0) */
easeOut?: number;
/** Offset into the source animation clip in seconds (default: 0) */
clipIn?: number;
/** Whether to remove the start offset of the animation (default: false) */
removeStartOffset?: boolean;
/** Pre-extrapolation mode (default: None) */
preExtrapolation?: ClipExtrapolation;
/** Post-extrapolation mode (default: None) */
postExtrapolation?: ClipExtrapolation;
/** Play the clip in reverse */
reversed?: boolean;
};
/**
* Options for an audio clip in the timeline builder
*/
export declare type AudioClipOptions = {
/** Start time of the clip in seconds. If omitted, placed after the previous clip on this track. */
start?: number;
/** Duration of the clip in seconds (required for audio since we can't infer it) */
duration: number;
/** Playback speed multiplier (default: 1) */
speed?: number;
/** Volume multiplier for this clip (default: 1) */
volume?: number;
/** Whether the audio should loop within the clip (default: false) */
loop?: boolean;
/** Ease-in duration in seconds (default: 0) */
easeIn?: number;
/** Ease-out duration in seconds (default: 0) */
easeOut?: number;
};
/**
* Options for an activation clip in the timeline builder
*/
export declare type ActivationClipOptions = {
/** Start time of the clip in seconds. If omitted, placed after the previous clip on this track. */
start?: number;
/** Duration of the clip in seconds (required) */
duration: number;
/** Ease-in duration in seconds (default: 0) */
easeIn?: number;
/** Ease-out duration in seconds (default: 0) */
easeOut?: number;
};
/**
* Options for a control clip in the timeline builder
*/
export declare type ControlClipOptions = {
/** Start time of the clip in seconds. If omitted, placed after the previous clip on this track. */
start?: number;
/** Duration of the clip in seconds (required) */
duration: number;
/** Whether to control the activation of the source object (default: true) */
controlActivation?: boolean;
/** Whether to update a nested PlayableDirector on the source object (default: true) */
updateDirector?: boolean;
};
/**
* Options for a signal marker in the timeline builder
*/
export declare type SignalMarkerOptions = {
/** Whether the signal should fire if the playback starts past its time (default: false) */
retroActive?: boolean;
/** Whether the signal should only fire once (default: false) */
emitOnce?: boolean;
};
/**
* Shared methods available on all track builders and the TimelineBuilder entry point.
* Provides track creation, build, and install methods.
*
* @category Animation and Sequencing
*/
export interface TimelineBuilderBase {
/** Adds an animation track. Chain `.clip()` or `.track()` to add content. */
animationTrack(name: string, binding?: Animator | Object3D | null): AnimationTrackBuilder;
/** Adds an audio track. Chain `.clip()` to add audio clips. */
audioTrack(name: string, binding?: AudioSource | Object3D | null, volume?: number): AudioTrackBuilder;
/** Adds an activation track. Chain `.clip()` to define activation windows. */
activationTrack(name: string, binding?: Object3D | null): ActivationTrackBuilder;
/** Adds a control track. Chain `.clip()` to control nested objects/timelines. */
controlTrack(name: string): ControlTrackBuilder;
/** Adds a signal track. Chain `.signal()` or `.marker()` to add events. */
signalTrack(name: string, binding?: SignalReceiver | Object3D | null): SignalTrackBuilder;
/** Adds a marker track. Chain `.marker()` to add markers. */
markerTrack(name: string): MarkerTrackBuilder;
/** Builds and returns the {@link TimelineAssetModel}. */
build(): TimelineAssetModel;
/** Builds the timeline, assigns it to the director, and wires up signal callbacks. */
install(director: PlayableDirector): TimelineAssetModel;
}
/**
* Builder for animation tracks.
* Provides `.clip()` for pre-built AnimationClips and `.track()` for inline animation definition.
*
* @category Animation and Sequencing
*/
export interface AnimationTrackBuilder extends TimelineBuilderBase {
/** Adds a pre-built AnimationClip */
clip(asset: AnimationClip, options?: AnimationClipOptions): AnimationTrackBuilder;
/** Adds a clip from a single {@link TrackDescriptor} */
clip(descriptor: TrackDescriptor, options?: AnimationClipOptions): AnimationTrackBuilder;
/** Adds a clip from multiple {@link TrackDescriptor}s */
clip(descriptors: TrackDescriptor[], options?: AnimationClipOptions): AnimationTrackBuilder;
/** Adds an animation track for an Object3D's position or scale */
track(target: Object3D, property: "position" | "scale", keyframes: KF<Vec3Value>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for an Object3D's quaternion */
track(target: Object3D, property: "quaternion", keyframes: KF<QuatValue>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for an Object3D's rotation (Euler, converted to quaternion) */
track(target: Object3D, property: "rotation", keyframes: KF<EulerValue>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for an Object3D's visibility */
track(target: Object3D, property: "visible", keyframes: KF<boolean>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for a material's numeric property */
track(target: Material, property: "opacity" | "roughness" | "metalness" | "alphaTest" | "emissiveIntensity" | "envMapIntensity" | "bumpScale" | "displacementScale" | "displacementBias", keyframes: KF<number>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for a material's color property */
track(target: Material, property: "color" | "emissive", keyframes: KF<ColorValue>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for a light's numeric property */
track(target: Light, property: "intensity" | "distance" | "angle" | "penumbra" | "decay", keyframes: KF<number>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for a light's color */
track(target: Light, property: "color", keyframes: KF<ColorValue>, options?: TrackOptions): AnimationTrackBuilder;
/** Adds an animation track for a camera's numeric property */
track(target: PerspectiveCamera, property: "fov" | "near" | "far" | "zoom", keyframes: KF<number>, options?: TrackOptions): AnimationTrackBuilder;
/** Mutes this track so it is skipped during playback */
muted(muted?: boolean): AnimationTrackBuilder;
}
/**
* Builder for audio tracks. Provides `.clip()` for adding audio clips by URL.
* @category Animation and Sequencing
*/
export interface AudioTrackBuilder extends TimelineBuilderBase {
/** Adds an audio clip by URL */
clip(url: string, options: AudioClipOptions): AudioTrackBuilder;
/** Mutes this track so it is skipped during playback */
muted(muted?: boolean): AudioTrackBuilder;
}
/**
* Builder for activation tracks. Provides `.clip()` for defining activation windows.
* @category Animation and Sequencing
*/
export interface ActivationTrackBuilder extends TimelineBuilderBase {
/** Adds an activation clip that shows/hides the bound object */
clip(options: ActivationClipOptions): ActivationTrackBuilder;
/** Mutes this track so it is skipped during playback */
muted(muted?: boolean): ActivationTrackBuilder;
}
/**
* Builder for control tracks. Provides `.clip()` for controlling nested objects/timelines.
* @category Animation and Sequencing
*/
export interface ControlTrackBuilder extends TimelineBuilderBase {
/** Adds a control clip for a source object */
clip(sourceObject: Object3D, options: ControlClipOptions): ControlTrackBuilder;
/** Mutes this track so it is skipped during playback */
muted(muted?: boolean): ControlTrackBuilder;
}
/**
* Builder for signal tracks. Provides `.signal()` for callback-based signals and `.marker()` for asset-based markers.
* @category Animation and Sequencing
*/
export interface SignalTrackBuilder extends TimelineBuilderBase {
/** Adds a signal with a callback that fires at the given time */
signal(time: number, callback: Function, options?: SignalMarkerOptions): SignalTrackBuilder;
/** Adds a signal marker referencing a signal asset by guid */
marker(time: number, asset: string, options?: SignalMarkerOptions): SignalTrackBuilder;
/** Mutes this track so it is skipped during playback */
muted(muted?: boolean): SignalTrackBuilder;
}
/**
* Builder for marker tracks. Provides `.marker()` for adding markers.
* @category Animation and Sequencing
*/
export interface MarkerTrackBuilder extends TimelineBuilderBase {
/** Adds a marker referencing a signal asset by guid */
marker(time: number, asset: string, options?: SignalMarkerOptions): MarkerTrackBuilder;
/** Mutes this track so it is skipped during playback */
muted(muted?: boolean): MarkerTrackBuilder;
}
/**
* A fluent builder for creating timeline assets ({@link TimelineAssetModel}) from code.
*
* Use {@link TimelineBuilder.create} to start building a timeline.
*
* @example Using build() for timelines without signal callbacks
* ```ts
* const timeline = TimelineBuilder.create("MySequence")
* .animationTrack("Character", animator)
* .clip(walkClip, { duration: 2, easeIn: 0.3 })
* .clip(runClip, { duration: 3, easeIn: 0.5, easeOut: 0.5 })
* .activationTrack("FX", particleObject)
* .clip({ start: 1, duration: 2 })
* .audioTrack("Music", audioSource)
* .clip("music.mp3", { start: 0, duration: 5, volume: 0.8 })
* .build();
*
* director.playableAsset = timeline;
* director.play();
* ```
*
* @example With inline tracks (no pre-built clips needed)
* ```ts
* TimelineBuilder.create("DoorSequence")
* .animationTrack("Door", door)
* .track(door, "position", { from: [0, 0, 0], to: [2, 0, 0], duration: 1 })
* .track(light, "intensity", { from: 0, to: 5, duration: 1 })
* .signalTrack("Events")
* .signal(0.5, () => playSound("creak"))
* .install(director);
*
* director.play();
* ```
*
* @example Using install() with signal callbacks
* ```ts
* TimelineBuilder.create("WithSignals")
* .animationTrack("Character", animator)
* .clip(walkClip, { duration: 2 })
* .signalTrack("Events")
* .signal(1.0, () => console.log("1 second!"))
* .signal(2.0, () => spawnParticles())
* .install(director);
*
* director.play();
* ```
*
* @category Animation and Sequencing
* @group Utilities
*/
export declare class TimelineBuilder {
private _name;
private _tracks;
private _currentTrack;
private _pendingSignals;
private _idProvider;
private constructor();
/**
* Creates a new TimelineBuilder instance.
* @param name - Name for the timeline asset
* @param seed - Optional numeric seed for deterministic guid generation. Defaults to `Date.now()`.
*/
static create(name?: string, seed?: number): TimelineBuilderBase;
/**
* Adds an animation track. Chain `.clip()` calls to add pre-built clips,
* or chain `.track()` calls to define animation data inline:
*
* @example With pre-built AnimationClip
* ```ts
* .animationTrack("Character", animator)
* .clip(walkClip, { duration: 2, easeIn: 0.3 })
* .clip(runClip, { duration: 3 })
* ```
*
* @example With inline tracks
* ```ts
* .animationTrack("Door", door)
* .track(door, "position", { from: [0, 0, 0], to: [2, 0, 0], duration: 1 })
* .track(light, "intensity", { from: 0, to: 5, duration: 1 })
* ```
*
* @param name - Display name for the track
* @param binding - The Animator or Object3D to animate
*/
animationTrack(name: string, binding?: Animator | Object3D | null): AnimationTrackBuilder;
/**
* Adds an audio track. Subsequent `.clip()` calls add audio clips to this track.
* @param name - Display name for the track
* @param binding - The AudioSource to play audio on (optional)
* @param volume - Track volume multiplier (default: 1)
*/
audioTrack(name: string, binding?: AudioSource | Object3D | null, volume?: number): AudioTrackBuilder;
/**
* Adds an activation track. Subsequent `.clip()` calls define when the bound object is active.
* @param name - Display name for the track
* @param binding - The Object3D to show/hide
*/
activationTrack(name: string, binding?: Object3D | null): ActivationTrackBuilder;
/**
* Adds a control track. Subsequent `.clip()` calls control nested timelines or objects.
* @param name - Display name for the track
*/
controlTrack(name: string): ControlTrackBuilder;
/**
* Adds a signal track. Use `.signal()` or `.marker()` to add signal markers.
* @param name - Display name for the track
* @param binding - The SignalReceiver component (optional — if using `.signal()` with callbacks, one is created automatically by {@link install})
*/
signalTrack(name: string, binding?: SignalReceiver | Object3D | null): SignalTrackBuilder;
/**
* Adds a marker track. Use `.marker()` to add markers.
* @param name - Display name for the track
*/
markerTrack(name: string): MarkerTrackBuilder;
/**
* Adds a clip to the current track. The clip type must match the track type.
*
* - On an **animation track**: pass an `AnimationClip`, a {@link TrackDescriptor}, or a `TrackDescriptor[]` — and optional {@link AnimationClipOptions}
* - On an **audio track**: pass a clip URL (string) and {@link AudioClipOptions}
* - On an **activation track**: pass {@link ActivationClipOptions}
* - On a **control track**: pass an Object3D and {@link ControlClipOptions}
*/
clip(asset: AnimationClip, options?: AnimationClipOptions): this;
clip(descriptor: TrackDescriptor, options?: AnimationClipOptions): this;
clip(descriptors: TrackDescriptor[], options?: AnimationClipOptions): this;
clip(url: string, options: AudioClipOptions): this;
clip(options: ActivationClipOptions): this;
clip(sourceObject: Object3D, options: ControlClipOptions): this;
/**
* Adds a signal marker to the current signal or marker track.
* @param time - Time in seconds when the signal fires
* @param asset - The signal asset identifier (guid string)
* @param options - Optional marker configuration
*/
marker(time: number, asset: string, options?: SignalMarkerOptions): this;
/**
* Adds a signal with a callback to the current signal track.
* This is a convenience method that automatically generates a signal asset guid,
* adds the marker, and registers the callback so that {@link install} can wire up
* the `SignalReceiver` on the director's GameObject.
*
* @param time - Time in seconds when the signal fires
* @param callback - The function to invoke when the signal fires
* @param options - Optional marker configuration
*
* @example
* ```ts
* const timeline = TimelineBuilder.create("Sequence")
* .signalTrack("Events")
* .signal(1.0, () => console.log("1 second reached!"))
* .signal(3.5, () => console.log("halfway!"), { emitOnce: true })
* .install(director);
* ```
*/
signal(time: number, callback: Function, options?: SignalMarkerOptions): this;
/**
* Mutes the current track so it is skipped during playback.
*/
muted(muted?: boolean): this;
/** Adds an animation track descriptor for an Object3D's position or scale to the current animation track */
track(target: Object3D, property: "position" | "scale", keyframes: KF<Vec3Value>, options?: TrackOptions): this;
/** Adds an animation track descriptor for an Object3D's quaternion to the current animation track */
track(target: Object3D, property: "quaternion", keyframes: KF<QuatValue>, options?: TrackOptions): this;
/** Adds an animation track descriptor for an Object3D's rotation (Euler, converted to quaternion) to the current animation track */
track(target: Object3D, property: "rotation", keyframes: KF<EulerValue>, options?: TrackOptions): this;
/** Adds an animation track descriptor for an Object3D's visibility to the current animation track */
track(target: Object3D, property: "visible", keyframes: KF<boolean>, options?: TrackOptions): this;
/** Adds an animation track descriptor for a material's numeric property to the current animation track */
track(target: Material, property: "opacity" | "roughness" | "metalness" | "alphaTest" | "emissiveIntensity" | "envMapIntensity" | "bumpScale" | "displacementScale" | "displacementBias", keyframes: KF<number>, options?: TrackOptions): this;
/** Adds an animation track descriptor for a material's color property to the current animation track */
track(target: Material, property: "color" | "emissive", keyframes: KF<ColorValue>, options?: TrackOptions): this;
/** Adds an animation track descriptor for a light's numeric property to the current animation track */
track(target: Light, property: "intensity" | "distance" | "angle" | "penumbra" | "decay", keyframes: KF<number>, options?: TrackOptions): this;
/** Adds an animation track descriptor for a light's color to the current animation track */
track(target: Light, property: "color", keyframes: KF<ColorValue>, options?: TrackOptions): this;
/** Adds an animation track descriptor for a camera's numeric property to the current animation track */
track(target: PerspectiveCamera, property: "fov" | "near" | "far" | "zoom", keyframes: KF<number>, options?: TrackOptions): this;
/**
* Builds and returns the {@link TimelineAssetModel}.
* Assign the result to `PlayableDirector.playableAsset` to play it.
*
* If you used `.signal()` with callbacks, use {@link install} instead — it calls `build()`
* internally and also wires up the SignalReceiver on the director's GameObject.
*/
build(): TimelineAssetModel;
/**
* Builds the timeline asset, assigns it to the director, and wires up any
* `.signal()` callbacks by creating/configuring a {@link SignalReceiver} on the
* director's GameObject.
*
* @param director - The PlayableDirector to install the timeline on
* @returns The built TimelineAssetModel (also assigned to `director.playableAsset`)
*
* @example
* ```ts
* TimelineBuilder.create("MyTimeline")
* .animationTrack("Anim", animator)
* .clip(walkClip, { duration: 2 })
* .signalTrack("Events")
* .signal(1.0, () => console.log("signal fired!"))
* .install(director);
*
* director.play();
* ```
*/
install(director: PlayableDirector): TimelineAssetModel;
private pushTrack;
/** Commits any pending `.track()` descriptors on the current animation track into a clip */
private commitInlineTracks;
}
export {};