public static IList <MuscleJoint> CopyHierarchy(IList <MuscleJoint> joints) { IList <MuscleJoint> mirrorJoints = new MuscleJoint[joints.Count]; for (int i = 0; i < joints.Count; i++) { MuscleJoint originalJoint = joints[i]; Transform mirrorTransform = new GameObject(joints[i].name + "_IK").transform; mirrorTransform.position = originalJoint.transform.position; mirrorTransform.rotation = originalJoint.transform.rotation; MuscleJoint mirrorJoint = mirrorTransform.gameObject.AddComponent <MuscleJoint>(); mirrorJoint.Initialize(originalJoint); GameObject.Destroy(mirrorJoint.GetComponent <SphereCollider>()); // Todo: superhacky if (i > 0) { mirrorTransform.parent = mirrorJoints[i - 1].transform; } mirrorJoints[i] = mirrorJoint; } return(mirrorJoints); }
public static void MirrorHierarchy(IList <MuscleJoint> source, IList <MuscleJoint> target) { for (int i = 0; i < source.Count; i++) { MuscleJoint originalJoint = source[i]; MuscleJoint mirrorTransform = target[i]; mirrorTransform.transform.position = originalJoint.transform.position; mirrorTransform.transform.rotation = originalJoint.transform.rotation; } }
private static void RandomizeChain(MuscleJoint joint, float amount) { if (!joint.transform.parent) { return; } joint.transform.parent.localRotation = Quaternion.Slerp( joint.transform.parent.localRotation, Random.rotationUniform, amount); }
private static Quaternion ClampLimitsEllipse(MuscleJoint joint, Quaternion proposedRotation) { Quaternion jointRotation = Quaternion.Inverse(joint.BaseLocalRotation) * proposedRotation; Vector3 proposedForward = jointRotation * Vector3.forward; Vector3 swingAxis = Vector3.Cross(Vector3.forward, proposedForward).normalized; float angle = MathUtils.AngleAroundAxis(Vector3.forward, proposedForward, swingAxis); angle = Mathf.Clamp(angle, -45f, 45f); return(joint.BaseLocalRotation * Quaternion.AngleAxis(angle, swingAxis)); }
/** Instantiates a muscle at the specified point. */ private void CreateMuscleFromJoint(MuscleJoint joint) { Vector3 point = joint.position; point.z = 0; /*GameObject muscleEmpty = new GameObject(); * muscleEmpty.name = "Muscle"; * currentMuscle = muscleEmpty.AddComponent<Muscle>(); * currentMuscle.AddLineRenderer(); * currentMuscle.SetMaterial(muscleMaterial);*/ //currentMuscle = ((GameObject) Instantiate(musclePreset, point, Quaternion.identity)).GetComponent<Muscle>(); currentMuscle = Muscle.Create(); currentMuscle.startingJoint = joint; currentMuscle.SetLinePoints(joint.position, joint.position); }
private void ActivateMusclesJoint(float[] angles, MuscleJoint joint) { //YZ if (joint.musclesYZ != null && joint.musclesYZ.Length > 0) { if (angles[0] > 0) { joint.musclesYZ[0].targetLength = GetMuscleLengthFromAngle(joint.musclesYZ[0], angles[0]); joint.musclesYZ[0].Activate(); } else { joint.musclesYZ[1].targetLength = GetMuscleLengthFromAngle(joint.musclesYZ[1], Mathf.Abs(angles[0])); joint.musclesYZ[1].Activate(); } } //ZX if (joint.musclesZX != null && joint.musclesZX.Length > 0) { if (angles[1] > 0) { joint.musclesZX[0].targetLength = GetMuscleLengthFromAngle(joint.musclesZX[0], angles[1]); joint.musclesZX[0].Activate(); } else { joint.musclesZX[1].targetLength = GetMuscleLengthFromAngle(joint.musclesZX[1], Mathf.Abs(angles[1])); joint.musclesZX[1].Activate(); } } //XY if (joint.musclesXY != null && joint.musclesXY.Length > 0) { if (angles[2] > 0) { joint.musclesXY[0].targetLength = GetMuscleLengthFromAngle(joint.musclesXY[0], angles[2]); joint.musclesXY[0].Activate(); } else { joint.musclesXY[1].targetLength = GetMuscleLengthFromAngle(joint.musclesXY[1], Mathf.Abs(angles[2])); joint.musclesXY[1].Activate(); } } }
// Todo: Locking joints by clicking on them private void Update() { // TODO Buttons don't work anymore bool mouseLeftDown = Input.GetButtonDown("unity_mouse_0"); bool mouseLeftUp = Input.GetButtonUp("unity_mouse_0"); bool mouseLeft = Input.GetButton("unity_mouse_0"); Vector3 cursorPosition = Input.mousePosition; if (mouseLeftDown) { GameObject selectedObject = QueryCursor(cursorPosition); if (selectedObject) { MuscleJoint joint = selectedObject.GetComponent <MuscleJoint>(); if (joint) { StartDragging(joint); } else { _camera.Center = selectedObject.transform.position; } } } if (mouseLeftUp) { StopDragging(); } if (mouseLeft) { if (_isDragging) { Drag(cursorPosition); } else { Look(); } } if (_jointHierarchy != null && _ikHierarchy != null) { IKSolver.Slerp(_ikHierarchy, _jointHierarchy, 10f * _clock.DeltaTime); } }
private static Quaternion ClampLimitsEuler(MuscleJoint joint, Quaternion proposedRotation) { Quaternion jointRotation = Quaternion.Inverse(joint.BaseLocalRotation) * proposedRotation; float angleX = jointRotation.eulerAngles.x; float angleY = jointRotation.eulerAngles.y; float angleZ = jointRotation.eulerAngles.z; angleX = WrapAngle(angleX); angleY = WrapAngle(angleY); angleZ = WrapAngle(angleZ); angleX = Mathf.Clamp(angleX, joint.Limits.X.Min, joint.Limits.X.Max); angleY = Mathf.Clamp(angleY, joint.Limits.Y.Min, joint.Limits.Y.Max); angleZ = Mathf.Clamp(angleZ, joint.Limits.Z.Min, joint.Limits.Z.Max); return(joint.BaseLocalRotation * Quaternion.Euler(angleX, angleY, angleZ)); }
private void StartDragging(MuscleJoint joint) { _isDragging = true; if (_ikHierarchy != null) { DestroyHierarchy(_ikHierarchy); } _jointHierarchy = IKSolver.GetHierarchy(joint); _ikHierarchy = IKSolver.CopyHierarchy(_jointHierarchy); _ikTarget.position = joint.transform.position; _ikTarget.rotation = joint.transform.rotation; Vector3 screenSpacePosition = _camera.GetComponent <Camera>().WorldToScreenPoint(joint.transform.position); _dragDepth = screenSpacePosition.z; Vector3 cursorPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenSpacePosition.z); _dragOffset = joint.transform.position - _camera.GetComponent <Camera>().ScreenToWorldPoint(cursorPosition); }
public static IList <MuscleJoint> GetHierarchy(MuscleJoint root) { IList <MuscleJoint> joints = new List <MuscleJoint>(); MuscleJoint joint = root; while (joint != null) { joints.Add(joint); joint = joint.transform.parent ? joint.transform.parent.GetComponent <MuscleJoint>() : null; } IList <MuscleJoint> orderedJoints = new List <MuscleJoint>(); for (int i = 0; i < joints.Count; i++) { orderedJoints.Add(joints[joints.Count - 1 - i]); } return(orderedJoints); }
private static bool IsWithinLimits(MuscleJoint joint, Quaternion proposedRotation) { Quaternion jointRotation = Quaternion.Inverse(joint.BaseLocalRotation) * proposedRotation; float angleX = jointRotation.eulerAngles.x; float angleY = jointRotation.eulerAngles.y; float angleZ = jointRotation.eulerAngles.z; angleX = WrapAngle(angleX); angleY = WrapAngle(angleY); angleZ = WrapAngle(angleZ); if (angleX > joint.Limits.X.Min || angleX < joint.Limits.X.Max || angleY > joint.Limits.Y.Min || angleY < joint.Limits.Y.Max || angleZ > joint.Limits.Z.Min || angleZ < joint.Limits.Z.Max) { return(false); } return(false); }
public void Initialize(MuscleJoint joint) { _joint = joint; _children = new List <IKJoint>(); }
/// <summary> /// Checks for click / touch events and handles them appropiately depending on the /// currently selected body part. /// </summary> private void HandleClicks() { // Middle click or two touches to move the camera if ((Input.GetMouseButton(2) && Input.touchCount == 0) || Input.touchCount == 2) { //if (EventSystem.current.IsPointerOverGameObject() || isPointerOverUIObject()) return; var position = Input.mousePosition; if (Input.touchCount == 2) { position = GetPinchCenter(Input.touches[0].position, Input.touches[1].position); } //position = ScreenToWorldPoint(position); var distance = lastTouchPos - position; lastTouchPos = position; if (firstMovementTouch) { firstMovementTouch = false; return; } firstMovementTouch = false; // move the camera by the distance distance = ScreenToWorldDistance(distance); buttonManager.MoveCamera(distance); return; } else { firstMovementTouch = true; lastTouchPos = Vector3.zero; } if (Input.GetMouseButtonDown(0)) // user clicked { if (EventSystem.current.IsPointerOverGameObject()) { return; } if (isPointerOverUIObject()) { return; } if (selectedPart == BuildSelection.Joint) // Place a JOINT { var pos = ScreenToWorldPoint(Input.mousePosition); pos.z = 0; // Grid logic if (grid.gameObject.activeSelf) { pos = grid.ClosestPointOnGrid(pos); } // Make sure the joint doesn't overlap another one bool noOverlap = true; foreach (var joint in joints) { if ((joint.center - pos).magnitude < jointNonOverlapRadius) { noOverlap = false; break; } } if (noOverlap) { PlaceJoint(pos); } } else if (selectedPart == BuildSelection.Bone) // Start placing BONE // find the selected joint { Joint joint = GetHoveringObject <Joint>(joints); if (joint != null) { CreateBoneFromJoint(joint); PlaceConnectionBetweenPoints(currentBone.gameObject, joint.center, ScreenToWorldPoint(Input.mousePosition), CONNECTION_WIDHT); } } else if (selectedPart == BuildSelection.Muscle) // Start placing MUSCLE // find the selected bone { Bone bone = GetHoveringObject <Bone>(bones); if (bone != null) { Vector3 mousePos = ScreenToWorldPoint(Input.mousePosition); MuscleJoint joint = bone.muscleJoint; CreateMuscleFromJoint(joint); //PlaceConnectionBetweenPoints(currentMuscle.gameObject, joint.position, mousePos, CONNECTION_WIDHT); currentMuscle.SetLinePoints(joint.position, mousePos); } } else if (selectedPart == BuildSelection.Delete) // Delete selected object //UpdateDeletedObjects(); { BodyComponent joint = GetHoveringObject <Joint>(joints); BodyComponent bone = GetHoveringObject <Bone>(bones); BodyComponent muscle = GetHoveringObject <Muscle>(muscles); BodyComponent toDelete = joint != null ? joint : (bone != null ? bone : muscle); if (toDelete != null) { toDelete.Delete(); UpdateDeletedObjects(); ResetCurrentCreatureName(); // The creature was modified Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto); } //UpdateDeletedObjects(); } else if (selectedPart == BuildSelection.Move) { // Make sure the user is hovering over a joint Joint joint = GetHoveringObject <Joint>(joints); if (joint != null) { currentMovingJoint = joint; ResetCurrentCreatureName(); // The creature was modified } } } else if (Input.GetMouseButton(0)) { // Mouse click & hold if (selectedPart == BuildSelection.Bone) { if (currentBone != null) { // check if user is hovering over an ending joint which is not the same as the starting // joint of the currentBone Joint joint = GetHoveringObject <Joint>(joints); Vector3 endingPoint = ScreenToWorldPoint(Input.mousePosition); if (joint != null && !joint.Equals(currentBone.startingJoint)) { endingPoint = joint.center; currentBone.endingJoint = joint; } PlaceConnectionBetweenPoints(currentBone.gameObject, currentBone.startingPoint, endingPoint, CONNECTION_WIDHT); } } else if (selectedPart == BuildSelection.Muscle) { if (currentMuscle != null) { // check if user is hovering over an ending joint which is not the same as the starting // joint of the currentMuscle Bone bone = GetHoveringObject <Bone>(bones); Vector3 endingPoint = ScreenToWorldPoint(Input.mousePosition); if (bone != null) { MuscleJoint joint = bone.muscleJoint; if (!joint.Equals(currentMuscle.startingJoint)) { endingPoint = joint.position; currentMuscle.endingJoint = joint; } else { currentMuscle.endingJoint = null; } } else { currentMuscle.endingJoint = null; } //PlaceConnectionBetweenPoints(currentMuscle.gameObject, currentMuscle.startingPoint, endingPoint, CONNECTION_WIDHT); currentMuscle.SetLinePoints(currentMuscle.startingPoint, endingPoint); } } else if (selectedPart == BuildSelection.Move) { if (currentMovingJoint != null) { // Move the joint to the mouse position. var newPoint = ScreenToWorldPoint(Input.mousePosition); if (grid.gameObject.activeSelf) { newPoint = grid.ClosestPointOnGrid(newPoint); } newPoint.z = 0; currentMovingJoint.MoveTo(newPoint); ResetCurrentCreatureName(); // The creature was modified } } } else if (Input.GetMouseButtonUp(0)) { if (selectedPart == BuildSelection.Bone) { PlaceCurrentBone(); } else if (selectedPart == BuildSelection.Muscle) { PlaceCurrentMuscle(); } else if (selectedPart == BuildSelection.Move) { currentMovingJoint = null; } } }
// Cyclic Coordinate Descent public static void Evaluate(IList <MuscleJoint> joints, Transform target) { MuscleJoint endEffector = joints[joints.Count - 1]; float error = (target.position - endEffector.transform.position).magnitude; if (error <= MaxError) { return; } bool done = false; int numIterations = 0; while (!done && numIterations < MaxIterations) { // From tip to root, skipping end effector for (int i = joints.Count - 2; i >= 0; i--) { MuscleJoint joint = joints[i]; Vector3 localJointNormal = joints[i + 1].transform.localPosition.normalized; // Find rotation to target Vector3 targetDirection = target.position - joint.transform.position; Vector3 localTargetDirection = joint.transform.parent ? joint.transform.parent.InverseTransformDirection( targetDirection) : targetDirection; Quaternion targetDelta = Quaternion.FromToRotation(joint.transform.localRotation * localJointNormal, localTargetDirection); // Find rotation to end effector Vector3 endDirection = endEffector.transform.position - joint.transform.position; Vector3 localEndDirection = joint.transform.parent ? joint.transform.parent.InverseTransformDirection(endDirection) : endDirection; Quaternion endDelta = Quaternion.FromToRotation(joint.transform.localRotation * localJointNormal, localEndDirection); // Construct a rotation that puts end effector on the line to (e.g. closest to) the target Quaternion proposedLocalRotation = targetDelta * Quaternion.Inverse(endDelta) * joint.transform.localRotation; // Enforce joint limits // Ensure bone rotation stays within limits if (!IsWithinLimits(joint, proposedLocalRotation)) { proposedLocalRotation = ClampToLimits(joint, proposedLocalRotation); //RandomizeChain(joint, 0.01f); } joint.transform.localRotation = proposedLocalRotation; } // Early out if we've reached a local minimum float newError = (target.position - endEffector.transform.position).magnitude; if (newError <= MaxError || error - newError < 0.01f) { done = true; } error = newError; numIterations++; } UnityEngine.Debug.Log(numIterations); }
public static void Slerp(MuscleJoint source, MuscleJoint target, float lerp) { target.transform.localRotation = Quaternion.Slerp(target.transform.localRotation, source.transform.localRotation, lerp); }
public void Initialize(MuscleJoint joint) { _baseLocalRotation = joint.BaseLocalRotation; _limits = joint.Limits; }
private static Quaternion ClampToLimits(MuscleJoint joint, Quaternion proposedRotation) { return(ClampLimitsEuler(joint, proposedRotation)); }