quantized_mesh_decoder/
lib.rs

1//https://github.com/CesiumGS/quantized-mesh
2//https://github.com/heremaps/quantized-mesh-decoder
3//https://blog.csdn.net/qgbihc/article/details/109207516
4//https://www.cnblogs.com/oloroso/p/11080222.html#24%E6%89%A9%E5%B1%95%E6%95%B0%E6%8D%AE
5#[derive(Debug)]
6pub struct QuantizedMeshHeader {
7    pub center_x: f64,
8    pub center_y: f64,
9    pub center_z: f64,
10    pub minimum_height: f32,
11    pub maximum_height: f32,
12    pub bounding_sphere_center_x: f64,
13    pub bounding_sphere_center_y: f64,
14    pub bounding_sphere_center_z: f64,
15    pub bounding_sphere_radius: f64,
16    pub horizon_occlusion_point_x: f64,
17    pub horizon_occlusion_point_y: f64,
18    pub horizon_occlusion_point_z: f64,
19}
20#[derive(Debug)]
21pub enum Indices {
22    IndexData16(Vec<u16>),
23    IndexData32(Vec<u32>),
24}
25impl Indices {
26    pub fn len(&self) -> usize {
27        match self {
28            Indices::IndexData16(v) => return v.len(),
29            Indices::IndexData32(v) => return v.len(),
30        }
31    }
32}
33#[derive(Default, Debug)]
34pub struct Extension {
35    pub vertex_normals: Option<Vec<u8>>,
36    pub water_mask: Option<Vec<u8>>,
37    pub metadata: Option<String>,
38}
39#[derive(Debug)]
40pub struct QuantizedMeshTerrainData {
41    pub header: QuantizedMeshHeader,
42    pub vertex_data: Vec<u16>,
43    pub triangle_indices: Indices,
44    pub west_indices: Indices,
45    pub north_indices: Indices,
46    pub east_indices: Indices,
47    pub south_indices: Indices,
48    pub extension: Extension,
49}
50fn zigzag_decode(value: u16) -> i16 {
51    (value >> 1) as i16 ^ -((value & 1) as i16)
52}
53use byteorder::{LittleEndian, ReadBytesExt};
54use std::io::Read;
55use std::vec;
56
57pub const SIXTY_FOUR_KILOBYTES: u32 = 65536;
58pub fn from_reader(mut rdr: impl Read) -> std::io::Result<QuantizedMeshTerrainData> {
59    //decode header
60    let center_x = rdr.read_f64::<LittleEndian>()?;
61    let center_y = rdr.read_f64::<LittleEndian>()?;
62    let center_z = rdr.read_f64::<LittleEndian>()?;
63    let minimum_height = rdr.read_f32::<LittleEndian>()?;
64    let maximum_height = rdr.read_f32::<LittleEndian>()?;
65    let bounding_sphere_center_x = rdr.read_f64::<LittleEndian>()?;
66    let bounding_sphere_center_y = rdr.read_f64::<LittleEndian>()?;
67    let bounding_sphere_center_z = rdr.read_f64::<LittleEndian>()?;
68    let bounding_sphere_radius = rdr.read_f64::<LittleEndian>()?;
69    let horizon_occlusion_point_x = rdr.read_f64::<LittleEndian>()?;
70    let horizon_occlusion_point_y = rdr.read_f64::<LittleEndian>()?;
71    let horizon_occlusion_point_z = rdr.read_f64::<LittleEndian>()?;
72    let header = QuantizedMeshHeader {
73        center_x,
74        center_y,
75        center_z,
76        maximum_height,
77        minimum_height,
78        bounding_sphere_center_x,
79        bounding_sphere_center_y,
80        bounding_sphere_center_z,
81        bounding_sphere_radius,
82        horizon_occlusion_point_x,
83        horizon_occlusion_point_y,
84        horizon_occlusion_point_z,
85    };
86    //decode vertex data
87    let vertex_count = rdr.read_u32::<LittleEndian>()? as usize;
88    let mut encoded_vertex_buffer = vec![0u16; vertex_count * 3];
89    rdr.read_u16_into::<LittleEndian>(&mut encoded_vertex_buffer)?;
90    let mut vertex_data = vec![0u16; vertex_count * 3];
91    let mut u = 0;
92    let mut v = 0;
93    let mut height = 0;
94    for i in 0..vertex_count {
95        u += zigzag_decode(encoded_vertex_buffer[i]);
96        v += zigzag_decode(encoded_vertex_buffer[i + vertex_count]);
97        height += zigzag_decode(encoded_vertex_buffer[i + vertex_count * 2]);
98        vertex_data[i] = u as u16;
99        vertex_data[i + vertex_count] = v as u16;
100        vertex_data[i + vertex_count * 2] = height as u16;
101    }
102    //TODO skip over any additional padding that was added for 2/4 byte alignment
103    // if (position % bytesPerIndex !== 0) {
104    //   position += bytesPerIndex - (position % bytesPerIndex)
105    // }
106    // decode triangle indices
107    let triangle_count = rdr.read_u32::<LittleEndian>()?;
108    let mut triangle_indices = create_typed_array_from_array_buffer(
109        &mut rdr,
110        vertex_count as u32,
111        triangle_count as u32,
112        (triangle_count * 3) as usize,
113    )?;
114    match triangle_indices {
115        Indices::IndexData16(ref mut indices) => {
116            // High water mark decoding based on decompressIndices_ in webgl-loader's loader.js.
117            // https://code.google.com/p/webgl-loader/source/browse/trunk/samples/loader.js?r=99#55
118            // Copyright 2012 Google Inc., Apache 2.0 license.
119            let mut highest = 0;
120            for i in 0..(triangle_count * 3) as usize {
121                let code = indices[i];
122                indices[i] = highest - code;
123                if code == 0 {
124                    highest += 1;
125                }
126            }
127        }
128        Indices::IndexData32(ref mut indices) => {
129            // High water mark decoding based on decompressIndices_ in webgl-loader's loader.js.
130            // https://code.google.com/p/webgl-loader/source/browse/trunk/samples/loader.js?r=99#55
131            // Copyright 2012 Google Inc., Apache 2.0 license.
132            let mut highest = 0;
133            for i in 0..(triangle_count * 3) as usize {
134                let code = indices[i];
135                indices[i] = highest - code;
136                if code == 0 {
137                    highest += 1;
138                }
139            }
140        }
141    }
142    let west_vertex_count = rdr.read_u32::<LittleEndian>()?;
143    let west_indices = create_typed_array_from_array_buffer(
144        &mut rdr,
145        vertex_count as u32,
146        west_vertex_count,
147        west_vertex_count as usize,
148    )?;
149    let south_vertex_count = rdr.read_u32::<LittleEndian>()?;
150    let south_indices = create_typed_array_from_array_buffer(
151        &mut rdr,
152        vertex_count as u32,
153        south_vertex_count,
154        south_vertex_count as usize,
155    )?;
156    let east_vertex_count = rdr.read_u32::<LittleEndian>()?;
157    let east_indices = create_typed_array_from_array_buffer(
158        &mut rdr,
159        vertex_count as u32,
160        east_vertex_count,
161        east_vertex_count as usize,
162    )?;
163
164    let north_vertex_count = rdr.read_u32::<LittleEndian>()?;
165    let north_indices = create_typed_array_from_array_buffer(
166        &mut rdr,
167        vertex_count as u32,
168        north_vertex_count,
169        north_vertex_count as usize,
170    )?;
171    let mut extension: Extension = Extension::default();
172    while let Ok(extension_id) = rdr.read_u8() {
173        let extension_length = rdr.read_u32::<LittleEndian>()?;
174        match extension_id {
175            //vertex normals
176            1 => {
177                let mut indices = vec![0u8; extension_length as usize];
178                rdr.read_exact(&mut indices)?;
179                extension.vertex_normals = Some(indices);
180            }
181            //water mask
182            2 => {
183                let mut indices = vec![0u8; extension_length as usize];
184                rdr.read_exact(&mut indices)?;
185                extension.water_mask = Some(indices);
186            }
187            //metadata
188            4 => {
189                let json_length = rdr.read_u32::<LittleEndian>()?;
190                let mut json_buffer = vec![0u8; json_length as usize];
191                rdr.read_exact(&mut json_buffer)?;
192                if let Ok(json_str) = std::str::from_utf8(&json_buffer) {
193                    let json_string: String = json_str.into();
194                    extension.metadata = Some(json_string);
195                };
196            }
197            _ => {
198                panic!("error");
199            }
200        }
201    }
202    Ok(QuantizedMeshTerrainData {
203        header,
204        vertex_data,
205        triangle_indices,
206        west_indices,
207        south_indices,
208        east_indices,
209        north_indices,
210        extension,
211    })
212}
213fn create_typed_array_from_array_buffer(
214    rdr: &mut impl Read,
215    vertex_count: u32,
216    _count: u32,
217    length: usize,
218) -> std::io::Result<Indices> {
219    if vertex_count < SIXTY_FOUR_KILOBYTES {
220        let mut indices = vec![0u16; length];
221        rdr.read_u16_into::<LittleEndian>(&mut indices)?;
222        return Ok(Indices::IndexData16(indices));
223    } else {
224        let mut indices = vec![0u32; length];
225        rdr.read_u32_into::<LittleEndian>(&mut indices)?;
226        return Ok(Indices::IndexData32(indices));
227    }
228}
229#[cfg(test)]
230mod tests {
231    use std::{path::Path, fs::File};
232    use super::*;
233    pub fn from_file(path: &'static str) -> QuantizedMeshTerrainData {
234        let path = Path::new(path);
235        let file = match File::open(&path) {
236            Err(why) => panic!("couldn't open {:?}", why),
237            Ok(file) => file,
238        };
239        match from_reader(file) {
240            Ok(terrain) => {
241                return terrain;
242            }
243            Err(error) => {
244                panic!("error {:?}", error);
245            }
246        }
247    }
248    const VERTEX_DATA_VERTEX_COUNT: u32 = 4;
249    const INDEX_DATA_TRIANGLE_COUNT: u32 = 2;
250
251    const TRIANGLE_ONE: [[u16; 3]; 3] = [[8380, 26387, 0], [9841, 24918, 32767], [9841, 26387, 0]];
252    const TRIANGLE_TWO: [[u16; 3]; 3] = [[9841, 24918, 32767], [8380, 26387, 0], [8380, 24918, 0]];
253    const GROUND_TRUTH_TRIANGLES: [[[u16; 3]; 3]; 2] = [TRIANGLE_ONE, TRIANGLE_TWO];
254    fn create_triangle(indices: &Vec<u32>, vertex_data: &Vec<u16>) -> Vec<[u16; 3]> {
255        let vertex_count = (vertex_data.len() / 3) as u32;
256        let mut triangle_list = vec![];
257        for i in indices.iter() {
258            let triangle = [
259                vertex_data[(i + 0) as usize],
260                vertex_data[(i + vertex_count) as usize],
261                vertex_data[(i + vertex_count * 2) as usize],
262            ];
263            triangle_list.push(triangle);
264        }
265        return triangle_list;
266    }
267    fn compare_triangles(t1: &Vec<[u16; 3]>, t2: &[[u16; 3]; 3]) {
268        for i in 0..3 {
269            for j in 0..3 {
270                assert!(t1[i][j] == t2[i][j]);
271            }
272        }
273    }
274
275    #[test]
276    fn test_opentin() {
277        let terrain_data = from_file("assets/tile-opentin.terrain");
278        assert!(terrain_data.triangle_indices.len() == (INDEX_DATA_TRIANGLE_COUNT * 3) as usize);
279        assert!(terrain_data.vertex_data.len() == (VERTEX_DATA_VERTEX_COUNT * 3) as usize);
280
281        for i in 0..INDEX_DATA_TRIANGLE_COUNT {
282            let index = i as usize;
283            let inner_indices = match terrain_data.triangle_indices {
284                Indices::IndexData16(ref v) => {
285                    vec![
286                        v[index * 3] as u32,
287                        v[index * 3 + 1] as u32,
288                        v[index * 3 + 2] as u32,
289                    ]
290                }
291                Indices::IndexData32(ref v) => {
292                    vec![v[index * 3], v[index * 3 + 1], v[index * 3 + 2]]
293                }
294            };
295            let triangle = create_triangle(&inner_indices, &terrain_data.vertex_data);
296            compare_triangles(&triangle, &GROUND_TRUTH_TRIANGLES[index])
297        }
298    }
299    #[test]
300    fn test_with_extension() {
301        let terrain_data = from_file("assets/tile-with-extensions.terrain");
302        assert!(terrain_data.extension.vertex_normals.is_some());
303        assert!(terrain_data.extension.water_mask.is_some());
304    }
305    #[test]
306    fn test_with_metadata_extension() {
307        let terrain_data = from_file("assets/tile-with-metadata-extension.terrain");
308        assert!(
309            terrain_data.extension.metadata
310                == Some(
311                    "{\"geometricerror\":1232.3392654126055,\"surfacearea\":91962509942.00667}"
312                        .into()
313                )
314        );
315    }
316}