Unity Chan 工程分享 - URP渲染实现 Unity版本:2019.4
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

342 行
15 KiB

using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Unity.Animations.SpringBones.GameObjectExtensions;
using UnityEditor.Localization.Editor;
namespace Unity.Animations.SpringBones
{
public class MirrorSpringBoneWindow : EditorWindow
{
private static class Styles
{
public static readonly string editorWindowTitle = Localization.Tr("SpringBone Mirror");
public static readonly GUIContent labelDoMirror = new GUIContent(Localization.Tr("Do Mirror"));
public static readonly GUIContent labelGetFromSelection = new GUIContent(Localization.Tr("Get from selection"));
public static readonly GUIContent labelSelectAllCopyDst = new GUIContent(Localization.Tr("Select all copy destination"));
public static readonly GUIContent labelSelectAllCopySrc = new GUIContent(Localization.Tr("Select all copy source"));
public static readonly GUIContent labelSelectAll = new GUIContent(Localization.Tr("Select All"));
public static readonly GUIContent labelConfigXbZero = new GUIContent(Localization.Tr("Set bone where X > 0"));
public static readonly GUIContent labelConfigXlZero = new GUIContent(Localization.Tr("Set bone where X < 0"));
public static readonly GUIContent labelSrc = new GUIContent(Localization.Tr("Source"));
public static readonly GUIContent labelDst = new GUIContent(Localization.Tr("→ Destination"));
}
public static void ShowWindow()
{
var window = GetWindow<MirrorSpringBoneWindow>(Styles.editorWindowTitle);
window.Show();
window.OnShow();
}
// private
private enum Axis { X, Y, Z }
private class BoneEntry
{
public BoneEntry(SpringBone newSourceBone, SpringBone newTargetBone)
{
sourceBone = newSourceBone;
targetBone = newTargetBone;
}
public SpringBone sourceBone;
public SpringBone targetBone;
public void ShowEntryUI(Rect rect)
{
var halfWidth = 0.5f * rect.width;
var sourceRect = new Rect(rect.x, rect.y, halfWidth, rect.height);
sourceBone = (SpringBone)EditorGUI.ObjectField(sourceRect, sourceBone, typeof(SpringBone), true);
var targetRect = new Rect(rect.x + halfWidth, rect.y, halfWidth, rect.height);
targetBone = (SpringBone)EditorGUI.ObjectField(targetRect, targetBone, typeof(SpringBone), true);
}
}
private const float Spacing = 8f;
private const float RowHeight = 30f;
private Axis mirrorAxis = Axis.X;
private List<BoneEntry> boneEntries;
private Vector2 scrollPosition;
private void OnGUI()
{
SpringBoneGUIStyles.ReacquireStyles();
if (boneEntries == null) { AcquireBonesFromSelection(); }
var uiRect = new Rect(Spacing, Spacing, position.width - Spacing * 2f, RowHeight);
uiRect = ShowUtilityButtons(uiRect);
uiRect = ShowBoneList(uiRect);
if (GUI.Button(uiRect, Styles.labelDoMirror, SpringBoneGUIStyles.ButtonStyle))
{
PerformMirror();
}
}
private Rect ShowUtilityButtons(Rect uiRect)
{
var buttonOffset = uiRect.height + Spacing;
if (GUI.Button(uiRect, Styles.labelGetFromSelection, SpringBoneGUIStyles.ButtonStyle))
{
AcquireBonesFromSelection();
}
uiRect.y += buttonOffset;
var halfRectWidth = 0.5f * (uiRect.width - Spacing);
var halfButtonRect = new Rect(uiRect.x, uiRect.y, halfRectWidth, uiRect.height);
if (GUI.Button(halfButtonRect, Styles.labelConfigXlZero, SpringBoneGUIStyles.ButtonStyle))
{
AcquireSourceBonesOnSideOfAxis(true);
}
halfButtonRect.x += halfRectWidth + Spacing;
if (GUI.Button(halfButtonRect, Styles.labelConfigXbZero, SpringBoneGUIStyles.ButtonStyle))
{
AcquireSourceBonesOnSideOfAxis(false);
}
uiRect.y += buttonOffset;
halfButtonRect.x = uiRect.x;
halfButtonRect.y = uiRect.y;
if (GUI.Button(halfButtonRect, Styles.labelSelectAllCopySrc, SpringBoneGUIStyles.ButtonStyle))
{
var sourceBones = boneEntries.Select(entry => entry.sourceBone).Where(bone => bone != null);
if (sourceBones.Any()) { Selection.objects = sourceBones.Select(bone => bone.gameObject).ToArray(); }
}
halfButtonRect.x += halfRectWidth + Spacing;
if (GUI.Button(halfButtonRect, Styles.labelSelectAllCopyDst, SpringBoneGUIStyles.ButtonStyle))
{
var targetBones = boneEntries.Select(entry => entry.targetBone).Where(bone => bone != null);
if (targetBones.Any()) { Selection.objects = targetBones.Select(bone => bone.gameObject).ToArray(); }
}
uiRect.y += buttonOffset;
if (GUI.Button(uiRect, Styles.labelSelectAll, SpringBoneGUIStyles.ButtonStyle))
{
var bonesToSelect = new List<SpringBone>();
bonesToSelect.AddRange(boneEntries.Select(entry => entry.sourceBone).Where(bone => bone != null));
bonesToSelect.AddRange(boneEntries.Select(entry => entry.targetBone).Where(bone => bone != null));
if (bonesToSelect.Any()) { Selection.objects = bonesToSelect.Select(bone => bone.gameObject).ToArray(); }
}
uiRect.y += buttonOffset;
return uiRect;
}
private Rect ShowBoneList(Rect uiRect)
{
var listBoxBottom = position.height - (Spacing * 2f + RowHeight);
var headerRowRect = new Rect(uiRect.x, uiRect.y, uiRect.width * 0.5f, uiRect.height);
GUI.Label(headerRowRect, Styles.labelSrc, SpringBoneGUIStyles.LabelStyle);
headerRowRect.x += headerRowRect.width;
GUI.Label(headerRowRect, Styles.labelDst, SpringBoneGUIStyles.LabelStyle);
uiRect.y += uiRect.height;
const float ScrollbarWidth = 20f;
const float EntryHeight = 24f;
var listBoxRect = new Rect(uiRect.x, uiRect.y, uiRect.width, listBoxBottom - uiRect.y);
var entryCount = boneEntries.Count;
var listContentsRect = new Rect(0f, 0f, uiRect.width - ScrollbarWidth, entryCount * EntryHeight);
scrollPosition = GUI.BeginScrollView(listBoxRect, scrollPosition, listContentsRect);
var entryRect = new Rect(0f, 0f, listContentsRect.width, EntryHeight);
for (int entryIndex = 0; entryIndex < entryCount; entryIndex++)
{
boneEntries[entryIndex].ShowEntryUI(entryRect);
entryRect.y += entryRect.height;
}
GUI.EndScrollView();
uiRect.y = listBoxBottom + Spacing;
return uiRect;
}
private static Vector3 MirrorPosition(Vector3 originalPosition, Axis mirrorAxis)
{
var targetPosition = originalPosition;
switch (mirrorAxis)
{
case Axis.X: targetPosition.x = -targetPosition.x; break;
case Axis.Y: targetPosition.y = -targetPosition.y; break;
case Axis.Z: targetPosition.z = -targetPosition.z; break;
}
return targetPosition;
}
private static T FindMirroredComponent<T>(GameObject rootObject, T sourceObject, Axis mirrorAxis, float threshold)
where T : Component
{
if (rootObject == null
|| sourceObject == null)
{
return null;
}
var possibleTargets = rootObject.GetComponentsInChildren<T>(true);
var targetPosition = MirrorPosition(sourceObject.transform.position, mirrorAxis);
var squaredThreshold = threshold * threshold;
return possibleTargets
.Where(item => (item.transform.position - targetPosition).sqrMagnitude <= squaredThreshold)
.FirstOrDefault();
}
private static IEnumerable<T> FindMirroredComponents<T>
(
GameObject rootObject,
IEnumerable<T> sourceObjects,
Axis mirrorAxis,
float threshold = 0.001f
) where T : Component
{
return sourceObjects.Select(item => FindMirroredComponent(rootObject, item, mirrorAxis, threshold));
}
private static SpringBone FindMirroredSpringBone(SpringBone sourceBone, Axis mirrorAxis, float threshold = 0.001f)
{
var manager = sourceBone.GetComponentInParent<SpringManager>();
return FindMirroredComponent(manager.gameObject, sourceBone, mirrorAxis, threshold);
}
private void AcquireBonesFromSelection()
{
if (boneEntries == null) { boneEntries = new List<BoneEntry>(); }
var selectedBones = Selection.gameObjects
.SelectMany(gameObject => gameObject.GetComponents<SpringBone>());
var newBoneEntries = selectedBones
.Select(bone => new BoneEntry(bone, FindMirroredSpringBone(bone, mirrorAxis)));
foreach (var entry in newBoneEntries.Where(entry => entry.sourceBone == entry.targetBone))
{
entry.targetBone = null;
}
boneEntries = newBoneEntries.ToList();
}
private void AcquireSourceBonesOnSideOfAxis(bool getLessThanZero)
{
if (boneEntries == null) { boneEntries = new List<BoneEntry>(); }
// Is there a better way of defining this?
System.Func<SpringBone, bool> boneMatches = item => false;
if (getLessThanZero)
{
switch (mirrorAxis)
{
case Axis.X: boneMatches = item => item.transform.position.x < 0f; break;
case Axis.Y: boneMatches = item => item.transform.position.y < 0f; break;
case Axis.Z: boneMatches = item => item.transform.position.z < 0f; break;
}
}
else
{
switch (mirrorAxis)
{
case Axis.X: boneMatches = item => item.transform.position.x > 0f; break;
case Axis.Y: boneMatches = item => item.transform.position.y > 0f; break;
case Axis.Z: boneMatches = item => item.transform.position.z > 0f; break;
}
}
var allSpringBones = GameObjectUtil.FindComponentsOfType<SpringBone>();
var selectedBones = allSpringBones.Where(boneMatches);
var newBoneEntries = selectedBones
.Select(bone => new BoneEntry(bone, FindMirroredSpringBone(bone, mirrorAxis)))
.Where(entry => entry.targetBone != null
&& entry.targetBone != entry.sourceBone);
boneEntries = newBoneEntries.ToList();
}
private void MirrorPivot(SpringBone sourceBone, SpringBone targetBone)
{
var sourcePivot = sourceBone.pivotNode;
var targetPivot = targetBone.pivotNode;
var targetPosition = MirrorPosition(sourcePivot.position, mirrorAxis);
var pivotDirection = -sourcePivot.right;
pivotDirection = MirrorPosition(pivotDirection, mirrorAxis);
var upDirection = MirrorPosition(sourcePivot.forward, mirrorAxis);
targetPivot.position = targetPosition;
targetPivot.LookAt(targetPosition + pivotDirection, upDirection);
targetPivot.Rotate(-90f, 90f, 0f, Space.Self);
}
private void PerformMirror()
{
var mirrorItems = boneEntries.Where(
item => item.sourceBone != null
&& item.targetBone != null
&& item.sourceBone != item.targetBone);
var undoItems = mirrorItems.Select(item => (Component)item.targetBone).ToList();
var allSkinBones = GameObjectUtil.FindComponentsOfType<SkinnedMeshRenderer>()
.SelectMany(renderer => renderer.bones)
.Distinct()
.ToArray();
var editablePivots = mirrorItems
.Select(item => item.targetBone.pivotNode)
.Where(pivotNode => pivotNode != null
&& SpringBoneSetup.IsPivotProbablySafeToDestroy(pivotNode, allSkinBones))
.ToArray();
undoItems.AddRange(editablePivots);
Undo.RecordObjects(undoItems.ToArray(), "Mirror SpringBones");
foreach (var mirrorItem in mirrorItems)
{
var sourceBone = mirrorItem.sourceBone;
var targetBone = mirrorItem.targetBone;
var rootManager = targetBone.GetComponentInParent<SpringManager>();
if (rootManager == null) { continue; }
targetBone.stiffnessForce = sourceBone.stiffnessForce;
targetBone.dragForce = sourceBone.dragForce;
targetBone.springForce = sourceBone.springForce;
targetBone.windInfluence = sourceBone.windInfluence;
if (editablePivots.Contains(targetBone.pivotNode))
{
MirrorPivot(sourceBone, targetBone);
}
targetBone.angularStiffness = sourceBone.angularStiffness;
sourceBone.yAngleLimits.CopyTo(targetBone.yAngleLimits);
sourceBone.zAngleLimits.CopyTo(targetBone.zAngleLimits);
targetBone.yAngleLimits.min = -sourceBone.yAngleLimits.max;
targetBone.yAngleLimits.max = -sourceBone.yAngleLimits.min;
targetBone.lengthLimitTargets = FindMirroredComponents(
rootManager.gameObject, sourceBone.lengthLimitTargets, mirrorAxis)
.Where(item => item != null)
.ToArray();
targetBone.radius = sourceBone.radius;
targetBone.sphereColliders = FindMirroredComponents(
rootManager.gameObject, sourceBone.sphereColliders, mirrorAxis)
.Where(item => item != null)
.ToArray();
targetBone.capsuleColliders = FindMirroredComponents(
rootManager.gameObject, sourceBone.capsuleColliders, mirrorAxis)
.Where(item => item != null)
.ToArray();
targetBone.panelColliders = FindMirroredComponents(
rootManager.gameObject, sourceBone.panelColliders, mirrorAxis)
.Where(item => item != null)
.ToArray();
}
}
private void OnShow()
{
AcquireBonesFromSelection();
}
}
}