draco_oxide/encode/point_cloud/
mod.rs1mod bit_encoder;
4mod kd_tree;
5
6use std::collections::HashMap;
7
8use draco_oxide_core::attribute::{Attribute, AttributeType, ComponentDataType};
9use draco_oxide_core::bit_coder::ByteWriter;
10use draco_oxide_core::point_cloud::PointCloud;
11use draco_oxide_core::types::{ConfigType, NdVector, PointIdx, Vector};
12use draco_oxide_core::utils::bit_coder::leb128_write;
13use thiserror::Error;
14
15use super::attribute::portabilization::Quantization;
16
17const GEOMETRY_TYPE_POINT_CLOUD: u8 = 0;
18const METHOD_KD_TREE: u8 = 1;
19const METADATA_FLAG_MASK: u16 = 0x8000;
20
21pub const MAX_COMPRESSION_LEVEL: u8 = 6;
23
24#[remain::sorted]
26#[derive(Error, Debug)]
27#[non_exhaustive]
28pub enum Err {
29 #[error("entropy error: {0}")]
31 Entropy(#[from] crate::encode::entropy::rans::Err),
32 #[error("attribute {0:?} holds a non-finite value")]
34 NonFiniteValue(AttributeType),
35 #[error("the point cloud has no points")]
37 NoPoints,
38 #[error("quantized value out of range for attribute {0:?}")]
40 QuantizedValueOutOfRange(AttributeType),
41 #[error("unsupported attribute layout: {0} components of {1:?}")]
43 UnsupportedAttributeLayout(usize, ComponentDataType),
44 #[error("unsupported component type for a point cloud: {0:?}")]
46 UnsupportedComponentType(ComponentDataType),
47 #[error("unsupported kd-tree compression level: {0}")]
49 UnsupportedCompressionLevel(u8),
50}
51
52#[derive(Clone, Debug)]
54pub struct Config {
55 compression_level: u8,
56 quantization: Quantization,
57 overrides: HashMap<AttributeType, Quantization>,
58 metadata: bool,
59}
60
61impl ConfigType for Config {
62 fn default() -> Self {
63 Self {
64 compression_level: MAX_COMPRESSION_LEVEL,
65 quantization: Quantization::Bits(11),
66 overrides: HashMap::new(),
67 metadata: false,
68 }
69 }
70}
71
72impl Config {
73 pub fn with_compression_level(mut self, level: u8) -> Self {
76 self.compression_level = level;
77 self
78 }
79
80 pub fn with_quantization(mut self, quantization: Quantization) -> Self {
82 self.quantization = quantization;
83 self
84 }
85
86 pub fn with_attribute_quantization(
88 mut self,
89 att_type: AttributeType,
90 quantization: Quantization,
91 ) -> Self {
92 self.overrides.insert(att_type, quantization);
93 self
94 }
95
96 pub fn with_metadata(mut self, metadata: bool) -> Self {
98 self.metadata = metadata;
99 self
100 }
101
102 pub fn validate(&self) -> Result<(), Err> {
104 if self.compression_level > MAX_COMPRESSION_LEVEL {
105 return Err(Err::UnsupportedCompressionLevel(self.compression_level));
106 }
107 Ok(())
108 }
109
110 fn quantization_for(&self, att_type: AttributeType) -> Quantization {
111 self.overrides
112 .get(&att_type)
113 .copied()
114 .unwrap_or(self.quantization)
115 }
116}
117
118enum Portable {
120 Quantized { min: Vec<f32>, range: f32, bits: u8 },
121 Unsigned,
122 Signed { mins: Vec<i32> },
123}
124
125pub(crate) fn encode_impl<W>(pc: PointCloud, writer: &mut W, cfg: Config) -> Result<(), Err>
127where
128 W: ByteWriter,
129{
130 cfg.validate()?;
131 let num_points = pc.num_points();
132 if num_points == 0 {
133 return Err(Err::NoPoints);
134 }
135
136 let attributes = pc.into_attributes();
137 let dimension: usize = attributes.iter().map(|a| a.get_num_components()).sum();
138
139 for b in b"DRACO" {
140 writer.write_u8(*b);
141 }
142 writer.write_u8(2);
143 writer.write_u8(3);
144 writer.write_u8(GEOMETRY_TYPE_POINT_CLOUD);
145 writer.write_u8(METHOD_KD_TREE);
146 let flags = if cfg.metadata { METADATA_FLAG_MASK } else { 0 };
147 writer.write_u16(flags);
148 if cfg.metadata {
149 super::metadata::encode_point_cloud_metadata(&attributes, writer);
150 }
151
152 writer.write_u32(num_points as u32);
153
154 writer.write_u8(1);
155 leb128_write(attributes.len() as u64, writer);
156 for (i, att) in attributes.iter().enumerate() {
157 att.get_attribute_type().write_to(writer);
158 att.get_component_type().write_to(writer);
159 writer.write_u8(att.get_num_components() as u8);
160 writer.write_u8(0);
161 leb128_write(i as u64, writer);
162 }
163
164 let mut points = vec![0u32; num_points * dimension];
165 let mut portables = Vec::with_capacity(attributes.len());
166 let mut offset = 0usize;
167 for att in &attributes {
168 let n = att.get_num_components();
169 portables.push(portabilize(
170 att,
171 &cfg,
172 num_points,
173 &mut points,
174 dimension,
175 offset,
176 )?);
177 offset += n;
178 }
179
180 writer.write_u8(cfg.compression_level);
181 kd_tree::encode_points(&mut points, dimension, cfg.compression_level, writer)?;
182
183 for portable in &portables {
184 if let Portable::Quantized { min, range, bits } = portable {
185 for m in min {
186 writer.write_u32(m.to_bits());
187 }
188 writer.write_u32(range.to_bits());
189 writer.write_u8(*bits);
190 }
191 }
192 for portable in &portables {
193 if let Portable::Signed { mins } = portable {
194 for &m in mins {
195 leb128_write(zigzag(m) as u64, writer);
196 }
197 }
198 }
199 Ok(())
200}
201
202fn portabilize(
204 att: &Attribute,
205 cfg: &Config,
206 num_points: usize,
207 points: &mut [u32],
208 dimension: usize,
209 offset: usize,
210) -> Result<Portable, Err> {
211 let num_components = att.get_num_components();
212 if !(1..=4).contains(&num_components) {
213 return Err(Err::UnsupportedAttributeLayout(
214 num_components,
215 att.get_component_type(),
216 ));
217 }
218 match num_components {
219 1 => portabilize_typed::<1>(att, cfg, num_points, points, dimension, offset),
220 2 => portabilize_typed::<2>(att, cfg, num_points, points, dimension, offset),
221 3 => portabilize_typed::<3>(att, cfg, num_points, points, dimension, offset),
222 _ => portabilize_typed::<4>(att, cfg, num_points, points, dimension, offset),
223 }
224}
225
226fn portabilize_typed<const N: usize>(
227 att: &Attribute,
228 cfg: &Config,
229 num_points: usize,
230 points: &mut [u32],
231 dimension: usize,
232 offset: usize,
233) -> Result<Portable, Err>
234where
235 NdVector<N, f32>: Vector<N, Component = f32>,
236 NdVector<N, u8>: Vector<N, Component = u8>,
237 NdVector<N, u16>: Vector<N, Component = u16>,
238 NdVector<N, u32>: Vector<N, Component = u32>,
239 NdVector<N, i8>: Vector<N, Component = i8>,
240 NdVector<N, i16>: Vector<N, Component = i16>,
241 NdVector<N, i32>: Vector<N, Component = i32>,
242{
243 let att_type = att.get_attribute_type();
244
245 macro_rules! write_signed {
246 ($ty:ty) => {{
247 let values: Vec<NdVector<N, $ty>> = (0..num_points)
248 .map(|p| att.get(PointIdx::from(p)))
249 .collect();
250 let mut mins = vec![i32::MAX; N];
251 for v in &values {
252 for c in 0..N {
253 mins[c] = mins[c].min(*v.get(c) as i32);
254 }
255 }
256 for (p, v) in values.iter().enumerate() {
257 for c in 0..N {
258 points[p * dimension + offset + c] = (*v.get(c) as i64 - mins[c] as i64) as u32;
261 }
262 }
263 Ok(Portable::Signed { mins })
264 }};
265 }
266
267 macro_rules! write_unsigned {
268 ($ty:ty) => {{
269 for p in 0..num_points {
270 let v: NdVector<N, $ty> = att.get(PointIdx::from(p));
271 for c in 0..N {
272 points[p * dimension + offset + c] = *v.get(c) as u32;
273 }
274 }
275 Ok(Portable::Unsigned)
276 }};
277 }
278
279 match att.get_component_type() {
280 ComponentDataType::F32 => {
281 let values: Vec<NdVector<N, f32>> = (0..num_points)
282 .map(|p| att.get(PointIdx::from(p)))
283 .collect();
284 let mut min = [f32::INFINITY; N];
285 let mut max = [f32::NEG_INFINITY; N];
286 for v in &values {
287 for c in 0..N {
288 let x = *v.get(c);
289 if !x.is_finite() {
290 return Err(Err::NonFiniteValue(att_type));
291 }
292 min[c] = min[c].min(x);
293 max[c] = max[c].max(x);
294 }
295 }
296 let mut range = 0.0f32;
299 for c in 0..N {
300 range = range.max(max[c] - min[c]);
301 }
302 if range == 0.0 {
303 range = 1.0;
304 }
305 let bits = cfg.quantization_for(att_type).resolve(range);
306 let max_quantized = (1u32 << bits) - 1;
307 let inverse_delta = max_quantized as f32 / range;
308 for (p, v) in values.iter().enumerate() {
309 for c in 0..N {
310 let q = ((*v.get(c) - min[c]) * inverse_delta + 0.5).floor();
311 if !(0.0..=max_quantized as f32).contains(&q) {
312 return Err(Err::QuantizedValueOutOfRange(att_type));
313 }
314 points[p * dimension + offset + c] = q as u32;
315 }
316 }
317 Ok(Portable::Quantized {
318 min: min.to_vec(),
319 range,
320 bits,
321 })
322 }
323 ComponentDataType::U8 => write_unsigned!(u8),
324 ComponentDataType::U16 => write_unsigned!(u16),
325 ComponentDataType::U32 => write_unsigned!(u32),
326 ComponentDataType::I8 => write_signed!(i8),
327 ComponentDataType::I16 => write_signed!(i16),
328 ComponentDataType::I32 => write_signed!(i32),
329 other => Err(Err::UnsupportedComponentType(other)),
330 }
331}
332
333fn zigzag(v: i32) -> u32 {
335 ((v << 1) ^ (v >> 31)) as u32
336}