draco_oxide/encode/attribute/portabilization/
mod.rs1pub mod octahedral_quantization;
2pub mod quantization_coordinate_wise;
3pub mod to_bits;
4
5use draco_oxide_core::attribute::{Attribute, AttributeType, ComponentDataType};
6use draco_oxide_core::bit_coder::ByteWriter;
7use draco_oxide_core::codec::attribute::Portable;
8use draco_oxide_core::debug_write;
9use draco_oxide_core::types::NdVector;
10use draco_oxide_core::types::{ConfigType, Vector};
11
12pub enum Portabilization<Data, const N: usize>
13where
14 Data: Vector<N> + Portable,
15{
16 QuantizationCoordinateWise(quantization_coordinate_wise::QuantizationCoordinateWise<Data, N>),
17 OctahedralQuantization(octahedral_quantization::OctahedralQuantization<Data, N>),
18 ToBits(to_bits::ToBits<Data, N>),
19}
20
21impl<Data, const N: usize> Portabilization<Data, N>
22where
23 Data: Vector<N> + Portable,
24 NdVector<N, i32>: Vector<N, Component = i32>,
25 NdVector<N, f32>: Vector<N, Component = f32> + Portable,
26{
27 pub fn new<W>(att: Attribute, cfg: Config, writer: &mut W) -> Self
32 where
33 W: ByteWriter,
34 {
35 debug_write!("Start of Portabilization Metadata", writer);
36 let out = match cfg.type_ {
38 PortabilizationType::QuantizationCoordinateWise => {
39 Portabilization::QuantizationCoordinateWise(
40 quantization_coordinate_wise::QuantizationCoordinateWise::<_, N>::new(
41 att, cfg, writer,
42 ),
43 )
44 }
45 PortabilizationType::OctahedralQuantization => Portabilization::OctahedralQuantization(
46 octahedral_quantization::OctahedralQuantization::new(att, cfg, writer),
47 ),
48 PortabilizationType::ToBits => {
49 Portabilization::ToBits(to_bits::ToBits::new(att, cfg, writer))
50 }
51 };
52 debug_write!("End of Portabilization Metadata", writer);
53 out
54 }
55
56 pub fn portabilize(self) -> Attribute {
57 match self {
58 Portabilization::QuantizationCoordinateWise(qcw) => qcw.portabilize(),
59 Portabilization::OctahedralQuantization(oct) => oct.portabilize(),
60 Portabilization::ToBits(tb) => tb.portabilize(),
61 }
62 }
63}
64
65pub trait PortabilizationImpl<const N: usize>
66where
67 NdVector<N, i32>: Vector<N, Component = i32>,
68{
69 fn portabilize(self) -> Attribute;
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum PortabilizationType {
75 QuantizationCoordinateWise,
76 OctahedralQuantization,
77 ToBits,
78}
79
80impl PortabilizationType {
81 pub(crate) fn get_id(&self) -> u8 {
82 match self {
83 PortabilizationType::ToBits => 1,
84 PortabilizationType::QuantizationCoordinateWise => 2,
85 PortabilizationType::OctahedralQuantization => 3,
86 }
87 }
88
89 pub(crate) fn write_to<W>(&self, writer: &mut W)
90 where
91 W: ByteWriter,
92 {
93 let id = self.get_id();
94 writer.write_u8(id);
95 }
96
97 pub(crate) fn default_for(ty: AttributeType, component_ty: ComponentDataType) -> Self {
103 if component_ty.is_integer() {
104 return PortabilizationType::ToBits;
105 }
106 match ty {
107 AttributeType::Normal => PortabilizationType::OctahedralQuantization,
108 _ => PortabilizationType::QuantizationCoordinateWise,
113 }
114 }
115}
116
117#[derive(Clone, Copy, Debug)]
118pub struct Config {
119 pub type_: PortabilizationType,
120 pub quantization: Quantization,
121}
122
123impl ConfigType for Config {
124 fn default() -> Self {
125 Config {
126 type_: PortabilizationType::QuantizationCoordinateWise,
127 quantization: Quantization::Bits(11),
128 }
129 }
130}
131
132impl Config {
133 pub fn oct_center(&self) -> i32 {
136 match self.type_ {
137 PortabilizationType::OctahedralQuantization => {
138 draco_oxide_core::codec::attribute::geom::oct_center(self.quantization.resolve(0.0))
139 }
140 _ => 0,
141 }
142 }
143
144 pub fn default_for(ty: AttributeType, component_ty: ComponentDataType) -> Self {
145 if component_ty.is_integer() {
146 return Config {
147 type_: PortabilizationType::ToBits,
148 quantization: Quantization::Bits(11), };
150 }
151 match ty {
152 AttributeType::Normal => Config {
153 type_: PortabilizationType::OctahedralQuantization,
154 quantization: Quantization::Bits(8),
155 },
156 AttributeType::TextureCoordinate => Config {
157 type_: PortabilizationType::QuantizationCoordinateWise,
158 quantization: Quantization::Bits(10),
159 },
160 _ => Self::default(),
161 }
162 }
163}
164
165#[derive(Clone, Copy, Debug, PartialEq)]
169pub enum Quantization {
170 Bits(u8),
172 MaxError(f32),
176 Bounded { range: f32, max_error: f32 },
181}
182
183impl Default for Quantization {
184 fn default() -> Self {
185 Quantization::Bits(11)
186 }
187}
188
189impl Quantization {
190 pub fn from_bounding_box(min: &[f32], max: &[f32], max_error: f32) -> Self {
193 let range = min
194 .iter()
195 .zip(max.iter())
196 .map(|(lo, hi)| hi - lo)
197 .fold(0.0_f32, f32::max);
198 Quantization::Bounded { range, max_error }
199 }
200
201 pub fn resolve(self, observed_range: f32) -> u8 {
205 let bits = match self {
206 Quantization::Bits(n) => n,
207 Quantization::MaxError(max_error) => bits_for_error(observed_range, max_error),
208 Quantization::Bounded { range, max_error } => bits_for_error(range, max_error),
209 };
210 bits.clamp(1, 30)
211 }
212}
213
214fn bits_for_error(range: f32, max_error: f32) -> u8 {
218 if range <= 0.0 || max_error <= 0.0 {
219 return 1;
220 }
221 let bits = (range / max_error + 1.0).log2().ceil();
222 bits.clamp(1.0, 30.0) as u8
223}