main
bairong 11 months ago
parent 852d367595
commit f5378c39b2

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 71fb31411335fcb47a200e5c52ca6ec1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,159 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RootMotion.FinalIK;
namespace Autohand {
[DefaultExecutionOrder(2), RequireComponent(typeof(VRIK))]
public class AutoHandVRIK : MonoBehaviour {
public Hand rightHand;
public Hand leftHand;
[Tooltip("The transform (or a child transform) of the Tracked VR controller")]
public Transform rightTrackedController;
[Tooltip("The transform (or a child transform) of the Tracked VR controller")]
public Transform leftTrackedController;
[HideInInspector, Tooltip("Should be a transform under the Auto Hand, can be used to adjust the IK offset so the hands connect with the arms properly (This is the point where the wrists follow the hands)")]
public Transform rightIKTarget = null;
[HideInInspector, Tooltip("Should be a transform under the Auto Hand, can be used to adjust the IK offset so the hands connect with the arms properly (This is the point where the wrists follow the hands)")]
public Transform leftIKTarget = null;
[HideInInspector, Tooltip("Should be a transform under the IK Character hierarchy, can be used to adjust the IK offset so the hands connect with the arms properly")]
public Transform rightHandFollowTarget = null;
[HideInInspector, Tooltip("Should be a transform under the IK Character hierarchy, can be used to adjust the IK offset so the hands connect with the arms properly")]
public Transform leftHandFollowTarget = null;
VRIK visibleIK;
VRIK invisibleIK;
bool isCopy = false;
bool resetQueued = false;
public void DesignateCopy() {
isCopy = true;
}
void Start() {
visibleIK = GetComponent<VRIK>();
if (!isCopy)
SetupIKCopy();
if(AutoHandPlayer.Instance != null)
visibleIK.transform.position -= Vector3.up * AutoHandPlayer.Instance.heightOffset;
if(!isCopy && AutoHandPlayer.Instance != null)
AutoHandPlayer.Instance.OnSnapTurn += AutoPlayerResetIKEvent;
if(!isCopy && AutoHandPlayer.Instance != null)
AutoHandPlayer.Instance.OnTeleported += AutoPlayerResetIKEvent;
}
void AutoPlayerResetIKEvent(AutoHandPlayer player) {
resetQueued = true;
}
private void LateUpdate() {
if(resetQueued) {
visibleIK.solver.Reset();
invisibleIK.solver.Reset();
visibleIK.UpdateSolverExternal();
invisibleIK.UpdateSolverExternal();
rightHand.SetMoveTo();
rightHand.SetHandLocation(rightHand.moveTo.position, rightHand.moveTo.rotation);
leftHand.SetMoveTo();
leftHand.SetHandLocation(leftHand.moveTo.position, leftHand.moveTo.rotation);
resetQueued = false;
}
}
//private void Update() {
// if(resetQueued) {
// invisibleIK.solver.Reset();
// invisibleIK.solver.Update();
// visibleIK.solver.Reset();
// visibleIK.solver.Update();
// }
//}
void SetupIKCopy() {
if(rightIKTarget == null)
{
rightIKTarget = new GameObject().transform;
rightIKTarget.name = "rightIKTarget";
rightIKTarget.transform.parent = rightHand.transform;
rightIKTarget.transform.localPosition = Vector3.zero;
rightIKTarget.transform.localRotation = Quaternion.identity;
}
if (leftIKTarget == null)
{
leftIKTarget = new GameObject().transform;
leftIKTarget.name = "leftIKTarget";
leftIKTarget.transform.parent = leftHand.transform;
leftIKTarget.transform.localPosition = Vector3.zero;
leftIKTarget.transform.localRotation = Quaternion.identity;
}
if (rightHandFollowTarget == null)
{
rightHandFollowTarget = new GameObject().transform;
rightHandFollowTarget.name = "rightHandTarget";
rightHandFollowTarget.transform.parent = rightHand.transform.parent;
rightHandFollowTarget.transform.localPosition = rightHand.transform.localPosition;
rightHandFollowTarget.transform.localRotation = rightHand.transform.localRotation;
}
if (leftHandFollowTarget == null)
{
leftHandFollowTarget = new GameObject().transform;
leftHandFollowTarget.name = "leftHandTarget";
leftHandFollowTarget.transform.parent = leftHand.transform.parent;
leftHandFollowTarget.transform.localPosition = leftHand.transform.localPosition;
leftHandFollowTarget.transform.localRotation = leftHand.transform.localRotation;
}
rightHand.transform.parent = visibleIK.transform.parent;
leftHand.transform.parent = visibleIK.transform.parent;
visibleIK.references.rightHand = rightHandFollowTarget;
visibleIK.references.leftHand = leftHandFollowTarget;
invisibleIK = Instantiate(visibleIK.gameObject, visibleIK.transform.parent).GetComponent<VRIK>();
invisibleIK.name = "Hidden IK Copy (Auto Hand + VRIK requirement)";
DeactivateEverything(invisibleIK.transform);
invisibleIK.enabled = true;
if (invisibleIK.CanGetComponent<AutoHandVRIK>(out var autoIK)) {
autoIK.DesignateCopy();
autoIK.enabled = true;
rightHand.follow = autoIK.rightHandFollowTarget;
leftHand.follow = autoIK.leftHandFollowTarget;
autoIK.invisibleIK = invisibleIK;
autoIK.visibleIK = visibleIK;
}
visibleIK.solver.rightArm.target = rightIKTarget;
visibleIK.solver.leftArm.target = leftIKTarget;
invisibleIK.solver.rightArm.target = rightTrackedController;
invisibleIK.solver.leftArm.target = leftTrackedController;
}
void DeactivateEverything(Transform deactivate) {
var behaviours = deactivate.GetComponents<Component>();
var childBehaviours = deactivate.GetComponentsInChildren<Component>();
for(int j = behaviours.Length - 1; j >= 0; j--)
if(!(behaviours[j] is Animator) && !(behaviours[j] is VRIK) && !(behaviours[j] is AutoHandVRIK) && !(behaviours[j] is Transform))
Destroy(behaviours[j]);
for(int j = childBehaviours.Length - 1; j >= 0; j--)
if(!(childBehaviours[j] is Animator) && !(childBehaviours[j] is VRIK) && !(childBehaviours[j] is AutoHandVRIK) && !(childBehaviours[j] is Transform))
Destroy(childBehaviours[j]);
}
}
}

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

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 15b1b958630b36b45ad5d22e80afc709
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 61bbdfe2a1a5b5948abfa4cae0ae82e0
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,2 @@
This package requires the FinalIK asset:
https://assetstore.unity.com/packages/tools/animation/final-ik-14290?gclid=Cj0KCQiAmpyRBhC-ARIsABs2EApHCXO_6XW-sbQe-B1sW0SI4sNfS_K2kPIi5HBHBWt-1YYfEwf-SkoaAmIKEALw_wcB&gclsrc=aw.ds

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3d63aae31d176a246ad326b2b9ce5f80
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c0422cfa295b1a24482dac8e3e90b2f6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3bf028df46ae5644c9287714b674fed9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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

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

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 490fde02f41ae014abec4edfce28648f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RootMotion
{
public abstract class AnimationModifier : MonoBehaviour
{
protected Animator animator;
protected Baker baker;
public virtual void OnInitiate(Baker baker, Animator animator)
{
this.baker = baker;
this.animator = animator;
}
public virtual void OnStartClip(AnimationClip clip) { }
public virtual void OnBakerUpdate(float normalizedTime) { }
}
}

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

@ -0,0 +1,60 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RootMotion
{
public class AnimationModifierStack : MonoBehaviour
{
public AnimationModifier[] modifiers = new AnimationModifier[0];
private Animator animator;
private Baker baker;
private void Start()
{
animator = GetComponent<Animator>();
baker = GetComponent<Baker>();
baker.OnStartClip += OnBakerStartClip;
baker.OnUpdateClip += OnBakerUpdateClip;
foreach (AnimationModifier m in modifiers)
{
m.OnInitiate(baker, animator);
}
}
private void OnBakerStartClip(AnimationClip clip, float normalizedTime)
{
foreach (AnimationModifier m in modifiers)
{
m.OnStartClip(clip);
}
}
private void OnBakerUpdateClip(AnimationClip clip, float normalizedTime)
{
foreach (AnimationModifier m in modifiers)
{
if (!m.enabled) continue;
m.OnBakerUpdate(normalizedTime);
}
}
private void LateUpdate()
{
if (!animator.enabled && !baker.isBaking) return;
if (baker.isBaking && baker.mode == Baker.Mode.AnimationClips) return;
if (animator.runtimeAnimatorController == null) return;
AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(0);
float n = info.normalizedTime;
foreach (AnimationModifier m in modifiers)
{
if (!m.enabled) continue;
m.OnBakerUpdate(n);
}
}
}
}

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

