pub mod octahedral_quantization;
pub mod quantization_coordinate_wise;
pub mod to_bits;
use draco_oxide_core::attribute::{Attribute, AttributeType};
use draco_oxide_core::bit_coder::ByteWriter;
use draco_oxide_core::codec::attribute::Portable;
use draco_oxide_core::debug_write;
use draco_oxide_core::types::NdVector;
use draco_oxide_core::types::{ConfigType, Vector};
pub enum Portabilization<Data, const N: usize>
where
Data: Vector<N> + Portable,
{
QuantizationCoordinateWise(quantization_coordinate_wise::QuantizationCoordinateWise<Data, N>),
OctahedralQuantization(octahedral_quantization::OctahedralQuantization<Data, N>),
ToBits(to_bits::ToBits<Data, N>),
}
impl<Data, const N: usize> Portabilization<Data, N>
where
Data: Vector<N> + Portable,
NdVector<N, i32>: Vector<N, Component = i32>,
NdVector<N, f32>: Vector<N, Component = f32> + Portable,
{
pub fn new<W>(att: Attribute, cfg: Config, writer: &mut W) -> Self
where
W: ByteWriter,
{
debug_write!("Start of Portabilization Metadata", writer);
let out = match cfg.type_ {
PortabilizationType::QuantizationCoordinateWise => {
Portabilization::QuantizationCoordinateWise(
quantization_coordinate_wise::QuantizationCoordinateWise::<_, N>::new(
att, cfg, writer,
),
)
}
PortabilizationType::OctahedralQuantization => Portabilization::OctahedralQuantization(
octahedral_quantization::OctahedralQuantization::new(att, cfg, writer),
),
PortabilizationType::ToBits => {
Portabilization::ToBits(to_bits::ToBits::new(att, cfg, writer))
}
PortabilizationType::Integer => {
unimplemented!("Integer portabilization is not implemented yet.")
}
};
debug_write!("End of Portabilization Metadata", writer);
out
}
pub fn portabilize(self) -> Attribute {
match self {
Portabilization::QuantizationCoordinateWise(qcw) => qcw.portabilize(),
Portabilization::OctahedralQuantization(oct) => oct.portabilize(),
Portabilization::ToBits(tb) => tb.portabilize(),
}
}
}
pub trait PortabilizationImpl<const N: usize>
where
NdVector<N, i32>: Vector<N, Component = i32>,
{
fn portabilize(self) -> Attribute;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PortabilizationType {
QuantizationCoordinateWise,
OctahedralQuantization,
#[allow(dead_code)]
Integer,
ToBits,
}
impl PortabilizationType {
pub(crate) fn get_id(&self) -> u8 {
match self {
PortabilizationType::ToBits => 1,
PortabilizationType::Integer => 1, PortabilizationType::QuantizationCoordinateWise => 2,
PortabilizationType::OctahedralQuantization => 3,
}
}
pub(crate) fn write_to<W>(&self, writer: &mut W)
where
W: ByteWriter,
{
let id = self.get_id();
writer.write_u8(id);
}
pub(crate) fn default_for(ty: AttributeType) -> Self {
match ty {
AttributeType::Normal => PortabilizationType::OctahedralQuantization,
AttributeType::Custom => PortabilizationType::ToBits,
_ => PortabilizationType::QuantizationCoordinateWise, }
}
}
#[derive(Clone, Copy, Debug)]
pub struct Config {
pub type_: PortabilizationType,
pub quantization: Quantization,
}
impl ConfigType for Config {
fn default() -> Self {
Config {
type_: PortabilizationType::QuantizationCoordinateWise,
quantization: Quantization::Bits(11),
}
}
}
impl Config {
pub fn default_for(ty: AttributeType) -> Self {
match ty {
AttributeType::Normal => Config {
type_: PortabilizationType::OctahedralQuantization,
quantization: Quantization::Bits(8),
},
AttributeType::TextureCoordinate => Config {
type_: PortabilizationType::QuantizationCoordinateWise,
quantization: Quantization::Bits(10),
},
AttributeType::Custom => Config {
type_: PortabilizationType::ToBits,
quantization: Quantization::Bits(11), },
_ => Self::default(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Quantization {
Bits(u8),
MaxError(f32),
Bounded { range: f32, max_error: f32 },
}
impl Default for Quantization {
fn default() -> Self {
Quantization::Bits(11)
}
}
impl Quantization {
pub fn from_bounding_box(min: &[f32], max: &[f32], max_error: f32) -> Self {
let range = min
.iter()
.zip(max.iter())
.map(|(lo, hi)| hi - lo)
.fold(0.0_f32, f32::max);
Quantization::Bounded { range, max_error }
}
pub fn resolve(self, observed_range: f32) -> u8 {
let bits = match self {
Quantization::Bits(n) => n,
Quantization::MaxError(max_error) => bits_for_error(observed_range, max_error),
Quantization::Bounded { range, max_error } => bits_for_error(range, max_error),
};
bits.clamp(1, 30)
}
}
fn bits_for_error(range: f32, max_error: f32) -> u8 {
if range <= 0.0 || max_error <= 0.0 {
return 1;
}
let bits = (range / max_error + 1.0).log2().ceil();
bits.clamp(1.0, 30.0) as u8
}