Skip to main content

n3_lib/
geometry.rs

1//! Derived render geometry, separate from unchanged wire records.
2use crate::{
3    binary::{Error, Result},
4    records::Record,
5};
6use serde_json::Value;
7
8/// Renderer-independent interleaved geometry.
9#[derive(Debug, Clone, PartialEq)]
10pub struct MeshGeometry {
11    /// Position(3), normal(3), UV(2), in model-space units.
12    pub vertices: Vec<f32>,
13    /// Triangle indices expanded from progressive storage where applicable.
14    pub indices: Vec<u32>,
15}
16fn error() -> Error {
17    Error {
18        offset: 0,
19        message: "Invalid derived mesh geometry".into(),
20    }
21}
22fn numbers(record: &Record, name: &str) -> Result<Vec<u32>> {
23    record
24        .get(name)
25        .and_then(Value::as_array)
26        .ok_or_else(error)?
27        .iter()
28        .map(|value| {
29            value
30                .as_u64()
31                .and_then(|number| u32::try_from(number).ok())
32                .ok_or_else(error)
33        })
34        .collect()
35}
36/// Expand progressive mesh indices without modifying the source record.
37pub fn mesh_geometry(record: &Record) -> Result<MeshGeometry> {
38    let vertices: Vec<f32> = numbers(record, "vertices")?
39        .into_iter()
40        .map(f32::from_bits)
41        .collect();
42    let mut indices = numbers(record, "indices")?;
43    let changes = record
44        .get("indexChanges")
45        .and_then(Value::as_array)
46        .ok_or_else(error)?;
47    let mut cursor = 0_i64;
48    for collapse in record
49        .get("collapses")
50        .and_then(Value::as_array)
51        .ok_or_else(error)?
52    {
53        cursor = cursor
54            .checked_add(collapse["verticesToLose"].as_i64().ok_or_else(error)?)
55            .ok_or_else(error)?;
56        let replacement = cursor - 1;
57        if replacement < 0 || replacement as usize >= vertices.len() / 8 {
58            continue;
59        }
60        let start = collapse["indexChangesOffset"]
61            .as_i64()
62            .ok_or_else(error)?
63            .max(0) as usize;
64        let count = collapse["indicesToChange"]
65            .as_i64()
66            .ok_or_else(error)?
67            .max(0) as usize;
68        for value in changes.iter().skip(start).take(count) {
69            if let Some(slot) = value
70                .as_u64()
71                .and_then(|slot| indices.get_mut(slot as usize))
72            {
73                *slot = replacement as u32;
74            }
75        }
76    }
77    if !vertices.len().is_multiple_of(8)
78        || indices
79            .iter()
80            .any(|&index| index as usize >= vertices.len() / 8)
81    {
82        return Err(error());
83    }
84    Ok(MeshGeometry { vertices, indices })
85}
86/// Expand skin position/UV indices into interleaved triangle geometry.
87pub fn skin_geometry(record: &Record) -> Result<MeshGeometry> {
88    let faces = numbers(record, "vertexIndices")?;
89    let uv_indices = numbers(record, "uvIndices")?;
90    let points = numbers(record, "positionNormals")?;
91    let uvs = numbers(record, "uvs")?;
92    let mut vertices = Vec::with_capacity(faces.len() * 8);
93    for (index, &face) in faces.iter().enumerate() {
94        let at = face as usize * 6;
95        let uv = *uv_indices.get(index).ok_or_else(error)? as usize * 2;
96        for &value in points.get(at..at + 6).ok_or_else(error)? {
97            vertices.push(f32::from_bits(value));
98        }
99        for &value in uvs.get(uv..uv + 2).ok_or_else(error)? {
100            vertices.push(f32::from_bits(value));
101        }
102    }
103    Ok(MeshGeometry {
104        vertices,
105        indices: (0..faces.len() as u32).collect(),
106    })
107}