Files
tinyusdz/doc/THREEJS_ANIMATION.md
Syoyo Fujita e546a47090 Add TypedArray support to PODTimeSamples and redesign animation system for Three.js compatibility
- PODTimeSamples: Add add_typed_array_sample() and get_typed_array_at() to store TypedArray<T> as 8-byte packed pointers instead of full Value objects
- Tydra: Redesign animation system to match glTF/Three.js architecture
  - Replace Animation struct with AnimationClip containing KeyframeSampler and AnimationChannel
  - Use flat float arrays for keyframe storage (matches Three.js KeyframeTrack)
  - Remove node_animations from Node struct - animations now reference nodes by index
  - Add AnimationPath enum (Translation/Rotation/Scale/Weights) matching glTF paths
  - Add AnimationInterpolation enum (Linear/Step/CubicSpline) matching glTF spec
- Documentation: Add THREEJS_ANIMATION.md with Three.js animation system reference
- Documentation: Add ANIMATION_SYSTEM_REDESIGN.md with migration guide

This makes Tydra animations directly compatible with Three.js AnimationClip and
glTF 2.0 animation structure for easier web export.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 23:25:01 +09:00

336 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Three.js Animation System Documentation
## Overview
This document describes Three.js's keyframe animation system with a focus on rigid node animations (translation, scale, rotation/quaternion), particularly as it relates to glTF loading and the design considerations for Tydra RenderScene conversion.
## Core Animation Architecture
### 1. Animation System Hierarchy
The Three.js animation system consists of three main components:
```
Keyframes (raw data)
KeyframeTrack (property animation)
AnimationClip (collection of tracks)
AnimationMixer (playback control)
```
### 2. KeyframeTrack Types
Three.js provides specialized track types for different properties:
- **VectorKeyframeTrack**: For `position` and `scale` (3D vectors)
- **QuaternionKeyframeTrack**: For `rotation` (quaternions)
- **NumberKeyframeTrack**: For single scalar values or individual components
- **ColorKeyframeTrack**: For color animations
- **BooleanKeyframeTrack**: For boolean properties
- **StringKeyframeTrack**: For string properties
## Rigid Node Animation Properties
### Translation (Position)
**Track Type**: `VectorKeyframeTrack`
**Property Path**: `.position`
**Value Type**: 3D vector [x, y, z]
**Units**: Scene units (typically meters in glTF)
```javascript
const positionKF = new THREE.VectorKeyframeTrack(
'.position',
[0, 1, 2], // Times in seconds
[0, 0, 0, // Position at t=0: (x=0, y=0, z=0)
30, 0, 0, // Position at t=1: (x=30, y=0, z=0)
0, 0, 0] // Position at t=2: (x=0, y=0, z=0)
);
```
### Scale
**Track Type**: `VectorKeyframeTrack`
**Property Path**: `.scale`
**Value Type**: 3D vector [x, y, z]
**Units**: Scale factors (1.0 = no scaling)
```javascript
const scaleKF = new THREE.VectorKeyframeTrack(
'.scale',
[0, 1, 2], // Times in seconds
[1, 1, 1, // Scale at t=0
2, 2, 2, // Scale at t=1 (2x in all axes)
1, 1, 1] // Scale at t=2
);
```
### Rotation
#### Quaternion Rotation (Recommended)
**Track Type**: `QuaternionKeyframeTrack`
**Property Path**: `.quaternion`
**Value Type**: Quaternion [x, y, z, w]
**Units**: Unit quaternion (normalized)
```javascript
const quaternionKF = new THREE.QuaternionKeyframeTrack(
'.quaternion',
[0, 1, 2], // Times in seconds
[0, 0, 0, 1, // Identity quaternion at t=0
0, 0.707, 0, 0.707, // 90° Y rotation at t=1
0, 0, 0, 1] // Identity at t=2
);
```
**Important**: Three.js animations use quaternions for rotations, NOT Euler angles. This avoids gimbal lock and provides smooth interpolation via spherical linear interpolation (slerp).
#### Euler Angle Rotation (Limited Support)
While `Object3D.rotation` stores Euler angles, **animations cannot directly target Euler angles**. You must either:
1. Use quaternions (recommended)
2. Animate individual rotation components:
```javascript
// Animate Y-axis rotation only
const rotationYKF = new THREE.NumberKeyframeTrack(
'.rotation[y]', // Note the bracket notation
[0, 1, 2],
[0, Math.PI/2, 0] // Radians
);
```
## Euler Angle Specifications
### Units
**All angles in Three.js are in RADIANS**, not degrees.
### Rotation Order
- **Default**: `'XYZ'`
- **Available orders**: `'XYZ'`, `'YZX'`, `'ZXY'`, `'XZY'`, `'YXZ'`, `'ZYX'`
- **Type**: Intrinsic Tait-Bryan angles
- **Meaning**: Rotations are performed with respect to the local coordinate system
- For 'XYZ': Rotate around local X, then local Y, then local Z
### Setting Euler Order
```javascript
object.rotation.order = 'YXZ'; // Change rotation order
```
## glTF Animation Mapping
### glTF to Three.js Translation
When GLTFLoader loads animations, it maps:
| glTF Path | Three.js Track Type | Three.js Property |
|-----------|-------------------|-------------------|
| `translation` | VectorKeyframeTrack | `.position` |
| `rotation` | QuaternionKeyframeTrack | `.quaternion` |
| `scale` | VectorKeyframeTrack | `.scale` |
| `weights` | NumberKeyframeTrack | `.morphTargetInfluences[i]` |
### glTF Animation Structure
```json
{
"animations": [{
"channels": [{
"sampler": 0,
"target": {
"node": 0,
"path": "rotation" // or "translation", "scale"
}
}],
"samplers": [{
"input": 0, // Accessor for time values
"output": 1, // Accessor for property values
"interpolation": "LINEAR" // or "STEP", "CUBICSPLINE"
}]
}]
}
```
### Interpolation Modes
1. **STEP**: Discrete, no interpolation
- Three.js: `THREE.InterpolateDiscrete`
2. **LINEAR**: Linear interpolation
- Position/Scale: Linear interpolation
- Rotation: Spherical linear interpolation (slerp)
- Three.js: `THREE.InterpolateLinear`
3. **CUBICSPLINE**: Cubic spline with tangents
- Requires in-tangent, value, out-tangent for each keyframe
- Three.js: Custom interpolant in GLTFLoader
- Can cause issues; "Always Sample Animation" in Blender forces LINEAR
### Loading and Playing glTF Animations
```javascript
const loader = new THREE.GLTFLoader();
loader.load('model.gltf', (gltf) => {
scene.add(gltf.scene);
// Create AnimationMixer
const mixer = new THREE.AnimationMixer(gltf.scene);
// Play all animations
gltf.animations.forEach((clip) => {
mixer.clipAction(clip).play();
});
// Update in render loop
function animate(deltaTime) {
mixer.update(deltaTime);
}
});
```
## Property Path Syntax
Three.js supports complex property paths:
```javascript
'.position' // Object position
'.rotation[x]' // X component of rotation
'.scale' // Object scale
'.material.opacity' // Material opacity
'.bones[R_hand].scale' // Specific bone scale
'.materials[3].diffuse[r]' // Red channel of 4th material
'.morphTargetInfluences[0]' // First morph target
```
## Data Storage Format
Keyframe data is stored in flat arrays:
```javascript
// For VectorKeyframeTrack (3 values per keyframe)
times = [0, 1, 2];
values = [x0, y0, z0, x1, y1, z1, x2, y2, z2];
// For QuaternionKeyframeTrack (4 values per keyframe)
times = [0, 1, 2];
values = [x0, y0, z0, w0, x1, y1, z1, w1, x2, y2, z2, w2];
```
## Complete Animation Example
```javascript
// Create keyframe tracks
const positionKF = new THREE.VectorKeyframeTrack(
'.position',
[0, 1, 2],
[0, 0, 0, 10, 5, 0, 0, 0, 0]
);
const quaternionKF = new THREE.QuaternionKeyframeTrack(
'.quaternion',
[0, 1, 2],
[0, 0, 0, 1,
0, 0.707, 0, 0.707,
0, 0, 0, 1]
);
const scaleKF = new THREE.VectorKeyframeTrack(
'.scale',
[0, 1, 2],
[1, 1, 1, 2, 2, 2, 1, 1, 1]
);
// Create animation clip
const clip = new THREE.AnimationClip('Action', 2, [
positionKF,
quaternionKF,
scaleKF
]);
// Create mixer and play
const mixer = new THREE.AnimationMixer(mesh);
const action = mixer.clipAction(clip);
action.play();
// Update in render loop
function animate() {
const delta = clock.getDelta();
mixer.update(delta);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
```
## Design Considerations for Tydra RenderScene
### Key Points for Implementation
1. **Use Quaternions for Rotations**
- Store rotations as quaternions to match Three.js animation system
- Avoid Euler angles in animation data
2. **Radians for All Angles**
- Ensure all angle values are in radians
- Convert from degrees if necessary
3. **Flat Array Storage**
- Store keyframe values in flat arrays
- Values array length = times array length × components per value
4. **Support Multiple Interpolation Modes**
- Implement STEP, LINEAR, and optionally CUBICSPLINE
- Use slerp for quaternion interpolation
5. **Time in Seconds**
- Animation times should be in seconds (floating point)
- Support arbitrary time ranges
6. **Property Path Mapping**
- Map USD properties to Three.js property paths
- Support nested property access
### Recommended Data Structure
```cpp
struct AnimationChannel {
enum PropertyType {
TRANSLATION, // vec3
ROTATION, // quat
SCALE // vec3
};
PropertyType type;
std::vector<float> times; // Keyframe times in seconds
std::vector<float> values; // Flat array of values
InterpolationType interpolation; // STEP, LINEAR, CUBICSPLINE
int nodeIndex; // Target node
};
struct AnimationClip {
std::string name;
float duration;
std::vector<AnimationChannel> channels;
};
```
## Known Issues and Workarounds
1. **Cubic Spline Issues**: GLTFLoader has had problems with cubic spline interpolation. Consider using LINEAR interpolation or implementing custom handling.
2. **Rotation > 360°**: When exporting from Blender, rotations over 360° may not export correctly to glTF. Use quaternions to avoid this issue.
3. **Gimbal Lock**: Using Euler angles can cause gimbal lock. Always prefer quaternions for rotations.
4. **Performance**: Large numbers of keyframes can impact performance. Consider optimizing by reducing keyframe count where possible.
## References
- [Three.js Animation System Documentation](https://threejs.org/docs/#manual/en/introduction/Animation-system)
- [Three.js Euler Documentation](https://threejs.org/docs/#api/en/math/Euler)
- [Three.js Quaternion Documentation](https://threejs.org/docs/#api/en/math/Quaternion)
- [glTF 2.0 Animation Specification](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations)
- [GLTFLoader Source Code](https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/GLTFLoader.js)