@ -0,0 +1,602 @@
using UnityEngine;
using System.Collections;
using UnityEngine.Playables;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace RootMotion
{
/// <summary>
/// Base class for animation bakers, handles timing, keyframing and saving AnimationClips.
/// </summary>
[HelpURL("http://www.root-motion.com/finalikdox/html/page3.html")]
[AddComponentMenu("Scripts/RootMotion/Baker")]
public abstract class Baker : MonoBehaviour
{
// Open the User Manual URL
[ContextMenu("User Manual")]
void OpenUserManual()
{
Application.OpenURL("http://www.root-motion.com/finalikdox/html/page3.html");
}
// Open the Script Reference URL
[ContextMenu("Scrpt Reference")]
void OpenScriptReference()
{
Application.OpenURL("http://www.root-motion.com/finalikdox/html/class_root_motion_1_1_baker.html");
}
// Link to the Final IK Google Group
[ContextMenu("Support Group")]
void SupportGroup()
{
Application.OpenURL("https://groups.google.com/forum/#!forum/final-ik");
}
// Link to the Final IK Asset Store thread in the Unity Community
[ContextMenu("Asset Store Thread")]
void ASThread()
{
Application.OpenURL("http://forum.unity3d.com/threads/final-ik-full-body-ik-aim-look-at-fabrik-ccd-ik-1-0-released.222685/");
}
[System.Serializable]
public enum Mode
{
AnimationClips = 0,
AnimationStates = 1,
PlayableDirector = 2,
Realtime = 3
}
/// <summary>
/// In AnimationClips, AnimationStates or PlayableDirector mode - the frame rate at which the animation clip will be sampled. In Realtime mode - the frame rate at which the pose will be sampled. With the latter, the frame rate is not guaranteed if the player is not able to reach it.
/// </summary>
[Tooltip("In AnimationClips, AnimationStates or PlayableDirector mode - the frame rate at which the animation clip will be sampled. In Realtime mode - the frame rate at which the pose will be sampled. With the latter, the frame rate is not guaranteed if the player is not able to reach it.")]
[Range(1, 90)] public int frameRate = 30;
/// <summary>
/// Maximum allowed error for keyframe reduction.
/// </summary>
[Tooltip("Maximum allowed error for keyframe reduction.")]
[Range(0f, 0.1f)] public float keyReductionError = 0.01f;
/// <summary>
/// AnimationClips mode can be used to bake a batch of AnimationClips directly without the need of setting up an AnimatorController. AnimationStates mode is useful for when you need to set up a more complex rig with layers and AvatarMasks in Mecanim. PlayableDirector mode bakes a Timeline. Realtime mode is for continuous baking of gameplay, ragdoll phsysics or PuppetMaster dynamics.
/// </summary>
[Tooltip("AnimationClips mode can be used to bake a batch of AnimationClips directly without the need of setting up an AnimatorController. " +
"AnimationStates mode is useful for when you need to set up a more complex rig with layers and AvatarMasks in Mecanim. " +
"PlayableDirector mode bakes a Timeline. " +
"Realtime mode is for continuous baking of gameplay, ragdoll phsysics or PuppetMaster dynamics.")]
public Mode mode;
/// <summary>
/// AnimationClips to bake.
/// </summary>
[Tooltip("AnimationClips to bake.")]
public AnimationClip[] animationClips = new AnimationClip[0];
/// <summary>
/// The name of the AnimationStates to bake (must be on the base layer) in the Animator (Right-click on this component header and select 'Find Animation States' to have Baker fill those in automatically, required that state names match with the names of the clips used in them).
/// </summary>
[Tooltip("The name of the AnimationStates to bake (must be on the base layer) in the Animator above (Right-click on this component header and select 'Find Animation States' to have Baker fill those in automatically, required that state names match with the names of the clips used in them).")]
public string[] animationStates = new string[0];
/// <summary>
/// The folder to save the baked AnimationClips to.
/// </summary>
[Tooltip("The folder to save the baked AnimationClips to.")]
public string saveToFolder = "Assets";
/// <summary>
/// String that will be added to each clip or animation state name for the saved clip. For example if your animation state/clip names were 'Idle' and 'Walk', then with '_Baked' as Append Name, the Baker will create 'Idle_Baked' and 'Walk_Baked' animation clips.
/// </summary>
[Tooltip("String that will be added to each clip or animation state name for the saved clip. For example if your animation state/clip names were 'Idle' and 'Walk', then with '_Baked' as Append Name, the Baker will create 'Idle_Baked' and 'Walk_Baked' animation clips.")]
public string appendName = "_Baked";
/// <summary>
/// Name of the created AnimationClip file.
/// </summary>
[Tooltip("Name of the created AnimationClip file.")]
public string saveName = "Baked Clip";
public bool isBaking { get; private set; }
public float bakingProgress { get; private set; }
[HideInInspector] public Animator animator;
[HideInInspector] public PlayableDirector director;
public delegate void BakerDelegate(AnimationClip clip, float time);
public BakerDelegate OnStartClip;
public BakerDelegate OnUpdateClip;
[System.Serializable]
public class ClipSettings
{
[System.Serializable]
public enum BasedUponRotation
{
Original = 0,
BodyOrientation = 1,
}
[System.Serializable]
public enum BasedUponY
{
Original = 0,
CenterOfMass = 1,
Feet = 2,
}
[System.Serializable]
public enum BasedUponXZ
{
Original = 0,
CenterOfMass = 1,
}
public bool loopTime;
public bool loopBlend;
public float cycleOffset;
public bool loopBlendOrientation;
public BasedUponRotation basedUponRotation;
public float orientationOffsetY;
public bool loopBlendPositionY;
public BasedUponY basedUponY;
public float level;
public bool loopBlendPositionXZ;
public BasedUponXZ basedUponXZ;
public bool mirror;
#if UNITY_EDITOR
public void ApplyTo(AnimationClipSettings settings)
{
settings.loopTime = loopTime;
settings.loopBlend = loopBlend;
settings.cycleOffset = cycleOffset;
settings.loopBlendOrientation = loopBlendOrientation;
settings.keepOriginalOrientation = basedUponRotation == BasedUponRotation.Original;
settings.orientationOffsetY = orientationOffsetY;
settings.loopBlendPositionY = loopBlendPositionY;
settings.keepOriginalPositionY = basedUponY == BasedUponY.Original;
settings.heightFromFeet = basedUponY == BasedUponY.Feet;
settings.level = level;
settings.loopBlendPositionXZ = loopBlendPositionXZ;
settings.keepOriginalPositionXZ = basedUponXZ == BasedUponXZ.Original;
settings.mirror = mirror;
}
#endif
}
/// <summary>
/// If enabled, baked clips will have the same AnimationClipSettings as the clips used for baking. If disabled, clip settings from below will be applied to all the baked clips.
/// </summary>
[Tooltip("If enabled, baked clips will have the same AnimationClipSettings as the clips used for baking. If disabled, clip settings from below will be applied to all the baked clips.")] public bool inheritClipSettings;
/// <summary>
/// AnimationClipSettings applied to the baked animation clip.
/// </summary>
[Tooltip("AnimationClipSettings applied to the baked animation clip.")] public ClipSettings clipSettings;
protected abstract Transform GetCharacterRoot();
protected abstract void OnStartBaking();
protected abstract void OnSetLoopFrame(float time);
protected abstract void OnSetCurves(ref AnimationClip clip);
protected abstract void OnSetKeyframes(float time, bool lastFrame);
protected float clipLength { get; private set; }
protected bool addLoopFrame;
#if UNITY_EDITOR
private AnimationClip[] bakedClips = new AnimationClip[0];
private AnimatorStateInfo currentClipStateInfo;
private int currentClipIndex;
private float startBakingTime;
private float nextKeyframeTime;
private bool firstFrame;
private int clipFrames;
private int clipFrameNo;
private bool setKeyframes;
private float currentClipTime;
private float clipFrameInterval;
#endif
// Start baking an animation state, clip or timeline, also called for each next clip in the baking array
public void BakeClip()
{
#if UNITY_EDITOR
if (mode == Mode.AnimationClips && inheritClipSettings)
{
AnimationClipSettings originalSettings = AnimationUtility.GetAnimationClipSettings(animationClips[currentClipIndex]);
addLoopFrame = originalSettings.loopTime;
}
else
{
if (mode == Mode.Realtime) addLoopFrame = clipSettings.loopTime && clipSettings.loopBlend;
else addLoopFrame = clipSettings.loopTime;
}
StartBaking();
#endif
}
public void StartBaking()
{
#if UNITY_EDITOR
isBaking = true;
nextKeyframeTime = 0f;
bakingProgress = 0f;
clipFrameNo = 0;
if (bakedClips.Length == 0)
{
switch (mode)
{
case Mode.AnimationClips:
bakedClips = new AnimationClip[animationClips.Length];
break;
case Mode.AnimationStates:
bakedClips = new AnimationClip[animationStates.Length];
break;
default:
bakedClips = new AnimationClip[1];
break;
}
}
OnStartBaking();
firstFrame = true;
#endif
}
public void StopBaking()
{
#if UNITY_EDITOR
if (!isBaking) return;
if (addLoopFrame)
{
OnSetLoopFrame(clipLength);
}
bakedClips[currentClipIndex] = new AnimationClip();
bakedClips[currentClipIndex].frameRate = frameRate;
OnSetCurves(ref bakedClips[currentClipIndex]);
bakedClips[currentClipIndex].EnsureQuaternionContinuity();
if (mode == Mode.Realtime)
{
isBaking = false;
SaveClips();
}
#endif
}
#if UNITY_EDITOR
[ContextMenu("Find Animation States")]
public void FindAnimationStates()
{
animator = GetComponent<Animator>();
if (animator == null)
{
Debug.LogError("No Animator found on Baker GameObject. Can not find animation states.");
return;
}
if (animator.runtimeAnimatorController == null)
{
Debug.LogError("Animator does not have a valid Controller. Can not find animation states.");
return;
}
var clips = animator.runtimeAnimatorController.animationClips;
animationStates = new string[clips.Length];
for (int i = 0; i < clips.Length; i++)
{
animationStates[i] = clips[i].name;
}
}
void Update()
{
// Baking procedure
if (isBaking)
{
if (mode != Mode.Realtime)
{
if (firstFrame)
{
transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
GetCharacterRoot().position = Vector3.zero;
GetCharacterRoot().rotation = Quaternion.identity;
StartAnimationUpdate();
currentClipTime = 0f;
firstFrame = false;
}
switch (mode)
{
case Mode.AnimationClips:
clipLength = animationClips[currentClipIndex].length;
bakingProgress = currentClipTime / clipLength;
break;
case Mode.AnimationStates:
currentClipStateInfo = animator.GetCurrentAnimatorStateInfo(0);
bakingProgress = currentClipStateInfo.normalizedTime;
if (currentClipStateInfo.speed <= 0f)
{
Debug.LogError("Baker can not bake a clip with 0 speed.");
return;
}
clipLength = currentClipStateInfo.length / currentClipStateInfo.speed / animator.speed;
break;
case Mode.PlayableDirector:
clipLength = (float)director.duration;
bakingProgress = (float)director.time / clipLength;
break;
}
clipFrames = (int)(clipLength * (frameRate));
clipFrameInterval = clipLength / (float)(clipFrames);
setKeyframes = true;
// Stop clip baking if the clip is finished, start baking the next clip if possible
if (clipFrameNo > clipFrames)
{
StopBaking();
currentClipIndex++;
if (currentClipIndex >= bakedClips.Length)
{
currentClipIndex = 0;
StopAnimationUpdate();
isBaking = false;
SaveClips();
return;
}
else
{
BakeClip();
setKeyframes = false;
}
}
if (!firstFrame) AnimationUpdate();
}
}
}
void LateUpdate()
{
if (!isBaking) return;
if (mode != Mode.Realtime)
{
if (setKeyframes)
{
OnSetKeyframes(clipFrameNo * clipFrameInterval, clipFrameNo >= clipFrames);
clipFrameNo++;
setKeyframes = false;
}
}
else
{
if (firstFrame)
{
startBakingTime = Time.time;
firstFrame = false;
}
if (Time.time < nextKeyframeTime) return;
OnSetKeyframes(Time.time - startBakingTime, false);
nextKeyframeTime = Time.time + (1f / (float)frameRate);
}
}
private void StartAnimationUpdate()
{
switch (mode)
{
case Mode.AnimationClips:
if (!AnimationMode.InAnimationMode()) AnimationMode.StartAnimationMode();
AnimationMode.BeginSampling();
AnimationMode.SampleAnimationClip(gameObject, animationClips[currentClipIndex], 0f);
AnimationMode.EndSampling();
if (OnStartClip != null) OnStartClip(animationClips[currentClipIndex], 0f);
break;
case Mode.AnimationStates:
animator.enabled = false;
animator.Play(animationStates[currentClipIndex], 0, 0f);
break;
case Mode.PlayableDirector:
director.enabled = false;
director.time = 0f;
director.Evaluate();
break;
}
}
private void StopAnimationUpdate()
{
switch (mode)
{
case Mode.AnimationClips:
if (AnimationMode.InAnimationMode()) AnimationMode.StopAnimationMode();
break;
case Mode.AnimationStates:
animator.enabled = true;
break;
case Mode.PlayableDirector:
director.enabled = true;
break;
}
}
private void AnimationUpdate()
{
switch (mode)
{
case Mode.AnimationClips:
if (!AnimationMode.InAnimationMode()) AnimationMode.StartAnimationMode();
AnimationMode.BeginSampling();
AnimationMode.SampleAnimationClip(gameObject, animationClips[currentClipIndex], currentClipTime);
AnimationMode.EndSampling();
if (OnUpdateClip != null) OnUpdateClip(animationClips[currentClipIndex], currentClipTime / animationClips[currentClipIndex].length);
currentClipTime += clipFrameInterval;
break;
case Mode.AnimationStates:
animator.Update(clipFrameInterval);
break;
case Mode.PlayableDirector:
director.time = currentClipTime;
director.Evaluate();
currentClipTime += clipFrameInterval;
break;
}
}
public void SaveClips()
{
var clips = GetBakedClips();
AnimationClip savedClip = null;
for (int i = 0; i < clips.Length; i++)
{
string path = GetFullPath(i);
if (mode == Mode.AnimationClips && inheritClipSettings)
{
AnimationClipSettings inheritedSettings = AnimationUtility.GetAnimationClipSettings(animationClips[i]);
SetClipSettings(clips[i], inheritedSettings);
AnimationUtility.SetAnimationClipSettings(clips[i], inheritedSettings);
}
else
{
AnimationClipSettings settings = AnimationUtility.GetAnimationClipSettings(clips[i]);
clipSettings.ApplyTo(settings);
SetClipSettings(clips[i], settings);
AnimationUtility.SetAnimationClipSettings(clips[i], settings);
}
var existing = AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip)) as AnimationClip;
if (existing != null)
{
// Overwrite
EditorUtility.CopySerialized(clips[i], existing);
}
else
{
// Create new asset
AssetDatabase.CreateAsset(clips[i], path);
}
/* v2.0
switch (mode)
{
case Baker.Mode.Realtime: break;
case Baker.Mode.AnimationClips:
AnimationClipSettings originalSettings = AnimationUtility.GetAnimationClipSettings(animationClips[i]);
SetClipSettings(clips[i], originalSettings);
AnimationUtility.SetAnimationClipSettings(clips[i], originalSettings);
break;
default:
AnimationClipSettings settings = AnimationUtility.GetAnimationClipSettings(clips[i]);
settings.loopTime = loop;
SetClipSettings(clips[i], settings);
AnimationUtility.SetAnimationClipSettings(clips[i], settings);
break;
}
var existing = AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip)) as AnimationClip;
if (existing != null)
{
// Overwrite with existing settings
AnimationClipSettings existingSettings = AnimationUtility.GetAnimationClipSettings(existing);
existingSettings.stopTime = clips[i].length;
AnimationUtility.SetAnimationClipSettings(clips[i], existingSettings);
EditorUtility.CopySerialized(clips[i], existing);
}
else
{
// Create new asset
AssetDatabase.CreateAsset(clips[i], path);
}
*/
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
savedClip = AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip)) as AnimationClip;
Debug.Log(path + " successfully baked.");
}
Selection.activeObject = savedClip;
ClearBakedClips();
}
protected virtual void SetClipSettings(AnimationClip clip, AnimationClipSettings settings) { }
private string GetFullPath(int clipIndex)
{
switch (mode)
{
case Baker.Mode.AnimationClips:
return saveToFolder + "/" + animationClips[clipIndex].name + appendName + ".anim";
case Baker.Mode.AnimationStates:
return saveToFolder + "/" + animationStates[clipIndex] + appendName + ".anim";
case Baker.Mode.PlayableDirector:
return saveToFolder + "/" + saveName + ".anim";
default:
return saveToFolder + "/" + saveName + ".anim";
}
}
private AnimationClip[] GetBakedClips()
{
return bakedClips;
}
private void ClearBakedClips()
{
bakedClips = new AnimationClip[0];
}
#endif
}
}

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

@ -0,0 +1,134 @@
using UnityEngine;
using System.Collections;
using System;
namespace RootMotion
{
/// <summary>
/// Baker for Generic/Legacy animation.
/// </summary>
public class GenericBaker : Baker
{
/// <summary>
/// If true, produced AnimationClips will be marked as Legacy and usable with the Legacy animation system.
/// </summary>
[Tooltip("If true, produced AnimationClips will be marked as Legacy and usable with the Legacy animation system.")]
public bool markAsLegacy;
/// <summary>
/// Root Transform of the hierarchy to bake.
/// </summary>
[Tooltip("Root Transform of the hierarchy to bake.")]
public Transform root;
/// <summary>
/// Root Node used for root motion.
/// </summary>
[Tooltip("Root Node used for root motion.")]
public Transform rootNode;
/// <summary>
/// List of Transforms to ignore, rotation curves will not be baked for these Transforms.
/// </summary>
[Tooltip("List of Transforms to ignore, rotation curves will not be baked for these Transforms.")]
public Transform[] ignoreList;
/// <summary>
/// LocalPosition curves will be baked for these Transforms only. If you are baking a character, the pelvis bone should be added to this array.
/// </summary>
[Tooltip("LocalPosition curves will be baked for these Transforms only. If you are baking a character, the pelvis bone should be added to this array.")]
public Transform[] bakePositionList;
private BakerTransform[] children = new BakerTransform[0];
private BakerTransform rootChild;
private int rootChildIndex = -1;
void Awake()
{
// Find all the child Transforms of the Animator
Transform[] childrenAndRoot = (Transform[])root.GetComponentsInChildren<Transform>();
children = new BakerTransform[0];
// Exlude the ignore list, construct the children array
for (int i = 0; i < childrenAndRoot.Length; i++)
{
if (!IsIgnored(childrenAndRoot[i]))
{
Array.Resize(ref children, children.Length + 1);
bool isRootNode = childrenAndRoot[i] == rootNode;
if (isRootNode) rootChildIndex = children.Length - 1;
children[children.Length - 1] = new BakerTransform(childrenAndRoot[i], root, BakePosition(childrenAndRoot[i]), isRootNode);
}
}
}
protected override Transform GetCharacterRoot()
{
return root;
}
protected override void OnStartBaking()
{
for (int i = 0; i < children.Length; i++)
{
children[i].Reset();
if (i == rootChildIndex) children[i].SetRelativeSpace(root.position, root.rotation);
}
}
protected override void OnSetLoopFrame(float time)
{
// TODO Change to SetLoopFrame like in HumanoidBaker
for (int i = 0; i < children.Length; i++) children[i].AddLoopFrame(time);
}
protected override void OnSetCurves(ref AnimationClip clip)
{
// TODO Length Multiplier
for (int i = 0; i < children.Length; i++) children[i].SetCurves(ref clip);
}
protected override void OnSetKeyframes(float time, bool lastFrame)
{
for (int i = 0; i < children.Length; i++) children[i].SetKeyframes(time);
}
// Is the specified Transform in the ignore list?
private bool IsIgnored(Transform t)
{
for (int i = 0; i < ignoreList.Length; i++)
{
if (t == ignoreList[i]) return true;
}
return false;
}
// Should we record the localPosition channels of the Transform?
private bool BakePosition(Transform t)
{
for (int i = 0; i < bakePositionList.Length; i++)
{
if (t == bakePositionList[i]) return true;
}
return false;
}
#if UNITY_EDITOR
protected override void SetClipSettings(AnimationClip clip, UnityEditor.AnimationClipSettings settings)
{
clip.legacy = markAsLegacy;
if (mode != Baker.Mode.AnimationClips)
{
clip.wrapMode = settings.loopTime ? WrapMode.Loop : WrapMode.Default;
}
}
#endif
}
}

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

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

