Files
webeditor3d/.kiro/specs/animation-playback/requirements.md

12 KiB

Requirements Document

Introduction

This feature adds animation playback support for imported GLB/GLTF model assets in the browser-based 3D scene editor. Animations embedded in GLB/GLTF files are already detected and stored in ModelAssetMetadata.animationNames during import. This slice surfaces those animation names in the editor UI, allows authors to configure a default animation clip per model instance, wires playAnimation and stopAnimation actions into the existing Trigger → Action → Target interaction system, drives playback in the runtime via three.js AnimationMixer, and persists all animation settings through save/load with a schema version bump and migration.

The scope is deliberately narrow: no timeline editor, no blend trees, no cross-fade authoring. The goal is a minimal, explicit, and correct first pass.

Glossary

  • AnimationClip: A named animation track embedded in a GLB/GLTF file, identified by its string name as stored in ModelAssetMetadata.animationNames.
  • AnimationMixer: The three.js AnimationMixer object that drives AnimationClip playback on a scene object.
  • ModelInstance: A placed scene instance of an imported model asset, stored in SceneDocument.modelInstances, separate from entities.
  • ModelAssetMetadata: Canonical metadata extracted from a GLB/GLTF file at import time, including animationNames: string[].
  • InteractionLink: A typed Trigger → Action → Target record stored in SceneDocument.interactionLinks.
  • PlayAnimationAction: A new InteractionAction type that targets a model instance and names a clip to play.
  • StopAnimationAction: A new InteractionAction type that targets a model instance and stops its active animation.
  • RuntimeAnimationState: Per-instance runtime state tracking the active AnimationMixer and current AnimationAction.
  • Document: The canonical editor state (SceneDocument), independent of three.js scene graph objects.
  • Runner: The browser runtime that loads and plays scenes.
  • Command: An undoable state transition applied to the Document.

Requirements

Requirement 1: Animation Metadata Availability

User Story: As an author, I want to see which animation clips are available on an imported model, so that I can make informed decisions when configuring animation playback.

Acceptance Criteria

  1. THE ModelAssetMetadata SHALL contain an animationNames field of type string[] that lists every animation clip name extracted from the imported GLB/GLTF file.
  2. WHEN a GLB/GLTF file containing zero named animations is imported, THE ModelAssetMetadata SHALL store an empty animationNames array.
  3. WHEN a GLB/GLTF file containing one or more animations is imported, THE ModelAssetMetadata SHALL store each animation name exactly once, sorted lexicographically.
  4. WHEN an animation clip has an empty or whitespace-only name, THE importer SHALL substitute a fallback name of the form "Animation N" where N is the one-based index of the clip.
  5. THE migrateSceneDocument function SHALL correctly read and validate the animationNames field when loading documents of version 12 or later.

Requirement 2: Model Instance Animation Configuration

User Story: As an author, I want to configure a default animation clip and autoplay setting on a model instance, so that the model plays the right animation when the scene starts.

Acceptance Criteria

  1. THE ModelInstance document type SHALL include an optional animationClipName field of type string | undefined that names the clip to play by default.
  2. THE ModelInstance document type SHALL include an optional animationAutoplay field of type boolean | undefined that controls whether the clip starts playing automatically when the runner loads the scene.
  3. WHEN animationClipName is set to a non-empty string, THE Runner SHALL use that clip name to look up the AnimationClip in the loaded GLTF animations array.
  4. WHEN animationClipName is undefined or empty, THE Runner SHALL not start any animation automatically for that model instance.
  5. WHEN animationAutoplay is true and animationClipName is set, THE Runner SHALL begin playing the named clip immediately when the scene loads, looping continuously.
  6. WHEN animationAutoplay is false or undefined, THE Runner SHALL not autoplay any animation for that model instance on scene load.
  7. THE createModelInstance factory function SHALL accept animationClipName and animationAutoplay as optional fields and include them in the returned ModelInstance.
  8. THE cloneModelInstance function SHALL copy animationClipName and animationAutoplay faithfully.
  9. THE areModelInstancesEqual function SHALL include animationClipName and animationAutoplay in its equality check.

Requirement 3: Interaction System — Play Animation Action

User Story: As an author, I want to wire a trigger to play a specific animation clip on a model instance, so that animations can be activated by player interaction or zone entry.

Acceptance Criteria

  1. THE InteractionAction union SHALL include a PlayAnimationAction type with fields type: "playAnimation", targetModelInstanceId: string, and clipName: string.
  2. WHEN a playAnimation action is dispatched, THE Runner SHALL locate the model instance by targetModelInstanceId and start playing the clip named clipName, looping continuously.
  3. WHEN a playAnimation action targets a model instance whose loaded asset does not contain a clip matching clipName, THE Runner SHALL log a warning and take no further action.
  4. WHEN a playAnimation action targets a model instance that is not present in the runtime scene, THE Runner SHALL log a warning and take no further action.
  5. THE createPlayAnimationInteractionLink factory function SHALL validate that sourceEntityId, targetModelInstanceId, and clipName are all non-empty strings.
  6. THE RuntimeInteractionDispatcher interface SHALL include a playAnimation(instanceId: string, clipName: string, link: InteractionLink): void method.

Requirement 4: Interaction System — Stop Animation Action

