draco_oxide/io/gltf/
geometry_extractor.rs

1//! Geometry extraction from glTF accessors.
2
3use serde_json::Value;
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7    #[error("Accessor {0} not found")]
8    AccessorNotFound(u64),
9    #[error("BufferView {0} not found")]
10    BufferViewNotFound(u64),
11    #[error("Accessor {0} has no bufferView (may be Draco-compressed)")]
12    NoBufferView(u64),
13    #[error("Buffer index {0} out of range")]
14    BufferOutOfRange(u64),
15    #[error("Buffer read out of bounds: offset {offset}, size {size}, buffer len {buffer_len}")]
16    OutOfBounds {
17        offset: usize,
18        size: usize,
19        buffer_len: usize,
20    },
21    #[error("Unsupported component type: {0}")]
22    UnsupportedComponentType(u32),
23    #[error("Unsupported accessor type: {0}")]
24    UnsupportedAccessorType(String),
25    #[error("Missing required field: {0}")]
26    MissingField(String),
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum ComponentType {
31    Byte = 5120,
32    UnsignedByte = 5121,
33    Short = 5122,
34    UnsignedShort = 5123,
35    UnsignedInt = 5125,
36    Float = 5126,
37}
38
39impl ComponentType {
40    pub fn from_u32(value: u32) -> Result<Self, Error> {
41        match value {
42            5120 => Ok(Self::Byte),
43            5121 => Ok(Self::UnsignedByte),
44            5122 => Ok(Self::Short),
45            5123 => Ok(Self::UnsignedShort),
46            5125 => Ok(Self::UnsignedInt),
47            5126 => Ok(Self::Float),
48            _ => Err(Error::UnsupportedComponentType(value)),
49        }
50    }
51
52    pub fn byte_size(self) -> usize {
53        match self {
54            Self::Byte | Self::UnsignedByte => 1,
55            Self::Short | Self::UnsignedShort => 2,
56            Self::UnsignedInt | Self::Float => 4,
57        }
58    }
59}
60
61#[derive(Debug, Clone)]
62pub struct AccessorInfo {
63    pub buffer_view_idx: u64,
64    pub byte_offset: usize,
65    pub component_type: ComponentType,
66    pub count: usize,
67    pub accessor_type: String,
68}
69
70#[derive(Debug, Clone)]
71pub struct BufferViewInfo {
72    pub buffer_idx: u64,
73    pub byte_offset: usize,
74    pub byte_length: usize,
75    pub byte_stride: Option<usize>,
76}
77
78pub fn get_accessor_info(json: &Value, accessor_idx: u64) -> Result<AccessorInfo, Error> {
79    let accessor = json
80        .get("accessors")
81        .and_then(|a| a.get(accessor_idx as usize))
82        .ok_or(Error::AccessorNotFound(accessor_idx))?;
83
84    let buffer_view_idx = accessor
85        .get("bufferView")
86        .and_then(|v| v.as_u64())
87        .ok_or(Error::NoBufferView(accessor_idx))?;
88
89    let byte_offset = accessor
90        .get("byteOffset")
91        .and_then(|v| v.as_u64())
92        .unwrap_or(0) as usize;
93    let component_type_raw = accessor
94        .get("componentType")
95        .and_then(|v| v.as_u64())
96        .ok_or_else(|| Error::MissingField("accessor.componentType".into()))?
97        as u32;
98    let component_type = ComponentType::from_u32(component_type_raw)?;
99    let count = accessor
100        .get("count")
101        .and_then(|v| v.as_u64())
102        .ok_or_else(|| Error::MissingField("accessor.count".into()))? as usize;
103    let accessor_type = accessor
104        .get("type")
105        .and_then(|v| v.as_str())
106        .ok_or_else(|| Error::MissingField("accessor.type".into()))?
107        .to_string();
108
109    Ok(AccessorInfo {
110        buffer_view_idx,
111        byte_offset,
112        component_type,
113        count,
114        accessor_type,
115    })
116}
117
118pub fn get_buffer_view_info(json: &Value, buffer_view_idx: u64) -> Result<BufferViewInfo, Error> {
119    let bv = json
120        .get("bufferViews")
121        .and_then(|b| b.get(buffer_view_idx as usize))
122        .ok_or(Error::BufferViewNotFound(buffer_view_idx))?;
123
124    let buffer_idx = bv
125        .get("buffer")
126        .and_then(|v| v.as_u64())
127        .ok_or_else(|| Error::MissingField("bufferView.buffer".into()))?;
128    let byte_offset = bv.get("byteOffset").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
129    let byte_length =
130        bv.get("byteLength")
131            .and_then(|v| v.as_u64())
132            .ok_or_else(|| Error::MissingField("bufferView.byteLength".into()))? as usize;
133    let byte_stride = bv
134        .get("byteStride")
135        .and_then(|v| v.as_u64())
136        .map(|v| v as usize);
137
138    Ok(BufferViewInfo {
139        buffer_idx,
140        byte_offset,
141        byte_length,
142        byte_stride,
143    })
144}
145
146fn component_count(accessor_type: &str) -> Result<usize, Error> {
147    match accessor_type {
148        "SCALAR" => Ok(1),
149        "VEC2" => Ok(2),
150        "VEC3" => Ok(3),
151        "VEC4" => Ok(4),
152        "MAT2" => Ok(4),
153        "MAT3" => Ok(9),
154        "MAT4" => Ok(16),
155        _ => Err(Error::UnsupportedAccessorType(accessor_type.to_string())),
156    }
157}
158
159pub fn read_accessor_as_f32(
160    json: &Value,
161    buffer: &[u8],
162    accessor_idx: u64,
163) -> Result<Vec<f32>, Error> {
164    let accessor = get_accessor_info(json, accessor_idx)?;
165    let buffer_view = get_buffer_view_info(json, accessor.buffer_view_idx)?;
166
167    if buffer_view.buffer_idx != 0 {
168        return Err(Error::BufferOutOfRange(buffer_view.buffer_idx));
169    }
170
171    let num_components = component_count(&accessor.accessor_type)?;
172    let element_size = accessor.component_type.byte_size() * num_components;
173    let stride = buffer_view.byte_stride.unwrap_or(element_size);
174    let base_offset = buffer_view.byte_offset + accessor.byte_offset;
175
176    let mut result = Vec::with_capacity(accessor.count * num_components);
177
178    for i in 0..accessor.count {
179        let element_offset = base_offset + i * stride;
180        for c in 0..num_components {
181            let offset = element_offset + c * accessor.component_type.byte_size();
182            let value = read_component_as_f32(buffer, offset, accessor.component_type)?;
183            result.push(value);
184        }
185    }
186
187    Ok(result)
188}
189
190pub fn read_accessor_as_u32(
191    json: &Value,
192    buffer: &[u8],
193    accessor_idx: u64,
194) -> Result<Vec<u32>, Error> {
195    let accessor = get_accessor_info(json, accessor_idx)?;
196    let buffer_view = get_buffer_view_info(json, accessor.buffer_view_idx)?;
197
198    if buffer_view.buffer_idx != 0 {
199        return Err(Error::BufferOutOfRange(buffer_view.buffer_idx));
200    }
201
202    let element_size = accessor.component_type.byte_size();
203    let stride = buffer_view.byte_stride.unwrap_or(element_size);
204    let base_offset = buffer_view.byte_offset + accessor.byte_offset;
205
206    let mut result = Vec::with_capacity(accessor.count);
207
208    for i in 0..accessor.count {
209        let offset = base_offset + i * stride;
210        let value = read_component_as_u32(buffer, offset, accessor.component_type)?;
211        result.push(value);
212    }
213
214    Ok(result)
215}
216
217fn read_component_as_f32(buffer: &[u8], offset: usize, ct: ComponentType) -> Result<f32, Error> {
218    let size = ct.byte_size();
219    if offset + size > buffer.len() {
220        return Err(Error::OutOfBounds {
221            offset,
222            size,
223            buffer_len: buffer.len(),
224        });
225    }
226
227    Ok(match ct {
228        ComponentType::Byte => buffer[offset] as i8 as f32,
229        ComponentType::UnsignedByte => buffer[offset] as f32,
230        ComponentType::Short => i16::from_le_bytes([buffer[offset], buffer[offset + 1]]) as f32,
231        ComponentType::UnsignedShort => {
232            u16::from_le_bytes([buffer[offset], buffer[offset + 1]]) as f32
233        }
234        ComponentType::UnsignedInt => u32::from_le_bytes([
235            buffer[offset],
236            buffer[offset + 1],
237            buffer[offset + 2],
238            buffer[offset + 3],
239        ]) as f32,
240        ComponentType::Float => f32::from_le_bytes([
241            buffer[offset],
242            buffer[offset + 1],
243            buffer[offset + 2],
244            buffer[offset + 3],
245        ]),
246    })
247}
248
249fn read_component_as_u32(buffer: &[u8], offset: usize, ct: ComponentType) -> Result<u32, Error> {
250    let size = ct.byte_size();
251    if offset + size > buffer.len() {
252        return Err(Error::OutOfBounds {
253            offset,
254            size,
255            buffer_len: buffer.len(),
256        });
257    }
258
259    Ok(match ct {
260        ComponentType::Byte => buffer[offset] as i8 as u32,
261        ComponentType::UnsignedByte => buffer[offset] as u32,
262        ComponentType::Short => i16::from_le_bytes([buffer[offset], buffer[offset + 1]]) as u32,
263        ComponentType::UnsignedShort => {
264            u16::from_le_bytes([buffer[offset], buffer[offset + 1]]) as u32
265        }
266        ComponentType::UnsignedInt => u32::from_le_bytes([
267            buffer[offset],
268            buffer[offset + 1],
269            buffer[offset + 2],
270            buffer[offset + 3],
271        ]),
272        ComponentType::Float => f32::from_le_bytes([
273            buffer[offset],
274            buffer[offset + 1],
275            buffer[offset + 2],
276            buffer[offset + 3],
277        ]) as u32,
278    })
279}
280
281pub fn read_accessor_as_vec3(
282    json: &Value,
283    buffer: &[u8],
284    accessor_idx: u64,
285) -> Result<Vec<[f32; 3]>, Error> {
286    let flat = read_accessor_as_f32(json, buffer, accessor_idx)?;
287    Ok(flat.chunks(3).map(|c| [c[0], c[1], c[2]]).collect())
288}
289
290pub fn read_accessor_as_vec2(
291    json: &Value,
292    buffer: &[u8],
293    accessor_idx: u64,
294) -> Result<Vec<[f32; 2]>, Error> {
295    let flat = read_accessor_as_f32(json, buffer, accessor_idx)?;
296    Ok(flat.chunks(2).map(|c| [c[0], c[1]]).collect())
297}
298
299pub fn read_accessor_as_vec4(
300    json: &Value,
301    buffer: &[u8],
302    accessor_idx: u64,
303) -> Result<Vec<[f32; 4]>, Error> {
304    let flat = read_accessor_as_f32(json, buffer, accessor_idx)?;
305    Ok(flat.chunks(4).map(|c| [c[0], c[1], c[2], c[3]]).collect())
306}
307
308pub fn read_accessor_as_scalar_f32(
309    json: &Value,
310    buffer: &[u8],
311    accessor_idx: u64,
312) -> Result<Vec<f32>, Error> {
313    let accessor = get_accessor_info(json, accessor_idx)?;
314    let buffer_view = get_buffer_view_info(json, accessor.buffer_view_idx)?;
315
316    if buffer_view.buffer_idx != 0 {
317        return Err(Error::BufferOutOfRange(buffer_view.buffer_idx));
318    }
319
320    let element_size = accessor.component_type.byte_size();
321    let stride = buffer_view.byte_stride.unwrap_or(element_size);
322    let base_offset = buffer_view.byte_offset + accessor.byte_offset;
323
324    let mut result = Vec::with_capacity(accessor.count);
325
326    for i in 0..accessor.count {
327        let offset = base_offset + i * stride;
328        let value = read_component_as_f32(buffer, offset, accessor.component_type)?;
329        result.push(value);
330    }
331
332    Ok(result)
333}
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338    use serde_json::json;
339
340    #[test]
341    fn test_read_vec3() {
342        let json = json!({
343            "accessors": [{ "bufferView": 0, "byteOffset": 0, "componentType": 5126, "count": 3, "type": "VEC3" }],
344            "bufferViews": [{ "buffer": 0, "byteOffset": 0, "byteLength": 36 }]
345        });
346
347        let mut buffer = Vec::new();
348        for v in [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] {
349            buffer.extend_from_slice(&v.to_le_bytes());
350        }
351
352        let positions = read_accessor_as_vec3(&json, &buffer, 0).unwrap();
353        assert_eq!(positions.len(), 3);
354        assert_eq!(positions[0], [1.0, 2.0, 3.0]);
355        assert_eq!(positions[1], [4.0, 5.0, 6.0]);
356    }
357}