1#[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 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 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 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 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 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 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 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 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}