first commit
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
internal static class AnimationRiggingContextMenus
|
||||
{
|
||||
[MenuItem("CONTEXT/Animator/Rig Setup", false, 611)]
|
||||
static void RigSetup(MenuCommand command)
|
||||
{
|
||||
var animator = command.context as Animator;
|
||||
|
||||
AnimationRiggingEditorUtils.RigSetup(animator.transform);
|
||||
}
|
||||
|
||||
[MenuItem("CONTEXT/Animator/Bone Renderer Setup", false, 612)]
|
||||
static void BoneRendererSetup(MenuCommand command)
|
||||
{
|
||||
var animator = command.context as Animator;
|
||||
|
||||
AnimationRiggingEditorUtils.BoneRendererSetup(animator.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91d51ce3535dd204d8a9271471f1583d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,147 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations.Rigging;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
internal static class AnimationRiggingEditorUtils
|
||||
{
|
||||
public static void RigSetup(Transform transform)
|
||||
{
|
||||
var rigBuilder = transform.GetComponent<RigBuilder>();
|
||||
|
||||
if (rigBuilder == null)
|
||||
rigBuilder = Undo.AddComponent<RigBuilder>(transform.gameObject);
|
||||
else
|
||||
Undo.RecordObject(rigBuilder, "Rig Builder Component Added.");
|
||||
|
||||
var name = "Rig";
|
||||
var cnt = 1;
|
||||
while (rigBuilder.transform.Find(string.Format("{0} {1}", name, cnt)) != null)
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
name = string.Format("{0} {1}", name, cnt);
|
||||
var rigGameObject = new GameObject(name);
|
||||
Undo.RegisterCreatedObjectUndo(rigGameObject, name);
|
||||
rigGameObject.transform.SetParent(rigBuilder.transform);
|
||||
|
||||
var rig = Undo.AddComponent<Rig>(rigGameObject);
|
||||
rigBuilder.layers.Add(new RigLayer(rig));
|
||||
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(rigBuilder))
|
||||
EditorUtility.SetDirty(rigBuilder);
|
||||
}
|
||||
|
||||
public static void BoneRendererSetup(Transform transform)
|
||||
{
|
||||
var boneRenderer = transform.GetComponent<BoneRenderer>();
|
||||
if (boneRenderer == null)
|
||||
boneRenderer = Undo.AddComponent<BoneRenderer>(transform.gameObject);
|
||||
else
|
||||
Undo.RecordObject(boneRenderer, "Bone renderer setup.");
|
||||
|
||||
var animator = transform.GetComponent<Animator>();
|
||||
var renderers = transform.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
var bones = new List<Transform>();
|
||||
if (animator != null && renderers != null && renderers.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < renderers.Length; ++i)
|
||||
{
|
||||
var renderer = renderers[i];
|
||||
for (int j = 0; j < renderer.bones.Length; ++j)
|
||||
{
|
||||
var bone = renderer.bones[j];
|
||||
if (!bones.Contains(bone))
|
||||
{
|
||||
bones.Add(bone);
|
||||
|
||||
for (int k = 0; k < bone.childCount; k++)
|
||||
{
|
||||
if (!bones.Contains(bone.GetChild(k)))
|
||||
bones.Add(bone.GetChild(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bones.AddRange(transform.GetComponentsInChildren<Transform>());
|
||||
}
|
||||
|
||||
boneRenderer.transforms = bones.ToArray();
|
||||
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(boneRenderer))
|
||||
EditorUtility.SetDirty(boneRenderer);
|
||||
}
|
||||
|
||||
public static void RestoreBindPose(Transform transform)
|
||||
{
|
||||
var animator = transform.GetComponentInParent<Animator>();
|
||||
var root = (animator) ? animator.transform : transform;
|
||||
var renderers = root.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
|
||||
if (renderers.Length == 0)
|
||||
{
|
||||
Debug.LogError(
|
||||
string.Format(
|
||||
"Could not restore bind pose because no SkinnedMeshRenderers " +
|
||||
"were found on {0} or any of its children.", root.name));
|
||||
return;
|
||||
}
|
||||
|
||||
Undo.RegisterFullObjectHierarchyUndo(root.gameObject, "Restore bind pose");
|
||||
|
||||
var bones = new Dictionary<Transform, Matrix4x4>();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
for (int i = 0; i < renderer.bones.Length; ++i)
|
||||
{
|
||||
if (!bones.ContainsKey(renderer.bones[i]))
|
||||
bones.Add(renderer.bones[i], renderer.sharedMesh.bindposes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var transforms = transform.GetComponentsInChildren<Transform>();
|
||||
var restoredPose = false;
|
||||
foreach (var t in transforms)
|
||||
{
|
||||
if (!bones.ContainsKey(t))
|
||||
continue;
|
||||
|
||||
// The root bone is the only bone in the skeleton
|
||||
// hierarchy that does not have a parent bone.
|
||||
var isRootBone = !bones.ContainsKey(t.parent);
|
||||
|
||||
var matrix = bones[t];
|
||||
var wMatrix = matrix.inverse;
|
||||
|
||||
if (!isRootBone)
|
||||
{
|
||||
if (t.parent)
|
||||
matrix *= bones[t.parent].inverse;
|
||||
matrix = matrix.inverse;
|
||||
|
||||
t.localScale = new Vector3(
|
||||
matrix.GetColumn(0).magnitude,
|
||||
matrix.GetColumn(1).magnitude,
|
||||
matrix.GetColumn(2).magnitude
|
||||
);
|
||||
t.localPosition = matrix.MultiplyPoint(Vector3.zero);
|
||||
}
|
||||
t.rotation = wMatrix.rotation;
|
||||
|
||||
restoredPose = true;
|
||||
}
|
||||
|
||||
if (!restoredPose)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
string.Format(
|
||||
"No valid bindpose(s) have been found for the selected transform: {0}.",
|
||||
transform.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47c3d3ff34626ed49adac10f7ea74b25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,90 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
internal static class AnimationRiggingMenu
|
||||
{
|
||||
static bool FilterSourceAndDestinationFromSelection(out Transform source, out Transform destination)
|
||||
{
|
||||
var selected = Selection.instanceIDs;
|
||||
if (selected == null || selected.Length != 2)
|
||||
{
|
||||
source = destination = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var srcGameObject = EditorUtility.InstanceIDToObject(selected[1]) as GameObject;
|
||||
var dstGameObject = EditorUtility.InstanceIDToObject(selected[0]) as GameObject;
|
||||
if (srcGameObject == null || dstGameObject == null)
|
||||
{
|
||||
source = destination = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
source = srcGameObject.transform;
|
||||
destination = dstGameObject.transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MenuItem("Animation Rigging/Align Transform", false, 0)]
|
||||
static void PerformTransformAlign()
|
||||
{
|
||||
if (FilterSourceAndDestinationFromSelection(out Transform src, out Transform dst))
|
||||
{
|
||||
Undo.RecordObject(dst, "Align transform " + dst.name + " with " + src.name);
|
||||
dst.SetPositionAndRotation(src.position, src.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Animation Rigging/Align Rotation", false, 0)]
|
||||
static void PerformRotationAlign()
|
||||
{
|
||||
if (FilterSourceAndDestinationFromSelection(out Transform src, out Transform dst))
|
||||
{
|
||||
Undo.RecordObject(dst, "Align rotation of " + dst.name + " with " + src.name);
|
||||
dst.rotation = src.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Animation Rigging/Align Position", false, 0)]
|
||||
static void PerformPositionAlign()
|
||||
{
|
||||
if (FilterSourceAndDestinationFromSelection(out Transform src, out Transform dst))
|
||||
{
|
||||
Undo.RecordObject(dst, "Align position of " + dst.name + " with " + src.name);
|
||||
dst.position = src.position;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Animation Rigging/Restore Bind Pose", false, 11)]
|
||||
static void RestoreBindPose()
|
||||
{
|
||||
var selection = Selection.activeTransform;
|
||||
if (selection == null)
|
||||
return;
|
||||
|
||||
AnimationRiggingEditorUtils.RestoreBindPose(selection);
|
||||
}
|
||||
|
||||
[MenuItem("Animation Rigging/Rig Setup", false, 12)]
|
||||
static void RigSetup()
|
||||
{
|
||||
var selection = Selection.activeTransform;
|
||||
if (selection == null)
|
||||
return;
|
||||
|
||||
AnimationRiggingEditorUtils.RigSetup(selection);
|
||||
}
|
||||
|
||||
[MenuItem("Animation Rigging/Bone Renderer Setup", false, 13)]
|
||||
static void BoneRendererSetup()
|
||||
{
|
||||
var selection = Selection.activeTransform;
|
||||
if (selection == null)
|
||||
return;
|
||||
|
||||
AnimationRiggingEditorUtils.BoneRendererSetup(selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 412e93db5b3ee8447bc607a8e21ca799
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,85 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
static class AnimationWindowUtils
|
||||
{
|
||||
static AnimationWindow m_AnimationWindow = null;
|
||||
|
||||
public static AnimationWindow animationWindow
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_AnimationWindow == null)
|
||||
m_AnimationWindow = FindWindowOpen();
|
||||
|
||||
return m_AnimationWindow;
|
||||
}
|
||||
}
|
||||
|
||||
public static AnimationClip activeAnimationClip
|
||||
{
|
||||
get
|
||||
{
|
||||
if (animationWindow != null)
|
||||
return animationWindow.animationClip;
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (animationWindow != null)
|
||||
animationWindow.animationClip = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void StartPreview()
|
||||
{
|
||||
if (animationWindow != null)
|
||||
animationWindow.previewing = true;
|
||||
}
|
||||
|
||||
public static void StopPreview()
|
||||
{
|
||||
if (animationWindow != null)
|
||||
animationWindow.previewing = false;
|
||||
}
|
||||
|
||||
public static bool isPreviewing
|
||||
{
|
||||
get
|
||||
{
|
||||
if (animationWindow != null)
|
||||
return animationWindow.previewing;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This does not check if there is an AnimationClip to play
|
||||
public static bool canPreview
|
||||
{
|
||||
get
|
||||
{
|
||||
if (animationWindow != null)
|
||||
return animationWindow.canPreview;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static AnimationWindow FindWindowOpen()
|
||||
{
|
||||
UnityEngine.Object[] objs = Resources.FindObjectsOfTypeAll(typeof(AnimationWindow));
|
||||
|
||||
foreach (UnityEngine.Object o in objs)
|
||||
{
|
||||
if (o.GetType() == typeof(AnimationWindow))
|
||||
return (AnimationWindow)o;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9235b286c8b543ab8660ef639271c0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,871 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations;
|
||||
using UnityEngine.Animations.Rigging;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class that groups bi-directional baking utilities.
|
||||
/// </summary>
|
||||
public static class BakeUtils
|
||||
{
|
||||
private const string kBakeToSkeletonUndoLabel = "Transfer motion to skeleton";
|
||||
private const string kBakeToConstraintUndoLabel = "Transfer motion to constraint";
|
||||
|
||||
interface IEvaluationGraph
|
||||
{
|
||||
void Evaluate(float time);
|
||||
}
|
||||
|
||||
class EvaluationGraph : IDisposable, IEvaluationGraph
|
||||
{
|
||||
SyncSceneToStreamLayer m_SyncSceneToStreamLayer;
|
||||
List<IRigLayer> m_RigLayers;
|
||||
PlayableGraph m_Graph;
|
||||
|
||||
AnimationClip m_Clip;
|
||||
bool m_ClipLoopTime;
|
||||
|
||||
public EvaluationGraph(RigBuilder rigBuilder, AnimationClip clip, AnimationClip defaultPoseClip, IReadOnlyDictionary<IRigConstraint, IRigConstraint> overrides, IRigConstraint lastConstraint = null)
|
||||
{
|
||||
m_SyncSceneToStreamLayer = new SyncSceneToStreamLayer();
|
||||
|
||||
bool stopBuilding = false;
|
||||
|
||||
var layers = rigBuilder.layers;
|
||||
m_RigLayers = new List<IRigLayer>(layers.Count);
|
||||
for (int i = 0; i < layers.Count; ++i)
|
||||
{
|
||||
if (stopBuilding == true)
|
||||
break;
|
||||
|
||||
if (layers[i].rig == null || !layers[i].active)
|
||||
continue;
|
||||
|
||||
IRigConstraint[] constraints = RigUtils.GetConstraints(layers[i].rig);
|
||||
if (constraints == null || constraints.Length == 0)
|
||||
continue;
|
||||
|
||||
var newConstraints = new List<IRigConstraint>(constraints.Length);
|
||||
foreach (IRigConstraint constraint in constraints)
|
||||
{
|
||||
if (overrides.TryGetValue(constraint, out IRigConstraint newConstraint))
|
||||
{
|
||||
if (newConstraint != null)
|
||||
{
|
||||
newConstraints.Add(newConstraint);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newConstraints.Add(constraint);
|
||||
}
|
||||
|
||||
if (constraint == lastConstraint)
|
||||
{
|
||||
stopBuilding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_RigLayers.Add(new OverrideRigLayer(layers[i].rig, newConstraints.ToArray()));
|
||||
}
|
||||
|
||||
m_Graph = PlayableGraph.Create("Evaluation-Graph");
|
||||
m_Graph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
|
||||
m_Clip = clip;
|
||||
|
||||
var settings = AnimationUtility.GetAnimationClipSettings(m_Clip);
|
||||
m_ClipLoopTime = settings.loopTime;
|
||||
|
||||
// Override loop time in clip asset.
|
||||
settings.loopTime = false;
|
||||
AnimationUtility.SetAnimationClipSettings(m_Clip, settings);
|
||||
|
||||
var defaultPosePlayable = AnimationClipPlayable.Create(m_Graph, defaultPoseClip);
|
||||
var clipPlayable = AnimationClipPlayable.Create(m_Graph, m_Clip);
|
||||
|
||||
defaultPosePlayable.SetApplyFootIK(false);
|
||||
clipPlayable.SetApplyFootIK(false);
|
||||
|
||||
AnimationLayerMixerPlayable mixer = AnimationLayerMixerPlayable.Create(m_Graph, 2);
|
||||
mixer.ConnectInput(0, defaultPosePlayable, 0, 1.0f);
|
||||
mixer.ConnectInput(1, clipPlayable, 0, 1.0f);
|
||||
|
||||
Playable inputPlayable = mixer;
|
||||
|
||||
var playableChains = RigBuilderUtils.BuildPlayables(animator, m_Graph, m_RigLayers, m_SyncSceneToStreamLayer);
|
||||
foreach (var chain in playableChains)
|
||||
{
|
||||
if (!chain.IsValid())
|
||||
continue;
|
||||
|
||||
chain.playables[0].AddInput(inputPlayable, 0, 1);
|
||||
inputPlayable = chain.playables[chain.playables.Length - 1];
|
||||
}
|
||||
|
||||
var output = AnimationPlayableOutput.Create(m_Graph, "bake-output", animator);
|
||||
output.SetSourcePlayable(inputPlayable);
|
||||
}
|
||||
|
||||
public void Evaluate(float time)
|
||||
{
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
return;
|
||||
|
||||
m_SyncSceneToStreamLayer.Update(m_RigLayers);
|
||||
|
||||
foreach (var layer in m_RigLayers)
|
||||
{
|
||||
if (layer.IsValid() && layer.active)
|
||||
layer.Update();
|
||||
}
|
||||
|
||||
AnimationMode.BeginSampling();
|
||||
AnimationMode.SamplePlayableGraph(m_Graph, 0, time);
|
||||
AnimationMode.EndSampling();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Graph.Destroy();
|
||||
for (int i = 0; i < m_RigLayers.Count; ++i)
|
||||
{
|
||||
m_RigLayers[i].Reset();
|
||||
}
|
||||
m_RigLayers.Clear();
|
||||
m_SyncSceneToStreamLayer.Reset();
|
||||
|
||||
// Restore loop time in clip asset.
|
||||
var settings = AnimationUtility.GetAnimationClipSettings(m_Clip);
|
||||
settings.loopTime = m_ClipLoopTime;
|
||||
|
||||
AnimationUtility.SetAnimationClipSettings(m_Clip, settings);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates if the Editor and the provided RigBuilder are in a correct state to do motion transfer.
|
||||
/// </summary>
|
||||
/// <param name="rigBuilder">The RigBuilder that will be used for motion transfer.</param>
|
||||
/// <returns>Returns true if both the editor and the provided RigBuilder are in a valid state for motion transfer. Returns false if the requirements are not met.</returns>
|
||||
public static bool TransferMotionValidate(RigBuilder rigBuilder)
|
||||
{
|
||||
if (!AnimationWindowUtils.isPreviewing || AnimationWindowUtils.activeAnimationClip == null)
|
||||
return false;
|
||||
|
||||
var selected = Selection.instanceIDs;
|
||||
if (selected.Length != 1)
|
||||
return false;
|
||||
|
||||
var selectedGO = EditorUtility.InstanceIDToObject(selected[0]) as GameObject;
|
||||
if (selectedGO != rigBuilder.gameObject)
|
||||
return false;
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
if (animator.isHuman)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates if the Editor and the provided Rig are in a correct state to do motion transfer.
|
||||
/// </summary>
|
||||
/// <param name="rig">The Rig that will be used for motion transfer.</param>
|
||||
/// <returns>Returns true if both the editor and the provided Rig are in a valid state for motion transfer. Returns false if the requirements are not met.</returns>
|
||||
public static bool TransferMotionValidate(Rig rig)
|
||||
{
|
||||
if (!AnimationWindowUtils.isPreviewing || AnimationWindowUtils.activeAnimationClip == null)
|
||||
return false;
|
||||
|
||||
var selected = Selection.instanceIDs;
|
||||
if (selected.Length != 1)
|
||||
return false;
|
||||
|
||||
var selectedGO = EditorUtility.InstanceIDToObject(selected[0]) as GameObject;
|
||||
if (selectedGO != rig.gameObject)
|
||||
return false;
|
||||
|
||||
var rigBuilder = rig.GetComponentInParent<RigBuilder>();
|
||||
if (rigBuilder == null)
|
||||
return false;
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
if (animator.isHuman)
|
||||
return false;
|
||||
|
||||
bool inRigBuilder = false;
|
||||
var layers = rigBuilder.layers;
|
||||
for (int i = 0; i < layers.Count; ++i)
|
||||
{
|
||||
if (layers[i].rig == rig && layers[i].active)
|
||||
inRigBuilder = true;
|
||||
}
|
||||
|
||||
return inRigBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates if the Editor and the provided RigConstraint are in a correct state to do motion transfer.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of RigConstraint that is to be validated.</typeparam>
|
||||
/// <param name="constraint">The RigConstraint that will be used for motion transfer.</param>
|
||||
/// <returns>Returns true if both the editor and the provided RigConstraint are in a valid state for motion transfer. Returns false if the requirements are not met.</returns>
|
||||
public static bool TransferMotionValidate<T>(T constraint)
|
||||
where T : MonoBehaviour, IRigConstraint
|
||||
{
|
||||
if (!AnimationWindowUtils.isPreviewing || AnimationWindowUtils.activeAnimationClip == null)
|
||||
return false;
|
||||
|
||||
var selected = Selection.instanceIDs;
|
||||
if (selected.Length != 1)
|
||||
return false;
|
||||
|
||||
var selectedGO = EditorUtility.InstanceIDToObject(selected[0]) as GameObject;
|
||||
if (selectedGO != constraint.gameObject)
|
||||
return false;
|
||||
|
||||
var rig = constraint.GetComponentInParent<Rig>();
|
||||
if (rig == null)
|
||||
return false;
|
||||
|
||||
var rigBuilder = rig.GetComponentInParent<RigBuilder>();
|
||||
if (rigBuilder == null)
|
||||
return false;
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
if (animator.isHuman)
|
||||
return false;
|
||||
|
||||
bool inRigBuilder = false;
|
||||
var layers = rigBuilder.layers;
|
||||
for (int i = 0; i < layers.Count; ++i)
|
||||
{
|
||||
if (layers[i].rig == rig && layers[i].active)
|
||||
inRigBuilder = true;
|
||||
}
|
||||
|
||||
if (!inRigBuilder)
|
||||
return false;
|
||||
|
||||
return constraint.IsValid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes motion from any RigConstraints in the RigBuilder to the skeleton.
|
||||
/// </summary>
|
||||
/// <param name="rigBuilder">The RigBuilder whose RigConstraints are to be baked.</param>
|
||||
public static void TransferMotionToSkeleton(RigBuilder rigBuilder)
|
||||
{
|
||||
List<RigLayer> layers = rigBuilder.layers;
|
||||
List<Rig> rigs = new List<Rig>(layers.Count);
|
||||
foreach(var layer in layers)
|
||||
{
|
||||
if (layer.rig != null && layer.active)
|
||||
{
|
||||
rigs.Add(layer.rig);
|
||||
}
|
||||
}
|
||||
|
||||
TransferMotionToSkeleton(rigBuilder, rigs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes motion from any RigConstraints in the Rig to the skeleton.
|
||||
/// </summary>
|
||||
/// <param name="rig">The Rig whose RigConstraints are to be baked.</param>
|
||||
public static void TransferMotionToSkeleton(Rig rig)
|
||||
{
|
||||
var rigBuilder = rig.GetComponentInParent<RigBuilder>();
|
||||
if (rigBuilder == null)
|
||||
throw new InvalidOperationException("No rigbuilder was found in the hierarchy.");
|
||||
|
||||
TransferMotionToSkeleton(rigBuilder, new Rig[]{rig});
|
||||
}
|
||||
|
||||
private static void TransferMotionToSkeleton(RigBuilder rigBuilder, IEnumerable<Rig> rigs)
|
||||
{
|
||||
var constraints = new List<IRigConstraint>();
|
||||
foreach(var rig in rigs)
|
||||
{
|
||||
constraints.AddRange(RigUtils.GetConstraints(rig));
|
||||
}
|
||||
|
||||
var clip = AnimationWindowUtils.activeAnimationClip;
|
||||
|
||||
// Make sure we have a clip selected
|
||||
if (clip == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"There is no clip to work on." +
|
||||
" The animation window must be open with an active clip!");
|
||||
}
|
||||
|
||||
AnimationClip editableClip = clip;
|
||||
if (!GetEditableClip(ref editableClip))
|
||||
return;
|
||||
|
||||
AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder);
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(editableClip, kBakeToSkeletonUndoLabel);
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
if (editableClip != clip)
|
||||
AddClipToAnimatorController(animator, editableClip);
|
||||
|
||||
var bindingsToRemove = new HashSet<EditorCurveBinding>();
|
||||
|
||||
foreach(IRigConstraint constraint in constraints)
|
||||
{
|
||||
var bakeParameters = FindBakeParameters(constraint);
|
||||
if (bakeParameters == null || !bakeParameters.canBakeToSkeleton)
|
||||
continue;
|
||||
|
||||
// Flush out animation mode modifications
|
||||
AnimationMode.BeginSampling();
|
||||
AnimationMode.EndSampling();
|
||||
|
||||
var bindings = bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint);
|
||||
BakeToSkeleton(rigBuilder, constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToSkeletonCurveFilterOptions);
|
||||
|
||||
bindingsToRemove.UnionWith(bakeParameters.GetSourceCurveBindings(rigBuilder, constraint));
|
||||
}
|
||||
|
||||
// Remove weight curve & force constraint to be active
|
||||
if (Preferences.forceConstraintWeightOnBake)
|
||||
{
|
||||
AnimationCurve zeroWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 0f);
|
||||
|
||||
foreach(var rig in rigs)
|
||||
{
|
||||
AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, rig), zeroWeightCurve);
|
||||
}
|
||||
}
|
||||
|
||||
if (Preferences.bakeToSkeletonAndRemoveCurves)
|
||||
RemoveCurves(editableClip, bindingsToRemove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes motion from the RigConstraint to the skeleton.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of RigConstraint that is to be baked.</typeparam>
|
||||
/// <param name="constraint">The RigConstraint that will be baked to the skeleton.</param>
|
||||
public static void TransferMotionToSkeleton<T>(T constraint)
|
||||
where T : MonoBehaviour, IRigConstraint
|
||||
{
|
||||
var rigBuilder = constraint.GetComponentInParent<RigBuilder>();
|
||||
if (rigBuilder == null)
|
||||
throw new InvalidOperationException("No rigbuilder was found in the hierarchy.");
|
||||
|
||||
var bakeParameters = FindBakeParameters(constraint);
|
||||
if (bakeParameters == null)
|
||||
throw new InvalidOperationException(string.Format("Could not find BakeParameters class for constraint {0}.", constraint != null ? constraint.ToString() : "no-name"));
|
||||
|
||||
if (!bakeParameters.canBakeToSkeleton)
|
||||
throw new InvalidOperationException("Constraint disallows transfering motion to skeleton.");
|
||||
|
||||
var bindings = bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint);
|
||||
var clip = AnimationWindowUtils.activeAnimationClip;
|
||||
|
||||
// Make sure we have a clip selected
|
||||
if (clip == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"There is no clip to work on." +
|
||||
" The animation window must be open with an active clip!");
|
||||
}
|
||||
|
||||
AnimationClip editableClip = clip;
|
||||
if (!GetEditableClip(ref editableClip))
|
||||
return;
|
||||
|
||||
AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder);
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(editableClip, kBakeToSkeletonUndoLabel);
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
if (editableClip != clip)
|
||||
AddClipToAnimatorController(animator, editableClip);
|
||||
|
||||
BakeToSkeleton(constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToSkeletonCurveFilterOptions);
|
||||
|
||||
if (Preferences.forceConstraintWeightOnBake)
|
||||
{
|
||||
AnimationCurve zeroWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 0f);
|
||||
AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, constraint), zeroWeightCurve);
|
||||
}
|
||||
|
||||
if (Preferences.bakeToSkeletonAndRemoveCurves)
|
||||
RemoveCurves(editableClip, bakeParameters.GetSourceCurveBindings(rigBuilder, constraint));
|
||||
|
||||
}
|
||||
|
||||
internal static void BakeToSkeleton<T>(T constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable<EditorCurveBinding> bindings, CurveFilterOptions filterOptions)
|
||||
where T : MonoBehaviour, IRigConstraint
|
||||
{
|
||||
// Make sure we have a rigbuilder (which guarantees an animator).
|
||||
var rigBuilder = constraint.GetComponentInParent<RigBuilder>();
|
||||
if (rigBuilder == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"No rigbuilder was found in the hierarchy. " +
|
||||
"A RigBuilder and Animator are required to construct valid bindings.");
|
||||
}
|
||||
|
||||
BakeToSkeleton(rigBuilder, constraint, clip, defaultPoseClip, bindings, filterOptions);
|
||||
}
|
||||
|
||||
private static void BakeToSkeleton(RigBuilder rigBuilder, IRigConstraint constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable<EditorCurveBinding> bindings, CurveFilterOptions filterOptions)
|
||||
{
|
||||
// Make sure the base constraint is valid
|
||||
if (constraint == null || !constraint.IsValid())
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format("The rig constraint {0} is not a valid constraint.",
|
||||
constraint != null ? constraint.ToString() : ""));
|
||||
}
|
||||
|
||||
var overrides = new Dictionary<IRigConstraint, IRigConstraint>();
|
||||
|
||||
using(var graph = new EvaluationGraph(rigBuilder, clip, defaultPoseClip, overrides, constraint))
|
||||
{
|
||||
BakeCurvesToClip(clip, bindings, rigBuilder, graph, filterOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes motion from the skeleton to the constraints in the RigBuilder.
|
||||
/// </summary>
|
||||
/// <param name="rigBuilder">The RigBuilder whose RigConstraints are to be baked.</param>
|
||||
public static void TransferMotionToConstraint(RigBuilder rigBuilder)
|
||||
{
|
||||
List<RigLayer> layers = rigBuilder.layers;
|
||||
List<Rig> rigs = new List<Rig>(layers.Count);
|
||||
foreach(var layer in layers)
|
||||
{
|
||||
if (layer.rig != null && layer.active)
|
||||
{
|
||||
rigs.Add(layer.rig);
|
||||
}
|
||||
}
|
||||
|
||||
TransferMotionToConstraint(rigBuilder, rigs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes motion from the skeleton to the constraints in the Rig.
|
||||
/// </summary>
|
||||
/// <param name="rig">The Rig whose RigConstraints are to be baked.</param>
|
||||
public static void TransferMotionToConstraint(Rig rig)
|
||||
{
|
||||
var rigBuilder = rig.GetComponentInParent<RigBuilder>();
|
||||
if (rigBuilder == null)
|
||||
throw new InvalidOperationException("No rigbuilder was found in the hierarchy.");
|
||||
|
||||
TransferMotionToConstraint(rigBuilder, new Rig[]{rig});
|
||||
}
|
||||
|
||||
private static void TransferMotionToConstraint(RigBuilder rigBuilder, IEnumerable<Rig> rigs)
|
||||
{
|
||||
var constraints = new List<IRigConstraint>();
|
||||
foreach(var rig in rigs)
|
||||
{
|
||||
constraints.AddRange(RigUtils.GetConstraints(rig));
|
||||
}
|
||||
|
||||
var clip = AnimationWindowUtils.activeAnimationClip;
|
||||
|
||||
// Make sure we have a clip selected
|
||||
if (clip == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"There is no clip to work on." +
|
||||
" The animation window must be open with an active clip!");
|
||||
}
|
||||
|
||||
AnimationClip editableClip = clip;
|
||||
if (!GetEditableClip(ref editableClip))
|
||||
return;
|
||||
|
||||
AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder);
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(editableClip, kBakeToConstraintUndoLabel);
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
if (editableClip != clip)
|
||||
AddClipToAnimatorController(animator, editableClip);
|
||||
|
||||
var bindingsToRemove = new HashSet<EditorCurveBinding>();
|
||||
|
||||
// Remove weight curve & force constraint to be active
|
||||
if (Preferences.forceConstraintWeightOnBake)
|
||||
{
|
||||
AnimationCurve oneWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 1f);
|
||||
|
||||
foreach(var rig in rigs)
|
||||
{
|
||||
AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, rig), oneWeightCurve);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(IRigConstraint constraint in constraints)
|
||||
{
|
||||
var bakeParameters = FindBakeParameters(constraint);
|
||||
if (bakeParameters == null || !bakeParameters.canBakeToConstraint)
|
||||
continue;
|
||||
|
||||
// Flush out animation mode modifications
|
||||
AnimationMode.BeginSampling();
|
||||
AnimationMode.EndSampling();
|
||||
|
||||
var bindings = bakeParameters.GetSourceCurveBindings(rigBuilder, constraint);
|
||||
BakeToConstraint(rigBuilder, constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToConstraintCurveFilterOptions);
|
||||
|
||||
bindingsToRemove.UnionWith(bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint));
|
||||
}
|
||||
|
||||
if (Preferences.bakeToConstraintAndRemoveCurves)
|
||||
RemoveCurves(editableClip, bindingsToRemove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes motion from the skeleton to the RigConstraint
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of RigConstraint that is to be baked.</typeparam>
|
||||
/// <param name="constraint">The RigConstraint that will be baked to.</param>
|
||||
public static void TransferMotionToConstraint<T>(T constraint)
|
||||
where T : MonoBehaviour, IRigConstraint
|
||||
{
|
||||
var rigBuilder = constraint.GetComponentInParent<RigBuilder>();
|
||||
if (rigBuilder == null)
|
||||
throw new InvalidOperationException("No rigbuilder was found in the hierarchy.");
|
||||
|
||||
var bakeParameters = FindBakeParameters(constraint);
|
||||
if (bakeParameters == null)
|
||||
throw new InvalidOperationException(string.Format("Could not find BakeParameters class for constraint {0}.", constraint != null ? constraint.ToString() : "no-name"));
|
||||
|
||||
if (!bakeParameters.canBakeToSkeleton)
|
||||
throw new InvalidOperationException("Constraint disallows transfering motion to constraint.");
|
||||
|
||||
var bindings = bakeParameters.GetSourceCurveBindings(rigBuilder, constraint);
|
||||
var clip = AnimationWindowUtils.activeAnimationClip;
|
||||
|
||||
// Make sure we have a clip selected
|
||||
if (clip == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"There is no clip to work on." +
|
||||
" The animation window must be open with an active clip!");
|
||||
}
|
||||
|
||||
AnimationClip editableClip = clip;
|
||||
if (!GetEditableClip(ref editableClip))
|
||||
return;
|
||||
|
||||
AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder);
|
||||
|
||||
Undo.RegisterCompleteObjectUndo(editableClip, kBakeToConstraintUndoLabel);
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
if (editableClip != clip)
|
||||
AddClipToAnimatorController(animator, editableClip);
|
||||
|
||||
// Remove weight curve & force constraint to be active
|
||||
if (Preferences.forceConstraintWeightOnBake)
|
||||
{
|
||||
AnimationCurve oneWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 1f);
|
||||
AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, constraint), oneWeightCurve);
|
||||
}
|
||||
|
||||
BakeToConstraint(constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToConstraintCurveFilterOptions);
|
||||
|
||||
if (Preferences.bakeToConstraintAndRemoveCurves)
|
||||
RemoveCurves(editableClip, bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint));
|
||||
}
|
||||
|
||||
internal static void BakeToConstraint<T>(T constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable<EditorCurveBinding> bindings, CurveFilterOptions filterOptions)
|
||||
where T : MonoBehaviour, IRigConstraint
|
||||
{
|
||||
// Make sure we have a rigbuilder (which guarantees an animator).
|
||||
var rigBuilder = constraint.GetComponentInParent<RigBuilder>();
|
||||
if(rigBuilder == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"No rigbuilder was found in the hierarchy. " +
|
||||
"A RigBuilder and Animator are required to construct valid bindings.");
|
||||
}
|
||||
|
||||
BakeToConstraint(rigBuilder, constraint, clip, defaultPoseClip, bindings, filterOptions);
|
||||
}
|
||||
|
||||
private static void BakeToConstraint(RigBuilder rigBuilder, IRigConstraint constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable<EditorCurveBinding> bindings, CurveFilterOptions filterOptions)
|
||||
{
|
||||
// Make sure the base constraint is valid
|
||||
if (constraint == null || !constraint.IsValid())
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format("The rig constraint {0} is not a valid constraint.",
|
||||
constraint != null ? constraint.ToString() : ""));
|
||||
}
|
||||
|
||||
// Check if the constraint is inverse solvable
|
||||
var inverseConstraint = FindInverseRigConstraint(constraint);
|
||||
if (inverseConstraint == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format("No inverse rig constraint could be found for {0}.",
|
||||
constraint.ToString()));
|
||||
}
|
||||
else if (!inverseConstraint.IsValid())
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format("The inverse rig constrain {1} for {0} is not a valid constraint.",
|
||||
constraint.ToString(),
|
||||
inverseConstraint.ToString()));
|
||||
}
|
||||
|
||||
var overrides = new Dictionary<IRigConstraint, IRigConstraint>();
|
||||
overrides.Add(constraint, inverseConstraint);
|
||||
|
||||
using(var graph = new EvaluationGraph(rigBuilder, clip, defaultPoseClip, overrides, constraint))
|
||||
{
|
||||
BakeCurvesToClip(clip, bindings, rigBuilder, graph, filterOptions);
|
||||
}
|
||||
}
|
||||
|
||||
private static AnimationClip CreateDefaultPose(RigBuilder rigBuilder)
|
||||
{
|
||||
if(rigBuilder == null)
|
||||
throw new ArgumentNullException("It is not possible to bake curves without an RigBuilder.");
|
||||
|
||||
var defaultPoseClip = new AnimationClip() { name = "DefaultPose" };
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
return defaultPoseClip;
|
||||
|
||||
var bindings = new List<EditorCurveBinding>();
|
||||
|
||||
var gameObjects = new Queue<GameObject>();
|
||||
gameObjects.Enqueue(rigBuilder.gameObject);
|
||||
|
||||
while (gameObjects.Count > 0)
|
||||
{
|
||||
var gameObject = gameObjects.Dequeue();
|
||||
|
||||
EditorCurveBinding[] allBindings = AnimationUtility.GetAnimatableBindings(gameObject, rigBuilder.gameObject);
|
||||
foreach (var binding in allBindings)
|
||||
{
|
||||
if (binding.isPPtrCurve)
|
||||
continue;
|
||||
|
||||
if (binding.type == typeof(GameObject))
|
||||
continue;
|
||||
|
||||
UnityEngine.Object target = gameObject.GetComponent(binding.type);
|
||||
if (!AnimationMode.IsPropertyAnimated(target, binding.propertyName))
|
||||
continue;
|
||||
|
||||
bindings.Add(binding);
|
||||
}
|
||||
|
||||
// Iterate over all child GOs
|
||||
for (int i = 0; i < gameObject.transform.childCount; i++)
|
||||
{
|
||||
Transform childTransform = gameObject.transform.GetChild(i);
|
||||
gameObjects.Enqueue(childTransform.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush out animation mode modifications
|
||||
AnimationMode.BeginSampling();
|
||||
AnimationMode.EndSampling();
|
||||
|
||||
foreach(var binding in bindings)
|
||||
{
|
||||
float floatValue;
|
||||
AnimationUtility.GetFloatValue(rigBuilder.gameObject, binding, out floatValue);
|
||||
|
||||
var key = new Keyframe(0f, floatValue);
|
||||
var curve = new AnimationCurve(new Keyframe[] {key});
|
||||
defaultPoseClip.SetCurve(binding.path, binding.type, binding.propertyName, curve);
|
||||
}
|
||||
|
||||
return defaultPoseClip;
|
||||
}
|
||||
|
||||
private static void BakeCurvesToClip(AnimationClip clip, IEnumerable<EditorCurveBinding> bindings, RigBuilder rigBuilder, IEvaluationGraph graph, CurveFilterOptions filterOptions)
|
||||
{
|
||||
if(rigBuilder == null)
|
||||
throw new ArgumentNullException("It is not possible to bake curves without an RigBuilder.");
|
||||
|
||||
if (clip == null)
|
||||
throw new ArgumentNullException("It is not possible to bake curves to a clip that is null.");
|
||||
|
||||
if (!AnimationMode.InAnimationMode())
|
||||
throw new ArgumentException("AnimationMode must be active during bake operation.");
|
||||
|
||||
var animator = rigBuilder.GetComponent<Animator>();
|
||||
|
||||
var recorder = new GameObjectRecorder(animator.gameObject);
|
||||
foreach (var binding in bindings)
|
||||
recorder.Bind(binding);
|
||||
|
||||
var frameCount = (int)(clip.length * clip.frameRate);
|
||||
float dt = 1f / clip.frameRate;
|
||||
float time = 0f;
|
||||
|
||||
graph?.Evaluate(0f);
|
||||
recorder.TakeSnapshot(0f);
|
||||
|
||||
for (int frame = 1; frame <= frameCount; ++frame)
|
||||
{
|
||||
time = frame / clip.frameRate;
|
||||
graph?.Evaluate(time);
|
||||
recorder.TakeSnapshot(dt);
|
||||
}
|
||||
|
||||
var tempClip = new AnimationClip();
|
||||
recorder.SaveToClip(tempClip, clip.frameRate, filterOptions);
|
||||
CopyCurvesToClip(tempClip, clip);
|
||||
}
|
||||
|
||||
private static void RemoveCurves(AnimationClip clip, IEnumerable<EditorCurveBinding> bindings)
|
||||
{
|
||||
if (clip == null)
|
||||
throw new ArgumentNullException("The destination animation clip cannot be null.");
|
||||
|
||||
var rotationBinding = new EditorCurveBinding();
|
||||
foreach(var binding in bindings)
|
||||
{
|
||||
// Remove the correct editor curve binding for a rotation curves
|
||||
if (EditorCurveBindingUtils.RemapRotationBinding(clip, binding, ref rotationBinding))
|
||||
AnimationUtility.SetEditorCurve(clip, rotationBinding, null);
|
||||
else
|
||||
AnimationUtility.SetEditorCurve(clip, binding, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyCurvesToClip(AnimationClip fromClip, AnimationClip toClip)
|
||||
{
|
||||
var rotationBinding = new EditorCurveBinding();
|
||||
var bindings = AnimationUtility.GetCurveBindings(fromClip);
|
||||
foreach(var binding in bindings)
|
||||
{
|
||||
var curve = AnimationUtility.GetEditorCurve(fromClip, binding);
|
||||
|
||||
if (EditorCurveBindingUtils.RemapRotationBinding(toClip, binding, ref rotationBinding))
|
||||
AnimationUtility.SetEditorCurve(toClip, rotationBinding, curve);
|
||||
else
|
||||
AnimationUtility.SetEditorCurve(toClip, binding, curve);
|
||||
}
|
||||
}
|
||||
|
||||
private static IRigConstraint FindInverseRigConstraint(IRigConstraint constraint)
|
||||
{
|
||||
if (constraint == null)
|
||||
return null;
|
||||
|
||||
var inverseConstraintTypes = TypeCache.GetTypesWithAttribute<InverseRigConstraintAttribute>();
|
||||
foreach (var inverseConstraintType in inverseConstraintTypes)
|
||||
{
|
||||
var attribute = inverseConstraintType.GetCustomAttribute<InverseRigConstraintAttribute>();
|
||||
|
||||
if (attribute.baseConstraint == constraint.GetType())
|
||||
return (IRigConstraint)Activator.CreateInstance(inverseConstraintType, new object[] { constraint });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static IBakeParameters FindBakeParameters(IRigConstraint constraint)
|
||||
{
|
||||
var constraintType = constraint.GetType();
|
||||
var bakeParametersTypes = TypeCache.GetTypesWithAttribute<BakeParametersAttribute>();
|
||||
foreach (var bakeParametersType in bakeParametersTypes)
|
||||
{
|
||||
if (!typeof(IBakeParameters).IsAssignableFrom(bakeParametersType))
|
||||
continue;
|
||||
|
||||
var attribute = bakeParametersType.GetCustomAttribute<BakeParametersAttribute>();
|
||||
if (attribute.constraintType == constraintType)
|
||||
return (IBakeParameters)Activator.CreateInstance(bakeParametersType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static EditorCurveBinding GetWeightCurveBinding(RigBuilder rigBuilder, Rig rig)
|
||||
{
|
||||
var path = AnimationUtility.CalculateTransformPath(rig.transform, rigBuilder.transform);
|
||||
var binding = EditorCurveBinding.FloatCurve(path, typeof(Rig), ConstraintProperties.s_Weight);
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static EditorCurveBinding GetWeightCurveBinding<T>(RigBuilder rigBuilder, T constraint)
|
||||
where T : MonoBehaviour, IRigConstraint
|
||||
{
|
||||
var path = AnimationUtility.CalculateTransformPath(constraint.transform, rigBuilder.transform);
|
||||
var binding = EditorCurveBinding.FloatCurve(path, typeof(T), ConstraintProperties.s_Weight);
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static bool GetEditableClip(ref AnimationClip clip)
|
||||
{
|
||||
if (clip == null)
|
||||
return false;
|
||||
|
||||
if ((clip.hideFlags & HideFlags.NotEditable) != 0)
|
||||
{
|
||||
var path = EditorUtility.SaveFilePanelInProject(
|
||||
"Save new clip",
|
||||
clip.name + "(Clone)",
|
||||
"anim",
|
||||
string.Format("Create an editable clone of the readonly clip {0}.", clip.name));
|
||||
|
||||
if (path == "")
|
||||
return false;
|
||||
|
||||
clip = UnityEngine.Object.Instantiate(clip);
|
||||
AssetDatabase.CreateAsset(clip, path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void AddClipToAnimatorController(Animator animator, AnimationClip clip)
|
||||
{
|
||||
RuntimeAnimatorController runtimeController = animator.runtimeAnimatorController;
|
||||
|
||||
AnimatorController effectiveController = runtimeController as AnimatorController;
|
||||
if (effectiveController == null)
|
||||
{
|
||||
AnimatorOverrideController overrideController = runtimeController as AnimatorOverrideController;
|
||||
if (overrideController != null)
|
||||
{
|
||||
effectiveController = overrideController.runtimeAnimatorController as AnimatorController;
|
||||
}
|
||||
}
|
||||
|
||||
if (effectiveController != null)
|
||||
{
|
||||
string title = "Add clip to controller";
|
||||
string message = String.Format("Do you want to add clip '{0}' to controller '{1}'?", clip.name, effectiveController.name);
|
||||
if (EditorUtility.DisplayDialog(title, message, "yes", "no", DialogOptOutDecisionType.ForThisSession, "com.unity.animation.rigging-add-clip-to-controller"))
|
||||
{
|
||||
effectiveController.AddMotion(clip);
|
||||
AnimationWindowUtils.activeAnimationClip = clip;
|
||||
AnimationWindowUtils.StartPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dc49fe6569e4a94196eaacaa0de329c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,82 @@
|
||||
using UnityEngine.Animations.Rigging;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
[CustomEditor(typeof(BoneRenderer))]
|
||||
[CanEditMultipleObjects]
|
||||
class BoneRendererInspector : Editor
|
||||
{
|
||||
static readonly GUIContent k_BoneSizeLabel = new GUIContent("Bone Size");
|
||||
static readonly GUIContent k_BoneColorLabel = new GUIContent("Color");
|
||||
static readonly GUIContent k_BoneShapeLabel = new GUIContent("Shape");
|
||||
static readonly GUIContent k_TripodSizeLabel = new GUIContent("Tripod Size");
|
||||
|
||||
SerializedProperty m_DrawBones;
|
||||
SerializedProperty m_BoneShape;
|
||||
SerializedProperty m_BoneSize;
|
||||
SerializedProperty m_BoneColor;
|
||||
|
||||
SerializedProperty m_DrawTripods;
|
||||
SerializedProperty m_TripodSize;
|
||||
|
||||
SerializedProperty m_Transforms;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
m_DrawBones = serializedObject.FindProperty("drawBones");
|
||||
m_BoneSize = serializedObject.FindProperty("boneSize");
|
||||
m_BoneShape = serializedObject.FindProperty("boneShape");
|
||||
m_BoneColor = serializedObject.FindProperty("boneColor");
|
||||
|
||||
m_DrawTripods = serializedObject.FindProperty("drawTripods");
|
||||
m_TripodSize = serializedObject.FindProperty("tripodSize");
|
||||
|
||||
m_Transforms = serializedObject.FindProperty("m_Transforms");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(m_DrawBones, k_BoneSizeLabel);
|
||||
using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue))
|
||||
EditorGUILayout.PropertyField(m_BoneSize, GUIContent.none);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue))
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(m_BoneShape, k_BoneShapeLabel);
|
||||
EditorGUILayout.PropertyField(m_BoneColor, k_BoneColorLabel);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(m_DrawTripods, k_TripodSizeLabel);
|
||||
using (new EditorGUI.DisabledScope(!m_DrawTripods.boolValue))
|
||||
EditorGUILayout.PropertyField(m_TripodSize, GUIContent.none);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
bool isDragPerformed = Event.current.type == EventType.DragPerform;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(m_Transforms, true);
|
||||
bool boneRendererDirty = EditorGUI.EndChangeCheck();
|
||||
boneRendererDirty |= Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed";
|
||||
boneRendererDirty |= Event.current.type == EventType.Used && isDragPerformed;
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (boneRendererDirty)
|
||||
{
|
||||
for (int i = 0; i < targets.Length; i++)
|
||||
{
|
||||
var boneRenderer = targets[i] as BoneRenderer;
|
||||
boneRenderer.ExtractBones();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56b70e56bbf6f4b739c75015365a6dc1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,498 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Experimental.SceneManagement;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Animations.Rigging;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
using BoneShape = BoneRenderer.BoneShape;
|
||||
|
||||
[InitializeOnLoad]
|
||||
static class BoneRendererUtils
|
||||
{
|
||||
private class BatchRenderer
|
||||
{
|
||||
const int kMaxDrawMeshInstanceCount = 1023;
|
||||
|
||||
public enum SubMeshType
|
||||
{
|
||||
BoneFaces,
|
||||
BoneWire,
|
||||
Count
|
||||
}
|
||||
|
||||
public Mesh mesh;
|
||||
public Material material;
|
||||
|
||||
private List<Matrix4x4> m_Matrices = new List<Matrix4x4>();
|
||||
private List<Vector4> m_Colors = new List<Vector4>();
|
||||
private List<Vector4> m_Highlights = new List<Vector4>();
|
||||
|
||||
public void AddInstance(Matrix4x4 matrix, Color color, Color highlight)
|
||||
{
|
||||
m_Matrices.Add(matrix);
|
||||
m_Colors.Add(color);
|
||||
m_Highlights.Add(highlight);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Matrices.Clear();
|
||||
m_Colors.Clear();
|
||||
m_Highlights.Clear();
|
||||
}
|
||||
|
||||
private static int RenderChunkCount(int totalCount)
|
||||
{
|
||||
return Mathf.CeilToInt((totalCount / (float)kMaxDrawMeshInstanceCount));
|
||||
}
|
||||
|
||||
private static T[] GetRenderChunk<T>(List<T> array, int chunkIndex)
|
||||
{
|
||||
int rangeCount = (chunkIndex < (RenderChunkCount(array.Count) - 1)) ?
|
||||
kMaxDrawMeshInstanceCount : array.Count - (chunkIndex * kMaxDrawMeshInstanceCount);
|
||||
|
||||
return array.GetRange(chunkIndex * kMaxDrawMeshInstanceCount, rangeCount).ToArray();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
if (m_Matrices.Count == 0 || m_Colors.Count == 0 || m_Highlights.Count == 0)
|
||||
return;
|
||||
|
||||
int count = System.Math.Min(m_Matrices.Count, System.Math.Min(m_Colors.Count, m_Highlights.Count));
|
||||
|
||||
Material mat = material;
|
||||
mat.SetPass(0);
|
||||
|
||||
MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
|
||||
CommandBuffer cb = new CommandBuffer();
|
||||
|
||||
Matrix4x4[] matrices = null;
|
||||
|
||||
int chunkCount = RenderChunkCount(count);
|
||||
for (int i = 0; i < chunkCount; ++i)
|
||||
{
|
||||
cb.Clear();
|
||||
matrices = GetRenderChunk(m_Matrices, i);
|
||||
propertyBlock.SetVectorArray("_Color", GetRenderChunk(m_Colors, i));
|
||||
|
||||
material.DisableKeyword("WIRE_ON");
|
||||
cb.DrawMeshInstanced(mesh, (int)SubMeshType.BoneFaces, material, 0, matrices, matrices.Length, propertyBlock);
|
||||
Graphics.ExecuteCommandBuffer(cb);
|
||||
|
||||
cb.Clear();
|
||||
propertyBlock.SetVectorArray("_Color", GetRenderChunk(m_Highlights, i));
|
||||
|
||||
material.EnableKeyword("WIRE_ON");
|
||||
cb.DrawMeshInstanced(mesh, (int)SubMeshType.BoneWire, material, 0, matrices, matrices.Length, propertyBlock);
|
||||
Graphics.ExecuteCommandBuffer(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<BoneRenderer> s_BoneRendererComponents = new List<BoneRenderer>();
|
||||
|
||||
private static BatchRenderer s_PyramidMeshRenderer;
|
||||
private static BatchRenderer s_BoxMeshRenderer;
|
||||
|
||||
private static Material s_Material;
|
||||
|
||||
private const float k_Epsilon = 1e-5f;
|
||||
|
||||
private const float k_BoneBaseSize = 2f;
|
||||
private const float k_BoneTipSize = 0.5f;
|
||||
|
||||
private static int s_ButtonHash = "BoneHandle".GetHashCode();
|
||||
|
||||
private static int s_VisibleLayersCache = 0;
|
||||
|
||||
static BoneRendererUtils()
|
||||
{
|
||||
BoneRenderer.onAddBoneRenderer += OnAddBoneRenderer;
|
||||
BoneRenderer.onRemoveBoneRenderer += OnRemoveBoneRenderer;
|
||||
SceneVisibilityManager.visibilityChanged += OnVisibilityChanged;
|
||||
EditorApplication.hierarchyChanged += OnHierarchyChanged;
|
||||
|
||||
SceneView.duringSceneGui += DrawSkeletons;
|
||||
|
||||
s_VisibleLayersCache = Tools.visibleLayers;
|
||||
}
|
||||
|
||||
private static Material material
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_Material)
|
||||
{
|
||||
Shader shader = (Shader)EditorGUIUtility.LoadRequired("BoneHandles.shader");
|
||||
s_Material = new Material(shader);
|
||||
s_Material.hideFlags = HideFlags.DontSaveInEditor;
|
||||
s_Material.enableInstancing = true;
|
||||
}
|
||||
|
||||
return s_Material;
|
||||
}
|
||||
}
|
||||
|
||||
private static BatchRenderer pyramidMeshRenderer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_PyramidMeshRenderer == null)
|
||||
{
|
||||
var mesh = new Mesh();
|
||||
mesh.name = "BoneRendererPyramidMesh";
|
||||
mesh.subMeshCount = (int)BatchRenderer.SubMeshType.Count;
|
||||
mesh.hideFlags = HideFlags.DontSave;
|
||||
|
||||
// Bone vertices
|
||||
Vector3[] vertices = new Vector3[]
|
||||
{
|
||||
new Vector3(0.0f, 1.0f, 0.0f),
|
||||
new Vector3(0.0f, 0.0f, -1.0f),
|
||||
new Vector3(-0.9f, 0.0f, 0.5f),
|
||||
new Vector3(0.9f, 0.0f, 0.5f),
|
||||
};
|
||||
|
||||
mesh.vertices = vertices;
|
||||
|
||||
// Build indices for different sub meshes
|
||||
int[] boneFaceIndices = new int[]
|
||||
{
|
||||
0, 2, 1,
|
||||
0, 1, 3,
|
||||
0, 3, 2,
|
||||
1, 2, 3
|
||||
};
|
||||
mesh.SetIndices(boneFaceIndices, MeshTopology.Triangles, (int)BatchRenderer.SubMeshType.BoneFaces);
|
||||
|
||||
int[] boneWireIndices = new int[]
|
||||
{
|
||||
0, 1, 0, 2, 0, 3, 1, 2, 2, 3, 3, 1
|
||||
};
|
||||
mesh.SetIndices(boneWireIndices, MeshTopology.Lines, (int)BatchRenderer.SubMeshType.BoneWire);
|
||||
|
||||
s_PyramidMeshRenderer = new BatchRenderer()
|
||||
{
|
||||
mesh = mesh,
|
||||
material = material
|
||||
};
|
||||
}
|
||||
|
||||
return s_PyramidMeshRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
private static BatchRenderer boxMeshRenderer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_BoxMeshRenderer == null)
|
||||
{
|
||||
var mesh = new Mesh();
|
||||
mesh.name = "BoneRendererBoxMesh";
|
||||
mesh.subMeshCount = (int)BatchRenderer.SubMeshType.Count;
|
||||
mesh.hideFlags = HideFlags.DontSave;
|
||||
|
||||
// Bone vertices
|
||||
Vector3[] vertices = new Vector3[]
|
||||
{
|
||||
new Vector3(-0.5f, 0.0f, 0.5f),
|
||||
new Vector3(0.5f, 0.0f, 0.5f),
|
||||
new Vector3(0.5f, 0.0f, -0.5f),
|
||||
new Vector3(-0.5f, 0.0f, -0.5f),
|
||||
new Vector3(-0.5f, 1.0f, 0.5f),
|
||||
new Vector3(0.5f, 1.0f, 0.5f),
|
||||
new Vector3(0.5f, 1.0f, -0.5f),
|
||||
new Vector3(-0.5f, 1.0f, -0.5f)
|
||||
};
|
||||
|
||||
mesh.vertices = vertices;
|
||||
|
||||
// Build indices for different sub meshes
|
||||
int[] boneFaceIndices = new int[]
|
||||
{
|
||||
0, 2, 1,
|
||||
0, 3, 2,
|
||||
|
||||
0, 1, 5,
|
||||
0, 5, 4,
|
||||
|
||||
1, 2, 6,
|
||||
1, 6, 5,
|
||||
|
||||
2, 3, 7,
|
||||
2, 7, 6,
|
||||
|
||||
3, 0, 4,
|
||||
3, 4, 7,
|
||||
|
||||
4, 5, 6,
|
||||
4, 6, 7
|
||||
};
|
||||
mesh.SetIndices(boneFaceIndices, MeshTopology.Triangles, (int)BatchRenderer.SubMeshType.BoneFaces);
|
||||
|
||||
int[] boneWireIndices = new int[]
|
||||
{
|
||||
0, 1, 1, 2, 2, 3, 3, 0,
|
||||
4, 5, 5, 6, 6, 7, 7, 4,
|
||||
0, 4, 1, 5, 2, 6, 3, 7
|
||||
};
|
||||
mesh.SetIndices(boneWireIndices, MeshTopology.Lines, (int)BatchRenderer.SubMeshType.BoneWire);
|
||||
|
||||
s_BoxMeshRenderer = new BatchRenderer()
|
||||
{
|
||||
mesh = mesh,
|
||||
material = material
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return s_BoxMeshRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
private static Matrix4x4 ComputeBoneMatrix(Vector3 start, Vector3 end, float length, float size)
|
||||
{
|
||||
Vector3 direction = (end - start) / length;
|
||||
Vector3 tangent = Vector3.Cross(direction, Vector3.up);
|
||||
if (Vector3.SqrMagnitude(tangent) < 0.1f)
|
||||
tangent = Vector3.Cross(direction, Vector3.right);
|
||||
tangent.Normalize();
|
||||
Vector3 bitangent = Vector3.Cross(direction, tangent);
|
||||
|
||||
float scale = length * k_BoneBaseSize * size;
|
||||
|
||||
return new Matrix4x4(
|
||||
new Vector4(tangent.x * scale, tangent.y * scale, tangent.z * scale , 0f),
|
||||
new Vector4(direction.x * length, direction.y * length, direction.z * length, 0f),
|
||||
new Vector4(bitangent.x * scale, bitangent.y * scale, bitangent.z * scale , 0f),
|
||||
new Vector4(start.x, start.y, start.z, 1f));
|
||||
}
|
||||
|
||||
static void DrawSkeletons(SceneView sceneview)
|
||||
{
|
||||
if (Tools.visibleLayers != s_VisibleLayersCache)
|
||||
{
|
||||
OnVisibilityChanged();
|
||||
s_VisibleLayersCache = Tools.visibleLayers;
|
||||
}
|
||||
|
||||
var gizmoColor = Gizmos.color;
|
||||
|
||||
pyramidMeshRenderer.Clear();
|
||||
boxMeshRenderer.Clear();
|
||||
|
||||
for (var i = 0; i < s_BoneRendererComponents.Count; i++)
|
||||
{
|
||||
var boneRenderer = s_BoneRendererComponents[i];
|
||||
|
||||
if (boneRenderer.bones == null)
|
||||
continue;
|
||||
|
||||
PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
if (prefabStage != null)
|
||||
{
|
||||
StageHandle stageHandle = prefabStage.stageHandle;
|
||||
if (stageHandle.IsValid() && !stageHandle.Contains(boneRenderer.gameObject))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (boneRenderer.drawBones)
|
||||
{
|
||||
var size = boneRenderer.boneSize * 0.025f;
|
||||
var shape = boneRenderer.boneShape;
|
||||
var color = boneRenderer.boneColor;
|
||||
var nubColor = new Color(color.r, color.g, color.b, color.a);
|
||||
var selectionColor = Color.white;
|
||||
|
||||
for (var j = 0; j < boneRenderer.bones.Length; j++)
|
||||
{
|
||||
var bone = boneRenderer.bones[j];
|
||||
if (bone.first == null || bone.second == null)
|
||||
continue;
|
||||
|
||||
DoBoneRender(bone.first, bone.second, shape, color, size);
|
||||
}
|
||||
|
||||
for (var k = 0; k < boneRenderer.tips.Length; k++)
|
||||
{
|
||||
var tip = boneRenderer.tips[k];
|
||||
if (tip == null)
|
||||
continue;
|
||||
|
||||
DoBoneRender(tip, null, shape, color, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (boneRenderer.drawTripods)
|
||||
{
|
||||
var size = boneRenderer.tripodSize * 0.025f;
|
||||
for (var j = 0; j < boneRenderer.transforms.Length; j++)
|
||||
{
|
||||
var tripodSize = 1f;
|
||||
var transform = boneRenderer.transforms[j];
|
||||
if (transform == null)
|
||||
continue;
|
||||
|
||||
var position = transform.position;
|
||||
var xAxis = position + transform.rotation * Vector3.right * size * tripodSize;
|
||||
var yAxis = position + transform.rotation * Vector3.up * size * tripodSize;
|
||||
var zAxis = position + transform.rotation * Vector3.forward * size * tripodSize;
|
||||
|
||||
Handles.color = Color.red;
|
||||
Handles.DrawLine(position, xAxis);
|
||||
Handles.color = Color.green;
|
||||
Handles.DrawLine(position, yAxis);
|
||||
Handles.color = Color.blue;
|
||||
Handles.DrawLine(position, zAxis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pyramidMeshRenderer.Render();
|
||||
boxMeshRenderer.Render();
|
||||
|
||||
Gizmos.color = gizmoColor;
|
||||
}
|
||||
|
||||
|
||||
private static void DoBoneRender(Transform transform, Transform childTransform, BoneShape shape, Color color, float size)
|
||||
{
|
||||
Vector3 start = transform.position;
|
||||
Vector3 end = childTransform != null ? childTransform.position : start;
|
||||
|
||||
GameObject boneGO = transform.gameObject;
|
||||
|
||||
float length = (end - start).magnitude;
|
||||
bool tipBone = (length < k_Epsilon);
|
||||
|
||||
int id = GUIUtility.GetControlID(s_ButtonHash, FocusType.Passive);
|
||||
Event evt = Event.current;
|
||||
|
||||
switch (evt.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.Layout:
|
||||
{
|
||||
HandleUtility.AddControl(id, tipBone ? HandleUtility.DistanceToCircle(start, k_BoneTipSize * size * 0.5f) : HandleUtility.DistanceToLine(start, end));
|
||||
break;
|
||||
}
|
||||
case EventType.MouseMove:
|
||||
if (id == HandleUtility.nearestControl)
|
||||
HandleUtility.Repaint();
|
||||
break;
|
||||
case EventType.MouseDown:
|
||||
{
|
||||
if (evt.alt)
|
||||
break;
|
||||
|
||||
if (HandleUtility.nearestControl == id && evt.button == 0)
|
||||
{
|
||||
if (!SceneVisibilityManager.instance.IsPickingDisabled(boneGO, false))
|
||||
{
|
||||
GUIUtility.hotControl = id; // Grab mouse focus
|
||||
EditorHelper.HandleClickSelection(boneGO, evt);
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EventType.MouseDrag:
|
||||
{
|
||||
if (!evt.alt && GUIUtility.hotControl == id)
|
||||
{
|
||||
if (!SceneVisibilityManager.instance.IsPickingDisabled(boneGO, false))
|
||||
{
|
||||
DragAndDrop.PrepareStartDrag();
|
||||
DragAndDrop.objectReferences = new UnityEngine.Object[] {transform};
|
||||
DragAndDrop.StartDrag(ObjectNames.GetDragAndDropTitle(transform));
|
||||
|
||||
GUIUtility.hotControl = 0;
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EventType.MouseUp:
|
||||
{
|
||||
if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2))
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EventType.Repaint:
|
||||
{
|
||||
Color highlight = color;
|
||||
|
||||
bool hoveringBone = GUIUtility.hotControl == 0 && HandleUtility.nearestControl == id;
|
||||
hoveringBone = hoveringBone && !SceneVisibilityManager.instance.IsPickingDisabled(transform.gameObject, false);
|
||||
|
||||
if (hoveringBone)
|
||||
{
|
||||
highlight = Handles.preselectionColor;
|
||||
}
|
||||
else if (Selection.Contains(boneGO) || Selection.activeObject == boneGO)
|
||||
{
|
||||
highlight = Handles.selectedColor;
|
||||
}
|
||||
|
||||
if (tipBone)
|
||||
{
|
||||
Handles.color = highlight;
|
||||
Handles.SphereHandleCap(0, start, Quaternion.identity, k_BoneTipSize * size, EventType.Repaint);
|
||||
}
|
||||
else if (shape == BoneShape.Line)
|
||||
{
|
||||
Handles.color = highlight;
|
||||
Handles.DrawLine(start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shape == BoneShape.Pyramid)
|
||||
pyramidMeshRenderer.AddInstance(ComputeBoneMatrix(start, end, length, size), color, highlight);
|
||||
else // if (shape == BoneShape.Box)
|
||||
boxMeshRenderer.AddInstance(ComputeBoneMatrix(start, end, length, size), color, highlight);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnAddBoneRenderer(BoneRenderer obj)
|
||||
{
|
||||
s_BoneRendererComponents.Add(obj);
|
||||
}
|
||||
|
||||
public static void OnRemoveBoneRenderer(BoneRenderer obj)
|
||||
{
|
||||
s_BoneRendererComponents.Remove(obj);
|
||||
}
|
||||
|
||||
public static void OnVisibilityChanged()
|
||||
{
|
||||
foreach(var boneRenderer in s_BoneRendererComponents)
|
||||
{
|
||||
boneRenderer.Invalidate();
|
||||
}
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
public static void OnHierarchyChanged()
|
||||
{
|
||||
foreach(var boneRenderer in s_BoneRendererComponents)
|
||||
{
|
||||
boneRenderer.Invalidate();
|
||||
}
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47d08c0b946e946b5bd8fc603eeed5b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,85 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
static class CommonContent
|
||||
{
|
||||
public static readonly GUIContent constrainedAxesPosition = EditorGUIUtility.TrTextContent(
|
||||
"Constrained Axes",
|
||||
"Specifies the axes to which the constraint can apply translation."
|
||||
);
|
||||
|
||||
public static readonly GUIContent constrainedAxesRotation = EditorGUIUtility.TrTextContent(
|
||||
"Constrained Axes",
|
||||
"Specifies the axes to which the constraint can apply rotation."
|
||||
);
|
||||
|
||||
public static readonly GUIContent constrainedObject = EditorGUIUtility.TrTextContent(
|
||||
"Constrained Object",
|
||||
"The GameObject affected by the Source Objects."
|
||||
);
|
||||
|
||||
public static readonly GUIContent maintainOffset = EditorGUIUtility.TrTextContent(
|
||||
"Maintain Offset",
|
||||
"Specifies whether to maintain the initial offset between the Constrained Object and the Source Objects"
|
||||
);
|
||||
|
||||
public static readonly GUIContent maintainPositionOffset = EditorGUIUtility.TrTextContent(
|
||||
"Maintain Offset",
|
||||
"Specifies whether to maintain the initial position offset between the Constrained Object and the Source Objects."
|
||||
);
|
||||
|
||||
public static readonly GUIContent maintainRotationOffset = EditorGUIUtility.TrTextContent(
|
||||
"Maintain Offset",
|
||||
"Specifies whether to maintain the initial rotation offset between the Constrained Object and the Source Objects."
|
||||
);
|
||||
|
||||
public static readonly GUIContent maintainIKTargetOffset = EditorGUIUtility.TrTextContent(
|
||||
"Maintain Target Offset",
|
||||
"Specifies whether to maintain the initial offset between the Tip and the Target."
|
||||
);
|
||||
|
||||
public static readonly GUIContent offsetPosition = EditorGUIUtility.TrTextContent(
|
||||
"Offset",
|
||||
"Specifies an additional local space translation offset to apply to the Constrained Object, after it has been translated toward its target."
|
||||
);
|
||||
|
||||
public static readonly GUIContent offsetRotation = EditorGUIUtility.TrTextContent(
|
||||
"Offset",
|
||||
"Specifies an additional local space rotation offset to apply to the Constrained Object, after it has been rotated toward its target."
|
||||
);
|
||||
|
||||
public static readonly GUIContent settings = EditorGUIUtility.TrTextContent(
|
||||
"Settings"
|
||||
);
|
||||
|
||||
public static readonly GUIContent sourceObjects = EditorGUIUtility.TrTextContent(
|
||||
"Source Objects",
|
||||
"The list of GameObjects that influence the Constrained Object's position and orientation, and the amount of weight they contribute to the final pose. " +
|
||||
"The constraint applies linearly interpolated, weighted translation and rotation toward each target. " +
|
||||
"The order of Source Objects does not affect the result."
|
||||
);
|
||||
|
||||
public static readonly GUIContent sourceObjectsWeightedPosition = EditorGUIUtility.TrTextContent(
|
||||
"Source Objects",
|
||||
"The list of GameObjects that influence the Constrained Object's position, and the amount of weight they contribute to the final pose. " +
|
||||
"The constraint calculates translation toward each target to produce a weighted sum. " +
|
||||
"The order of Source Objects does not affect the result."
|
||||
);
|
||||
|
||||
public static readonly GUIContent sourceObjectsWeightedRotation = EditorGUIUtility.TrTextContent(
|
||||
"Source Objects",
|
||||
"The list of GameObjects that influence the Constrained Object's orientation, and the amount of weight they contribute to the final pose. " +
|
||||
"The constraint calculates rotation toward each target to produce a weighted sum. " +
|
||||
"The order of Source Objects does not affect the result."
|
||||
);
|
||||
|
||||
public static readonly GUIContent weight = EditorGUIUtility.TrTextContent(
|
||||
"Weight",
|
||||
"The overall weight of the constraint. " +
|
||||
"If set to 0, the constraint has no influence on the Constrained Object. " +
|
||||
"When set to 1, it applies full influence with the current settings. " +
|
||||
"Intermediate values are interpolated linearly."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f0a8cdd0db7492b962f3f63b841f506
|
||||
timeCreated: 1607533731
|
||||
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class that provides an easy way of retrieving EditorCurveBindings for common data types.
|
||||
/// </summary>
|
||||
public static class EditorCurveBindingUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Collects EditorCurveBindings for a Vector3 on a MonoBehavior.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type of the MonoBehavior the Vector3 is found on.</typeparam>
|
||||
/// <param name="root">The root to which the bindings are relative. Generally the root has the Animator which animates the Vector3.</param>
|
||||
/// <param name="component">The MonoBehavior on which the Vector3 is found.</param>
|
||||
/// <param name="propertyName">Name of the Vector3 variable we are constructing a binding for.</param>
|
||||
/// <param name="bindings">List to which the bindings for the Vector3 will be appended.</param>
|
||||
public static void CollectVector3Bindings<T>(Transform root, T component, string propertyName, List<EditorCurveBinding> bindings)
|
||||
where T : MonoBehaviour
|
||||
{
|
||||
if (root == null || component == null || propertyName == "" || bindings == null)
|
||||
throw new ArgumentNullException("Arguments cannot be null.");
|
||||
|
||||
var path = AnimationUtility.CalculateTransformPath(component.transform, root);
|
||||
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(T), propertyName + ".x"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(T), propertyName + ".y"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(T), propertyName + ".z"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects translation, rotation and scale bindings for a Transform component.
|
||||
/// </summary>
|
||||
/// <param name="root">The root to which the bindings are relative. Generally the root has the Animator which animates the Transform.</param>
|
||||
/// <param name="transform">The transform whose bindings are collected.</param>
|
||||
/// <param name="bindings">List to which the bindings for the Transform will be appended.</param>
|
||||
public static void CollectTRSBindings(Transform root, Transform transform, List<EditorCurveBinding> bindings)
|
||||
{
|
||||
CollectPositionBindings(root, transform, bindings);
|
||||
CollectRotationBindings(root, transform, bindings);
|
||||
CollectScaleBindings(root, transform, bindings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects translation, rotation bindings for a Transform component.
|
||||
/// </summary>
|
||||
/// <param name="root">The root to which the bindings are relative. Generally the root has the Animator which animates the Transform.</param>
|
||||
/// <param name="transform">The transform whose bindings are collected.</param>
|
||||
/// <param name="bindings">List to which the bindings for the Transform will be appended.</param>
|
||||
public static void CollectTRBindings(Transform root, Transform transform, List<EditorCurveBinding> bindings)
|
||||
{
|
||||
CollectPositionBindings(root, transform, bindings);
|
||||
CollectRotationBindings(root, transform, bindings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects translation bindings for a Transform component.
|
||||
/// </summary>
|
||||
/// <param name="root">The root to which the bindings are relative. Generally the root has the Animator which animates the Transform.</param>
|
||||
/// <param name="transform">The transform whose bindings are collected.</param>
|
||||
/// <param name="bindings">List to which the bindings for the Transform will be appended.</param>
|
||||
public static void CollectPositionBindings(Transform root, Transform transform, List<EditorCurveBinding> bindings)
|
||||
{
|
||||
if (root == null || transform == null || bindings == null)
|
||||
throw new ArgumentNullException("Arguments cannot be null.");
|
||||
|
||||
var path = AnimationUtility.CalculateTransformPath(transform, root);
|
||||
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalPosition.x"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalPosition.y"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalPosition.z"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects rotation bindings for a Transform component.
|
||||
/// </summary>
|
||||
/// <param name="root">The root to which the bindings are relative. Generally the root has the Animator which animates the Transform.</param>
|
||||
/// <param name="transform">The transform whose bindings are collected.</param>
|
||||
/// <param name="bindings">List to which the bindings for the Transform will be appended.</param>
|
||||
public static void CollectRotationBindings(Transform root, Transform transform, List<EditorCurveBinding> bindings)
|
||||
{
|
||||
if (root == null || transform == null || bindings == null)
|
||||
throw new ArgumentNullException("Arguments cannot be null.");
|
||||
|
||||
var path = AnimationUtility.CalculateTransformPath(transform, root);
|
||||
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "localEulerAnglesRaw.x"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "localEulerAnglesRaw.y"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "localEulerAnglesRaw.z"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects scale bindings for a Transform component.
|
||||
/// </summary>
|
||||
/// <param name="root">The root to which the bindings are relative. Generally the root has the Animator which animates the Transform.</param>
|
||||
/// <param name="transform">The transform whose bindings are collected.</param>
|
||||
/// <param name="bindings">List to which the bindings for the Transform will be appended.</param>
|
||||
public static void CollectScaleBindings(Transform root, Transform transform, List<EditorCurveBinding> bindings)
|
||||
{
|
||||
if (root == null || transform == null || bindings == null)
|
||||
throw new ArgumentNullException("Arguments cannot be null.");
|
||||
|
||||
var path = AnimationUtility.CalculateTransformPath(transform, root);
|
||||
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalScale.x"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalScale.y"));
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalScale.z"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects the binding for a single float property on a MonoBehavior.
|
||||
/// </summary>
|
||||
/// <param name="root">The root to which the bindings are relative. Generally the root has the Animator which animates the float property.</param>
|
||||
/// <param name="component">The component on which the property is found.</param>
|
||||
/// <param name="propertyName">The name of the float property whose bindings are collected.</param>
|
||||
/// <param name="bindings">List to which the bindings for the Transform will be appended.</param>
|
||||
public static void CollectPropertyBindings(Transform root, MonoBehaviour component, string propertyName, List<EditorCurveBinding> bindings)
|
||||
{
|
||||
if (root == null || component == null || bindings == null)
|
||||
throw new ArgumentNullException("Arguments cannot be null.");
|
||||
|
||||
var path = AnimationUtility.CalculateTransformPath(component.transform, root);
|
||||
|
||||
bindings.Add(EditorCurveBinding.FloatCurve(path, component.GetType(), propertyName));
|
||||
}
|
||||
|
||||
internal static bool RemapRotationBinding(AnimationClip clip, EditorCurveBinding binding, ref EditorCurveBinding rotationBinding)
|
||||
{
|
||||
if (!binding.propertyName.StartsWith("localEulerAngles"))
|
||||
return false;
|
||||
|
||||
string suffix = binding.propertyName.Split('.')[1];
|
||||
|
||||
rotationBinding = binding;
|
||||
|
||||
// Euler Angles
|
||||
rotationBinding.propertyName = "localEulerAnglesRaw." + suffix;
|
||||
if (AnimationUtility.GetEditorCurve(clip, rotationBinding) != null)
|
||||
return true;
|
||||
|
||||
// Euler Angles (Quaternion) interpolation
|
||||
rotationBinding.propertyName = "localEulerAnglesBaked." + suffix;
|
||||
if (AnimationUtility.GetEditorCurve(clip, rotationBinding) != null)
|
||||
return true;
|
||||
|
||||
// Quaternion interpolation
|
||||
rotationBinding.propertyName = "localEulerAngles." + suffix;
|
||||
if (AnimationUtility.GetEditorCurve(clip, rotationBinding) != null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3827b1eea1bd6048965bb412d1daabc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,221 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations.Rigging;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class to manage ReorderableList appearance in the editor.
|
||||
/// </summary>
|
||||
public static class ReorderableListHelper
|
||||
{
|
||||
const int k_NoHeaderHeight = 2;
|
||||
const int k_ElementHeightPadding = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ReorderableList using a SerializedProperty array as source.
|
||||
/// </summary>
|
||||
/// <param name="obj">SerializedObject owning the SerializedProperty.</param>
|
||||
/// <param name="property">SerializedProperty array.</param>
|
||||
/// <param name="draggable">Toggles whether an object is draggable in the list. True when an object is draggable, false otherwise.</param>
|
||||
/// <param name="displayHeader">Displays the ReorderableList header.</param>
|
||||
/// <returns>Returns a new ReorderableList.</returns>
|
||||
public static ReorderableList Create(SerializedObject obj, SerializedProperty property, bool draggable = true, bool displayHeader = false)
|
||||
{
|
||||
var list = new ReorderableList(obj, property, draggable, displayHeader, true, true);
|
||||
|
||||
list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
var element = list.serializedProperty.GetArrayElementAtIndex(index);
|
||||
|
||||
var offset = k_ElementHeightPadding * 0.5f;
|
||||
rect.y += offset;
|
||||
rect.height = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
EditorGUI.PropertyField(rect, element, GUIContent.none);
|
||||
};
|
||||
|
||||
list.elementHeight = EditorGUIUtility.singleLineHeight + k_ElementHeightPadding;
|
||||
|
||||
if (!displayHeader)
|
||||
list.headerHeight = k_NoHeaderHeight;
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper class to manage WeightedTransform and WeightedTransformArray appearance in the editor.
|
||||
/// </summary>
|
||||
[Obsolete("This is now handled automatically in the inspector for WeightedTransformArray.", false)]
|
||||
public static class WeightedTransformHelper
|
||||
{
|
||||
const int k_NoHeaderHeight = 2;
|
||||
const int k_ElementHeightPadding = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ReorderableList using a WeightedTransformArray as source.
|
||||
/// </summary>
|
||||
/// <param name="property">The serialized property of the WeightedTransformArray.</param>
|
||||
/// <param name="array">The source WeightedTransformArray.</param>
|
||||
/// <param name="range">Range attribute given for weights in WeightedTransform. No boundaries are set if null.</param>
|
||||
/// <param name="draggable">Toggles whether a WeightedTransform is draggable in the list. True when WeightedTransform is draggable, false otherwise.</param>
|
||||
/// <param name="displayHeader">Displays the ReorderableList header.</param>
|
||||
/// <returns>Returns a new ReorderableList for a WeightedTransformArray.</returns>
|
||||
public static ReorderableList CreateReorderableList(SerializedProperty property, ref WeightedTransformArray array, RangeAttribute range = null, bool draggable = true, bool displayHeader = false)
|
||||
{
|
||||
var reorderableList = new ReorderableList(array, typeof(WeightedTransform), draggable, displayHeader, true, true);
|
||||
|
||||
reorderableList.drawElementBackgroundCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
// Didn't find a way to register a callback before we repaint the reorderable list to toggle draggable flag.
|
||||
// Ideally, we'd need a callback canReorderElementCallback that would enable/disable draggable handle.
|
||||
reorderableList.draggable = !AnimationMode.InAnimationMode() && !Application.isPlaying;
|
||||
ReorderableList.defaultBehaviours.DrawElementBackground(rect, index, isActive, isFocused, reorderableList.draggable);
|
||||
};
|
||||
|
||||
reorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
var element = property.FindPropertyRelative("m_Item" + index);
|
||||
|
||||
var offset = k_ElementHeightPadding * 0.5f;
|
||||
rect.y += offset;
|
||||
rect.height = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
WeightedTransformOnGUI(rect, element, range);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
var transformProperty = element.FindPropertyRelative("transform");
|
||||
var weightProperty = element.FindPropertyRelative("weight");
|
||||
|
||||
reorderableList.list[index] = new WeightedTransform(transformProperty.objectReferenceValue as Transform, weightProperty.floatValue);;
|
||||
}
|
||||
};
|
||||
|
||||
reorderableList.onCanAddCallback = (ReorderableList list) =>
|
||||
{
|
||||
return list.list.Count < WeightedTransformArray.k_MaxLength && !AnimationMode.InAnimationMode() && !Application.isPlaying;
|
||||
};
|
||||
|
||||
reorderableList.onCanRemoveCallback = (ReorderableList list) =>
|
||||
{
|
||||
return !AnimationMode.InAnimationMode() && !Application.isPlaying;
|
||||
};
|
||||
|
||||
reorderableList.onAddCallback = (ReorderableList list) =>
|
||||
{
|
||||
list.list.Add(WeightedTransform.Default(1f));
|
||||
};
|
||||
|
||||
reorderableList.elementHeight = EditorGUIUtility.singleLineHeight + k_ElementHeightPadding;
|
||||
|
||||
if (!displayHeader)
|
||||
reorderableList.headerHeight = k_NoHeaderHeight;
|
||||
|
||||
return reorderableList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display a single WeightedTransform in the editor.
|
||||
/// </summary>
|
||||
/// <param name="rect">Rectangle on the screen to use for the WeightedTransform.</param>
|
||||
/// <param name="property">Serialized property </param>
|
||||
/// <param name="range">Range attribute given for weights in WeightedTransform. No boundaries are set if null.</param>
|
||||
public static void WeightedTransformOnGUI(Rect rect, SerializedProperty property, RangeAttribute range = null) =>
|
||||
WeightedTransformDrawer.DoGUI(rect, property, range?.min ?? float.NaN, range?.max ?? float.NaN);
|
||||
}
|
||||
|
||||
internal static class MaintainOffsetHelper
|
||||
{
|
||||
static readonly string[] k_MaintainOffsetTypeLables = { "None", "Position and Rotation", "Position", "Rotation"};
|
||||
static readonly int[] k_BitsToIndex = new int[] {0, 2, 3, 1};
|
||||
static readonly int[] k_IndexToBits = new int[] {0, 3, 1, 2};
|
||||
|
||||
public static void DoDropdown(GUIContent label, SerializedProperty maintainPosition, SerializedProperty maintainRotation)
|
||||
{
|
||||
int currIndex = k_BitsToIndex[System.Convert.ToInt32(maintainPosition.boolValue) | (System.Convert.ToInt32(maintainRotation.boolValue) << 1)];
|
||||
int newIndex = EditorGUILayout.Popup(label, currIndex, k_MaintainOffsetTypeLables);
|
||||
if (newIndex == currIndex)
|
||||
return;
|
||||
|
||||
var bits = k_IndexToBits[newIndex];
|
||||
maintainPosition.boolValue = (bits & 0x1) != 0;
|
||||
maintainRotation.boolValue = (bits & 0x2) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class EditorHelper
|
||||
{
|
||||
private const string EditorFolder = "Packages/com.unity.animation.rigging/Editor/";
|
||||
private const string ShadersFolder = EditorFolder + "Shaders/";
|
||||
private const string ShapesFolder = EditorFolder + "Shapes/";
|
||||
|
||||
public static Shader LoadShader(string filename)
|
||||
{
|
||||
return AssetDatabase.LoadAssetAtPath<Shader>(ShadersFolder + filename);
|
||||
}
|
||||
|
||||
public static Mesh LoadShape(string filename)
|
||||
{
|
||||
return AssetDatabase.LoadAssetAtPath<Mesh>(ShapesFolder + filename);
|
||||
}
|
||||
|
||||
public static T GetClosestComponent<T>(Transform transform, Transform root = null)
|
||||
{
|
||||
if (transform == null)
|
||||
return default(T);
|
||||
|
||||
var top = (root != null) ? root : transform.root;
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool gotComponent = transform.TryGetComponent<T>(out T component);
|
||||
if (gotComponent) return component;
|
||||
if (transform == top) break;
|
||||
transform = transform.parent;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static void HandleClickSelection(GameObject gameObject, Event evt)
|
||||
{
|
||||
if (evt.shift || EditorGUI.actionKey)
|
||||
{
|
||||
UnityEngine.Object[] existingSelection = Selection.objects;
|
||||
|
||||
// For shift, we check if EXACTLY the active GO is hovered by mouse and then subtract. Otherwise additive.
|
||||
// For control/cmd, we check if ANY of the selected GO is hovered by mouse and then subtract. Otherwise additive.
|
||||
// Control/cmd takes priority over shift.
|
||||
bool subtractFromSelection = EditorGUI.actionKey ? Selection.Contains(gameObject) : Selection.activeGameObject == gameObject;
|
||||
if (subtractFromSelection)
|
||||
{
|
||||
// subtract from selection
|
||||
var newSelection = new UnityEngine.Object[existingSelection.Length - 1];
|
||||
|
||||
int index = Array.IndexOf(existingSelection, gameObject);
|
||||
|
||||
System.Array.Copy(existingSelection, newSelection, index);
|
||||
System.Array.Copy(existingSelection, index + 1, newSelection, index, newSelection.Length - index);
|
||||
|
||||
Selection.objects = newSelection;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add to selection
|
||||
var newSelection = new UnityEngine.Object[existingSelection.Length + 1];
|
||||
System.Array.Copy(existingSelection, newSelection, existingSelection.Length);
|
||||
newSelection[existingSelection.Length] = gameObject;
|
||||
|
||||
Selection.objects = newSelection;
|
||||
}
|
||||
}
|
||||
else
|
||||
Selection.activeObject = gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5683d572ae4e82746901186dcc3f26cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations.Rigging;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(ExpandChildrenAttribute))]
|
||||
class ExpandChildrenDrawer : PropertyDrawer
|
||||
{
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
property.isExpanded = true;
|
||||
return EditorGUI.GetPropertyHeight(property)
|
||||
- EditorGUIUtility.standardVerticalSpacing
|
||||
- EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
var endProperty = property.GetEndProperty();
|
||||
var childProperty = property.Copy();
|
||||
childProperty.NextVisible(true);
|
||||
while (!SerializedProperty.EqualContents(childProperty, endProperty))
|
||||
{
|
||||
position.height = EditorGUI.GetPropertyHeight(childProperty);
|
||||
OnChildPropertyGUI(position, childProperty);
|
||||
position.y += position.height + EditorGUIUtility.standardVerticalSpacing;
|
||||
childProperty.NextVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnChildPropertyGUI(Rect position, SerializedProperty childProperty)
|
||||
{
|
||||
EditorGUI.PropertyField(position, childProperty, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc65bad2b0f4a43c4a49ffb6bfb2437a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to easily store a foldout state for a custom editor.
|
||||
/// </summary>
|
||||
class FoldoutState
|
||||
{
|
||||
bool m_Initialized;
|
||||
bool m_Value;
|
||||
string m_Name;
|
||||
|
||||
public bool value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_Initialized)
|
||||
{
|
||||
m_Value = EditorPrefs.GetBool(m_Name, m_Value);
|
||||
m_Initialized = true;
|
||||
}
|
||||
return m_Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_Value != value)
|
||||
EditorPrefs.SetBool(m_Name, m_Value = value);
|
||||
}
|
||||
}
|
||||
|
||||
FoldoutState() {}
|
||||
|
||||
public static FoldoutState Create<T>(string name, bool value) =>
|
||||
new FoldoutState
|
||||
{
|
||||
m_Name = $"{typeof(T)}.{name}",
|
||||
m_Value = value
|
||||
};
|
||||
|
||||
public static FoldoutState ForSettings<T>() => Create<T>("Settings", false);
|
||||
|
||||
public static FoldoutState ForSourceObjects<T>() => Create<T>("SourceObjects", true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfb779bb474841c58c97b2660149692e
|
||||
timeCreated: 1608320504
|
||||
@@ -0,0 +1,271 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
internal static class Preferences
|
||||
{
|
||||
static readonly string k_Prefix = "com.unity.animation.rigging";
|
||||
|
||||
static readonly string k_BakeToConstraintPrefix = k_Prefix + ".bakeToConstraint";
|
||||
static readonly string k_BakeToSkeletonPrefix = k_Prefix + ".bakeToSkeleton";
|
||||
|
||||
static readonly string k_UnrollRotation = ".unrollRotation";
|
||||
static readonly string k_KeyReduceSuffix = ".keyReduceEnable";
|
||||
static readonly string k_KeyReducePositionErrorSuffix = ".keyReducePositionError";
|
||||
static readonly string k_KeyReduceRotationErrorSuffix = ".keyReduceRotationError";
|
||||
static readonly string k_KeyReduceScaleErrorSuffix = ".keyReduceScaleError";
|
||||
static readonly string k_KeyReduceFloatErrorSuffix = ".keyReduceFloatError";
|
||||
|
||||
static readonly string k_RemoveCurvesSuffix = ".removeCurves";
|
||||
static readonly string k_ForceWeightSuffix = ".forceWeight";
|
||||
|
||||
static CurveFilterOptions m_BakeToConstraintCurveFilterOptions;
|
||||
static CurveFilterOptions m_BakeToSkeletonCurveFilterOptions;
|
||||
|
||||
static bool m_BakeToConstraintAndRemoveCurves;
|
||||
static bool m_BakeToSkeletonAndRemoveCurves;
|
||||
|
||||
static bool m_ForceConstraintWeightOnBake;
|
||||
|
||||
static Preferences()
|
||||
{
|
||||
m_BakeToConstraintCurveFilterOptions = new CurveFilterOptions()
|
||||
{
|
||||
unrollRotation = EditorPrefs.GetBool(k_BakeToConstraintPrefix + k_UnrollRotation, true),
|
||||
keyframeReduction = EditorPrefs.GetBool(k_BakeToConstraintPrefix + k_KeyReduceSuffix, true),
|
||||
positionError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReducePositionErrorSuffix, 0.5f),
|
||||
rotationError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReduceRotationErrorSuffix, 0.5f),
|
||||
scaleError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReduceScaleErrorSuffix, 0.5f),
|
||||
floatError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReduceFloatErrorSuffix, 0.5f)
|
||||
};
|
||||
|
||||
m_BakeToSkeletonCurveFilterOptions = new CurveFilterOptions()
|
||||
{
|
||||
unrollRotation = EditorPrefs.GetBool(k_BakeToSkeletonPrefix + k_UnrollRotation, true),
|
||||
keyframeReduction = EditorPrefs.GetBool(k_BakeToSkeletonPrefix + k_KeyReduceSuffix, true),
|
||||
positionError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReducePositionErrorSuffix, 0.5f),
|
||||
rotationError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReduceRotationErrorSuffix, 0.5f),
|
||||
scaleError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReduceScaleErrorSuffix, 0.5f),
|
||||
floatError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReduceFloatErrorSuffix, 0.5f)
|
||||
};
|
||||
|
||||
m_BakeToConstraintAndRemoveCurves = EditorPrefs.GetBool(k_BakeToConstraintPrefix + k_RemoveCurvesSuffix, false);
|
||||
m_BakeToSkeletonAndRemoveCurves = EditorPrefs.GetBool(k_BakeToSkeletonPrefix + k_RemoveCurvesSuffix, false);
|
||||
|
||||
m_ForceConstraintWeightOnBake = EditorPrefs.GetBool(k_Prefix + k_ForceWeightSuffix, true);
|
||||
}
|
||||
|
||||
public static void SetDefaultValues()
|
||||
{
|
||||
var defaultOptions = new CurveFilterOptions()
|
||||
{
|
||||
unrollRotation = true,
|
||||
keyframeReduction = true,
|
||||
positionError = .5f,
|
||||
rotationError = .5f,
|
||||
scaleError = .5f,
|
||||
floatError = .5f
|
||||
};
|
||||
|
||||
bakeToConstraintCurveFilterOptions = defaultOptions;
|
||||
bakeToSkeletonCurveFilterOptions = defaultOptions;
|
||||
|
||||
bakeToConstraintAndRemoveCurves = false;
|
||||
bakeToSkeletonAndRemoveCurves = false;
|
||||
|
||||
forceConstraintWeightOnBake = true;
|
||||
}
|
||||
|
||||
public static CurveFilterOptions bakeToConstraintCurveFilterOptions
|
||||
{
|
||||
get => m_BakeToConstraintCurveFilterOptions;
|
||||
set
|
||||
{
|
||||
m_BakeToConstraintCurveFilterOptions = value;
|
||||
|
||||
EditorPrefs.SetBool(k_BakeToConstraintPrefix + k_UnrollRotation, m_BakeToConstraintCurveFilterOptions.unrollRotation);
|
||||
EditorPrefs.SetBool(k_BakeToConstraintPrefix + k_KeyReduceSuffix, m_BakeToConstraintCurveFilterOptions.keyframeReduction);
|
||||
EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReducePositionErrorSuffix, m_BakeToConstraintCurveFilterOptions.positionError);
|
||||
EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReduceRotationErrorSuffix, m_BakeToConstraintCurveFilterOptions.rotationError);
|
||||
EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReduceScaleErrorSuffix, m_BakeToConstraintCurveFilterOptions.scaleError);
|
||||
EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReduceFloatErrorSuffix, m_BakeToConstraintCurveFilterOptions.floatError);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool bakeToConstraintAndRemoveCurves
|
||||
{
|
||||
get => m_BakeToConstraintAndRemoveCurves;
|
||||
set
|
||||
{
|
||||
m_BakeToConstraintAndRemoveCurves = value;
|
||||
EditorPrefs.SetBool(k_BakeToConstraintPrefix + k_RemoveCurvesSuffix, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static CurveFilterOptions bakeToSkeletonCurveFilterOptions
|
||||
{
|
||||
get => m_BakeToSkeletonCurveFilterOptions;
|
||||
set
|
||||
{
|
||||
m_BakeToSkeletonCurveFilterOptions = value;
|
||||
|
||||
EditorPrefs.SetBool(k_BakeToSkeletonPrefix + k_UnrollRotation, m_BakeToSkeletonCurveFilterOptions.unrollRotation);
|
||||
EditorPrefs.SetBool(k_BakeToSkeletonPrefix + k_KeyReduceSuffix, m_BakeToSkeletonCurveFilterOptions.keyframeReduction);
|
||||
EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReducePositionErrorSuffix, m_BakeToSkeletonCurveFilterOptions.positionError);
|
||||
EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReduceRotationErrorSuffix, m_BakeToSkeletonCurveFilterOptions.rotationError);
|
||||
EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReduceScaleErrorSuffix, m_BakeToSkeletonCurveFilterOptions.scaleError);
|
||||
EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReduceFloatErrorSuffix, m_BakeToSkeletonCurveFilterOptions.floatError);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool bakeToSkeletonAndRemoveCurves
|
||||
{
|
||||
get => m_BakeToSkeletonAndRemoveCurves;
|
||||
set
|
||||
{
|
||||
m_BakeToSkeletonAndRemoveCurves = value;
|
||||
EditorPrefs.SetBool(k_BakeToSkeletonPrefix + k_RemoveCurvesSuffix, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool forceConstraintWeightOnBake
|
||||
{
|
||||
get => m_ForceConstraintWeightOnBake;
|
||||
set
|
||||
{
|
||||
m_ForceConstraintWeightOnBake = value;
|
||||
EditorPrefs.SetBool(k_Prefix + k_ForceWeightSuffix, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PreferencesProvider : SettingsProvider
|
||||
{
|
||||
private class Styles
|
||||
{
|
||||
public static readonly int marginLeft = 10;
|
||||
public static readonly int marginTop = 10;
|
||||
public static readonly int majorSpacing = 10;
|
||||
public static readonly int minorSpacing = 5;
|
||||
public static readonly int resetButtonWidth = 120;
|
||||
|
||||
public static readonly GUIContent forceWeightsLabel = EditorGUIUtility.TrTextContent("Force Weights On Bake", "Remove weight curves and set constraints weights to zero or one after baking operation.");
|
||||
public static readonly GUIContent resetPreferencesButton = EditorGUIUtility.TrTextContent("Use Defaults", "Reset all the Animation Rigging preferenecs back to default settings.");
|
||||
|
||||
public static readonly GUIContent bakeToConstraintLabel = EditorGUIUtility.TrTextContent("Transfer Motion To Constraint");
|
||||
public static readonly GUIContent bakeToSkeletonLabel = EditorGUIUtility.TrTextContent("Transfer Motion To Skeleton");
|
||||
|
||||
public static readonly GUIContent unrollRotationLabel = EditorGUIUtility.TrTextContent("Unroll Rotation", "Unroll rotation will adjust rotation to avoid discontinuity in between keyframes generated by baking operations.");
|
||||
public static readonly GUIContent keyReduceEnableLabel = EditorGUIUtility.TrTextContent("Apply keyframe reduction", "Keyframe Reduction will remove unecessary keys in animation curves generated by baking operations.");
|
||||
public static readonly GUIContent keyReducePositionErrorLabel = EditorGUIUtility.TrTextContent("Position Error", "Tolerance used in keyframe reduction for position values (percentage value between 0 and 100).");
|
||||
public static readonly GUIContent keyReduceRotationErrorLabel = EditorGUIUtility.TrTextContent("Rotation Error", "Tolerance used in keyframe reduction for rotation values (percentage value between 0 and 100).");
|
||||
public static readonly GUIContent keyReduceScaleErrorLabel = EditorGUIUtility.TrTextContent("Scale Error", "Tolerance used in keyframe reduction for scale values (percentage value between 0 and 100).");
|
||||
public static readonly GUIContent keyReduceFloatErrorLabel = EditorGUIUtility.TrTextContent("Float Error", "Tolerance used in keyframe reduction for float values (percentage value between 0 and 100).");
|
||||
|
||||
public static readonly GUIContent removeCurvesLabel = EditorGUIUtility.TrTextContent("Remove Curves", "Original curves are removed after baking operation.");
|
||||
}
|
||||
|
||||
public PreferencesProvider(string path, SettingsScope scopes, IEnumerable<string> keywords = null)
|
||||
: base(path, scopes, keywords)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnActivate(string searchContext, VisualElement rootElement)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnGUI(string searchContext)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(Styles.marginLeft);
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Space(Styles.marginTop);
|
||||
|
||||
// Force weights
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool newValue = EditorGUILayout.Toggle(Styles.forceWeightsLabel, Preferences.forceConstraintWeightOnBake);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Preferences.forceConstraintWeightOnBake = newValue;
|
||||
|
||||
GUILayout.Space(Styles.majorSpacing);
|
||||
|
||||
// Transfer to constraint
|
||||
EditorGUILayout.LabelField(Styles.bakeToConstraintLabel, EditorStyles.boldLabel);
|
||||
|
||||
// - Remove curves
|
||||
EditorGUI.BeginChangeCheck();
|
||||
newValue = EditorGUILayout.Toggle(Styles.removeCurvesLabel, Preferences.bakeToConstraintAndRemoveCurves);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Preferences.bakeToConstraintAndRemoveCurves = newValue;
|
||||
|
||||
// - Keyframe reduction
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var curveFilterOptions = Preferences.bakeToConstraintCurveFilterOptions;
|
||||
curveFilterOptions.unrollRotation = EditorGUILayout.Toggle(Styles.unrollRotationLabel, curveFilterOptions.unrollRotation);
|
||||
curveFilterOptions.keyframeReduction = EditorGUILayout.Toggle(Styles.keyReduceEnableLabel, curveFilterOptions.keyframeReduction);
|
||||
using (new EditorGUI.DisabledScope(!curveFilterOptions.keyframeReduction))
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
curveFilterOptions.positionError = EditorGUILayout.Slider(Styles.keyReducePositionErrorLabel, curveFilterOptions.positionError, 0f, 100f);
|
||||
curveFilterOptions.rotationError = EditorGUILayout.Slider(Styles.keyReduceRotationErrorLabel, curveFilterOptions.rotationError, 0f, 100f);
|
||||
curveFilterOptions.scaleError = EditorGUILayout.Slider(Styles.keyReduceScaleErrorLabel, curveFilterOptions.scaleError, 0f, 100f);
|
||||
curveFilterOptions.floatError = EditorGUILayout.Slider(Styles.keyReduceFloatErrorLabel, curveFilterOptions.floatError, 0f, 100f);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Preferences.bakeToConstraintCurveFilterOptions = curveFilterOptions;
|
||||
|
||||
GUILayout.Space(Styles.majorSpacing);
|
||||
|
||||
// Transfer to Skeleton
|
||||
EditorGUILayout.LabelField(Styles.bakeToSkeletonLabel, EditorStyles.boldLabel);
|
||||
|
||||
// - Remove curves
|
||||
EditorGUI.BeginChangeCheck();
|
||||
newValue = EditorGUILayout.Toggle(Styles.removeCurvesLabel, Preferences.bakeToSkeletonAndRemoveCurves);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Preferences.bakeToSkeletonAndRemoveCurves = newValue;
|
||||
|
||||
// - Keyframe reduction
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
curveFilterOptions = Preferences.bakeToSkeletonCurveFilterOptions;
|
||||
curveFilterOptions.unrollRotation = EditorGUILayout.Toggle(Styles.unrollRotationLabel, curveFilterOptions.unrollRotation);
|
||||
curveFilterOptions.keyframeReduction = EditorGUILayout.Toggle(Styles.keyReduceEnableLabel, curveFilterOptions.keyframeReduction);
|
||||
using (new EditorGUI.DisabledScope(!curveFilterOptions.keyframeReduction))
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
curveFilterOptions.positionError = EditorGUILayout.Slider(Styles.keyReducePositionErrorLabel, curveFilterOptions.positionError, 0f, 100f);
|
||||
curveFilterOptions.rotationError = EditorGUILayout.Slider(Styles.keyReduceRotationErrorLabel, curveFilterOptions.rotationError, 0f, 100f);
|
||||
curveFilterOptions.scaleError = EditorGUILayout.Slider(Styles.keyReduceScaleErrorLabel, curveFilterOptions.scaleError, 0f, 100f);
|
||||
curveFilterOptions.floatError = EditorGUILayout.Slider(Styles.keyReduceFloatErrorLabel, curveFilterOptions.floatError, 0f, 100f);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Preferences.bakeToSkeletonCurveFilterOptions = curveFilterOptions;
|
||||
|
||||
GUILayout.Space(Styles.majorSpacing);
|
||||
|
||||
// Reset to defaults
|
||||
if (GUILayout.Button(Styles.resetPreferencesButton, GUILayout.Width(Styles.resetButtonWidth)))
|
||||
Preferences.SetDefaultValues();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreateAnimationRiggingProjectSettingProvider()
|
||||
{
|
||||
return new PreferencesProvider(
|
||||
"Preferences/Animation Rigging",
|
||||
SettingsScope.User,
|
||||
GetSearchKeywordsFromGUIContentProperties<Styles>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd9b01ca0e39c426cb60419730fb3b29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,202 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
#if !SUPPORTS_SCENE_VIEW_OVERLAYS
|
||||
static class SceneViewOverlay
|
||||
{
|
||||
public delegate void WindowFunction(Object target, SceneView sceneView);
|
||||
|
||||
static SceneView s_SceneView;
|
||||
static List<OverlayWindow> s_Windows;
|
||||
|
||||
static SceneViewOverlay()
|
||||
{
|
||||
s_Windows = new List<OverlayWindow>();
|
||||
}
|
||||
|
||||
public static void Begin(SceneView sceneView)
|
||||
{
|
||||
s_SceneView = sceneView;
|
||||
|
||||
if (Event.current.type == EventType.Layout)
|
||||
s_Windows.Clear();
|
||||
}
|
||||
|
||||
static class Styles
|
||||
{
|
||||
public static readonly GUIStyle sceneViewOverlayTransparentBackground = "SceneViewOverlayTransparentBackground";
|
||||
public static readonly GUIStyle title = new GUIStyle(GUI.skin.window);
|
||||
|
||||
public static readonly float windowPadding = 9f;
|
||||
public static readonly float minWidth = 210f;
|
||||
|
||||
static Styles()
|
||||
{
|
||||
title.padding.top = title.padding.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
public static void End()
|
||||
{
|
||||
s_Windows.Sort();
|
||||
|
||||
if (s_Windows.Count > 0)
|
||||
{
|
||||
var windowOverlayRect = new Rect(0, 0f, s_SceneView.position.width, s_SceneView.position.height);
|
||||
GUILayout.Window("SceneViewOverlay".GetHashCode(), windowOverlayRect, WindowTrampoline, "", Styles.sceneViewOverlayTransparentBackground);
|
||||
}
|
||||
|
||||
s_SceneView = null;
|
||||
}
|
||||
|
||||
static void WindowTrampoline(int id)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.BeginVertical(GUILayout.MinWidth(Styles.minWidth));
|
||||
|
||||
var paddingOffset = -Styles.windowPadding;
|
||||
|
||||
bool showSceneViewWindows = SceneView.lastActiveSceneView == s_SceneView;
|
||||
|
||||
foreach (OverlayWindow win in s_Windows)
|
||||
{
|
||||
if (!showSceneViewWindows && win.editorWindow != s_SceneView)
|
||||
continue;
|
||||
|
||||
GUILayout.Space(Styles.windowPadding + paddingOffset);
|
||||
paddingOffset = 0f;
|
||||
ResetGUIState();
|
||||
if (win.canCollapse)
|
||||
{
|
||||
GUILayout.BeginVertical(Styles.title);
|
||||
|
||||
win.expanded = EditorGUILayout.Foldout(win.expanded, win.title, true);
|
||||
|
||||
if (win.expanded)
|
||||
win.sceneViewFunc(win.target, s_SceneView);
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.BeginVertical(win.title, GUI.skin.window);
|
||||
win.sceneViewFunc(win.target, s_SceneView);
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
|
||||
var inputEaterRect = GUILayoutUtility.GetLastRect();
|
||||
EatMouseInput(inputEaterRect);
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
static void EatMouseInput(Rect position)
|
||||
{
|
||||
var id = GUIUtility.GetControlID("SceneViewOverlay".GetHashCode(), FocusType.Passive, position);
|
||||
switch (Event.current.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if (position.Contains(Event.current.mousePosition))
|
||||
{
|
||||
GUIUtility.hotControl = id;
|
||||
Event.current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
Event.current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
Event.current.Use();
|
||||
break;
|
||||
case EventType.ScrollWheel:
|
||||
if (position.Contains(Event.current.mousePosition))
|
||||
Event.current.Use();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetGUIState()
|
||||
{
|
||||
GUI.skin = null;
|
||||
GUI.backgroundColor = GUI.contentColor = Color.white;
|
||||
GUI.color = Color.white;
|
||||
GUI.enabled = true;
|
||||
GUI.changed = false;
|
||||
EditorGUI.indentLevel = 0;
|
||||
EditorGUIUtility.fieldWidth = 0;
|
||||
EditorGUIUtility.labelWidth = 0;
|
||||
EditorGUIUtility.hierarchyMode = false;
|
||||
EditorGUIUtility.wideMode = false;
|
||||
}
|
||||
|
||||
// pass window parameter to render in sceneviews that are not the active view.
|
||||
public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order)
|
||||
{
|
||||
Window(title, sceneViewFunc, order, null);
|
||||
}
|
||||
|
||||
// pass window parameter to render in sceneviews that are not the active view.
|
||||
public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, Object target, EditorWindow window = null)
|
||||
{
|
||||
if (Event.current.type != EventType.Layout)
|
||||
return;
|
||||
|
||||
var newWindow = new OverlayWindow(title, sceneViewFunc, order, target)
|
||||
{
|
||||
secondaryOrder = s_Windows.Count,
|
||||
canCollapse = false
|
||||
};
|
||||
|
||||
|
||||
s_Windows.Add(newWindow);
|
||||
}
|
||||
}
|
||||
|
||||
internal class OverlayWindow : IComparable<OverlayWindow>
|
||||
{
|
||||
public OverlayWindow(GUIContent title, SceneViewOverlay.WindowFunction guiFunction, int primaryOrder, Object target)
|
||||
{
|
||||
this.title = title;
|
||||
this.sceneViewFunc = guiFunction;
|
||||
this.primaryOrder = primaryOrder;
|
||||
this.target = target;
|
||||
this.canCollapse = true;
|
||||
this.expanded = true;
|
||||
}
|
||||
|
||||
public SceneViewOverlay.WindowFunction sceneViewFunc { get; }
|
||||
public int primaryOrder { get; }
|
||||
public int secondaryOrder { get; set; }
|
||||
public Object target { get; }
|
||||
public EditorWindow editorWindow { get; set; }
|
||||
|
||||
public bool canCollapse { get; set; }
|
||||
public bool expanded { get; set; }
|
||||
|
||||
public GUIContent title { get; }
|
||||
|
||||
public int CompareTo(OverlayWindow other)
|
||||
{
|
||||
var result = other.primaryOrder.CompareTo(primaryOrder);
|
||||
if (result == 0)
|
||||
result = other.secondaryOrder.CompareTo(secondaryOrder);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12e21a601544b4644bd45d51de0e8095
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations.Rigging;
|
||||
|
||||
namespace UnityEditor.Animations.Rigging
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(Vector3Bool))]
|
||||
class Vector3BoolDrawer : PropertyDrawer
|
||||
{
|
||||
private const int k_Offset = 16;
|
||||
private const int k_ToggleWidth = 50;
|
||||
private static readonly GUIContent k_XLabel = new GUIContent("X");
|
||||
private static readonly GUIContent k_YLabel = new GUIContent("Y");
|
||||
private static readonly GUIContent k_ZLabel = new GUIContent("Z");
|
||||
|
||||
private SerializedProperty m_X;
|
||||
private SerializedProperty m_Y;
|
||||
private SerializedProperty m_Z;
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
return EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(rect, label, property);
|
||||
|
||||
m_X = property.FindPropertyRelative("x");
|
||||
m_Y = property.FindPropertyRelative("y");
|
||||
m_Z = property.FindPropertyRelative("z");
|
||||
|
||||
rect = EditorGUI.PrefixLabel(rect, label);
|
||||
|
||||
int indentLvl = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
rect.x -= 1;
|
||||
var xRect = new Rect (rect.xMin, rect.yMin, k_ToggleWidth, rect.height);
|
||||
var yRect = new Rect (xRect.xMax, rect.yMin, k_ToggleWidth, rect.height);
|
||||
var zRect = new Rect (yRect.xMax, rect.yMin, k_ToggleWidth, rect.height);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
DrawToggleField(xRect, m_X, k_XLabel);
|
||||
DrawToggleField(yRect, m_Y, k_YLabel);
|
||||
DrawToggleField(zRect, m_Z, k_ZLabel);
|
||||
if(EditorGUI.EndChangeCheck())
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
|
||||
EditorGUI.indentLevel = indentLvl;
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
void DrawToggleField(Rect rect, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.LabelField(rect, label);
|
||||
rect.x += k_Offset;
|
||||
rect.width -= k_Offset;
|
||||
EditorGUI.PropertyField(rect, property, GUIContent.none);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a7b477fca018024b969f39a241674b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user