use draco_core::decoder_buffer::DecoderBuffer;
use draco_core::draco_types::DataType;
use draco_core::encoder_buffer::EncoderBuffer;
use draco_core::encoder_options::EncoderOptions;
use draco_core::geometry_attribute::{GeometryAttributeType, PointAttribute};
use draco_core::geometry_indices::{FaceIndex, PointIndex};
use draco_core::mesh::Mesh;
use draco_core::mesh_decoder::MeshDecoder;
use draco_core::mesh_encoder::MeshEncoder;
fn create_grid_mesh(width: u32, height: u32) -> Mesh {
let mut mesh = Mesh::new();
let num_points = width * height;
mesh.set_num_points(num_points as usize);
let mut pos_attr = PointAttribute::new();
pos_attr.init(
GeometryAttributeType::Position,
3,
DataType::Float32,
false,
num_points as usize,
);
for y in 0..height {
for x in 0..width {
let i = y * width + x;
let coords = [x as f32, y as f32, 0.0f32];
let offset = (i as usize) * 3 * 4;
pos_attr
.buffer_mut()
.update(&coords[0].to_le_bytes(), Some(offset));
pos_attr
.buffer_mut()
.update(&coords[1].to_le_bytes(), Some(offset + 4));
pos_attr
.buffer_mut()
.update(&coords[2].to_le_bytes(), Some(offset + 8));
}
}
mesh.add_attribute(pos_attr);
let mut face_idx = 0;
for y in 0..height - 1 {
for x in 0..width - 1 {
let p0 = y * width + x;
let p1 = y * width + (x + 1);
let p2 = (y + 1) * width + x;
let p3 = (y + 1) * width + (x + 1);
mesh.set_face(
FaceIndex(face_idx),
[PointIndex(p0), PointIndex(p1), PointIndex(p2)],
);
face_idx += 1;
mesh.set_face(
FaceIndex(face_idx),
[PointIndex(p1), PointIndex(p3), PointIndex(p2)],
);
face_idx += 1;
}
}
mesh.set_num_faces(face_idx as usize);
mesh
}
fn verify_mesh_attributes(original: &Mesh, decoded: &Mesh, max_error: f32) {
assert!(
decoded.num_points() >= original.num_points(),
"Decoded points {} < Original points {}",
decoded.num_points(),
original.num_points()
);
let orig_attr = original.attribute(0);
let dec_attr = decoded.attribute(0);
let orig_data = orig_attr.buffer().data();
let dec_data = dec_attr.buffer().data();
let mut decoded_points = Vec::new();
println!("Decoded Points (total: {}):", decoded.num_points());
for i in 0..decoded.num_points() {
let offset = i * 3 * 4;
let dx = f32::from_le_bytes(dec_data[offset..offset + 4].try_into().unwrap());
let dy = f32::from_le_bytes(dec_data[offset + 4..offset + 8].try_into().unwrap());
let dz = f32::from_le_bytes(dec_data[offset + 8..offset + 12].try_into().unwrap());
decoded_points.push([dx, dy, dz]);
}
let min_x = decoded_points
.iter()
.map(|p| p[0])
.fold(f32::INFINITY, f32::min);
let max_x = decoded_points
.iter()
.map(|p| p[0])
.fold(f32::NEG_INFINITY, f32::max);
let min_y = decoded_points
.iter()
.map(|p| p[1])
.fold(f32::INFINITY, f32::min);
let max_y = decoded_points
.iter()
.map(|p| p[1])
.fold(f32::NEG_INFINITY, f32::max);
println!(
" Point range: x=[{:.3}, {:.3}], y=[{:.3}, {:.3}]",
min_x, max_x, min_y, max_y
);
println!("Decoded point values (first 20):");
for (i, p) in decoded_points.iter().enumerate().take(20) {
println!(" Point {}: ({:.3}, {:.3}, {:.3})", i, p[0], p[1], p[2]);
}
println!("Decoded faces (total: {}):", decoded.num_faces());
for i in 0..std::cmp::min(5, decoded.num_faces()) {
let face = decoded.face(FaceIndex(i as u32));
println!(" Face {}: {:?}", i, face);
}
fn round_f32_to_i32(v: f32) -> i32 {
v.round() as i32
}
for i in 0..original.num_points() {
let offset = i * 3 * 4;
let ox = f32::from_le_bytes(orig_data[offset..offset + 4].try_into().unwrap());
let oy = f32::from_le_bytes(orig_data[offset + 4..offset + 8].try_into().unwrap());
let oz = f32::from_le_bytes(orig_data[offset + 8..offset + 12].try_into().unwrap());
let rox = round_f32_to_i32(ox);
let roy = round_f32_to_i32(oy);
let roz = round_f32_to_i32(oz);
let mut found = false;
for dp in &decoded_points {
if round_f32_to_i32(dp[0]) == rox
&& round_f32_to_i32(dp[1]) == roy
&& round_f32_to_i32(dp[2]) == roz
{
found = true;
break;
}
if (ox - dp[0]).abs() <= max_error
&& (oy - dp[1]).abs() <= max_error
&& (oz - dp[2]).abs() <= max_error
{
found = true;
break;
}
}
assert!(
found,
"Point {} ({}, {}, {}) not found in decoded mesh",
i, ox, oy, oz
);
}
}
#[test]
fn test_grid_encoding_parallelogram() {
let mesh = create_grid_mesh(5, 5);
let mut options = EncoderOptions::default();
options.set_global_int("encoding_method", 1); options.set_global_int("encoding_speed", 5); options.set_attribute_int(0, "quantization_bits", 10);
let mut encoder = MeshEncoder::new();
encoder.set_mesh(mesh.clone());
let mut buffer = EncoderBuffer::new();
encoder
.encode(&options, &mut buffer)
.expect("Encode failed");
println!("Parallelogram encoded size: {}", buffer.data().len());
let mut decoder = MeshDecoder::new();
let mut decoded_mesh = Mesh::new();
let mut decoder_buffer = DecoderBuffer::new(buffer.data());
decoder
.decode(&mut decoder_buffer, &mut decoded_mesh)
.expect("Decode failed");
verify_mesh_attributes(&mesh, &decoded_mesh, 0.01);
}
#[test]
fn test_grid_encoding_difference() {
let mesh = create_grid_mesh(10, 10);
let mut options = EncoderOptions::default();
options.set_global_int("encoding_method", 1); options.set_global_int("encoding_speed", 10); options.set_attribute_int(0, "quantization_bits", 14);
let mut encoder = MeshEncoder::new();
encoder.set_mesh(mesh.clone());
let mut buffer = EncoderBuffer::new();
encoder
.encode(&options, &mut buffer)
.expect("Encode failed");
println!("Difference encoded size: {}", buffer.data().len());
let mut decoder = MeshDecoder::new();
let mut decoded_mesh = Mesh::new();
let mut decoder_buffer = DecoderBuffer::new(buffer.data());
decoder
.decode(&mut decoder_buffer, &mut decoded_mesh)
.expect("Decode failed");
verify_mesh_attributes(&mesh, &decoded_mesh, 0.01);
}
#[test]
fn test_quantization_levels() {
let mesh = create_grid_mesh(5, 5);
let q_levels = [8, 10, 16];
for &q in &q_levels {
let mut options = EncoderOptions::default();
options.set_global_int("encoding_method", 1);
options.set_attribute_int(0, "quantization_bits", q);
let mut encoder = MeshEncoder::new();
encoder.set_mesh(mesh.clone());
let mut buffer = EncoderBuffer::new();
encoder
.encode(&options, &mut buffer)
.expect("Encode failed");
let mut decoder = MeshDecoder::new();
let mut decoded_mesh = Mesh::new();
let mut decoder_buffer = DecoderBuffer::new(buffer.data());
decoder
.decode(&mut decoder_buffer, &mut decoded_mesh)
.expect("Decode failed");
let range = 4.0;
let max_error = range / ((1 << q) as f32 - 1.0);
verify_mesh_attributes(&mesh, &decoded_mesh, max_error * 1.5);
}
}
#[test]
fn test_grid_encoding_sequential() {
let mesh = create_grid_mesh(10, 10);
let mut options = EncoderOptions::default();
options.set_global_int("encoding_method", 0); options.set_global_int("encoding_speed", 5); options.set_attribute_int(0, "quantization_bits", 14);
let mut encoder = MeshEncoder::new();
encoder.set_mesh(mesh.clone());
let mut buffer = EncoderBuffer::new();
encoder
.encode(&options, &mut buffer)
.expect("Encode failed");
println!("Sequential encoded size: {}", buffer.data().len());
let mut decoder = MeshDecoder::new();
let mut decoded_mesh = Mesh::new();
let mut decoder_buffer = DecoderBuffer::new(buffer.data());
decoder
.decode(&mut decoder_buffer, &mut decoded_mesh)
.expect("Decode failed");
verify_mesh_attributes(&mesh, &decoded_mesh, 0.002);
}
#[test]
fn a_uint32_attribute_keeps_values_above_i32_max_through_a_round_trip() {
const WIDTH: u32 = 5;
const HEIGHT: u32 = 5;
const NUM_POINTS: usize = (WIDTH * HEIGHT) as usize;
const BASE: u32 = 0xFFFF_0000;
let mut mesh = Mesh::new();
mesh.set_num_points(NUM_POINTS);
let mut positions = PointAttribute::new();
positions.init(
GeometryAttributeType::Position,
3,
DataType::Uint32,
false,
NUM_POINTS,
);
let mut authored = Vec::with_capacity(NUM_POINTS * 3);
for y in 0..HEIGHT {
for x in 0..WIDTH {
let index = (y * WIDTH + x) as usize;
let value = if (x + y) % 2 == 0 {
[BASE + x * 16, BASE + y * 16, BASE + (x + y) * 4]
} else {
[x * 16, y * 16, (x + y) * 4]
};
for (component, scalar) in value.iter().enumerate() {
positions
.buffer_mut()
.write((index * 3 + component) * 4, &scalar.to_le_bytes());
}
authored.extend_from_slice(&value);
}
}
mesh.add_attribute(positions);
let mut tex_coords = PointAttribute::new();
tex_coords.init(
GeometryAttributeType::TexCoord,
2,
DataType::Uint16,
false,
NUM_POINTS,
);
for point in 0..NUM_POINTS {
let u = (point as u16).wrapping_mul(701);
let v = (point as u16).wrapping_mul(263);
tex_coords.buffer_mut().write(point * 4, &u.to_le_bytes());
tex_coords
.buffer_mut()
.write(point * 4 + 2, &v.to_le_bytes());
}
mesh.add_attribute(tex_coords);
let mut faces = Vec::new();
for y in 0..HEIGHT - 1 {
for x in 0..WIDTH - 1 {
let i = y * WIDTH + x;
faces.push([i, i + 1, i + WIDTH]);
faces.push([i + 1, i + WIDTH + 1, i + WIDTH]);
}
}
mesh.try_set_num_faces(faces.len()).expect("face count");
for (index, face) in faces.iter().enumerate() {
mesh.set_face_from_indices(index, *face);
}
let mut options = EncoderOptions::new();
options.set_attribute_int(1, "prediction_scheme", 5);
let mut encoder = MeshEncoder::new();
encoder.set_mesh(mesh);
let mut buffer = EncoderBuffer::new();
encoder
.encode(&options, &mut buffer)
.expect("a uint32 attribute above i32::MAX encodes");
let mut decoded = Mesh::new();
MeshDecoder::new()
.decode(&mut DecoderBuffer::new(buffer.data()), &mut decoded)
.expect("and decodes again");
let attribute = decoded
.named_attribute(GeometryAttributeType::Position)
.expect("decoded position attribute");
assert_eq!(attribute.data_type(), DataType::Uint32);
let mut decoded_values = Vec::with_capacity(NUM_POINTS);
for point in 0..NUM_POINTS {
let value_index = attribute.mapped_index(PointIndex(point as u32));
let mut triple = [0u32; 3];
for (component, scalar) in triple.iter_mut().enumerate() {
let mut bytes = [0u8; 4];
attribute.buffer().read(
value_index.0 as usize * attribute.byte_stride() as usize + component * 4,
&mut bytes,
);
*scalar = u32::from_le_bytes(bytes);
}
decoded_values.push(triple);
}
decoded_values.sort_unstable();
let mut authored_values: Vec<[u32; 3]> = authored.as_chunks::<3>().0.to_vec();
authored_values.sort_unstable();
assert_eq!(
decoded_values, authored_values,
"a uint32 value above i32::MAX did not survive the round trip"
);
assert!(
authored_values
.iter()
.flatten()
.any(|&v| v > i32::MAX as u32),
"the fixture has to carry values upstream would refuse, or it pins nothing"
);
}