initial upload

This commit is contained in:
tom.hempel
2025-09-21 22:42:26 +02:00
commit d03bcd4ba5
6231 changed files with 351582 additions and 0 deletions

View File

@ -0,0 +1,38 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for AimIK.
* */
[CustomEditor(typeof(AimIK))]
public class AimIKInspector : IKInspector {
private AimIK script { get { return target as AimIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverAim
IKSolverAimInspector.AddInspector(solver, !Application.isPlaying);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverAimInspector.AddScene(script.solver, new Color(1f, 0f, 0.5f, 1f), true);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 4a8f3f357746148a98faf7ccd416a2a6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,189 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using RootMotion;
namespace RootMotion.FinalIK {
/// <summary>
/// Custom inspector for the Aim Poser for visualizing pose range
/// </summary>
[CustomEditor(typeof(AimPoser))]
public class AimPoserInspector : Editor {
[System.Serializable]
public struct ColorDirection {
public Vector3 direction;
public Vector3 color;
public float dot;
public ColorDirection(Vector3 direction, Vector3 color) {
this.direction = direction.normalized;
this.color = color;
this.dot = 0;
}
}
private AimPoser script { get { return target as AimPoser; }}
private ColorDirection[] colorDirections;
private static Vector3[] poly = new Vector3[36];
void OnSceneGUI() {
for (int i = 0; i < script.poses.Length; i++) {
script.poses[i].yaw = Mathf.Clamp(script.poses[i].yaw, 0, 180);
script.poses[i].pitch = Mathf.Clamp(script.poses[i].pitch, 0, 180);
}
if (colorDirections == null) {
colorDirections = new ColorDirection[6] {
new ColorDirection(Vector3.right, Vector3.right),
new ColorDirection(Vector3.up, Vector3.up),
new ColorDirection(Vector3.forward, Vector3.forward),
new ColorDirection(Vector3.left, new Vector3(0f, 1f, 1f)),
new ColorDirection(Vector3.down, new Vector3(1f, 0f, 1f)),
new ColorDirection(Vector3.back, new Vector3(1f, 1f, 0f))
};
}
for (int i = 0; i < script.poses.Length; i++) {
if (script.poses[i].visualize) {
DrawPose(script.poses[i], script.transform.position, script.transform.rotation, GetDirectionColor(script.poses[i].direction));
}
}
}
private Color GetDirectionColor(Vector3 localDirection) {
localDirection = localDirection.normalized;
// Calculating dot products for all AxisDirections
for (int i = 0; i < colorDirections.Length; i++) {
colorDirections[i].dot = Mathf.Clamp(Vector3.Dot(colorDirections[i].direction, localDirection), 0f, 1f);
}
// Summing up the arm bend axis
Vector3 sum = Vector3.zero;
for (int i = 0; i < colorDirections.Length; i++) {
sum = Vector3.Lerp(sum, colorDirections[i].color, colorDirections[i].dot * colorDirections[i].dot);
}
return new Color(sum.x, sum.y, sum.z);
}
private static void DrawPose(AimPoser.Pose pose, Vector3 position, Quaternion rotation, Color color) {
if (pose.pitch <= 0f || pose.yaw <= 0f) return;
if (pose.direction == Vector3.zero) return;
Handles.color = color;
GUI.color = color;
Vector3 up = rotation * Vector3.up;
Vector3 normalizedPoseDirection = pose.direction.normalized;
Vector3 direction = rotation * normalizedPoseDirection;
// Direction and label
Handles.DrawLine(position, position + direction);
Inspector.ConeCap(0, position + direction, Quaternion.LookRotation(direction), 0.05f);
Handles.Label(position + direction.normalized * 1.1f, pose.name);
if (pose.yaw >= 180f && pose.pitch >= 180f) {
Handles.color = Color.white;
GUI.color = Color.white;
return;
}
Quaternion halfYaw = Quaternion.AngleAxis(pose.yaw, up);
float directionPitch = Vector3.Angle(up, direction);
Vector3 crossRight = halfYaw * Vector3.Cross(up, direction);
Vector3 crossLeft = Quaternion.Inverse(halfYaw) * Vector3.Cross(up, direction);
bool isVertical = normalizedPoseDirection == Vector3.up || normalizedPoseDirection == Vector3.down;
if (isVertical) {
crossRight = halfYaw * Vector3.right;
crossLeft = Quaternion.Inverse(halfYaw) * Vector3.right;
}
float minPitch = Mathf.Clamp(directionPitch - pose.pitch, 0f, 180f);
float maxPitch = Mathf.Clamp(directionPitch + pose.pitch, 0f, 180f);
Quaternion upToCornerUpperRight = Quaternion.AngleAxis(minPitch, crossRight);
Quaternion upToCornerLowerRight = Quaternion.AngleAxis(maxPitch, crossRight);
Quaternion upToCornerUpperLeft = Quaternion.AngleAxis(minPitch, crossLeft);
Quaternion upToCornerLowerLeft = Quaternion.AngleAxis(maxPitch, crossLeft);
Vector3 toCornerUpperRight = upToCornerUpperRight * up;
Vector3 toCornerLowerRight = upToCornerLowerRight * up;
Vector3 toCornerUpperLeft = upToCornerUpperLeft * up;
Vector3 toCornerLowerLeft = upToCornerLowerLeft * up;
if (pose.yaw < 180f) {
Handles.DrawLine(position, position + toCornerUpperRight);
Handles.DrawLine(position, position + toCornerUpperLeft);
Handles.DrawLine(position, position + toCornerLowerRight);
Handles.DrawLine(position, position + toCornerLowerLeft);
}
Vector3 d = direction;
if (isVertical) d = Vector3.forward;
if (pose.pitch < 180f) {
DrawPolyLineOnSphere(position, toCornerUpperLeft, toCornerUpperRight, d, Vector3.up, color);
DrawPolyLineOnSphere(position, toCornerLowerLeft, toCornerLowerRight, d, Vector3.up, color);
}
if (pose.yaw < 180f) {
DrawPolyLineOnSphere(position, toCornerUpperLeft, toCornerLowerLeft, Quaternion.Inverse(halfYaw) * d, crossLeft, color);
DrawPolyLineOnSphere(position, toCornerUpperRight, toCornerLowerRight, halfYaw * d, crossRight, color);
}
Handles.color = Color.white;
GUI.color = Color.white;
}
private static void DrawPolyLineOnSphere(Vector3 center, Vector3 d1, Vector3 d2, Vector3 direction, Vector3 axis, Color color) {
Handles.color = color;
Vector3 normal = axis;
Vector3 d1Ortho = d1;
Vector3.OrthoNormalize(ref normal, ref d1Ortho);
normal = axis;
Vector3 d2Ortho = d2;
Vector3.OrthoNormalize(ref normal, ref d2Ortho);
normal = axis;
Vector3 directionOrtho = direction;
Vector3.OrthoNormalize(ref normal, ref directionOrtho);
float angle = Vector3.Angle(d1Ortho, d2Ortho);
float dot = Vector3.Dot(directionOrtho, d1Ortho);
if (dot < 0) {
angle = 180 + (180 - angle);
}
int segments = Mathf.Clamp(Mathf.RoundToInt(angle / 36f) * 5, 3, 36);
float segmentF = angle / (float)(segments - 1);
for (int i = 0; i < segments; i++) {
poly[i] = center + Quaternion.AngleAxis(i * segmentF, axis) * d1;
}
Handles.color = new Color(color.r, color.g, color.b, color.a * 0.1f);
for (int i = 0; i < segments; i++) {
Handles.DrawLine(center, poly[i]);
}
Handles.color = color;
for (int i = 0; i < segments - 1; i++) {
Handles.DrawLine(poly[i], poly[i + 1]);
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 079587d3635eb48c3a5c0d57f8c3cc6f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for ArmIK.
* */
[CustomEditor(typeof(ArmIK))]
public class ArmIKInspector : IKInspector {
private ArmIK script { get { return target as ArmIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverTrigonometric
IKSolverArmInspector.AddInspector(solver, !Application.isPlaying, true);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverArmInspector.AddScene(script.solver, new Color(0f, 1f, 1f, 1f), true);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 07bc62a41db738b488fc9430a0a20772
timeCreated: 1528379362
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,115 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
using System;
namespace RootMotion.FinalIK {
/*
* Custom inspector for Biped IK.
* */
[CustomEditor(typeof(BipedIK))]
public class BipedIKInspector : Editor {
private BipedIK script { get { return target as BipedIK; }}
private int selectedSolver = -1;
private SerializedProperty references, solvers;
private SerializedProperty[] solversProps;
private SerializedContent fixTransforms;
public void OnEnable() {
if (serializedObject == null) return;
// Store the MonoScript for changing script execution order
if (!Application.isPlaying) {
MonoScript monoScript = MonoScript.FromMonoBehaviour(script);
// Changing the script execution order to make sure BipedIK always executes after any other script except FullBodyBipedIK
int executionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (executionOrder != 9998) MonoImporter.SetExecutionOrder(monoScript, 9998);
}
references = serializedObject.FindProperty("references");
solvers = serializedObject.FindProperty("solvers");
solversProps = BipedIKSolversInspector.FindProperties(solvers);
fixTransforms = new SerializedContent(serializedObject.FindProperty("fixTransforms"), new GUIContent("Fix Transforms", "If true, will fix all the Transforms used by the solver to their initial state in each Update. This prevents potential problems with unanimated bones and animator culling with a small cost of performance."));
// Automatically detecting references
if (!Application.isPlaying) {
if (script.references.isEmpty) {
BipedReferences.AutoDetectReferences(ref script.references, script.transform, new BipedReferences.AutoDetectParams(false, true));
references.isExpanded = true;
solvers.isExpanded = false;
for (int i = 0; i < solversProps.Length; i++) solversProps[i].isExpanded = false;
// Setting default values and initiating
script.InitiateBipedIK();
script.SetToDefaults();
EditorUtility.SetDirty(script);
} else script.InitiateBipedIK();
Warning.logged = false;
string message = string.Empty;
if (Application.isPlaying) {
if (BipedReferences.SetupError(script.references, ref message) || BipedReferences.SetupWarning(script.references, ref message)) {
Warning.Log(message, script.references.root, false);
}
}
}
}
// Override the default warning box
private void AddWarningBox(string message) {
EditorGUILayout.Space();
EditorGUILayout.LabelField("Invalid/incomplete setup, can't initiate solver. " + message, EditorStyles.helpBox);
EditorGUILayout.Space();
}
public override void OnInspectorGUI() {
serializedObject.Update();
EditorGUILayout.Space();
Inspector.AddContent(fixTransforms);
string message = string.Empty;
// Editing References
if (BipedReferencesInspector.AddModifiedInspector(references)) {
if (!Application.isPlaying) {
Warning.logged = false;
if (!BipedReferences.SetupError(script.references, ref message)) {
script.InitiateBipedIK();
}
}
}
if (BipedReferences.SetupError(script.references, ref message)) {
// Warning box
AddWarningBox(message);
Warning.Log(message, script.transform, false);
} else {
// Editing Solvers
BipedIKSolversInspector.AddInspector(solvers, solversProps);
}
EditorGUILayout.Space();
serializedObject.ApplyModifiedProperties();
}
void OnSceneGUI() {
if (!script.enabled) return;
// Draw the scene view helpers for the solvers
BipedIKSolversInspector.AddScene(script.solvers, ref selectedSolver);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: f5864bb0a05eb491697063685c4d6cb3
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,120 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for Biped IK Solvers.
* */
public class BipedIKSolversInspector: IKSolverInspector {
/*
* Returns all solvers SeiralizedProperties
* */
public static SerializedProperty[] FindProperties(SerializedProperty prop) {
SerializedProperty[] props = new SerializedProperty[8] {
prop.FindPropertyRelative("leftFoot"),
prop.FindPropertyRelative("rightFoot"),
prop.FindPropertyRelative("leftHand"),
prop.FindPropertyRelative("rightHand"),
prop.FindPropertyRelative("spine"),
prop.FindPropertyRelative("aim"),
prop.FindPropertyRelative("lookAt"),
prop.FindPropertyRelative("pelvis"),
};
return props;
}
/*
* Draws the custom inspector for BipedIK.Solvers
* */
public static void AddInspector(SerializedProperty prop, SerializedProperty[] props) {
EditorGUILayout.PropertyField(prop, false);
if (prop.isExpanded) {
for (int i = 0; i < props.Length; i++) {
BeginProperty(props[i]);
if (props[i].isExpanded) {
if (i <= 3) IKSolverLimbInspector.AddInspector(props[i], false, false);
else if (i == 4) IKSolverHeuristicInspector.AddInspector(props[i], false, false);
else if (i == 5) IKSolverAimInspector.AddInspector(props[i], false);
else if (i == 6) IKSolverLookAtInspector.AddInspector(props[i], false, false);
else if (i == 7) ConstraintsInspector.AddInspector(props[i]);
}
EndProperty(props[i]);
}
}
}
/*
* Draws the scene view helpers for BipedIK.Solvers
* */
public static void AddScene(BipedIKSolvers solvers, ref int selected) {
// Draw limbs
for (int i = 0; i < solvers.limbs.Length; i++) {
IKSolverLimbInspector.AddScene(solvers.limbs[i] as IKSolverLimb, GetSolverColor(i), selected == i);
}
// Draw spine
IKSolverHeuristicInspector.AddScene(solvers.spine, GetSolverColor(4), selected == 4);
// Draw look at
IKSolverLookAtInspector.AddScene(solvers.lookAt, GetSolverColor(5), selected == 5);
// Draw aim
IKSolverAimInspector.AddScene(solvers.aim, GetSolverColor(6), selected == 6);
// Draw constraints
ConstraintsInspector.AddScene(solvers.pelvis, GetSolverColor(7), selected == 7);
// Selecting solvers
if (Application.isPlaying) {
for (int i = 0; i < solvers.ikSolvers.Length; i++) {
Handles.color = GetSolverColor(i);
if (solvers.ikSolvers[i].GetIKPositionWeight() > 0 && selected != i && solvers.ikSolvers[i].initiated) {
if (Inspector.DotButton(solvers.ikSolvers[i].GetIKPosition(), Quaternion.identity, GetHandleSize(solvers.ikSolvers[i].GetIKPosition()), GetHandleSize(solvers.ikSolvers[i].GetIKPosition()))) selected = i;
}
}
if ((solvers.pelvis.positionWeight > 0 || solvers.pelvis.rotationWeight > 0) && selected != solvers.ikSolvers.Length) {
Handles.color = GetSolverColor(7);
if (Inspector.DotButton(solvers.pelvis.position, Quaternion.identity, GetHandleSize(solvers.pelvis.position), GetHandleSize(solvers.pelvis.position))) selected = solvers.ikSolvers.Length;
}
}
}
/*
* Gets the color of the solver at index.
* */
private static Color GetSolverColor(int index) {
if (index == 0 || index == 2) return new Color(0f, 0.8f, 1f, 1f); // Left limb
if (index == 1 || index == 3) return new Color(0.3f, 1f, 0.3f, 1f); // Right limb
if (index == 4) return new Color(1f, 0.5f, 0.5f, 1f); // Spine
if (index == 5) return new Color(0.2f, 0.5f, 1f, 1f); // Look At
if (index == 6) return new Color(1f, 0f, 0.5f, 1f); // Aim
if (index == 7) return new Color(0.9f, 0.9f, 0.9f, 1f); // Pelvis
return Color.white;
}
/*
* Begin property box
* */
private static void BeginProperty(SerializedProperty prop) {
EditorGUI.indentLevel = 1;
EditorGUILayout.BeginVertical("Box");
EditorGUILayout.PropertyField(prop, false);
}
/*
* End Property box
* */
private static void EndProperty(SerializedProperty prop) {
EditorGUILayout.EndVertical();
if (prop.isExpanded) EditorGUILayout.Space();
EditorGUI.indentLevel = 1;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0569aef68e5c24672b37607a948d3c92
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for CCDIK.
* */
[CustomEditor(typeof(CCDIK))]
public class CCDIKInspector : IKInspector {
private CCDIK script { get { return target as CCDIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverCCD
IKSolverHeuristicInspector.AddInspector(solver, !Application.isPlaying, true);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverHeuristicInspector.AddScene(script.solver, Color.cyan, true);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 90fdc724098474c78a5ae764777a5008
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,67 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for Constraints
* */
public class ConstraintsInspector: IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for Constraints
* */
public static void AddInspector(SerializedProperty prop) {
if (!prop.isExpanded) return;
// Main properties
EditorGUILayout.PropertyField(prop.FindPropertyRelative("target"), new GUIContent("Target", "Target transform for the pelvis (optional). If assigned, will overwrite pelvis.position in each update."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("positionOffset"), new GUIContent("Pos Offset", "Pelvis offset from animation. If there is no animation playing and Fix Transforms is unchecked, it will make the character fly away."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("positionWeight"), new GUIContent("Pos Weight", "The weight of lerping the pelvis to bipedIK.solvers.pelvis.position."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("rotationOffset"), new GUIContent("Rot Offset", "Pelvis rotation offset from animation. If there is no animation playing and Fix Transforms is unchecked, it will make the character spin."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("rotationWeight"), new GUIContent("Rot Weight", "The weiight of slerping the pelvis to bipedIK.solver.pelvis.rotation."));
EditorGUILayout.Space();
}
/*
* Draws the scene view helpers for Constraints
* */
public static void AddScene(Constraints constraints, Color color, bool modifiable) {
if (!constraints.IsValid()) return;
Handles.color = color;
GUI.color = color;
// Transform
Inspector.SphereCap(0, constraints.transform.position, Quaternion.identity, GetHandleSize(constraints.transform.position));
// Target
Handles.color = new Color(color.r, color.g, color.b, color.a * constraints.positionWeight);
Handles.DrawLine(constraints.transform.position, constraints.position);
Handles.color = color;
if (Application.isPlaying && modifiable && (constraints.positionWeight > 0 || constraints.rotationWeight > 0)) {
Inspector.CubeCap(0, constraints.position, Quaternion.Euler(constraints.rotation), GetHandleSize(constraints.transform.position));
// Manipulating position and rotation
switch(Tools.current) {
case Tool.Move:
constraints.position = Handles.PositionHandle(constraints.position, Quaternion.Euler(constraints.rotation));
break;
case Tool.Rotate:
constraints.rotation = Handles.RotationHandle(Quaternion.Euler(constraints.rotation), constraints.position).eulerAngles;
break;
}
}
Handles.color = Color.white;
GUI.color = Color.white;
}
#endregion Public methods
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b9903cbc6483347caadff80dc461c23f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,87 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
namespace RootMotion.FinalIK
{
[CustomEditor(typeof(EditorIK))]
public class EditorIKInspector : Editor
{
private EditorIK script { get { return target as EditorIK; } }
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (Application.isPlaying) return;
if (!script.enabled) return;
EditorGUILayout.Space();
if (script.defaultPose != null && script.ik != null && !script.ik.GetIKSolver().executedInEditor)
{
if (GUILayout.Button("Store Default Pose"))
{
script.StoreDefaultPose();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(script.defaultPose);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
if (script.defaultPose.poseStored && script.defaultPose.localPositions.Length == script.bones.Length)
{
if (GUILayout.Button("Reset To Default Pose"))
{
script.defaultPose.Restore(script.bones);
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
}
}
EditorGUILayout.Space();
if (script.defaultPose != null && script.defaultPose.poseStored && script.ik != null)
{
if (!script.ik.GetIKSolver().executedInEditor)
{
bool isValid = script.ik.GetIKSolver().IsValid();
EditorGUI.BeginDisabledGroup(!isValid);
if (GUILayout.Button(isValid? "Start Solver": "'Start Solver' disabled for invalid solver setup"))
{
bool initiated = script.Initiate();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(script.defaultPose);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
var ikS = new SerializedObject(script.ik);
ikS.FindProperty("solver").FindPropertyRelative("executedInEditor").boolValue = initiated;
ikS.ApplyModifiedProperties();
script.Update();
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
EditorGUI.EndDisabledGroup();
}
if (script.ik.GetIKSolver().executedInEditor)
{
if (GUILayout.Button("Stop"))
{
var ikS = new SerializedObject(script.ik);
ikS.FindProperty("solver").FindPropertyRelative("executedInEditor").boolValue = false;
ikS.ApplyModifiedProperties();
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4bdb775e141709e40972e0ed29aecc04
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for FABRIK.
* */
[CustomEditor(typeof(FABRIK))]
public class FABRIKInspector : IKInspector {
private FABRIK script { get { return target as FABRIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverFABRIK
IKSolverHeuristicInspector.AddInspector(solver, !Application.isPlaying, false);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverHeuristicInspector.AddScene(script.solver, Color.cyan, true);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 35028d751824a4378ab31047db78a4ac
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for FABRIKRoot.
* */
[CustomEditor(typeof(FABRIKRoot))]
public class FABRIKRootInspector : IKInspector {
private FABRIKRoot script { get { return target as FABRIKRoot; }}
private FABRIKChain selectedChain;
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void AddInspector() {
// Draw the inspector for IKSolverFABRIKRoot
IKSolverFABRIKRootInspector.AddInspector(solver, !Application.isPlaying);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverFABRIKRootInspector.AddScene(script.solver, Color.cyan, true, ref selectedChain);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 97f4f303b23be42ddabd838850dd9c03
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,99 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion.FinalIK {
// Custom Scene View handles for the FingerRig.
[CustomEditor(typeof(FingerRig))]
public class FingerRigInspector : Editor {
private FingerRig script { get { return target as FingerRig; }}
private int selected = -1;
private MonoScript monoScript;
void OnEnable() {
if (serializedObject == null) return;
// Changing the script execution order
if (!Application.isPlaying) {
monoScript = MonoScript.FromMonoBehaviour(script);
int currentExecutionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (currentExecutionOrder != 10000) MonoImporter.SetExecutionOrder(monoScript, 10000);
}
}
void OnSceneGUI() {
if (!script.enabled) return;
string message = string.Empty;
if (!script.IsValid(ref message)) return;
if (Application.isPlaying && !script.initiated) return;
Color color = Color.cyan;
color.a = script.weight;
Handles.color = color;
GUI.color = color;
// Display the bones
if (!Application.isPlaying) {
for (int i = 0; i < script.fingers.Length; i++) {
Handles.DrawLine(script.fingers[i].bone1.position, script.fingers[i].bone2.position);
Inspector.SphereCap(0, script.fingers[i].bone1.position, Quaternion.identity, IKSolverInspector.GetHandleSize(script.fingers[i].bone1.position) * 0.5f);
Inspector.SphereCap(0, script.fingers[i].bone2.position, Quaternion.identity, IKSolverInspector.GetHandleSize(script.fingers[i].bone2.position) * 0.5f);
if (script.fingers[i].bone3 != null) {
Handles.DrawLine(script.fingers[i].bone2.position, script.fingers[i].bone3.position);
Handles.DrawLine(script.fingers[i].bone3.position, script.fingers[i].tip.position);
Inspector.SphereCap(0, script.fingers[i].bone3.position, Quaternion.identity, IKSolverInspector.GetHandleSize(script.fingers[i].bone3.position) * 0.5f);
} else {
Handles.DrawLine(script.fingers[i].bone2.position, script.fingers[i].tip.position);
}
Inspector.SphereCap(0, script.fingers[i].tip.position, Quaternion.identity, IKSolverInspector.GetHandleSize(script.fingers[i].tip.position) * 0.5f);
}
}
// Selecting solvers
if (Application.isPlaying) {
if (selected >= 0 && selected < script.fingers.Length) {
if (script.fingers[selected].weight > 0f) {
color.a = script.weight * script.fingers[selected].weight;
Handles.color = color;
float size = IKSolverInspector.GetHandleSize(script.fingers[selected].IKPosition);
Inspector.CubeCap(0, script.fingers[selected].IKPosition, script.fingers[selected].IKRotation, size);
if (script.fingers[selected].target == null) {
switch(Tools.current) {
case Tool.Move:
script.fingers[selected].IKPosition = Handles.PositionHandle(script.fingers[selected].IKPosition, Tools.pivotRotation == PivotRotation.Local? script.fingers[selected].IKRotation: Quaternion.identity);
break;
case Tool.Rotate:
script.fingers[selected].IKRotation = Handles.RotationHandle(script.fingers[selected].IKRotation, script.fingers[selected].IKPosition);
break;
}
}
}
}
for (int i = 0; i < script.fingers.Length; i++) {
color.a = script.weight * script.fingers[i].weight;
Handles.color = color;
Handles.DrawLine(script.fingers[i].tip.position, script.fingers[i].IKPosition);
if (script.fingers[i].weight > 0 && selected != i && script.fingers[i].initiated) {
float size = IKSolverInspector.GetHandleSize(script.fingers[i].IKPosition) * 0.5f;
if (Inspector.DotButton(script.fingers[i].IKPosition, Quaternion.identity, size, size)) selected = i;
}
}
}
Handles.color = Color.white;
GUI.color = Color.white;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: aa6fa0b5b780d4401b031336dd86cfa4
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,107 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for FullBodyBipedIK.
* */
[CustomEditor(typeof(FullBodyBipedIK))]
public class FullBodyBipedIKInspector : IKInspector {
private FullBodyBipedIK script { get { return target as FullBodyBipedIK; }}
private int selectedEffector;
private SerializedProperty references;
private bool autodetected;
private static Color color {
get {
return new Color(0f, 0.75f, 1f);
}
}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9999;
return script;
}
protected override void OnEnableVirtual() {
references = serializedObject.FindProperty("references");
// Autodetecting References
if (script.references.IsEmpty(false) && script.enabled) {
BipedReferences.AutoDetectReferences(ref script.references, script.transform, new BipedReferences.AutoDetectParams(true, false));
script.solver.rootNode = IKSolverFullBodyBiped.DetectRootNodeBone(script.references);
Initiate();
if (Application.isPlaying) Warning.Log("Biped references were auto-detected on a FullBodyBipedIK component that was added in runtime. Note that this only happens in the Editor and if the GameObject is selected (for quick and convenient debugging). If you want to add FullBodyBipedIK dynamically in runtime via script, you will have to use BipedReferences.AutodetectReferences() for automatic biped detection.", script.transform);
references.isExpanded = !script.references.isFilled;
}
}
protected override void AddInspector() {
// While in editor
if (!Application.isPlaying) {
// Editing References, if they have changed, reinitiate.
if (BipedReferencesInspector.AddModifiedInspector(references)) {
Initiate();
return; // Don't draw immediatelly to avoid errors
}
// Root Node
IKSolverFullBodyBipedInspector.AddReferences(true, solver);
// Reinitiate if rootNode has changed
if (serializedObject.ApplyModifiedProperties()) {
Initiate();
return; // Don't draw immediatelly to avoid errors
}
} else {
// While in play mode
// Draw the references and the root node for UMA
BipedReferencesInspector.AddModifiedInspector(references);
IKSolverFullBodyBipedInspector.AddReferences(true, solver);
}
string errorMessage = string.Empty;
if (script.ReferencesError(ref errorMessage) || !script.solver.IsValid(ref errorMessage)) {
AddWarningBox(errorMessage);
Warning.Log(errorMessage, script.transform, false);
} else {
// Draw the inspector for IKSolverFullBody
IKSolverFullBodyBipedInspector.AddInspector(solver, false);
}
EditorGUILayout.Space();
}
private void Initiate() {
Warning.logged = false;
// Check for possible errors, if found, do not initiate
string message = "";
if (script.ReferencesError(ref message)) {
Warning.Log(message, script.transform, false);
return;
}
// Notify of possible problems, but still initiate
if (script.ReferencesWarning(ref message)) Warning.Log(message, script.transform, false);
// Initiate
script.solver.SetToReferences(script.references, script.solver.rootNode);
}
// Draw the scene view handles
void OnSceneGUI() {
// Draw the scene veiw helpers
if (!script.references.isFilled) return;
IKSolverFullBodyBipedInspector.AddScene(target, script.solver, color, ref selectedEffector, script.transform);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a1af94c10682a46c0849b487ad86dd40
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,13 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
public class GroundingInspector : Editor {
public static void Visualize(Grounding grounding, Transform root, Transform foot) {
Inspector.SphereCap (0, foot.position + root.forward * grounding.footCenterOffset, root.rotation, grounding.footRadius * 2f);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 43cffbb650ededd4bbfb46bf19a74782
timeCreated: 1487065127
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKEffector
* */
public class IKEffectorInspector: IKSolverInspector {
#region Public methods
public static void DrawArrayElementEffector(SerializedProperty effector, bool editHierarchy) {
if (!editHierarchy) return;
if (effector.FindPropertyRelative("bones").arraySize > 1) {
GUILayout.BeginHorizontal();
GUILayout.Space(indent);
AddClampedFloat(effector.FindPropertyRelative("falloff"), new GUIContent("Distance Falloff", string.Empty), 0f, Mathf.Infinity);
GUILayout.EndHorizontal();
}
AddArray(effector.FindPropertyRelative("bones"), new GUIContent("Bones", string.Empty), editHierarchy, false, null, OnAddToArrayBone, DrawArrayElementLabelBone, false);
if (effector.isExpanded) EditorGUILayout.Space();
}
public static void OnAddToArrayEffector(SerializedProperty effector) {
effector.FindPropertyRelative("positionWeight").floatValue = 0f;
effector.FindPropertyRelative("rotationWeight").floatValue = 0f;
effector.FindPropertyRelative("falloff").floatValue = 0.5f;
effector.FindPropertyRelative("position").vector3Value = Vector3.zero;
effector.FindPropertyRelative("positionOffset").vector3Value = Vector3.zero;
}
public static void DrawArrayElementLabelEffector(SerializedProperty effector, bool editHierarchy) {
GUILayout.Space(Inspector.indent);
if (editHierarchy) {
EditorGUILayout.PropertyField(effector, new GUIContent(GetArrayName(effector.FindPropertyRelative("bones"), "Effector"), string.Empty), false, GUILayout.Width(100));
} else {
EditorGUILayout.LabelField(new GUIContent(GetArrayName(effector.FindPropertyRelative("bones"), "Effector"), string.Empty), GUILayout.Width(100));
}
GUILayout.Space(10);
GUILayout.Label("Position", GUILayout.Width(50));
effector.FindPropertyRelative("positionWeight").floatValue = GUILayout.HorizontalSlider(effector.FindPropertyRelative("positionWeight").floatValue, 0f, 1f, GUILayout.Width(50));
GUILayout.Space(5);
GUILayout.Label("Rotation", GUILayout.Width(50));
effector.FindPropertyRelative("rotationWeight").floatValue = GUILayout.HorizontalSlider(effector.FindPropertyRelative("rotationWeight").floatValue, 0f, 1f, GUILayout.Width(50));
if (!editHierarchy && effector.FindPropertyRelative("bones").arraySize > 1) {
EditorGUILayout.LabelField(new GUIContent("Falloff", string.Empty), GUILayout.Width(50));
EditorGUILayout.PropertyField(effector.FindPropertyRelative("falloff"), GUIContent.none);
effector.FindPropertyRelative("falloff").floatValue = Mathf.Clamp(effector.FindPropertyRelative("falloff").floatValue, 0f, Mathf.Infinity);
}
}
public static void AddScene(IKEffector e, Color color, bool modifiable, float size) {
if (!modifiable) return;
// Draw effectors
bool rotate = e.isEndEffector;
float weight = rotate? Mathf.Max(e.positionWeight, e.rotationWeight): e.positionWeight;
if (e.bone != null && weight > 0) {
//if (Application.isPlaying) {
Handles.color = new Color(color.r, color.g, color.b, weight);
Handles.DrawLine(e.position, e.bone.position);
Inspector.SphereCap(0, e.bone.position, Quaternion.identity, size * 0.5f);
// Manipulating position and rotation
if (e.target == null) {
switch(Tools.current) {
case Tool.Move:
e.position = Handles.PositionHandle(e.position, Quaternion.identity);
break;
case Tool.Rotate:
if (rotate) e.rotation = Handles.RotationHandle(e.rotation, e.position);
break;
}
}
if (rotate) Inspector.CubeCap(0, e.position, e.rotation, size);
else Inspector.SphereCap(0, e.position, Quaternion.identity, size);
//}
}
}
#endregion Public methods
private static void DrawArrayElementLabelBone(SerializedProperty bone, bool editHierarchy) {
AddObjectReference(bone, GUIContent.none, editHierarchy, 0, 300);
}
private static void OnAddToArrayBone(SerializedProperty bone) {
bone.objectReferenceValue = null;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: f11a75612fdb244758156ae2d26a95ec
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
// Custom inspector for IKExecutionOrder
[CustomEditor(typeof(IKExecutionOrder))]
public class IKExecutionOrderInspector : Editor {
private IKExecutionOrder script { get { return target as IKExecutionOrder; }}
private MonoScript monoScript;
void OnEnable() {
if (serializedObject == null) return;
// Changing the script execution order
if (!Application.isPlaying) {
int executionOrder = 9996;
monoScript = MonoScript.FromMonoBehaviour(script);
int currentExecutionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (currentExecutionOrder != executionOrder) MonoImporter.SetExecutionOrder(monoScript, executionOrder);
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: e2d73247f9c4b4190a30f8e4cc054bc5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,67 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Base abstract class for IK component inspectors.
* */
public abstract class IKInspector : Editor {
protected abstract void AddInspector();
protected abstract MonoBehaviour GetMonoBehaviour(out int executionOrder);
protected SerializedProperty solver;
protected SerializedContent fixTransforms;
protected SerializedContent[] content;
protected virtual void OnApplyModifiedProperties() {}
protected virtual void OnEnableVirtual() {}
private MonoScript monoScript;
void OnEnable() {
if (serializedObject == null) return;
// Changing the script execution order
if (!Application.isPlaying) {
int executionOrder = 0;
monoScript = MonoScript.FromMonoBehaviour(GetMonoBehaviour(out executionOrder));
int currentExecutionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (currentExecutionOrder != executionOrder) MonoImporter.SetExecutionOrder(monoScript, executionOrder);
}
solver = serializedObject.FindProperty("solver");
fixTransforms = new SerializedContent(serializedObject.FindProperty("fixTransforms"), new GUIContent("Fix Transforms", "If true, will fix all the Transforms used by the solver to their initial state in each Update. This prevents potential problems with unanimated bones and animator culling with a small cost of performance. Not recommended for CCD and FABRIK solvers."));
OnEnableVirtual();
}
// Override the default warning box
protected virtual void AddWarningBox(string message) {
EditorGUILayout.Space();
EditorGUILayout.LabelField("Invalid/incomplete setup, can not initiate the solver. " + message, EditorStyles.helpBox);
EditorGUILayout.Space();
}
#region Inspector
public override void OnInspectorGUI() {
if (serializedObject == null) return;
serializedObject.Update();
Inspector.AddContent(fixTransforms);
AddInspector();
if (serializedObject.ApplyModifiedProperties()) {
OnApplyModifiedProperties();
}
}
#endregion Inspector
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 00e26cb6b10c641c49833ea4316e25cf
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,103 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverAim
* */
public class IKSolverAimInspector: IKSolverInspector {
#region Public methods
/// <summary>
/// Draws the custom inspector for IKSolverAim
/// </summary>
public static void AddInspector(SerializedProperty prop, bool editHierarchy) {
IKSolverHeuristicInspector.AddTarget(prop);
if (!prop.FindPropertyRelative("XY").boolValue) EditorGUILayout.PropertyField(prop.FindPropertyRelative("poleTarget"), new GUIContent("Pole Target", "If assigned, will automatically set polePosition to the position of this Transform."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("transform"), new GUIContent("Aim Transform", "The transform that you want to be aimed at the target. Needs to be a lineal descendant of the bone hierarchy. For example, if you wish to aim a gun, it should be the gun, one of its children or the hand bone."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("axis"), new GUIContent("Axis", "The local axis of the Transform that you want to be aimed at IKPosition."));
if (!prop.FindPropertyRelative("XY").boolValue) EditorGUILayout.PropertyField(prop.FindPropertyRelative("poleAxis"), new GUIContent("Pole Axis", "Keeps that axis of the Aim Transform directed at the polePosition."));
EditorGUILayout.Space();
IKSolverHeuristicInspector.AddIKPositionWeight(prop);
if (!prop.FindPropertyRelative("XY").boolValue) EditorGUILayout.PropertyField(prop.FindPropertyRelative("poleWeight"), new GUIContent("Pole Weight", "The weight of the Pole."));
IKSolverHeuristicInspector.AddProps(prop);
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampWeight"), new GUIContent("Clamp Weight", "Clamping rotation of the solver. 0 is free rotation, 1 is completely clamped to transform axis."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampSmoothing"), new GUIContent("Clamp Smoothing", "Number of sine smoothing iterations applied on clamping to make the clamping point smoother."));
IKSolverHeuristicInspector.AddBones(prop, editHierarchy, true);
}
/// <summary>
/// Draws the scene view helpers for IKSolverAim
/// </summary>
public static void AddScene(IKSolverAim solver, Color color, bool modifiable) {
// Protect from null reference errors
if (solver.transform == null) return;
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying) {
string message = string.Empty;
if (!solver.IsValid(ref message)) return;
}
Handles.color = color;
GUI.color = color;
// Display the bones
for (int i = 0; i < solver.bones.Length; i++) {
IKSolver.Bone bone = solver.bones[i];
if (i < solver.bones.Length - 1) Handles.DrawLine(bone.transform.position, solver.bones[i + 1].transform.position);
Inspector.SphereCap(0, solver.bones[i].transform.position, Quaternion.identity, GetHandleSize(solver.bones[i].transform.position));
}
if (solver.axis != Vector3.zero) Inspector.ConeCap(0, solver.transform.position, Quaternion.LookRotation(solver.transform.rotation * solver.axis), GetHandleSize(solver.transform.position) * 2f);
// Selecting joint and manipulating IKPosition
if (Application.isPlaying && solver.IKPositionWeight > 0) {
if (modifiable) {
Inspector.SphereCap(0, solver.IKPosition, Quaternion.identity, GetHandleSize(solver.IKPosition));
// Manipulating position
solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
}
// Draw a transparent line from transform to IKPosition
Handles.color = new Color(color.r, color.g, color.b, color.a * solver.IKPositionWeight);
Handles.DrawLine(solver.bones[solver.bones.Length - 1].transform.position, solver.transform.position);
Handles.DrawLine(solver.transform.position, solver.IKPosition);
}
Handles.color = color;
// Pole
if (Application.isPlaying && solver.poleWeight > 0f) {
if (modifiable) {
Inspector.SphereCap(0, solver.polePosition, Quaternion.identity, GetHandleSize(solver.IKPosition) * 0.5f);
// Manipulating position
solver.polePosition = Handles.PositionHandle(solver.polePosition, Quaternion.identity);
}
// Draw a transparent line from transform to polePosition
Handles.color = new Color(color.r, color.g, color.b, color.a * solver.poleWeight);
Handles.DrawLine(solver.transform.position, solver.polePosition);
}
Handles.color = Color.white;
GUI.color = Color.white;
}
#endregion Public methods
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 13082609a38964d0486119efce14e42c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,110 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverArm
* */
public class IKSolverArmInspector: IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverTrigonometric
* */
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
EditorGUI.indentLevel = 0;
// Bone references
if (showReferences) {
EditorGUILayout.Space();
AddObjectReference(prop.FindPropertyRelative("chest.transform"), new GUIContent("Chest", "The last spine bone."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("shoulder.transform"), new GUIContent("Shoulder", "The shoulder (clavicle) bone."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("upperArm.transform"), new GUIContent("Upper Arm", "The upper arm bone."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("forearm.transform"), new GUIContent("Forearm", "The forearm bone."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("hand.transform"), new GUIContent("Hand", "The hand bone."), editHierarchy, 100);
EditorGUILayout.Space();
}
// Hand
EditorGUILayout.PropertyField(prop.FindPropertyRelative("isLeft"), new GUIContent("Is Left", "Check this if this is the left arm, uncheck if right."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Position Weight", "Solver weight for smooth blending."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKRotationWeight"), new GUIContent("Rotation Weight", "Weight of last bone's rotation to IKRotation."));
// Shoulder
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.shoulderRotationMode"), new GUIContent("Shoulder Rotation Mode", " Different techniques for shoulder bone rotation."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.shoulderRotationWeight"), new GUIContent("Shoulder Rotation Weight", " The weight of shoulder rotation."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.shoulderTwistWeight"), new GUIContent("Shoulder Twist Weight", " The weight of twisting the shoulders backwards when arms are lifted up."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.shoulderYawOffset"), new GUIContent("Shoulder Yaw Offset", " Tweak this value to adjust shoulder rotation around the yaw (up) axis."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.shoulderPitchOffset"), new GUIContent("Shoulder Pitch Offset", " Tweak this value to adjust shoulder rotation around the pitch (forward) axis."));
// Bending
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.bendGoal"), new GUIContent("Bend Goal", "If assigned, the knee will be bent in the direction towards this transform."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.bendGoalWeight"), new GUIContent("Bend Goal Weight", "Weight of the bend goal."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.swivelOffset"), new GUIContent("Swivel Offset", "Angular offset of the arm's bending direction."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.wristToPalmAxis"), new GUIContent("Wrist To Palm Axis", "Local axis of the hand bone that points from the wrist towards the palm. Used for defining hand bone orientation."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.palmToThumbAxis"), new GUIContent("Palm To Thumb Axis", "Local axis of the hand bone that points from the palm towards the thumb. Used for defining hand bone orientation."));
// Stretching
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.armLengthMlp"), new GUIContent("Arm Length Mlp", "Use this to make the arm shorter/longer."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("arm.stretchCurve"), new GUIContent("Stretch Curve", "Evaluates stretching of the arm by target distance relative to arm length. Value at time 1 represents stretching amount at the point where distance to the target is equal to arm length. Value at time 2 represents stretching amount at the point where distance to the target is double the arm length. Value represents the amount of stretching. Linear stretching would be achieved with a linear curve going up by 45 degrees. Increase the range of stretching by moving the last key up and right at the same amount. Smoothing in the curve can help reduce elbow snapping (start stretching the arm slightly before target distance reaches arm length)."));
}
/*
* Draws the scene view helpers for IKSolverTrigonometric
* */
public static void AddScene(IKSolverArm solver, Color color, bool modifiable) {
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
//float length = Vector3.Distance(solver.bone1.transform.position, solver.bone2.transform.position) + Vector3.Distance(solver.bone2.transform.position, solver.bone3.transform.position);
//float size = length * 0.05f;
Handles.color = color;
GUI.color = color;
// Chain lines
Handles.DrawLine(solver.chest.transform.position, solver.shoulder.transform.position);
Handles.DrawLine(solver.shoulder.transform.position, solver.upperArm.transform.position);
Handles.DrawLine(solver.upperArm.transform.position, solver.forearm.transform.position);
Handles.DrawLine(solver.forearm.transform.position, solver.hand.transform.position);
// Joints
Inspector.SphereCap(0, solver.chest.transform.position, Quaternion.identity, GetHandleSize(solver.chest.transform.position));
Inspector.SphereCap(0, solver.shoulder.transform.position, Quaternion.identity, GetHandleSize(solver.shoulder.transform.position));
Inspector.SphereCap(0, solver.upperArm.transform.position, Quaternion.identity, GetHandleSize(solver.upperArm.transform.position));
Inspector.SphereCap(0, solver.forearm.transform.position, Quaternion.identity, GetHandleSize(solver.forearm.transform.position));
Inspector.SphereCap(0, solver.hand.transform.position, Quaternion.identity, GetHandleSize(solver.hand.transform.position));
if (Application.isPlaying && (solver.IKPositionWeight > 0 || solver.IKRotationWeight > 0)) {
if (modifiable) {
Inspector.CubeCap(0, solver.IKPosition, solver.IKRotation, GetHandleSize(solver.IKPosition));
// Manipulating position and rotation
switch(Tools.current) {
case Tool.Move:
if (solver.arm.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
break;
case Tool.Rotate:
if (solver.arm.target == null) solver.IKRotation = Handles.RotationHandle(solver.IKRotation, solver.IKPosition);
break;
}
}
// Target
Handles.color = new Color(color.r, color.g, color.b, color.a * Mathf.Max(solver.IKPositionWeight, solver.IKRotationWeight));
Handles.DrawLine(solver.hand.transform.position, solver.IKPosition);
}
Handles.color = Color.white;
GUI.color = Color.white;
}
#endregion Public methods
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2096c71e8e55def4bafe5d7f1604ea55
timeCreated: 1528379406
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,94 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverFABRIKRoot
* */
public class IKSolverFABRIKRootInspector : IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverFABRIKRoot
* */
public static void AddInspector(SerializedProperty prop, bool editHierarchy) {
AddClampedInt(prop.FindPropertyRelative("iterations"), new GUIContent("Iterations", "Solver iterations."), 0, int.MaxValue);
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Weight", "Solver weight."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("rootPin"), new GUIContent("Root Pin", "Weight of keeping all FABRIK Trees pinned to the root position."));
EditorGUILayout.Space();
EditorGUI.indentLevel = 0;
EditorGUILayout.PropertyField(prop.FindPropertyRelative("chains"), new GUIContent("Chains", "FABRIK chains."), true);
EditorGUILayout.Space();
}
/*
* Draws the scene view helpers for IKSolverFABRIKRoot
* */
public static void AddScene(IKSolverFABRIKRoot solver, Color color, bool modifiable, ref FABRIKChain selected) {
// Protect from null reference errors
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying) {
string message = string.Empty;
if (!solver.IsValid(ref message)) return;
}
Handles.color = color;
// Selecting solvers
if (Application.isPlaying) {
SelectChain(solver.chains, ref selected, color);
}
AddSceneChain(solver.chains, color, selected);
// Root pin
Handles.color = new Color(Mathf.Lerp(1f, color.r, solver.rootPin), Mathf.Lerp(1f, color.g, solver.rootPin), Mathf.Lerp(1f, color.b, solver.rootPin), Mathf.Lerp(0.5f, 1f, solver.rootPin));
if (solver.GetRoot() != null) {
Handles.DrawLine(solver.chains[0].ik.solver.bones[0].transform.position, solver.GetRoot().position);
Inspector.CubeCap(0, solver.GetRoot().position, Quaternion.identity, GetHandleSize(solver.GetRoot().position));
}
}
#endregion Public methods
private static Color col, midColor, endColor;
private static void SelectChain(FABRIKChain[] chain, ref FABRIKChain selected, Color color) {
foreach (FABRIKChain c in chain) {
if (c.ik.solver.IKPositionWeight > 0 && selected != c) {
Handles.color = GetChainColor(c, color);
if (Inspector.DotButton(c.ik.solver.GetIKPosition(), Quaternion.identity, GetHandleSize(c.ik.solver.GetIKPosition()), GetHandleSize(c.ik.solver.GetIKPosition()))) {
selected = c;
return;
}
}
}
}
private static Color GetChainColor(FABRIKChain chain, Color color) {
float midWeight = chain.pin;
midColor = new Color(Mathf.Lerp(1f, color.r, midWeight), Mathf.Lerp(1f, color.g, midWeight), Mathf.Lerp(1f, color.b, midWeight), Mathf.Lerp(0.5f, 1f, midWeight));
float endWeight = chain.pull;
endColor = new Color(Mathf.Lerp(1f, color.r, endWeight), Mathf.Lerp(0f, color.g, endWeight), Mathf.Lerp(0f, color.b, endWeight), Mathf.Lerp(0.5f, 1f, endWeight));
return chain.children.Length == 0? endColor: midColor;
}
private static void AddSceneChain(FABRIKChain[] chain, Color color, FABRIKChain selected) {
foreach (FABRIKChain c in chain) {
col = GetChainColor(c, color);
IKSolverHeuristicInspector.AddScene(c.ik.solver as IKSolverHeuristic, col, selected == c);
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 47b6cc68611af428d82361a2eb3d8505
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,339 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
namespace RootMotion.FinalIK {
// Custom inspector and scene view tools for IKSolverFullBodyBiped
public class IKSolverFullBodyBipedInspector : IKSolverInspector {
#region Public methods
public static void AddReferences(bool editHierarchy, SerializedProperty prop) {
// RootNode
if (editHierarchy) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("rootNode"), new GUIContent("Root Node", "Select one of the bones in the (lower) spine."));
}
}
// Draws the custom inspector for IKSolverFullBodybiped
public static void AddInspector(SerializedProperty prop, bool editWeights) {
IKSolverFullBodyInspector.AddInspector(prop, editWeights);
EditorGUILayout.Space();
EditorGUI.indentLevel = 0;
AddSolver(prop);
}
// Draws the scene view helpers for IKSolverFullBodyBiped
public static void AddScene(UnityEngine.Object target, IKSolverFullBodyBiped solver, Color color, ref int selectedEffector, Transform root) {
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
bool modifiable = solver.initiated;
float heightF = Vector3.Distance(solver.chain[1].nodes[0].transform.position, solver.chain[1].nodes[1].transform.position) +
Vector3.Distance(solver.chain[3].nodes[0].transform.position, solver.chain[3].nodes[1].transform.position);
float size = Mathf.Clamp(heightF * 0.075f, 0.001f, Mathf.Infinity);
// Bend goals
for (int i = 0; i < solver.chain.Length; i++) {
if (solver.chain[i].nodes.Length == 3 && solver.chain[i].bendConstraint.bendGoal != null && solver.chain[i].bendConstraint.weight > 0f) {
Color c = color;
c.a = solver.chain[i].bendConstraint.weight;
Handles.color = c;
Handles.DrawLine(solver.chain[i].nodes[1].transform.position, solver.chain[i].bendConstraint.bendGoal.position);
Inspector.SphereCap(0, solver.chain[i].nodes[1].transform.position, Quaternion.identity, size * 0.5f);
Inspector.SphereCap(0, solver.chain[i].bendConstraint.bendGoal.position, Quaternion.identity, size * 0.5f);
Handles.color = Color.white;
}
}
// Chain
if (!modifiable) {
for (int i = 0; i < solver.chain.Length; i++) {
IKSolverFullBodyInspector.AddChain(solver.chain, i, color, size);
}
Handles.DrawLine(solver.chain[1].nodes[0].transform.position, solver.chain[2].nodes[0].transform.position);
Handles.DrawLine(solver.chain[3].nodes[0].transform.position, solver.chain[4].nodes[0].transform.position);
AddLimbHelper(solver.chain[1], size);
AddLimbHelper(solver.chain[2], size);
AddLimbHelper(solver.chain[3], size, root);
AddLimbHelper(solver.chain[4], size, root);
}
// Effectors
IKSolverFullBodyInspector.AddScene(target, solver, color, modifiable, ref selectedEffector, size);
}
// Scene view handles to help with limb setup
private static void AddLimbHelper(FBIKChain chain, float size, Transform root = null) {
Vector3 cross = Vector3.Cross((chain.nodes[1].transform.position - chain.nodes[0].transform.position).normalized, (chain.nodes[2].transform.position - chain.nodes[0].transform.position).normalized);
Vector3 bendDirection = -Vector3.Cross(cross.normalized, (chain.nodes[2].transform.position - chain.nodes[0].transform.position).normalized);
if (bendDirection != Vector3.zero) {
Color c = Handles.color;
bool inverted = root != null && Vector3.Dot(root.forward, bendDirection.normalized) < 0f;
// Inverted bend direction
if (inverted) {
GUI.color = new Color(1f, 0.75f, 0.75f);
Handles.color = Color.yellow;
if (Inspector.DotButton(chain.nodes[1].transform.position, Quaternion.identity, size * 0.5f, size)) {
Warning.logged = false;
Warning.Log("The bend direction of this limb appears to be inverted. Please rotate this bone so that the limb is bent in its natural bending direction. If this limb is supposed to be bent in the direction pointed by the arrow, ignore this warning.", root, true);
}
}
Inspector.ArrowCap(0, chain.nodes[1].transform.position, Quaternion.LookRotation(bendDirection), size * 2f);
GUI.color = Color.white;
Handles.color = c;
} else {
// The limb is completely stretched out
Color c = Handles.color;
Handles.color = Color.red;
GUI.color = new Color(1f, 0.75f, 0.75f);
if (Inspector.DotButton(chain.nodes[1].transform.position, Quaternion.identity, size * 0.5f, size)) {
Warning.logged = false;
Warning.Log("The limb is completely stretched out. Full Body Biped IK does not know which way the limb should be bent. Please rotate this bone slightly in its bending direction.", root, true);
}
GUI.color = Color.white;
Handles.color = c;
}
}
#endregion Public methods
private const string style = "Box";
private static void AddProperty(SerializedProperty prop, GUIContent guiContent) {
GUILayout.BeginHorizontal();
GUILayout.Space(50);
EditorGUILayout.PropertyField(prop, guiContent);
GUILayout.Space(20);
GUILayout.EndHorizontal();
}
private static void AddSolver(SerializedProperty prop) {
var chains = prop.FindPropertyRelative("chain");
AddBody(prop, chains.GetArrayElementAtIndex(0), new GUIContent("Body", string.Empty));
AddLimb(prop, chains.GetArrayElementAtIndex(1), FullBodyBipedChain.LeftArm, new GUIContent("Left Arm", string.Empty));
AddLimb(prop, chains.GetArrayElementAtIndex(2), FullBodyBipedChain.RightArm, new GUIContent("Right Arm", string.Empty));
AddLimb(prop, chains.GetArrayElementAtIndex(3), FullBodyBipedChain.LeftLeg, new GUIContent("Left Leg", string.Empty));
AddLimb(prop, chains.GetArrayElementAtIndex(4), FullBodyBipedChain.RightLeg, new GUIContent("Right Leg", string.Empty));
}
private static void AddBody(SerializedProperty prop, SerializedProperty chain, GUIContent guiContent) {
EditorGUILayout.PropertyField(chain, guiContent, false);
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.BeginVertical();
if (chain.isExpanded) {
var effectors = prop.FindPropertyRelative("effectors");
var effector = effectors.GetArrayElementAtIndex(0);
var spineMapping = prop.FindPropertyRelative("spineMapping");
var headMapping = prop.FindPropertyRelative("boneMappings").GetArrayElementAtIndex(0);
GUILayout.BeginVertical(style);
DrawLabel("Body Effector", startEffectorIcon);
AddProperty(effector.FindPropertyRelative("target"), new GUIContent("Target", "Target Transform (optional, you can also use bodyEffector.position and bodyEffector.rotation directly)."));
AddProperty(effector.FindPropertyRelative("positionWeight"), new GUIContent("Position Weight", "The weight of pinning the effector bone to the effector position."));
AddProperty(effector.FindPropertyRelative("effectChildNodes"), new GUIContent("Use Thighs", "If true, the effect of the body effector will be applied to also the thigh effectors (IKEffector.effectChildNodes)."));
DrawLabel("Chain", null);
// Spine stiffness
AddProperty(prop.FindPropertyRelative("spineStiffness"), new GUIContent("Spine Stiffness", "The bend resistance of the spine."));
// Pull Body
AddProperty(prop.FindPropertyRelative("pullBodyVertical"), new GUIContent("Pull Body Vertical", "Weight of hand effectors pulling the body vertically."));
AddProperty(prop.FindPropertyRelative("pullBodyHorizontal"), new GUIContent("Pull Body Horizontal", "Weight of hand effectors pulling the body horizontally."));
DrawLabel("Mapping", null);
AddProperty(spineMapping.FindPropertyRelative("iterations"), new GUIContent("Spine Iterations", "The number of FABRIK iterations for mapping the spine bones to the solver armature."));
AddProperty(spineMapping.FindPropertyRelative("twistWeight"), new GUIContent("Spine Twist Weight", "The weight of spine twist."));
AddProperty(headMapping.FindPropertyRelative("maintainRotationWeight"), new GUIContent("Maintain Head Rot", "The weight of maintaining the bone's animated rotation in world space."));
GUILayout.Space(5);
GUILayout.EndVertical();
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
private static void AddLimb(SerializedProperty prop, SerializedProperty chain, FullBodyBipedChain chainType, GUIContent guiContent) {
EditorGUILayout.PropertyField(chain, guiContent, false);
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.BeginVertical();
if (chain.isExpanded) {
var effectors = prop.FindPropertyRelative("effectors");
var endEffector = effectors.GetArrayElementAtIndex(GetEndEffectorIndex(chainType));
var startEffector = effectors.GetArrayElementAtIndex(GetStartEffectorIndex(chainType));
var mapping = prop.FindPropertyRelative("limbMappings").GetArrayElementAtIndex(GetLimbMappingIndex(chainType));
GUILayout.BeginVertical(style);
DrawLabel(GetEndEffectorName(chainType), endEffectorIcon);
AddProperty(endEffector.FindPropertyRelative("target"), new GUIContent("Target", "Target Transform (optional, you can also use IKEffector.position and IKEffector.rotation directly)."));
AddProperty(endEffector.FindPropertyRelative("positionWeight"), new GUIContent("Position Weight", "The weight of pinning the effector bone to the effector position."));
AddProperty(endEffector.FindPropertyRelative("rotationWeight"), new GUIContent("Rotation Weight", "The weight of pinning the effector bone to the effector rotation."));
AddProperty(endEffector.FindPropertyRelative("maintainRelativePositionWeight"), new GUIContent("Maintain Relative Pos", "Maintains the position of the hand/foot fixed relative to the chest/hips while effector positionWeight is not weighed in."));
DrawLabel(GetStartEffectorName(chainType), startEffectorIcon);
AddProperty(startEffector.FindPropertyRelative("target"), new GUIContent("Target", "Target Transform (optional, you can also use IKEffector.position and IKEffector.rotation directly)."));
AddProperty(startEffector.FindPropertyRelative("positionWeight"), new GUIContent("Position Weight", "The weight of pinning the effector bone to the effector position."));
DrawLabel("Chain", null);
AddProperty(chain.FindPropertyRelative("pull"), new GUIContent("Pull", "The weight of pulling other chains."));
AddProperty(chain.FindPropertyRelative("reach"), new GUIContent("Reach", "Pulls the first node closer to the last node of the chain."));
AddProperty(chain.FindPropertyRelative("push"), new GUIContent("Push", "The weight of the end-effector pushing the first node."));
AddProperty(chain.FindPropertyRelative("pushParent"), new GUIContent("Push Parent", "The amount of push force transferred to the parent (from hand or foot to the body)."));
AddProperty(chain.FindPropertyRelative("reachSmoothing"), new GUIContent("Reach Smoothing", "Smoothing the effect of the Reach with the expense of some accuracy."));
AddProperty(chain.FindPropertyRelative("pushSmoothing"), new GUIContent("Push Smoothing", "Smoothing the effect of the Push."));
AddProperty(chain.FindPropertyRelative("bendConstraint").FindPropertyRelative("bendGoal"), new GUIContent("Bend Goal", "The Transform to bend towards (optional, you can also use ik.leftArmChain.bendConstraint.direction)."));
AddProperty(chain.FindPropertyRelative("bendConstraint").FindPropertyRelative("weight"), new GUIContent("Bend Goal Weight", "The weight of to bending towards the Bend Goal (optional, you can also use ik.leftArmChain.bendConstraint.weight)."));
DrawLabel("Mapping", null);
AddProperty(mapping.FindPropertyRelative("weight"), new GUIContent("Mapping Weight", "The weight of mapping the limb to its IK pose. This can be useful if you want to disable the effect of IK for the limb."));
AddProperty(mapping.FindPropertyRelative("maintainRotationWeight"), new GUIContent(GetEndBoneMappingName(chainType), "The weight of maintaining the bone's animated rotation in world space."));
GUILayout.Space(5);
GUILayout.EndVertical();
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
private static void DrawLabel(string label, Texture2D texture) {
GUILayout.Space(3);
GUILayout.BeginHorizontal();
if (texture != null) {
Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(16), GUILayout.Height(16));
GUI.DrawTexture(rect, texture);
} else GUILayout.Space(21);
EditorGUILayout.LabelField(new GUIContent(label, string.Empty));
GUILayout.EndHorizontal();
GUILayout.Space(3);
}
private static string GetEndEffectorName(FullBodyBipedChain chain) {
switch(chain) {
case FullBodyBipedChain.LeftArm: return "Left Hand Effector";
case FullBodyBipedChain.RightArm: return "Right Hand Effector";
case FullBodyBipedChain.LeftLeg: return "Left Foot Effector";
case FullBodyBipedChain.RightLeg: return "Right Foot Effector";
default: return string.Empty;
}
}
private static string GetStartEffectorName(FullBodyBipedChain chain) {
switch(chain) {
case FullBodyBipedChain.LeftArm: return "Left Shoulder Effector";
case FullBodyBipedChain.RightArm: return "Right Shoulder Effector";
case FullBodyBipedChain.LeftLeg: return "Left Thigh Effector";
case FullBodyBipedChain.RightLeg: return "Right Thigh Effector";
default: return string.Empty;
}
}
private static string GetEndBoneMappingName(FullBodyBipedChain chain) {
switch(chain) {
case FullBodyBipedChain.LeftArm: return "Maintain Hand Rot";
case FullBodyBipedChain.RightArm: return "Maintain Hand Rot";
case FullBodyBipedChain.LeftLeg: return "Maintain Foot Rot";
case FullBodyBipedChain.RightLeg: return "Maintain Foot Rot";
default: return string.Empty;
}
}
private static int GetEndEffectorIndex(FullBodyBipedChain chainType) {
switch(chainType) {
case FullBodyBipedChain.LeftArm: return 5;
case FullBodyBipedChain.RightArm: return 6;
case FullBodyBipedChain.LeftLeg: return 7;
case FullBodyBipedChain.RightLeg: return 8;
}
return 0;
}
private static int GetStartEffectorIndex(FullBodyBipedChain chainType) {
switch(chainType) {
case FullBodyBipedChain.LeftArm: return 1;
case FullBodyBipedChain.RightArm: return 2;
case FullBodyBipedChain.LeftLeg: return 3;
case FullBodyBipedChain.RightLeg: return 4;
}
return 0;
}
private static int GetLimbMappingIndex(FullBodyBipedChain chainType) {
switch(chainType) {
case FullBodyBipedChain.LeftArm: return 0;
case FullBodyBipedChain.RightArm: return 1;
case FullBodyBipedChain.LeftLeg: return 2;
case FullBodyBipedChain.RightLeg: return 3;
}
return 0;
}
private static Texture2D endEffectorIcon {
get {
if (_endEffectorIcon == null) _endEffectorIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/RootMotion/FinalIK/Gizmos/EndEffector Icon.png", typeof(Texture2D));
return _endEffectorIcon;
}
}
private static Texture2D _endEffectorIcon;
private static Texture2D startEffectorIcon {
get {
if (_startEffectorIcon == null) _startEffectorIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/RootMotion/FinalIK/Gizmos/MidEffector Icon.png", typeof(Texture2D));
return _startEffectorIcon;
}
}
private static Texture2D _startEffectorIcon;
private static Texture2D chainIcon {
get {
if (_chainIcon == null) _chainIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/RootMotion/FinalIK/Gizmos/Chain Icon.png", typeof(Texture2D));
return _chainIcon;
}
}
private static Texture2D _chainIcon;
private static Texture2D mappingIcon {
get {
if (_mappingIcon == null) _mappingIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/RootMotion/FinalIK/Gizmos/Mapping Icon.png", typeof(Texture2D));
return _mappingIcon;
}
}
private static Texture2D _mappingIcon;
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 533c5d8f60bd14648a10c7c1ad3f9ee5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,80 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverFullBody
* */
public class IKSolverFullBodyInspector : IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverFullBody
* */
public static void AddInspector(SerializedProperty prop, bool editWeights) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Weight", "Solver weight for smooth blending (ik.solver.IKPositionWeight)."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("iterations"), new GUIContent("Iterations", "Solver iterations per frame."));
}
/*
* Draws the scene view helpers for IKSolverFullBody
* */
public static void AddScene(UnityEngine.Object target, IKSolverFullBody solver, Color color, bool modifiable, ref int selectedEffector, float size) {
if (!modifiable) return;
if (!solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
// Effectors
for (int i = 0; i < solver.effectors.Length; i++) {
bool rotate = solver.effectors[i].isEndEffector;
float weight = rotate? Mathf.Max(solver.effectors[i].positionWeight, solver.effectors[i].rotationWeight): solver.effectors[i].positionWeight;
if (weight > 0 && selectedEffector != i) {
Handles.color = color;
if (rotate) {
if (Inspector.DotButton(solver.effectors[i].position, solver.effectors[i].rotation, size * 0.5f, size * 0.5f)) {
selectedEffector = i;
return;
}
} else {
if (Inspector.SphereButton(solver.effectors[i].position, solver.effectors[i].rotation, size, size)) {
selectedEffector = i;
return;
}
}
}
}
for (int i = 0; i < solver.effectors.Length; i++) IKEffectorInspector.AddScene(solver.effectors[i], color, modifiable && i == selectedEffector, size);
if (GUI.changed) EditorUtility.SetDirty(target);
}
#endregion Public methods
public static void AddChain(FBIKChain[] chain, int index, Color color, float size) {
Handles.color = color;
for (int i = 0; i < chain[index].nodes.Length - 1; i++) {
Handles.DrawLine(GetNodePosition(chain[index].nodes[i]), GetNodePosition(chain[index].nodes[i + 1]));
Inspector.SphereCap(0, GetNodePosition(chain[index].nodes[i]), Quaternion.identity, size);
}
Inspector.SphereCap(0, GetNodePosition(chain[index].nodes[chain[index].nodes.Length - 1]), Quaternion.identity, size);
for (int i = 0; i < chain[index].children.Length; i++) {
Handles.DrawLine(GetNodePosition(chain[index].nodes[chain[index].nodes.Length - 1]), GetNodePosition(chain[chain[index].children[i]].nodes[0]));
}
}
private static Vector3 GetNodePosition(IKSolver.Node node) {
if (Application.isPlaying) return node.solverPosition;
return node.transform.position;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9a32242b195b44820b383881d973c731
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,104 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IK solvers extending IKSolverHeuristic
* */
public class IKSolverHeuristicInspector: IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverHeuristic
* */
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool editWeights) {
AddTarget(prop);
AddIKPositionWeight(prop);
AddProps(prop);
AddBones(prop, editHierarchy, editWeights);
}
public static void AddTarget(SerializedProperty prop) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
}
public static void AddIKPositionWeight(SerializedProperty prop) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Weight", "Solver weight for smooth blending."));
}
public static void AddProps(SerializedProperty prop) {
AddClampedFloat(prop.FindPropertyRelative("tolerance"), new GUIContent("Tolerance", "Minimum offset from last reached position. Will stop solving if offset is less than tolerance. If tolerance is zero, will iterate until maxIterations."));
AddClampedInt(prop.FindPropertyRelative("maxIterations"), new GUIContent("Max Iterations", "Max solver iterations per frame."), 0, int.MaxValue);
}
public static void AddBones(SerializedProperty prop, bool editHierarchy, bool editWeights) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("useRotationLimits"), new GUIContent("Use Rotation Limits", "If true, rotation limits (if existing) will be applied on each iteration."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("XY"), new GUIContent("2D", "If true, will solve only in the XY plane."));
EditorGUILayout.Space();
weights = editWeights;
if (editHierarchy || editWeights) {
AddArray(prop.FindPropertyRelative("bones"), new GUIContent("Bones", string.Empty), editHierarchy, false, null, OnAddToArrayBone, DrawArrayElementLabelBone);
}
EditorGUILayout.Space();
}
/*
* Draws the scene view helpers for IKSolverHeuristic
* */
public static void AddScene(IKSolverHeuristic solver, Color color, bool modifiable) {
// Protect from null reference errors
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
Handles.color = color;
GUI.color = color;
// Display the bones
for (int i = 0; i < solver.bones.Length; i++) {
IKSolver.Bone bone = solver.bones[i];
if (i < solver.bones.Length - 1) Handles.DrawLine(bone.transform.position, solver.bones[i + 1].transform.position);
Inspector.SphereCap(0, solver.bones[i].transform.position, Quaternion.identity, GetHandleSize(solver.bones[i].transform.position));
}
// Selecting joint and manipulating IKPosition
if (Application.isPlaying && solver.IKPositionWeight > 0) {
if (modifiable) {
Inspector.CubeCap(0, solver.IKPosition, solver.GetRoot().rotation, GetHandleSize(solver.IKPosition));
// Manipulating position
if (solver.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
}
// Draw a transparent line from last bone to IKPosition
Handles.color = new Color(color.r, color.g, color.b, color.a * solver.IKPositionWeight);
Handles.DrawLine(solver.bones[solver.bones.Length - 1].transform.position, solver.IKPosition);
}
Handles.color = Color.white;
GUI.color = Color.white;
}
#endregion Public methods
private static bool weights;
private static void DrawArrayElementLabelBone(SerializedProperty bone, bool editHierarchy) {
AddObjectReference(bone.FindPropertyRelative("transform"), GUIContent.none, editHierarchy, 0, weights? 100: 200);
if (weights) AddWeightSlider(bone.FindPropertyRelative("weight"));
}
private static void OnAddToArrayBone(SerializedProperty bone) {
bone.FindPropertyRelative("weight").floatValue = 1f;
}
private static void AddWeightSlider(SerializedProperty prop) {
GUILayout.Label("Weight", GUILayout.Width(45));
EditorGUILayout.PropertyField(prop, GUIContent.none);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d5806071df7484ff0bfa7330e9adb04e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
namespace RootMotion.FinalIK {
/*
* Contains helper methods for managing IKSolver's fields.
* */
public class IKSolverInspector: Inspector {
public static float GetHandleSize(Vector3 position) {
float s = HandleUtility.GetHandleSize(position) * 0.1f;
return Mathf.Lerp(s, 0.025f, 0.2f);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: f8fadad0d2804439bad44738bf137eb3
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,102 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverLeg
* */
public class IKSolverLegInspector: IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverTrigonometric
* */
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
EditorGUI.indentLevel = 0;
// Bone references
if (showReferences) {
EditorGUILayout.Space();
AddObjectReference(prop.FindPropertyRelative("pelvis.transform"), new GUIContent("Pelvis", "The pelvis (hips)."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("thigh.transform"), new GUIContent("Thigh", "The upper leg."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("calf.transform"), new GUIContent("Calf", "The lower leg."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("foot.transform"), new GUIContent("Foot", "The ankle."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("toe.transform"), new GUIContent("Toe", "The first toe bone."), editHierarchy, 100);
EditorGUILayout.Space();
}
// Foot/Toe
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Position Weight", "Solver weight for smooth blending."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKRotationWeight"), new GUIContent("Rotation Weight", "Weight of last bone's rotation to IKRotation."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("heelOffset"), new GUIContent("Heel Offset", "Offset of the heel in world space."));
// Bending
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.bendGoal"), new GUIContent("Bend Goal", "If assigned, the knee will be bent in the direction towards this transform."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.bendGoalWeight"), new GUIContent("Bend Goal Weight", "Weight of the bend goal."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.swivelOffset"), new GUIContent("Swivel Offset", "Angular offset of the leg's bending direction."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.bendToTargetWeight"), new GUIContent("Bend To Target Weight", "If 0, the bend plane will be locked to the rotation of the pelvis and rotating the foot will have no effect on the knee direction. If 1, to the target rotation of the leg so that the knee will bend towards the forward axis of the foot. Values in between will be slerped between the two."));
// Stretching
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.legLengthMlp"), new GUIContent("Leg Length Mlp", "Use this to make the leg shorter/longer."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.stretchCurve"), new GUIContent("Stretch Curve", "Evaluates stretching of the leg by target distance relative to leg length. Value at time 1 represents stretching amount at the point where distance to the target is equal to leg length. Value at time 1 represents stretching amount at the point where distance to the target is double the leg length. Value represents the amount of stretching. Linear stretching would be achieved with a linear curve going up by 45 degrees. Increase the range of stretching by moving the last key up and right at the same amount. Smoothing in the curve can help reduce knee snapping (start stretching the arm slightly before target distance reaches leg length)."));
}
/*
* Draws the scene view helpers for IKSolverTrigonometric
* */
public static void AddScene(IKSolverLeg solver, Color color, bool modifiable) {
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
//float length = Vector3.Distance(solver.bone1.transform.position, solver.bone2.transform.position) + Vector3.Distance(solver.bone2.transform.position, solver.bone3.transform.position);
//float size = length * 0.05f;
Handles.color = color;
GUI.color = color;
// Chain lines
Handles.DrawLine(solver.pelvis.transform.position, solver.thigh.transform.position);
Handles.DrawLine(solver.thigh.transform.position, solver.calf.transform.position);
Handles.DrawLine(solver.calf.transform.position, solver.foot.transform.position);
Handles.DrawLine(solver.foot.transform.position, solver.toe.transform.position);
// Joints
Inspector.SphereCap(0, solver.pelvis.transform.position, Quaternion.identity, GetHandleSize(solver.pelvis.transform.position));
Inspector.SphereCap(0, solver.thigh.transform.position, Quaternion.identity, GetHandleSize(solver.thigh.transform.position));
Inspector.SphereCap(0, solver.calf.transform.position, Quaternion.identity, GetHandleSize(solver.calf.transform.position));
Inspector.SphereCap(0, solver.foot.transform.position, Quaternion.identity, GetHandleSize(solver.foot.transform.position));
Inspector.SphereCap(0, solver.toe.transform.position, Quaternion.identity, GetHandleSize(solver.toe.transform.position));
if (Application.isPlaying && (solver.IKPositionWeight > 0 || solver.IKRotationWeight > 0)) {
if (modifiable) {
Inspector.CubeCap(0, solver.IKPosition, solver.IKRotation, GetHandleSize(solver.IKPosition));
// Manipulating position and rotation
switch(Tools.current) {
case Tool.Move:
if (solver.leg.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
break;
case Tool.Rotate:
if (solver.leg.target == null) solver.IKRotation = Handles.RotationHandle(solver.IKRotation, solver.IKPosition);
break;
}
}
// Target
Handles.color = new Color(color.r, color.g, color.b, color.a * Mathf.Max(solver.IKPositionWeight, solver.IKRotationWeight));
Handles.DrawLine(solver.toe.transform.position, solver.IKPosition);
}
Handles.color = Color.white;
GUI.color = Color.white;
}
#endregion Public methods
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b8601eb42d45b294dba88abac3e27d24
timeCreated: 1487059931
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverLimb
* */
public class IKSolverLimbInspector: IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverLimb
* */
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
// Draw the trigonometric IK inspector
IKSolverTrigonometricInspector.AddInspector(prop, editHierarchy, showReferences);
EditorGUILayout.Space();
if (showReferences && editHierarchy) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("goal"), new GUIContent("Avatar IK Goal", "Avatar IK Goal here is only used by the 'Arm' bend modifier."));
}
EditorGUILayout.PropertyField(prop.FindPropertyRelative("maintainRotationWeight"), new GUIContent("Maintain Rotation", "Weight of rotating the last bone back to the rotation it had before solving IK."));
// Bend normal modifier.
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendModifier"), new GUIContent("Bend Modifier", "Bend normal modifier."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendModifierWeight"), new GUIContent("Bend Modifier Weight", "Weight of the bend modifier."));
if (prop.FindPropertyRelative("bendModifier").enumValueIndex == 4) EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendGoal"), new GUIContent("Bend Goal", "The bend goal Transform (optional, you can also use IKSolverTrigonometric.SetBendGoalPosition(Vector position, float weight)."));
EditorGUILayout.Space();
}
/*
* Draws the scene view helpers for IKSolverLimb
* */
public static void AddScene(IKSolverLimb solver, Color color, bool modifiable) {
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
if (solver.bendGoal != null && solver.bendModifierWeight > 0f) {
Color c = color;
c.a = solver.bendModifierWeight;
Handles.color = c;
Handles.DrawLine(solver.bone2.transform.position, solver.bendGoal.position);
Inspector.SphereCap(0, solver.bendGoal.position, Quaternion.identity, GetHandleSize(solver.bendGoal.position) * 0.5f);
Handles.color = Color.white;
}
IKSolverTrigonometricInspector.AddScene(solver as IKSolverTrigonometric, color, modifiable);
}
#endregion Public methods
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 57956e150e00f474b9c03c7155dc43e3
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,122 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverLookAt
* */
public class IKSolverLookAtInspector: IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverLookAt
* */
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Weight", "Solver weight for smooth blending."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bodyWeight"), new GUIContent("Body Weight", "Weight of rotating spine to target."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("headWeight"), new GUIContent("Head Weight", "Weight of rotating head to target."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("eyesWeight"), new GUIContent("Eyes Weight", "Weight of rotating eyes to target."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampWeight"), new GUIContent("Clamp Weight", "Clamping rotation of spine and head. 0 is free rotation, 1 is completely clamped to forward."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampWeightHead"), new GUIContent("Clamp Weight Head", "Clamping rotation of the head. 0 is free rotation, 1 is completely clamped to forward."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampWeightEyes"), new GUIContent("Clamp Weight Eyes", "Clamping rotation of the eyes. 0 is free rotation, 1 is completely clamped to forward."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampSmoothing"), new GUIContent("Clamp Smoothing", "Number of sine smoothing iterations applied on clamping to make the clamping point smoother."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("spineWeightCurve"), new GUIContent("Spine Weight Curve", "Weight distribution between spine bones (first bone is evaluated at time 0.0, last bone at 1.0)."));
// References
if (showReferences) {
EditorGUILayout.Space();
EditorGUILayout.PropertyField(prop.FindPropertyRelative("head.transform"), new GUIContent("Head", "The head bone."));
EditorGUILayout.Space();
AddArray(prop.FindPropertyRelative("spine"), new GUIContent("Spine", string.Empty), editHierarchy, false, null, null, DrawArrayElementLabelBone);
EditorGUILayout.Space();
AddArray(prop.FindPropertyRelative("eyes"), new GUIContent("Eyes", string.Empty), editHierarchy, false, null, null, DrawArrayElementLabelBone);
}
EditorGUILayout.Space();
}
/*
* Draws the scene view helpers for IKSolverLookAt
* */
public static void AddScene(IKSolverLookAt solver, Color color, bool modifiable) {
// Protect from null reference errors
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
// Display the Spine
if (solver.spine.Length > 0) {
Handles.color = color;
GUI.color = color;
for (int i = 0; i < solver.spine.Length; i++) {
IKSolverLookAt.LookAtBone bone = solver.spine[i];
if (i < solver.spine.Length - 1) Handles.DrawLine(bone.transform.position, solver.spine[i + 1].transform.position);
Inspector.SphereCap(0, bone.transform.position, Quaternion.identity, GetHandleSize(bone.transform.position));
}
// Draw a transparent line from last bone to IKPosition
if (Application.isPlaying) {
Handles.color = new Color(color.r, color.g, color.b, color.a * solver.IKPositionWeight * solver.bodyWeight);
Handles.DrawLine(solver.spine[solver.spine.Length - 1].transform.position, solver.IKPosition);
}
}
// Display the eyes
if (solver.eyes.Length > 0) {
for (int i = 0; i < solver.eyes.Length; i++) {
DrawLookAtBoneInScene(solver.eyes[i], solver.IKPosition, color, solver.IKPositionWeight * solver.eyesWeight);
}
}
// Display the head
if (solver.head.transform != null) {
DrawLookAtBoneInScene(solver.head, solver.IKPosition, color, solver.IKPositionWeight * solver.headWeight);
}
Handles.color = color;
GUI.color = color;
// Selecting joint and manipulating IKPosition
if (Application.isPlaying && solver.IKPositionWeight > 0) {
if (modifiable) {
Inspector.SphereCap(0, solver.IKPosition, Quaternion.identity, GetHandleSize(solver.IKPosition));
// Manipulating position
if (solver.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
}
}
Handles.color = Color.white;
GUI.color = Color.white;
}
#endregion Public methods
private static void DrawArrayElementLabelBone(SerializedProperty bone, bool editHierarchy) {
AddObjectReference(bone.FindPropertyRelative("transform"), GUIContent.none, editHierarchy, 0, 300);
}
private static void DrawLookAtBoneInScene(IKSolverLookAt.LookAtBone bone, Vector3 IKPosition, Color color, float lineWeight) {
Handles.color = color;
GUI.color = color;
Inspector.SphereCap(0, bone.transform.position, Quaternion.identity, GetHandleSize(bone.transform.position));
// Draw a transparent line from last bone to IKPosition
if (Application.isPlaying && lineWeight > 0) {
Handles.color = new Color(color.r, color.g, color.b, color.a * lineWeight);
Handles.DrawLine(bone.transform.position, IKPosition);
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5cf17f5d7f4954636ad6575fd9f9832c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,97 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view tools for IKSolverTrigonometric
* */
public class IKSolverTrigonometricInspector: IKSolverInspector {
#region Public methods
/*
* Draws the custom inspector for IKSolverTrigonometric
* */
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
EditorGUI.indentLevel = 0;
// Bone references
if (showReferences) {
EditorGUILayout.Space();
AddObjectReference(prop.FindPropertyRelative("bone1.transform"), new GUIContent("Bone 1", "The first bone in the hierarchy (thigh or upper arm)."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("bone2.transform"), new GUIContent("Bone 2", "The second bone in the hierarchy (calf or forearm)."), editHierarchy, 100);
AddObjectReference(prop.FindPropertyRelative("bone3.transform"), new GUIContent("Bone 3", "The last bone in the hierarchy (foot or hand)."), editHierarchy, 100);
EditorGUILayout.Space();
}
EditorGUILayout.PropertyField(prop.FindPropertyRelative("target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Position Weight", "Solver weight for smooth blending."));
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKRotationWeight"), new GUIContent("Rotation Weight", "Weight of last bone's rotation to IKRotation."));
if (showReferences) {
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendNormal"), new GUIContent("Bend Normal", "Bend plane normal."));
}
}
public static void AddTestInspector(SerializedObject obj) {
SerializedProperty solver = obj.FindProperty("solver");
EditorGUILayout.PropertyField(solver.FindPropertyRelative("bendNormal"));
}
/*
* Draws the scene view helpers for IKSolverTrigonometric
* */
public static void AddScene(IKSolverTrigonometric solver, Color color, bool modifiable) {
if (Application.isPlaying && !solver.initiated) return;
if (!Application.isPlaying && !solver.IsValid()) return;
//float length = Vector3.Distance(solver.bone1.transform.position, solver.bone2.transform.position) + Vector3.Distance(solver.bone2.transform.position, solver.bone3.transform.position);
//float size = length * 0.05f;
Handles.color = color;
GUI.color = color;
Vector3 bendPosition = solver.bone2.transform.position;
Vector3 endPosition = solver.bone3.transform.position;
// Chain lines
Handles.DrawLine(solver.bone1.transform.position, bendPosition);
Handles.DrawLine(bendPosition, endPosition);
// Joints
Inspector.SphereCap(0, solver.bone1.transform.position, Quaternion.identity, GetHandleSize(solver.bone1.transform.position));
Inspector.SphereCap(0, bendPosition, Quaternion.identity, GetHandleSize(bendPosition));
Inspector.SphereCap(0, endPosition, Quaternion.identity, GetHandleSize(endPosition));
if (Application.isPlaying && (solver.IKPositionWeight > 0 || solver.IKRotationWeight > 0)) {
if (modifiable) {
Inspector.CubeCap(0, solver.IKPosition, solver.IKRotation, GetHandleSize(solver.IKPosition));
// Manipulating position and rotation
switch(Tools.current) {
case Tool.Move:
if (solver.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
break;
case Tool.Rotate:
if (solver.target == null) solver.IKRotation = Handles.RotationHandle(solver.IKRotation, solver.IKPosition);
break;
}
}
// Target
Handles.color = new Color(color.r, color.g, color.b, color.a * Mathf.Max(solver.IKPositionWeight, solver.IKRotationWeight));
Handles.DrawLine(endPosition, solver.IKPosition);
}
Handles.color = Color.white;
GUI.color = Color.white;
}
#endregion Public methods
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3474539eeb23c491fbd6a10e0951ce9d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,108 @@
using UnityEngine;
using UnityEditor;
namespace RootMotion.FinalIK {
/*
* Custom inspector and scene view helpers for the InteractionTarget.
* */
[CustomEditor(typeof(InteractionTarget))]
public class InteractionTargetInspector : Editor {
private InteractionTarget script { get { return target as InteractionTarget; }}
private const string twistAxisLabel = " Twist Axis";
private const float size = 0.005f;
private static Color targetColor = new Color(0.2f, 1f, 0.5f);
private static Color pivotColor = new Color(0.2f, 0.5f, 1f);
void OnSceneGUI() {
Handles.color = targetColor;
Inspector.SphereCap(0, script.transform.position, Quaternion.identity, size);
DrawChildrenRecursive(script.transform);
if (script.pivot != null) {
Handles.color = pivotColor;
GUI.color = pivotColor;
Inspector.SphereCap(0, script.pivot.position, Quaternion.identity, size);
if (script.rotationMode == InteractionTarget.RotationMode.TwoDOF)
{
Vector3 twistAxisWorld = script.pivot.rotation * script.twistAxis.normalized * size * 40;
Handles.DrawLine(script.pivot.position, script.pivot.position + twistAxisWorld);
Inspector.SphereCap(0, script.pivot.position + twistAxisWorld, Quaternion.identity, size);
Inspector.CircleCap(0, script.pivot.position, Quaternion.LookRotation(twistAxisWorld), size * 20);
Handles.Label(script.pivot.position + twistAxisWorld, twistAxisLabel);
}
}
Handles.color = Color.white;
GUI.color = Color.white;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(serializedObject.FindProperty("effectorType"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("multipliers"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("interactionSpeedMlp"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("pivot"));
EditorGUILayout.Space();
EditorGUILayout.PropertyField(serializedObject.FindProperty("rotationMode"));
int rotationMode = serializedObject.FindProperty("rotationMode").enumValueIndex;
if (rotationMode == 0)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("twistAxis"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("twistWeight"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("swingWeight"));
} else if (rotationMode == 1)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("threeDOFWeight"));
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("rotateOnce"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("usePoser"));
if (serializedObject.FindProperty("usePoser").boolValue)
{
var bones = serializedObject.FindProperty("bones");
EditorGUILayout.PropertyField(bones);
if (GUILayout.Button("Auto-Assign Bones"))
{
var children = script.GetComponentsInChildren<Transform>();
if (children.Length > 1)
{
bones.ClearArray();
for (int i = 1; i < children.Length; i++)
{
bones.InsertArrayElementAtIndex(i - 1);
bones.GetArrayElementAtIndex(i - 1).objectReferenceValue = children[i];
}
}
}
}
if (serializedObject.ApplyModifiedProperties())
{
EditorUtility.SetDirty(script);
}
}
private void DrawChildrenRecursive(Transform t) {
for (int i = 0; i < t.childCount; i++) {
Handles.DrawLine(t.position, t.GetChild(i).position);
Inspector.SphereCap(0, t.GetChild(i).position, Quaternion.identity, size);
DrawChildrenRecursive(t.GetChild(i));
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 244e0878f45b6442cac28a34942f9ac3
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,206 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace RootMotion.FinalIK {
// Custom scene view helpers for the InteractionTrigger
[CustomEditor(typeof(InteractionTrigger))]
public class InteractionTriggerInspector : Editor {
private InteractionTrigger script { get { return target as InteractionTrigger; }}
void OnSceneGUI() {
if (!script.enabled) return;
var collider = script.GetComponent<Collider>();
if (collider != null)
collider.isTrigger = true;
else {
Warning.Log ("InteractionTrigger requires a Collider component.", script.transform, true);
return;
}
if (script.ranges.Length == 0) return;
for (int i = 0; i < script.ranges.Length; i++) {
DrawRange (script.ranges[i], i);
}
Handles.BeginGUI();
int h = script.ranges.Length * 18;
GUILayout.BeginArea(new Rect(10, 10, 200, h + 25), "InteractionTrigger Visualization", "Window");
// Rotating display
for (int i = 0; i < script.ranges.Length; i++) {
script.ranges[i].show = GUILayout.Toggle(script.ranges[i].show, new GUIContent(" Show Range " + i.ToString() + ": " + script.ranges[i].name, string.Empty));
}
GUILayout.EndArea();
Handles.EndGUI();
}
private void DrawRange(InteractionTrigger.Range range, int index) {
range.name = string.Empty;
for (int i = 0; i < range.interactions.Length; i++) {
if (range.name.Length > 50) {
range.name += "...";
break;
}
if (i > 0) range.name += "; ";
for (int e = 0; e < range.interactions[i].effectors.Length; e++) {
if (e > 0) range.name += ", ";
range.name += range.interactions[i].effectors[e].ToString();
}
if (range.interactions[i].interactionObject != null) {
range.name += ": " + range.interactions[i].interactionObject.name;
}
}
if (!range.show) return;
Color color = GetColor(index);
Handles.color = color;
GUI.color = color;
// Character Position
DrawCharacterPosition(range, index);
// Camera Position
DrawCameraPosition(range, index);
Handles.color = Color.white;
GUI.color = Color.white;
}
private void DrawCharacterPosition(InteractionTrigger.Range range, int index) {
Vector3 labelPosition = script.transform.position - Vector3.up * index * 0.05f;
if (!range.characterPosition.use) {
Handles.Label(labelPosition, "Character Position is not used for Range " + index.ToString() + ": " + range.name);
return;
}
range.characterPosition.radius = Mathf.Max(range.characterPosition.radius, 0f);
if (range.characterPosition.radius <= 0f) {
Handles.Label(labelPosition, "Character Position radius is zero for Range " + index.ToString() + ": " + range.name);
return;
}
if (range.characterPosition.maxAngle <= 0f) {
Handles.Label(labelPosition, "Character Position max angle is zero for Range " + index.ToString() + ": " + range.name);
return;
}
Vector3 f = script.transform.forward;
if (range.characterPosition.fixYAxis) f.y = 0f;
if (f == Vector3.zero) {
Handles.Label(script.transform.position - Vector3.up * index * 0.05f, "Invalid rotation of InteractionTrigger for Range " + index.ToString() + ": " + range.name);
return; // Singularity
}
Quaternion triggerRotation = Quaternion.LookRotation(f, (range.characterPosition.fixYAxis? Vector3.up: script.transform.up));
Vector3 position = script.transform.position + triggerRotation * range.characterPosition.offset3D;
Vector3 direction = triggerRotation * range.characterPosition.direction3D;
Quaternion rotation = direction == Vector3.zero? triggerRotation: Quaternion.LookRotation(direction, (range.characterPosition.fixYAxis? Vector3.up: script.transform.up));
Vector3 up = rotation * Vector3.up;
Vector3 forward = rotation * Vector3.forward;
Handles.DrawWireDisc(position, up, range.characterPosition.radius);
if (range.characterPosition.orbit) {
float mag = range.characterPosition.offset.magnitude;
if (mag - range.characterPosition.radius > 0f) {
Handles.DrawWireDisc(script.transform.position, up, mag - range.characterPosition.radius);
}
Handles.DrawWireDisc(script.transform.position, up, mag + range.characterPosition.radius);
}
Vector3 x = forward * range.characterPosition.radius;
Quaternion q = Quaternion.AngleAxis(-range.characterPosition.maxAngle, up);
Vector3 dir = q * x;
if (direction != Vector3.zero && range.characterPosition.maxAngle < 180f) {
Handles.DrawLine(position, position + x);
Inspector.DotCap(0, position + x, Quaternion.identity, range.characterPosition.radius * 0.01f);
}
Handles.Label(position - Vector3.up * index * 0.05f, "Character Position for Range " + index.ToString() + ": " + range.name);
Color color = Handles.color;
Color transparent = new Color(color.r, color.g, color.b, 0.3f);
Handles.color = transparent;
Handles.DrawSolidArc(position, up, dir, range.characterPosition.maxAngle * 2f, range.characterPosition.radius);
Handles.color = color;
}
private void DrawCameraPosition(InteractionTrigger.Range range, int index) {
if (range.cameraPosition.lookAtTarget == null) return;
Vector3 labelPosition = range.cameraPosition.lookAtTarget.transform.position - Vector3.up * index * 0.05f;
if (range.cameraPosition.direction == Vector3.zero) {
Handles.Label(labelPosition, "Camera Position direction is Vector3.zero for Range" + index.ToString() + ": " + range.name);
return;
}
if (range.cameraPosition.maxAngle <= 0f) {
Handles.Label(labelPosition, "Camera Position max angle is zero for Range" + index.ToString() + ": " + range.name);
return;
}
range.cameraPosition.maxDistance = Mathf.Max(range.cameraPosition.maxDistance, 0f);
if (range.cameraPosition.maxDistance <= 0f) {
Handles.Label(labelPosition, "Camera Position Max Distance is zero for Range" + index.ToString() + ": " + range.name);
return;
}
Quaternion targetRotation = range.cameraPosition.GetRotation();
Vector3 position = range.cameraPosition.lookAtTarget.transform.position;
Vector3 direction = targetRotation * range.cameraPosition.direction;
direction = direction.normalized * range.cameraPosition.maxDistance;
Handles.DrawLine(position, position + direction);
Inspector.DotCap(0, position + direction, Quaternion.identity, 0.005f);
Handles.Label(position + direction * 1.1f, "Camera Position for Range " + index.ToString() + ": " + range.name);
if (range.cameraPosition.maxAngle >= 180f) return;
float r = Mathf.Sin(range.cameraPosition.maxAngle * Mathf.Deg2Rad) * range.cameraPosition.maxDistance;
float d = Mathf.Cos(range.cameraPosition.maxAngle * Mathf.Deg2Rad) * range.cameraPosition.maxDistance;
Quaternion rotation = targetRotation * Quaternion.LookRotation(range.cameraPosition.direction);
Inspector.CircleCap(0, position + direction.normalized * d, rotation, r);
if (SceneView.lastActiveSceneView != null && SceneView.lastActiveSceneView.camera != null) {
//Vector3 c = Vector3.Cross(direction, SceneView.lastActiveSceneView.camera.transform.forward);
Vector3 c = Vector3.Cross(direction, (range.cameraPosition.lookAtTarget.transform.position - SceneView.lastActiveSceneView.camera.transform.position).normalized);
c = Vector3.Cross(direction, c);
Quaternion dirRotation = Quaternion.AngleAxis(range.cameraPosition.maxAngle, c);
Vector3 dir3 = dirRotation * direction;
Handles.DrawLine(position, position + dir3);
Vector3 dir4 = Quaternion.Inverse(dirRotation) * direction;
Handles.DrawLine(position, position + dir4);
Handles.DrawWireArc(position, -c, dir3, range.cameraPosition.maxAngle * 2, range.cameraPosition.maxDistance);
}
}
private static Color GetColor(int index) {
float i = (float)index + 1f;
return new Color(1f / i, i * 0.1f, (i * i) + 0.1f);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 873f095e66d4c4a1cad1da5353934f00
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for LegIK.
* */
[CustomEditor(typeof(LegIK))]
public class LegIKInspector : IKInspector {
private LegIK script { get { return target as LegIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverTrigonometric
IKSolverLegInspector.AddInspector(solver, !Application.isPlaying, true);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverLegInspector.AddScene(script.solver, new Color(0f, 1f, 1f, 1f), true);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a1d4a2dfd093628479ab4e1611fab8cf
timeCreated: 1487059923
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for LimbIK.
* */
[CustomEditor(typeof(LimbIK))]
public class LimbIKInspector : IKInspector {
private LimbIK script { get { return target as LimbIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverLimb
IKSolverLimbInspector.AddInspector(solver, !Application.isPlaying, true);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverLimbInspector.AddScene(script.solver, new Color(0f, 1f, 1f, 1f), true);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: f0d1da4d6345d4ebbb58a7fbdb461e88
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for LookAtIK.
* */
[CustomEditor(typeof(LookAtIK))]
public class LookAtIKInspector : IKInspector {
private LookAtIK script { get { return target as LookAtIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverTrigonometric
IKSolverLookAtInspector.AddInspector(solver, !Application.isPlaying, true);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverLookAtInspector.AddScene(script.solver, new Color(0f, 1f, 1f, 1f), true);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 76c03a228cc7d4cfab5b65bf55ca89a0
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,93 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK
{
/*
* Custom inspector for RotationLimitAngle.
* */
[CustomEditor(typeof(RotationLimitAngle))]
[CanEditMultipleObjects]
public class RotationLimitAngleInspector : RotationLimitInspector
{
private RotationLimitAngle script { get { return target as RotationLimitAngle; } }
#region Inspector
public override void OnInspectorGUI()
{
GUI.changed = false;
// Draw the default inspector
DrawDefaultInspector();
script.limit = Mathf.Clamp(script.limit, 0, 180);
if (GUI.changed) EditorUtility.SetDirty(script);
}
#endregion Inspector
#region Scene
void OnSceneGUI()
{
// Set defaultLocalRotation so that the initial local rotation will be the zero point for the rotation limit
if (!Application.isPlaying && !script.defaultLocalRotationOverride) script.defaultLocalRotation = script.transform.localRotation;
if (script.axis == Vector3.zero) return;
DrawRotationSphere(script.transform.position);
// Display the main axis
DrawArrow(script.transform.position, Direction(script.axis), colorDefault, "Axis", 0.02f);
Vector3 swing = script.axis.normalized;
// Display limits
lastPoint = script.transform.position;
for (int i = 0; i < 360; i += 2)
{
Quaternion offset = Quaternion.AngleAxis(i, swing);
Quaternion limitedRotation = Quaternion.AngleAxis(script.limit, offset * script.crossAxis);
Vector3 limitedDirection = Direction(limitedRotation * swing);
Handles.color = colorDefaultTransparent;
Vector3 limitPoint = script.transform.position + limitedDirection;
if (i == 0) zeroPoint = limitPoint;
Handles.DrawLine(script.transform.position, limitPoint);
if (i > 0)
{
Handles.color = colorDefault;
Handles.DrawLine(limitPoint, lastPoint);
if (i == 358) Handles.DrawLine(limitPoint, zeroPoint);
}
lastPoint = limitPoint;
}
Handles.color = Color.white;
}
private Vector3 lastPoint, zeroPoint;
/*
* Converting directions from local space to world space
* */
private Vector3 Direction(Vector3 v)
{
if (script.transform.parent == null) return script.defaultLocalRotation * v;
return script.transform.parent.rotation * (script.defaultLocalRotation * v);
}
#endregion Scene
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c1549de5f5a6641758443bebbd5eb733
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,119 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK
{
/*
* Custom inspector for RotationLimitHinge
* */
[CustomEditor(typeof(RotationLimitHinge))]
[CanEditMultipleObjects]
public class RotationLimitHingeInspector : RotationLimitInspector
{
private RotationLimitHinge script { get { return target as RotationLimitHinge; } }
#region Inspector
public override void OnInspectorGUI()
{
GUI.changed = false;
// Draw the default inspector
DrawDefaultInspector();
if (GUI.changed) EditorUtility.SetDirty(script);
}
#endregion Inspector
#region Scene
void OnSceneGUI()
{
// Set defaultLocalRotation so that the initial local rotation will be the zero point for the rotation limit
if (!Application.isPlaying && !script.defaultLocalRotationOverride) script.defaultLocalRotation = script.transform.localRotation;
if (script.axis == Vector3.zero) return;
// Quick Editing Tools
Handles.BeginGUI();
GUILayout.BeginArea(new Rect(10, 10, 200, 50), "Rotation Limit Hinge", "Window");
// Rotating display
if (GUILayout.Button("Rotate display 90 degrees"))
{
if (!Application.isPlaying) Undo.RecordObject(script, "Rotate Display");
script.zeroAxisDisplayOffset += 90;
if (script.zeroAxisDisplayOffset >= 360) script.zeroAxisDisplayOffset = 0;
}
GUILayout.EndArea();
Handles.EndGUI();
// Normalize the main axes
Vector3 axis = Direction(script.axis.normalized);
Vector3 cross = Direction(Quaternion.AngleAxis(script.zeroAxisDisplayOffset, script.axis) * script.crossAxis.normalized);
// Axis vector
DrawArrow(script.transform.position, axis, colorDefault, "Axis", 0.02f);
if (script.useLimits)
{
// Zero rotation vector
DrawArrow(script.transform.position, cross * 0.5f, colorDefault, " 0", 0.02f);
// Arcs for the rotation limit
Handles.color = colorDefaultTransparent;
Handles.DrawSolidArc(script.transform.position, axis, cross, script.min, 0.5f);
Handles.DrawSolidArc(script.transform.position, axis, cross, script.max, 0.5f);
}
Handles.color = colorDefault;
GUI.color = colorDefault;
Inspector.CircleCap(0, script.transform.position, Quaternion.LookRotation(axis, cross), 0.5f);
if (!script.useLimits) return;
// Handles for adjusting rotation limits in the scene
Quaternion minRotation = Quaternion.AngleAxis(script.min, axis);
Handles.DrawLine(script.transform.position, script.transform.position + minRotation * cross);
Quaternion maxRotation = Quaternion.AngleAxis(script.max, axis);
Handles.DrawLine(script.transform.position, script.transform.position + maxRotation * cross);
// Undoable scene handles
float min = script.min;
min = DrawLimitHandle(min, script.transform.position + minRotation * cross, Quaternion.identity, 0.5f, "Min", -10);
if (min != script.min)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Min Limit");
script.min = min;
}
float max = script.max;
max = DrawLimitHandle(max, script.transform.position + maxRotation * cross, Quaternion.identity, 0.5f, "Max", 10);
if (max != script.max)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Max Limit");
script.max = max;
}
Handles.color = Color.white;
GUI.color = Color.white;
}
/*
* Converting directions from local space to world space
* */
private Vector3 Direction(Vector3 v)
{
if (script.transform.parent == null) return script.defaultLocalRotation * v;
return script.transform.parent.rotation * (script.defaultLocalRotation * v);
}
#endregion Scene
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9c6dcd6b6b7a74dd6b1ef5428b42217b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,88 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK
{
/*
* Base class for all RotationLimitInspector containing common helper methods and drawing instructions
* */
public class RotationLimitInspector : Editor
{
#region Public methods
// Universal color pallettes
public static Color colorDefault { get { return new Color(0.0f, 1.0f, 1.0f, 1.0f); } }
public static Color colorDefaultTransparent
{
get
{
Color d = colorDefault;
return new Color(d.r, d.g, d.b, 0.2f);
}
}
public static Color colorHandles { get { return new Color(1.0f, 0.5f, 0.25f, 1.0f); } }
public static Color colorRotationSphere { get { return new Color(1.0f, 1.0f, 1.0f, 0.1f); } }
public static Color colorInvalid { get { return new Color(1.0f, 0.3f, 0.3f, 1.0f); } }
public static Color colorValid { get { return new Color(0.2f, 1.0f, 0.2f, 1.0f); } }
/*
* Draws the default rotation limit sphere to the scene
* */
public static void DrawRotationSphere(Vector3 position)
{
Handles.color = colorRotationSphere;
Inspector.SphereCap(0, position, Quaternion.identity, 2.0f);
Handles.color = Color.white;
}
/*
* Draws a custom arrow to the scene
* */
public static void DrawArrow(Vector3 position, Vector3 direction, Color color, string label = "", float size = 0.01f)
{
Handles.color = color;
Handles.DrawLine(position, position + direction);
Inspector.SphereCap(0, position + direction, Quaternion.identity, size);
Handles.color = Color.white;
if (label != "")
{
GUI.color = color;
Handles.Label(position + direction, label);
GUI.color = Color.white;
}
}
/*
* Draws a handle for adjusting rotation limits in the scene
* */
public static float DrawLimitHandle(float limit, Vector3 position, Quaternion rotation, float radius, string label, float openingValue)
{
limit = Inspector.ScaleValueHandleSphere(limit, position, rotation, radius, 1);
string labelInfo = label + ": " + limit.ToString();
// If value is 0, draw a button to 'open' the value, because we cant scale 0
if (limit == 0)
{
labelInfo = "Open " + label;
if (Inspector.SphereButton(position, rotation, radius * 0.2f, radius * 0.07f))
{
limit = openingValue;
}
}
Handles.Label(position, labelInfo);
return limit;
}
#endregion
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 45e20680cc7804d86b30b931badba933
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,419 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
using System;
using System.Reflection;
namespace RootMotion.FinalIK
{
/*
* Custom inspector for RotationLimitPolygonal
* */
[CustomEditor(typeof(RotationLimitPolygonal))]
[CanEditMultipleObjects]
public class RotationLimitPolygonalInspector : RotationLimitInspector
{
/*
* Used for quick symmetric editing in the scene
* */
public enum Symmetry
{
Off,
X,
Y,
Z
}
private RotationLimitPolygonal script { get { return target as RotationLimitPolygonal; } }
private RotationLimitPolygonal clone;
private int selectedPoint = -1, deletePoint = -1, addPoint = -1;
private float degrees = 90;
private Symmetry symmetry;
#region Inspector
public void OnEnable()
{
// If initialized, set up the default polygon
if (script.points == null || (script.points != null && script.points.Length < 3))
{
script.ResetToDefault();
EditorUtility.SetDirty(script);
}
}
public override void OnInspectorGUI()
{
GUI.changed = false;
// Clamping values
script.twistLimit = Mathf.Clamp(script.twistLimit, 0, 180);
script.smoothIterations = Mathf.Clamp(script.smoothIterations, 0, 3);
DrawDefaultInspector();
if (GUI.changed) EditorUtility.SetDirty(script);
}
#endregion Inspector
#region Scene
public void OnSceneGUI()
{
GUI.changed = false;
// Set defaultLocalRotation so that the initial local rotation will be the zero point for the rotation limit
if (!Application.isPlaying && !script.defaultLocalRotationOverride) script.defaultLocalRotation = script.transform.localRotation;
if (script.axis == Vector3.zero) return;
// Quick Editing Tools
Handles.BeginGUI();
GUILayout.BeginArea(new Rect(10, 10, 550, 140), "Rotation Limit Polygonal", "Window");
// Cloning values from another RotationLimitPolygonal
EditorGUILayout.BeginHorizontal();
if (Inspector.Button("Clone From", "Make this rotation limit identical to another", script, GUILayout.Width(220))) CloneLimit();
clone = (RotationLimitPolygonal)EditorGUILayout.ObjectField("", clone, typeof(RotationLimitPolygonal), true);
EditorGUILayout.EndHorizontal();
// Symmetry
symmetry = (Symmetry)EditorGUILayout.EnumPopup("Symmetry", symmetry, GUILayout.Width(220));
// Flipping
EditorGUILayout.BeginHorizontal();
if (Inspector.Button("Flip X", "Flip points along local X axis", script, GUILayout.Width(100))) FlipLimit(0);
if (Inspector.Button("Flip Y", "Flip points along local Y axis", script, GUILayout.Width(100))) FlipLimit(1);
if (Inspector.Button("Flip Z", "Flip points along local Z axis", script, GUILayout.Width(100))) FlipLimit(2);
GUILayout.Label("Flip everything along axis");
EditorGUILayout.EndHorizontal();
// Rotating
EditorGUILayout.BeginHorizontal();
if (Inspector.Button("Rotate X", "Rotate points along X axis by Degrees", script, GUILayout.Width(100))) RotatePoints(degrees, Vector3.right);
if (Inspector.Button("Rotate Y", "Rotate points along Y axis by Degrees", script, GUILayout.Width(100))) RotatePoints(degrees, Vector3.up);
if (Inspector.Button("Rotate Z", "Rotate points along Z axis by Degrees", script, GUILayout.Width(100))) RotatePoints(degrees, Vector3.forward);
degrees = EditorGUILayout.FloatField("Degrees", degrees, GUILayout.Width(200));
EditorGUILayout.EndHorizontal();
// Smooth/Optimize
EditorGUILayout.BeginHorizontal();
if (Inspector.Button("Smooth", "Double the points", script)) Smooth();
if (Inspector.Button("Optimize", "Delete every second point", script)) Optimize();
EditorGUILayout.EndHorizontal();
GUILayout.EndArea();
Handles.EndGUI();
// Rebuild reach cones
script.BuildReachCones();
// Draw a white transparent sphere
DrawRotationSphere(script.transform.position);
// Draw Axis
DrawArrow(script.transform.position, Direction(script.axis), colorDefault, "Axis", 0.02f);
// Display limit points
for (int i = 0; i < script.points.Length; i++)
{
Color color = GetColor(i); // Paint the point in green or red if it belongs to an invalid reach cone
Handles.color = color;
GUI.color = color;
// Line from the center to the point and the label
Handles.DrawLine(script.transform.position, script.transform.position + Direction(script.points[i].point));
Handles.Label(script.transform.position + Direction(script.points[i].point + new Vector3(-0.02f, 0, 0)), " " + i.ToString());
// Selecting points
Handles.color = colorHandles;
if (Inspector.DotButton(script.transform.position + Direction(script.points[i].point), script.transform.rotation, 0.02f, 0.02f))
{
selectedPoint = i;
}
Handles.color = Color.white;
GUI.color = Color.white;
// Limit point GUI
if (i == selectedPoint)
{
Handles.BeginGUI();
//GUILayout.BeginArea(new Rect(Screen.width - 240, Screen.height - 180, 230, 130), "Limit Point " + i.ToString(), "Window");
GUILayout.BeginArea(new Rect(10, 160, 230, 130), "Limit Point " + i.ToString(), "Window");
if (Inspector.Button("Delete", "Delete this point", script))
{
if (script.points.Length > 3)
{
// Using the deletePoint index here because we dont want to delete points from the array that we are iterating
deletePoint = i;
}
else if (!Warning.logged) script.LogWarning("Polygonal Rotation Limit should have at least 3 limit points");
}
if (Inspector.Button("Add Point", "Add a new point next to this one", script))
{
addPoint = i;
}
// Store point for undo
Vector3 oldPoint = script.points[i].point;
// Manual input for the point position
Inspector.AddVector3(ref script.points[i].point, "Point", script, GUILayout.Width(210));
EditorGUILayout.Space();
// Tangent weight
Inspector.AddFloat(ref script.points[i].tangentWeight, "Tangent Weight", "Weight of this point's tangent. Used in smoothing.", script, -Mathf.Infinity, Mathf.Infinity, GUILayout.Width(150));
GUILayout.EndArea();
Handles.EndGUI();
// Moving Points
Vector3 pointWorld = Handles.PositionHandle(script.transform.position + Direction(script.points[i].point), Quaternion.identity);
Vector3 newPoint = InverseDirection(pointWorld - script.transform.position);
if (newPoint != script.points[i].point)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Move Limit Point");
script.points[i].point = newPoint;
}
// Symmetry
if (symmetry != Symmetry.Off && script.points.Length > 3 && oldPoint != script.points[i].point)
{
RotationLimitPolygonal.LimitPoint symmetryPoint = GetClosestPoint(Symmetrize(oldPoint, symmetry));
if (symmetryPoint != script.points[i])
{
symmetryPoint.point = Symmetrize(script.points[i].point, symmetry);
}
}
}
// Normalize the point
script.points[i].point = script.points[i].point.normalized;
}
// Display smoothed polygon
for (int i = 0; i < script.P.Length; i++)
{
Color color = GetColor(i);
// Smoothed triangles are transparent
Handles.color = new Color(color.r, color.g, color.b, 0.25f);
Handles.DrawLine(script.transform.position, script.transform.position + Direction(script.P[i]));
Handles.color = color;
if (i < script.P.Length - 1) Handles.DrawLine(script.transform.position + Direction(script.P[i]), script.transform.position + Direction(script.P[i + 1]));
else Handles.DrawLine(script.transform.position + Direction(script.P[i]), script.transform.position + Direction(script.P[0]));
Handles.color = Color.white;
}
// Deleting points
if (deletePoint != -1)
{
DeletePoint(deletePoint);
selectedPoint = -1;
deletePoint = -1;
}
// Adding points
if (addPoint != -1)
{
AddPoint(addPoint);
addPoint = -1;
}
if (GUI.changed) EditorUtility.SetDirty(script);
}
private Color GetColor(int i)
{
// Paint the polygon in red if the reach cone is invalid
return script.reachCones[i].isValid ? colorDefault : colorInvalid;
}
/*
* Doubles the number of Limit Points
* */
private void Smooth()
{
int length = script.points.Length;
for (int i = 0; i < length; i++)
{
AddPoint(i + i);
}
}
/*
* Reduces the number of Limit Points
* */
private void Optimize()
{
for (int i = 1; i < script.points.Length; i++)
{
if (script.points.Length > 3) DeletePoint(i);
}
}
/*
* Flips the rotation limit along the axis
* */
private void FlipLimit(int axis)
{
script.axis[axis] = -script.axis[axis];
foreach (RotationLimitPolygonal.LimitPoint limitPoint in script.points) limitPoint.point[axis] = -limitPoint.point[axis];
Array.Reverse(script.points);
script.BuildReachCones();
}
private void RotatePoints(float degrees, Vector3 axis)
{
foreach (RotationLimitPolygonal.LimitPoint limitPoint in script.points) limitPoint.point = Quaternion.AngleAxis(degrees, axis) * limitPoint.point;
script.BuildReachCones();
}
/*
* Converting directions from local space to world space
* */
private Vector3 Direction(Vector3 v)
{
if (script.transform.parent == null) return script.defaultLocalRotation * v;
return script.transform.parent.rotation * (script.defaultLocalRotation * v);
}
/*
* Inverse of Direction(Vector3 v)
* */
private Vector3 InverseDirection(Vector3 v)
{
if (script.transform.parent == null) return Quaternion.Inverse(script.defaultLocalRotation) * v;
return Quaternion.Inverse(script.defaultLocalRotation) * Quaternion.Inverse(script.transform.parent.rotation) * v;
}
/*
* Removing Limit Points
* */
private void DeletePoint(int p)
{
RotationLimitPolygonal.LimitPoint[] newPoints = new RotationLimitPolygonal.LimitPoint[0];
for (int i = 0; i < script.points.Length; i++)
{
if (i != p)
{
Array.Resize(ref newPoints, newPoints.Length + 1);
newPoints[newPoints.Length - 1] = script.points[i];
}
}
script.points = newPoints;
script.BuildReachCones();
}
/*
* Creating new Limit Points
* */
private void AddPoint(int p)
{
RotationLimitPolygonal.LimitPoint[] newPoints = new RotationLimitPolygonal.LimitPoint[script.points.Length + 1];
for (int i = 0; i < p + 1; i++) newPoints[i] = script.points[i];
newPoints[p + 1] = new RotationLimitPolygonal.LimitPoint();
Vector3 nextPoint = Vector3.forward;
if (p < script.points.Length - 1) nextPoint = script.points[p + 1].point;
else nextPoint = script.points[0].point;
newPoints[p + 1].point = Vector3.Lerp(script.points[p].point, nextPoint, 0.5f);
for (int i = p + 2; i < newPoints.Length; i++) newPoints[i] = script.points[i - 1];
script.points = newPoints;
script.BuildReachCones();
}
/*
* Clone properties from another RotationLimitPolygonal
* */
private void CloneLimit()
{
if (clone == null) return;
if (clone == script)
{
script.LogWarning("Can't clone from self.");
return;
}
script.axis = clone.axis;
script.twistLimit = clone.twistLimit;
script.smoothIterations = clone.smoothIterations;
script.points = new RotationLimitPolygonal.LimitPoint[clone.points.Length];
for (int i = 0; i < script.points.Length; i++)
{
script.points[i] = (RotationLimitPolygonal.LimitPoint)CloneObject(clone.points[i]);
}
script.BuildReachCones();
}
private static object CloneObject(object o)
{
Type t = o.GetType();
object clone = Activator.CreateInstance(t);
foreach (FieldInfo fi in t.GetFields())
{
fi.SetValue(clone, fi.GetValue(o));
}
return clone;
}
/*
* Flipping vectors for symmetry
* */
private static Vector3 Symmetrize(Vector3 v, Symmetry symmetry)
{
switch (symmetry)
{
case Symmetry.X: return new Vector3(-v.x, v.y, v.z);
case Symmetry.Y: return new Vector3(v.x, -v.y, v.z);
case Symmetry.Z: return new Vector3(v.x, v.y, -v.z);
default: return v;
}
}
/*
* Returns closest point to a position. Used for symmetric editing
* */
private RotationLimitPolygonal.LimitPoint GetClosestPoint(Vector3 v)
{
float closestDistace = Mathf.Infinity;
RotationLimitPolygonal.LimitPoint closestPoint = null;
foreach (RotationLimitPolygonal.LimitPoint limitPoint in script.points)
{
if (limitPoint.point == v) return limitPoint;
float d = Vector3.Distance(limitPoint.point, v);
if (d < closestDistace)
{
closestPoint = limitPoint;
closestDistace = d;
}
}
return closestPoint;
}
#endregion Scene
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: de47701c76e93420cb6fe33b292880b9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,487 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
using System;
namespace RootMotion.FinalIK
{
/*
* Custom inspector for RotationLimitSpline
* */
[CustomEditor(typeof(RotationLimitSpline))]
[CanEditMultipleObjects]
public class RotationLimitSplineInspector : RotationLimitInspector
{
// Determines if we are dragging the handle's limits or angle
public enum ScaleMode
{
Limit,
Angle
}
// In Smooth TangentMode, in and out tangents will always be the same, Independent allowes for difference
public enum TangentMode
{
Smooth,
Independent
}
private RotationLimitSpline script { get { return target as RotationLimitSpline; } }
private RotationLimitSpline clone;
private ScaleMode scaleMode;
private TangentMode tangentMode;
private int selectedHandle = -1, deleteHandle = -1, addHandle = -1;
#region Inspector
public void OnEnable()
{
// Check if RotationLimitspline is properly set up, if not, reset to defaults
if (script.spline == null)
{
script.spline = new AnimationCurve(defaultKeys);
EditorUtility.SetDirty(script);
}
else if (script.spline.keys.Length < 4)
{
script.spline.keys = defaultKeys;
EditorUtility.SetDirty(script);
}
}
/*
* Returns the default keyframes for the RotationLimitspline
* */
private static Keyframe[] defaultKeys
{
get
{
Keyframe[] k = new Keyframe[5];
// Values for a simple elliptic spline
k[0].time = 0.01f;
k[0].value = 30;
k[0].inTangent = 0.01f;
k[0].outTangent = 0.01f;
k[1].time = 90;
k[1].value = 45;
k[1].inTangent = 0.01f;
k[1].outTangent = 0.01f;
k[2].time = 180;
k[2].value = 30;
k[2].inTangent = 0.01f;
k[2].outTangent = 0.01f;
k[3].time = 270;
k[3].value = 45;
k[3].inTangent = 0.01f;
k[3].outTangent = 0.01f;
k[4].time = 360;
k[4].value = 30;
k[4].inTangent = 0.01f;
k[4].outTangent = 0.01f;
return k;
}
}
public override void OnInspectorGUI()
{
GUI.changed = false;
DrawDefaultInspector();
script.twistLimit = Mathf.Clamp(script.twistLimit, 0, 180);
if (GUI.changed) EditorUtility.SetDirty(script);
}
/*
* Make sure the keyframes and tangents are valid
* */
private void ValidateKeyframes(Keyframe[] keys)
{
keys[keys.Length - 1].value = keys[0].value;
keys[keys.Length - 1].time = keys[0].time + 360;
keys[keys.Length - 1].inTangent = keys[0].inTangent;
}
#endregion Inspector
#region Scene
void OnSceneGUI()
{
GUI.changed = false;
// Get the keyframes of the AnimationCurve to be manipulated
Keyframe[] keys = script.spline.keys;
// Set defaultLocalRotation so that the initial local rotation will be the zero point for the rotation limit
if (!Application.isPlaying && !script.defaultLocalRotationOverride) script.defaultLocalRotation = script.transform.localRotation;
if (script.axis == Vector3.zero) return;
// Make the curve loop
script.spline.postWrapMode = WrapMode.Loop;
script.spline.preWrapMode = WrapMode.Loop;
DrawRotationSphere(script.transform.position);
// Display the main axis
DrawArrow(script.transform.position, Direction(script.axis), colorDefault, "Axis", 0.02f);
Vector3 swing = script.axis.normalized;
// Editing tools GUI
Handles.BeginGUI();
GUILayout.BeginArea(new Rect(10, 10, 440, 100), "Rotation Limit Spline", "Window");
// Scale Mode and Tangent Mode
GUILayout.BeginHorizontal();
scaleMode = (ScaleMode)EditorGUILayout.EnumPopup("Drag Handle", scaleMode);
tangentMode = (TangentMode)EditorGUILayout.EnumPopup("Drag Tangents", tangentMode);
GUILayout.EndHorizontal();
EditorGUILayout.Space();
if (Inspector.Button("Rotate 90 degrees", "Rotate rotation limit around axis.", script, GUILayout.Width(220)))
{
if (!Application.isPlaying) Undo.RecordObject(script, "Handle Value");
for (int i = 0; i < keys.Length; i++) keys[i].time += 90;
}
// Cloning values from another RotationLimitSpline
EditorGUILayout.BeginHorizontal();
if (Inspector.Button("Clone From", "Make this rotation limit identical to another", script, GUILayout.Width(220)))
{
CloneLimit();
keys = script.spline.keys;
}
clone = (RotationLimitSpline)EditorGUILayout.ObjectField("", clone, typeof(RotationLimitSpline), true);
EditorGUILayout.EndHorizontal();
GUILayout.EndArea();
Handles.EndGUI();
// Draw keyframes
for (int i = 0; i < keys.Length - 1; i++)
{
float angle = keys[i].time;
// Start drawing handles
Quaternion offset = Quaternion.AngleAxis(angle, swing);
Quaternion rotation = Quaternion.AngleAxis(keys[i].value, offset * script.crossAxis);
Vector3 position = script.transform.position + Direction(rotation * swing);
Handles.Label(position, " " + i.ToString());
// Dragging Values
if (selectedHandle == i)
{
Handles.color = colorHandles;
switch (scaleMode)
{
case ScaleMode.Limit:
float inputValue = keys[i].value;
inputValue = Mathf.Clamp(Inspector.ScaleValueHandleSphere(inputValue, position, Quaternion.identity, 0.5f, 0), 0.01f, 180);
if (keys[i].value != inputValue)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Handle Value");
keys[i].value = inputValue;
}
break;
case ScaleMode.Angle:
float inputTime = keys[i].time;
inputTime = Inspector.ScaleValueHandleSphere(inputTime, position, Quaternion.identity, 0.5f, 0);
if (keys[i].time != inputTime)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Handle Angle");
keys[i].time = inputTime;
}
break;
}
}
// Handle select button
if (selectedHandle != i)
{
Handles.color = Color.blue;
if (Inspector.SphereButton(position, script.transform.rotation, 0.05f, 0.05f))
{
selectedHandle = i;
}
}
// Tangents
if (selectedHandle == i)
{
// Evaluate positions before and after the key to get the tangent positions
Vector3 prevPosition = GetAnglePosition(keys[i].time - 1);
Vector3 nextPosition = GetAnglePosition(keys[i].time + 1);
// Draw handles for the tangents
Handles.color = Color.white;
Vector3 toNext = (nextPosition - position).normalized * 0.3f;
float outTangent = keys[i].outTangent;
outTangent = Inspector.ScaleValueHandleSphere(outTangent, position + toNext, Quaternion.identity, 0.2f, 0);
Vector3 toPrev = (prevPosition - position).normalized * 0.3f;
float inTangent = keys[i].inTangent;
inTangent = Inspector.ScaleValueHandleSphere(inTangent, position + toPrev, Quaternion.identity, 0.2f, 0);
if (outTangent != keys[i].outTangent || inTangent != keys[i].inTangent) selectedHandle = i;
// Make the other tangent match the dragged tangent (if in "Smooth" TangentMode)
switch (tangentMode)
{
case TangentMode.Smooth:
if (outTangent != keys[i].outTangent)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Tangents");
keys[i].outTangent = outTangent;
keys[i].inTangent = outTangent;
}
else if (inTangent != keys[i].inTangent)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Tangents");
keys[i].outTangent = inTangent;
keys[i].inTangent = inTangent;
}
break;
case TangentMode.Independent:
if (outTangent != keys[i].outTangent)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Tangents");
keys[i].outTangent = outTangent;
}
else if (inTangent != keys[i].inTangent)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Tangents");
keys[i].inTangent = inTangent;
}
break;
}
// Draw lines and labels to tangent handles
Handles.color = Color.white;
GUI.color = Color.white;
Handles.DrawLine(position, position + toNext);
Handles.Label(position + toNext, " Out");
Handles.DrawLine(position, position + toPrev);
Handles.Label(position + toPrev, " In");
}
}
// Selected Point GUI
if (selectedHandle != -1)
{
Handles.BeginGUI();
//GUILayout.BeginArea(new Rect(Screen.width - 240, Screen.height - 200, 230, 150), "Handle " + selectedHandle.ToString(), "Window");
GUILayout.BeginArea(new Rect(10, 120, 230, 150), "Handle " + selectedHandle.ToString(), "Window");
if (Inspector.Button("Delete", "Delete this handle", script))
{
if (keys.Length > 4)
{
deleteHandle = selectedHandle;
}
else if (!Warning.logged) script.LogWarning("Spline Rotation Limit should have at least 3 handles");
}
if (Inspector.Button("Add Handle", "Add a new handle next to this one", script))
{
addHandle = selectedHandle;
}
// Clamp the key angles to previous and next handle angles
float prevTime = 0, nextTime = 0;
if (selectedHandle < keys.Length - 2) nextTime = keys[selectedHandle + 1].time;
else nextTime = keys[0].time + 360;
if (selectedHandle == 0) prevTime = keys[keys.Length - 2].time - 360;
else prevTime = keys[selectedHandle - 1].time;
// Angles
float inputTime = keys[selectedHandle].time;
inputTime = Mathf.Clamp(EditorGUILayout.FloatField(new GUIContent("Angle", "Angle of the point (0-360)."), inputTime), prevTime, nextTime);
if (keys[selectedHandle].time != inputTime)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Handle Angle");
keys[selectedHandle].time = inputTime;
}
// Limits
float inputValue = keys[selectedHandle].value;
inputValue = Mathf.Clamp(EditorGUILayout.FloatField(new GUIContent("Limit", "Max angular limit from Axis at this angle"), inputValue), 0, 180);
if (keys[selectedHandle].value != inputValue)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Handle Limit");
keys[selectedHandle].value = inputValue;
}
// In Tangents
float inputInTangent = keys[selectedHandle].inTangent;
inputInTangent = EditorGUILayout.FloatField(new GUIContent("In Tangent", "In tangent of the handle point on the spline"), inputInTangent);
if (keys[selectedHandle].inTangent != inputInTangent)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Handle In Tangent");
keys[selectedHandle].inTangent = inputInTangent;
}
// Out tangents
float inputOutTangent = keys[selectedHandle].outTangent;
inputOutTangent = EditorGUILayout.FloatField(new GUIContent("Out Tangent", "Out tangent of the handle point on the spline"), inputOutTangent);
if (keys[selectedHandle].outTangent != inputOutTangent)
{
if (!Application.isPlaying) Undo.RecordObject(script, "Handle Out Tangent");
keys[selectedHandle].outTangent = inputOutTangent;
}
GUILayout.EndArea();
Handles.EndGUI();
}
// Make sure the keyframes are valid;
ValidateKeyframes(keys);
// Replace the AnimationCurve keyframes with the manipulated keyframes
script.spline.keys = keys;
// Display limits
for (int i = 0; i < 360; i += 2)
{
float evaluatedLimit = script.spline.Evaluate((float)i);
Quaternion offset = Quaternion.AngleAxis(i, swing);
Quaternion evaluatedRotation = Quaternion.AngleAxis(evaluatedLimit, offset * script.crossAxis);
Quaternion testRotation = Quaternion.AngleAxis(179.9f, offset * script.crossAxis);
Quaternion limitedRotation = script.LimitSwing(testRotation);
Vector3 evaluatedDirection = evaluatedRotation * swing;
Vector3 limitedDirection = limitedRotation * swing;
// Display the limit points in red if they are out of range
bool isValid = Vector3.Distance(evaluatedDirection, limitedDirection) < 0.01f && evaluatedLimit >= 0;
Color color = isValid ? colorDefaultTransparent : colorInvalid;
Vector3 limitPoint = script.transform.position + Direction(evaluatedDirection);
Handles.color = color;
if (i == 0) zeroPoint = limitPoint;
Handles.DrawLine(script.transform.position, limitPoint);
if (i > 0)
{
Handles.color = isValid ? colorDefault : colorInvalid;
Handles.DrawLine(limitPoint, lastPoint);
if (i == 358) Handles.DrawLine(limitPoint, zeroPoint);
}
lastPoint = limitPoint;
}
// Deleting points
if (deleteHandle != -1)
{
DeleteHandle(deleteHandle);
selectedHandle = -1;
deleteHandle = -1;
}
// Adding points
if (addHandle != -1)
{
AddHandle(addHandle);
addHandle = -1;
}
Handles.color = Color.white;
if (GUI.changed) EditorUtility.SetDirty(script);
}
private Vector3 lastPoint, zeroPoint;
/*
* Return the evaluated position for the specified angle
* */
private Vector3 GetAnglePosition(float angle)
{
Vector3 swing = script.axis.normalized;
Quaternion offset = Quaternion.AngleAxis(angle, swing);
Quaternion rotation = Quaternion.AngleAxis(script.spline.Evaluate(angle), offset * script.crossAxis);
return script.transform.position + Direction(rotation * swing);
}
/*
* Converting directions from local space to world space
* */
private Vector3 Direction(Vector3 v)
{
if (script.transform.parent == null) return script.defaultLocalRotation * v;
return script.transform.parent.rotation * (script.defaultLocalRotation * v);
}
/*
* Removing Handles
* */
private void DeleteHandle(int p)
{
Keyframe[] keys = script.spline.keys;
Keyframe[] newKeys = new Keyframe[0];
for (int i = 0; i < keys.Length; i++)
{
if (i != p)
{
Array.Resize(ref newKeys, newKeys.Length + 1);
newKeys[newKeys.Length - 1] = keys[i];
}
}
script.spline.keys = newKeys;
}
/*
* Creating new Handles
* */
private void AddHandle(int p)
{
Keyframe[] keys = script.spline.keys;
Keyframe[] newKeys = new Keyframe[keys.Length + 1];
for (int i = 0; i < p + 1; i++) newKeys[i] = keys[i];
float nextTime = 0;
if (p < keys.Length - 1) nextTime = keys[p + 1].time;
else nextTime = keys[0].time;
float newTime = Mathf.Lerp(keys[p].time, nextTime, 0.5f);
float newValue = script.spline.Evaluate(newTime);
newKeys[p + 1] = new Keyframe(newTime, newValue);
for (int i = p + 2; i < newKeys.Length; i++) newKeys[i] = keys[i - 1];
script.spline.keys = newKeys;
}
/*
* Clone properties from another RotationLimitSpline
* */
private void CloneLimit()
{
if (clone == null) return;
if (clone == script)
{
script.LogWarning("Can't clone from self.");
return;
}
script.axis = clone.axis;
script.twistLimit = clone.twistLimit;
script.spline.keys = clone.spline.keys;
}
#endregion Scene
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: bcf14365f408140e9b73e2b9bcc0e88f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for TrigonometricIK.
* */
[CustomEditor(typeof(TrigonometricIK))]
public class TrigonometricIKInspector : IKInspector {
private TrigonometricIK script { get { return target as TrigonometricIK; }}
protected override MonoBehaviour GetMonoBehaviour(out int executionOrder) {
executionOrder = 9997;
return script;
}
protected override void OnApplyModifiedProperties() {
if (!Application.isPlaying) script.solver.Initiate(script.transform);
}
protected override void AddInspector() {
// Draw the inspector for IKSolverTrigonometric
IKSolverTrigonometricInspector.AddInspector(solver, !Application.isPlaying, true);
// Warning box
string message = string.Empty;
if (!script.solver.IsValid(ref message)) AddWarningBox(message);
}
void OnSceneGUI() {
// Draw the scene veiw helpers
IKSolverTrigonometricInspector.AddScene(script.solver, new Color(0f, 1f, 1f, 1f), true);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 98c4f265c1c854d718c42d0308c186d9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace RootMotion.FinalIK
{
[CustomEditor(typeof(UniversalPoser))]
public class UniversalPoserInspector : Editor
{
private UniversalPoser script { get { return target as UniversalPoser; } }
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(serializedObject.FindProperty("fixTransforms"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("poseRoot"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("weight"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("localRotationWeight"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("localPositionWeight"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("targetAxis1"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("targetAxis2"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("axis1"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("axis2"));
var bones = serializedObject.FindProperty("bones");
EditorGUILayout.PropertyField(bones);
if (GUILayout.Button("Auto-Assign Bones"))
{
var children = script.GetComponentsInChildren<Transform>();
if (children.Length > 1)
{
bones.ClearArray();
for (int i = 1; i < children.Length; i++)
{
bones.InsertArrayElementAtIndex(i - 1);
bones.GetArrayElementAtIndex(i - 1).FindPropertyRelative("bone").objectReferenceValue = children[i];
}
}
}
if (serializedObject.ApplyModifiedProperties())
{
EditorUtility.SetDirty(script);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fadbe5ce83d31a498af4e227c5970b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace RootMotion.FinalIK {
/*
* Custom inspector for VRIK.
* */
[CustomEditor(typeof(VRIK))]
public class VRIKInspector : Editor {
private VRIK script { get { return target as VRIK; }}
private MonoScript monoScript;
void OnEnable() {
if (serializedObject == null) return;
// Changing the script execution order
if (!Application.isPlaying) {
monoScript = MonoScript.FromMonoBehaviour(script as MonoBehaviour);
int currentExecutionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (currentExecutionOrder != 9998) MonoImporter.SetExecutionOrder(monoScript, 9998);
if (script.references.isEmpty) script.AutoDetectReferences();
script.solver.DefaultAnimationCurves();
script.solver.GuessHandOrientations(script.references, true);
// TODO Set dirty
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6677b87ab01b6d341b8dba26424ac1c9
timeCreated: 1461012106
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d0741bad3e4d74bb99540a06e7929fc4
folderAsset: yes
timeCreated: 1436186162
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion.Demos {
/// <summary>
/// Scene view helper for the LimbIK BendGoal
/// </summary>
[CustomEditor(typeof(BendGoal))]
public class BendGoalInspector : Editor {
private BendGoal script { get { return target as BendGoal; }}
public override void OnInspectorGUI() {
DrawDefaultInspector();
}
void OnSceneGUI() {
if (script.limbIK == null) return;
if (script.limbIK.solver.bone2 == null) return;
if (script.limbIK.solver.bone2.transform == null) return;
Handles.color = Color.cyan;
Vector3 bonePosition = script.limbIK.solver.bone2.transform.position;
Handles.DrawLine(script.transform.position, bonePosition);
Inspector.SphereCap(0, script.transform.position, Quaternion.identity, 0.05f);
Inspector.SphereCap(0, bonePosition, Quaternion.identity, 0.025f);
Handles.color = Color.white;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 684cc44fa10474fefa6a837d949e7449
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: