添加FinalIK插件和 Cinemachine插件
This commit is contained in:
parent
80693df307
commit
cdd3bf38ba
218
FactoryNumeric/Assets/AssetRaw/Scenes/scene_factoryOne.unity
Normal file
218
FactoryNumeric/Assets/AssetRaw/Scenes/scene_factoryOne.unity
Normal file
@ -0,0 +1,218 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 3
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 0
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &1627954909
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1627954912}
|
||||
- component: {fileID: 1627954911}
|
||||
- component: {fileID: 1627954910}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &1627954910
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1627954909}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &1627954911
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1627954909}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 1
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &1627954912
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1627954909}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 1627954912}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d5f1c9639028d54aa61fb516f7d24b1
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
FactoryNumeric/Assets/Plugins/RootMotion.meta
Normal file
8
FactoryNumeric/Assets/Plugins/RootMotion.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9bd989d8fda8564eb25fc8a1f22a110
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
9
FactoryNumeric/Assets/Plugins/RootMotion/Baker.meta
Normal file
9
FactoryNumeric/Assets/Plugins/RootMotion/Baker.meta
Normal file
@ -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:
|
602
FactoryNumeric/Assets/Plugins/RootMotion/Baker/Scripts/Baker.cs
Normal file
602
FactoryNumeric/Assets/Plugins/RootMotion/Baker/Scripts/Baker.cs
Normal file
@ -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, keyReductionError);
|
||||
}
|
||||
|
||||
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,651 @@
|
||||
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, float maxError)
|
||||
{
|
||||
if (recordPosition)
|
||||
{
|
||||
BakerUtilities.ReduceKeyframes(posX, maxError);
|
||||
BakerUtilities.ReduceKeyframes(posY, maxError);
|
||||
BakerUtilities.ReduceKeyframes(posZ, maxError);
|
||||
|
||||
clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX);
|
||||
clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY);
|
||||
clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ);
|
||||
}
|
||||
|
||||
BakerUtilities.ReduceKeyframes(rotX, maxError);
|
||||
BakerUtilities.ReduceKeyframes(rotY, maxError);
|
||||
BakerUtilities.ReduceKeyframes(rotZ, maxError);
|
||||
BakerUtilities.ReduceKeyframes(rotW, maxError);
|
||||
|
||||
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:
|
File diff suppressed because it is too large
Load Diff
@ -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:
|
File diff suppressed because it is too large
Load Diff
@ -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:
|
8
FactoryNumeric/Assets/Plugins/RootMotion/Editor.meta
Normal file
8
FactoryNumeric/Assets/Plugins/RootMotion/Editor.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c05102473068a24da654fee356905fc
|
||||
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:
|
@ -0,0 +1,102 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace RootMotion.FinalIK {
|
||||
|
||||
/*
|
||||
* Custom inspector and scene view tools for IKSolverLeg
|
||||
* */
|
||||
public class IKSolverLegInspector: IKSolverInspector {
|
||||
|
||||
#region Public methods
|
||||
|
||||
/*
|
||||
* Draws the custom inspector for IKSolverTrigonometric
|
||||
* */
|
||||
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
// Bone references
|
||||
if (showReferences) {
|
||||
EditorGUILayout.Space();
|
||||
AddObjectReference(prop.FindPropertyRelative("pelvis.transform"), new GUIContent("Pelvis", "The pelvis (hips)."), editHierarchy, 100);
|
||||
AddObjectReference(prop.FindPropertyRelative("thigh.transform"), new GUIContent("Thigh", "The upper leg."), editHierarchy, 100);
|
||||
AddObjectReference(prop.FindPropertyRelative("calf.transform"), new GUIContent("Calf", "The lower leg."), editHierarchy, 100);
|
||||
AddObjectReference(prop.FindPropertyRelative("foot.transform"), new GUIContent("Foot", "The ankle."), editHierarchy, 100);
|
||||
AddObjectReference(prop.FindPropertyRelative("toe.transform"), new GUIContent("Toe", "The first toe bone."), editHierarchy, 100);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
// Foot/Toe
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Position Weight", "Solver weight for smooth blending."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKRotationWeight"), new GUIContent("Rotation Weight", "Weight of last bone's rotation to IKRotation."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("heelOffset"), new GUIContent("Heel Offset", "Offset of the heel in world space."));
|
||||
|
||||
// Bending
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.bendGoal"), new GUIContent("Bend Goal", "If assigned, the knee will be bent in the direction towards this transform."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.bendGoalWeight"), new GUIContent("Bend Goal Weight", "Weight of the bend goal."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.swivelOffset"), new GUIContent("Swivel Offset", "Angular offset of the leg's bending direction."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.bendToTargetWeight"), new GUIContent("Bend To Target Weight", "If 0, the bend plane will be locked to the rotation of the pelvis and rotating the foot will have no effect on the knee direction. If 1, to the target rotation of the leg so that the knee will bend towards the forward axis of the foot. Values in between will be slerped between the two."));
|
||||
|
||||
// Stretching
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.legLengthMlp"), new GUIContent("Leg Length Mlp", "Use this to make the leg shorter/longer."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("leg.stretchCurve"), new GUIContent("Stretch Curve", "Evaluates stretching of the leg by target distance relative to leg length. Value at time 1 represents stretching amount at the point where distance to the target is equal to leg length. Value at time 1 represents stretching amount at the point where distance to the target is double the leg length. Value represents the amount of stretching. Linear stretching would be achieved with a linear curve going up by 45 degrees. Increase the range of stretching by moving the last key up and right at the same amount. Smoothing in the curve can help reduce knee snapping (start stretching the arm slightly before target distance reaches leg length)."));
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws the scene view helpers for IKSolverTrigonometric
|
||||
* */
|
||||
public static void AddScene(IKSolverLeg solver, Color color, bool modifiable) {
|
||||
if (Application.isPlaying && !solver.initiated) return;
|
||||
if (!Application.isPlaying && !solver.IsValid()) return;
|
||||
|
||||
//float length = Vector3.Distance(solver.bone1.transform.position, solver.bone2.transform.position) + Vector3.Distance(solver.bone2.transform.position, solver.bone3.transform.position);
|
||||
//float size = length * 0.05f;
|
||||
|
||||
Handles.color = color;
|
||||
GUI.color = color;
|
||||
|
||||
// Chain lines
|
||||
Handles.DrawLine(solver.pelvis.transform.position, solver.thigh.transform.position);
|
||||
Handles.DrawLine(solver.thigh.transform.position, solver.calf.transform.position);
|
||||
Handles.DrawLine(solver.calf.transform.position, solver.foot.transform.position);
|
||||
Handles.DrawLine(solver.foot.transform.position, solver.toe.transform.position);
|
||||
|
||||
// Joints
|
||||
Inspector.SphereCap(0, solver.pelvis.transform.position, Quaternion.identity, GetHandleSize(solver.pelvis.transform.position));
|
||||
Inspector.SphereCap(0, solver.thigh.transform.position, Quaternion.identity, GetHandleSize(solver.thigh.transform.position));
|
||||
Inspector.SphereCap(0, solver.calf.transform.position, Quaternion.identity, GetHandleSize(solver.calf.transform.position));
|
||||
Inspector.SphereCap(0, solver.foot.transform.position, Quaternion.identity, GetHandleSize(solver.foot.transform.position));
|
||||
Inspector.SphereCap(0, solver.toe.transform.position, Quaternion.identity, GetHandleSize(solver.toe.transform.position));
|
||||
|
||||
if (Application.isPlaying && (solver.IKPositionWeight > 0 || solver.IKRotationWeight > 0)) {
|
||||
if (modifiable) {
|
||||
Inspector.CubeCap(0, solver.IKPosition, solver.IKRotation, GetHandleSize(solver.IKPosition));
|
||||
|
||||
// Manipulating position and rotation
|
||||
switch(Tools.current) {
|
||||
case Tool.Move:
|
||||
if (solver.leg.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
|
||||
break;
|
||||
case Tool.Rotate:
|
||||
if (solver.leg.target == null) solver.IKRotation = Handles.RotationHandle(solver.IKRotation, solver.IKPosition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Target
|
||||
Handles.color = new Color(color.r, color.g, color.b, color.a * Mathf.Max(solver.IKPositionWeight, solver.IKRotationWeight));
|
||||
Handles.DrawLine(solver.toe.transform.position, solver.IKPosition);
|
||||
}
|
||||
|
||||
Handles.color = Color.white;
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
#endregion Public methods
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8601eb42d45b294dba88abac3e27d24
|
||||
timeCreated: 1487059931
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,63 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System;
|
||||
|
||||
namespace RootMotion.FinalIK {
|
||||
|
||||
/*
|
||||
* Custom inspector and scene view tools for IKSolverLimb
|
||||
* */
|
||||
public class IKSolverLimbInspector: IKSolverInspector {
|
||||
|
||||
#region Public methods
|
||||
|
||||
/*
|
||||
* Draws the custom inspector for IKSolverLimb
|
||||
* */
|
||||
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
|
||||
// Draw the trigonometric IK inspector
|
||||
IKSolverTrigonometricInspector.AddInspector(prop, editHierarchy, showReferences);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (showReferences && editHierarchy) {
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("goal"), new GUIContent("Avatar IK Goal", "Avatar IK Goal here is only used by the 'Arm' bend modifier."));
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("maintainRotationWeight"), new GUIContent("Maintain Rotation", "Weight of rotating the last bone back to the rotation it had before solving IK."));
|
||||
|
||||
// Bend normal modifier.
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendModifier"), new GUIContent("Bend Modifier", "Bend normal modifier."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendModifierWeight"), new GUIContent("Bend Modifier Weight", "Weight of the bend modifier."));
|
||||
|
||||
if (prop.FindPropertyRelative("bendModifier").enumValueIndex == 4) EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendGoal"), new GUIContent("Bend Goal", "The bend goal Transform (optional, you can also use IKSolverTrigonometric.SetBendGoalPosition(Vector position, float weight)."));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws the scene view helpers for IKSolverLimb
|
||||
* */
|
||||
public static void AddScene(IKSolverLimb solver, Color color, bool modifiable) {
|
||||
if (Application.isPlaying && !solver.initiated) return;
|
||||
if (!Application.isPlaying && !solver.IsValid()) return;
|
||||
|
||||
if (solver.bendGoal != null && solver.bendModifierWeight > 0f) {
|
||||
Color c = color;
|
||||
c.a = solver.bendModifierWeight;
|
||||
Handles.color = c;
|
||||
|
||||
Handles.DrawLine(solver.bone2.transform.position, solver.bendGoal.position);
|
||||
Inspector.SphereCap(0, solver.bendGoal.position, Quaternion.identity, GetHandleSize(solver.bendGoal.position) * 0.5f);
|
||||
|
||||
Handles.color = Color.white;
|
||||
}
|
||||
|
||||
IKSolverTrigonometricInspector.AddScene(solver as IKSolverTrigonometric, color, modifiable);
|
||||
}
|
||||
|
||||
#endregion Public methods
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57956e150e00f474b9c03c7155dc43e3
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,122 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System;
|
||||
|
||||
namespace RootMotion.FinalIK {
|
||||
|
||||
/*
|
||||
* Custom inspector and scene view tools for IKSolverLookAt
|
||||
* */
|
||||
public class IKSolverLookAtInspector: IKSolverInspector {
|
||||
|
||||
#region Public methods
|
||||
|
||||
/*
|
||||
* Draws the custom inspector for IKSolverLookAt
|
||||
* */
|
||||
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
|
||||
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Weight", "Solver weight for smooth blending."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bodyWeight"), new GUIContent("Body Weight", "Weight of rotating spine to target."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("headWeight"), new GUIContent("Head Weight", "Weight of rotating head to target."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("eyesWeight"), new GUIContent("Eyes Weight", "Weight of rotating eyes to target."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampWeight"), new GUIContent("Clamp Weight", "Clamping rotation of spine and head. 0 is free rotation, 1 is completely clamped to forward."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampWeightHead"), new GUIContent("Clamp Weight Head", "Clamping rotation of the head. 0 is free rotation, 1 is completely clamped to forward."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampWeightEyes"), new GUIContent("Clamp Weight Eyes", "Clamping rotation of the eyes. 0 is free rotation, 1 is completely clamped to forward."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("clampSmoothing"), new GUIContent("Clamp Smoothing", "Number of sine smoothing iterations applied on clamping to make the clamping point smoother."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("spineWeightCurve"), new GUIContent("Spine Weight Curve", "Weight distribution between spine bones (first bone is evaluated at time 0.0, last bone at 1.0)."));
|
||||
|
||||
// References
|
||||
if (showReferences) {
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("head.transform"), new GUIContent("Head", "The head bone."));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
AddArray(prop.FindPropertyRelative("spine"), new GUIContent("Spine", string.Empty), editHierarchy, false, null, null, DrawArrayElementLabelBone);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
AddArray(prop.FindPropertyRelative("eyes"), new GUIContent("Eyes", string.Empty), editHierarchy, false, null, null, DrawArrayElementLabelBone);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws the scene view helpers for IKSolverLookAt
|
||||
* */
|
||||
public static void AddScene(IKSolverLookAt solver, Color color, bool modifiable) {
|
||||
// Protect from null reference errors
|
||||
if (Application.isPlaying && !solver.initiated) return;
|
||||
if (!Application.isPlaying && !solver.IsValid()) return;
|
||||
|
||||
// Display the Spine
|
||||
if (solver.spine.Length > 0) {
|
||||
Handles.color = color;
|
||||
GUI.color = color;
|
||||
|
||||
for (int i = 0; i < solver.spine.Length; i++) {
|
||||
IKSolverLookAt.LookAtBone bone = solver.spine[i];
|
||||
|
||||
if (i < solver.spine.Length - 1) Handles.DrawLine(bone.transform.position, solver.spine[i + 1].transform.position);
|
||||
Inspector.SphereCap(0, bone.transform.position, Quaternion.identity, GetHandleSize(bone.transform.position));
|
||||
}
|
||||
|
||||
// Draw a transparent line from last bone to IKPosition
|
||||
if (Application.isPlaying) {
|
||||
Handles.color = new Color(color.r, color.g, color.b, color.a * solver.IKPositionWeight * solver.bodyWeight);
|
||||
Handles.DrawLine(solver.spine[solver.spine.Length - 1].transform.position, solver.IKPosition);
|
||||
}
|
||||
}
|
||||
|
||||
// Display the eyes
|
||||
if (solver.eyes.Length > 0) {
|
||||
for (int i = 0; i < solver.eyes.Length; i++) {
|
||||
DrawLookAtBoneInScene(solver.eyes[i], solver.IKPosition, color, solver.IKPositionWeight * solver.eyesWeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Display the head
|
||||
if (solver.head.transform != null) {
|
||||
DrawLookAtBoneInScene(solver.head, solver.IKPosition, color, solver.IKPositionWeight * solver.headWeight);
|
||||
}
|
||||
|
||||
Handles.color = color;
|
||||
GUI.color = color;
|
||||
|
||||
// Selecting joint and manipulating IKPosition
|
||||
if (Application.isPlaying && solver.IKPositionWeight > 0) {
|
||||
if (modifiable) {
|
||||
Inspector.SphereCap(0, solver.IKPosition, Quaternion.identity, GetHandleSize(solver.IKPosition));
|
||||
|
||||
// Manipulating position
|
||||
if (solver.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
|
||||
}
|
||||
}
|
||||
|
||||
Handles.color = Color.white;
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
#endregion Public methods
|
||||
|
||||
private static void DrawArrayElementLabelBone(SerializedProperty bone, bool editHierarchy) {
|
||||
AddObjectReference(bone.FindPropertyRelative("transform"), GUIContent.none, editHierarchy, 0, 300);
|
||||
}
|
||||
|
||||
private static void DrawLookAtBoneInScene(IKSolverLookAt.LookAtBone bone, Vector3 IKPosition, Color color, float lineWeight) {
|
||||
Handles.color = color;
|
||||
GUI.color = color;
|
||||
|
||||
Inspector.SphereCap(0, bone.transform.position, Quaternion.identity, GetHandleSize(bone.transform.position));
|
||||
|
||||
// Draw a transparent line from last bone to IKPosition
|
||||
if (Application.isPlaying && lineWeight > 0) {
|
||||
Handles.color = new Color(color.r, color.g, color.b, color.a * lineWeight);
|
||||
Handles.DrawLine(bone.transform.position, IKPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cf17f5d7f4954636ad6575fd9f9832c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,97 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace RootMotion.FinalIK {
|
||||
|
||||
/*
|
||||
* Custom inspector and scene view tools for IKSolverTrigonometric
|
||||
* */
|
||||
public class IKSolverTrigonometricInspector: IKSolverInspector {
|
||||
|
||||
#region Public methods
|
||||
|
||||
/*
|
||||
* Draws the custom inspector for IKSolverTrigonometric
|
||||
* */
|
||||
public static void AddInspector(SerializedProperty prop, bool editHierarchy, bool showReferences) {
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
// Bone references
|
||||
if (showReferences) {
|
||||
EditorGUILayout.Space();
|
||||
AddObjectReference(prop.FindPropertyRelative("bone1.transform"), new GUIContent("Bone 1", "The first bone in the hierarchy (thigh or upper arm)."), editHierarchy, 100);
|
||||
AddObjectReference(prop.FindPropertyRelative("bone2.transform"), new GUIContent("Bone 2", "The second bone in the hierarchy (calf or forearm)."), editHierarchy, 100);
|
||||
AddObjectReference(prop.FindPropertyRelative("bone3.transform"), new GUIContent("Bone 3", "The last bone in the hierarchy (foot or hand)."), editHierarchy, 100);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("target"), new GUIContent("Target", "The target Transform. Solver IKPosition will be automatically set to the position of the target."));
|
||||
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKPositionWeight"), new GUIContent("Position Weight", "Solver weight for smooth blending."));
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("IKRotationWeight"), new GUIContent("Rotation Weight", "Weight of last bone's rotation to IKRotation."));
|
||||
|
||||
if (showReferences) {
|
||||
EditorGUILayout.PropertyField(prop.FindPropertyRelative("bendNormal"), new GUIContent("Bend Normal", "Bend plane normal."));
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddTestInspector(SerializedObject obj) {
|
||||
SerializedProperty solver = obj.FindProperty("solver");
|
||||
EditorGUILayout.PropertyField(solver.FindPropertyRelative("bendNormal"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Draws the scene view helpers for IKSolverTrigonometric
|
||||
* */
|
||||
public static void AddScene(IKSolverTrigonometric solver, Color color, bool modifiable) {
|
||||
if (Application.isPlaying && !solver.initiated) return;
|
||||
if (!Application.isPlaying && !solver.IsValid()) return;
|
||||
|
||||
//float length = Vector3.Distance(solver.bone1.transform.position, solver.bone2.transform.position) + Vector3.Distance(solver.bone2.transform.position, solver.bone3.transform.position);
|
||||
//float size = length * 0.05f;
|
||||
|
||||
Handles.color = color;
|
||||
GUI.color = color;
|
||||
|
||||
Vector3 bendPosition = solver.bone2.transform.position;
|
||||
Vector3 endPosition = solver.bone3.transform.position;
|
||||
|
||||
// Chain lines
|
||||
Handles.DrawLine(solver.bone1.transform.position, bendPosition);
|
||||
Handles.DrawLine(bendPosition, endPosition);
|
||||
|
||||
// Joints
|
||||
Inspector.SphereCap(0, solver.bone1.transform.position, Quaternion.identity, GetHandleSize(solver.bone1.transform.position));
|
||||
Inspector.SphereCap(0, bendPosition, Quaternion.identity, GetHandleSize(bendPosition));
|
||||
Inspector.SphereCap(0, endPosition, Quaternion.identity, GetHandleSize(endPosition));
|
||||
|
||||
if (Application.isPlaying && (solver.IKPositionWeight > 0 || solver.IKRotationWeight > 0)) {
|
||||
if (modifiable) {
|
||||
Inspector.CubeCap(0, solver.IKPosition, solver.IKRotation, GetHandleSize(solver.IKPosition));
|
||||
|
||||
// Manipulating position and rotation
|
||||
switch(Tools.current) {
|
||||
case Tool.Move:
|
||||
if (solver.target == null) solver.IKPosition = Handles.PositionHandle(solver.IKPosition, Quaternion.identity);
|
||||
break;
|
||||
case Tool.Rotate:
|
||||
if (solver.target == null) solver.IKRotation = Handles.RotationHandle(solver.IKRotation, solver.IKPosition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Target
|
||||
Handles.color = new Color(color.r, color.g, color.b, color.a * Mathf.Max(solver.IKPositionWeight, solver.IKRotationWeight));
|
||||
Handles.DrawLine(endPosition, solver.IKPosition);
|
||||
}
|
||||
|
||||
Handles.color = Color.white;
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
#endregion Public methods
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3474539eeb23c491fbd6a10e0951ce9d
|
||||
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…
x
Reference in New Issue
Block a user