first commit

This commit is contained in:
lethanhsonvsp
2025-11-17 15:16:36 +07:00
commit a40d0921eb
17012 changed files with 2652386 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5f0a8cdd0db7492b962f3f63b841f506
timeCreated: 1607533731

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dfb779bb474841c58c97b2660149692e
timeCreated: 1608320504

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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