@ -0,0 +1,128 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Reflection;
namespace RootMotion
{
public class TQ
{
public TQ() { }
public TQ(Vector3 translation, Quaternion rotation)
{
t = translation;
q = rotation;
}
public Vector3 t;
public Quaternion q;
}
/*
Written with the kind help of the one commonly known as Mecanim-Dev.
*/
public class AvatarUtility
{
public static Quaternion GetPostRotation(Avatar avatar, AvatarIKGoal avatarIKGoal)
{
int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
return (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
}
/// <summary>
/// Get IK position and rotation for foot/hand bone position/rotation.
/// </summary>
public static TQ GetIKGoalTQ(Avatar avatar, float humanScale, AvatarIKGoal avatarIKGoal, TQ bodyPositionRotation, TQ boneTQ)
{
int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
MethodInfo methodGetAxisLength = typeof(Avatar).GetMethod("GetAxisLength", BindingFlags.Instance | BindingFlags.NonPublic);
if (methodGetAxisLength == null) throw new InvalidOperationException("Cannot find GetAxisLength method.");
MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
Quaternion postRotation = (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
var goalTQ = new TQ(boneTQ.t, boneTQ.q * postRotation);
if (avatarIKGoal == AvatarIKGoal.LeftFoot || avatarIKGoal == AvatarIKGoal.RightFoot)
{
// Here you could use animator.leftFeetBottomHeight or animator.rightFeetBottomHeight rather than GetAxisLenght
// Both are equivalent but GetAxisLength is the generic way and work for all human bone
float axislength = (float)methodGetAxisLength.Invoke(avatar, new object[] { humanId });
Vector3 footBottom = new Vector3(axislength, 0, 0);
goalTQ.t += (goalTQ.q * footBottom);
}
// IK goal are in avatar body local space
Quaternion invRootQ = Quaternion.Inverse(bodyPositionRotation.q);
goalTQ.t = invRootQ * (goalTQ.t - bodyPositionRotation.t);
goalTQ.q = invRootQ * goalTQ.q;
goalTQ.t /= humanScale;
goalTQ.q = Quaternion.LookRotation(goalTQ.q * Vector3.forward, goalTQ.q * Vector3.up);
return goalTQ;
}
public static TQ WorldSpaceIKGoalToBone(TQ goalTQ, Avatar avatar, AvatarIKGoal avatarIKGoal)
{
int humanId = (int)HumanIDFromAvatarIKGoal(avatarIKGoal);
if (humanId == (int)HumanBodyBones.LastBone) throw new InvalidOperationException("Invalid human id.");
MethodInfo methodGetAxisLength = typeof(Avatar).GetMethod("GetAxisLength", BindingFlags.Instance | BindingFlags.NonPublic);
if (methodGetAxisLength == null) throw new InvalidOperationException("Cannot find GetAxisLength method.");
MethodInfo methodGetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.Instance | BindingFlags.NonPublic);
if (methodGetPostRotation == null) throw new InvalidOperationException("Cannot find GetPostRotation method.");
Quaternion postRotation = (Quaternion)methodGetPostRotation.Invoke(avatar, new object[] { humanId });
if (avatarIKGoal == AvatarIKGoal.LeftFoot || avatarIKGoal == AvatarIKGoal.RightFoot)
{
// Here you could use animator.leftFeetBottomHeight or animator.rightFeetBottomHeight rather than GetAxisLenght
// Both are equivalent but GetAxisLength is the generic way and work for all human bone
float axislength = (float)methodGetAxisLength.Invoke(avatar, new object[] { humanId });
Vector3 footBottom = new Vector3(axislength, 0, 0);
goalTQ.t -= (goalTQ.q * footBottom);
}
TQ boneTQ = new TQ(goalTQ.t, goalTQ.q * Quaternion.Inverse(postRotation));
return boneTQ;
}
public static TQ GetWorldSpaceIKGoal(BakerHumanoidQT ikQT, BakerHumanoidQT rootQT, float time, float humanScale)
{
var tq = ikQT.Evaluate(time);
var rTQ = rootQT.Evaluate(time);
tq.q = rTQ.q * tq.q;
tq.t = rTQ.t + rTQ.q * tq.t;
tq.t *= humanScale;
return tq;
}
public static HumanBodyBones HumanIDFromAvatarIKGoal(AvatarIKGoal avatarIKGoal)
{
switch (avatarIKGoal)
{
case AvatarIKGoal.LeftFoot: return HumanBodyBones.LeftFoot;
case AvatarIKGoal.RightFoot: return HumanBodyBones.RightFoot;
case AvatarIKGoal.LeftHand: return HumanBodyBones.LeftHand;
case AvatarIKGoal.RightHand: return HumanBodyBones.RightHand;
default: return HumanBodyBones.LastBone;
}
}
}
}

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

@ -0,0 +1,642 @@
using UnityEngine;
using System.Collections;
namespace RootMotion
{
public static class BakerUtilities
{
public static void ReduceKeyframes(AnimationCurve curve, float maxError)
{
if (maxError <= 0f) return;
curve.keys = GetReducedKeyframes(curve, maxError);
// TODO Flatten outTangent for keys that have the next key and testAfter sampled to the same value in the original clip. Same thing for the inTangent
}
public static Keyframe[] GetReducedKeyframes(AnimationCurve curve, float maxError)
{
Keyframe[] keys = curve.keys;
int i = 1;
while (i < keys.Length - 1 && keys.Length > 2)
{
Keyframe[] testKeys = new Keyframe[keys.Length - 1];
int c = 0;
for (int n = 0; n < keys.Length; n++)
{
if (i != n)
{
testKeys[c] = new Keyframe(keys[n].time, keys[n].value, keys[n].inTangent, keys[n].outTangent);
c++;
}
}
AnimationCurve testCurve = new AnimationCurve();
testCurve.keys = testKeys;
float test0 = Mathf.Abs(testCurve.Evaluate(keys[i].time) - keys[i].value);
float beforeTime = keys[i].time + (keys[i - 1].time - keys[i].time) * 0.5f;
float afterTime = keys[i].time + (keys[i + 1].time - keys[i].time) * 0.5f;
float testBefore = Mathf.Abs(testCurve.Evaluate(beforeTime) - curve.Evaluate(beforeTime));
float testAfter = Mathf.Abs(testCurve.Evaluate(afterTime) - curve.Evaluate(afterTime));
if (test0 < maxError && testBefore < maxError && testAfter < maxError)
{
keys = testKeys;
}
else
{
i++;
}
}
return keys;
}
public static void SetLoopFrame(float time, AnimationCurve curve)
{
/*
Keyframe[] keys = curve.keys;
keys[keys.Length - 1].value = keys[0].value;
keys[keys.Length - 1].inTangent = keys[0].inTangent;
keys[keys.Length - 1].outTangent = keys[0].outTangent;
keys[keys.Length - 1].time = time;
curve.keys = keys;
*/
Keyframe[] keys = curve.keys;
keys[keys.Length - 1].value = keys[0].value;
float inTangent = Mathf.Lerp(keys[0].inTangent, keys[keys.Length - 1].inTangent, 0.5f);
keys[0].inTangent = inTangent;
keys[keys.Length - 1].inTangent = inTangent;
float outTangent = Mathf.Lerp(keys[0].outTangent, keys[keys.Length - 1].outTangent, 0.5f);
keys[0].outTangent = outTangent;
keys[keys.Length - 1].outTangent = outTangent;
keys[keys.Length - 1].time = time;
curve.keys = keys;
}
public static void SetTangentMode(AnimationCurve curve)
{
#if UNITY_EDITOR
if (curve.length < 2) return;
for (int i = 1; i < curve.length - 1; i++)
{
UnityEditor.AnimationUtility.SetKeyLeftTangentMode(curve, i, UnityEditor.AnimationUtility.TangentMode.ClampedAuto);
UnityEditor.AnimationUtility.SetKeyRightTangentMode(curve, i, UnityEditor.AnimationUtility.TangentMode.ClampedAuto);
}
#endif
}
// Realigns quaternion keys to ensure shortest interpolation paths.
public static Quaternion EnsureQuaternionContinuity(Quaternion lastQ, Quaternion q)
{
Quaternion flipped = new Quaternion(-q.x, -q.y, -q.z, -q.w);
Quaternion midQ = new Quaternion(
Mathf.Lerp(lastQ.x, q.x, 0.5f),
Mathf.Lerp(lastQ.y, q.y, 0.5f),
Mathf.Lerp(lastQ.z, q.z, 0.5f),
Mathf.Lerp(lastQ.w, q.w, 0.5f)
);
Quaternion midQFlipped = new Quaternion(
Mathf.Lerp(lastQ.x, flipped.x, 0.5f),
Mathf.Lerp(lastQ.y, flipped.y, 0.5f),
Mathf.Lerp(lastQ.z, flipped.z, 0.5f),
Mathf.Lerp(lastQ.w, flipped.w, 0.5f)
);
float angle = Quaternion.Angle(lastQ, midQ);
float angleFlipped = Quaternion.Angle(lastQ, midQFlipped);
return angleFlipped < angle ? flipped : q;
}
}
//Manages the Animation Curves for Humanoid Q/T channels.
[System.Serializable]
public class BakerHumanoidQT
{
private Transform transform;
private string Qx, Qy, Qz, Qw;
private string Tx, Ty, Tz;
// Animation curves for each channel of the Transform
public AnimationCurve rotX, rotY, rotZ, rotW;
public AnimationCurve posX, posY, posZ;
private AvatarIKGoal goal;
private Quaternion lastQ;
private bool lastQSet;
// The custom constructor
public BakerHumanoidQT(string name)
{
Qx = name + "Q.x";
Qy = name + "Q.y";
Qz = name + "Q.z";
Qw = name + "Q.w";
Tx = name + "T.x";
Ty = name + "T.y";
Tz = name + "T.z";
Reset();
}
public BakerHumanoidQT(Transform transform, AvatarIKGoal goal, string name)
{
this.transform = transform;
this.goal = goal;
Qx = name + "Q.x";
Qy = name + "Q.y";
Qz = name + "Q.z";
Qw = name + "Q.w";
Tx = name + "T.x";
Ty = name + "T.y";
Tz = name + "T.z";
Reset();
}
public Quaternion EvaluateRotation(float time)
{
Quaternion q = new Quaternion(rotX.Evaluate(time), rotY.Evaluate(time), rotZ.Evaluate(time), rotW.Evaluate(time));
return q;
//return q.normalized;
}
public Vector3 EvaluatePosition(float time)
{
return new Vector3(posX.Evaluate(time), posY.Evaluate(time), posZ.Evaluate(time));
}
public TQ Evaluate(float time)
{
return new TQ(EvaluatePosition(time), EvaluateRotation(time));
}
public void GetCurvesFromClip(AnimationClip clip, Animator animator)
{
#if UNITY_EDITOR
rotX = GetEditorCurve(clip, Qx);
rotY = GetEditorCurve(clip, Qy);
rotZ = GetEditorCurve(clip, Qz);
rotW = GetEditorCurve(clip, Qw);
posX = GetEditorCurve(clip, Tx);
posY = GetEditorCurve(clip, Ty);
posZ = GetEditorCurve(clip, Tz);
#endif
}
#if UNITY_EDITOR
private AnimationCurve GetEditorCurve(AnimationClip clip, string propertyPath)
{
var binding = UnityEditor.EditorCurveBinding.FloatCurve(string.Empty, typeof(Animator), propertyPath);
return UnityEditor.AnimationUtility.GetEditorCurve(clip, binding);
}
#endif
// Clear all curves
public void Reset()
{
rotX = new AnimationCurve();
rotY = new AnimationCurve();
rotZ = new AnimationCurve();
rotW = new AnimationCurve();
posX = new AnimationCurve();
posY = new AnimationCurve();
posZ = new AnimationCurve();
lastQ = Quaternion.identity;
lastQSet = false;
}
public void SetIKKeyframes(float time, Avatar avatar, Transform root, float humanScale, Vector3 bodyPosition, Quaternion bodyRotation)
{
Vector3 bonePos = transform.position;
Quaternion boneRot = transform.rotation;
if (root.parent != null)
{
bonePos = root.parent.InverseTransformPoint(bonePos);
boneRot = Quaternion.Inverse(root.parent.rotation) * boneRot;
}
TQ IKTQ = AvatarUtility.GetIKGoalTQ(avatar, humanScale, goal, new TQ(bodyPosition, bodyRotation), new TQ(bonePos, boneRot));
Quaternion rot = IKTQ.q;
if (lastQSet) rot = BakerUtilities.EnsureQuaternionContinuity(lastQ, IKTQ.q);
//rot.Normalize();
lastQ = rot;
lastQSet = true;
rotX.AddKey(time, rot.x);
rotY.AddKey(time, rot.y);
rotZ.AddKey(time, rot.z);
rotW.AddKey(time, rot.w);
Vector3 pos = IKTQ.t;
posX.AddKey(time, pos.x);
posY.AddKey(time, pos.y);
posZ.AddKey(time, pos.z);
}
public void SetKeyframes(float time, Vector3 pos, Quaternion rot)
{
// Rotation flipping already prevented in HumanoidBaker.UpdateHumanPose().
rotX.AddKey(time, rot.x);
rotY.AddKey(time, rot.y);
rotZ.AddKey(time, rot.z);
rotW.AddKey(time, rot.w);
posX.AddKey(time, pos.x);
posY.AddKey(time, pos.y);
posZ.AddKey(time, pos.z);
}
public void MoveLastKeyframes(float time)
{
MoveLastKeyframe(time, rotX);
MoveLastKeyframe(time, rotY);
MoveLastKeyframe(time, rotZ);
MoveLastKeyframe(time, rotW);
MoveLastKeyframe(time, posX);
MoveLastKeyframe(time, posY);
MoveLastKeyframe(time, posZ);
}
// Add a copy of the first frame to the specified time
public void SetLoopFrame(float time)
{
BakerUtilities.SetLoopFrame(time, rotX);
BakerUtilities.SetLoopFrame(time, rotY);
BakerUtilities.SetLoopFrame(time, rotZ);
BakerUtilities.SetLoopFrame(time, rotW);
BakerUtilities.SetLoopFrame(time, posX);
BakerUtilities.SetLoopFrame(time, posY);
BakerUtilities.SetLoopFrame(time, posZ);
}
public void SetRootLoopFrame(float time)
{
/*
// TODO Should be based on AnimationClipSettings
BakerUtilities.SetLoopFrame(time, rotX);
BakerUtilities.SetLoopFrame(time, rotY);
BakerUtilities.SetLoopFrame(time, rotZ);
BakerUtilities.SetLoopFrame(time, rotW);
BakerUtilities.SetLoopFrame(time, posY);
*/
}
private void MoveLastKeyframe(float time, AnimationCurve curve)
{
Keyframe[] keys = curve.keys;
keys[keys.Length - 1].time = time;
curve.keys = keys;
}
public void MultiplyLength(AnimationCurve curve, float mlp)
{
Keyframe[] keys = curve.keys;
for (int i = 0; i < keys.Length; i++)
{
keys[i].time *= mlp;
}
curve.keys = keys;
}
// Add curves to the AnimationClip for each channel
public void SetCurves(ref AnimationClip clip, float maxError, float lengthMlp)
{
MultiplyLength(rotX, lengthMlp);
MultiplyLength(rotY, lengthMlp);
MultiplyLength(rotZ, lengthMlp);
MultiplyLength(rotW, lengthMlp);
MultiplyLength(posX, lengthMlp);
MultiplyLength(posY, lengthMlp);
MultiplyLength(posZ, lengthMlp);
BakerUtilities.ReduceKeyframes(rotX, maxError);
BakerUtilities.ReduceKeyframes(rotY, maxError);
BakerUtilities.ReduceKeyframes(rotZ, maxError);
BakerUtilities.ReduceKeyframes(rotW, maxError);
BakerUtilities.ReduceKeyframes(posX, maxError);
BakerUtilities.ReduceKeyframes(posY, maxError);
BakerUtilities.ReduceKeyframes(posZ, maxError);
BakerUtilities.SetTangentMode(rotX);
BakerUtilities.SetTangentMode(rotY);
BakerUtilities.SetTangentMode(rotZ);
BakerUtilities.SetTangentMode(rotW);
/*
BakerUtilities.SetTangentMode(posX);
BakerUtilities.SetTangentMode(posY);
BakerUtilities.SetTangentMode(posZ);
*/
clip.SetCurve(string.Empty, typeof(Animator), Qx, rotX);
clip.SetCurve(string.Empty, typeof(Animator), Qy, rotY);
clip.SetCurve(string.Empty, typeof(Animator), Qz, rotZ);
clip.SetCurve(string.Empty, typeof(Animator), Qw, rotW);
clip.SetCurve(string.Empty, typeof(Animator), Tx, posX);
clip.SetCurve(string.Empty, typeof(Animator), Ty, posY);
clip.SetCurve(string.Empty, typeof(Animator), Tz, posZ);
}
}
// Manages the Animation Curves for a single Transform that is a child of the root Transform.
[System.Serializable]
public class BakerMuscle
{
// Animation curves for each channel of the Transform
public AnimationCurve curve;
private int muscleIndex = -1;
private string propertyName;
// The custom constructor
public BakerMuscle(int muscleIndex)
{
this.muscleIndex = muscleIndex;
this.propertyName = MuscleNameToPropertyName(HumanTrait.MuscleName[muscleIndex]);
Reset();
}
private string MuscleNameToPropertyName(string n)
{
// Left fingers
if (n == "Left Index 1 Stretched") return "LeftHand.Index.1 Stretched";
if (n == "Left Index 2 Stretched") return "LeftHand.Index.2 Stretched";
if (n == "Left Index 3 Stretched") return "LeftHand.Index.3 Stretched";
if (n == "Left Middle 1 Stretched") return "LeftHand.Middle.1 Stretched";
if (n == "Left Middle 2 Stretched") return "LeftHand.Middle.2 Stretched";
if (n == "Left Middle 3 Stretched") return "LeftHand.Middle.3 Stretched";
if (n == "Left Ring 1 Stretched") return "LeftHand.Ring.1 Stretched";
if (n == "Left Ring 2 Stretched") return "LeftHand.Ring.2 Stretched";
if (n == "Left Ring 3 Stretched") return "LeftHand.Ring.3 Stretched";
if (n == "Left Little 1 Stretched") return "LeftHand.Little.1 Stretched";
if (n == "Left Little 2 Stretched") return "LeftHand.Little.2 Stretched";
if (n == "Left Little 3 Stretched") return "LeftHand.Little.3 Stretched";
if (n == "Left Thumb 1 Stretched") return "LeftHand.Thumb.1 Stretched";
if (n == "Left Thumb 2 Stretched") return "LeftHand.Thumb.2 Stretched";
if (n == "Left Thumb 3 Stretched") return "LeftHand.Thumb.3 Stretched";
if (n == "Left Index Spread") return "LeftHand.Index.Spread";
if (n == "Left Middle Spread") return "LeftHand.Middle.Spread";
if (n == "Left Ring Spread") return "LeftHand.Ring.Spread";
if (n == "Left Little Spread") return "LeftHand.Little.Spread";
if (n == "Left Thumb Spread") return "LeftHand.Thumb.Spread";
// Right fingers
if (n == "Right Index 1 Stretched") return "RightHand.Index.1 Stretched";
if (n == "Right Index 2 Stretched") return "RightHand.Index.2 Stretched";
if (n == "Right Index 3 Stretched") return "RightHand.Index.3 Stretched";
if (n == "Right Middle 1 Stretched") return "RightHand.Middle.1 Stretched";
if (n == "Right Middle 2 Stretched") return "RightHand.Middle.2 Stretched";
if (n == "Right Middle 3 Stretched") return "RightHand.Middle.3 Stretched";
if (n == "Right Ring 1 Stretched") return "RightHand.Ring.1 Stretched";
if (n == "Right Ring 2 Stretched") return "RightHand.Ring.2 Stretched";
if (n == "Right Ring 3 Stretched") return "RightHand.Ring.3 Stretched";
if (n == "Right Little 1 Stretched") return "RightHand.Little.1 Stretched";
if (n == "Right Little 2 Stretched") return "RightHand.Little.2 Stretched";
if (n == "Right Little 3 Stretched") return "RightHand.Little.3 Stretched";
if (n == "Right Thumb 1 Stretched") return "RightHand.Thumb.1 Stretched";
if (n == "Right Thumb 2 Stretched") return "RightHand.Thumb.2 Stretched";
if (n == "Right Thumb 3 Stretched") return "RightHand.Thumb.3 Stretched";
if (n == "Right Index Spread") return "RightHand.Index.Spread";
if (n == "Right Middle Spread") return "RightHand.Middle.Spread";
if (n == "Right Ring Spread") return "RightHand.Ring.Spread";
if (n == "Right Little Spread") return "RightHand.Little.Spread";
if (n == "Right Thumb Spread") return "RightHand.Thumb.Spread";
return n;
}
public void MultiplyLength(AnimationCurve curve, float mlp)
{
Keyframe[] keys = curve.keys;
for (int i = 0; i < keys.Length; i++)
{
keys[i].time *= mlp;
}
curve.keys = keys;
}
// Add curves to the AnimationClip for each channel
public void SetCurves(ref AnimationClip clip, float maxError, float lengthMlp)
{
MultiplyLength(curve, lengthMlp);
BakerUtilities.ReduceKeyframes(curve, maxError);
// BakerUtilities.SetTangentMode(curve);
clip.SetCurve(string.Empty, typeof(Animator), propertyName, curve);
}
// Clear all curves
public void Reset()
{
curve = new AnimationCurve();
}
// Record a keyframe for each channel
public void SetKeyframe(float time, float[] muscles)
{
curve.AddKey(time, muscles[muscleIndex]);
}
// Add a copy of the first frame to the specified time
public void SetLoopFrame(float time)
{
BakerUtilities.SetLoopFrame(time, curve);
}
}
//Manages the Animation Curves for a single Transform that is a child of the root Transform.
[System.Serializable]
public class BakerTransform
{
public Transform transform; // The Transform component to record
// Animation curves for each channel of the Transform
public AnimationCurve
posX, posY, posZ,
rotX, rotY, rotZ, rotW;
private string relativePath; // Path relative to the root
private bool recordPosition; // Should we record the localPosition if the transform?
private Vector3 relativePosition;
private bool isRootNode;
private Quaternion relativeRotation;
// The custom constructor
public BakerTransform(Transform transform, Transform root, bool recordPosition, bool isRootNode)
{
this.transform = transform;
this.recordPosition = recordPosition || isRootNode;
this.isRootNode = isRootNode;
relativePath = string.Empty;
#if UNITY_EDITOR
relativePath = UnityEditor.AnimationUtility.CalculateTransformPath(transform, root);
#endif
Reset();
}
public void SetRelativeSpace(Vector3 position, Quaternion rotation)
{
relativePosition = position;
relativeRotation = rotation;
}
// Add curves to the AnimationClip for each channel
public void SetCurves(ref AnimationClip clip)
{
if (recordPosition)
{
clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX);
clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY);
clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ);
}
clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX);
clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY);
clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ);
clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW);
if (isRootNode) AddRootMotionCurves(ref clip);
// @todo probably only need to do it once for the clip
clip.EnsureQuaternionContinuity(); // DOH!
}
private void AddRootMotionCurves(ref AnimationClip clip)
{
if (recordPosition)
{
clip.SetCurve("", typeof(Animator), "MotionT.x", posX);
clip.SetCurve("", typeof(Animator), "MotionT.y", posY);
clip.SetCurve("", typeof(Animator), "MotionT.z", posZ);
}
clip.SetCurve("", typeof(Animator), "MotionQ.x", rotX);
clip.SetCurve("", typeof(Animator), "MotionQ.y", rotY);
clip.SetCurve("", typeof(Animator), "MotionQ.z", rotZ);
clip.SetCurve("", typeof(Animator), "MotionQ.w", rotW);
}
// Clear all curves
public void Reset()
{
posX = new AnimationCurve();
posY = new AnimationCurve();
posZ = new AnimationCurve();
rotX = new AnimationCurve();
rotY = new AnimationCurve();
rotZ = new AnimationCurve();
rotW = new AnimationCurve();
}
public void ReduceKeyframes(float maxError)
{
BakerUtilities.ReduceKeyframes(rotX, maxError);
BakerUtilities.ReduceKeyframes(rotY, maxError);
BakerUtilities.ReduceKeyframes(rotZ, maxError);
BakerUtilities.ReduceKeyframes(rotW, maxError);
BakerUtilities.ReduceKeyframes(posX, maxError);
BakerUtilities.ReduceKeyframes(posY, maxError);
BakerUtilities.ReduceKeyframes(posZ, maxError);
}
// Record a keyframe for each channel
public void SetKeyframes(float time)
{
if (recordPosition)
{
Vector3 pos = transform.localPosition;
if (isRootNode)
{
pos = transform.position - relativePosition;
}
posX.AddKey(time, pos.x);
posY.AddKey(time, pos.y);
posZ.AddKey(time, pos.z);
}
Quaternion rot = transform.localRotation;
if (isRootNode)
{
rot = Quaternion.Inverse(relativeRotation) * transform.rotation;
}
rotX.AddKey(time, rot.x);
rotY.AddKey(time, rot.y);
rotZ.AddKey(time, rot.z);
rotW.AddKey(time, rot.w);
}
// Add a copy of the first frame to the specified time
public void AddLoopFrame(float time)
{
// TODO change to SetLoopFrame
if (recordPosition && !isRootNode)
{
posX.AddKey(time, posX.keys[0].value);
posY.AddKey(time, posY.keys[0].value);
posZ.AddKey(time, posZ.keys[0].value);
}
rotX.AddKey(time, rotX.keys[0].value);
rotY.AddKey(time, rotY.keys[0].value);
rotZ.AddKey(time, rotZ.keys[0].value);
rotW.AddKey(time, rotW.keys[0].value);
}
}
}

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

