#![allow(clippy::float_cmp, clippy::too_many_lines)] // Exact BSD-3-Clause fixture provenance at LibreMetaverse // 2aa70bb68513b39795da5d13c88f31b86e85a3ba (see LICENSE.md): // AnimationTrackTests.cs 9f83830d4278906e4edf8d5ad71d59f96e63ae5860e3b3cc9e191de05741f3d0 // AnimeshPlayerTests.cs b79a4f667c73c30376ee6f550436b26e76efd0fa0383d67bc388a74f07f679ce // AnimeshSkinningTests.cs 72f51e5dc4fa59e87c150aacb93aae26aabad71fb45e1830cd46e7c5216e1266 // AvatarManagerTests.cs f7547d5caed1d4669eccfad57836f4d2243c8275b1d182e7cb363660b3eabb68 // LindenSkeletonTests.cs 23f7e3ffaf8c4422e8dc2e42ed4223b16175228153aba8eb69cd7ef7e2037b76 use crate::animesh::{AnimationTrack, AnimeshPlayer, JointPose}; use crate::rendering::{ Face, FaceMask, Joint, JointSupportCategory, LindenSkeleton, MeshSkinData, Vertex, VertexWeight, }; use crate::{AnimeshSkinning, BinBVHAnimationReader, GridClient, PrimitiveTextureEntryFace}; use libremetaverse_types::compat::Object; use libremetaverse_types::{Error, Matrix4, UUID, Vector2, Vector3}; use std::collections::{HashMap, HashSet}; use std::sync::Arc; fn random_uuid() -> UUID { UUID::random().expect("UUID.Random") } fn vector3(x: f32, y: f32, z: f32) -> Vector3 { Vector3::new_with_single_single_single(x, y, z).expect("Vector3 constructor") } fn assert_close(actual: f32, expected: f32, tolerance: f32) { assert!( (actual - expected).abs() <= tolerance, "expected {expected} +/- {tolerance}, got {actual}" ); } fn make_animation( in_point: f32, out_point: f32, loop_: bool, ease_in: f32, ease_out: f32, ) -> BinBVHAnimationReader { let mut data = Vec::new(); data.extend_from_slice(&1_u16.to_le_bytes()); data.extend_from_slice(&0_u16.to_le_bytes()); data.extend_from_slice(&2_i32.to_le_bytes()); data.extend_from_slice(&(out_point - in_point).to_le_bytes()); data.push(0); data.extend_from_slice(&in_point.to_le_bytes()); data.extend_from_slice(&out_point.to_le_bytes()); data.extend_from_slice(&i32::from(loop_).to_le_bytes()); data.extend_from_slice(&ease_in.to_le_bytes()); data.extend_from_slice(&ease_out.to_le_bytes()); data.extend_from_slice(&0_u32.to_le_bytes()); data.extend_from_slice(&0_u32.to_le_bytes()); BinBVHAnimationReader::new(data).expect("BinBVHAnimationReader constructor") } fn make_track( in_point: f32, out_point: f32, loop_: bool, ease_in: f32, ease_out: f32, ) -> AnimationTrack { let mut track = AnimationTrack::new(random_uuid()).expect("AnimationTrack constructor"); track.set_data(Some(make_animation( in_point, out_point, loop_, ease_in, ease_out, ))); track } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.DecodeRotation_ZeroVector_ReturnsIdentity::test fd67db1a9689e3e624fbe8bea98d6278774e4d67f85e611aac6f76105240a6b1 translated #[test] fn animation_track_decode_rotation_zero_vector_returns_identity() { let quaternion = AnimationTrack::decode_rotation(Vector3::zero()).expect("DecodeRotation"); assert_close(quaternion.w, 1.0, 1.0e-5); assert_close(quaternion.x, 0.0, 1.0e-5); assert_close(quaternion.y, 0.0, 1.0e-5); assert_close(quaternion.z, 0.0, 1.0e-5); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.DecodeRotation_Result_IsNormalized::test 880fdfb75fd9318afa21d3b5296790b3ebad944b43a998a924691fa74efbb2fe translated #[test] fn animation_track_decode_rotation_result_is_normalized() { let vectors = [ vector3(0.1, 0.2, 0.3), vector3(0.5, 0.0, 0.0), vector3(0.0, 0.0, 0.99), Vector3::zero(), ]; for value in vectors { let quaternion = AnimationTrack::decode_rotation(value).expect("DecodeRotation"); let magnitude = (quaternion.x * quaternion.x + quaternion.y * quaternion.y + quaternion.z * quaternion.z + quaternion.w * quaternion.w) .sqrt(); assert_close(magnitude, 1.0, 1.0e-5); } } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.DecodeRotation_WIsRecoveredFromXyz::test 738fd51ea71da85b3b2d43ec30b8e9883b5364c9cbd7c3cbe87de3ea6778daa8 translated #[test] fn animation_track_decode_rotation_w_is_recovered_from_xyz() { let quaternion = AnimationTrack::decode_rotation(vector3(0.6, 0.0, 0.0)).expect("DecodeRotation"); assert_close(quaternion.x, 0.6, 1.0e-5); assert_close(quaternion.w, 0.8, 1.0e-5); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.DecodeRotation_WAlwaysNonNegative::test 679c12e5cdaac7a970f15606229b1843d82e68b28883f87cc5d73567b04c9b57 translated #[test] fn animation_track_decode_rotation_w_always_non_negative() { for value in [ vector3(0.5, 0.5, 0.5), vector3(-0.5, -0.5, -0.5), vector3(0.0, 0.0, 0.9), ] { let quaternion = AnimationTrack::decode_rotation(value).expect("DecodeRotation"); assert!(quaternion.w >= 0.0); } } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.Advance_WithNullData_DoesNotChangeCurrentTime::test cfd6f6ed1f568fe0dda207cc3222c487a3f8b509a5aa0743109c04032a29b808 translated #[test] fn animation_track_advance_with_null_data_does_not_change_current_time() { let mut track = AnimationTrack::new(random_uuid()).expect("AnimationTrack constructor"); track.advance(1.0).expect("Advance"); assert_eq!(track.current_time(), 0.0); assert!(!track.is_finished()); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.EaseWeight_WithNullData_ReturnsZero::test 7e74e5323ca9870782821a6985f446030d1a8858182bdf82d7718617eacd8ef5 translated #[test] fn animation_track_ease_weight_with_null_data_returns_zero() { let track = AnimationTrack::new(random_uuid()).expect("AnimationTrack constructor"); assert_eq!(track.ease_weight(), 0.0); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.EvaluatePose_WithNullData_LeavesEmptyDictionaryUnchanged::test 287210ac2f57c80bd77688a97720b0a7ca63118ce3c8da591274b53aeef88769 translated #[test] fn animation_track_evaluate_pose_with_null_data_leaves_empty_dictionary_unchanged() { let track = AnimationTrack::new(random_uuid()).expect("AnimationTrack constructor"); let mut pose = HashMap::::new(); track.evaluate_pose(&mut pose).expect("EvaluatePose"); assert!(pose.is_empty()); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.Advance_NonLooping_BeforeOutPoint_AdvancesTime::test da6ab7efced5ff86e83c320685d6fef33df2a2eb62795206b9f78e87a51ca93d translated #[test] fn animation_track_advance_non_looping_before_out_point_advances_time() { let mut track = make_track(0.0, 10.0, false, 0.0, 0.0); track.advance(3.0).expect("Advance"); assert_close(track.current_time(), 3.0, 1.0e-5); assert!(!track.is_finished()); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.Advance_NonLooping_PastOutPoint_ClampsAndSetsFinished::test 02acfe2c10a27a6222e8d222de47255c68afc46ba97cc9319f8239af021fb365 translated #[test] fn animation_track_advance_non_looping_past_out_point_clamps_and_sets_finished() { let mut track = make_track(0.0, 5.0, false, 0.0, 0.0); track.advance(10.0).expect("Advance"); assert_close(track.current_time(), 5.0, 1.0e-5); assert!(track.is_finished()); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.Advance_Looping_WrapsAroundToInPoint::test e5e29dec8ece4c3ed19691aa0da0b0199de14902184a91548451c40390af4f8e translated #[test] fn animation_track_advance_looping_wraps_around_to_in_point() { let mut track = make_track(1.0, 4.0, true, 0.0, 0.0); track.advance(6.0).expect("Advance"); assert_close(track.current_time(), 3.0, 1.0e-4); assert!(!track.is_finished()); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.Advance_Finished_DoesNotAdvanceFurther::test 17680a41433f4f274645b15f49c4b4f7efea42af4542ee3f6c2e16aa8614f994 translated #[test] fn animation_track_advance_finished_does_not_advance_further() { let mut track = make_track(0.0, 5.0, false, 0.0, 0.0); track.advance(10.0).expect("Advance"); let stopped_at = track.current_time(); track.advance(5.0).expect("Advance"); assert_eq!(track.current_time(), stopped_at); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.EaseWeight_InMiddle_IsOne::test 49a02b2910e60254f80760d40b62b41fc02a4747d85a7b41ee8d082d0352d3cf translated #[test] fn animation_track_ease_weight_in_middle_is_one() { let mut track = make_track(0.0, 10.0, false, 1.0, 1.0); track.advance(5.0).expect("Advance"); assert_close(track.ease_weight(), 1.0, 1.0e-5); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.EaseWeight_DuringEaseIn_IsFractional::test 419c26af8c1a989a7f99d1b32ca28a41768d7171a4452466f909e1fe61cea186 translated #[test] fn animation_track_ease_weight_during_ease_in_is_fractional() { let mut track = make_track(0.0, 10.0, false, 2.0, 0.0); track.advance(1.0).expect("Advance"); assert_close(track.ease_weight(), 0.5, 1.0e-5); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.EaseWeight_DuringEaseOut_DecreasesTowardZero::test 18fd883c525f528f0ead69d3df187b106980ff939091c3ad56b156c2d05c91b0 translated #[test] fn animation_track_ease_weight_during_ease_out_decreases_toward_zero() { let mut track = make_track(0.0, 10.0, false, 0.0, 4.0); track.advance(8.0).expect("Advance"); assert_close(track.ease_weight(), 0.5, 1.0e-5); } // parity-case: LibreMetaverse.Tests/AnimationTrackTests.cs::AnimationTrackTests.EaseWeight_NoEaseCurves_IsAlwaysOne::test 49421472fcd3dcdb56cb9bb2448b2808bf76757454a67f2ced7524d9a6f89d6e translated #[test] fn animation_track_ease_weight_no_ease_curves_is_always_one() { let mut track = make_track(0.0, 10.0, false, 0.0, 0.0); track.advance(5.0).expect("Advance"); assert_eq!(track.ease_weight(), 1.0); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.GetOrAddTrack_CreatesNewTrack::test b7fcdc9f7c02167a6307facaf720390298d68e9c909792d4feab3b78d9b87d7f translated #[test] fn animesh_player_get_or_add_track_creates_new_track() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); let id = random_uuid(); let track = player.get_or_add_track(id).expect("GetOrAddTrack"); assert!(track.animation_id().equals_with_uuid(id)); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.GetOrAddTrack_SameId_ReturnsSameTrack::test aaec517ee073f26f6ed324cb4391a945221ff0349348bba3757b2378b38df610 translated #[test] fn animesh_player_get_or_add_track_same_id_returns_same_track() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); let id = random_uuid(); let first = player.get_or_add_track(id).expect("GetOrAddTrack"); let second = player.get_or_add_track(id).expect("GetOrAddTrack"); assert!(Arc::ptr_eq(&first, &second)); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.GetOrAddTrack_DifferentIds_ReturnDifferentTracks::test fe47b6dabc58df9078751501e3bb7e65b0aeb4c0929f4523aded0cb624d64bb1 translated #[test] fn animesh_player_get_or_add_track_different_ids_return_different_tracks() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); let first = player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); let second = player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); assert!(!Arc::ptr_eq(&first, &second)); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.TrackCount_IncreasesWithNewTracks::test 241b4b3606c0d5c8625701bd21ec41438ac9a410eb11620d1609ddd4d2d58dda translated #[test] fn animesh_player_track_count_increases_with_new_tracks() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); assert_eq!(player.track_count(), 2); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.TrackCount_StableForDuplicateId::test b36dfe47c90dd24fe489ae408cfb299f7214eddb3dc9d5ba42830ab9950d9268 translated #[test] fn animesh_player_track_count_stable_for_duplicate_id() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); let id = random_uuid(); player.get_or_add_track(id).expect("GetOrAddTrack"); player.get_or_add_track(id).expect("GetOrAddTrack"); assert_eq!(player.track_count(), 1); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.RetainOnly_RemovesAbsentIds::test d4f15706802d59901e8f07fc17f1d11063b6c182a7a40833d988191edd556f46 translated #[test] fn animesh_player_retain_only_removes_absent_ids() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); let keep = random_uuid(); let drop = random_uuid(); player.get_or_add_track(keep).expect("GetOrAddTrack"); player.get_or_add_track(drop).expect("GetOrAddTrack"); player .retain_only(HashSet::from([keep])) .expect("RetainOnly"); assert_eq!(player.track_count(), 1); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.RetainOnly_KeepsPresentIds::test eb568b72c8930dea6678a4b54ff2dd47101cef0ed8f6380875cb3fdd27ec6ad2 translated #[test] fn animesh_player_retain_only_keeps_present_ids() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); let id = random_uuid(); let original = player.get_or_add_track(id).expect("GetOrAddTrack"); player.retain_only(HashSet::from([id])).expect("RetainOnly"); let retained = player.get_or_add_track(id).expect("GetOrAddTrack"); assert!(Arc::ptr_eq(&original, &retained)); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.RetainOnly_EmptySet_RemovesAllTracks::test 8a8ee666933d4bd920c1d239f5180b9b4ee0d6eb7be67a6965fe29ca6f22edff translated #[test] fn animesh_player_retain_only_empty_set_removes_all_tracks() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); player.retain_only(HashSet::new()).expect("RetainOnly"); assert_eq!(player.track_count(), 0); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.Update_WithNoTracks_DoesNotThrow::test 866542d8e7e14a1f52a4f6b79aa8284cc5347d1a78a44ed9b4950b67078ff592 translated #[test] fn animesh_player_update_with_no_tracks_does_not_throw() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); player.update(0.016).expect("Update"); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.Update_WithTracksButNoData_DoesNotThrow::test b29bcf3676a3fca8d1e352ff672350a7bcb92a101dd6f622c53ee2b9a64a3536 translated #[test] fn animesh_player_update_with_tracks_but_no_data_does_not_throw() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); player.update(0.016).expect("Update"); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.EvaluatePose_NoLoadedData_ReturnsEmptyDictionary::test 3427b5c94f64a7465a8f3d4d13a1b67e2d002df81a4e736398e1934c8b08cb4b translated #[test] fn animesh_player_evaluate_pose_no_loaded_data_returns_empty_dictionary() { let mut player = AnimeshPlayer::new(random_uuid()).expect("AnimeshPlayer constructor"); player .get_or_add_track(random_uuid()) .expect("GetOrAddTrack"); let pose = player.evaluate_pose().expect("EvaluatePose"); assert!(pose.is_empty()); } // parity-case: LibreMetaverse.Tests/AnimeshPlayerTests.cs::AnimeshPlayerTests.ObjectID_StoresConstructorValue::test f2a2efabccbf63c110336cb5785487eb65ee7347e1ff0bb499295e4d5e255654 translated #[test] fn animesh_player_object_id_stores_constructor_value() { let id = random_uuid(); let player = AnimeshPlayer::new(id).expect("AnimeshPlayer constructor"); assert!(player.object_id().equals_with_uuid(id)); } fn make_joint(name: &str, children: Vec) -> Joint { let mut joint = Joint::new().expect("Joint constructor"); joint.base.set_name(name.to_owned()); joint.set_bone(Some(children)); joint } fn make_skeleton(root_name: &str, root_children: Vec) -> LindenSkeleton { let mut skeleton = LindenSkeleton::new().expect("LindenSkeleton constructor"); skeleton.set_bone(make_joint(root_name, root_children)); skeleton } fn make_single_joint_skeleton(joint_name: &str) -> LindenSkeleton { let mut joint = Joint::new().expect("Joint constructor"); joint.base.set_name(joint_name.to_owned()); joint.base.set_pos(vec![0.0, 0.0, 0.0]); joint.base.set_rot(vec![0.0, 0.0, 0.0]); let mut skeleton = LindenSkeleton::new().expect("LindenSkeleton constructor"); skeleton.set_bone(joint); skeleton } fn identity_floats() -> Vec { vec![ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ] } fn make_skin_data(joint_name: &str) -> MeshSkinData { let mut skin_data = MeshSkinData::new().expect("MeshSkinData constructor"); skin_data.joint_names = vec![joint_name.to_owned()]; skin_data.inverse_bind_matrices = identity_floats(); skin_data.bind_shape_matrix = identity_floats(); skin_data } fn assert_matrix_identity(matrix: Matrix4, tolerance: f32) { let identity = Matrix4::identity(); assert_close(matrix.m11, identity.m11, tolerance); assert_close(matrix.m12, identity.m12, tolerance); assert_close(matrix.m13, identity.m13, tolerance); assert_close(matrix.m14, identity.m14, tolerance); assert_close(matrix.m21, identity.m21, tolerance); assert_close(matrix.m22, identity.m22, tolerance); assert_close(matrix.m23, identity.m23, tolerance); assert_close(matrix.m24, identity.m24, tolerance); assert_close(matrix.m31, identity.m31, tolerance); assert_close(matrix.m32, identity.m32, tolerance); assert_close(matrix.m33, identity.m33, tolerance); assert_close(matrix.m34, identity.m34, tolerance); assert_close(matrix.m41, identity.m41, tolerance); assert_close(matrix.m42, identity.m42, tolerance); assert_close(matrix.m43, identity.m43, tolerance); assert_close(matrix.m44, identity.m44, tolerance); } fn vertex(position: Vector3) -> Vertex { Vertex { position, normal: Vector3::zero(), tex_coord: Vector2::zero(), } } fn face(vertices: Vec, weights: Option>) -> Face { Face { begin_s: 0, begin_t: 0, center: Vector3::zero(), edge: Vec::new(), id: 0, indices: Vec::new(), mask: FaceMask(0), max_extent: Vector3::zero(), min_extent: Vector3::zero(), normalized_scale: Vector3::zero(), num_s: 0, num_t: 0, tex_coords1: None, texture_face: PrimitiveTextureEntryFace::default(), user_data: Object::from(""), vertices, weights, } } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.ComputeSkinningMatrices_NullPose_ThrowsArgumentNullException::test 877768309d784145e02873759fa3726bc2ca6eb4b9d35b0974b98d3c0a06b97a translated #[test] fn animesh_skinning_compute_matrices_null_pose_throws_argument_null() { let skeleton = make_single_joint_skeleton("mRoot"); let skin_data = make_skin_data("mRoot"); let Err(error) = AnimeshSkinning::compute_skinning_matrices(None, Some(skeleton), Some(skin_data)) else { panic!("null pose must fail"); }; assert_eq!(error, Error::ArgumentNull); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.ComputeSkinningMatrices_NullSkeleton_ThrowsArgumentNullException::test 97d7eef733424588868943400ef2a9664243d5b0c1df884e3cf3e2bc3fa299d6 translated #[test] fn animesh_skinning_compute_matrices_null_skeleton_throws_argument_null() { let pose = HashMap::::new(); let skin_data = make_skin_data("mRoot"); let Err(error) = AnimeshSkinning::compute_skinning_matrices(Some(pose), None, Some(skin_data)) else { panic!("null skeleton must fail"); }; assert_eq!(error, Error::ArgumentNull); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.ComputeSkinningMatrices_NullSkinData_ThrowsArgumentNullException::test a0e41b5457d413f4fb8da3d159c74318bf9b74333ce8962612610c862fca8b06 translated #[test] fn animesh_skinning_compute_matrices_null_skin_data_throws_argument_null() { let pose = HashMap::::new(); let skeleton = make_single_joint_skeleton("mRoot"); let Err(error) = AnimeshSkinning::compute_skinning_matrices(Some(pose), Some(skeleton), None) else { panic!("null skin data must fail"); }; assert_eq!(error, Error::ArgumentNull); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.ComputeSkinningMatrices_EmptySkinData_ReturnsEmptyArray::test 412f936f350e1aba601fb736a91087c89976c751a38e1653eec24a958f7c71ba translated #[test] fn animesh_skinning_compute_matrices_empty_skin_data_returns_empty_array() { let pose = HashMap::::new(); let skeleton = make_single_joint_skeleton("mRoot"); let mut skin_data = MeshSkinData::new().expect("MeshSkinData constructor"); skin_data.joint_names = Vec::new(); skin_data.inverse_bind_matrices = Vec::new(); let matrices = AnimeshSkinning::compute_skinning_matrices(Some(pose), Some(skeleton), Some(skin_data)) .expect("ComputeSkinningMatrices"); assert!(matrices.is_empty()); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.ComputeSkinningMatrices_JointNotInSkeleton_ProducesIdentity::test c0807fdb1975324c81bda29c46ab84faaad2d19b61b257180f47b6eaa2800146 translated #[test] fn animesh_skinning_compute_matrices_joint_not_in_skeleton_produces_identity() { let pose = HashMap::::new(); let skeleton = make_single_joint_skeleton("mRoot"); let skin_data = make_skin_data("nonexistent"); let matrices = AnimeshSkinning::compute_skinning_matrices(Some(pose), Some(skeleton), Some(skin_data)) .expect("ComputeSkinningMatrices"); assert_eq!(matrices.len(), 1); assert_matrix_identity(matrices[0], 0.0); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.ComputeSkinningMatrices_IdentitySkeletonAndBindPose_ProducesIdentityMatrix::test 78e0605212c88c8ae33dd68ebe8a3ece75627761f81225b188801dc7ef9e5b32 translated #[test] fn animesh_skinning_compute_matrices_identity_skeleton_and_bind_pose_produces_identity() { let pose = HashMap::::new(); let skeleton = make_single_joint_skeleton("mRoot"); let skin_data = make_skin_data("mRoot"); let matrices = AnimeshSkinning::compute_skinning_matrices(Some(pose), Some(skeleton), Some(skin_data)) .expect("ComputeSkinningMatrices"); assert_eq!(matrices.len(), 1); assert_matrix_identity(matrices[0], 1.0e-5); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.DeformVertices_NullWeights_PassesPositionsThrough::test bbeb45d84770e6b766272bf9a5d48bd415ba98681d9da70d256598429566144d translated #[test] fn animesh_skinning_deform_vertices_null_weights_passes_positions_through() { let face = face( vec![ vertex(vector3(1.0, 2.0, 3.0)), vertex(vector3(4.0, 5.0, 6.0)), ], None, ); let mut positions = [Vector3::zero(), Vector3::zero()]; AnimeshSkinning::deform_vertices(face, Vec::new(), Matrix4::identity(), &mut positions, None) .expect("DeformVertices"); assert_eq!( (positions[0].x, positions[0].y, positions[0].z), (1.0, 2.0, 3.0) ); assert_eq!( (positions[1].x, positions[1].y, positions[1].z), (4.0, 5.0, 6.0) ); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.DeformVertices_ShortOutputSpan_ThrowsArgumentException::test dfb10b33cedddadbaa5431f1fdf8a1c59369441e94618f97ef13f53ab931907b translated #[test] fn animesh_skinning_deform_vertices_short_output_span_throws_argument() { let face = face(vec![vertex(Vector3::zero()), vertex(Vector3::zero())], None); let mut positions = [Vector3::zero()]; let Err(error) = AnimeshSkinning::deform_vertices( face, Vec::new(), Matrix4::identity(), &mut positions, None, ) else { panic!("short output span must fail"); }; assert_eq!(error, Error::Argument); } // parity-case: LibreMetaverse.Tests/AnimeshSkinningTests.cs::AnimeshSkinningTests.DeformVertices_IdentitySkinning_FullWeight_PreservesPositions::test fcf305a633047e7a4181954fee4990db3537e549f2cb940b08e6d3f0f544c8e1 translated #[test] fn animesh_skinning_deform_vertices_identity_skinning_full_weight_preserves_positions() { let expected = vector3(3.0, 7.0, -2.0); let face = face( vec![vertex(expected)], Some(vec![VertexWeight { joint0: 0, joint1: 0, joint2: 0, joint3: 0, weight0: 1.0, weight1: 0.0, weight2: 0.0, weight3: 0.0, }]), ); let mut positions = [Vector3::zero()]; AnimeshSkinning::deform_vertices( face, vec![Matrix4::identity()], Matrix4::identity(), &mut positions, None, ) .expect("DeformVertices"); assert_close(positions[0].x, expected.x, 1.0e-5); assert_close(positions[0].y, expected.y, 1.0e-5); assert_close(positions[0].z, expected.z, 1.0e-5); } // parity-case: LibreMetaverse.Tests/AvatarManagerTests.cs::AvatarManagerTests.RequestOwnAvatarTextures_WhenNotConnected_DoesNotThrow::test bf3539f68a0adc58c2166289ca1c75f6ff03906f71ffe2fb749f6990404b541a translated #[test] fn avatar_manager_request_own_avatar_textures_when_not_connected_does_not_throw() { let client = GridClient::new().expect("GridClient constructor"); client .avatars() .request_own_avatar_textures() .expect("RequestOwnAvatarTextures"); } // parity-case: LibreMetaverse.Tests/AvatarManagerTests.cs::AvatarManagerTests.UpdateLastReceivedCOFVersion_AcceptsHigherVersion::test 3656364491dcd7a05c868b11ea0d8be48609d10c6054b9696f54cbac10cfe6df translated #[test] fn appearance_manager_update_last_received_cof_version_accepts_higher_version() { let client = GridClient::new().expect("GridClient constructor"); client .appearance() .update_last_received_cof_version(3) .expect("UpdateLastReceivedCOFVersion"); client .appearance() .update_last_received_cof_version(8) .expect("UpdateLastReceivedCOFVersion"); assert_eq!(client.appearance().last_update_received_cof_version(), 8); } // parity-case: LibreMetaverse.Tests/AvatarManagerTests.cs::AvatarManagerTests.UpdateLastReceivedCOFVersion_RejectsLowerVersion::test f4a827fcace9d50306a8c375183e056140996d2a39b02e4dd12fc6cc79e5d0f0 translated #[test] fn appearance_manager_update_last_received_cof_version_rejects_lower_version() { let client = GridClient::new().expect("GridClient constructor"); client .appearance() .update_last_received_cof_version(10) .expect("UpdateLastReceivedCOFVersion"); client .appearance() .update_last_received_cof_version(4) .expect("UpdateLastReceivedCOFVersion"); assert_eq!(client.appearance().last_update_received_cof_version(), 10); } // parity-case: LibreMetaverse.Tests/AvatarManagerTests.cs::AvatarManagerTests.UpdateLastReceivedCOFVersion_EqualVersion_IsNotAdvanced::test e3db5962cdad5e18ad27a36d0d7cca2d471e96df7513315d0f34bb0db99d723c translated #[test] fn appearance_manager_update_last_received_cof_version_equal_version_is_not_advanced() { let client = GridClient::new().expect("GridClient constructor"); client .appearance() .update_last_received_cof_version(6) .expect("UpdateLastReceivedCOFVersion"); client .appearance() .update_last_received_cof_version(6) .expect("UpdateLastReceivedCOFVersion"); assert_eq!(client.appearance().last_update_received_cof_version(), 6); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_EmptyFilter_ReturnsEmptyList::test 59857e0c12d374a3063e98311f7d262ca9e4fe3bde43af4698d20a6e6e550a64 translated #[test] fn linden_skeleton_build_expanded_joint_list_empty_filter_returns_empty_list() { let skeleton = make_skeleton( "mPelvis", vec![make_joint("mTorso", vec![make_joint("mChest", Vec::new())])], ); let result = skeleton .build_expanded_joint_list(Box::new(Vec::::new().into_iter())) .expect("BuildExpandedJointList"); assert!(result.is_empty()); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_NoBoneChildren_ReturnsEmptyList::test c744ad17de5b5dd0e594973181307ddaba0d9b0ecf0ce3ff258e108de45e82be translated #[test] fn linden_skeleton_build_expanded_joint_list_no_bone_children_returns_empty_list() { let mut root = Joint::new().expect("Joint constructor"); root.base.set_name("mPelvis".to_owned()); root.set_bone(None); let mut skeleton = LindenSkeleton::new().expect("LindenSkeleton constructor"); skeleton.set_bone(root); let result = skeleton .build_expanded_joint_list(Box::new(vec!["mPelvis".to_owned()].into_iter())) .expect("BuildExpandedJointList"); assert!(result.is_empty()); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_LinearChain_AllInFilter_ProducesCorrectSequence::test c2fa9fc02df25cb6eb083746a100abf551d208ea036cadb26a1e94649d33e560 translated #[test] fn linden_skeleton_build_expanded_joint_list_linear_chain_all_in_filter() { let skeleton = make_skeleton( "root", vec![make_joint( "A", vec![make_joint("B", vec![make_joint("C", Vec::new())])], )], ); let result = skeleton .build_expanded_joint_list(Box::new(["A", "B", "C"].map(str::to_owned).into_iter())) .expect("BuildExpandedJointList"); assert_eq!(result, vec!["A", "B", "C"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_IntermediateBoneNotInFilter_IsSkippedAsPlaceholder::test 872b6dae196f3700d62b44b6fc3ec50cd41ca78cf36fa3f63d5967b6e1e47bce translated #[test] fn linden_skeleton_build_expanded_joint_list_intermediate_not_in_filter_is_skipped() { let skeleton = make_skeleton( "root", vec![make_joint( "A", vec![make_joint("B", vec![make_joint("C", Vec::new())])], )], ); let result = skeleton .build_expanded_joint_list(Box::new(["A", "C"].map(str::to_owned).into_iter())) .expect("BuildExpandedJointList"); assert!(!result.contains(&"B".to_owned())); assert_eq!(result, vec!["A", "C"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_MultipleConsecutiveIntermediates_AllSkipped::test 033ff727d7f4585321f81b2e4584395bdcbf22ad89eac601be67a1887735f944 translated #[test] fn linden_skeleton_build_expanded_joint_list_multiple_intermediates_all_skipped() { let skeleton = make_skeleton( "root", vec![make_joint( "A", vec![make_joint( "inter1", vec![make_joint("inter2", vec![make_joint("B", Vec::new())])], )], )], ); let result = skeleton .build_expanded_joint_list(Box::new(["A", "B"].map(str::to_owned).into_iter())) .expect("BuildExpandedJointList"); assert!(!result.contains(&"inter1".to_owned())); assert!(!result.contains(&"inter2".to_owned())); assert_eq!(result, vec!["A", "B"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_BranchingHierarchy_BranchPointRepeatedPerChild::test ff01368c80870ec7d9b4e5eed52bbb3254e8fbf0c06d09f245abf2a450eae6c6 translated #[test] fn linden_skeleton_build_expanded_joint_list_branch_point_repeated_per_child() { let skeleton = make_skeleton( "root", vec![make_joint( "A", vec![ make_joint("B", vec![make_joint("C", Vec::new())]), make_joint("D", Vec::new()), ], )], ); let result = skeleton .build_expanded_joint_list(Box::new( ["A", "B", "C", "D"].map(str::to_owned).into_iter(), )) .expect("BuildExpandedJointList"); assert_eq!(result, vec!["A", "B", "C", "A", "D"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_BranchingWithIntermediates_EffectiveParentUsedForAllBranches::test 5145f5db024a09f833a903f992f42ecc827973f7eddae31a7164e91df0b03ab3 translated #[test] fn linden_skeleton_build_expanded_joint_list_branching_uses_effective_parent() { let skeleton = make_skeleton( "root", vec![make_joint( "A", vec![ make_joint("B", vec![make_joint("C", Vec::new())]), make_joint("D", Vec::new()), ], )], ); let result = skeleton .build_expanded_joint_list(Box::new(["A", "C", "D"].map(str::to_owned).into_iter())) .expect("BuildExpandedJointList"); assert!(!result.contains(&"B".to_owned())); assert_eq!(result, vec!["A", "C", "A", "D"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_OnlyLeafInFilter_ReachableViaRootPlaceholder::test e4b894c6b0479350e3ef57aab2ccb8d720a7d37df02e09ad4317953804f098fe translated #[test] fn linden_skeleton_build_expanded_joint_list_only_leaf_in_filter() { let skeleton = make_skeleton( "root", vec![make_joint( "inter1", vec![make_joint("inter2", vec![make_joint("leaf", Vec::new())])], )], ); let result = skeleton .build_expanded_joint_list(Box::new(vec!["leaf".to_owned()].into_iter())) .expect("BuildExpandedJointList"); assert_eq!(result, vec!["leaf"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_SingleFilteredDirectChild_ProducesTwoEntries::test f9191747ebd65cf4bbe5b7780400a526f26db86545a08e581348b78f2bf61d18 translated #[test] fn linden_skeleton_build_expanded_joint_list_single_filtered_direct_child() { let skeleton = make_skeleton("root", vec![make_joint("A", Vec::new())]); let result = skeleton .build_expanded_joint_list(Box::new(vec!["A".to_owned()].into_iter())) .expect("BuildExpandedJointList"); assert_eq!(result, vec!["A"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildExpandedJointList_SpineBonesSkipped_DownstreamIndicesNotShifted::test 779aefe6b9fb007f17fed5d2bb6da5d81cf908d938fdceff7c9b155747d5dad9 translated #[test] fn linden_skeleton_build_expanded_joint_list_spine_bones_skipped() { let skeleton = make_skeleton( "mPelvis", vec![make_joint( "mTorso", vec![make_joint( "mSpine1", vec![make_joint( "mChest", vec![make_joint("mNeck", vec![make_joint("mHead", Vec::new())])], )], )], )], ); let result = skeleton .build_expanded_joint_list(Box::new( ["mTorso", "mChest", "mHead"].map(str::to_owned).into_iter(), )) .expect("BuildExpandedJointList"); assert!(!result.contains(&"mSpine1".to_owned())); assert!(!result.contains(&"mNeck".to_owned())); assert_eq!(result, vec!["mTorso", "mChest", "mHead"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.JointBase_SupportCategory_ReturnsBase_WhenAttributeIsBase::test 809bde18eab918a7ba51c1af1659718ec7f3a7ffc91c3dd8d462b298c78ce968 translated #[test] fn joint_base_support_category_returns_base_when_attribute_is_base() { let mut joint = Joint::new().expect("Joint constructor"); joint.base.set_name("mTorso".to_owned()); joint.base.set_support("base".to_owned()); assert_eq!(joint.base.support_category(), JointSupportCategory::Base); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.JointBase_SupportCategory_ReturnsExtended_WhenAttributeIsExtended::test 47c16fd9e292b4d5de4e28f3b932ef8f7e04e087fd1a0023db3666e85e28762b translated #[test] fn joint_base_support_category_returns_extended_when_attribute_is_extended() { let mut joint = Joint::new().expect("Joint constructor"); joint.base.set_name("mTail1".to_owned()); joint.base.set_support("extended".to_owned()); assert_eq!( joint.base.support_category(), JointSupportCategory::Extended ); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.JointBase_SupportCategory_DefaultsToBase_WhenAttributeIsMissing::test 4e5faf4477a615488ac257c511ec1a02c8f53f64e34260fd48af297807dae723 translated #[test] fn joint_base_support_category_defaults_to_base_when_attribute_is_missing() { let mut joint = Joint::new().expect("Joint constructor"); joint.base.set_name("mTest".to_owned()); assert_eq!(joint.base.support_category(), JointSupportCategory::Base); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.Joint_GetAliasesList_ReturnsSplitAliases_WhenAttributeIsSet::test b28dd565d89d53fd1e5f75fa0cc4956661e9ceab65a30290c60d72f619a5e533 translated #[test] fn joint_get_aliases_list_returns_split_aliases_when_attribute_is_set() { let mut joint = Joint::new().expect("Joint constructor"); joint.base.set_name("mPelvis".to_owned()); joint.set_aliases("hip avatar_mPelvis".to_owned()); assert_eq!( joint.get_aliases_list().expect("GetAliasesList"), vec!["hip", "avatar_mPelvis"] ); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.Joint_GetAliasesList_ReturnsEmpty_WhenAttributeIsEmpty::test fdab4aa0aafa3739c96c673e974ad89cbdaca458c8f1c0e12a38dd6074b9850a translated #[test] fn joint_get_aliases_list_returns_empty_when_attribute_is_empty() { let mut joint = Joint::new().expect("Joint constructor"); joint.base.set_name("mHead".to_owned()); assert!(joint.get_aliases_list().expect("GetAliasesList").is_empty()); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.GetAllJoints_ReturnsAllJointsDepthFirst::test 93b9b834a8f87acb434c927512fd355e171890afe7a50499f6bed1f7c020d218 translated #[test] fn linden_skeleton_get_all_joints_returns_all_joints_depth_first() { let skeleton = make_skeleton( "root", vec![ make_joint("A", vec![make_joint("B", Vec::new())]), make_joint("C", Vec::new()), ], ); let names: Vec = skeleton .get_all_joints() .expect("GetAllJoints") .map(|joint| joint.base.name()) .collect(); assert_eq!(names, vec!["root", "A", "B", "C"]); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.BuildJointDictionary_ContainsCanonicalNamesAndAliases::test d9fd2e8103d19e2c399b0e8a4dd11db2dfd0290bffd678253f64f3c07bd98c24 translated #[test] fn linden_skeleton_build_joint_dictionary_contains_names_and_aliases() { let mut child = Joint::new().expect("Joint constructor"); child.base.set_name("mPelvis".to_owned()); child.set_aliases("hip avatar_mPelvis".to_owned()); let skeleton = make_skeleton("root", vec![child]); let dictionary = skeleton .build_joint_dictionary() .expect("BuildJointDictionary"); assert!(dictionary.contains_key("root")); assert!(dictionary.contains_key("mPelvis")); assert!(dictionary.contains_key("hip")); assert!(dictionary.contains_key("avatar_mPelvis")); assert!(Arc::ptr_eq( dictionary.get("hip").expect("hip alias"), dictionary.get("mPelvis").expect("canonical name") )); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.GetBone_ReturnsBone_ByCanonicalName::test 87761df2ed95f6dc61a89717a794956faca53a28092464c276406bd777e888a1 translated #[test] fn linden_skeleton_get_bone_returns_bone_by_canonical_name() { let skeleton = make_skeleton("root", vec![make_joint("mTorso", Vec::new())]); let joint = skeleton .get_bone("mTorso".to_owned()) .expect("GetBone") .expect("mTorso joint"); assert_eq!(joint.base.name(), "mTorso"); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.GetBone_ReturnsBone_ByAlias::test 2bad9d7c6ad2b2944537bbf30c89370957f4ce54785c797e0bf234b44aacce2a translated #[test] fn linden_skeleton_get_bone_returns_bone_by_alias() { let mut child = Joint::new().expect("Joint constructor"); child.base.set_name("mPelvis".to_owned()); child.set_aliases("hip".to_owned()); let skeleton = make_skeleton("root", vec![child]); let joint = skeleton .get_bone("hip".to_owned()) .expect("GetBone") .expect("hip alias"); assert_eq!(joint.base.name(), "mPelvis"); } // parity-case: LibreMetaverse.Tests/LindenSkeletonTests.cs::LindenSkeletonTests.GetBone_ReturnsNull_ForUnknownName::test 19b80d25352f1181a0ccb5e96d068840440dc9352e0e8760ee7774edd414451c translated #[test] fn linden_skeleton_get_bone_returns_none_for_unknown_name() { let skeleton = make_skeleton("root", vec![make_joint("mTorso", Vec::new())]); assert!( skeleton .get_bone("nonexistent".to_owned()) .expect("GetBone") .is_none() ); }