public void Lerp() { var expected = new CFrame(2.5f, 3.5f, 4.5f, 0.858355939f, -0.272098392f, 0.434956968f, 0.359638363f, 0.923728108f, -0.131858498f, -0.365903497f, 0.269608736f, 0.890744507f); var p1 = new CFrame(1, 2, 3) * CFrame.Angles(0.1f, 0.2f, 0.3f); var p2 = new CFrame(4, 5, 6) * CFrame.Angles(0.2f, 0.7f, 0.3f); var actual = p1.lerp(p2, 0.5f); Assert.AreEqual(expected, actual, "Rotation * Translation did not return expected results."); }
public static string Assemble(KeyframeSequence sequence, List <Bone> rig) { StudioMdlWriter animWriter = new StudioMdlWriter(); List <Keyframe> keyframes = new List <Keyframe>(); Dictionary <string, Bone> boneLookup = new Dictionary <string, Bone>(); List <Node> nodes = animWriter.Nodes; foreach (Bone bone in rig) { Node node = bone.Node; if (node != null) { nodes.Add(node); string boneName = node.Name; if (!boneLookup.ContainsKey(boneName)) { boneLookup.Add(boneName, bone); } } } foreach (Keyframe kf in sequence.GetChildrenOfClass <Keyframe>()) { Pose rootPart = kf.FindFirstChild <Pose>("HumanoidRootPart"); if (rootPart != null) { // We don't need the rootpart for this. foreach (Pose subPose in rootPart.GetChildrenOfClass <Pose>()) { subPose.Parent = kf; } rootPart.Destroy(); } kf.Time /= sequence.TimeScale; keyframes.Add(kf); } keyframes.Sort(0, keyframes.Count, sorter); Keyframe lastKeyframe = keyframes[keyframes.Count - 1]; float fLength = lastKeyframe.Time; int frameCount = ToFrameRate(fLength); // Animations in source are kinda dumb, because theres no frame interpolation. // I have to account for every single CFrame for every single frame. Dictionary <int, Dictionary <string, Pose> > keyframeMap = new Dictionary <int, Dictionary <string, Pose> >(); for (int i = 0; i <= frameCount; i++) { keyframeMap[i] = new Dictionary <string, Pose>(); } foreach (Keyframe kf in keyframes) { int frame = ToFrameRate(kf.Time); Dictionary <string, Pose> poseMap = keyframeMap[frame]; List <Pose> poses = GatherPoses(kf); foreach (Pose pose in poses) { poseMap[pose.Name] = pose; } } List <BoneKeyframe> boneKeyframes = animWriter.Skeleton; Keyframe baseFrame = keyframes[0]; for (int i = 0; i < frameCount; i++) { BoneKeyframe frame = new BoneKeyframe(); frame.Time = i; if (sequence.AvatarType == AvatarType.R15) { frame.DeltaSequence = true; frame.BaseRig = rig; } List <Bone> bones = frame.Bones; foreach (Node node in nodes) { PosePair closestPoses = GetClosestPoses(keyframeMap, i, node.Name); float current = i; float min = closestPoses.Min.Frame; float max = closestPoses.Max.Frame; float alpha = (min == max ? 0 : (current - min) / (max - min)); Pose pose0 = closestPoses.Min.Pose; Pose pose1 = closestPoses.Max.Pose; float weight = EasingUtil.GetEasing(pose1.PoseEasingStyle, pose1.PoseEasingDirection, 1 - alpha); CFrame lastCFrame = pose0.CFrame; CFrame nextCFrame = pose1.CFrame; Bone baseBone = boneLookup[node.Name]; CFrame c0 = baseBone.C0; CFrame c1 = baseBone.C1; CFrame interp = lastCFrame.lerp(nextCFrame, weight); // some ugly manual fixes. // todo: make this unnecessary :( if (sequence.AvatarType == AvatarType.R6) { Vector3 pos = interp.p; CFrame rot = interp - pos; if (node.Name == "Torso") { float[] ang = interp.toEulerAnglesXYZ(); rot = CFrame.Angles(ang[0], ang[2], ang[1]); pos = new Vector3(pos.x, pos.z, pos.y); } else if (node.Name.StartsWith("Right")) { pos *= new Vector3(-1, 1, 1); } if (node.Name.Contains("Arm") || node.Name.Contains("Leg")) { pos = new Vector3(pos.z, pos.y, pos.x); } if (sequence.Name == "Climb" && node.Name.Contains("Leg")) // https://www.youtube.com/watch?v=vfJ7DqyDl9w { pos += new Vector3(-.1f, 0, 0); } interp = new CFrame(pos) * rot; } else if (sequence.AvatarType == AvatarType.R15) { if (node.Name.Contains("UpperArm")) { Vector3 pos = interp.p; CFrame rot = interp - pos; float[] ang = rot.toEulerAnglesXYZ(); if (sequence.Name == "Climb" || sequence.Name == "Swim") { rot = CFrame.Angles(ang[0], -ang[2], -ang[1]); } interp = new CFrame(pos) * rot; } } Bone bone = new Bone(node.Name, i, interp); bone.Node = node; bones.Add(bone); } boneKeyframes.Add(frame); } return(animWriter.BuildFile()); }