use draco_core::decoder_buffer::DecoderBuffer;
use draco_core::encoder_buffer::EncoderBuffer;
use draco_core::encoder_options::EncoderOptions;
use draco_core::geometry_attribute::GeometryAttributeType;
use draco_core::mesh::Mesh;
use draco_core::mesh_decoder::MeshDecoder;
use draco_core::mesh_encoder::MeshEncoder;
use std::fs;
use std::path::PathBuf;
fn get_testdata_path() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.pop(); path.pop(); path.push("testdata");
path
}
fn decode_drc(data: &[u8]) -> Result<Mesh, draco_core::DracoError> {
let mut decoder = MeshDecoder::new();
let mut mesh = Mesh::new();
let mut buffer = DecoderBuffer::new(data);
decoder.decode(&mut buffer, &mut mesh)?;
Ok(mesh)
}
fn encode_mesh(
mesh: &Mesh,
speed: i32,
quantization_bits: i32,
) -> Result<Vec<u8>, draco_core::DracoError> {
let mut encoder = MeshEncoder::new();
encoder.set_mesh(mesh.clone());
let mut options = EncoderOptions::new();
options.set_global_int("encoding_method", 1); options.set_global_int("encoding_speed", speed);
options.set_global_int("decoding_speed", speed);
for i in 0..mesh.num_attributes() {
let att = mesh.attribute(i);
let bits = match att.attribute_type() {
GeometryAttributeType::Position => quantization_bits,
_ => 8,
};
options.set_attribute_int(i, "quantization_bits", bits);
}
let mut enc_buffer = EncoderBuffer::new();
encoder.encode(&options, &mut enc_buffer)?;
Ok(enc_buffer.data().to_vec())
}
fn extract_positions(mesh: &Mesh) -> Vec<[f32; 3]> {
let pos_att_id = mesh.named_attribute_id(GeometryAttributeType::Position);
if pos_att_id < 0 {
return Vec::new();
}
let pos_attr = mesh.attribute(pos_att_id);
let num_entries = pos_attr.size();
let buffer = pos_attr.buffer();
let byte_stride = pos_attr.byte_stride() as usize;
let mut positions = Vec::with_capacity(num_entries);
for i in 0..num_entries {
let offset = i * byte_stride;
let mut bytes = [0u8; 12];
if offset + 12 <= buffer.data_size() {
buffer.read(offset, &mut bytes);
let x = f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let y = f32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
let z = f32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
positions.push([x, y, z]);
}
}
positions
}
#[test]
fn compare_cpp_vs_rust_sphere_decode() {
let testdata = get_testdata_path();
println!("\n=== Comparing C++ vs Rust Decoder Results ===\n");
for speed in 0..=10 {
let cpp_path = testdata
.join("reference_cpp")
.join(format!("cpp_encoded_sphere_speed_{}.drc", speed));
if !cpp_path.exists() {
println!("Skipping speed {} - file not found: {:?}", speed, cpp_path);
continue;
}
let cpp_data = fs::read(&cpp_path).expect("Failed to read C++ encoded file");
println!("Speed {}: C++ file size = {} bytes", speed, cpp_data.len());
match decode_drc(&cpp_data) {
Ok(mesh) => {
println!(
" Decoded: {} vertices, {} faces",
mesh.num_points(),
mesh.num_faces()
);
let positions = extract_positions(&mesh);
println!(" First 3 vertices:");
for (i, p) in positions.iter().take(3).enumerate() {
println!(" v{}: [{:.6}, {:.6}, {:.6}]", i, p[0], p[1], p[2]);
}
}
Err(e) => {
println!(" DECODE ERROR: {:?}", e);
}
}
println!();
}
}
#[test]
fn compare_cpp_vs_rust_roundtrip_sphere() {
let testdata = get_testdata_path();
println!("\n=== Comparing C++ Decode vs Rust Encode-Decode ===\n");
let cpp_path = testdata
.join("reference_cpp")
.join("cpp_encoded_sphere_speed_5.drc");
if !cpp_path.exists() {
println!("C++ reference file not found, skipping test");
return;
}
let cpp_data = fs::read(&cpp_path).expect("Failed to read C++ file");
let cpp_mesh = decode_drc(&cpp_data).expect("Failed to decode C++ mesh");
println!(
"C++ reference mesh: {} vertices, {} faces",
cpp_mesh.num_points(),
cpp_mesh.num_faces()
);
let cpp_positions = extract_positions(&cpp_mesh);
for speed in 0..=10 {
match encode_mesh(&cpp_mesh, speed, 14) {
Ok(rust_encoded) => {
println!("Speed {}: Rust encoded {} bytes", speed, rust_encoded.len());
match decode_drc(&rust_encoded) {
Ok(rust_decoded) => {
let rust_positions = extract_positions(&rust_decoded);
let mut max_diff = 0.0f32;
let mut diff_count = 0;
let min_count = cpp_positions.len().min(rust_positions.len());
for i in 0..min_count {
for j in 0..3 {
let diff = (cpp_positions[i][j] - rust_positions[i][j]).abs();
if diff > 0.001 {
diff_count += 1;
if diff_count <= 5 {
println!(
" v{}.{}: C++={:.6} Rust={:.6} diff={:.6}",
i, j, cpp_positions[i][j], rust_positions[i][j], diff
);
}
}
max_diff = max_diff.max(diff);
}
}
if diff_count > 5 {
println!(" ... and {} more differences", diff_count - 5);
}
println!(" Max difference: {:.6}", max_diff);
}
Err(e) => println!(" Rust decode error: {:?}", e),
}
}
Err(e) => println!("Speed {}: Rust encode error: {:?}", speed, e),
}
println!();
}
}
#[test]
fn binary_compare_cpp_vs_rust_encoded() {
let testdata = get_testdata_path();
println!("\n=== Binary Comparison: C++ vs Rust Encoder Output ===\n");
let cpp_path = testdata
.join("reference_cpp")
.join("cpp_encoded_sphere_speed_5.drc");
if !cpp_path.exists() {
println!("C++ reference file not found, skipping test");
return;
}
let cpp_data = fs::read(&cpp_path).expect("Failed to read C++ file");
let mesh = decode_drc(&cpp_data).expect("Failed to decode C++ mesh");
println!(
"Reference mesh: {} vertices, {} faces\n",
mesh.num_points(),
mesh.num_faces()
);
for speed in [0, 1, 2, 5, 8, 10] {
let cpp_path = testdata
.join("reference_cpp")
.join(format!("cpp_encoded_sphere_speed_{}.drc", speed));
if !cpp_path.exists() {
continue;
}
let cpp_encoded = fs::read(&cpp_path).expect("Failed to read C++ file");
match encode_mesh(&mesh, speed, 14) {
Ok(rust_encoded) => {
println!("Speed {}:", speed);
println!(" C++ size: {} bytes", cpp_encoded.len());
println!(" Rust size: {} bytes", rust_encoded.len());
let min_len = cpp_encoded.len().min(rust_encoded.len());
let mut first_diff = None;
let mut diff_count = 0;
for i in 0..min_len {
if cpp_encoded[i] != rust_encoded[i] {
diff_count += 1;
if first_diff.is_none() {
first_diff = Some(i);
}
}
}
if let Some(pos) = first_diff {
println!(" First byte difference at position {}", pos);
println!(" Total different bytes: {} / {}", diff_count, min_len);
let start = pos.saturating_sub(4);
let end = (pos + 8).min(min_len);
print!(" C++ bytes [{}-{}]: ", start, end);
for i in start..end {
print!("{:02x} ", cpp_encoded[i]);
}
println!();
print!(" Rust bytes [{}-{}]: ", start, end);
for i in start..end {
print!("{:02x} ", rust_encoded[i]);
}
println!();
} else if cpp_encoded.len() == rust_encoded.len() {
println!(" IDENTICAL!");
} else {
println!(
" Same content for first {} bytes, but different lengths",
min_len
);
}
}
Err(e) => println!("Speed {}: Rust encode error: {:?}", speed, e),
}
println!();
}
}