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};
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 PortabilizationType::Integer => {
52 unimplemented!("Integer portabilization is not implemented yet.")
53 }
54 };
55 debug_write!("End of Portabilization Metadata", writer);
56 out
57 }
58
59 pub fn portabilize(self) -> Attribute {
60 match self {
61 Portabilization::QuantizationCoordinateWise(qcw) => qcw.portabilize(),
62 Portabilization::OctahedralQuantization(oct) => oct.portabilize(),
63 Portabilization::ToBits(tb) => tb.portabilize(),
64 }
65 }
66}
67
68pub trait PortabilizationImpl<const N: usize>
69where
70 NdVector<N, i32>: Vector<N, Component = i32>,
71{
72 fn portabilize(self) -> Attribute;
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77pub enum PortabilizationType {
78 QuantizationCoordinateWise,
79 OctahedralQuantization,
80 #[allow(dead_code)]
81 Integer,
82 ToBits,
83}
84
85impl PortabilizationType {
86 pub(crate) fn get_id(&self) -> u8 {
87 match self {
88 PortabilizationType::ToBits => 1,
89 PortabilizationType::Integer => 1, PortabilizationType::QuantizationCoordinateWise => 2,
91 PortabilizationType::OctahedralQuantization => 3,
92 }
93 }
94
95 pub(crate) fn write_to<W>(&self, writer: &mut W)
96 where
97 W: ByteWriter,
98 {
99 let id = self.get_id();
100 writer.write_u8(id);
101 }
102
103 pub(crate) fn default_for(ty: AttributeType) -> Self {
104 match ty {
105 AttributeType::Normal => PortabilizationType::OctahedralQuantization,
106 AttributeType::Custom => PortabilizationType::ToBits,
107 _ => PortabilizationType::QuantizationCoordinateWise, }
109 }
110}
111
112#[derive(Clone, Copy, Debug)]
113pub struct Config {
114 pub type_: PortabilizationType,
115 pub quantization: Quantization,
116}
117
118impl ConfigType for Config {
119 fn default() -> Self {
120 Config {
121 type_: PortabilizationType::QuantizationCoordinateWise,
122 quantization: Quantization::Bits(11),
123 }
124 }
125}
126
127impl Config {
128 pub fn default_for(ty: AttributeType) -> Self {
129 match ty {
130 AttributeType::Normal => Config {
131 type_: PortabilizationType::OctahedralQuantization,
132 quantization: Quantization::Bits(8),
133 },
134 AttributeType::TextureCoordinate => Config {
135 type_: PortabilizationType::QuantizationCoordinateWise,
136 quantization: Quantization::Bits(10),
137 },
138 AttributeType::Custom => Config {
139 type_: PortabilizationType::ToBits,
140 quantization: Quantization::Bits(11), },
142 _ => Self::default(),
143 }
144 }
145}
146
147#[derive(Clone, Copy, Debug, PartialEq)]
151pub enum Quantization {
152 Bits(u8),
154 MaxError(f32),
158 Bounded { range: f32, max_error: f32 },
163}
164
165impl Default for Quantization {
166 fn default() -> Self {
167 Quantization::Bits(11)
168 }
169}
170
171impl Quantization {
172 pub fn from_bounding_box(min: &[f32], max: &[f32], max_error: f32) -> Self {
175 let range = min
176 .iter()
177 .zip(max.iter())
178 .map(|(lo, hi)| hi - lo)
179 .fold(0.0_f32, f32::max);
180 Quantization::Bounded { range, max_error }
181 }
182
183 pub fn resolve(self, observed_range: f32) -> u8 {
187 let bits = match self {
188 Quantization::Bits(n) => n,
189 Quantization::MaxError(max_error) => bits_for_error(observed_range, max_error),
190 Quantization::Bounded { range, max_error } => bits_for_error(range, max_error),
191 };
192 bits.clamp(1, 30)
193 }
194}
195
196fn bits_for_error(range: f32, max_error: f32) -> u8 {
200 if range <= 0.0 || max_error <= 0.0 {
201 return 1;
202 }
203 let bits = (range / max_error + 1.0).log2().ceil();
204 bits.clamp(1.0, 30.0) as u8
205}