/** * A rig-driven 3D model loaded from an animated glTF/GLB asset. Unlike a static * {@link GLTFScene} (which flattens each node into an independent {@link Mesh}), * a `GLTFModel` keeps the node **hierarchy** intact so a parent transform * carries its children — e.g. rotating a character's `torso` moves the attached * `arm` and `head`. Each frame, the active animation clip is sampled, world * matrices are propagated down the tree, and every part mesh's placement is * re-derived. * * The animation API mirrors {@link Sprite} for familiarity — `setCurrentAnimation`, * `isCurrentAnimation`, `getAnimationNames`, `play`/`pause`, `animationpause` — * but uses the cleaner options form everywhere: `setCurrentAnimation(name, { * loop, speed, onComplete, next })`. Here `animationspeed` is a **playback * multiplier** (1 = authored speed), not a per-frame delay. * * The model is placed like any other renderable: `pos`, `depth` and the * transform helpers (`rotate`, `scale`) move the **whole rig**, and compose * with whatever the active clip is doing — so a walk cycle plays wherever the * character happens to stand. * * Instances are created automatically by {@link GLTFScene} when the asset * defines animation channels; you usually obtain one via `level.load(...)` * rather than constructing it directly. * @augments Container * @example * const boat = new me.GLTFModel(me.loader.getGLTF("boat"), { scale: 30, lit: true }); * boat.setCurrentAnimation("paddle", { loop: true }); * app.world.addChild(boat); * * // ...then drive it like anything else * boat.pos.set(steerX, waterLevel); * boat.depth = travelled; * boat.rotate(lean - lastLean, AXIS_Z); */ export default class GLTFModel extends Container { /** * @param {import("../../loader/loader.js").GLTFData} data - the parsed glTF descriptor, as returned by {@link loader.getGLTF} * @param {object} [options] * @param {number} [options.scale=1] - pixels per glTF unit (uniform scene scale) * @param {boolean} [options.rightHanded=true] - glTF Y-up → engine Y-down via a rotation (no mirror) * @param {boolean} [options.lit=false] - render the part meshes through the lit batcher * @param {boolean} [options.castGroundShadow] - give the parts a ground shadow; omit to inherit the application setting * @param {number} [options.shadowGroundY] - world Y of the floor those shadows land on */ constructor(data: import("../../loader/loader.js").GLTFData, options?: { scale?: number | undefined; rightHanded?: boolean | undefined; lit?: boolean | undefined; castGroundShadow?: boolean | undefined; shadowGroundY?: number | undefined; }); /** * playback multiplier for the current animation (1 = authored speed). * @type {number} * @default 1 */ animationspeed: number; /** * pause/resume the current animation without losing its pose or time. * @type {boolean} * @default false */ animationpause: boolean; /** * the names of every animation clip defined by the source asset. * @returns {string[]} * @example * model.getAnimationNames(); // ["idle", "walk", "sprint", ...] */ getAnimationNames(): string[]; /** * return true if `name` is the currently playing animation. * @param {string} name - animation clip id * @returns {boolean} */ isCurrentAnimation(name: string): boolean; /** * play the given animation clip. The second argument mirrors {@link Sprite} * and accepts the same forms: omit to loop forever, a `string` to chain to * another clip when this one ends, a `function` legacy completion callback * (return `false` to hold the final pose), or an options object. * @param {string} name - animation clip id (see {@link GLTFModel#getAnimationNames}) * @param {AnimationOptionsInput} [options] - loop / chain / completion behavior * @param {boolean} [preserveTime=false] - keep the current playback time instead of restarting at 0 * @returns {GLTFModel} this, for chaining * @example * model.setCurrentAnimation("walk"); // loop forever * model.setCurrentAnimation("die", { loop: false }); // play once, hold last pose * model.setCurrentAnimation("jump", { next: "idle" }); // jump, then idle * model.setCurrentAnimation("walk", { speed: 2 }); // twice as fast * model.setCurrentAnimation("emote-yes", () => spawnFx()); // legacy callback */ setCurrentAnimation(name: string, options?: AnimationOptionsInput, preserveTime?: boolean): GLTFModel; /** * Play an animation clip, or resume the current one. A shorthand for * {@link GLTFModel#setCurrentAnimation}: call with a clip name to switch to * (and start) it, or with no argument to resume after {@link GLTFModel#pause}. * Always clears the paused state. * @param {string} [name] - clip id to play; omit to just resume * @param {AnimationOptionsInput} [options] - loop / chain / completion behavior (see {@link GLTFModel#setCurrentAnimation}) * @returns {GLTFModel} this, for chaining * @example * model.play("walk"); // switch to + play "walk" * model.play("die", { loop: false }); // play once, hold the last pose * model.pause(); * model.play(); // resume */ play(name?: string, options?: AnimationOptionsInput): GLTFModel; /** * Pause the current animation, freezing it at its current pose. Resume with * {@link GLTFModel#play}. * @returns {GLTFModel} this, for chaining */ pause(): GLTFModel; /** * Stop playback and reset the rig to its bind/rest pose (no clip active). * After this {@link GLTFModel#isCurrentAnimation} is false for every clip; * call {@link GLTFModel#play} to start again. (Use {@link GLTFModel#pause} * instead to freeze in place.) * @returns {GLTFModel} this, for chaining */ stop(): GLTFModel; /** * {@link Container#draw} translates the renderer by `pos` so children draw * in container-local space. This rig's children are already in world space, * so that translation is undone here rather than doubling the placement. * Paired with `autoTransform = false`, which suppresses the matching * rotation fold. * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance * @param {Camera2d} [viewport] - the camera rendering this frame */ draw(renderer: CanvasRenderer | WebGLRenderer, viewport?: Camera2d): void; /** * Where this model is, as a 2D box — what culling, picking and the physics * broadphase all read. * * A `Container` has no dimensions of its own, and one left that way reports * an EMPTY bounds: a model claiming to be nowhere, which the broadphase * cannot place. A rig's size is known at load from the glTF scene AABB, so * this is a placement of a measurement taken once in the constructor rather * than a walk of the parts — `Container#updateBounds` with * `enableChildBoundsUpdate` would re-measure the whole rig on every call, * and that flag is for a group whose extent really is its members' union. * * The box is the model's bounding SPHERE squared off, so a turned model * still reports something that contains it. Generous bounds only cost * broadphase pruning; tight ones lose contacts. * @param {boolean} [absolute=true] - in world rather than local coordinates * @returns {Bounds} this model's bounding box */ updateBounds(absolute?: boolean): Bounds; } import Container from "../../renderable/container.js"; import type { AnimationOptionsInput } from "../../renderable/animation.ts"; //# sourceMappingURL=GLTFModel.d.ts.map