User Story: As an author, I want to wire a trigger to stop the animation on a model instance, so that animations can be halted by player interaction or zone exit.

Acceptance Criteria

  1. THE InteractionAction union SHALL include a StopAnimationAction type with fields type: "stopAnimation" and targetModelInstanceId: string.
  2. WHEN a stopAnimation action is dispatched, THE Runner SHALL locate the model instance by targetModelInstanceId and stop any currently playing animation, leaving the model in its stopped pose.
  3. WHEN a stopAnimation action targets a model instance with no active animation, THE Runner SHALL take no action and produce no error.
  4. WHEN a stopAnimation action targets a model instance that is not present in the runtime scene, THE Runner SHALL log a warning and take no further action.
  5. THE createStopAnimationInteractionLink factory function SHALL validate that sourceEntityId and targetModelInstanceId are both non-empty strings.
  6. THE RuntimeInteractionDispatcher interface SHALL include a stopAnimation(instanceId: string, link: InteractionLink): void method.

Requirement 5: Runtime Animation Playback

User Story: As a player, I want model animations to play smoothly in the runner, so that the scene feels alive and responsive.

Acceptance Criteria

  1. WHEN the Runner loads a scene, THE RuntimeHost SHALL create one AnimationMixer per model instance that has a loaded GLTF asset with at least one animation clip.
  2. WHEN the Runner's per-frame render loop executes, THE RuntimeHost SHALL call mixer.update(dt) for every active AnimationMixer, where dt is the elapsed time in seconds since the previous frame.
  3. WHEN a model instance is removed from the scene or the scene is reloaded, THE RuntimeHost SHALL stop and dispose of the associated AnimationMixer.
  4. WHEN animationAutoplay is true and animationClipName is set on a model instance, THE RuntimeHost SHALL start the named clip playing on scene load before the first rendered frame.
  5. WHEN a playAnimation action is dispatched for a model instance that already has an active animation, THE RuntimeHost SHALL stop the current animation and start the new clip.
  6. THE RuntimeModelInstance runtime data type SHALL include animationClipName: string | undefined and animationAutoplay: boolean | undefined so the runner can act on them without re-reading the document.

Requirement 6: Inspector UI — Animation Configuration

User Story: As an author, I want to configure animation settings on a selected model instance in the inspector panel, so that I can set up autoplay and default clips without editing JSON.

Acceptance Criteria

  1. WHEN a model instance is selected and its asset has at least one animation clip, THE Inspector SHALL display an animation section showing the list of available clip names from ModelAssetMetadata.animationNames.
  2. WHEN a model instance is selected and its asset has zero animation clips, THE Inspector SHALL not display an animation section.
  3. WHEN the author selects a clip name from the animation section, THE Inspector SHALL dispatch an UpsertModelInstanceCommand that sets animationClipName on the model instance.
  4. WHEN the author toggles the autoplay checkbox, THE Inspector SHALL dispatch an UpsertModelInstanceCommand that sets animationAutoplay on the model instance.
  5. WHEN the author clears the clip selection, THE Inspector SHALL dispatch an UpsertModelInstanceCommand that sets animationClipName to undefined.
  6. THE Inspector animation section SHALL reflect the current animationClipName and animationAutoplay values from the selected model instance.

User Story: As an author, I want to create play/stop animation interaction links in the interaction panel, so that I can connect triggers to animation actions.

Acceptance Criteria

  1. WHEN the author creates a new interaction link and selects the playAnimation action type, THE Interaction Panel SHALL display fields for selecting the target model instance and entering or selecting the clip name.
  2. WHEN the author creates a new interaction link and selects the stopAnimation action type, THE Interaction Panel SHALL display a field for selecting the target model instance.
  3. WHEN the author saves a playAnimation link, THE Panel SHALL dispatch an UpsertInteractionLinkCommand with a valid PlayAnimationAction.
  4. WHEN the author saves a stopAnimation link, THE Panel SHALL dispatch an UpsertInteractionLinkCommand with a valid StopAnimationAction.
  5. WHEN an existing playAnimation or stopAnimation link is displayed in the interaction panel, THE Panel SHALL show the resolved model instance name and clip name.

Requirement 8: Persistence — Schema Migration

User Story: As an author, I want my scenes to load correctly after the animation feature is added, so that existing work is not lost.

Acceptance Criteria

  1. THE SCENE_DOCUMENT_VERSION constant SHALL be incremented to 12 to reflect the addition of animation fields.
  2. WHEN migrateSceneDocument reads a document at version 11, THE migration SHALL produce a valid version-12 document with animationClipName: undefined and animationAutoplay: undefined on every existing model instance.
  3. WHEN migrateSceneDocument reads a document at version 12, THE migration SHALL read and validate animationClipName and animationAutoplay on each model instance, accepting undefined or valid string/boolean values respectively.
  4. WHEN migrateSceneDocument reads a document at version 12 containing a playAnimation or stopAnimation interaction link, THE migration SHALL read and validate the action fields correctly.
  5. THE migrateSceneDocument function SHALL reject a version-12 document where a playAnimation action has an empty clipName string.
  6. THE serialization round-trip (serialize then deserialize) SHALL produce a document equal to the original for any valid version-12 document containing animation fields.