draco_core/keyframe_animation.rs
1//! Keyframe animation container built on top of [`PointCloud`].
2//!
3//! Mirrors C++ Draco's `draco/animation/keyframe_animation.h`. A
4//! [`KeyframeAnimation`] is a point cloud whose first attribute (unique id `0`)
5//! always stores per-frame `f32` timestamps and whose remaining attributes each
6//! store one keyframe track. Because Draco implements keyframe animation as a
7//! point-cloud-like sequential stream, encode/decode reuse the existing
8//! sequential point-cloud path (see [`crate::keyframe_animation_encoder`] and
9//! [`crate::keyframe_animation_decoder`]).
10
11use crate::draco_types::DataType;
12use crate::geometry_attribute::{GeometryAttributeType, PointAttribute};
13use crate::point_cloud::PointCloud;
14
15/// Attribute unique id reserved for the timestamp track.
16const TIMESTAMP_ID: u32 = 0;
17
18/// Holds keyframe animation data as a [`PointCloud`].
19///
20/// The first attribute is always the timestamp track. Each additional attribute
21/// is one keyframe track with the same number of frames.
22#[derive(Debug, Default, Clone)]
23pub struct KeyframeAnimation {
24 point_cloud: PointCloud,
25}
26
27impl KeyframeAnimation {
28 /// Creates an empty keyframe animation.
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 /// Wraps an existing point cloud (e.g. a freshly decoded one).
34 pub fn from_point_cloud(point_cloud: PointCloud) -> Self {
35 Self { point_cloud }
36 }
37
38 /// Returns the number of animation frames (equal to the point count).
39 pub fn num_frames(&self) -> i32 {
40 self.point_cloud.num_points() as i32
41 }
42
43 /// Sets the number of animation frames (equal to the point count).
44 pub fn set_num_frames(&mut self, num_frames: i32) {
45 self.point_cloud.set_num_points(num_frames.max(0) as usize);
46 }
47
48 /// Returns the number of keyframe tracks (attributes minus the timestamp).
49 pub fn num_animations(&self) -> i32 {
50 self.point_cloud.num_attributes() - 1
51 }
52
53 /// Sets the per-frame timestamps. Must be called only once, before any
54 /// keyframe data is added unless a track was added first.
55 ///
56 /// Returns `false` if a timestamp track already holds data or if the frame
57 /// count is inconsistent with previously added keyframes. Mirrors C++
58 /// `KeyframeAnimation::SetTimestamps`.
59 pub fn set_timestamps(&mut self, timestamps: &[f32]) -> bool {
60 let num_frames = timestamps.len() as i32;
61 if self.point_cloud.num_attributes() > 0 {
62 // Timestamp attribute may be set only once.
63 match self.timestamps() {
64 Some(ts) if ts.size() > 0 => return false,
65 _ => {}
66 }
67 // Frame count must match keyframes added earlier.
68 if num_frames != self.num_frames() {
69 return false;
70 }
71 } else {
72 // This is the first attribute.
73 self.set_num_frames(num_frames);
74 }
75
76 let mut timestamp_att = PointAttribute::new();
77 timestamp_att.init(
78 GeometryAttributeType::Generic,
79 1,
80 DataType::Float32,
81 false,
82 num_frames as usize,
83 );
84 let bytes: &[u8] = bytemuck::cast_slice(timestamps);
85 timestamp_att.buffer_mut().write(0, bytes);
86 self.point_cloud
87 .set_attribute(TIMESTAMP_ID as i32, timestamp_att);
88 true
89 }
90
91 /// Adds one keyframe track and returns its animation id, or `-1` on error.
92 ///
93 /// `num_components` is the number of scalar components per frame, and `data`
94 /// holds `num_components * num_frames` values laid out frame-major. Mirrors
95 /// C++ `KeyframeAnimation::AddKeyframes`.
96 pub fn add_keyframes<T: bytemuck::NoUninit>(
97 &mut self,
98 data_type: DataType,
99 num_components: u32,
100 data: &[T],
101 ) -> i32 {
102 if num_components == 0 {
103 return -1;
104 }
105 // Reserve attribute id 0 for timestamps if nothing has been added yet.
106 if self.point_cloud.num_attributes() == 0 {
107 let mut temp_att = PointAttribute::new();
108 temp_att.init(
109 GeometryAttributeType::Generic,
110 num_components as u8,
111 data_type,
112 false,
113 0,
114 );
115 self.point_cloud.add_attribute(temp_att);
116 self.set_num_frames(data.len() as i32 / num_components as i32);
117 }
118
119 if data.len() != num_components as usize * self.num_frames() as usize {
120 return -1;
121 }
122
123 let mut keyframe_att = PointAttribute::new();
124 keyframe_att.init(
125 GeometryAttributeType::Generic,
126 num_components as u8,
127 data_type,
128 false,
129 self.num_frames() as usize,
130 );
131 let bytes: &[u8] = bytemuck::cast_slice(data);
132 keyframe_att.buffer_mut().write(0, bytes);
133 self.point_cloud.add_attribute(keyframe_att)
134 }
135
136 /// Returns the timestamp track, if present.
137 pub fn timestamps(&self) -> Option<&PointAttribute> {
138 self.point_cloud.attribute_by_unique_id(TIMESTAMP_ID)
139 }
140
141 /// Returns the keyframe track identified by `animation_id`, if present.
142 pub fn keyframes(&self, animation_id: i32) -> Option<&PointAttribute> {
143 if animation_id < 0 {
144 return None;
145 }
146 self.point_cloud.attribute_by_unique_id(animation_id as u32)
147 }
148
149 /// Returns the underlying point cloud.
150 pub fn point_cloud(&self) -> &PointCloud {
151 &self.point_cloud
152 }
153
154 /// Consumes the animation, returning the underlying point cloud.
155 pub fn into_point_cloud(self) -> PointCloud {
156 self.point_cloud
157 }
158}