@ -0,0 +1,196 @@
using UnityEngine;
using System.Collections;
using UnityEngine.Playables;
namespace RootMotion
{
/// <summary>
/// Baker for Humanoid animation.
/// </summary>
public class HumanoidBaker : Baker
{
/// <summary>
/// Should the hand IK curves be added to the animation? Disable this if the original hand positions are not important when using the clip on another character via Humanoid retargeting.
/// </summary>
[Tooltip("Should the hand IK curves be added to the animation? Disable this if the original hand positions are not important when using the clip on another character via Humanoid retargeting.")]
public bool bakeHandIK = true;
/// <summary>
/// Max keyframe reduction error for the Root.Q/T, LeftFoot IK and RightFoot IK channels. Having a larger error value for 'Key Reduction Error' and a smaller one for this enables you to optimize clip data size without the floating feet effect by enabling 'Foot IK' in the Animator.
/// </summary>
[Tooltip("Max keyframe reduction error for the Root.Q/T, LeftFoot IK and RightFoot IK channels. Having a larger error value for 'Key Reduction Error' and a smaller one for this enables you to optimize clip data size without the floating feet effect by enabling 'Foot IK' in the Animator.")]
[Range(0f, 0.1f)] public float IKKeyReductionError;
/// <summary>
/// Frame rate divider for the muscle curves. If you had 'Frame Rate' set to 30, and this value set to 3, the muscle curves will be baked at 10 fps. Only the Root Q/T and Hand and Foot IK curves will be baked at 30. This enables you to optimize clip data size without the floating feet effect by enabling 'Foot IK' in the Animator.
/// </summary>
[Tooltip("Frame rate divider for the muscle curves. If you have 'Frame Rate' set to 30, and this value set to 3, the muscle curves will be baked at 10 fps. Only the Root Q/T and Hand and Foot IK curves will be baked at 30. This enables you to optimize clip data size without the floating feet effect by enabling 'Foot IK' in the Animator.")]
[Range(1, 9)] public int muscleFrameRateDiv = 1;
private BakerMuscle[] bakerMuscles;
private BakerHumanoidQT rootQT;
private BakerHumanoidQT leftFootQT;
private BakerHumanoidQT rightFootQT;
private BakerHumanoidQT leftHandQT;
private BakerHumanoidQT rightHandQT;
private float[] muscles = new float[0];
private HumanPose pose = new HumanPose();
private HumanPoseHandler handler;
private Vector3 bodyPosition;
private Quaternion bodyRotation = Quaternion.identity;
private int mN = 0;
private Quaternion lastBodyRotation = Quaternion.identity;
void Awake()
{
animator = GetComponent<Animator>();
director = GetComponent<PlayableDirector>();
if (mode == Mode.AnimationStates || mode == Mode.AnimationClips)
{
if (animator == null || !animator.isHuman)
{
Debug.LogError("HumanoidBaker GameObject does not have a Humanoid Animator component, can not bake.");
enabled = false;
return;
}
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
}
else if (mode == Mode.PlayableDirector)
{
if (director == null)
{
Debug.LogError("HumanoidBaker GameObject does not have a PlayableDirector component, can not bake.");
}
}
muscles = new float[HumanTrait.MuscleCount];
bakerMuscles = new BakerMuscle[HumanTrait.MuscleCount];
for (int i = 0; i < bakerMuscles.Length; i++)
{
bakerMuscles[i] = new BakerMuscle(i);
}
rootQT = new BakerHumanoidQT("Root");
leftFootQT = new BakerHumanoidQT(animator.GetBoneTransform(HumanBodyBones.LeftFoot), AvatarIKGoal.LeftFoot, "LeftFoot");
rightFootQT = new BakerHumanoidQT(animator.GetBoneTransform(HumanBodyBones.RightFoot), AvatarIKGoal.RightFoot, "RightFoot");
leftHandQT = new BakerHumanoidQT(animator.GetBoneTransform(HumanBodyBones.LeftHand), AvatarIKGoal.LeftHand, "LeftHand");
rightHandQT = new BakerHumanoidQT(animator.GetBoneTransform(HumanBodyBones.RightHand), AvatarIKGoal.RightHand, "RightHand");
handler = new HumanPoseHandler(animator.avatar, animator.transform);
}
protected override Transform GetCharacterRoot()
{
return animator.transform;
}
protected override void OnStartBaking()
{
rootQT.Reset();
leftFootQT.Reset();
rightFootQT.Reset();
leftHandQT.Reset();
rightHandQT.Reset();
for (int i = 0; i < bakerMuscles.Length; i++)
{
bakerMuscles[i].Reset();
}
mN = muscleFrameRateDiv;
lastBodyRotation = Quaternion.identity;
}
protected override void OnSetLoopFrame(float time)
{
for (int i = 0; i < bakerMuscles.Length; i++) bakerMuscles[i].SetLoopFrame(time);
rootQT.MoveLastKeyframes(time);
leftFootQT.SetLoopFrame(time);
rightFootQT.SetLoopFrame(time);
leftHandQT.SetLoopFrame(time);
rightHandQT.SetLoopFrame(time);
}
protected override void OnSetCurves(ref AnimationClip clip)
{
float length = bakerMuscles[0].curve.keys[bakerMuscles[0].curve.keys.Length - 1].time;
float lengthMlp = mode != Mode.Realtime ? clipLength / length : 1f;
for (int i = 0; i < bakerMuscles.Length; i++) bakerMuscles[i].SetCurves(ref clip, keyReductionError, lengthMlp);
rootQT.SetCurves(ref clip, IKKeyReductionError, lengthMlp);
leftFootQT.SetCurves(ref clip, IKKeyReductionError, lengthMlp);
rightFootQT.SetCurves(ref clip, IKKeyReductionError, lengthMlp);
if (bakeHandIK)
{
leftHandQT.SetCurves(ref clip, IKKeyReductionError, lengthMlp);
rightHandQT.SetCurves(ref clip, IKKeyReductionError, lengthMlp);
}
}
protected override void OnSetKeyframes(float time, bool lastFrame)
{
// Skip muscle frames
mN++;
bool updateMuscles = true;
if (mN < muscleFrameRateDiv && !lastFrame)
{
updateMuscles = false;
}
if (mN >= muscleFrameRateDiv) mN = 0;
UpdateHumanPose();
if (updateMuscles)
{
for (int i = 0; i < bakerMuscles.Length; i++) bakerMuscles[i].SetKeyframe(time, muscles);
}
rootQT.SetKeyframes(time, bodyPosition, bodyRotation);
Vector3 bodyPositionScaled = bodyPosition * animator.humanScale;
leftFootQT.SetIKKeyframes(time, animator.avatar, animator.transform, animator.humanScale, bodyPositionScaled, bodyRotation);
rightFootQT.SetIKKeyframes(time, animator.avatar, animator.transform, animator.humanScale, bodyPositionScaled, bodyRotation);
leftHandQT.SetIKKeyframes(time, animator.avatar, animator.transform, animator.humanScale, bodyPositionScaled, bodyRotation);
rightHandQT.SetIKKeyframes(time, animator.avatar, animator.transform, animator.humanScale, bodyPositionScaled, bodyRotation);
}
private void UpdateHumanPose()
{
handler.GetHumanPose(ref pose);
bodyPosition = pose.bodyPosition;
bodyRotation = pose.bodyRotation;
bodyRotation = BakerUtilities.EnsureQuaternionContinuity(lastBodyRotation, bodyRotation);
lastBodyRotation = bodyRotation;
for (int i = 0; i < pose.muscles.Length; i++)
{
muscles[i] = pose.muscles[i];
}
}
#if UNITY_EDITOR
protected override void SetClipSettings(AnimationClip clip, UnityEditor.AnimationClipSettings settings)
{
/* v2.0
settings.loopBlendOrientation = true;
settings.loopBlendPositionY = true;
settings.keepOriginalOrientation = true;
settings.keepOriginalPositionY = true;
*/
}
#endif
}
}

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

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

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a4b02f60233a6cf4eb1c424f90323213
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3451f091c656bfc429396cc701ac5b8d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,69 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!91 &9100000
AnimatorController:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_Name: Humanoid Baker Clip Sampler
serializedVersion: 5
m_AnimatorParameters: []
m_AnimatorLayers:
- serializedVersion: 5
m_Name: Base Layer
m_StateMachine: {fileID: 110737558}
m_Mask: {fileID: 0}
m_Motions: []
m_Behaviours: []
m_BlendingMode: 0
m_SyncedLayerIndex: -1
m_DefaultWeight: 0
m_IKPass: 0
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}
--- !u!1102 &110235224
AnimatorState:
serializedVersion: 5
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_Name: Clip 1
m_Speed: 1
m_CycleOffset: 0
m_Transitions: []
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 1
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 7400000, guid: 739b2627204c24240aa40c4f67f59d0e, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1107 &110737558
AnimatorStateMachine:
serializedVersion: 5
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_Name: Base Layer
m_ChildStates:
- serializedVersion: 1
m_State: {fileID: 110235224}
m_Position: {x: 300, y: 36, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions: []
m_EntryTransitions: []
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: 110235224}

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 24d41cb619dc9dc42bf10705eb128c97
timeCreated: 1516284817
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0bd8d9a8cbacde343967c95c4458318e
timeCreated: 1517387718
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3349a7526568cf042bf8609694db3327
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,53 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RootMotion.Demos
{
// Adds simple FK rotation offset to bones.
public class FKOffset : MonoBehaviour
{
[System.Serializable]
public class Offset
{
[HideInInspector] public string name;
public HumanBodyBones bone;
public Vector3 rotationOffset;
private Transform t;
public void Apply(Animator animator)
{
if (t == null) t = animator.GetBoneTransform(bone);
if (t == null) return;
t.localRotation *= Quaternion.Euler(rotationOffset);
}
}
public Offset[] offsets;
private Animator animator;
private void Start()
{
animator = GetComponent<Animator>();
}
private void LateUpdate()
{
foreach (Offset offset in offsets)
{
offset.Apply(animator);
}
}
private void OnDrawGizmosSelected()
{
foreach (Offset offset in offsets)
{
offset.name = offset.bone.ToString();
}
}
}
}

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

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 39b6548830366c04697b5de123deec0a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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

