- 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>
5.6 KiB
Animation System Redesign for Three.js Compatibility
Overview
The Tydra RenderScene animation system has been redesigned to be compatible with Three.js/glTF animation architecture. The new design separates animation data from the node hierarchy, making it easier to export to and work with Three.js/WebGL applications.
Key Changes
1. New Animation Structures
AnimationInterpolation (enum)
Matches glTF interpolation modes:
Linear- Linear interpolation (slerp for quaternions)Step- Discrete/stepped interpolationCubicSpline- Cubic spline with tangents
AnimationPath (enum)
Defines which property is animated (matches glTF animation paths):
Translation- Position (vec3) → Three.js.positionRotation- Rotation (quaternion) → Three.js.quaternionScale- Scale (vec3) → Three.js.scaleWeights- Morph target weights (float array) → Three.js.morphTargetInfluences
KeyframeSampler (struct)
Stores keyframe data in flat arrays (matches glTF sampler and Three.js KeyframeTrack):
struct KeyframeSampler {
std::vector<float> times; // Keyframe times in seconds
std::vector<float> values; // Flat array of values
AnimationInterpolation interpolation;
};
Value format:
- Translation/Scale:
[x0,y0,z0, x1,y1,z1, ...](3 floats/keyframe) - Rotation:
[x0,y0,z0,w0, x1,y1,z1,w1, ...](4 floats/keyframe) - Weights:
[w0, w1, w2, ...](1 float/keyframe/target)
AnimationChannel (struct)
Binds sampler to a node property (matches glTF animation channel):
struct AnimationChannel {
AnimationPath path; // Which property to animate
int32_t target_node; // Index into RenderScene::nodes
int32_t sampler; // Index into AnimationClip::samplers
};
AnimationClip (struct)
Collection of channels and samplers (matches glTF Animation and Three.js AnimationClip):
struct AnimationClip {
std::string name;
std::string prim_name;
std::string abs_path;
std::string display_name;
float duration;
std::vector<KeyframeSampler> samplers;
std::vector<AnimationChannel> channels;
};
2. Removed from Node
The node_animations field has been removed from the Node struct. Animation data is now managed independently at the RenderScene level.
Before:
struct Node {
// ...
std::vector<AnimationChannel> node_animations; // OLD: Embedded in node
};
After:
struct Node {
// ...
// Animation data moved to RenderScene::animations
// Animations reference nodes by index (AnimationChannel::target_node)
};
3. RenderScene Integration
RenderScene::animations now uses AnimationClip:
class RenderScene {
// ...
std::vector<AnimationClip> animations; // NEW: AnimationClip instead of Animation
};
Migration Path
Old System (USD-centric)
// Animation embedded in each node
node.node_animations[i].type = AnimationChannel::ChannelType::Translation;
node.node_animations[i].translations.samples[j].t = time;
node.node_animations[i].translations.samples[j].value = position;
New System (glTF/Three.js compatible)
// Animation as independent clips
AnimationClip clip;
clip.name = "Walk";
// Create sampler
KeyframeSampler sampler;
sampler.times = {0.0f, 1.0f, 2.0f};
sampler.values = {0,0,0, 1,0,0, 0,0,0}; // Flat array
sampler.interpolation = AnimationInterpolation::Linear;
clip.samplers.push_back(sampler);
// Create channel linking sampler to node property
AnimationChannel channel;
channel.path = AnimationPath::Translation;
channel.target_node = 5; // Index into RenderScene::nodes
channel.sampler = 0; // Index into clip.samplers
clip.channels.push_back(channel);
// Add to scene
scene.animations.push_back(clip);
Benefits
- Three.js Compatibility: Direct mapping to Three.js AnimationClip/KeyframeTrack
- glTF Compatible: Matches glTF 2.0 animation structure
- Separation of Concerns: Animations independent from scene hierarchy
- Memory Efficient: Flat array storage reduces overhead
- Flexible: Multiple channels can target the same node with different samplers
Three.js Export Example
// In Three.js/JavaScript
const times = new Float32Array(samplers[0].times);
const values = new Float32Array(samplers[0].values);
// Create KeyframeTrack based on path
let track;
switch(channel.path) {
case 'Translation':
track = new THREE.VectorKeyframeTrack(
'.position', times, values
);
break;
case 'Rotation':
track = new THREE.QuaternionKeyframeTrack(
'.quaternion', times, values
);
break;
case 'Scale':
track = new THREE.VectorKeyframeTrack(
'.scale', times, values
);
break;
}
// Create AnimationClip
const clip = new THREE.AnimationClip(name, duration, [track]);
Important Notes
- Rotations use Quaternions: Not Euler angles (matches Three.js requirement)
- Times in Seconds: All animation times are in seconds (float)
- Flat Arrays: Values stored as flat float arrays for efficiency
- Node Indexing: Channels reference nodes by index, not path
- Backward Compatibility: Old
AnimationSampler<T>template still exists for legacy USD data
Related Documentation
- See
doc/THREEJS_ANIMATION.mdfor Three.js animation system details - See glTF 2.0 specification for animation format details
Implementation Status
- ✅ Header structures defined (
src/tydra/render-data.hh) - ✅
node_animationsremoved fromNode - ✅
RenderScene::animationsupdated to useAnimationClip - ✅ Function signatures updated
- ✅ Compilation verified
- ⏳ Implementation of converter functions (TODO)
- ⏳ Three.js exporter (TODO)