/// <summary> /// Creates a list of bone index values for the given avatar bone /// and its children. /// </summary> /// <param name="avatarBone">The root bone to start search</param> /// <param name="parentBones">List of parent bones from the avatar /// renderer</param> /// <returns></returns> List <int> FindInfluencedBones(AvatarBone avatarBone, ReadOnlyCollection <int> parentBones) { // New list of bones that will be influenced List <int> influencedList = new List <int>(); // Add the first bone influencedList.Add((int)avatarBone); // Start searching after the first bone int currentBoneID = influencedList[0] + 1; // Loop until we are done with all of the bones while (currentBoneID < parentBones.Count) { // Check to see if the current bone is a child of any of the // previous bones we have found if (influencedList.Contains(parentBones[currentBoneID])) { // Add the bone to the influenced list influencedList.Add(currentBoneID); } // Move to the next bone currentBoneID++; } return(influencedList); }
private static void AddJoint(Ragdoll ragdoll, Skeleton skeleton, AvatarBone parentBone, AvatarBone childBone, float errorReduction, float softness) { int parentIndex = (int)parentBone; int childIndex = (int)childBone; // To define AnchorPositionALocal/AnchorPositionBLocal: // To get the AnchorPositionALocal we apply jointPosesAbsolute[indexA].Inverse to // convert the joint pose from model space into the joints space of parentBone. Then we apply // ragdoll.BodyOffsets[boneAIndex].Inverse to convert from joint space to body space. The result is // the joint position of B in body space of A. // To get AnchorPositionBLocal, we only have to apply the inverse offset. BallJoint joint = new BallJoint { BodyA = ragdoll.Bodies[parentIndex], BodyB = ragdoll.Bodies[childIndex], CollisionEnabled = false, AnchorPositionALocal = (ragdoll.BodyOffsets[parentIndex].Inverse * skeleton.GetBindPoseAbsoluteInverse(parentIndex) * skeleton.GetBindPoseAbsoluteInverse(childIndex).Inverse).Translation, AnchorPositionBLocal = ragdoll.BodyOffsets[childIndex].Inverse.Position, ErrorReduction = errorReduction, Softness = softness, }; ragdoll.Joints[childIndex] = joint; }
public static bool TryGetBone(AvatarBone bone, out Transform transform) { if (TryGetInstance(out var avatar)) { transform = bone switch { AvatarBone.Head => avatar.headBone, AvatarBone.Body => avatar.bodyBone, AvatarBone.Legs => avatar.legsBone, AvatarBone.Broom => avatar.broomBone, _ => throw new NotImplementedException(), }; return(transform); } transform = default; return(false); }
private static void AddTwistSwingLimit(Ragdoll ragdoll, Skeleton skeleton, AvatarBone parentBone, AvatarBone childBone, Matrix33F orientationA, Matrix33F orientationB, Vector3F minimum, Vector3F maximum) { int parentIndex = (int)parentBone; int childIndex = (int)childBone; // The difficult part is to define the constraint anchor orientation. // Here is how we do it: // When we look at the front side of an Avatar in bind pose, the x-axis is parallel // to the arms. y points up and z is normal to the those axes. // // To define orientationA/B: // The anchor x-axis is the twist axis. That means, this is already the correct axis // for the hands (wrist joints) and orientationA/B are therefore Matrix33F.Identity. // For the Head, the twist axis must point up. Therefore orientationA/B must be a 90° // rotation about z to rotate the twist axis up. // For the shoulder-elbow connection, orientationA is Matrix.Identity. The swing cone must // not be parallel to the arm axis (because the elbow cannot bend backwards). Therefore, // orientationB defines a rotation that rotates the twist axis (= swing cone center) to the // front. // // To define AnchorOrientationALocal/AnchorOrientationBLocal: // AnchorOrientationALocal must be a rotation matrix that transforms a vector from local // constraint anchor space to local body space of A. // orientationA defines the constraint anchor orientation in model space. // With jointPosesAbsolute[boneAIndex].Orientation.Transposed, we convert from model space // to joint space. With ragdoll.BodyOffsets[boneAIndex].Orientation.Transposed, we convert from joint // space to body space. The combined rotation matrix converts from constraint anchor space // to body space. var limit = new TwistSwingLimit { BodyA = ragdoll.Bodies[parentIndex], BodyB = ragdoll.Bodies[childIndex], AnchorOrientationALocal = ragdoll.BodyOffsets[parentIndex].Orientation.Transposed * skeleton.GetBindPoseAbsoluteInverse(parentIndex).Rotation.ToRotationMatrix33() * orientationA, AnchorOrientationBLocal = ragdoll.BodyOffsets[childIndex].Orientation.Transposed * skeleton.GetBindPoseAbsoluteInverse(childIndex).Rotation.ToRotationMatrix33() * orientationB, Minimum = minimum, Maximum = maximum, ErrorReduction = 0.2f, Softness = 0.001f }; ragdoll.Limits[childIndex] = limit; }
private static void AddAngularLimit(Ragdoll ragdoll, Skeleton skeleton, AvatarBone parentBone, AvatarBone childBone, Matrix33F orientationA, Matrix33F orientationB, Vector3F minimum, Vector3F maximum) { // Similar to AddTwistSwingLimit int parentIndex = (int)parentBone; int childIndex = (int)childBone; var limit = new AngularLimit { BodyA = ragdoll.Bodies[parentIndex], BodyB = ragdoll.Bodies[childIndex], AnchorOrientationALocal = ragdoll.BodyOffsets[parentIndex].Orientation.Transposed * skeleton.GetBindPoseAbsoluteInverse(parentIndex).Rotation.ToRotationMatrix33() * orientationA, AnchorOrientationBLocal = ragdoll.BodyOffsets[childIndex].Orientation.Transposed * skeleton.GetBindPoseAbsoluteInverse(childIndex).Rotation.ToRotationMatrix33() * orientationB, Minimum = minimum, Maximum = maximum, ErrorReduction = new Vector3F(0.2f), Softness = new Vector3F(0.001f) }; ragdoll.Limits[childIndex] = limit; }
private static void AddMotor(Ragdoll ragdoll, AvatarBone parentBone, AvatarBone childBone) { // A quaternion motor controls the relative orientation between two bodies. The target // orientation is specified with a quaternion. The target orientations are set in // SetMotorTargets. // We can use the motors to achieve following results: // - No motors: The ragdoll joints are not damped and the bones swing a lot (within the // allowed limits). // - Damping: If DampingConstant > 0 and SpringConstant == 0, the relative bone rotations // are damped. This simulates joint friction and muscles forces acting against the movement. // - Springs: If DampingConstant > 0 and SpringConstant > 0, the motors try to move the // ragdoll limbs to a pose defined by the TargetOrientations of the motors. This could be, // for example, a defensive pose of a character. // - Animation: Like "Springs" but the TargetOrientation is changed in each frame. This // way the ragdoll performs a user defined animation while still reacting to impacts. int parentIndex = (int)parentBone; int childIndex = (int)childBone; var motor = new RagdollMotor(childIndex, parentIndex); ragdoll.Motors[childIndex] = motor; }
/// <summary> /// Creates a list of bone index values for the given avatar bone /// and its children. /// </summary> /// <param name="avatarBone">The root bone to start search</param> /// <param name="parentBones">List of parent bones from the avatar /// renderer</param> /// <returns></returns> List<int> FindInfluencedBones(AvatarBone avatarBone, ReadOnlyCollection<int> parentBones) { // New list of bones that will be influenced List<int> influencedList = new List<int>(); // Add the first bone influencedList.Add((int)avatarBone); // Start searching after the first bone int currentBoneID = influencedList[0] + 1; // Loop until we are done with all of the bones while (currentBoneID < parentBones.Count) { // Check to see if the current bone is a child of any of the // previous bones we have found if (influencedList.Contains(parentBones[currentBoneID])) { // Add the bone to the influenced list influencedList.Add(currentBoneID); } // Move to the next bone currentBoneID++; } return influencedList; }
public Matrix GetBoneTransform(AvatarBone avatarBone) { return(this.GetBoneTransform((int)avatarBone)); }
public int GetBone(AvatarBone avatarBone) { return((int)avatarBone); }