@ -0,0 +1,239 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
using UnityEngine.Playables;
using System;
namespace RootMotion
{
public class BakerInspector : Editor
{
protected void DrawKeyframeSettings(Baker script)
{
if (script.isBaking) return;
EditorGUILayout.Space();
EditorGUILayout.PropertyField(serializedObject.FindProperty("frameRate"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("keyReductionError"));
}
protected void DrawModeSettings(Baker script)
{
if (script.isBaking) return;
EditorGUILayout.Space();
switch(script.mode)
{
case Baker.Mode.AnimationClips:
EditorGUILayout.PropertyField(serializedObject.FindProperty("inheritClipSettings"));
if (!serializedObject.FindProperty("inheritClipSettings").boolValue) DrawClipSettings();
break;
default:
DrawClipSettings();
break;
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(serializedObject.FindProperty("mode"));
switch (script.mode)
{
case Baker.Mode.AnimationClips:
EditorGUILayout.PropertyField(serializedObject.FindProperty("animationClips"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("appendName"));
break;
case Baker.Mode.AnimationStates:
EditorGUILayout.PropertyField(serializedObject.FindProperty("animationStates"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("appendName"));
break;
case Baker.Mode.PlayableDirector:
EditorGUILayout.PropertyField(serializedObject.FindProperty("saveName"));
break;
default:
EditorGUILayout.PropertyField(serializedObject.FindProperty("saveName"));
break;
}
//EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
EditorGUILayout.LabelField(new GUIContent("Save To Folder"));
if (EditorGUILayout.DropdownButton(new GUIContent(serializedObject.FindProperty("saveToFolder").stringValue, "The folder to save the baked AnimationClips to."), FocusType.Passive, GUILayout.MaxWidth(400)))
{
serializedObject.FindProperty("saveToFolder").stringValue = SaveClipFolderPanel.Apply(serializedObject.FindProperty("saveToFolder").stringValue);
}
//EditorGUILayout.EndHorizontal();
}
private void DrawClipSettings()
{
var p = serializedObject.FindProperty("clipSettings");
EditorGUILayout.PropertyField(p, false);
if (p.isExpanded)
{
EditorGUILayout.BeginVertical("Box");
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(p.FindPropertyRelative("loopTime"));
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(p.FindPropertyRelative("loopBlend"), new GUIContent("Loop Pose"));
EditorGUILayout.PropertyField(p.FindPropertyRelative("cycleOffset"));
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField(new GUIContent("Root Transform Rotation"));
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(p.FindPropertyRelative("loopBlendOrientation"), new GUIContent("Bake Into Pose"));
EditorGUILayout.PropertyField(p.FindPropertyRelative("basedUponRotation"), new GUIContent("Based Upon"));
EditorGUILayout.PropertyField(p.FindPropertyRelative("orientationOffsetY"), new GUIContent("Offset"));
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField(new GUIContent("Root Transform Position (Y)"));
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(p.FindPropertyRelative("loopBlendPositionY"), new GUIContent("Bake Into Pose"));
EditorGUILayout.PropertyField(p.FindPropertyRelative("basedUponY"), new GUIContent("Based Upon (at Start)"));
EditorGUILayout.PropertyField(p.FindPropertyRelative("level"), new GUIContent("Offset"));
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField(new GUIContent("Root Transform Position (XZ)"));
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(p.FindPropertyRelative("loopBlendPositionXZ"), new GUIContent("Bake Into Pose"));
EditorGUILayout.PropertyField(p.FindPropertyRelative("basedUponXZ"), new GUIContent("Based Upon"));
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.PropertyField(p.FindPropertyRelative("mirror"));
EditorGUI.indentLevel--;
EditorGUILayout.EndVertical();
}
}
private void TryBake(Baker script)
{
switch (script.mode)
{
case Baker.Mode.AnimationClips:
if (script.animator == null)
{
EditorGUILayout.LabelField("No Animator found on Baker GameObject", EditorStyles.helpBox);
return;
}
if (script.animator.isHuman && script.animator.runtimeAnimatorController == null)
{
EditorGUILayout.LabelField("Humanoid Animator needs to have a valid Controller assigned for clip baking (Unity crash bug)", EditorStyles.helpBox);
return;
}
if (script.animationClips.Length == 0)
{
EditorGUILayout.LabelField("Please add AnimationClips to bake", EditorStyles.helpBox);
return;
}
foreach (AnimationClip clip in script.animationClips)
{
if (clip == null)
{
EditorGUILayout.LabelField("One of the AnimationClips is null, can not bake.", EditorStyles.helpBox);
return;
}
}
if (GUILayout.Button("Bake Animation Clips")) script.BakeClip();
return;
case Baker.Mode.AnimationStates:
if (script.animator == null)
{
EditorGUILayout.LabelField("No Animator found on Baker GameObject", EditorStyles.helpBox);
return;
}
if (script.animationStates.Length == 0)
{
EditorGUILayout.LabelField("Please add Animation State names to bake. The Animator must contain AnimationStates with matching names. If AnimationState names match with clip names used in them, you can have the Baker fill the names in automatically by right-clicking on the component header and selecting 'Find Animation States'.", EditorStyles.helpBox);
return;
}
for (int i = 0; i < script.animationStates.Length; i++)
{
if (script.animationStates[i] == string.Empty || script.animationStates[i] == "")
{
EditorGUILayout.LabelField("One of the animation state names in 'Animation States' is empty, can not bake.", EditorStyles.helpBox);
return;
}
}
if (GUILayout.Button("Bake Animation States")) script.BakeClip();
return;
case Baker.Mode.PlayableDirector:
if (script.director == null)
{
EditorGUILayout.LabelField("No PlayableDirector found on Baker GameObject", EditorStyles.helpBox);
return;
}
if (GUILayout.Button("Bake Timeline")) script.BakeClip();
break;
case Baker.Mode.Realtime:
if (GUILayout.Button("Start Baking")) script.StartBaking();
return;
}
}
protected void DrawButtons(Baker script)
{
if (!script.enabled) return;
if (script.animator == null)
{
serializedObject.FindProperty("animator").objectReferenceValue = script.GetComponent<Animator>();
}
if (script.director == null)
{
serializedObject.FindProperty("director").objectReferenceValue = script.GetComponent<PlayableDirector>();
}
if (!Application.isPlaying)
{
EditorGUILayout.LabelField("Enter Play Mode to bake.", EditorStyles.helpBox);
return;
}
if (!script.isBaking)
{
TryBake(script);
}
else
{
GUI.color = Color.red;
switch (script.mode)
{
case Baker.Mode.Realtime:
if (GUILayout.Button("Stop Baking")) script.StopBaking();
break;
default:
GUILayout.Label("Baking Progress: " + System.Math.Round(script.bakingProgress, 2));
break;
}
GUI.color = Color.white;
EditorUtility.SetDirty(script);
}
}
}
}

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

@ -0,0 +1,52 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion
{
[CustomEditor(typeof(GenericBaker))]
public class GenericBakerInspector : BakerInspector
{
private GenericBaker script { get { return target as GenericBaker; } }
private MonoScript monoScript;
void OnEnable()
{
// Changing the script execution order
if (!Application.isPlaying)
{
monoScript = MonoScript.FromMonoBehaviour(script);
int currentExecutionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (currentExecutionOrder != 15001) MonoImporter.SetExecutionOrder(monoScript, 15001);
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawKeyframeSettings(script as Baker);
DrawGenericKeyframeSettings(script);
DrawModeSettings(script as Baker);
DrawButtons(script as Baker);
if (serializedObject.ApplyModifiedProperties())
{
EditorUtility.SetDirty(script);
}
}
private void DrawGenericKeyframeSettings(GenericBaker script)
{
if (script.isBaking) return;
EditorGUILayout.PropertyField(serializedObject.FindProperty("markAsLegacy"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("root"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("rootNode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("ignoreList"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("bakePositionList"), true);
}
}
}

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8f544a5bc87054e74ac2e415889ba75a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

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

@ -0,0 +1,38 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion
{
public static class AnimationUtilityExtended
{
/// <summary>
/// Copies the curves with the specified property names from clipFrom to clipTo.
/// </summary>
/// <param name="fromClip">copy from clip.</param>
/// <param name="toClip">paste to clip</param>
/// <param name="propertyNames">Property names ("Root.T", "Root.Q", "LeftFoot.T"...).</param>
public static void CopyCurves(AnimationClip fromClip, AnimationClip toClip, string[] propertyNames)
{
EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(fromClip);
for (int i = 0; i < bindings.Length; i++)
{
for (int n = 0; n < propertyNames.Length; n++)
{
if (bindings[i].propertyName == propertyNames[n])
{
CopyCurve(fromClip, toClip, bindings[i]);
}
}
}
}
public static void CopyCurve(AnimationClip fromClip, AnimationClip toClip, EditorCurveBinding binding)
{
AnimationCurve curve = AnimationUtility.GetEditorCurve(fromClip, binding);
toClip.SetCurve(string.Empty, typeof(Animator), binding.propertyName, curve);
}
}
}

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

@ -0,0 +1,22 @@
using UnityEngine;
using UnityEditor;
using System.IO;
using System;
namespace RootMotion
{
public class SaveClipFolderPanel : EditorWindow
{
public static string Apply(string currentPath)
{
string path = EditorUtility.SaveFolderPanel("Save clip(s) to folder", currentPath, "");
if (path.Length != 0)
{
return path.Substring(path.IndexOf("Assets/"));
}
return currentPath;
}
}
}

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

@ -0,0 +1,52 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion
{
[CustomEditor(typeof(HumanoidBaker))]
public class HumanoidBakerInspector : BakerInspector
{
private HumanoidBaker script { get { return target as HumanoidBaker; } }
private MonoScript monoScript;
void OnEnable()
{
// Changing the script execution order
if (!Application.isPlaying)
{
monoScript = MonoScript.FromMonoBehaviour(script);
int currentExecutionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (currentExecutionOrder != 15000) MonoImporter.SetExecutionOrder(monoScript, 15000);
}
}
// TODO Move this to BakerInspector.cs
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawKeyframeSettings(script as Baker);
DrawHumanoidKeyframeSettings(script);
DrawModeSettings(script as Baker);
DrawButtons(script as Baker);
if (serializedObject.ApplyModifiedProperties())
{
EditorUtility.SetDirty(script);
}
}
protected void DrawHumanoidKeyframeSettings(HumanoidBaker script)
{
if (script.isBaking) return;
EditorGUILayout.PropertyField(serializedObject.FindProperty("IKKeyReductionError"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("muscleFrameRateDiv"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("bakeHandIK"));
}
}
}

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

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

@ -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);
}
}
}

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

@ -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]);
}
}
}
}

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

@ -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);
}
}
}

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

@ -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);
}
}
}

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

@ -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;
}
}
}

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

@ -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);
}
}
}

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

@ -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
}
}

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

@ -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();
}
}
}
}
}
}

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

@ -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);
}
}
}

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

@ -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);
}
}
}

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

@ -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;
}
}
}

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

@ -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);
}
}
}

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

@ -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);
}
}
}

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

@ -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;
}
}
}

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

@ -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);
}
}
}
}

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

@ -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
}
}

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

@ -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
}
}

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

@ -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
}
}

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

@ -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);
}
}
}
}

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

@ -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;
}
}

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

@ -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;
}
}
}

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

@ -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);
}
}
}

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

@ -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);
}
}
}

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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save