Files
Chili Palmer 611db567a0
Some checks failed
Native code generation / deterministic (push) Failing after 2m7s
Imaging and meshing gate / native (push) Failing after 4m9s
JPEG 2000 feature / linux (push) Successful in 2m55s
Native Rust workspace compile / compile (push) Failing after 6m15s
Skia feature / linux (push) Successful in 30m59s
Implement native SimpleRenderer reference pipeline (#76)
2026-08-10 21:50:59 +00:00

118 lines
4.2 KiB
Rust

use libremetaverse::rendering::{DetailLevel, SimpleMesh};
use libremetaverse::{
Primitive, PrimitiveConstructionData, PrimitiveSculptData, PrimitiveTextureEntry,
};
use libremetaverse_imaging::{ManagedImage, ManagedImageImageChannels};
use libremetaverse_rendering_simple::SimpleRenderer;
use libremetaverse_types::{HoleType, PCode, PathCurve, ProfileCurve, SculptType, UUID};
fn primitive(profile: ProfileCurve, path: PathCurve) -> Primitive {
let mut primitive = Primitive::new_with_constructor().expect("Primitive constructor");
let mut data =
PrimitiveConstructionData::new_with_constructor().expect("ConstructionData constructor");
data.p_code = PCode::Prim;
data.path_curve = path;
data.set_profile_curve_with_property(profile);
data.path_scale_x = 0.8;
data.path_scale_y = 0.7;
data.path_begin = 0.05;
data.path_end = 0.95;
data.profile_begin = 0.03;
data.profile_end = 0.97;
data.profile_hollow = 0.15;
data.set_profile_hole(HoleType::Circle);
data.path_shear_x = 0.1;
data.path_shear_y = -0.1;
data.path_revolutions = 1.25;
data.path_twist = 0.1;
primitive.prim_data = data;
primitive.textures = Some(
PrimitiveTextureEntry::new_with_uuid(UUID::zero()).expect("texture entry constructor"),
);
primitive
}
fn assert_valid(mesh: &SimpleMesh) {
assert!(!mesh.vertices.is_empty());
assert!(!mesh.indices.is_empty());
assert_eq!(mesh.indices.len() % 3, 0);
for index in &mesh.indices {
assert!(usize::from(*index) < mesh.vertices.len());
}
for vertex in &mesh.vertices {
assert!(vertex.position.x.is_finite());
assert!(vertex.position.y.is_finite());
assert!(vertex.position.z.is_finite());
assert!(vertex.normal.x.is_finite());
assert!(vertex.normal.y.is_finite());
assert!(vertex.normal.z.is_finite());
assert!(vertex.tex_coord.x.is_finite());
assert!(vertex.tex_coord.y.is_finite());
}
}
fn sculpt_image() -> ManagedImage {
let mut image = ManagedImage::new(32, 32, ManagedImageImageChannels::COLOR)
.expect("sculpt image constructor");
for (index, red) in image.red.iter_mut().enumerate() {
*red = u8::try_from(index % 256).expect("bounded sample");
image.green[index] = u8::try_from((index * 3) % 256).expect("bounded sample");
image.blue[index] = u8::try_from((index * 7) % 256).expect("bounded sample");
}
image
}
#[test]
fn every_profile_path_and_lod_produces_checked_geometry() {
let renderer = SimpleRenderer::new().expect("renderer");
for profile in [
ProfileCurve::Circle,
ProfileCurve::EqualTriangle,
ProfileCurve::HalfCircle,
ProfileCurve::IsoTriangle,
ProfileCurve::RightTriangle,
ProfileCurve::Square,
] {
for path in [PathCurve::Flexible, PathCurve::Line, PathCurve::Circle] {
for lod in [
DetailLevel::Low,
DetailLevel::Medium,
DetailLevel::High,
DetailLevel::Highest,
] {
let mesh = renderer
.generate_simple_mesh(primitive(profile, path), lod)
.expect("supported prim topology");
assert_valid(&mesh);
}
}
}
}
#[test]
fn every_sculpt_topology_produces_checked_geometry_at_each_lod() {
let renderer = SimpleRenderer::new().expect("renderer");
for sculpt_type in [
SculptType::Plane,
SculptType::Cylinder,
SculptType::Sphere,
SculptType::Torus,
] {
for lod in [
DetailLevel::Low,
DetailLevel::Medium,
DetailLevel::High,
DetailLevel::Highest,
] {
let mut prim = primitive(ProfileCurve::Square, PathCurve::Line);
let mut sculpt = PrimitiveSculptData::new_with_constructor().expect("sculpt metadata");
sculpt.set_type_(sculpt_type);
prim.sculpt = Some(sculpt);
let mesh = renderer
.generate_simple_sculpt_mesh(prim, sculpt_image(), lod)
.expect("supported sculpt topology");
assert_valid(&mesh);
}
}
}