add Physics support

This commit is contained in:
1605296300@qq.com
2022-08-10 16:54:20 +08:00
parent 33c8d35c84
commit f797fbb2fd
855 changed files with 649609 additions and 98 deletions
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 2be36e9c0fdfcca44a4ae5dea045b62c
guid: 1fdd4de1668fa554d87e5651110ab090
folderAsset: yes
DefaultImporter:
externalObjects: {}
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 577d9725f58264943855b8ac185531fe
folderAsset: yes
timeCreated: 1466788344
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 14f21d7a1e53a8c4e87b25526a7eb63c
folderAsset: yes
timeCreated: 1466788345
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aadad8ac54f29e44583510294ac5c312
timeCreated: 1466788355
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,76 @@
fileFormatVersion: 2
guid: 6a3c684705042f345975d924f6983e36
timeCreated: 1466788352
licenseType: Store
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Android:
enabled: 1
settings:
CPU: AnyCPU
Any:
enabled: 0
settings: {}
Editor:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux:
enabled: 0
settings:
CPU: x86
Linux64:
enabled: 0
settings:
CPU: x86_64
OSXIntel:
enabled: 0
settings:
CPU: AnyCPU
OSXIntel64:
enabled: 0
settings:
CPU: AnyCPU
SamsungTV:
enabled: 1
settings:
STV_MODEL: STANDARD_13
Tizen:
enabled: 1
settings: {}
WebGL:
enabled: 1
settings: {}
Win:
enabled: 0
settings:
CPU: AnyCPU
Win64:
enabled: 0
settings:
CPU: AnyCPU
WindowsStoreApps:
enabled: 1
settings:
CPU: AnyCPU
DontProcess: False
PlaceholderPath: Assets/JsonDotNet/Assemblies/Standalone/Newtonsoft.Json.dll
SDK: AnySDK
ScriptingBackend: Il2Cpp
iOS:
enabled: 1
settings:
CompileFlags:
FrameworkDependencies:
tvOS:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 01ef782d02bb1994dbe418b69432552b
folderAsset: yes
timeCreated: 1466788344
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d6807fedb8dcaf04682d2c84f0ab753f
timeCreated: 1466788355
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,75 @@
fileFormatVersion: 2
guid: 17aef65a15b471f468b5fbeb4ff0c6a1
timeCreated: 1466788349
licenseType: Store
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Android:
enabled: 0
settings:
CPU: AnyCPU
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux:
enabled: 1
settings:
CPU: x86
Linux64:
enabled: 1
settings:
CPU: x86_64
LinuxUniversal:
enabled: 1
settings:
CPU: AnyCPU
OSXIntel:
enabled: 1
settings:
CPU: AnyCPU
OSXIntel64:
enabled: 1
settings:
CPU: AnyCPU
OSXUniversal:
enabled: 1
settings:
CPU: AnyCPU
SamsungTV:
enabled: 0
settings:
STV_MODEL: STANDARD_13
Win:
enabled: 1
settings:
CPU: AnyCPU
Win64:
enabled: 1
settings:
CPU: AnyCPU
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
DontProcess: False
PlaceholderPath:
SDK: AnySDK
ScriptingBackend: Il2Cpp
iOS:
enabled: 0
settings:
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1418141139a6ac443b18cb05c0643a29
folderAsset: yes
timeCreated: 1466788345
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 36f7323c55864364d8bb88c736e4bca6
timeCreated: 1466788355
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,67 @@
fileFormatVersion: 2
guid: 9b6ba260dada0ea4a871a42011f8b87d
timeCreated: 1466788355
licenseType: Store
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Android:
enabled: 0
settings:
CPU: AnyCPU
Any:
enabled: 0
settings: {}
Editor:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux:
enabled: 0
settings:
CPU: x86
Linux64:
enabled: 0
settings:
CPU: x86_64
OSXIntel:
enabled: 0
settings:
CPU: AnyCPU
OSXIntel64:
enabled: 0
settings:
CPU: AnyCPU
SamsungTV:
enabled: 0
settings:
STV_MODEL: STANDARD_13
Win:
enabled: 0
settings:
CPU: AnyCPU
Win64:
enabled: 0
settings:
CPU: AnyCPU
WindowsStoreApps:
enabled: 1
settings:
CPU: AnyCPU
DontProcess: False
PlaceholderPath: Assets/JsonDotNet/Assemblies/Standalone/Newtonsoft.Json.dll
SDK: AnySDK
ScriptingBackend: DotNet
iOS:
enabled: 0
settings:
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 76f828f47ce26cc43991113c6a39dbbf
folderAsset: yes
timeCreated: 1466010535
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e7d9a07cc3f02a41a575406e7230846
timeCreated: 1466788421
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9a6f8c7c1ea72ce46831c5e1b6150d0c
timeCreated: 1466790933
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<linker>
<assembly fullname="System">
<type fullname="System.ComponentModel.TypeConverter" preserve="all" />
<!-- <namespace fullname="System.ComponentModel" preserve="all" /> -->
</assembly>
</linker>
+6
View File
@@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 06314f49bdda26043963578d60a0a7ee
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:
+78
View File
@@ -0,0 +1,78 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: New Material 1
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 20, y: 20}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 10309, guid: 0000000000000000f000000000000000, type: 0}
m_Scale: {x: 20, y: 20}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 0.2735849, g: 0.2735849, b: 0.2735849, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 00a6b3b340ccb10498d0851a4580bef1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 667e8e9da20031342bb8ca7bfd025c32
guid: 8c928dfb7b5a0d249ac7656d0636010d
folderAsset: yes
DefaultImporter:
externalObjects: {}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a8c00ee8c4666954cb9915799f0248d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+9
View 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,498 @@
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>
/// Sets the baked animation clip to loop time and matches the last frame keys with the first. Note that if you are overwriting a previously baked clip, AnimationClipSettings will be copied from the existing clip.
/// </summary>
[Tooltip("Sets the baked animation clip to loop time and matches the last frame keys with the first. Note that when overwriting a previously baked clip, AnimationClipSettings will be copied from the existing clip.")]
public bool loop = true;
/// <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; }
[SerializeField] [HideInInspector] public Animator animator;
[SerializeField] [HideInInspector] public PlayableDirector director;
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; }
#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;
private const bool setLoopFrame = true; // Makes sure first and last keyframes in the baked clip match up if baking a looping clip.
#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
switch (mode)
{
case Mode.AnimationClips:
AnimationClipSettings originalSettings = AnimationUtility.GetAnimationClipSettings(animationClips[currentClipIndex]);
loop = originalSettings.loopTime;
break;
}
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 (mode != Mode.Realtime && loop && setLoopFrame)
{
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();
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();
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);
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);
AnimationUtility.SetAnimationClipSettings(clips[i], existingSettings);
EditorUtility.CopySerialized(clips[i], existing);
}
else
{
// Create new asset
AssetDatabase.CreateAsset(clips[i], path);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
savedClip = AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip)) as AnimationClip;
Debug.Log(path + " successfully baked.");
}
Selection.activeObject = savedClip;
ClearBakedClips();
}
protected virtual void SetClipSettings(AnimationClip clip, AnimationClipSettings settings) {}
private string GetFullPath(int clipIndex)
{
switch (mode)
{
case Baker.Mode.AnimationClips:
return saveToFolder + "/" + animationClips[clipIndex].name + appendName + ".anim";
case Baker.Mode.AnimationStates:
return saveToFolder + "/" + animationStates[clipIndex] + appendName + ".anim";
case Baker.Mode.PlayableDirector:
return saveToFolder + "/" + saveName + ".anim";
default:
return saveToFolder + "/" + saveName + ".anim";
}
}
private AnimationClip[] GetBakedClips()
{
return bakedClips;
}
private void ClearBakedClips()
{
bakedClips = new AnimationClip[0];
}
#endif
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ab14efce3fefe4f4982f0fa6911d0cd4
timeCreated: 1516617114
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,134 @@
using UnityEngine;
using System.Collections;
using System;
namespace RootMotion
{
/// <summary>
/// Baker for Generic/Legacy animation.
/// </summary>
public class GenericBaker : Baker
{
/// <summary>
/// If true, produced AnimationClips will be marked as Legacy and usable with the Legacy animation system.
/// </summary>
[Tooltip("If true, produced AnimationClips will be marked as Legacy and usable with the Legacy animation system.")]
public bool markAsLegacy;
/// <summary>
/// Root Transform of the hierarchy to bake.
/// </summary>
[Tooltip("Root Transform of the hierarchy to bake.")]
public Transform root;
/// <summary>
/// Root Node used for root motion.
/// </summary>
[Tooltip("Root Node used for root motion.")]
public Transform rootNode;
/// <summary>
/// List of Transforms to ignore, rotation curves will not be baked for these Transforms.
/// </summary>
[Tooltip("List of Transforms to ignore, rotation curves will not be baked for these Transforms.")]
public Transform[] ignoreList;
/// <summary>
/// LocalPosition curves will be baked for these Transforms only. If you are baking a character, the pelvis bone should be added to this array.
/// </summary>
[Tooltip("LocalPosition curves will be baked for these Transforms only. If you are baking a character, the pelvis bone should be added to this array.")]
public Transform[] bakePositionList;
private BakerTransform[] children = new BakerTransform[0];
private BakerTransform rootChild;
private int rootChildIndex = -1;
void Awake()
{
// Find all the child Transforms of the Animator
Transform[] childrenAndRoot = (Transform[])root.GetComponentsInChildren<Transform>();
children = new BakerTransform[0];
// Exlude the ignore list, construct the children array
for (int i = 0; i < childrenAndRoot.Length; i++)
{
if (!IsIgnored(childrenAndRoot[i]))
{
Array.Resize(ref children, children.Length + 1);
bool isRootNode = childrenAndRoot[i] == rootNode;
if (isRootNode) rootChildIndex = children.Length - 1;
children[children.Length - 1] = new BakerTransform(childrenAndRoot[i], root, BakePosition(childrenAndRoot[i]), isRootNode);
}
}
}
protected override Transform GetCharacterRoot()
{
return root;
}
protected override void OnStartBaking()
{
for (int i = 0; i < children.Length; i++)
{
children[i].Reset();
if (i == rootChildIndex) children[i].SetRelativeSpace(root.position, root.rotation);
}
}
protected override void OnSetLoopFrame(float time)
{
// TODO Change to SetLoopFrame like in HumanoidBaker
for (int i = 0; i < children.Length; i++) children[i].AddLoopFrame(time);
}
protected override void OnSetCurves(ref AnimationClip clip)
{
// TODO Length Multiplier
for (int i = 0; i < children.Length; i++) children[i].SetCurves(ref clip);
}
protected override void OnSetKeyframes(float time, bool lastFrame)
{
for (int i = 0; i < children.Length; i++) children[i].SetKeyframes(time);
}
// Is the specified Transform in the ignore list?
private bool IsIgnored(Transform t)
{
for (int i = 0; i < ignoreList.Length; i++)
{
if (t == ignoreList[i]) return true;
}
return false;
}
// Should we record the localPosition channels of the Transform?
private bool BakePosition(Transform t)
{
for (int i = 0; i < bakePositionList.Length; i++)
{
if (t == bakePositionList[i]) return true;
}
return false;
}
#if UNITY_EDITOR
protected override void SetClipSettings(AnimationClip clip, UnityEditor.AnimationClipSettings settings)
{
clip.legacy = markAsLegacy;
if (mode != Baker.Mode.AnimationClips)
{
clip.wrapMode = loop ? 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,90 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Reflection;
namespace RootMotion
{
public class 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 HumanBodyBones HumanIDFromAvatarIKGoal(AvatarIKGoal avatarIKGoal)
{
HumanBodyBones humanId = HumanBodyBones.LastBone;
switch (avatarIKGoal)
{
case AvatarIKGoal.LeftFoot: humanId = HumanBodyBones.LeftFoot; break;
case AvatarIKGoal.RightFoot: humanId = HumanBodyBones.RightFoot; break;
case AvatarIKGoal.LeftHand: humanId = HumanBodyBones.LeftHand; break;
case AvatarIKGoal.RightHand: humanId = HumanBodyBones.RightHand; break;
}
return humanId;
}
}
}
@@ -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,571 @@
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;
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();
}
// 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, float humanScale, Vector3 bodyPosition, Quaternion bodyRotation)
{
// TODO Use character scale
TQ IKTQ = AvatarUtility.GetIKGoalTQ(avatar, humanScale, goal, new TQ(bodyPosition, bodyRotation), new TQ(transform.position, transform.rotation));
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);
}
private void MoveLastKeyframe(float time, AnimationCurve curve)
{
Keyframe[] keys = curve.keys;
keys[keys.Length - 1].time = time;
curve.keys = keys;
}
public void MultiplyLength(AnimationCurve curve, float mlp)
{
Keyframe[] keys = curve.keys;
for (int i = 0; i < keys.Length; i++)
{
keys[i].time *= mlp;
}
curve.keys = keys;
}
// Add curves to the AnimationClip for each channel
public void SetCurves(ref AnimationClip clip, float maxError, float lengthMlp)
{
MultiplyLength(rotX, lengthMlp);
MultiplyLength(rotY, lengthMlp);
MultiplyLength(rotZ, lengthMlp);
MultiplyLength(rotW, lengthMlp);
MultiplyLength(posX, lengthMlp);
MultiplyLength(posY, lengthMlp);
MultiplyLength(posZ, lengthMlp);
BakerUtilities.ReduceKeyframes(rotX, maxError);
BakerUtilities.ReduceKeyframes(rotY, maxError);
BakerUtilities.ReduceKeyframes(rotZ, maxError);
BakerUtilities.ReduceKeyframes(rotW, maxError);
BakerUtilities.ReduceKeyframes(posX, maxError);
BakerUtilities.ReduceKeyframes(posY, maxError);
BakerUtilities.ReduceKeyframes(posZ, maxError);
BakerUtilities.SetTangentMode(rotX);
BakerUtilities.SetTangentMode(rotY);
BakerUtilities.SetTangentMode(rotZ);
BakerUtilities.SetTangentMode(rotW);
/*
BakerUtilities.SetTangentMode(posX);
BakerUtilities.SetTangentMode(posY);
BakerUtilities.SetTangentMode(posZ);
*/
clip.SetCurve(string.Empty, typeof(Animator), Qx, rotX);
clip.SetCurve(string.Empty, typeof(Animator), Qy, rotY);
clip.SetCurve(string.Empty, typeof(Animator), Qz, rotZ);
clip.SetCurve(string.Empty, typeof(Animator), Qw, rotW);
clip.SetCurve(string.Empty, typeof(Animator), Tx, posX);
clip.SetCurve(string.Empty, typeof(Animator), Ty, posY);
clip.SetCurve(string.Empty, typeof(Animator), Tz, posZ);
}
}
// Manages the Animation Curves for a single Transform that is a child of the root Transform.
[System.Serializable]
public class BakerMuscle
{
// Animation curves for each channel of the Transform
public AnimationCurve curve;
private int muscleIndex = -1;
private string propertyName;
// The custom constructor
public BakerMuscle(int muscleIndex)
{
this.muscleIndex = muscleIndex;
this.propertyName = MuscleNameToPropertyName(HumanTrait.MuscleName[muscleIndex]);
Reset();
}
private string MuscleNameToPropertyName(string n)
{
// Left fingers
if (n == "Left Index 1 Stretched") return "LeftHand.Index.1 Stretched";
if (n == "Left Index 2 Stretched") return "LeftHand.Index.2 Stretched";
if (n == "Left Index 3 Stretched") return "LeftHand.Index.3 Stretched";
if (n == "Left Middle 1 Stretched") return "LeftHand.Middle.1 Stretched";
if (n == "Left Middle 2 Stretched") return "LeftHand.Middle.2 Stretched";
if (n == "Left Middle 3 Stretched") return "LeftHand.Middle.3 Stretched";
if (n == "Left Ring 1 Stretched") return "LeftHand.Ring.1 Stretched";
if (n == "Left Ring 2 Stretched") return "LeftHand.Ring.2 Stretched";
if (n == "Left Ring 3 Stretched") return "LeftHand.Ring.3 Stretched";
if (n == "Left Little 1 Stretched") return "LeftHand.Little.1 Stretched";
if (n == "Left Little 2 Stretched") return "LeftHand.Little.2 Stretched";
if (n == "Left Little 3 Stretched") return "LeftHand.Little.3 Stretched";
if (n == "Left Thumb 1 Stretched") return "LeftHand.Thumb.1 Stretched";
if (n == "Left Thumb 2 Stretched") return "LeftHand.Thumb.2 Stretched";
if (n == "Left Thumb 3 Stretched") return "LeftHand.Thumb.3 Stretched";
if (n == "Left Index Spread") return "LeftHand.Index.Spread";
if (n == "Left Middle Spread") return "LeftHand.Middle.Spread";
if (n == "Left Ring Spread") return "LeftHand.Ring.Spread";
if (n == "Left Little Spread") return "LeftHand.Little.Spread";
if (n == "Left Thumb Spread") return "LeftHand.Thumb.Spread";
// Right fingers
if (n == "Right Index 1 Stretched") return "RightHand.Index.1 Stretched";
if (n == "Right Index 2 Stretched") return "RightHand.Index.2 Stretched";
if (n == "Right Index 3 Stretched") return "RightHand.Index.3 Stretched";
if (n == "Right Middle 1 Stretched") return "RightHand.Middle.1 Stretched";
if (n == "Right Middle 2 Stretched") return "RightHand.Middle.2 Stretched";
if (n == "Right Middle 3 Stretched") return "RightHand.Middle.3 Stretched";
if (n == "Right Ring 1 Stretched") return "RightHand.Ring.1 Stretched";
if (n == "Right Ring 2 Stretched") return "RightHand.Ring.2 Stretched";
if (n == "Right Ring 3 Stretched") return "RightHand.Ring.3 Stretched";
if (n == "Right Little 1 Stretched") return "RightHand.Little.1 Stretched";
if (n == "Right Little 2 Stretched") return "RightHand.Little.2 Stretched";
if (n == "Right Little 3 Stretched") return "RightHand.Little.3 Stretched";
if (n == "Right Thumb 1 Stretched") return "RightHand.Thumb.1 Stretched";
if (n == "Right Thumb 2 Stretched") return "RightHand.Thumb.2 Stretched";
if (n == "Right Thumb 3 Stretched") return "RightHand.Thumb.3 Stretched";
if (n == "Right Index Spread") return "RightHand.Index.Spread";
if (n == "Right Middle Spread") return "RightHand.Middle.Spread";
if (n == "Right Ring Spread") return "RightHand.Ring.Spread";
if (n == "Right Little Spread") return "RightHand.Little.Spread";
if (n == "Right Thumb Spread") return "RightHand.Thumb.Spread";
return n;
}
public void MultiplyLength(AnimationCurve curve, float mlp)
{
Keyframe[] keys = curve.keys;
for (int i = 0; i < keys.Length; i++)
{
keys[i].time *= mlp;
}
curve.keys = keys;
}
// Add curves to the AnimationClip for each channel
public void SetCurves(ref AnimationClip clip, float maxError, float lengthMlp)
{
MultiplyLength(curve, lengthMlp);
BakerUtilities.ReduceKeyframes(curve, maxError);
// BakerUtilities.SetTangentMode(curve);
clip.SetCurve(string.Empty, typeof(Animator), propertyName, curve);
}
// Clear all curves
public void Reset()
{
curve = new AnimationCurve();
}
// Record a keyframe for each channel
public void SetKeyframe(float time, float[] muscles)
{
curve.AddKey(time, muscles[muscleIndex]);
}
// Add a copy of the first frame to the specified time
public void SetLoopFrame(float time)
{
BakerUtilities.SetLoopFrame(time, curve);
}
}
//Manages the Animation Curves for a single Transform that is a child of the root Transform.
[System.Serializable]
public class BakerTransform
{
public Transform transform; // The Transform component to record
// Animation curves for each channel of the Transform
public AnimationCurve
posX, posY, posZ,
rotX, rotY, rotZ, rotW;
private string relativePath; // Path relative to the root
private bool recordPosition; // Should we record the localPosition if the transform?
private Vector3 relativePosition;
private bool isRootNode;
private Quaternion relativeRotation;
// The custom constructor
public BakerTransform(Transform transform, Transform root, bool recordPosition, bool isRootNode)
{
this.transform = transform;
this.recordPosition = recordPosition || isRootNode;
this.isRootNode = isRootNode;
relativePath = string.Empty;
#if UNITY_EDITOR
relativePath = UnityEditor.AnimationUtility.CalculateTransformPath(transform, root);
#endif
Reset();
}
public void SetRelativeSpace(Vector3 position, Quaternion rotation)
{
relativePosition = position;
relativeRotation = rotation;
}
// Add curves to the AnimationClip for each channel
public void SetCurves(ref AnimationClip clip)
{
if (recordPosition)
{
clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX);
clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY);
clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ);
}
clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX);
clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY);
clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ);
clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW);
if (isRootNode) AddRootMotionCurves(ref clip);
// @todo probably only need to do it once for the clip
clip.EnsureQuaternionContinuity(); // DOH!
}
private void AddRootMotionCurves(ref AnimationClip clip)
{
if (recordPosition)
{
clip.SetCurve("", typeof(Animator), "MotionT.x", posX);
clip.SetCurve("", typeof(Animator), "MotionT.y", posY);
clip.SetCurve("", typeof(Animator), "MotionT.z", posZ);
}
clip.SetCurve("", typeof(Animator), "MotionQ.x", rotX);
clip.SetCurve("", typeof(Animator), "MotionQ.y", rotY);
clip.SetCurve("", typeof(Animator), "MotionQ.z", rotZ);
clip.SetCurve("", typeof(Animator), "MotionQ.w", rotW);
}
// Clear all curves
public void Reset()
{
posX = new AnimationCurve();
posY = new AnimationCurve();
posZ = new AnimationCurve();
rotX = new AnimationCurve();
rotY = new AnimationCurve();
rotZ = new AnimationCurve();
rotW = new AnimationCurve();
}
public void ReduceKeyframes(float maxError)
{
BakerUtilities.ReduceKeyframes(rotX, maxError);
BakerUtilities.ReduceKeyframes(rotY, maxError);
BakerUtilities.ReduceKeyframes(rotZ, maxError);
BakerUtilities.ReduceKeyframes(rotW, maxError);
BakerUtilities.ReduceKeyframes(posX, maxError);
BakerUtilities.ReduceKeyframes(posY, maxError);
BakerUtilities.ReduceKeyframes(posZ, maxError);
}
// Record a keyframe for each channel
public void SetKeyframes(float time)
{
if (recordPosition)
{
Vector3 pos = transform.localPosition;
if (isRootNode)
{
pos = transform.position - relativePosition;
}
posX.AddKey(time, pos.x);
posY.AddKey(time, pos.y);
posZ.AddKey(time, pos.z);
}
Quaternion rot = transform.localRotation;
if (isRootNode)
{
rot = Quaternion.Inverse(relativeRotation) * transform.rotation;
}
rotX.AddKey(time, rot.x);
rotY.AddKey(time, rot.y);
rotZ.AddKey(time, rot.z);
rotW.AddKey(time, rot.w);
}
// Add a copy of the first frame to the specified time
public void AddLoopFrame(float time)
{
// TODO change to SetLoopFrame
if (recordPosition && !isRootNode)
{
posX.AddKey(time, posX.keys[0].value);
posY.AddKey(time, posY.keys[0].value);
posZ.AddKey(time, posZ.keys[0].value);
}
rotX.AddKey(time, rotX.keys[0].value);
rotY.AddKey(time, rotY.keys[0].value);
rotZ.AddKey(time, rotZ.keys[0].value);
rotW.AddKey(time, rotW.keys[0].value);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 582052db0a6096149a7b2b75914ef3a6
timeCreated: 1516263395
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,194 @@
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.humanScale, bodyPositionScaled, bodyRotation);
rightFootQT.SetIKKeyframes(time, animator.avatar, animator.humanScale, bodyPositionScaled, bodyRotation);
leftHandQT.SetIKKeyframes(time, animator.avatar, animator.humanScale, bodyPositionScaled, bodyRotation);
rightHandQT.SetIKKeyframes(time, animator.avatar, 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)
{
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][SerializeField] 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
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 10875975faa2a3c4f815508f7f376c21
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,176 @@
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();
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("loop"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("animationStates"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("appendName"));
break;
case Baker.Mode.PlayableDirector:
EditorGUILayout.PropertyField(serializedObject.FindProperty("loop"));
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 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: 100c77ad1e0b943c5b6e6e67b5649801
folderAsset: yes
timeCreated: 1436186340
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,63 @@
using UnityEditor;
using UnityEngine;
namespace RootMotion.Dynamics {
// Custom drawer for BehaviourPuppet MasterProps.
[CustomPropertyDrawer (typeof (BehaviourPuppet.MasterProps))]
public class BehaviourPuppetMasterPropsDrawer : PropertyDrawer {
private float lineHeight = 22f;
public override float GetPropertyHeight(SerializedProperty prop, GUIContent label) {
lineHeight = base.GetPropertyHeight(prop, label);
int enumValueIndex = prop.FindPropertyRelative("normalMode").enumValueIndex;
switch(enumValueIndex) {
case 1: return lineHeight * 2;
case 2: return lineHeight * 3;
default: return lineHeight;
}
}
private void NextLine(ref Rect pos) {
pos = EditorGUI.IndentedRect(pos);
pos.width -= pos.x - pos.x;
pos.x = pos.x;
pos.y += lineHeight;
pos.height = lineHeight;
}
public override void OnGUI (Rect pos, SerializedProperty prop, GUIContent label) {
int indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField (new Rect(pos.x, pos.y, pos.width, lineHeight), prop.FindPropertyRelative ("normalMode"), new GUIContent("Normal Mode", "Active mode keeps the puppet and it's mapping active at all times. " +
"Unmapped mode blends in mapping only if the puppet becomes in contact with something and blends out again later to maintain 100% animation accuracy while not in contact. " +
"If Kinematic, will set the PuppetMaster.mode to Active when any of the muscles collides with something (static colliders are ignored if 'Activate On Static Collisions' is false) or is hit. PuppetMaster.mode will be set to Kinematic when all muscles are pinned and the puppet is in normal Puppet state. This will increase performance and enable you to have full animation accuracy when PuppetMaster is not needed. Note that collision events are only broadcasted if one of the Rigidbodies is not kinematic, so if you need 2 characters to wake up each other, one has to be active."));
int enumValueIndex = prop.FindPropertyRelative("normalMode").enumValueIndex;
switch(enumValueIndex) {
case 1:
NextLine(ref pos);
EditorGUI.PropertyField (pos, prop.FindPropertyRelative ("mappingBlendSpeed"), new GUIContent("Mapping Blend Speed", "The speed of blending in mapping in case of contact."));
break;
case 2:
NextLine(ref pos);
EditorGUI.PropertyField (pos, prop.FindPropertyRelative ("activateOnStaticCollisions"), new GUIContent("Activate On Static Collisions", "The speed of blending in mapping in case of contact."));
NextLine(ref pos);
EditorGUI.PropertyField (pos, prop.FindPropertyRelative ("activateOnImpulse"), new GUIContent("Activate On Impulse", "Minimum collision impulse for activating the puppet."));
break;
default: break;
}
EditorGUI.EndChangeCheck();
EditorGUI.indentLevel = indent;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 78a5ae1e71291429f8916d4c02a498b7
timeCreated: 1452015016
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,254 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion.Dynamics
{
[CustomEditor(typeof(PropMuscle))]
public class PropMuscleInspector : Editor
{
private PropMuscle script { get { return target as PropMuscle; } }
private GUIStyle style = new GUIStyle();
private GUIStyle miniLabelStyle = new GUIStyle();
private static Color pro = new Color(0.5f, 0.7f, 0.3f, 1f);
private static Color free = new Color(0.2f, 0.3f, 0.1f, 1f);
private static Color sceneColor = new Color(0.2f, 0.7f, 1f);
public override void OnInspectorGUI()
{
if (script == null) return;
serializedObject.Update();
style.wordWrap = true;
style.normal.textColor = EditorGUIUtility.isProSkin ? pro : free;
miniLabelStyle.wordWrap = true;
miniLabelStyle.fontSize = 10;
miniLabelStyle.normal.textColor = EditorStyles.miniLabel.normal.textColor;
if (!Application.isPlaying)
{
if (GUILayout.Button("Remove This Prop Muscle"))
{
//int index = script.muscle.index;
//Debug.Log(index);
var puppetMasterS = new SerializedObject(script.puppetMaster);
SerializedProperty muscles = puppetMasterS.FindProperty("muscles");
int index = 0;
ConfigurableJoint joint = script.GetComponent<ConfigurableJoint>();
for (int i = 0; i < puppetMasterS.FindProperty("muscles").arraySize; i++)
{
muscles.GetArrayElementAtIndex(i).FindPropertyRelative("index").intValue = i;
if (muscles.GetArrayElementAtIndex(i).FindPropertyRelative("joint").objectReferenceValue == joint) index = i;
}
/*
if (script.muscle.additionalPin != null)
{
script.muscle.additionalPin.gameObject.hideFlags = HideFlags.None;
script.muscle.additionalPinTarget.gameObject.hideFlags = HideFlags.None;
}
script.muscle.target.gameObject.hideFlags = HideFlags.None;
*/
var target = script.muscle.target;
muscles.DeleteArrayElementAtIndex(index);
for (int i = 0; i < puppetMasterS.FindProperty("muscles").arraySize; i++)
{
muscles.GetArrayElementAtIndex(i).FindPropertyRelative("index").intValue = i;
}
puppetMasterS.ApplyModifiedProperties();
PuppetMasterInspector.DestroyImmediateSafe(target.gameObject);
PuppetMasterInspector.DestroyImmediateSafe(script.gameObject);
return;
}
}
DrawDefaultInspector();
//GUILayout.Space(10);
if (!Application.isPlaying)
{
if (script.muscle.additionalPin == null)
{
if (GUILayout.Button("Add Additional Pin"))
{
AddAdditionalPin();
}
}
else
{
if (GUILayout.Button("Remove Additional Pin"))
{
RemoveAdditionalPin();
}
}
EditorGUILayout.LabelField("Additional Pins help to pin this prop muscle to animation from another point. Without it, the prop muscle would be actuated by Muscle Spring only, which is likely not enough to follow fast swinging animations. Move the additional pin by 'Additional Pin Offset' towards the end of the prop (tip of the sword).", miniLabelStyle);
}
serializedObject.ApplyModifiedProperties();
}
private bool AddAdditionalPin()
{
if (script.muscle.additionalPin != null) return false;
ConfigurableJoint additionalJoint = null;
Transform additionalTarget = null;
AddAdditionalPinUnserialized(script, out additionalJoint, out additionalTarget);
var puppetMasterS = new SerializedObject(script.puppetMaster);
puppetMasterS.FindProperty("muscles").GetArrayElementAtIndex(script.muscle.index).FindPropertyRelative("additionalPin").objectReferenceValue = additionalJoint;
puppetMasterS.FindProperty("muscles").GetArrayElementAtIndex(script.muscle.index).FindPropertyRelative("additionalPinTarget").objectReferenceValue = additionalTarget;
puppetMasterS.ApplyModifiedProperties();
serializedObject.ApplyModifiedProperties();
//muscle.additionalPin.gameObject.hideFlags = HideFlags.HideInHierarchy;
//muscle.additionalPinTarget.gameObject.hideFlags = HideFlags.HideInHierarchy;
return true;
}
public static void AddAdditionalPinUnserialized(PropMuscle script, out ConfigurableJoint joint, out Transform target)
{
var addGO = new GameObject("Additional Pin");
addGO.gameObject.layer = script.gameObject.layer;
addGO.transform.parent = script.transform;
addGO.transform.localPosition = script.additionalPinOffset;
addGO.transform.localRotation = Quaternion.identity;
//script.lastAdditionalPinOffset = additionalPinOffset;
addGO.AddComponent<Rigidbody>();
joint = addGO.AddComponent<ConfigurableJoint>();
joint.connectedBody = script.muscle.joint.GetComponent<Rigidbody>();
joint.autoConfigureConnectedAnchor = false;
joint.connectedAnchor = script.additionalPinOffset;
joint.xMotion = ConfigurableJointMotion.Locked;
joint.yMotion = ConfigurableJointMotion.Locked;
joint.zMotion = ConfigurableJointMotion.Locked;
joint.angularXMotion = ConfigurableJointMotion.Locked;
joint.angularYMotion = ConfigurableJointMotion.Locked;
joint.angularZMotion = ConfigurableJointMotion.Locked;
var addTargetGO = new GameObject("Additional Pin Target");
addTargetGO.layer = script.muscle.target.gameObject.layer;
addTargetGO.transform.parent = script.muscle.target;
addTargetGO.transform.position = addGO.transform.position;
addTargetGO.transform.rotation = addGO.transform.rotation;
target = addTargetGO.transform;
}
private bool RemoveAdditionalPin()
{
if (script.muscle.additionalPin == null) return false;
//script.muscle.additionalPin.gameObject.hideFlags = HideFlags.None;
//script.muscle.additionalPinTarget.gameObject.hideFlags = HideFlags.None;
PuppetMasterInspector.DestroyImmediateSafe(script.muscle.additionalPin.gameObject);
PuppetMasterInspector.DestroyImmediateSafe(script.muscle.additionalPinTarget.gameObject);
var puppetMasterS = new SerializedObject(script.puppetMaster);
puppetMasterS.FindProperty("muscles").GetArrayElementAtIndex(script.muscle.index).FindPropertyRelative("additionalPin").objectReferenceValue = null;
puppetMasterS.FindProperty("muscles").GetArrayElementAtIndex(script.muscle.index).FindPropertyRelative("additionalPinTarget").objectReferenceValue = null;
puppetMasterS.ApplyModifiedProperties();
return true;
}
private static float GetHandleSize(Vector3 position)
{
float s = HandleUtility.GetHandleSize(position) * 0.1f;
return Mathf.Lerp(s, 0.025f, 0.2f);
}
void OnSceneGUI() {
DrawScene(script);
}
private static void CubeCapSafe(Vector3 position, Quaternion rotation, float size)
{
#if UNITY_5_6_OR_NEWER
Handles.CubeHandleCap(0, position, rotation, size, EventType.Repaint);
#else
Handles.CubeCap(0, position, rotation, size);
#endif
}
private static void SphereCapSafe(Vector3 position, Quaternion rotation, float size)
{
#if UNITY_5_6_OR_NEWER
Handles.SphereHandleCap(0, position, rotation, size, EventType.Repaint);
#else
Handles.SphereCap(0, position, rotation, size);
#endif
}
public static void DrawScene(PropMuscle script)
{
if (script == null) return;
if (Application.isPlaying) return;
GUIStyle sceneLabelStyle = new GUIStyle();
sceneLabelStyle.wordWrap = false;
sceneLabelStyle.normal.textColor = sceneColor;
Handles.color = sceneColor;
float size = GetHandleSize(script.transform.position);
if (Selection.activeGameObject == script.gameObject)
{
Handles.Label(script.transform.position + script.transform.forward * size * 2f, new GUIContent(script.transform.name), sceneLabelStyle);
} else
{
Handles.Label(script.transform.position + script.transform.forward * size * 2f, new GUIContent("Click to select Prop Muscle"), sceneLabelStyle);
}
if (Selection.activeGameObject == script.gameObject)
{
CubeCapSafe(script.transform.position, script.transform.rotation, size * 2f);
} else
{
if (DotButton(script.transform.position, script.transform.rotation, size, size))
{
Selection.activeGameObject = script.gameObject;
}
}
if (script.muscle.additionalPin != null)
{
Handles.DrawLine(script.transform.position, script.muscle.additionalPin.transform.position);
SphereCapSafe(script.muscle.additionalPin.transform.position, script.muscle.additionalPin.transform.rotation, size);
if (Selection.activeGameObject == script.gameObject)
{
Handles.Label(script.muscle.additionalPin.transform.position + script.transform.forward * size * 2f, new GUIContent("Additional Pin"), sceneLabelStyle);
}
}
Handles.color = Color.white;
}
public static bool DotButton(Vector3 position, Quaternion direction, float size, float pickSize)
{
#if UNITY_5_6_OR_NEWER
return Handles.Button(position, direction, size, pickSize, Handles.DotHandleCap);
#else
return Handles.Button(position, direction, size, pickSize, Handles.DotCap);
#endif
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2d0583daa68217f49b271ee02c9374f5
timeCreated: 1550562021
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,21 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
namespace RootMotion.Dynamics {
[CustomEditor(typeof(PuppetMasterHumanoidConfig))]
public class PuppetMasterHumanoidConfigInspector : Editor {
private PuppetMasterHumanoidConfig script { get { return target as PuppetMasterHumanoidConfig; } }
public override void OnInspectorGUI() {
foreach (PuppetMasterHumanoidConfig.HumanoidMuscle m in script.muscles) {
m.name = m.bone.ToString();
}
DrawDefaultInspector ();
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f97a5dd098e6d624caf82f29ed6896d2
timeCreated: 1485252527
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,437 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
namespace RootMotion.Dynamics
{
[CustomEditor(typeof(PuppetMaster))]
public class PuppetMasterInspector : Editor
{
private PuppetMaster script { get { return target as PuppetMaster; } }
private Transform animatedCharacter;
private bool isValid;
private int characterControllerLayer = 8;
private int ragdollLayer = 9;
private ConfigurableJoint addPropMuscleTo;
private MonoScript monoScript;
private Animator targetAnimator;
private SkinnedMeshRenderer[] skinnedMeshRenderers = new SkinnedMeshRenderer[0];
// Colors
private GUIStyle style = new GUIStyle();
private GUIStyle miniLabelStyle = new GUIStyle();
private static Color pro = new Color(0.5f, 0.7f, 0.3f, 1f);
private static Color free = new Color(0.2f, 0.3f, 0.1f, 1f);
#region Inspector
void OnEnable()
{
if (!Application.isPlaying)
{
monoScript = MonoScript.FromMonoBehaviour(script);
int currentExecutionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (currentExecutionOrder != 10100) MonoImporter.SetExecutionOrder(monoScript, 10100);
// Fix negative collider sizes for ragdolls created before PuppetMaster 0.3
BoxCollider[] boxColliders = script.GetComponentsInChildren<BoxCollider>();
foreach (BoxCollider box in boxColliders)
{
box.size = new Vector3(Mathf.Abs(box.size.x), Mathf.Abs(box.size.y), Mathf.Abs(box.size.z));
}
CapsuleCollider[] capsuleColliders = script.GetComponentsInChildren<CapsuleCollider>();
foreach (CapsuleCollider capsule in capsuleColliders)
{
capsule.height = Mathf.Abs(capsule.height);
capsule.radius = Mathf.Abs(capsule.radius);
}
if (script.transform.parent != null)
{
targetAnimator = script.transform.parent.GetComponentInChildren<Animator>();
}
}
else
{
if (script.initiated) targetAnimator = script.targetAnimator;
}
if (script.transform.parent != null)
{
skinnedMeshRenderers = script.transform.parent.GetComponentsInChildren<SkinnedMeshRenderer>();
}
isValid = IsValid();
}
private bool IsValid()
{
if (script.muscles.Length > 0) return true;
ConfigurableJoint[] joints = script.gameObject.GetComponentsInChildren<ConfigurableJoint>();
if (joints == null || joints.Length == 0)
{
return false;
}
if (animatedCharacter == null) animatedCharacter = script.transform;
return true;
}
public override void OnInspectorGUI()
{
if (script == null) return;
serializedObject.Update();
if (script.muscles.Length > 0 && script.muscles[0].target != null && script.targetRoot == null) script.targetRoot = script.FindTargetRootRecursive(script.muscles[0].target);
style.wordWrap = true;
style.normal.textColor = EditorGUIUtility.isProSkin ? pro : free;
miniLabelStyle.wordWrap = true;
miniLabelStyle.fontSize = 10;
miniLabelStyle.normal.textColor = EditorStyles.miniLabel.normal.textColor;
if (!isValid)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField(
"To set up PuppetMaster, add the component to a ragdoll character that is built with ConfigurableJoints. " +
"If your ragdoll uses other types of joints, you can easily convert them by selecting the character and clicking on GameObject/Convert To ConfigurableJoints."
, style);
EditorGUILayout.Space();
return;
}
if (!Application.isPlaying)
{
for (int i = 0; i < script.muscles.Length; i++)
{
script.muscles[i].index = i;
}
}
foreach (Muscle m in script.muscles)
{
if (m.joint == null)
{
m.name = "Missing Joint Reference!";
}
else if (m.target == null)
{
m.name = "Missing Target Reference!";
}
else m.name = m.joint.name;
}
if (script.muscles.Length == 0)
{
GUILayout.Space(5);
animatedCharacter = (Transform)EditorGUILayout.ObjectField(new GUIContent("Animated Target", "If it is assigned to the same GameObject, it will be duplicated and cleaned up of all the ragdoll components. It can also be an instance of the character or even another character as long as the positions of the bones match. The rotations of the bones don't need to be identical. That makes it possible to share ragdoll structures and is also most useful when the feet bones are not aligned to the ground (like Mixamo characters). In that case you can simply rotate the ragdoll's feet to align, keeping the target's feet where they are."), animatedCharacter, typeof(Transform), true);
GUILayout.Space(5);
characterControllerLayer = EditorGUILayout.IntField(new GUIContent("Character Controller Layer", "The layer to assign the character controller to. Collisions between this layer and the 'Ragdoll Layer' will be ignored, or else the ragdoll would collide with the character controller."), characterControllerLayer);
ragdollLayer = EditorGUILayout.IntField(new GUIContent("Ragdoll Layer", "The layer to assign the PuppetMaster and all it's muscles to. Collisions between this layer and the 'Character Controller Layer' will be ignored, or else the ragdoll would collide with the character controller."), ragdollLayer);
if (characterControllerLayer == ragdollLayer)
{
GUILayout.Space(5);
EditorGUILayout.LabelField(
"The 'Character Controller Layer' must not be the same as the 'Ragdoll Layer', your ragdoll bones would collide with the character controller."
, style);
EditorGUILayout.Space();
}
if (animatedCharacter != null && characterControllerLayer != ragdollLayer)
{
GUILayout.Space(5);
if (GUILayout.Button("Set Up PuppetMaster", GUILayout.MaxWidth(140)))
{
#if UNITY_2018_3_OR_NEWER
if (animatedCharacter == script.transform)
{
if (PrefabUtility.IsPartOfPrefabInstance(animatedCharacter.gameObject)) PrefabUtility.UnpackPrefabInstance(PrefabUtility.GetNearestPrefabInstanceRoot(animatedCharacter.gameObject), PrefabUnpackMode.Completely, InteractionMode.UserAction);
} else
{
if (PrefabUtility.IsPartOfPrefabInstance(script.gameObject)) PrefabUtility.UnpackPrefabInstance(PrefabUtility.GetNearestPrefabInstanceRoot(script.gameObject), PrefabUnpackMode.Completely, InteractionMode.UserAction);
}
#endif
script.SetUpTo(animatedCharacter, characterControllerLayer, ragdollLayer);
animatedCharacter = null;
}
EditorGUILayout.LabelField("Setting up PuppetMaster is not undoable. Make sure to make a backup duplicate of the character before you do that.", miniLabelStyle);
if (animatedCharacter == script.transform)
{
GUILayout.Space(5);
EditorGUILayout.BeginVertical("Box");
GUILayout.Label("NB! 'Animated Target' is set to the same GameObject as this. PuppetMaster will make a duplicate of this GameObject and break the prefab to remove ragdoll components. It is recommended to set up the ragdoll on a new model instance, add PuppetMater to that and assign the original character as it's 'Animated Target'.", miniLabelStyle);
EditorGUILayout.EndVertical();
}
}
GUILayout.Space(5);
}
else
{
// Fix muscle positions and rotations to their targets
if (!Application.isPlaying && script.targetRoot != null)
{
if (script.transform.parent == script.targetRoot.parent)
{
script.transform.localPosition = Vector3.zero;
script.transform.localRotation = Quaternion.identity;
script.targetRoot.localPosition = Vector3.zero;
script.targetRoot.localRotation = Quaternion.identity;
}
foreach (Muscle m in script.muscles)
{
if (m.joint != null && m.target != null && !m.isPropMuscle)
{
m.joint.transform.position = m.target.position;
var r = m.joint.GetComponent<Rigidbody>();
bool hasChild = false;
foreach (Muscle otherMuscle in script.muscles)
{
if (otherMuscle != m && otherMuscle.joint != null && otherMuscle.joint.connectedBody != null)
{
if (otherMuscle.joint.connectedBody == r)
{
hasChild = true;
break;
}
}
}
if (hasChild)
{
m.joint.transform.rotation = m.target.rotation;
}
}
}
}
if (!Application.isPlaying && script.humanoidConfig != null && script.targetRoot != null && script.targetAnimator != null && script.targetAnimator.isHuman)
{
script.humanoidConfig.ApplyTo(script);
GUILayout.Space(5);
EditorGUILayout.BeginVertical("Box");
GUILayout.Label("Properties of this PuppetMaster are locked to the Humanoid Config file.", miniLabelStyle);
EditorGUILayout.EndVertical();
GUILayout.Space(5);
}
script.muscleSpring = Mathf.Clamp(script.muscleSpring, 0f, script.muscleSpring);
script.muscleDamper = Mathf.Clamp(script.muscleDamper, 0f, script.muscleDamper);
DrawDefaultInspector();
GUILayout.Space(10);
// Prop Muscles
GUIStyle largeHeaderStyle = new GUIStyle(GUI.skin.label);
largeHeaderStyle.fontSize = 16;
largeHeaderStyle.fontStyle = FontStyle.Normal;
//largeHeaderStyle.alignment = TextAnchor.LowerLeft;
largeHeaderStyle.alignment = TextAnchor.MiddleLeft;
GUI.color = Color.grey * 0.7f;
EditorGUILayout.LabelField("Prop Muscles", largeHeaderStyle);
GUI.color = Color.white;
EditorGUILayout.BeginHorizontal("Box");
addPropMuscleTo = (ConfigurableJoint)EditorGUILayout.ObjectField(GUIContent.none, addPropMuscleTo, typeof(ConfigurableJoint), true);
if (GUILayout.Button("Add Prop Muscle"))
{
serializedObject.ApplyModifiedProperties();
AddPropMuscle(script, addPropMuscleTo, addPropMuscleTo.transform.position, addPropMuscleTo.transform.rotation, Vector3.forward * 0.5f);
serializedObject.Update();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.LabelField("Prop Muscles are muscles that props (PuppetMasterProp) can be attached to. For example, to put a sword in the right hand, assign the right hand muscle above, click on 'Add Prop Muscle', then move the added muscle to where you would want the sword to be held. To pick up the sword, just assign it as 'Current Prop' in the PropMuscle component on the prop muscle.", miniLabelStyle);
GUILayout.Space(5);
}
bool animatorWarning = targetAnimator != null && targetAnimator.cullingMode != AnimatorCullingMode.AlwaysAnimate;
if (animatorWarning)
{
GUILayout.Space(5);
EditorGUILayout.BeginVertical("Box");
GUILayout.Label("If Animator 'Culling Mode' is not set to 'Always Animate', animation might pause and cause glitches when the ragdoll is out of SkinnedMeshRenderer.bounds.", miniLabelStyle);
EditorGUILayout.EndVertical();
}
bool rendererWarning = false;
foreach (SkinnedMeshRenderer s in skinnedMeshRenderers)
{
if (!s.updateWhenOffscreen)
{
rendererWarning = true;
break;
}
}
if (rendererWarning)
{
if (!animatorWarning) GUILayout.Space(5);
EditorGUILayout.BeginVertical("Box");
GUILayout.Label("One of the SkinnedMeshRenderers in the animated character hierarchy has 'Update When Offscreen' set to false. This might cause it to disappear from view when the ragdoll is out of the renderer's bounds.", miniLabelStyle);
EditorGUILayout.EndVertical();
}
serializedObject.ApplyModifiedProperties();
}
public static void DestroyImmediateSafe(GameObject go)
{
#if UNITY_2018_3_OR_NEWER
if (PrefabUtility.IsPartOfPrefabInstance(go)) PrefabUtility.UnpackPrefabInstance(PrefabUtility.GetNearestPrefabInstanceRoot(go), PrefabUnpackMode.Completely, InteractionMode.UserAction);
#endif
DestroyImmediate(go);
}
public static bool AddPropMuscle(PuppetMaster script, ConfigurableJoint addPropMuscleTo, Vector3 position, Quaternion rotation, Vector3 additionalPinOffset, Transform targetParent = null)
{
var serializedObject = new SerializedObject(script);
if (addPropMuscleTo != null)
{
bool isFlat = script.HierarchyIsFlat();
var addToMuscle = script.GetMuscle(addPropMuscleTo);
if (addToMuscle != null)
{
GameObject go = new GameObject("Prop Muscle " + addPropMuscleTo.name);
go.layer = addPropMuscleTo.gameObject.layer;
go.transform.parent = isFlat ? script.transform : addPropMuscleTo.transform;
go.transform.position = position;
go.transform.rotation = rotation;
var r = go.AddComponent<Rigidbody>();
GameObject target = new GameObject("Prop Muscle Target " + addPropMuscleTo.name);
target.gameObject.layer = addToMuscle.target.gameObject.layer;
target.transform.parent = targetParent == null ? addToMuscle.target : targetParent;
target.transform.position = go.transform.position;
target.transform.rotation = go.transform.rotation;
var joint = go.AddComponent<ConfigurableJoint>();
joint.connectedBody = addToMuscle.joint.gameObject.GetComponent<Rigidbody>();
joint.xMotion = ConfigurableJointMotion.Locked;
joint.yMotion = ConfigurableJointMotion.Locked;
joint.zMotion = ConfigurableJointMotion.Locked;
joint.angularXMotion = ConfigurableJointMotion.Locked;
joint.angularYMotion = ConfigurableJointMotion.Locked;
joint.angularZMotion = ConfigurableJointMotion.Locked;
r.interpolation = joint.connectedBody.interpolation;
int newLength = script.muscles.Length + 1;
int newMuscleIndex = newLength - 1;
SerializedProperty muscles = serializedObject.FindProperty("muscles");
muscles.InsertArrayElementAtIndex(newMuscleIndex);
muscles.GetArrayElementAtIndex(newMuscleIndex).FindPropertyRelative("index").intValue = newMuscleIndex;
muscles.GetArrayElementAtIndex(newMuscleIndex).FindPropertyRelative("joint").objectReferenceValue = joint;
muscles.GetArrayElementAtIndex(newMuscleIndex).FindPropertyRelative("target").objectReferenceValue = target.transform;
muscles.GetArrayElementAtIndex(newMuscleIndex).FindPropertyRelative("props").FindPropertyRelative("group").intValue = 8;
muscles.GetArrayElementAtIndex(newMuscleIndex).FindPropertyRelative("isPropMuscle").boolValue = true;
serializedObject.ApplyModifiedProperties();
var propMuscle = joint.gameObject.AddComponent<PropMuscle>();
var propMuscleS = new SerializedObject(propMuscle);
propMuscleS.FindProperty("puppetMaster").objectReferenceValue = script;
propMuscleS.FindProperty("additionalPinOffset").vector3Value = additionalPinOffset;
propMuscleS.ApplyModifiedProperties();
if (additionalPinOffset != Vector3.zero)
{
propMuscleS.Update();
ConfigurableJoint additionalJoint = null;
Transform additionalTarget = null;
PropMuscleInspector.AddAdditionalPinUnserialized(propMuscle, out additionalJoint, out additionalTarget);
muscles.GetArrayElementAtIndex(newMuscleIndex).FindPropertyRelative("additionalPin").objectReferenceValue = additionalJoint;
muscles.GetArrayElementAtIndex(newMuscleIndex).FindPropertyRelative("additionalPinTarget").objectReferenceValue = additionalTarget;
propMuscleS.ApplyModifiedProperties();
}
serializedObject.ApplyModifiedProperties();
// TODO Do not add if addPropMuscleTo already has a PropMuscle
return true;
}
else
{
Debug.LogError("Can't add Prop Muscle to a ConfigurableJoint that is not in the list of PuppetMaster.muscles.", script.transform);
return false;
}
}
else
{
Debug.LogError("Please assign the ConfigurableJoint of the muscle you wish to add the Prop Muscle to.", script.transform);
return false;
}
}
private bool IsRagdoll()
{
Rigidbody[] rigidbodies = (Rigidbody[])script.gameObject.GetComponentsInChildren<Rigidbody>();
foreach (Rigidbody r in rigidbodies)
{
if (r != script.GetComponent<Rigidbody>()) return true;
}
return false;
}
#endregion Inspector
#region Scene
void OnSceneGUI()
{
if (script == null) return;
if (Application.isPlaying) return;
foreach (Muscle m in script.muscles)
{
if (m.joint != null)
{
var propMuscle = m.joint.GetComponent<PropMuscle>();
if (propMuscle != null)
{
PropMuscleInspector.DrawScene(propMuscle);
}
}
}
Handles.color = Color.white;
}
#endregion Scene
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d63c5bd59fb5541eeb1098d1f4a6d01c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,37 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion.Dynamics
{
[CustomEditor(typeof(PuppetMasterProp))]
public class PuppetMasterPropInspector : Editor
{
private PuppetMasterProp script { get { return target as PuppetMasterProp; } }
private GUIStyle style = new GUIStyle();
private GUIStyle miniLabelStyle = new GUIStyle();
private static Color pro = new Color(0.5f, 0.7f, 0.3f, 1f);
private static Color free = new Color(0.2f, 0.3f, 0.1f, 1f);
public override void OnInspectorGUI()
{
if (script == null) return;
serializedObject.Update();
style.wordWrap = true;
style.normal.textColor = EditorGUIUtility.isProSkin ? pro : free;
miniLabelStyle.wordWrap = true;
miniLabelStyle.fontSize = 10;
miniLabelStyle.normal.textColor = EditorStyles.miniLabel.normal.textColor;
DrawDefaultInspector();
script.muscleProps.group = Muscle.Group.Prop;
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a7f9c20921f4b1942b1f7b410dd66836
timeCreated: 1550561801
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
using UnityEngine;
using UnityEditor;
namespace RootMotion.Dynamics {
[CustomPropertyDrawer (typeof (Weight))]
public class WeightDrawer : PropertyDrawer {
const int valueWidth = 30;
const int gap = 20;
const float min = 0;
const float max = 1;
public override void OnGUI (Rect pos, SerializedProperty prop, GUIContent label) {
SerializedProperty mode = prop.FindPropertyRelative("mode");
SerializedProperty floatValue = prop.FindPropertyRelative ("floatValue");
SerializedProperty curve = prop.FindPropertyRelative ("curve");
SerializedProperty tooltip = prop.FindPropertyRelative("tooltip");
label.tooltip = tooltip.stringValue;
Rect left = new Rect(pos.x, pos.y, pos.width - valueWidth - gap, pos.height);
Rect right = new Rect (pos.width - valueWidth, pos.y, valueWidth, pos.height);
Weight.Mode m = (Weight.Mode)mode.enumValueIndex;
if (m == Weight.Mode.Float) {
floatValue.floatValue = EditorGUI.FloatField(left, label, floatValue.floatValue);
} else {
// Draw curve
int indent = EditorGUI.indentLevel;
EditorGUI.PropertyField (left, curve, label);
EditorGUI.indentLevel = indent;
}
int i = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
m = (Weight.Mode)EditorGUI.EnumPopup(right, GUIContent.none, ( Weight.Mode)mode.enumValueIndex, EditorStyles.miniBoldLabel);
mode.enumValueIndex = (int)m;
EditorGUI.indentLevel = i;
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f882d03cc62ed40c7b8febce6ef4a0de
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 98d02ab32f6fa4551b1983c76cea4586
folderAsset: yes
timeCreated: 1452250914
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,75 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using RootMotion.Dynamics;
namespace RootMotion.Demos {
[CustomEditor(typeof(CharacterMeleeDemo))]
public class CharacterMeleeDemoInspector : Editor {
private CharacterMeleeDemo script { get { return target as CharacterMeleeDemo; }}
private GameObject replace;
private static Color pro = new Color(0.7f, 0.9f, 0.5f, 1f);
private static Color free = new Color(0.4f, 0.5f, 0.3f, 1f);
public override void OnInspectorGUI() {
GUI.changed = false;
if (!Application.isPlaying) {
GUI.color = EditorGUIUtility.isProSkin? pro: free;
EditorGUILayout.BeginHorizontal();
replace = (GameObject)EditorGUILayout.ObjectField("Replace Character Model", replace, typeof(GameObject), true);
if (replace != null) {
if (GUILayout.Button("Replace")) {
Debug.Log("PropMuscle needs to be set up manually and assigned as 'Prop Muscle' in the CharacterMeleeDemo component on the character.");
/* TODO Update to PropMuscle
PropRoot propRoot = script.propRoot;
Vector3 localPosition = propRoot.transform.localPosition;
Quaternion localRotation = propRoot.transform.localRotation;
propRoot.transform.parent = null;
CharacterPuppetInspector.ReplacePuppetModel(script as CharacterThirdPerson, replace);
Animator animator = script.characterAnimation.GetComponent<Animator>();
PuppetMaster puppetMaster = script.transform.parent.GetComponentInChildren<PuppetMaster>();
propRoot.transform.parent = animator.GetBoneTransform(HumanBodyBones.RightHand);
propRoot.transform.localPosition = localPosition;
propRoot.transform.localRotation = localRotation;
propRoot.puppetMaster = puppetMaster;
propRoot.connectTo = GetRigidbody(puppetMaster, animator.GetBoneTransform(HumanBodyBones.RightLowerArm));
Debug.Log("You probably need to adjust the localPosition and localRotation of the Prop Root to match this character's hand.");
*/
UserControlAI[] userControls = (UserControlAI[])GameObject.FindObjectsOfType<UserControlAI>();
foreach (UserControlAI ai in userControls) {
if (ai.moveTarget == null) {
ai.moveTarget = script.transform.parent.GetComponentInChildren<PuppetMaster>().muscles[0].joint.transform;
}
}
}
}
EditorGUILayout.EndHorizontal();
GUI.color = Color.white;
}
DrawDefaultInspector();
if (GUI.changed) EditorUtility.SetDirty(script);
}
private Rigidbody GetRigidbody(PuppetMaster puppetMaster, Transform target) {
foreach (Muscle m in puppetMaster.muscles) {
if (m.target == target) return m.joint.GetComponent<Rigidbody>();
}
return null;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5c5c74fcc11b441abb014b8119cb9e5f
timeCreated: 1452255675
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,176 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using RootMotion.Dynamics;
namespace RootMotion.Demos {
[CustomEditor(typeof(CharacterPuppet))]
public class CharacterPuppetInspector : Editor {
private CharacterPuppet script { get { return target as CharacterPuppet; }}
private GameObject replace;
private static Color pro = new Color(0.7f, 0.9f, 0.5f, 1f);
private static Color free = new Color(0.4f, 0.5f, 0.3f, 1f);
public override void OnInspectorGUI() {
GUI.changed = false;
if (!Application.isPlaying) {
GUI.color = EditorGUIUtility.isProSkin? pro: free;
EditorGUILayout.BeginHorizontal();
replace = (GameObject)EditorGUILayout.ObjectField("Replace Character Model", replace, typeof(GameObject), true);
if (replace != null) {
if (GUILayout.Button("Replace")) {
ReplacePuppetModel(script as CharacterThirdPerson, replace);
replace = null;
}
}
GUI.color = Color.white;
EditorGUILayout.EndHorizontal();
}
DrawDefaultInspector();
if (GUI.changed) EditorUtility.SetDirty(script);
}
public static void ReplacePuppetModel(CharacterThirdPerson script, GameObject replace) {
// Drag your Humanoid character to the scene, parent it to the "Animation Controller" just where the Pilot is
GameObject find = GameObject.Find(replace.name);
bool isSceneObject = find != null && find == replace;
GameObject instance = isSceneObject? replace: (GameObject)GameObject.Instantiate(replace, script.transform.position, script.transform.rotation);
instance.transform.parent = script.characterAnimation.transform;
instance.name = replace.name;
instance.transform.position = script.transform.position;
instance.transform.rotation = script.transform.rotation;
Animator animator = instance.GetComponentInChildren<Animator>();
bool isAnimator = animator != null;
if (!isAnimator || !animator.isHuman) {
Debug.LogWarning("The demo character controller requires a Humanoid character.");
return;
}
SkinnedMeshRenderer[] skinnedMeshRenders = instance.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (SkinnedMeshRenderer s in skinnedMeshRenders) s.updateWhenOffscreen = true;
// Unparent "Character Controller" from the "ThirdPersonPuppet"
Transform oldRoot = script.transform.parent;
PuppetMaster oldPM = script.transform.parent.GetComponentInChildren<PuppetMaster>();
RigidbodyInterpolation oldInterpolation = oldPM.muscles[0].joint.GetComponent<Rigidbody>().interpolation;
BehaviourBase[] behaviours = script.transform.parent.GetComponentsInChildren<BehaviourBase>();
Camera camera = script.transform.parent.GetComponentInChildren<Camera>();
CameraController cameraController = null;
if (camera != null) cameraController = camera.gameObject.GetComponent<CameraController>();
LayerSetup layerSetup = oldPM.GetComponent<LayerSetup>();
script.transform.parent = null;
// Delete the "Pilot"
DestroyImmediate(script.characterAnimation.transform.GetChild(0).gameObject);
// Replace the Avatar in the Animator component of the "Animation Controller" gameobject.
Animator characterAnimator = script.characterAnimation.GetComponent<Animator>();
characterAnimator.avatar = animator.avatar;
// Add BipedRagdollCreator to the "Animation Controller", use it to create a ragdoll, delete the component when done
Joint joint = instance.GetComponentInChildren<Joint>();
bool isRagdoll = joint != null;
bool isConfigurable = isRagdoll && joint is ConfigurableJoint;
if (isRagdoll && !isConfigurable) {
JointConverter.ToConfigurable(instance);
}
if (!isRagdoll) {
BipedRagdollReferences r = BipedRagdollReferences.FromAvatar(animator);
BipedRagdollCreator.Options options = BipedRagdollCreator.AutodetectOptions(r);
// Edit options here if you need to
BipedRagdollCreator.Create(r, options);
}
// Delete the Animator from your character root (not the one on "Animation Controller")
DestroyImmediate(animator);
// Add PuppetMaster to the "Character Controller", click on Set Up PuppetMaster.
int cLayer = layerSetup != null? layerSetup.characterControllerLayer: script.gameObject.layer;
int rLayer = layerSetup != null? layerSetup.ragdollLayer: oldPM.muscles[0].joint.gameObject.layer;
PuppetMaster newPuppetMaster = PuppetMaster.SetUp(script.transform, cLayer, rLayer);
foreach (Muscle m in newPuppetMaster.muscles) {
m.joint.GetComponent<Rigidbody>().interpolation = oldInterpolation;
}
// Move the "Puppet" and "Fall" behaviours from the old rig to the new one (parent them to the "Behaviours").
Transform behaviourRoot = newPuppetMaster.transform.parent.Find("Behaviours");
Transform oldBehaviourRoot = behaviours.Length > 0? behaviours[0].transform.parent: FindChildNameContaining(oldRoot, "Behaviours");
foreach (BehaviourBase behaviour in behaviours) behaviour.transform.parent = behaviourRoot;
if (oldBehaviourRoot != null) DestroyImmediate(oldBehaviourRoot.gameObject);
// 9. Move the "Character Camera" to the new rig, replace the "Target" of the CharacterController component
if (camera != null) camera.transform.parent = newPuppetMaster.transform.parent;
if (cameraController != null) cameraController.target = newPuppetMaster.muscles[0].joint.transform;
// THIS IS NOT REQUIRED SINCE 0.3 Use the RagdollEditor (add it to the PuppetMaster) to multiply the mass of the ragdoll by 10
// 10. Copy the LayerSetup component from the old PuppetMaster to the new one if you have not set up the layers in the Layer Collision Matrix.
if (layerSetup != null) {
LayerSetup newLayerSetup = newPuppetMaster.gameObject.AddComponent<LayerSetup>();
if (newLayerSetup != null) {
newLayerSetup.characterController = layerSetup.characterController;
newLayerSetup.characterControllerLayer = layerSetup.characterControllerLayer;
newLayerSetup.ragdollLayer = layerSetup.ragdollLayer;
newLayerSetup.ignoreCollisionWithCharacterController = layerSetup.ignoreCollisionWithCharacterController;
}
}
// 11. Make sure the new PuppetMaster settings match with the old one, delete the old rig
newPuppetMaster.state = oldPM.state;
newPuppetMaster.stateSettings = oldPM.stateSettings;
newPuppetMaster.mode = oldPM.mode;
newPuppetMaster.blendTime = oldPM.blendTime;
newPuppetMaster.fixTargetTransforms = oldPM.fixTargetTransforms;
newPuppetMaster.solverIterationCount = oldPM.solverIterationCount;
newPuppetMaster.visualizeTargetPose = oldPM.visualizeTargetPose;
newPuppetMaster.mappingWeight = oldPM.mappingWeight;
newPuppetMaster.pinWeight = oldPM.pinWeight;
newPuppetMaster.muscleWeight = oldPM.muscleWeight;
newPuppetMaster.muscleSpring = oldPM.muscleSpring;
newPuppetMaster.muscleDamper = oldPM.muscleDamper;
newPuppetMaster.pinPow = oldPM.pinPow;
newPuppetMaster.pinDistanceFalloff = oldPM.pinDistanceFalloff;
newPuppetMaster.updateJointAnchors = oldPM.updateJointAnchors;
newPuppetMaster.supportTranslationAnimation = oldPM.supportTranslationAnimation;
newPuppetMaster.angularLimits = oldPM.angularLimits;
newPuppetMaster.internalCollisions = oldPM.internalCollisions;
// 12 Parent this CharacterPuppet to the new puppet root
script.transform.parent = newPuppetMaster.transform.parent;
// 13. Delete the old root
script.transform.parent.name = oldRoot.name;
for (int i = 0; i < oldRoot.childCount; i++) {
var child = oldRoot.GetChild(i);
if (child != oldPM.transform && child != behaviourRoot) {
child.parent = script.transform.parent;
}
}
DestroyImmediate(oldRoot.gameObject);
}
private static Transform FindChildNameContaining(Transform t, string name) {
for (int i = 0; i < t.childCount; i++) {
if (t.GetChild(i).name.Contains(name)) return t.GetChild(i);
}
return null;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d86142a0934834027a39b8a2e105c009
timeCreated: 1452250930
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e216fee95a1f04dd38a1e457aed7617a
folderAsset: yes
timeCreated: 1436186405
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,168 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using RootMotion;
namespace RootMotion.Dynamics {
[CustomEditor(typeof(BipedRagdollCreator))]
public class BipedRagdollCreatorInspector : Editor {
public BipedRagdollCreator script { get { return target as BipedRagdollCreator; }}
private GUIStyle miniLabelStyle = new GUIStyle();
void OnEnable() {
if (script == null) return;
//if (Application.isPlaying) return;
// Autodetection
if (script.references.IsEmpty(false)) {
Animator animator = script.gameObject.GetComponent<Animator>();
if (animator == null && script.references.root != null) {
animator = script.references.root.GetComponentInChildren<Animator>();
if (animator == null) animator = GetAnimatorInParents(script.references.root);
}
if (animator != null) {
script.references = BipedRagdollReferences.FromAvatar(animator);
} else {
BipedReferences r = new BipedReferences();
BipedReferences.AutoDetectReferences(ref r, script.transform, BipedReferences.AutoDetectParams.Default);
if (r.isFilled) script.references = BipedRagdollReferences.FromBipedReferences(r);
}
if (!OnRoot()) {
Debug.LogWarning("BipedRagdollCreator must be added to the root of the character. Destroying the component.");
DestroyImmediate(script);
return;
}
string msg = string.Empty;
if (script.references.IsValid(ref msg)) {
script.options = BipedRagdollCreator.AutodetectOptions(script.references);
//BipedRagdollCreator.Create(script.references, script.options);
//if (animator != null) DestroyImmediate(animator);
//if (script.GetComponent<Animation>() != null) DestroyImmediate(script.GetComponent<Animation>());
}
}
}
private bool OnRoot() {
if (script.references.Contains(script.transform, true)) return false;
if (script.references.root != null) {
var bipedRagdollCreatorOnChild = script.references.root.GetComponentInChildren<BipedRagdollCreator>();
if (bipedRagdollCreatorOnChild != null && bipedRagdollCreatorOnChild.transform != script.references.root) return false;
}
return true;
}
public override void OnInspectorGUI() {
miniLabelStyle.wordWrap = true;
miniLabelStyle.fontSize = 9;
miniLabelStyle.normal.textColor = EditorStyles.miniLabel.normal.textColor;
serializedObject.Update();
//if (Application.isPlaying) {
// GUILayout.BeginVertical("Box");
// GUILayout.Label("Can not edit ragdolls in play mode.");
// GUILayout.EndVertical();
// return;
//}
GUI.changed = false;
GUILayout.Space(10);
EditorGUILayout.BeginVertical("Box");
int indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(serializedObject.FindProperty("references"), new GUIContent("References", "Definition of the biped ragdoll."), true);
if (GUI.changed) {
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(script);
}
string msg = string.Empty;
bool referencesValid = script.references.IsValid(ref msg);
if (serializedObject.FindProperty("references").isExpanded) GUILayout.Space(5);
EditorGUILayout.EndVertical();
if (referencesValid) {
if (!script.canBuild) {
UnityEditor.EditorApplication.isPaused = true;
GUILayout.Space(5);
if (GUILayout.Button("Create a Ragdoll")) {
script.canBuild = true;
}
GUILayout.Label("Clears all existing physics components, creates a new ragdoll and starts live-updating. NB! THIS CAN NOT BE UNDONE!", miniLabelStyle);
GUILayout.Space(5);
}
if (script.canBuild) {
GUI.changed = false;
//GUILayout.Space(5);
EditorGUILayout.BeginVertical("Box");
EditorGUILayout.PropertyField(serializedObject.FindProperty("options"), new GUIContent("Options", "Options for automatic ragdoll generation."), true);
if (GUI.changed) {
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(script);
}
if (serializedObject.FindProperty("options").isExpanded) GUILayout.Space(5);
EditorGUILayout.EndVertical();
BipedRagdollCreator.Create(script.references, script.options);
GUILayout.Space(10);
if (GUILayout.Button("Done")) {
UnityEditor.EditorApplication.isPaused = false;
EditorGUI.indentLevel = indent;
DestroyImmediate(script);
return;
}
GUILayout.Label("Removes this component.", miniLabelStyle);
if (GUILayout.Button("Start Editing Manually")) {
script.gameObject.AddComponent<RagdollEditor>();
EditorGUI.indentLevel = indent;
DestroyImmediate(script);
return;
}
GUILayout.Label("Replaces this component with the RagdollEditor.", miniLabelStyle);
GUILayout.Space(10);
}
} else {
GUILayout.Space(10);
GUILayout.BeginVertical("Box");
GUILayout.Label(msg);
GUILayout.EndVertical();
GUILayout.Space(10);
if (script.canBuild) RagdollCreator.ClearAll(script.transform);
}
EditorGUI.indentLevel = indent;
}
private static Animator GetAnimatorInParents(Transform transform) {
if (transform.GetComponent<Animator>() != null) return transform.GetComponent<Animator>();
if (transform.parent == null) return null;
return GetAnimatorInParents(transform.parent);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3b4d10c84c3764847812025f2e5986e6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,48 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System;
namespace RootMotion.Dynamics {
[CustomEditor(typeof(CharacterJoint))]
public class CharacterJointInspector : JointInspector {
private static Color
highTwistColor = new Color(1, 0.75f, 0, 0.3f),
lowTwistColor = new Color(0.8f, 0.65f, 0, 0.3f),
swing1Color = new Color(0, 1, 0, 0.3f),
swing2Color = new Color(0, 0, 1, 0.3f);
public static void DrawJoint(CharacterJoint joint, bool modifiable = true, float alphaMlp = 1f) {
if (joint == null) return;
Vector3 axis = Vector3.zero;
if (joint.axis != Vector3.zero) axis = joint.axis.normalized;
Vector3 swingAxis = Vector3.zero;
if (joint.swingAxis != Vector3.zero) swingAxis = joint.swingAxis.normalized;
Vector3 crossAxis = swingAxis;
if (swingAxis != axis) crossAxis = Vector3.Cross(swingAxis, axis);
float newLowTwistLimit = DrawJointLimit(joint as Joint, "Low Twist Limit", axis, joint.lowTwistLimit.limit, MlpAlpha(lowTwistColor, alphaMlp), -25, modifiable);
joint.lowTwistLimit = NewJointLimit(newLowTwistLimit, joint.lowTwistLimit, -180, 180);
float newHighTwistLimit = joint.highTwistLimit.limit;
if (newLowTwistLimit != 0 || (newLowTwistLimit == 0 && newHighTwistLimit != 0)) newHighTwistLimit = DrawJointLimit(joint as Joint, "High Twist Limit", axis, joint.highTwistLimit.limit, MlpAlpha(highTwistColor, alphaMlp), 25, modifiable);
joint.highTwistLimit = NewJointLimit(newHighTwistLimit, joint.highTwistLimit, -180, 180);
float newSwing1Limit = DrawJointLimit(joint as Joint, "Swing1 Limit", swingAxis, -joint.swing1Limit.limit, MlpAlpha(swing1Color, alphaMlp), 25, false);
newSwing1Limit = DrawJointLimit(joint as Joint, "Swing1 Limit", swingAxis, -newSwing1Limit, MlpAlpha(swing1Color, alphaMlp), 25, modifiable);
joint.swing1Limit = NewJointLimit(newSwing1Limit, joint.swing1Limit, 0, 180);
float newSwing2Limit = DrawJointLimit(joint as Joint, "Swing2 Limit", crossAxis, -joint.swing2Limit.limit, MlpAlpha(swing2Color, alphaMlp), 25, false);
newSwing2Limit = DrawJointLimit(joint as Joint, "Swing2 Limit", crossAxis, -newSwing2Limit, MlpAlpha(swing2Color, alphaMlp), 25, modifiable);
joint.swing2Limit = NewJointLimit(newSwing2Limit, joint.swing2Limit, 0, 180);
}
public void OnSceneGUI() {
DrawJoint(target as CharacterJoint);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: df75c9b4717ec4cf996ae3055c26373e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,250 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion.Dynamics {
// Tools for editing Colliders.
public class ColliderTools: Editor {
public static void ConvertToSphereCollider(CapsuleCollider capsule) {
SphereCollider sphere = Undo.AddComponent(capsule.gameObject, typeof(SphereCollider)) as SphereCollider;
sphere.center = capsule.center;
sphere.radius = capsule.radius;
Undo.DestroyObjectImmediate(capsule);
}
public static void ConvertToSphereCollider(BoxCollider box) {
SphereCollider sphere = Undo.AddComponent(box.gameObject, typeof(SphereCollider)) as SphereCollider;
sphere.center = box.center;
sphere.radius = Mathf.Max(new float[3] { box.size.x, box.size.y, box.size.z }) * 0.5f;
Undo.DestroyObjectImmediate(box);
}
public static void ConvertToBoxCollider(SphereCollider sphere) {
BoxCollider box = Undo.AddComponent(sphere.gameObject, typeof(BoxCollider)) as BoxCollider;
box.center = sphere.center;
box.size = Vector3.one * sphere.radius * 2f;
Undo.DestroyObjectImmediate(sphere);
}
public static void ConvertToCapsuleCollider(SphereCollider sphere) {
CapsuleCollider capsule = Undo.AddComponent(sphere.gameObject, typeof(CapsuleCollider)) as CapsuleCollider;
capsule.center = sphere.center;
capsule.radius = sphere.radius;
capsule.height = sphere.radius;
Undo.DestroyObjectImmediate(sphere);
}
public static void ConvertToBoxCollider(CapsuleCollider capsule) {
BoxCollider box = Undo.AddComponent(capsule.gameObject, typeof(BoxCollider)) as BoxCollider;
box.center = capsule.center;
float diameter = capsule.radius * 2f;
float height = Mathf.Max(diameter, capsule.height);
if (capsule.direction == 0) box.size = new Vector3(height, diameter, diameter);
else if (capsule.direction == 1) box.size = new Vector3(diameter, height, diameter);
else if (capsule.direction == 2) box.size = new Vector3(diameter, diameter, height);
Undo.DestroyObjectImmediate(capsule);
}
public static void ConvertToCapsuleCollider(BoxCollider box) {
float absX = Mathf.Abs(box.size.x);
float absY = Mathf.Abs(box.size.y);
float absZ = Mathf.Abs(box.size.z);
CapsuleCollider capsule = Undo.AddComponent(box.gameObject, typeof(CapsuleCollider)) as CapsuleCollider;
capsule.center = box.center;
capsule.direction = 0;
if (absY > absX && absY > absZ) capsule.direction = 1;
if (absZ > absX && absZ > absY) capsule.direction = 2;
if (capsule.direction == 0) {
capsule.height = absX;
capsule.radius = Mathf.Max(absY, absZ) * 0.5f;
} else if (capsule.direction == 1) {
capsule.height = absY;
capsule.radius = Mathf.Max(absX, absZ) * 0.5f;
} else if (capsule.direction == 2) {
capsule.height = absZ;
capsule.radius = Mathf.Max(absX, absY) * 0.5f;
}
Undo.DestroyObjectImmediate(box);
}
public static void SetColliderCenterWorld(Collider r, Collider s, Vector3 value, Transform root) {
Vector3 newCenter = r.transform.InverseTransformPoint(value);
Vector3 centerWorld = Vector3.zero;
Vector3 deltaWorld = Vector3.zero;
if (r is BoxCollider) {
var box = r as BoxCollider;
centerWorld = r.transform.TransformPoint(box.center);
deltaWorld = value - centerWorld;
box.center = newCenter;
if (s != null && s is BoxCollider) s.transform.GetComponent<BoxCollider>().center += s.transform.InverseTransformVector(SymmetryTools.Mirror(deltaWorld, root));
return;
}
if (r is CapsuleCollider) {
var capsule = r as CapsuleCollider;
centerWorld = r.transform.TransformPoint(capsule.center);
deltaWorld = value - centerWorld;
capsule.center = newCenter;
if (s != null && s is CapsuleCollider) s.transform.GetComponent<CapsuleCollider>().center += s.transform.InverseTransformVector(SymmetryTools.Mirror(deltaWorld, root));
return;
}
if (r is SphereCollider) {
var sphere = r as SphereCollider;
centerWorld = r.transform.TransformPoint(sphere.center);
deltaWorld = value - centerWorld;
sphere.center = newCenter;
if (s != null && s is SphereCollider) s.transform.GetComponent<SphereCollider>().center += s.transform.InverseTransformVector(SymmetryTools.Mirror(deltaWorld, root));
return;
}
}
public static Vector3 GetColliderCenterWorld(Collider r) {
if (r == null) return Vector3.zero;
if (r is BoxCollider) {
return r.transform.TransformPoint((r as BoxCollider).center);
}
if (r is CapsuleCollider) {
return r.transform.TransformPoint((r as CapsuleCollider).center);
}
if (r is SphereCollider) {
return r.transform.TransformPoint((r as SphereCollider).center);
}
return r.transform.position;
}
public static void SetColliderSize(Collider r, Collider s, Vector3 value, Vector3 lastValue, Vector3 lastValueS, Transform root) {
Vector3 delta = Vector3.zero;
Vector3 deltaM = Vector3.zero;
if (r is BoxCollider) {
var box = r as BoxCollider;
delta = value - box.size;
box.size = value;
if (s != null && s is BoxCollider) {
var boxS = s as BoxCollider;
deltaM = SymmetryTools.MirrorDelta(r.transform, s.transform, root, delta);
boxS.size += deltaM;
}
}
if (r is CapsuleCollider) {
var capsule = r as CapsuleCollider;
delta = value - GetCapsuleSize(capsule);
SetCapsuleSize(capsule, value, lastValue);
if (s != null && s is CapsuleCollider) {
var capsuleS = s as CapsuleCollider;
if (capsuleS != null) {
deltaM = SymmetryTools.MirrorDelta(r.transform, s.transform, root, delta);
Vector3 capsuleSSize = GetCapsuleSize(capsuleS);
capsuleSSize += deltaM;
SetCapsuleSize(capsuleS, capsuleSSize, lastValueS);
}
}
}
if (r is SphereCollider) {
var sphere = r as SphereCollider;
delta = value - GetColliderSize(r);
SetSphereSize(sphere, value, lastValue);
if (s != null && s is SphereCollider) {
var sphereS = s as SphereCollider;
Vector3 sphereSSize = GetColliderSize(s);
sphereSSize += delta;
if (sphereS != null) {
SetSphereSize(sphereS, sphereSSize, lastValueS);
}
}
}
}
public static Vector3 GetColliderSize(Collider r) {
if (r == null) return Vector3.zero;
if (r is BoxCollider) return (r as BoxCollider).size;
if (r is CapsuleCollider) return GetCapsuleSize(r as CapsuleCollider);
if (r is SphereCollider) return Vector3.one * (r as SphereCollider).radius;
return r.transform.localScale;
}
public static void SetCapsuleSize(CapsuleCollider capsule, Vector3 size, Vector3 lastSize) {
if (capsule.direction == 0) {
capsule.height = size.x;
float absY = Mathf.Abs(size.y - lastSize.y);
float absZ = Mathf.Abs(size.z - lastSize.z);
capsule.radius = absY > absZ? size.y: size.z;
}
if (capsule.direction == 1) {
capsule.height = size.y;
float absX = Mathf.Abs(size.x - lastSize.x);
float absZ = Mathf.Abs(size.z - lastSize.z);
capsule.radius = absX > absZ? size.x: size.z;
}
if (capsule.direction == 2) {
capsule.height = size.z;
float absX = Mathf.Abs(size.x - lastSize.x);
float absY = Mathf.Abs(size.y - lastSize.y);
capsule.radius = absX > absY? size.x: size.y;
}
}
public static void SetSphereSize(SphereCollider sphere, Vector3 size, Vector3 lastSize) {
if (size.x != lastSize.z) {
sphere.radius += size.x - lastSize.x;
} else if (size.y != lastSize.y) {
sphere.radius += size.y - lastSize.y;
} else if (size.z != lastSize.z) {
sphere.radius += size.z - lastSize.z;
}
}
public static Vector3 GetCapsuleSize(CapsuleCollider capsule) {
if (capsule.direction == 0) return new Vector3(capsule.height, capsule.radius, capsule.radius);
if (capsule.direction == 1) return new Vector3(capsule.radius, capsule.height, capsule.radius);
return new Vector3(capsule.radius, capsule.radius, capsule.height);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9ed7bfe787e81435c8b76e75353a45fb
timeCreated: 1444653054
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,56 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System;
namespace RootMotion.Dynamics {
[CustomEditor(typeof(ConfigurableJoint))]
public class ConfigurableJointInspector : JointInspector {
private static Color
highAngularXColor = new Color(1, 0.75f, 0, 0.3f),
lowAngularXColor = new Color(0.8f, 0.65f, 0, 0.3f),
angularYColor = new Color(0, 1, 0, 0.3f),
angularZColor = new Color(0, 0, 1, 0.3f);
private const string lowX = "Low X", highX = "High X", y = "Y", z = "Z";
public static void DrawJoint(ConfigurableJoint joint, bool modifiable = true, float alphaMlp = 1f) {
if (joint == null) return;
Vector3 axis = Vector3.zero;
if (joint.axis != Vector3.zero) axis = joint.axis.normalized;
Vector3 secondaryAxis = Vector3.zero;
if (joint.secondaryAxis != Vector3.zero) secondaryAxis = joint.secondaryAxis.normalized;
Vector3 crossAxis = secondaryAxis;
if (secondaryAxis != axis) crossAxis = Vector3.Cross(secondaryAxis, axis);
if (joint.angularXMotion == ConfigurableJointMotion.Limited) {
float newLowAngularXLimit = DrawJointLimit(joint as Joint, lowX, axis, joint.lowAngularXLimit.limit, MlpAlpha(lowAngularXColor, alphaMlp), -25, modifiable);
joint.lowAngularXLimit = NewJointLimit(newLowAngularXLimit, joint.lowAngularXLimit, -180, 180);
float newHighAngularXLimit = joint.highAngularXLimit.limit;
if (newLowAngularXLimit != 0 || (newLowAngularXLimit == 0 && newHighAngularXLimit != 0)) newHighAngularXLimit = DrawJointLimit(joint as Joint, highX, axis, joint.highAngularXLimit.limit, MlpAlpha(highAngularXColor, alphaMlp), 25, modifiable);
joint.highAngularXLimit = NewJointLimit(newHighAngularXLimit, joint.highAngularXLimit, -180, 180);
}
if (joint.angularYMotion == ConfigurableJointMotion.Limited) {
float newAngularYLimit = DrawJointLimit(joint as Joint, y, secondaryAxis, -joint.angularYLimit.limit, MlpAlpha(angularYColor, alphaMlp), 25, false);
newAngularYLimit = DrawJointLimit(joint as Joint, y, secondaryAxis, -newAngularYLimit, MlpAlpha(angularYColor, alphaMlp), 25, modifiable);
joint.angularYLimit = NewJointLimit(newAngularYLimit, joint.angularYLimit, 0, 180);
}
if (joint.angularZMotion == ConfigurableJointMotion.Limited) {
float newAngularZLimit = DrawJointLimit(joint as Joint, z, crossAxis, -joint.angularZLimit.limit, MlpAlpha(angularZColor, alphaMlp), 25, false);
newAngularZLimit = DrawJointLimit(joint as Joint, z, crossAxis, -newAngularZLimit, MlpAlpha(angularZColor, alphaMlp), 25, modifiable);
joint.angularZLimit = NewJointLimit(newAngularZLimit, joint.angularZLimit, 0, 180);
}
}
public void OnSceneGUI() {
DrawJoint(target as ConfigurableJoint);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 79e48320bea5b4a49a128eabdb1b39b2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,27 @@
using UnityEngine;
using UnityEditor;
namespace RootMotion.Dynamics {
/// <summary>
/// Converting 3D joints to ConfigurableJoints
/// </summary>
public class ConvertToConfigurableJoints: Editor {
/// <summary>
/// Converts any 3D joints on the selected Transform and it's children to ConfigurableJoints
/// </summary>
//[MenuItem ("Component/Physics/Convert to ConfigurableJoints")]
[MenuItem ("GameObject/Convert to ConfigurableJoints")]
public static void ConvertSelected() {
if (Selection.activeGameObject == null) {
Debug.Log("Please select the root of the ragdoll that you wish to convert to use ConfigurableJoints.");
return;
}
// @todo undo
JointConverter.ToConfigurable(Selection.activeGameObject);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c49a237763c444bda35d3387fed6c35
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace RootMotion.Dynamics
{
public class DragRig : MonoBehaviour
{
private Rigidbody rig;
private void Start()
{
rig = transform.GetComponent<Rigidbody>();
}
private void OnMouseDrag()
{
Vector3 cursorPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
rig.AddForce(0.01f * Time.deltaTime * (cursorPos-transform.position).normalized,ForceMode.Force);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b32271905ed4afe44b53328a916f382a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,139 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
namespace RootMotion.Dynamics {
public class JointInspector : Editor {
#region Public methods
private static bool isDragging;
public static float DrawJointLimit(Joint joint, string label, Vector3 axis, float limit, Color color, float openValue, bool drawHandles) {
if (Event.current.type == EventType.MouseDown) isDragging = false;
if (Event.current.type == EventType.MouseUp) isDragging = false;
float radius = HandleUtility.GetHandleSize(joint.transform.position);
Vector3 center = joint.transform.TransformPoint(joint.anchor);
Quaternion axisOffset = Quaternion.AngleAxis(GetViewOffset(joint, axis), axis);
Vector3 limitAxis = joint.transform.TransformDirection(axisOffset * axis).normalized;
Vector3 limitAxisOrtho = joint.transform.TransformDirection(axisOffset * new Vector3(axis.z, axis.x, axis.y)).normalized;
Vector3 limitAxisCross = Vector3.Cross(limitAxis, limitAxisOrtho);
Handles.color = color;
Handles.DrawSolidArc(center, limitAxis, limitAxisCross, -limit, radius);
Handles.color = new Color(color.r, color.g, color.b, 1f);
GUI.color = new Color(color.r, color.g, color.b, 1f);
//if (limitAxis != Vector3.zero) Handles.CircleCap(0, center, Quaternion.LookRotation(limitAxis), radius);
Quaternion angleAxis = Quaternion.AngleAxis(-limit, limitAxis);
Vector3 handleVector = angleAxis * limitAxisCross;
Quaternion handleRotation = Quaternion.AngleAxis(-limit, limitAxis);
if (limitAxisCross != Vector3.zero && limitAxisOrtho != Vector3.zero) {
handleRotation = Quaternion.AngleAxis(-limit, limitAxis) * Quaternion.LookRotation(limitAxisCross) * Quaternion.LookRotation(limitAxisOrtho);
}
if (!drawHandles) return limit;
float newLimit = Inspector.ScaleValueHandleSphere(limit, center + handleVector * radius, Quaternion.identity, radius, 1);
//float newLimit = Handles.han(handleRotation, center + handleVector * radius, limit);
string labelInfo = label;
if (newLimit == 0) {
labelInfo = "Open " + label;
if (Inspector.SphereButton(center + handleVector * radius, handleRotation, radius * 0.2f, radius * 0.07f)) {
newLimit = openValue;
}
}
Handles.Label(center + handleVector * radius * 1.2f, labelInfo);
if (newLimit != limit) {
if (!isDragging) {
Undo.RecordObject(joint, "Change Joint Limits");
isDragging = true;
}
}
GUI.color = Color.white;
return newLimit;
}
public static SoftJointLimit NewJointLimit(float limit, SoftJointLimit referenceJointLimit, float min, float max) {
SoftJointLimit newJointLimit = new SoftJointLimit();
newJointLimit.limit = Mathf.Clamp(limit, min, max);
newJointLimit.bounciness = referenceJointLimit.bounciness;
return newJointLimit;
}
public static Color MlpAlpha(Color color, float alphaMlp) {
return new Color(color.r, color.g, color.b, color.a * alphaMlp);
}
#endregion
private static float GetViewOffset(Joint joint, Vector3 axis) {
if (joint.connectedBody == null) return 0;
Vector3 directionAxis = joint.transform.right;
Axis viewAxis = GetViewAxis(joint);
switch(viewAxis) {
case Axis.Y: directionAxis = joint.transform.up; break;
case Axis.Z: directionAxis = joint.transform.forward; break;
}
if (FlipAxis(joint, viewAxis)) directionAxis = -directionAxis;
Vector3 axisWorld = joint.transform.TransformDirection(axis).normalized;
Vector3 axisWorldOrtho = joint.transform.TransformDirection(new Vector3(axis.z, axis.x, axis.y)).normalized;
float dot = Vector3.Dot(axisWorldOrtho, directionAxis);
float dot2 = Vector3.Dot(axisWorldOrtho, Vector3.Cross(directionAxis, axisWorld));
if (dot >= 0.5f) return -90;
if (dot2 >= 0.5f) return 0;
if (dot < -0.5f) return 90;
if (dot2 < -0.5f) return 180;
return 0;
}
private static Axis GetViewAxis(Joint joint) {
if (joint.connectedBody == null) return Axis.Z;
CapsuleCollider capsule = joint.GetComponent<CapsuleCollider>();
if (capsule != null && capsule.center != Vector3.zero) return AxisTools.ToAxis(capsule.center);
else {
BoxCollider box = joint.GetComponent<BoxCollider>();
if (box != null && box.center != Vector3.zero) return AxisTools.ToAxis(box.center);
}
return AxisTools.GetAxisToPoint(joint.transform, joint.connectedBody.worldCenterOfMass);
}
private static bool FlipAxis(Joint joint, Axis axis) {
Vector3 direction = Vector3.Normalize(joint.transform.position - joint.connectedBody.worldCenterOfMass);
CapsuleCollider capsuleCollider = joint.GetComponent<CapsuleCollider>();
if (capsuleCollider != null) {
direction = Vector3.Normalize(joint.transform.position - (joint.transform.position - (joint.transform.rotation * capsuleCollider.center)));
}
switch(axis) {
case Axis.X:
return Vector3.Dot(joint.transform.right, direction) < 0;
case Axis.Y:
return Vector3.Dot(joint.transform.up, direction) < 0;
default: return Vector3.Dot(joint.transform.forward, direction) < 0;
}
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2c166e20a769d4ec3a0f504c4fa3ffe7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,276 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
namespace RootMotion.Dynamics {
public enum JointAxis {
Primary,
Secondary,
Tertiary
}
// Tools for editing Joints.
public class JointTools: Editor {
public static Quaternion LocalToJointSpace(ConfigurableJoint joint) {
Vector3 forward = Vector3.Cross (joint.axis, joint.secondaryAxis).normalized;
Vector3 up = Vector3.Cross(forward, joint.axis).normalized;
if (forward == up) {
Debug.LogWarning("Joint " + joint.name + " secondaryAxis is in the exact same direction as it's axis. Please make sure they are not aligned.");
}
return Quaternion.LookRotation(forward, up);
}
public static Quaternion LocalToJointSpace(CharacterJoint joint) {
Vector3 forward = Vector3.Cross (joint.axis, joint.swingAxis).normalized;
Vector3 up = Vector3.Cross(forward, joint.axis).normalized;
if (forward == up) {
Debug.LogWarning("Joint " + joint.name + " swingAxis is in the exact same direction as it's axis. Please make sure they are not aligned.");
}
return Quaternion.LookRotation(forward, up);
}
public static Quaternion JointToLocalSpace(ConfigurableJoint joint) {
return Quaternion.Inverse(LocalToJointSpace(joint));
}
public static Quaternion JointToLocalSpace(CharacterJoint joint) {
return Quaternion.Inverse(LocalToJointSpace(joint));
}
public static JointAxis GetJointAxis(ConfigurableJoint joint, Vector3 vLocal) {
return GetJointAxis(joint.axis, joint.secondaryAxis, vLocal);
}
public static JointAxis GetJointAxis(CharacterJoint joint, Vector3 vLocal) {
return GetJointAxis(joint.axis, joint.swingAxis, vLocal);
}
public static JointAxis GetJointAxis(Vector3 axis, Vector3 secondaryAxis, Vector3 vLocal) {
vLocal = vLocal.normalized;
float axisDot = Mathf.Abs(Vector3.Dot(axis, vLocal));
float secondaryAxisDot = Mathf.Abs(Vector3.Dot(secondaryAxis, vLocal));
float tertiaryAxisDot = Mathf.Abs(Vector3.Dot(Vector3.Cross(axis, secondaryAxis), vLocal));
if (axisDot > secondaryAxisDot && axisDot > tertiaryAxisDot) return JointAxis.Primary;
if (secondaryAxisDot > axisDot && secondaryAxisDot > tertiaryAxisDot) return JointAxis.Secondary;
return JointAxis.Tertiary;
}
public static JointAxis GetSymmetricJointAxis(ConfigurableJoint joint, JointAxis jointAxis, ConfigurableJoint symmetricJoint, Transform root) {
return GetSymmetricJointAxis(joint.transform, joint.axis, joint.secondaryAxis, jointAxis, symmetricJoint.transform, symmetricJoint.axis, symmetricJoint.secondaryAxis, root);
}
public static JointAxis GetSymmetricJointAxis(CharacterJoint joint, JointAxis jointAxis, CharacterJoint symmetricJoint, Transform root) {
return GetSymmetricJointAxis(joint.transform, joint.axis, joint.swingAxis, jointAxis, symmetricJoint.transform, symmetricJoint.axis, symmetricJoint.swingAxis, root);
}
public static JointAxis GetSymmetricJointAxis(Transform jointTransform, Vector3 axis, Vector3 secondaryAxis, JointAxis jointAxis, Transform symmetricTransform, Vector3 symmetricAxis, Vector3 symmetricSecondaryAxis, Transform root) {
Vector3 a = JointAxisToVector3(axis, secondaryAxis, jointAxis);
Vector3 aWorld = jointTransform.rotation * a;
Vector3 aWorldMirror = SymmetryTools.Mirror(aWorld, root);
Vector3 aLocalMirror = Quaternion.Inverse(symmetricTransform.rotation) * aWorldMirror;
Vector3 sAVector = AxisTools.GetAxisVectorToDirection(symmetricTransform, aWorldMirror);
Vector3 sA = Vector3.Project(aLocalMirror, sAVector);
return GetJointAxis(symmetricAxis, symmetricSecondaryAxis, sA);
}
public static Vector3 JointAxisToVector3(ConfigurableJoint joint, JointAxis jointAxis) {
return JointAxisToVector3(joint.axis, joint.secondaryAxis, jointAxis);
}
public static Vector3 JointAxisToVector3(CharacterJoint joint, JointAxis jointAxis) {
return JointAxisToVector3(joint.axis, joint.swingAxis, jointAxis);
}
public static Vector3 JointAxisToVector3(Vector3 axis, Vector3 secondaryAxis, JointAxis jointAxis) {
switch(jointAxis) {
case JointAxis.Primary: return axis;
case JointAxis.Secondary: return secondaryAxis;
default: return Vector3.Cross(axis, secondaryAxis);
}
}
public static void ApplyXDeltaToJointLimit(ref ConfigurableJoint joint, float delta, JointAxis jointAxis, bool low) {
switch(jointAxis) {
case JointAxis.Primary:
if (low) {
SoftJointLimit lowX = joint.lowAngularXLimit;
lowX.limit += delta;
joint.lowAngularXLimit = lowX;
} else {
SoftJointLimit highX = joint.highAngularXLimit;
highX.limit -= delta;
joint.highAngularXLimit = highX;
}
break;
case JointAxis.Secondary:
SoftJointLimit y = joint.angularYLimit;
y.limit += delta * 0.5f;
joint.angularYLimit = y;
break;
case JointAxis.Tertiary:
SoftJointLimit z = joint.angularZLimit;
z.limit += delta * 0.5f;
joint.angularZLimit = z;
break;
}
}
public static void ApplyXDeltaToJointLimit(ref CharacterJoint joint, float delta, JointAxis jointAxis, bool low) {
switch(jointAxis) {
case JointAxis.Primary:
if (low) {
SoftJointLimit lowX = joint.lowTwistLimit;
lowX.limit += delta;
joint.lowTwistLimit = lowX;
} else {
SoftJointLimit highX = joint.highTwistLimit;
highX.limit -= delta;
joint.highTwistLimit = highX;
}
break;
case JointAxis.Secondary:
SoftJointLimit y = joint.swing1Limit;
y.limit += delta * 0.5f;
joint.swing1Limit = y;
break;
case JointAxis.Tertiary:
SoftJointLimit z = joint.swing2Limit;
z.limit += delta * 0.5f;
joint.swing2Limit = z;
break;
}
}
public static void ApplyDeltaToJointLimit(ref ConfigurableJoint joint, float delta, JointAxis jointAxis) {
switch(jointAxis) {
case JointAxis.Primary:
SoftJointLimit lowX = joint.lowAngularXLimit;
lowX.limit += delta;
joint.lowAngularXLimit = lowX;
SoftJointLimit highX = joint.highAngularXLimit;
highX.limit += delta;
joint.highAngularXLimit = highX;
break;
case JointAxis.Secondary:
SoftJointLimit y = joint.angularYLimit;
y.limit += delta;
joint.angularYLimit = y;
break;
case JointAxis.Tertiary:
SoftJointLimit z = joint.angularZLimit;
z.limit += delta;
joint.angularZLimit = z;
break;
}
}
public static void ApplyDeltaToJointLimit(ref CharacterJoint joint, float delta, JointAxis jointAxis) {
switch(jointAxis) {
case JointAxis.Primary:
SoftJointLimit lowX = joint.lowTwistLimit;
lowX.limit += delta;
joint.lowTwistLimit = lowX;
SoftJointLimit highX = joint.highTwistLimit;
highX.limit += delta;
joint.highTwistLimit = highX;
break;
case JointAxis.Secondary:
SoftJointLimit y = joint.swing1Limit;
y.limit += delta;
joint.swing1Limit = y;
break;
case JointAxis.Tertiary:
SoftJointLimit z = joint.swing2Limit;
z.limit += delta;
joint.swing2Limit = z;
break;
}
}
public static Vector3 GetLowXAxisWorld(Joint joint) {
return joint.transform.rotation * joint.axis;
}
public static Vector3 GetHighXAxisWorld(Joint joint) {
return joint.transform.rotation * -joint.axis;
}
public static void SwitchXY(ref ConfigurableJoint joint) {
Undo.RecordObject(joint, "Switch Yellow/Green Joint Axis");
Vector3 axis = joint.axis;
joint.axis = joint.secondaryAxis;
joint.secondaryAxis = axis;
}
public static void SwitchXZ(ref ConfigurableJoint joint) {
Undo.RecordObject(joint, "Switch Yellow/Blue Joint Axis");
Vector3 cross = Vector3.Cross(joint.axis, joint.secondaryAxis).normalized;
joint.axis = cross;
}
public static void SwitchYZ(ref ConfigurableJoint joint) {
Undo.RecordObject(joint, "Switch Green/Blue Joint Axis");
Vector3 cross = Vector3.Cross(joint.axis, joint.secondaryAxis).normalized;
joint.secondaryAxis = cross;
}
public static void SwitchXY(ref CharacterJoint joint) {
Undo.RecordObject(joint, "Switch Yellow/Green Joint Axis");
Vector3 axis = joint.axis;
joint.axis = joint.swingAxis;
joint.swingAxis = axis;
}
public static void SwitchXZ(ref CharacterJoint joint) {
Undo.RecordObject(joint, "Switch Yellow/Blue Joint Axis");
Vector3 cross = Vector3.Cross(joint.axis, joint.swingAxis).normalized;
joint.axis = cross;
}
public static void SwitchYZ(ref CharacterJoint joint) {
Undo.RecordObject(joint, "Switch Green/Blue Joint Axis");
Vector3 cross = Vector3.Cross(joint.axis, joint.swingAxis).normalized;
joint.swingAxis = cross;
}
public static void InvertAxis(ref Joint joint) {
Undo.RecordObject(joint, "Invert Axis");
joint.axis = -joint.axis;
}
public static void InvertSecondaryAxis(ref Joint joint) {
if (joint is ConfigurableJoint) {
var j = joint as ConfigurableJoint;
Undo.RecordObject(j, "Invert Secondary Axis");
j.secondaryAxis = -j.secondaryAxis;
}
if (joint is CharacterJoint) {
var j = joint as CharacterJoint;
Undo.RecordObject(j, "Invert Swing Axis");
j.swingAxis = -j.swingAxis;
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7d88fb763cb86436197038e855690500
timeCreated: 1444653109
licenseType: Store
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