use anyhow::Result;
use std::io::Read;
use super::prp::PlasmaRead;
const K_POS_QUANTUM: f32 = 1.0 / (1 << 10) as f32;
const K_WEIGHT_QUANTUM: f32 = 1.0 / (1 << 15) as f32;
const K_UVW_QUANTUM: f32 = 1.0 / (1u32 << 16) as f32;
const K_POSITION: usize = 0;
const K_WEIGHT: usize = 1;
const K_UVW: usize = 2;
const K_NUM_FLOAT_FIELDS: usize = K_UVW + 8;
const K_QUANTA: [f32; K_NUM_FLOAT_FIELDS] = [
K_POS_QUANTUM,
K_WEIGHT_QUANTUM,
K_UVW_QUANTUM, K_UVW_QUANTUM, K_UVW_QUANTUM, K_UVW_QUANTUM,
K_UVW_QUANTUM, K_UVW_QUANTUM, K_UVW_QUANTUM, K_UVW_QUANTUM,
];
const K_SAME_MASK: u16 = 0x8000;
const K_UV_COUNT_MASK: u8 = 0x0f;
const K_SKIN_WEIGHT_MASK: u8 = 0x30;
const K_SKIN_INDICES: u8 = 0x40;
struct FloatCode {
offset: f32,
all_same: bool,
count: u16,
}
struct ByteCode {
count: u16,
val: u8,
same: bool,
}
pub struct VertDecoder {
floats: [[FloatCode; 3]; K_NUM_FLOAT_FIELDS],
colors: [ByteCode; 4],
}
impl VertDecoder {
pub fn new() -> Self {
Self {
floats: std::array::from_fn(|_| std::array::from_fn(|_| FloatCode {
offset: 0.0, all_same: false, count: 0,
})),
colors: std::array::from_fn(|_| ByteCode {
count: 0, val: 0, same: false,
}),
}
}
fn i_read_float(reader: &mut impl Read, dst: &mut [u8], pos: &mut usize,
offset: f32, quantum: f32) -> Result<()> {
let ival = reader.read_u16()?;
let fval = ival as f32 * quantum + offset;
dst[*pos..*pos + 4].copy_from_slice(&fval.to_le_bytes());
*pos += 4;
Ok(())
}
fn i_decode_float(&mut self, reader: &mut impl Read, field: usize, chan: usize,
dst: &mut [u8], pos: &mut usize) -> Result<()> {
if self.floats[field][chan].count == 0 {
self.floats[field][chan].offset = reader.read_f32()?;
self.floats[field][chan].all_same = reader.read_u8()? != 0;
self.floats[field][chan].count = reader.read_u16()?;
}
if !self.floats[field][chan].all_same {
Self::i_read_float(reader, dst, pos, self.floats[field][chan].offset, K_QUANTA[field])?;
} else {
let bytes = self.floats[field][chan].offset.to_le_bytes();
dst[*pos..*pos + 4].copy_from_slice(&bytes);
*pos += 4;
}
self.floats[field][chan].count -= 1;
Ok(())
}
fn i_decode_normal(reader: &mut impl Read, dst: &mut [u8], pos: &mut usize) -> Result<()> {
for _ in 0..3 {
let ix = reader.read_u8()?;
let x = (ix as f32 / 255.9 - 0.5) * 2.0;
dst[*pos..*pos + 4].copy_from_slice(&x.to_le_bytes());
*pos += 4;
}
Ok(())
}
fn i_decode_byte(&mut self, reader: &mut impl Read, chan: usize,
dst: &mut [u8], pos: &mut usize) -> Result<()> {
if self.colors[chan].count == 0 {
let cnt = reader.read_u16()?;
if cnt & K_SAME_MASK != 0 {
self.colors[chan].same = true;
self.colors[chan].val = reader.read_u8()?;
self.colors[chan].count = cnt & !K_SAME_MASK;
} else {
self.colors[chan].same = false;
self.colors[chan].count = cnt;
}
}
if !self.colors[chan].same {
dst[*pos] = reader.read_u8()?;
} else {
dst[*pos] = self.colors[chan].val;
}
*pos += 1;
self.colors[chan].count -= 1;
Ok(())
}
fn i_decode_color(&mut self, reader: &mut impl Read,
dst: &mut [u8], pos: &mut usize) -> Result<()> {
self.i_decode_byte(reader, 0, dst, pos)?;
self.i_decode_byte(reader, 1, dst, pos)?;
self.i_decode_byte(reader, 2, dst, pos)?;
self.i_decode_byte(reader, 3, dst, pos)?;
Ok(())
}
fn i_num_weights(format: u8) -> usize {
((format & K_SKIN_WEIGHT_MASK) >> 4) as usize
}
fn i_decode(&mut self, reader: &mut impl Read, dst: &mut [u8], pos: &mut usize,
format: u8) -> Result<()> {
self.i_decode_float(reader, K_POSITION, 0, dst, pos)?;
self.i_decode_float(reader, K_POSITION, 1, dst, pos)?;
self.i_decode_float(reader, K_POSITION, 2, dst, pos)?;
let num_weights = Self::i_num_weights(format);
for j in 0..num_weights {
self.i_decode_float(reader, K_WEIGHT, j, dst, pos)?;
}
if format & K_SKIN_INDICES != 0 {
let idx = reader.read_u32()?;
dst[*pos..*pos + 4].copy_from_slice(&idx.to_le_bytes());
*pos += 4;
}
Self::i_decode_normal(reader, dst, pos)?;
self.i_decode_color(reader, dst, pos)?;
dst[*pos..*pos + 4].copy_from_slice(&[0u8; 4]);
*pos += 4;
let num_uvws = (format & K_UV_COUNT_MASK) as usize;
for i in 0..num_uvws {
self.i_decode_float(reader, K_UVW + i, 0, dst, pos)?;
self.i_decode_float(reader, K_UVW + i, 1, dst, pos)?;
self.i_decode_float(reader, K_UVW + i, 2, dst, pos)?;
}
Ok(())
}
pub fn read(&mut self, reader: &mut impl Read, format: u8, stride: usize,
num_verts: u16) -> Result<Vec<u8>> {
let total_size = num_verts as usize * stride;
let mut data = vec![0u8; total_size];
for i in 0..num_verts as usize {
let mut pos = i * stride;
if let Err(e) = self.i_decode(reader, &mut data, &mut pos, format) {
if i == 0 {
return Err(e);
}
log::warn!("Vertex decode failed at vertex {}/{}: {}, returning partial data",
i, num_verts, e);
data.truncate(i * stride);
return Ok(data);
}
}
Ok(data)
}
}
pub fn calc_vertex_stride(format: u8) -> usize {
let mut size: usize = std::mem::size_of::<f32>() * (3 + 3);
let num_uvs = (format & K_UV_COUNT_MASK) as usize;
size += std::mem::size_of::<f32>() * 3 * num_uvs;
let num_weights = ((format & K_SKIN_WEIGHT_MASK) >> 4) as usize;
if num_weights > 0 {
size += std::mem::size_of::<f32>() * num_weights;
if format & K_SKIN_INDICES != 0 {
size += std::mem::size_of::<u32>();
}
}
size += std::mem::size_of::<u32>() * 2;
size
}