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); } } }