Skip to main content

quantized_mesh/
encoding.rs

1//! Encoding functions and main encoder for quantized-mesh format.
2
3use std::io::{self, Write};
4
5use flate2::Compression;
6use flate2::write::GzEncoder;
7
8use crate::{
9    EdgeIndices, ExtensionId, QuantizedMeshHeader, QuantizedVertices, TileMetadata, WaterMask,
10};
11
12/// Encode a value using zig-zag encoding.
13///
14/// Maps signed integers to unsigned integers so that small magnitude values
15/// (positive or negative) have small encoded values.
16///
17/// ```text
18/// 0 -> 0, -1 -> 1, 1 -> 2, -2 -> 3, 2 -> 4, ...
19/// ```
20#[inline]
21pub fn zigzag_encode(value: i32) -> u32 {
22    ((value << 1) ^ (value >> 31)) as u32
23}
24
25/// Decode a zig-zag encoded value.
26#[inline]
27pub fn zigzag_decode(value: u32) -> i32 {
28    ((value >> 1) as i32) ^ (-((value & 1) as i32))
29}
30
31/// Encode vertex coordinates using zig-zag delta encoding.
32///
33/// Each value is encoded as the zig-zag encoded difference from the previous value.
34pub fn encode_zigzag_delta(values: &[u16]) -> Vec<u16> {
35    let mut result = Vec::with_capacity(values.len());
36    let mut prev = 0i32;
37
38    for &value in values {
39        let current = value as i32;
40        let delta = current - prev;
41        result.push(zigzag_encode(delta) as u16);
42        prev = current;
43    }
44
45    result
46}
47
48/// Decode zig-zag delta encoded values.
49pub fn decode_zigzag_delta(encoded: &[u16]) -> Vec<u16> {
50    let mut result = Vec::with_capacity(encoded.len());
51    let mut value = 0i32;
52
53    for &enc in encoded {
54        let delta = zigzag_decode(enc as u32);
55        value += delta;
56        result.push(value as u16);
57    }
58
59    result
60}
61
62/// Encode indices using high-water mark encoding.
63///
64/// This encoding is efficient when indices reference recently added vertices.
65pub fn encode_high_water_mark(indices: &[u32]) -> Vec<u32> {
66    let mut result = Vec::with_capacity(indices.len());
67    let mut highest = 0u32;
68
69    for &index in indices {
70        let code = if index == highest {
71            highest += 1;
72            0
73        } else {
74            highest - index
75        };
76        result.push(code);
77    }
78
79    result
80}
81
82/// Decode high-water mark encoded indices.
83pub fn decode_high_water_mark(encoded: &[u32]) -> Vec<u32> {
84    let mut result = Vec::with_capacity(encoded.len());
85    let mut highest = 0u32;
86
87    for &code in encoded {
88        let index = highest - code;
89        if code == 0 {
90            highest += 1;
91        }
92        result.push(index);
93    }
94
95    result
96}
97
98/// Oct-encode a unit normal vector to 2 bytes.
99///
100/// Uses octahedron encoding for efficient normal compression.
101pub fn oct_encode_normal(normal: [f32; 3]) -> [u8; 2] {
102    let [mut x, mut y, z] = normal;
103
104    // Project to octahedron
105    let inv_l1 = 1.0 / (x.abs() + y.abs() + z.abs());
106    x *= inv_l1;
107    y *= inv_l1;
108
109    // Unfold lower hemisphere
110    if z < 0.0 {
111        let ox = x;
112        x = (1.0 - y.abs()) * if ox >= 0.0 { 1.0 } else { -1.0 };
113        y = (1.0 - ox.abs()) * if y >= 0.0 { 1.0 } else { -1.0 };
114    }
115
116    // Map from [-1, 1] to [0, 255]
117    let encode = |v: f32| -> u8 { ((v * 0.5 + 0.5) * 255.0).clamp(0.0, 255.0) as u8 };
118
119    [encode(x), encode(y)]
120}
121
122/// Options for encoding quantized mesh.
123#[derive(Debug, Clone, Default)]
124pub struct EncodeOptions {
125    /// Include oct-encoded vertex normals
126    pub include_normals: bool,
127    /// Vertex normals (required if include_normals is true)
128    pub normals: Option<Vec<[f32; 3]>>,
129    /// Include water mask
130    pub include_water_mask: bool,
131    /// Water mask data
132    pub water_mask: Option<WaterMask>,
133    /// Include metadata extension with tile availability
134    pub include_metadata: bool,
135    /// Metadata for tile availability
136    pub metadata: Option<TileMetadata>,
137    /// Gzip compression level (0-9, default 6)
138    pub compression_level: u32,
139}
140
141/// Quantized mesh encoder.
142///
143/// Encodes terrain mesh data into the quantized-mesh-1.0 format.
144pub struct QuantizedMeshEncoder {
145    header: QuantizedMeshHeader,
146    vertices: QuantizedVertices,
147    indices: Vec<u32>,
148    edge_indices: EdgeIndices,
149}
150
151impl QuantizedMeshEncoder {
152    /// Create a new encoder with mesh data.
153    pub fn new(
154        header: QuantizedMeshHeader,
155        vertices: QuantizedVertices,
156        indices: Vec<u32>,
157        edge_indices: EdgeIndices,
158    ) -> Self {
159        Self {
160            header,
161            vertices,
162            indices,
163            edge_indices,
164        }
165    }
166
167    /// Encode to quantized-mesh format without compression.
168    pub fn encode(&self) -> Vec<u8> {
169        self.encode_with_options(&EncodeOptions::default())
170    }
171
172    /// Encode with options (extensions, compression).
173    pub fn encode_with_options(&self, options: &EncodeOptions) -> Vec<u8> {
174        let mut output = Vec::new();
175        self.encode_to_with_options(&mut output, options)
176            .expect("Failed to encode to Vec");
177        output
178    }
179
180    /// Encode to a writer without compression.
181    pub fn encode_to<W: Write>(&self, writer: W) -> io::Result<()> {
182        self.encode_to_with_options(writer, &EncodeOptions::default())
183    }
184
185    /// Encode to a writer with options (extensions, compression).
186    pub fn encode_to_with_options<W: Write>(
187        &self,
188        mut writer: W,
189        options: &EncodeOptions,
190    ) -> io::Result<()> {
191        if options.compression_level == 0 {
192            self.encode_uncompressed_to(&mut writer, options)
193        } else {
194            // Gzip compress
195            let mut encoder = GzEncoder::new(writer, Compression::new(options.compression_level));
196            self.encode_uncompressed_to(&mut encoder, options)?;
197            encoder.finish()?;
198            Ok(())
199        }
200    }
201
202    /// Encode without compression to a writer.
203    fn encode_uncompressed_to<W: Write>(
204        &self,
205        writer: &mut W,
206        options: &EncodeOptions,
207    ) -> io::Result<()> {
208        let vertex_count = self.vertices.len();
209        let use_32bit = vertex_count > 65535;
210
211        // Write header (88 bytes)
212        writer.write_all(&self.header.to_bytes())?;
213
214        // Write vertex count
215        writer.write_all(&(vertex_count as u32).to_le_bytes())?;
216
217        // Write encoded vertex data
218        let encoded_u = encode_zigzag_delta(&self.vertices.u);
219        let encoded_v = encode_zigzag_delta(&self.vertices.v);
220        let encoded_height = encode_zigzag_delta(&self.vertices.height);
221
222        for &u in &encoded_u {
223            writer.write_all(&u.to_le_bytes())?;
224        }
225        for &v in &encoded_v {
226            writer.write_all(&v.to_le_bytes())?;
227        }
228        for &h in &encoded_height {
229            writer.write_all(&h.to_le_bytes())?;
230        }
231
232        // Calculate current position for padding
233        // header (88) + vertex_count (4) + vertices (vertex_count * 6)
234        let current_pos = 88 + 4 + vertex_count * 6;
235
236        // Padding for index alignment
237        if use_32bit {
238            // Align to 4 bytes
239            let padding = (4 - (current_pos % 4)) % 4;
240            for _ in 0..padding {
241                writer.write_all(&[0])?;
242            }
243        } else {
244            // Align to 2 bytes
245            let padding = (2 - (current_pos % 2)) % 2;
246            for _ in 0..padding {
247                writer.write_all(&[0])?;
248            }
249        }
250
251        // Write triangle count
252        let triangle_count = self.indices.len() / 3;
253        writer.write_all(&(triangle_count as u32).to_le_bytes())?;
254
255        // Write encoded indices
256        let encoded_indices = encode_high_water_mark(&self.indices);
257        if use_32bit {
258            for &idx in &encoded_indices {
259                writer.write_all(&idx.to_le_bytes())?;
260            }
261        } else {
262            for &idx in &encoded_indices {
263                writer.write_all(&(idx as u16).to_le_bytes())?;
264            }
265        }
266
267        // Write edge indices
268        self.write_edge_indices_to(writer, &self.edge_indices.west, use_32bit)?;
269        self.write_edge_indices_to(writer, &self.edge_indices.south, use_32bit)?;
270        self.write_edge_indices_to(writer, &self.edge_indices.east, use_32bit)?;
271        self.write_edge_indices_to(writer, &self.edge_indices.north, use_32bit)?;
272
273        // Write extensions
274        if options.include_normals
275            && let Some(normals) = &options.normals
276        {
277            self.write_normals_extension_to(writer, normals)?;
278        }
279
280        if options.include_water_mask {
281            let water_mask = options.water_mask.as_ref().cloned().unwrap_or_default();
282            self.write_water_mask_extension_to(writer, &water_mask)?;
283        }
284
285        if options.include_metadata
286            && let Some(metadata) = &options.metadata
287        {
288            self.write_metadata_extension_to(writer, metadata)?;
289        }
290
291        Ok(())
292    }
293
294    fn write_edge_indices_to<W: Write>(
295        &self,
296        writer: &mut W,
297        indices: &[u32],
298        use_32bit: bool,
299    ) -> io::Result<()> {
300        writer.write_all(&(indices.len() as u32).to_le_bytes())?;
301        if use_32bit {
302            for &idx in indices {
303                writer.write_all(&idx.to_le_bytes())?;
304            }
305        } else {
306            for &idx in indices {
307                writer.write_all(&(idx as u16).to_le_bytes())?;
308            }
309        }
310        Ok(())
311    }
312
313    fn write_normals_extension_to<W: Write>(
314        &self,
315        writer: &mut W,
316        normals: &[[f32; 3]],
317    ) -> io::Result<()> {
318        // Extension header
319        writer.write_all(&[ExtensionId::OctEncodedVertexNormals as u8])?;
320        let length = (normals.len() * 2) as u32;
321        writer.write_all(&length.to_le_bytes())?;
322
323        // Oct-encoded normals
324        for &normal in normals {
325            let encoded = oct_encode_normal(normal);
326            writer.write_all(&encoded)?;
327        }
328        Ok(())
329    }
330
331    fn write_water_mask_extension_to<W: Write>(
332        &self,
333        writer: &mut W,
334        water_mask: &WaterMask,
335    ) -> io::Result<()> {
336        writer.write_all(&[ExtensionId::WaterMask as u8])?;
337
338        match water_mask {
339            WaterMask::Uniform(value) => {
340                writer.write_all(&1u32.to_le_bytes())?;
341                writer.write_all(&[*value])?;
342            }
343            WaterMask::Grid(grid) => {
344                writer.write_all(&(256 * 256u32).to_le_bytes())?;
345                writer.write_all(grid.as_ref())?;
346            }
347        }
348        Ok(())
349    }
350
351    fn write_metadata_extension_to<W: Write>(
352        &self,
353        writer: &mut W,
354        metadata: &TileMetadata,
355    ) -> io::Result<()> {
356        // Serialize metadata to JSON
357        let json = serde_json::to_string(metadata)
358            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
359        let json_bytes = json.as_bytes();
360
361        // Extension header
362        writer.write_all(&[ExtensionId::Metadata as u8])?;
363
364        // Extension length (4 bytes for json length + actual json)
365        let extension_length = 4 + json_bytes.len() as u32;
366        writer.write_all(&extension_length.to_le_bytes())?;
367
368        // JSON length
369        writer.write_all(&(json_bytes.len() as u32).to_le_bytes())?;
370
371        // JSON data
372        writer.write_all(json_bytes)?;
373        Ok(())
374    }
375}
376
377#[cfg(test)]
378mod tests {
379    use super::*;
380
381    #[test]
382    fn test_zigzag_encode() {
383        assert_eq!(zigzag_encode(0), 0);
384        assert_eq!(zigzag_encode(-1), 1);
385        assert_eq!(zigzag_encode(1), 2);
386        assert_eq!(zigzag_encode(-2), 3);
387        assert_eq!(zigzag_encode(2), 4);
388    }
389
390    #[test]
391    fn test_zigzag_roundtrip() {
392        for i in -1000..1000 {
393            assert_eq!(zigzag_decode(zigzag_encode(i)), i);
394        }
395    }
396
397    #[test]
398    fn test_zigzag_delta_roundtrip() {
399        let values: Vec<u16> = vec![0, 100, 50, 200, 150, 32767, 0];
400        let encoded = encode_zigzag_delta(&values);
401        let decoded = decode_zigzag_delta(&encoded);
402        assert_eq!(values, decoded);
403    }
404
405    #[test]
406    fn test_high_water_mark_simple() {
407        // Sequential indices
408        let indices = vec![0, 1, 2, 3, 4, 5];
409        let encoded = encode_high_water_mark(&indices);
410        // All zeros because each index equals highest
411        assert_eq!(encoded, vec![0, 0, 0, 0, 0, 0]);
412    }
413
414    #[test]
415    fn test_high_water_mark_roundtrip() {
416        let indices = vec![0, 1, 2, 1, 3, 2, 0, 4, 3];
417        let encoded = encode_high_water_mark(&indices);
418        let decoded = decode_high_water_mark(&encoded);
419        assert_eq!(indices, decoded);
420    }
421
422    #[test]
423    fn test_oct_encode_normal() {
424        // Up vector
425        let up = [0.0f32, 0.0, 1.0];
426        let encoded = oct_encode_normal(up);
427        // Should be near center (127, 127)
428        assert!((encoded[0] as i32 - 127).abs() < 2);
429        assert!((encoded[1] as i32 - 127).abs() < 2);
430
431        // Down vector
432        let down = [0.0f32, 0.0, -1.0];
433        let encoded = oct_encode_normal(down);
434        // Should be at corners
435        assert!(encoded[0] == 0 || encoded[0] == 255);
436    }
437
438    #[test]
439    fn test_encoder_basic() {
440        let header = QuantizedMeshHeader::default();
441        let vertices = QuantizedVertices {
442            u: vec![0, 32767, 0, 32767],
443            v: vec![0, 0, 32767, 32767],
444            height: vec![0, 0, 0, 0],
445        };
446        let indices = vec![0, 1, 2, 1, 3, 2];
447        let edge_indices = EdgeIndices::from_vertices(&vertices);
448
449        let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
450        let data = encoder.encode_with_options(&EncodeOptions {
451            compression_level: 0,
452            ..Default::default()
453        });
454
455        // Should have at least header + some data
456        assert!(data.len() > 88);
457
458        // First 88 bytes should be header
459        let parsed_header = QuantizedMeshHeader::from_bytes(&data).unwrap();
460        assert_eq!(parsed_header.min_height, 0.0);
461    }
462
463    #[test]
464    fn test_encoder_with_compression() {
465        let header = QuantizedMeshHeader::default();
466        let vertices = QuantizedVertices {
467            u: vec![0, 32767, 0, 32767],
468            v: vec![0, 0, 32767, 32767],
469            height: vec![0, 0, 0, 0],
470        };
471        let indices = vec![0, 1, 2, 1, 3, 2];
472        let edge_indices = EdgeIndices::from_vertices(&vertices);
473
474        let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
475
476        let _uncompressed = encoder.encode_with_options(&EncodeOptions {
477            compression_level: 0,
478            ..Default::default()
479        });
480
481        let compressed = encoder.encode_with_options(&EncodeOptions {
482            compression_level: 6,
483            ..Default::default()
484        });
485
486        // Compressed should typically be smaller (or at least start with gzip magic)
487        assert_eq!(&compressed[0..2], &[0x1f, 0x8b]); // gzip magic number
488    }
489
490    #[test]
491    fn test_encoder_with_extensions() {
492        let header = QuantizedMeshHeader::default();
493        let vertices = QuantizedVertices {
494            u: vec![0, 32767, 0, 32767],
495            v: vec![0, 0, 32767, 32767],
496            height: vec![0, 0, 0, 0],
497        };
498        let indices = vec![0, 1, 2, 1, 3, 2];
499        let edge_indices = EdgeIndices::from_vertices(&vertices);
500
501        let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
502
503        let normals = vec![[0.0, 0.0, 1.0]; 4];
504
505        let data = encoder.encode_with_options(&EncodeOptions {
506            compression_level: 0,
507            include_normals: true,
508            normals: Some(normals),
509            include_water_mask: true,
510            water_mask: Some(WaterMask::Uniform(0)),
511            ..Default::default()
512        });
513
514        // Should be larger with extensions
515        let without_ext = encoder.encode_with_options(&EncodeOptions {
516            compression_level: 0,
517            ..Default::default()
518        });
519
520        assert!(data.len() > without_ext.len());
521    }
522
523    #[test]
524    fn test_encode_to_writer() {
525        let header = QuantizedMeshHeader::default();
526        let vertices = QuantizedVertices {
527            u: vec![0, 32767, 0, 32767],
528            v: vec![0, 0, 32767, 32767],
529            height: vec![0, 0, 0, 0],
530        };
531        let indices = vec![0, 1, 2, 1, 3, 2];
532        let edge_indices = EdgeIndices::from_vertices(&vertices);
533
534        let encoder = QuantizedMeshEncoder::new(header, vertices, indices, edge_indices);
535
536        // Encode to Vec via encode_with_options
537        let data_vec = encoder.encode_with_options(&EncodeOptions {
538            compression_level: 0,
539            ..Default::default()
540        });
541
542        // Encode to writer
543        let mut data_writer = Vec::new();
544        encoder
545            .encode_to_with_options(
546                &mut data_writer,
547                &EncodeOptions {
548                    compression_level: 0,
549                    ..Default::default()
550                },
551            )
552            .expect("Failed to encode to writer");
553
554        // Both should produce the same output
555        assert_eq!(data_vec, data_writer);
556    }
557
558    #[test]
559    fn test_encode_to_writer_compressed() {
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        // Encode to writer with compression
572        let mut data_writer = Vec::new();
573        encoder
574            .encode_to_with_options(
575                &mut data_writer,
576                &EncodeOptions {
577                    compression_level: 6,
578                    ..Default::default()
579                },
580            )
581            .expect("Failed to encode to writer");
582
583        // Should be gzip compressed
584        assert_eq!(&data_writer[0..2], &[0x1f, 0x8b]);
585    }
586}