1use std::io::{self, Write};
8
9use flate2::Compression;
10use flate2::write::GzEncoder;
11
12use crate::{
13 EdgeIndices, ExtensionId, QuantizedMeshHeader, QuantizedVertices, TileMetadata, WaterMask,
14};
15
16#[inline]
25pub fn zigzag_encode(value: i32) -> u32 {
26 ((value << 1) ^ (value >> 31)) as u32
27}
28
29#[inline]
31pub fn zigzag_decode(value: u32) -> i32 {
32 ((value >> 1) as i32) ^ (-((value & 1) as i32))
33}
34
35pub fn oct_encode_normal(normal: [f32; 3]) -> [u8; 2] {
39 let [mut x, mut y, z] = normal;
40
41 let inv_l1 = 1.0 / (x.abs() + y.abs() + z.abs());
43 x *= inv_l1;
44 y *= inv_l1;
45
46 if z < 0.0 {
48 let ox = x;
49 x = (1.0 - y.abs()) * if ox >= 0.0 { 1.0 } else { -1.0 };
50 y = (1.0 - ox.abs()) * if y >= 0.0 { 1.0 } else { -1.0 };
51 }
52
53 let encode = |v: f32| -> u8 { ((v * 0.5 + 0.5) * 255.0).clamp(0.0, 255.0) as u8 };
55
56 [encode(x), encode(y)]
57}
58
59pub fn oct_encode_normals_into(normals: &[[f32; 3]], out: &mut [u8]) {
75 assert_eq!(
76 out.len(),
77 normals.len() * 2,
78 "normal output length mismatch: expected {}, got {}",
79 normals.len() * 2,
80 out.len()
81 );
82
83 #[cfg(not(target_arch = "wasm32"))]
84 for (n, o) in normals.iter().zip(out.chunks_exact_mut(2)) {
85 o.copy_from_slice(&oct_encode_normal(*n));
86 }
87
88 #[cfg(target_arch = "wasm32")]
89 oct_encode_normals_simd128(normals, out);
90}
91
92#[cfg(target_arch = "wasm32")]
96fn oct_encode_normals_simd128(normals: &[[f32; 3]], out: &mut [u8]) {
97 use wide::{CmpGe, CmpLt, f32x4};
98
99 let one = f32x4::splat(1.0);
100 let neg_one = f32x4::splat(-1.0);
101 let zero = f32x4::splat(0.0);
102 let half = f32x4::splat(0.5);
103 let n255 = f32x4::splat(255.0);
104
105 let mut nchunks = normals.chunks_exact(4);
106 let mut ochunks = out.chunks_exact_mut(8);
107 for (nc, oc) in nchunks.by_ref().zip(ochunks.by_ref()) {
108 let x = f32x4::new([nc[0][0], nc[1][0], nc[2][0], nc[3][0]]);
109 let y = f32x4::new([nc[0][1], nc[1][1], nc[2][1], nc[3][1]]);
110 let z = f32x4::new([nc[0][2], nc[1][2], nc[2][2], nc[3][2]]);
111
112 let inv_l1 = one / (x.abs() + y.abs() + z.abs());
113 let px = x * inv_l1;
114 let py = y * inv_l1;
115
116 let neg = z.cmp_lt(zero);
118 let sign_x = px.cmp_ge(zero).blend(one, neg_one);
119 let sign_y = py.cmp_ge(zero).blend(one, neg_one);
120 let fx = (one - py.abs()) * sign_x;
121 let fy = (one - px.abs()) * sign_y;
122 let px = neg.blend(fx, px);
123 let py = neg.blend(fy, py);
124
125 let bx = ((px * half + half) * n255).max(zero).min(n255).to_array();
127 let by = ((py * half + half) * n255).max(zero).min(n255).to_array();
128 for k in 0..4 {
129 oc[k * 2] = bx[k] as u8;
130 oc[k * 2 + 1] = by[k] as u8;
131 }
132 }
133
134 for (n, o) in nchunks
136 .remainder()
137 .iter()
138 .zip(ochunks.into_remainder().chunks_exact_mut(2))
139 {
140 o.copy_from_slice(&oct_encode_normal(*n));
141 }
142}
143
144#[derive(Debug, Clone, Default)]
146pub struct EncodeOptions {
147 pub include_normals: bool,
149 pub normals: Option<Vec<[f32; 3]>>,
151 pub include_water_mask: bool,
153 pub water_mask: Option<WaterMask>,
155 pub include_metadata: bool,
157 pub metadata: Option<TileMetadata>,
159 pub compression_level: u32,
161}
162
163pub struct QuantizedMeshEncoder {
167 header: QuantizedMeshHeader,
168 vertices: QuantizedVertices,
169 indices: Vec<u32>,
170 edge_indices: EdgeIndices,
171}
172
173impl QuantizedMeshEncoder {
174 pub fn new(
176 header: QuantizedMeshHeader,
177 vertices: QuantizedVertices,
178 indices: Vec<u32>,
179 edge_indices: EdgeIndices,
180 ) -> Self {
181 Self {
182 header,
183 vertices,
184 indices,
185 edge_indices,
186 }
187 }
188
189 pub fn encode(&self) -> Vec<u8> {
191 self.encode_with_options(&EncodeOptions::default())
192 }
193
194 pub fn encode_with_options(&self, options: &EncodeOptions) -> Vec<u8> {
196 let mut output = Vec::new();
197 self.encode_to_with_options(&mut output, options)
198 .expect("Failed to encode to Vec");
199 output
200 }
201
202 pub fn encode_to<W: Write>(&self, writer: W) -> io::Result<()> {
204 self.encode_to_with_options(writer, &EncodeOptions::default())
205 }
206
207 pub fn encode_to_with_options<W: Write>(
209 &self,
210 writer: W,
211 options: &EncodeOptions,
212 ) -> io::Result<()> {
213 if options.compression_level == 0 {
214 self.encode_uncompressed_to(writer, options)
215 } else {
216 let mut encoder = GzEncoder::new(writer, Compression::new(options.compression_level));
217 self.encode_uncompressed_to(&mut encoder, options)?;
218 encoder.finish()?;
219 Ok(())
220 }
221 }
222
223 fn encode_uncompressed_to<W: Write>(
226 &self,
227 mut writer: W,
228 options: &EncodeOptions,
229 ) -> io::Result<()> {
230 let vertex_count = self.vertices.len();
231 let use_32bit = vertex_count > 65535;
232
233 writer.write_all(&self.header.to_bytes())?;
235 writer.write_all(&(vertex_count as u32).to_le_bytes())?;
237
238 write_zigzag_delta_to(&mut writer, &self.vertices.u)?;
240 write_zigzag_delta_to(&mut writer, &self.vertices.v)?;
241 write_zigzag_delta_to(&mut writer, &self.vertices.height)?;
242
243 let current_pos = 88 + 4 + vertex_count * 6;
245 let align = if use_32bit { 4 } else { 2 };
246 let padding = (align - (current_pos % align)) % align;
247 if padding > 0 {
248 let zeros = [0u8; 4];
249 writer.write_all(&zeros[..padding])?;
250 }
251
252 let triangle_count = self.indices.len() / 3;
254 writer.write_all(&(triangle_count as u32).to_le_bytes())?;
255 write_high_water_mark_to(&mut writer, &self.indices, use_32bit)?;
256
257 for edge in [
259 &self.edge_indices.west,
260 &self.edge_indices.south,
261 &self.edge_indices.east,
262 &self.edge_indices.north,
263 ] {
264 writer.write_all(&(edge.len() as u32).to_le_bytes())?;
265 write_indices_to(&mut writer, edge, use_32bit)?;
266 }
267
268 if options.include_normals
270 && let Some(normals) = &options.normals
271 {
272 write_normals_extension_to(&mut writer, normals)?;
273 }
274 if options.include_water_mask {
275 let water_mask = options.water_mask.as_ref().cloned().unwrap_or_default();
276 write_water_mask_extension_to(&mut writer, &water_mask)?;
277 }
278 if options.include_metadata
279 && let Some(metadata) = &options.metadata
280 {
281 write_metadata_extension_to(&mut writer, metadata)?;
282 }
283
284 Ok(())
285 }
286}
287
288const WRITE_BUF: usize = 4096;
295
296fn write_zigzag_delta_to<W: Write>(writer: &mut W, values: &[u16]) -> io::Result<()> {
297 let mut buf = [0u8; WRITE_BUF];
298 let mut len = 0;
299 let mut prev = 0i32;
300 for &value in values {
301 let current = value as i32;
302 let delta = current - prev;
303 let bytes = (zigzag_encode(delta) as u16).to_le_bytes();
304 buf[len] = bytes[0];
305 buf[len + 1] = bytes[1];
306 len += 2;
307 prev = current;
308 if len + 2 > WRITE_BUF {
309 writer.write_all(&buf[..len])?;
310 len = 0;
311 }
312 }
313 if len > 0 {
314 writer.write_all(&buf[..len])?;
315 }
316 Ok(())
317}
318
319fn write_high_water_mark_to<W: Write>(
320 writer: &mut W,
321 indices: &[u32],
322 use_32bit: bool,
323) -> io::Result<()> {
324 let mut buf = [0u8; WRITE_BUF];
325 let mut len = 0;
326 let mut highest = 0u32;
327 let stride = if use_32bit { 4 } else { 2 };
328 for &index in indices {
329 let code = if index == highest {
330 highest += 1;
331 0
332 } else {
333 highest - index
334 };
335 if use_32bit {
336 buf[len..len + 4].copy_from_slice(&code.to_le_bytes());
337 } else {
338 buf[len..len + 2].copy_from_slice(&(code as u16).to_le_bytes());
339 }
340 len += stride;
341 if len + stride > WRITE_BUF {
342 writer.write_all(&buf[..len])?;
343 len = 0;
344 }
345 }
346 if len > 0 {
347 writer.write_all(&buf[..len])?;
348 }
349 Ok(())
350}
351
352fn write_indices_to<W: Write>(writer: &mut W, indices: &[u32], use_32bit: bool) -> io::Result<()> {
353 let mut buf = [0u8; WRITE_BUF];
354 let mut len = 0;
355 let stride = if use_32bit { 4 } else { 2 };
356 for &idx in indices {
357 if use_32bit {
358 buf[len..len + 4].copy_from_slice(&idx.to_le_bytes());
359 } else {
360 buf[len..len + 2].copy_from_slice(&(idx as u16).to_le_bytes());
361 }
362 len += stride;
363 if len + stride > WRITE_BUF {
364 writer.write_all(&buf[..len])?;
365 len = 0;
366 }
367 }
368 if len > 0 {
369 writer.write_all(&buf[..len])?;
370 }
371 Ok(())
372}
373
374fn write_normals_extension_to<W: Write>(writer: &mut W, normals: &[[f32; 3]]) -> io::Result<()> {
375 writer.write_all(&[ExtensionId::OctEncodedVertexNormals as u8])?;
376 writer.write_all(&((normals.len() * 2) as u32).to_le_bytes())?;
377 let mut buf = [0u8; WRITE_BUF];
380 for chunk in normals.chunks(WRITE_BUF / 2) {
381 let n = chunk.len() * 2;
382 oct_encode_normals_into(chunk, &mut buf[..n]);
383 writer.write_all(&buf[..n])?;
384 }
385 Ok(())
386}
387
388fn write_water_mask_extension_to<W: Write>(
389 writer: &mut W,
390 water_mask: &WaterMask,
391) -> io::Result<()> {
392 writer.write_all(&[ExtensionId::WaterMask as u8])?;
393 match water_mask {
394 WaterMask::Uniform(value) => {
395 writer.write_all(&1u32.to_le_bytes())?;
396 writer.write_all(&[*value])?;
397 }
398 WaterMask::Grid(grid) => {
399 writer.write_all(&(256 * 256u32).to_le_bytes())?;
400 writer.write_all(grid.as_ref())?;
401 }
402 }
403 Ok(())
404}
405
406fn write_metadata_extension_to<W: Write>(
407 writer: &mut W,
408 metadata: &TileMetadata,
409) -> io::Result<()> {
410 let json = serde_json::to_string(metadata)
411 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
412 let json_bytes = json.as_bytes();
413 writer.write_all(&[ExtensionId::Metadata as u8])?;
414 let extension_length = 4 + json_bytes.len() as u32;
415 writer.write_all(&extension_length.to_le_bytes())?;
416 writer.write_all(&(json_bytes.len() as u32).to_le_bytes())?;
417 writer.write_all(json_bytes)?;
418 Ok(())
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424
425 fn spread_normals(n: usize) -> Vec<[f32; 3]> {
428 (0..n)
429 .map(|i| {
430 let a = (i as f32) * 0.61803398875;
431 let b = (i as f32) * 0.15915494309;
432 let x = a.sin() * 0.9;
433 let y = b.cos() * 0.9;
434 let z = 1.0 - x.abs() - y.abs();
435 let len = (x * x + y * y + z * z).sqrt().max(1e-6);
436 [x / len, y / len, z / len]
437 })
438 .collect()
439 }
440
441 #[test]
442 fn oct_encode_normals_into_matches_per_normal() {
443 let normals = spread_normals(1003); let mut batch = vec![0u8; normals.len() * 2];
445 oct_encode_normals_into(&normals, &mut batch);
446 for (i, n) in normals.iter().enumerate() {
447 assert_eq!(
448 [batch[i * 2], batch[i * 2 + 1]],
449 oct_encode_normal(*n),
450 "batch encode mismatch at normal {i}"
451 );
452 }
453 }
454
455 #[test]
456 fn test_zigzag_encode() {
457 assert_eq!(zigzag_encode(0), 0);
458 assert_eq!(zigzag_encode(-1), 1);
459 assert_eq!(zigzag_encode(1), 2);
460 assert_eq!(zigzag_encode(-2), 3);
461 assert_eq!(zigzag_encode(2), 4);
462 }
463
464 #[test]
465 fn test_zigzag_roundtrip() {
466 for i in -1000..1000 {
467 assert_eq!(zigzag_decode(zigzag_encode(i)), i);
468 }
469 }
470
471 #[test]
472 fn test_oct_encode_normal() {
473 let up = [0.0f32, 0.0, 1.0];
474 let encoded = oct_encode_normal(up);
475 assert!((encoded[0] as i32 - 127).abs() < 2);
476 assert!((encoded[1] as i32 - 127).abs() < 2);
477
478 let down = [0.0f32, 0.0, -1.0];
479 let encoded = oct_encode_normal(down);
480 assert!(encoded[0] == 0 || encoded[0] == 255);
481 }
482
483 #[test]
484 fn test_encoder_basic() {
485 let header = QuantizedMeshHeader::default();
486 let vertices = QuantizedVertices {
487 u: vec![0, 32767, 0, 32767],
488 v: vec![0, 0, 32767, 32767],
489 height: vec![0, 0, 0, 0],
490 };
491 let indices = vec![0, 1, 2, 1, 3, 2];
492 let edge_indices = EdgeIndices::from_vertices(&vertices);
493
494 let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
495 let data = encoder.encode_with_options(&EncodeOptions {
496 compression_level: 0,
497 ..Default::default()
498 });
499
500 assert!(data.len() > 88);
501 let parsed_header = QuantizedMeshHeader::from_bytes(&data).unwrap();
502 assert_eq!(parsed_header.min_height, 0.0);
503 }
504
505 #[test]
506 fn test_encoder_with_compression() {
507 let header = QuantizedMeshHeader::default();
508 let vertices = QuantizedVertices {
509 u: vec![0, 32767, 0, 32767],
510 v: vec![0, 0, 32767, 32767],
511 height: vec![0, 0, 0, 0],
512 };
513 let indices = vec![0, 1, 2, 1, 3, 2];
514 let edge_indices = EdgeIndices::from_vertices(&vertices);
515
516 let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
517
518 let compressed = encoder.encode_with_options(&EncodeOptions {
519 compression_level: 6,
520 ..Default::default()
521 });
522
523 assert_eq!(&compressed[0..2], &[0x1f, 0x8b]);
524 }
525
526 #[test]
527 fn test_encoder_with_extensions() {
528 let header = QuantizedMeshHeader::default();
529 let vertices = QuantizedVertices {
530 u: vec![0, 32767, 0, 32767],
531 v: vec![0, 0, 32767, 32767],
532 height: vec![0, 0, 0, 0],
533 };
534 let indices = vec![0, 1, 2, 1, 3, 2];
535 let edge_indices = EdgeIndices::from_vertices(&vertices);
536
537 let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
538
539 let normals = vec![[0.0, 0.0, 1.0]; 4];
540
541 let data = encoder.encode_with_options(&EncodeOptions {
542 compression_level: 0,
543 include_normals: true,
544 normals: Some(normals),
545 include_water_mask: true,
546 water_mask: Some(WaterMask::Uniform(0)),
547 ..Default::default()
548 });
549
550 let without_ext = encoder.encode_with_options(&EncodeOptions {
551 compression_level: 0,
552 ..Default::default()
553 });
554
555 assert!(data.len() > without_ext.len());
556 }
557
558 #[test]
559 fn test_encode_to_writer_matches_encode_with_options() {
560 let header = QuantizedMeshHeader::default();
561 let vertices = QuantizedVertices {
562 u: vec![0, 32767, 0, 32767],
563 v: vec![0, 0, 32767, 32767],
564 height: vec![0, 0, 0, 0],
565 };
566 let indices = vec![0, 1, 2, 1, 3, 2];
567 let edge_indices = EdgeIndices::from_vertices(&vertices);
568
569 let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
570
571 let data_vec = encoder.encode_with_options(&EncodeOptions {
572 compression_level: 0,
573 ..Default::default()
574 });
575
576 let mut data_writer = Vec::new();
577 encoder
578 .encode_to_with_options(
579 &mut data_writer,
580 &EncodeOptions {
581 compression_level: 0,
582 ..Default::default()
583 },
584 )
585 .expect("Failed to encode to writer");
586
587 assert_eq!(data_vec, data_writer);
588 }
589
590 #[test]
591 fn test_encode_to_writer_compressed() {
592 let header = QuantizedMeshHeader::default();
593 let vertices = QuantizedVertices {
594 u: vec![0, 32767, 0, 32767],
595 v: vec![0, 0, 32767, 32767],
596 height: vec![0, 0, 0, 0],
597 };
598 let indices = vec![0, 1, 2, 1, 3, 2];
599 let edge_indices = EdgeIndices::from_vertices(&vertices);
600
601 let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
602
603 let mut data_writer = Vec::new();
604 encoder
605 .encode_to_with_options(
606 &mut data_writer,
607 &EncodeOptions {
608 compression_level: 6,
609 ..Default::default()
610 },
611 )
612 .expect("Failed to encode to writer");
613
614 assert_eq!(&data_writer[0..2], &[0x1f, 0x8b]);
615 }
616}