/// <summary> /// Performs a local motion planning to estimate the next pose /// </summary> /// <param name="velocity"></param> /// <param name="time"></param> /// <param name="currentPosition"></param> /// <param name="currentRotation"></param> /// <param name="targetPosition"></param> /// <param name="targetRotation"></param> /// <returns></returns> private MTransform DoLocalMotionPlanning(double velocity, TimeSpan time, MVector3 currentPosition, MQuaternion currentRotation, MVector3 targetPosition, MQuaternion targetRotation) { //Create a resulting transform MTransform result = new MTransform(); //Estimate the delta MVector3 deltaPosition = targetPosition.Subtract(currentPosition); //Estimate the meximum allowed delta double maxTranslationDelta = velocity * time.TotalSeconds; //Limit the maximum if (deltaPosition.Magnitude() >= maxTranslationDelta) { deltaPosition = deltaPosition.Normalize(); deltaPosition = deltaPosition.Multiply(maxTranslationDelta); } float angularVelocityReach = 100f; double angle = Math.Abs(MQuaternionExtensions.Angle(currentRotation, targetRotation)); double maxAngle = angularVelocityReach * time.TotalSeconds; //Estimate the blendweihgt for the oreitnation blending double weight = Math.Min(1, maxAngle / angle); result.Position = currentPosition.Add(deltaPosition); result.Rotation = MQuaternionExtensions.Slerp(currentRotation, targetRotation, (float)weight); //result.Time = time; return(result); }
/// <summary> /// Performs the actual motion planning /// </summary> /// <param name="velocity"></param> /// <param name="angularVelocity"></param> /// <param name="time"></param> /// <param name="currentPosition"></param> /// <param name="currentRotation"></param> /// <param name="targetPosition"></param> /// <param name="targetRotation"></param> /// <returns></returns> private MTransform DoLocalMotionPlanning(float velocity, float angularVelocity, TimeSpan time, MVector3 currentPosition, MQuaternion currentRotation, MVector3 targetPosition, MQuaternion targetRotation) { MTransform result = new MTransform(); MVector3 delta = targetPosition.Subtract(currentPosition); double angle = Math.Abs(MQuaternionExtensions.Angle(currentRotation, targetRotation)); double maxTranslationDelta = velocity * time.TotalSeconds; if (delta.Magnitude() >= maxTranslationDelta) { delta = delta.Normalize(); delta = delta.Multiply(maxTranslationDelta); } //To do consider self collision double maxAngle = angularVelocity * time.TotalSeconds; if (angle < maxAngle) { angle = maxAngle; } double weight = Math.Min(1, maxAngle / angle); result.Position = currentPosition.Add(delta); result.Rotation = MQuaternionExtensions.Slerp(currentRotation, targetRotation, (float)weight); //result.Time = time; return(result); }
/// <summary> /// Computes the rotation to rotate from one vector to the other /// </summary> /// <param name="from">The start direction</param> /// <param name="to">The desired direction</param> /// <returns></returns> private static MQuaternion FromToRotation(MVector3 from, MVector3 to) { //Normalize both vectors from = from.Normalize(); to = to.Normalize(); //Estimate the rotation axis MVector3 axis = MVector3Extensions.Cross(from, to).Normalize(); //Compute the phi angle double phi = Math.Acos(MVector3Extensions.Dot(from, to)) / (from.Magnitude() * to.Magnitude()); //Create a new quaternion representing the rotation MQuaternion result = new MQuaternion() { X = Math.Sin(phi / 2) * axis.X, Y = Math.Sin(phi / 2) * axis.Y, Z = Math.Sin(phi / 2) * axis.Z, W = Math.Cos(phi / 2) }; //Perform is nan check and return identity quaternion if (double.IsNaN(result.W) || double.IsNaN(result.X) || double.IsNaN(result.Y) || double.IsNaN(result.Z)) { result = new MQuaternion(0, 0, 0, 1); } //Return the estimated rotation return(result); }
/// <summary> /// Performs local motion planning to reach the defined point /// </summary> /// <param name="velocity"></param> /// <param name="time"></param> /// <param name="currentPosition"></param> /// <param name="currentRotation"></param> /// <param name="targetPosition"></param> /// <param name="targetRotation"></param> /// <returns></returns> private MTransform DoLocalMotionPlanning(double velocity, double angularVelocity, TimeSpan time, MVector3 currentPosition, MQuaternion currentRotation, MVector3 targetPosition, MQuaternion targetRotation) { //Create a new transform representing the result MTransform result = new MTransform(); //Estimate the vector to reach the goal MVector3 delta = targetPosition.Subtract(currentPosition); float distance = delta.Magnitude(); //Determine the angular distance double angle = Math.Abs(MQuaternionExtensions.Angle(currentRotation, targetRotation)); //Determine the max translation delta and max angle double maxTranslationDelta = velocity * time.TotalSeconds; double maxAngle = angularVelocity * time.TotalSeconds; //Compute the translation weight float translationWeight = (float)Math.Min(1, maxTranslationDelta / delta.Magnitude()); //Compute the rotation weight float rotationWeight = (float)Math.Min(1, maxAngle / angle); //Limit the translation if (delta.Magnitude() >= maxTranslationDelta) { delta = delta.Normalize(); delta = delta.Multiply(maxTranslationDelta); } //Compute the new position result.Position = currentPosition.Add(delta); if (angularVelocity == 0) { result.Rotation = MQuaternionExtensions.Slerp(currentRotation, targetRotation, translationWeight); } else { result.Rotation = MQuaternionExtensions.Slerp(currentRotation, targetRotation, rotationWeight); } return(result); }
/// <summary> /// Do step routine in which the actual simulation result is generated /// </summary> /// <param name="time"></param> /// <param name="simulationState"></param> /// <returns></returns> public override MSimulationResult DoStep(double time, MSimulationState simulationState) { //Create a new simulation result MSimulationResult result = new MSimulationResult() { Events = new List <MSimulationEvent>(), Constraints = simulationState.Constraints ?? new List <MConstraint>(), SceneManipulations = new List <MSceneManipulation>() }; this.SkeletonAccess.SetChannelData(simulationState.Current.Copy()); // Target position we want to transform to MVector3 targetPos = this.targetTransform.Position; // Fully body posture. Only Pelvis Center (first joint) has to be manipulated. List <double> posture = simulationState.Current.PostureData; // Current position and distance to target. MVector3 currentPos = this.SkeletonAccess.GetRootPosition(simulationState.Initial.AvatarID); MVector3 distance = targetPos.Subtract(currentPos); // Current rotation and rotational diff to target MQuaternion currentRot = this.SkeletonAccess.GetRootRotation(simulationState.Initial.AvatarID); MQuaternion targetRot = this.targetTransform.Rotation; MVector3 newPos; MVector3 deltaDistance = distance.Clone(); if (this.velocity > 0) { deltaDistance = distance.Normalize().Multiply(this.velocity * time); Console.WriteLine("Delta v: " + deltaDistance.Magnitude() + " " + time + " " + this.velocity); } // If no velocity set or distance very close, directly morph to target position and rotation. if (this.velocity <= 0 || distance.Magnitude() < deltaDistance.Magnitude()) { newPos = targetPos; // Set rotation //posture[3] = this.targetTransform.Rotation.W; //posture[4] = this.targetTransform.Rotation.X; //posture[5] = this.targetTransform.Rotation.Y; //posture[6] = this.targetTransform.Rotation.Z; // Add end event. Console.WriteLine("Finished with vel " + this.velocity + " at " + distance.Magnitude()); result.Events.Add(new MSimulationEvent(this.instruction.Name, mmiConstants.MSimulationEvent_End, this.instruction.ID)); } else // if velocity > 0 and distance sufficiently large, we should apply linear translation with the provided velocity. { newPos = currentPos.Add(deltaDistance); Console.WriteLine("Target Location: " + this.targetTransform.Position + " " + currentPos + " " + distance + " " + deltaDistance + " " + newPos); } Console.WriteLine("newposrot: " + newPos + " " + targetRot); this.SkeletonAccess.SetRootPosition(simulationState.Current.AvatarID, newPos); this.SkeletonAccess.SetRootRotation(simulationState.Current.AvatarID, targetRot); result.Posture = this.SkeletonAccess.GetCurrentPostureValues(simulationState.Current.AvatarID); Console.WriteLine("Frame : " + result.Posture.PostureData[0] + " " + result.Posture.PostureData[1] + " " + result.Posture.PostureData[2] + " " + result.Posture.PostureData[3] + " " + result.Posture.PostureData[4] + " " + result.Posture.PostureData[5] + " " + result.Posture.PostureData[6] + " "); //Return the result return(result); }
/// <summary> /// Basic do step routine that is executed for each frame and generates the actual motion. /// </summary> /// <param name="time"></param> /// <param name="simulationState"></param> /// <returns></returns> private MSimulationResult DoStepBlending(double time, MSimulationState simulationState) { //Create a new simulation result MSimulationResult result = new MSimulationResult() { Events = simulationState.Events ?? new List <MSimulationEvent>(), Constraints = simulationState.Constraints, SceneManipulations = simulationState.SceneManipulations ?? new List <MSceneManipulation>(), Posture = simulationState.Current }; //Directly operate on the global constraints -> since no ik is computed List <MConstraint> globalConstraints = result.Constraints; //Assign the global constraints to the constraint manager this.constraintManager.SetConstraints(ref globalConstraints); //Use the initial state (approved posture of last frame) this.SkeletonAccess.SetChannelData(simulationState.Initial); //Get the current hand position and rotation MVector3 currentHandPosition = this.SkeletonAccess.GetGlobalJointPosition(this.AvatarDescription.AvatarID, this.handJoint); MQuaternion currentHandRotation = this.SkeletonAccess.GetGlobalJointRotation(this.AvatarDescription.AvatarID, this.handJoint); MJointConstraint jointConstraint = null; switch (this.handJoint) { case MJointType.LeftWrist: jointConstraint = this.constraintManager.GetEndeffectorConstraint(MJointType.LeftWrist); break; case MJointType.RightWrist: jointConstraint = this.constraintManager.GetEndeffectorConstraint(MJointType.RightWrist); break; } //Handle the joint constraint if (jointConstraint != null) { //Get the current hand positon based on the constraint currentHandPosition = jointConstraint.GeometryConstraint.GetGlobalPosition(this.SceneAccess); currentHandRotation = jointConstraint.GeometryConstraint.GetGlobalRotation(this.SceneAccess); } //Set the skeleton to the current state this.SkeletonAccess.SetChannelData(simulationState.Current); //Determine the target hand position (either underlying MMU or given via boundary constraints) MVector3 targetHandPosition = this.SkeletonAccess.GetGlobalJointPosition(this.AvatarDescription.AvatarID, this.handJoint); MQuaternion targetHandRotation = this.SkeletonAccess.GetGlobalJointRotation(this.AvatarDescription.AvatarID, this.handJoint); //Move the hand from the current position to the target position MVector3 deltaPosition = targetHandPosition.Subtract(currentHandPosition); float angle = (float)MQuaternionExtensions.Angle(currentHandRotation, targetHandRotation); //Compute the distance of the hand to the target hand position float distanceToGoal = deltaPosition.Magnitude(); //Compute the max distance which can be covered within the current frame float maxDistance = (float)(time * this.velocity); //Compute the max allowed angle float maxAngle = (float)(time * this.angularVelocity); //Compute the weight for slerping (weight increases with shrinking distance to target) float translationWeight = Math.Min(1.0f, maxDistance / distanceToGoal); //Compute the rotation weight float rotationWeight = Math.Min(1.0f, maxAngle / angle); //Blend from the current rotation to the target result.Posture = Blending.PerformBlend((IntermediateSkeleton)this.SkeletonAccess, simulationState.Initial, simulationState.Current, Math.Min(translationWeight, rotationWeight), true); this.SkeletonAccess.SetChannelData(result.Posture); if (distanceToGoal < 0.01f) { result.Events.Add(new MSimulationEvent() { Name = "Release Finished", Reference = this.instruction.ID, Type = mmiConstants.MSimulationEvent_End }); //Remove all constraints for the respective hand this.constraintManager.RemoveEndeffectorConstraints(this.handJoint); } else { //Remove the endeffector constraint this.constraintManager.RemoveEndeffectorConstraints(this.handJoint); //Update the constraint this.constraintManager.SetEndeffectorConstraint(this.handJoint, this.SkeletonAccess.GetGlobalJointPosition(result.Posture.AvatarID, this.handJoint), this.SkeletonAccess.GetGlobalJointRotation(result.Posture.AvatarID, this.handJoint)); } return(result); }
public static double SqrMagnitude(this MVector3 vector) { return(vector.Magnitude() * vector.Magnitude()); }
public void ScaleSkeleton(MAvatarPosture globalTarget, Dictionary <MJointType, string> joint_map) { List <MJointType> spine = new List <MJointType>() { MJointType.S1L5Joint, MJointType.T12L1Joint, MJointType.T1T2Joint, MJointType.C4C5Joint, MJointType.HeadJoint }; if (spine.Contains(this.GetMJoint().Type)) { //float shoulder_height = getJointPosition(HumanBodyBones.LeftUpperArm, anim).y; double shoulder_height = getJointPosition(globalTarget, joint_map[MJointType.HeadJoint]).Y; double hip_height = getJointPosition(globalTarget, joint_map[MJointType.PelvisCentre]).Y; double spine_length = shoulder_height - hip_height; ((RJoint)this.parentJoint).boneLength = (this.GetOffsetPositions()).Multiply(spine_length).Magnitude(); this.GetOffsetPositions().X = 0; this.GetOffsetPositions().Y = 1.0; this.GetOffsetPositions().Z = 0; } if (this.joint.Type == MJointType.Root) { //MVector3 rootPos = getJointPosition(globalTarget, joint_map[MJointType.Root]); //this.SetOffsets(rootPos); } else if (this.joint.Type == MJointType.PelvisCentre) { MVector3 hips = getJointPosition(globalTarget, joint_map[MJointType.PelvisCentre]).Clone(); MVector3 shoulder = getJointPosition(globalTarget, joint_map[MJointType.LeftShoulder]); MVector3 leftupleg = getJointPosition(globalTarget, joint_map[MJointType.LeftHip]); // find the average z axis position of shoulders and hips to find initial position of hip hips.Z = (shoulder.Z + leftupleg.Z) / 2; // raise hip to the hight of the target avatars hip height. hips.Y = leftupleg.Y; MVector3 root = new MVector3(hips.X, 0, hips.Z); ((RJoint)this.parentJoint).SetOffsets(root); hips.X = 0; hips.Z = 0; this.SetOffsets(hips); } else { double factor = 0.0d; if (this.parentJoint.children.Count == 3 && this.parentJoint.children[0] != this) { // If there are two children (e.g. pelvis / upper spine), take the half distance between both children factor = GetJointDistance(globalTarget, joint_map[this.parentJoint.children[1].GetMJoint().Type], joint_map[this.parentJoint.children[2].GetMJoint().Type]) / 2.0d; } else { // If there is one child, take the parent bone length to scale target position factor = ((RJoint)this.parentJoint).boneLength; } // scale offset to scale the skeleton to match bone lengths. this.SetOffsets(this.GetOffsetPositions().Multiply(factor)); // handle special cases for shoulders and hands string jointID = this.GetMJoint().ID; if (jointID.Contains("Shoulder")) { // in case of shoulders, they have to be raised in up direction. double shoulder_height = getJointPosition(globalTarget, joint_map[this.GetMJoint().Type]).Y; double current_Pos = this.GetShoulderHeight(); shoulder_height = shoulder_height - current_Pos; this.GetOffsetPositions().Y = shoulder_height; } } if (this.children.Count > 0 && this.children[0].GetMJoint().ID.Contains("Tip")) { // Finger Tips MVector3 offsetVector = this.children[0].GetOffsetPositions(); this.boneLength = offsetVector.Magnitude(); } else { // if this is not the last joint in a sequence, scale depending on distance to child human bone if (joint_map.ContainsKey(this.children[0].GetMJoint().Type) && joint_map.ContainsKey(this.GetMJoint().Type)) { this.boneLength = GetJointDistance(globalTarget, joint_map[this.GetMJoint().Type], joint_map[this.children[0].GetMJoint().Type]); } else { this.boneLength = 0.1; } } // recurse foreach (Joint child in this.children) { if (!child.GetMJoint().ID.Contains("Tip")) { ((RJoint)(child)).ScaleSkeleton(globalTarget, joint_map); } } if (this.GetMJoint().ID.Contains("Wrist")) { MVector3 wristPos = this.GetGlobalPosition();//getJointPosition(globalTarget, joint_map[this.GetMJoint().Type]); MTransform Twrist = new MTransform("tmp", this.GetGlobalPosition(), this.GetGlobalRotation()); Joint middle = this.children[0]; Joint index = this.children[1]; Joint ring = this.children[2]; Joint little = this.children[3]; Joint thumb = this.children[4]; double middle_shift = 0.0; if (joint_map.ContainsKey(middle.GetMJoint().Type)) { MVector3 MiddlePos = ((RJoint)middle).GetFingerPosition(globalTarget, joint_map, 0); middle_shift = MiddlePos.Z; MiddlePos.Z = 0; ((RJoint)this.children[0]).SetOffsets(MiddlePos); } else { ((RJoint)this.children[0]).SetOffsets(new MVector3(0, 0.1, 0)); } if (joint_map.ContainsKey(index.GetMJoint().Type)) { MVector3 IndexPos = ((RJoint)index).GetFingerPosition(globalTarget, joint_map, middle_shift); ((RJoint)this.children[1]).SetOffsets(IndexPos); } else { ((RJoint)this.children[1]).SetOffsets(new MVector3(0, 0.1, 0.02)); } if (joint_map.ContainsKey(ring.GetMJoint().Type)) { MVector3 RingPos = ((RJoint)ring).GetFingerPosition(globalTarget, joint_map, middle_shift); ((RJoint)this.children[2]).SetOffsets(RingPos); } else { ((RJoint)this.children[2]).SetOffsets(new MVector3(0, 0.1, -0.02)); } if (joint_map.ContainsKey(little.GetMJoint().Type)) { MVector3 LittlePos = ((RJoint)little).GetFingerPosition(globalTarget, joint_map, middle_shift); ((RJoint)this.children[3]).SetOffsets(LittlePos); } else { ((RJoint)this.children[3]).SetOffsets(new MVector3(0, 0.1, -0.04)); } if (joint_map.ContainsKey(thumb.GetMJoint().Type)) { MVector3 ThumbPos = ((RJoint)thumb).GetFingerPosition(globalTarget, joint_map, middle_shift); ((RJoint)this.children[4]).SetOffsets(ThumbPos); } else { ((RJoint)this.children[4]).SetOffsets(new MVector3(0, 0.02, 0.02)); } } }
/// <summary> /// Method performs a local motion planning and tries to reach the specified goal position and rotation using the given velocity,angular velocity and time. /// </summary> /// <param name="velocity"></param> /// <param name="angularVelocity"></param> /// <param name="time"></param> /// <param name="currentPosition"></param> /// <param name="currentRotation"></param> /// <param name="targetPosition"></param> /// <param name="targetRotation"></param> /// <returns></returns> private MTransform DoLocalMotionPlanning(double velocity, double angularVelocity, TimeSpan time, MVector3 currentPosition, MQuaternion currentRotation, MVector3 targetPosition, MQuaternion targetRotation, bool collisionAvoidance) { //Create a new transform representing the result MTransform result = new MTransform(); //Estimate the delta MVector3 delta = targetPosition.Subtract(currentPosition); //Determine the current delta angle double angle = Math.Abs(MQuaternionExtensions.Angle(currentRotation, targetRotation)); //Determine the max translation delta and max angle in the current frame double maxTranslationDelta = velocity * time.TotalSeconds; double maxAngle = angularVelocity * time.TotalSeconds; //Estimate the blend weight for the rotation and position float rotationWeight = (float)Math.Min(1, maxAngle / angle); float positionWeight = (float)Math.Min(1, maxTranslationDelta / delta.Magnitude()); //Limit the max translation if (delta.Magnitude() >= maxTranslationDelta) { delta = delta.Normalize(); delta = delta.Multiply(maxTranslationDelta); } if (collisionAvoidance) { MVector3 collisionAvoidanceForce = this.ComputCollisionAvoidance(currentPosition, delta); //if (collisionAvoidanceForce.Magnitude() > 0) // MMICSharp.Adapter.Logger.Log(MMICSharp.Adapter.Log_level.L_INFO, "Collision avoidance force: " + collisionAvoidanceForce.Magnitude()); //Add the collision avoidance force on top delta = delta.Add(collisionAvoidanceForce); //Limit the max translation if (delta.Magnitude() >= maxTranslationDelta) { delta = delta.Normalize(); delta = delta.Multiply(maxTranslationDelta); } } //Compute the new position result.Position = currentPosition.Add(delta); //Compute the new rotation by interpolating towards the target rotation if (angularVelocity > 0) { result.Rotation = MQuaternionExtensions.Slerp(currentRotation, targetRotation, rotationWeight); } //Use the rotation weight else { result.Rotation = MQuaternionExtensions.Slerp(currentRotation, targetRotation, positionWeight); } //Return the simulation result return(result); }
public override MSimulationResult DoStep(double time, MSimulationState simulationState) { //Create a new simulation result MSimulationResult result = new MSimulationResult() { Events = simulationState.Events ?? new List <MSimulationEvent>(), Constraints = simulationState.Constraints ?? new List <MConstraint>(), SceneManipulations = simulationState.SceneManipulations ?? new List <MSceneManipulation>() }; //Assign the constraints to a temp varilable List <MConstraint> constraints = result.Constraints; //Use the constraint manager to manage the constraints constraintManager.SetConstraints(ref constraints); //Get the hand position and rotation of the last frame (approved result) this.SkeletonAccess.SetChannelData(simulationState.Initial); MVector3 currentHandPosition = this.SkeletonAccess.GetGlobalJointPosition(this.AvatarDescription.AvatarID, this.handJoint); MQuaternion currentHandRotation = this.SkeletonAccess.GetGlobalJointRotation(this.AvatarDescription.AvatarID, this.handJoint); //Get the desired hand position (of the underlying motion e.g. idle) this.SkeletonAccess.SetChannelData(simulationState.Current); MVector3 targetHandPosition = this.SkeletonAccess.GetGlobalJointPosition(this.AvatarDescription.AvatarID, this.handJoint); MQuaternion targetHandRotation = this.SkeletonAccess.GetGlobalJointRotation(this.AvatarDescription.AvatarID, this.handJoint); //Add an offset on top of the position if desired if (this.addOffset) { targetHandPosition = ComputeNewPositionWithOffset(targetHandPosition, simulationState.Current); } //Move the hand from the current position to the target position MVector3 deltaPosition = targetHandPosition.Subtract(currentHandPosition); //Compute the distance of the hand to the target hand position float distanceToGoal = deltaPosition.Magnitude(); //Create positioning finished event if not already created and distance below threshold if (distanceToGoal < this.positioningFinishedThreshold && !this.positioningFinished) { result.Events.Add(new MSimulationEvent("PositioningFinished", "PositioningFinished", this.instruction.ID)); this.positioningFinished = true; } //Compute the current velocity based on the general max velocity and the velocity of the root motion float currentVelocity = this.velocity + this.ComputeRootVelocity(time, simulationState); //Compute the max distance which can be covered within the current frame float maxDistance = (float)(time * currentVelocity); //Compute the weight for slerping (weight increases with shrinking distance to target) float weight = Math.Max(0, 1 - distanceToGoal); //Create a new transform representing the next hand transform MTransform newHandTransform = new MTransform("", currentHandPosition.Clone(), currentHandRotation.Clone()) { //Compute the new hand position (normalize delta position and multiply by max distance) Position = currentHandPosition.Add(deltaPosition.Normalize().Multiply(Math.Min(deltaPosition.Magnitude(), maxDistance))), //Just perform an interpolation to gather new hand rotation (weight is determined by the translation distance) Rotation = MQuaternionExtensions.Slerp(currentHandRotation, targetHandRotation, weight) }; //Compute the corresponding positon/rotation of the object and //adjust the transformation of the object which should be moved result.SceneManipulations.Add(new MSceneManipulation() { Transforms = new List <MTransformManipulation>() { new MTransformManipulation() { Target = this.objectTransform.ID, //Compute the new global position of the object Position = newHandTransform.TransformPoint(this.objectPositionOffset), //Compute the new global rotation of the object Rotation = newHandTransform.TransformRotation(this.objectRotationOffset) } } }); //Set the desired endeffector constraints constraintManager.SetEndeffectorConstraint(this.handJoint, newHandTransform.Position, newHandTransform.Rotation); //Generate a new posture using the ik solver and the specified constraints MIKServiceResult ikResult = this.ServiceAccess.IKService.CalculateIKPosture(simulationState.Current, constraintManager.GetJointConstraints(), new Dictionary <string, string>()); result.Posture = ikResult.Posture; //Return the result return(result); }
/// <summary> /// Do step routine in which the actual simulation result is generated /// </summary> /// <param name="time"></param> /// <param name="simulationState"></param> /// <returns></returns> public override MSimulationResult DoStep(double time, MSimulationState simulationState) { //Create a new simulation result MSimulationResult result = new MSimulationResult() { Events = simulationState.Events != null ? simulationState.Events : new List <MSimulationEvent>(), Constraints = simulationState.Constraints, SceneManipulations = simulationState.SceneManipulations != null ? simulationState.SceneManipulations : new List <MSceneManipulation>() }; //Create variables representing the next object position/rotation MTransform nextObjectTransform = subjectTransform.Clone(); //Use the constraint manager to manage the constraints List <MConstraint> tmpConstraints = result.Constraints; //Set the constraints constraintManager.SetConstraints(ref tmpConstraints); //Compute the new hand position and rotation MVector3 deltaPosition = this.targetObjectTransform.Position.Subtract(subjectTransform.Position); float distanceToGoal = deltaPosition.Magnitude(); //Get the current object position float maxDistance = (float)time * 1.0f; //Check the current distance to goal if (distanceToGoal < 0.01f) { result.Events.Add(new MSimulationEvent(this.instruction.Name, mmiConstants.MSimulationEvent_End, this.instruction.ID)); } else { //Compute the new hand position (normalize delta position and multiply by max distance) nextObjectTransform.Position = this.subjectTransform.Position.Add(deltaPosition.Normalize().Multiply(Math.Min(distanceToGoal, maxDistance))); //Compute the weight for slerping (weight increases with shrinking distance to target) float weight = Math.Max(0, 1 - distanceToGoal); //Just perform an interpolation to gather new hand rotation (weight is determined by the translation distance) nextObjectTransform.Rotation = MQuaternionExtensions.Slerp(this.subjectTransform.Rotation, this.targetObjectTransform.Rotation, weight); } //Adjust the transformation of the object which should be moved result.SceneManipulations.Add(new MSceneManipulation() { Transforms = new List <MTransformManipulation>() { new MTransformManipulation() { Target = this.subjectTransform.ID, Position = nextObjectTransform.Position, Rotation = nextObjectTransform.Rotation } } }); //Get the current hand position in global space MVector3 globalHandPosition = nextObjectTransform.TransformPoint(this.handPositionOffset); MQuaternion globalHandRotation = nextObjectTransform.TransformRotation(this.handRotationOffset); //Set the desired endeffector constraints constraintManager.SetEndeffectorConstraint(this.handJoint, globalHandPosition, globalHandRotation); MIKServiceResult ikResult = this.ServiceAccess.IKService.CalculateIKPosture(simulationState.Current, constraintManager.GetJointConstraints(), new Dictionary <string, string>()); //Generate a new posture using the ik solver and the specified constraints result.Posture = ikResult.Posture; //Return the result return(result); }
/// <summary> /// Basic euclidean distance function for float arrays /// </summary> /// <param name="vector1"></param> /// <param name="vector2"></param> /// <returns></returns> private static float EuclideanDistance(MVector3 vector1, MVector3 vector2) { MVector3 diff = vector1.Subtract(vector2); return(diff.Magnitude()); }