305 lines
12 KiB
C#
305 lines
12 KiB
C#
using System;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.Splines;
|
|
using UnityEngine.Splines.ExtrusionShapes;
|
|
|
|
namespace UnityEditor.Splines
|
|
{
|
|
[CustomEditor(typeof(SplineExtrude))]
|
|
[CanEditMultipleObjects]
|
|
class SplineExtrudeEditor : SplineComponentEditor
|
|
{
|
|
SerializedProperty m_Container;
|
|
SerializedProperty m_RebuildOnSplineChange;
|
|
SerializedProperty m_RebuildFrequency;
|
|
SerializedProperty m_SegmentsPerUnit;
|
|
SerializedProperty m_Capped;
|
|
SerializedProperty m_Radius;
|
|
SerializedProperty m_Range;
|
|
SerializedProperty m_Shape;
|
|
SerializedProperty m_UpdateColliders;
|
|
SerializedProperty m_FlipNormals;
|
|
|
|
static readonly GUIContent k_RangeContent = new GUIContent(L10n.Tr("Range"), L10n.Tr("The section of the Spline to extrude."));
|
|
static readonly GUIContent k_AdvancedContent = new GUIContent(L10n.Tr("Advanced"), L10n.Tr("Advanced Spline Extrude settings."));
|
|
static readonly GUIContent k_PercentageContent = new GUIContent(L10n.Tr("Percentage"), L10n.Tr("The section of the Spline to extrude in percentages."));
|
|
static readonly GUIContent k_ShapeContent = new GUIContent(L10n.Tr("Shape Extrude"), L10n.Tr("Shape Extrude settings."));
|
|
static readonly GUIContent k_ShapeSettings = EditorGUIUtility.TrTextContent("Settings");
|
|
static readonly GUIContent k_GeometryContent = new GUIContent(L10n.Tr("Geometry"), L10n.Tr("Mesh Geometry settings."));
|
|
|
|
static readonly string k_SourceSplineContainer = L10n.Tr("Source Spline Container");
|
|
static readonly string k_CapEnds = L10n.Tr("Cap Ends");
|
|
static readonly string k_AutoRefreshGeneration = L10n.Tr("Auto Refresh Generation");
|
|
static readonly string k_To = L10n.Tr("to");
|
|
static readonly string k_From = L10n.Tr("from");
|
|
|
|
SplineExtrude[] m_Components;
|
|
|
|
bool m_AnyMissingMesh;
|
|
|
|
protected void OnEnable()
|
|
{
|
|
m_Container = serializedObject.FindProperty("m_Container");
|
|
m_RebuildOnSplineChange = serializedObject.FindProperty("m_RebuildOnSplineChange");
|
|
m_RebuildFrequency = serializedObject.FindProperty("m_RebuildFrequency");
|
|
m_SegmentsPerUnit = serializedObject.FindProperty("m_SegmentsPerUnit");
|
|
m_Capped = serializedObject.FindProperty("m_Capped");
|
|
m_Radius = serializedObject.FindProperty("m_Radius");
|
|
m_Range = serializedObject.FindProperty("m_Range");
|
|
m_UpdateColliders = serializedObject.FindProperty("m_UpdateColliders");
|
|
m_Shape = serializedObject.FindProperty("m_Shape");
|
|
m_FlipNormals = serializedObject.FindProperty("m_FlipNormals");
|
|
|
|
m_Components = targets.Select(x => x as SplineExtrude).Where(y => y != null).ToArray();
|
|
m_AnyMissingMesh = false;
|
|
|
|
EditorSplineUtility.AfterSplineWasModified += OnSplineModified;
|
|
SplineContainer.SplineAdded += OnContainerSplineSetModified;
|
|
SplineContainer.SplineRemoved += OnContainerSplineSetModified;
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
EditorSplineUtility.AfterSplineWasModified -= OnSplineModified;
|
|
SplineContainer.SplineAdded -= OnContainerSplineSetModified;
|
|
SplineContainer.SplineRemoved -= OnContainerSplineSetModified;
|
|
}
|
|
|
|
void OnSplineModified(Spline spline)
|
|
{
|
|
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
|
return;
|
|
|
|
foreach (var extrude in m_Components)
|
|
{
|
|
if (extrude.Container != null && extrude.Splines.Contains(spline))
|
|
extrude.Rebuild();
|
|
}
|
|
}
|
|
|
|
void OnContainerSplineSetModified(SplineContainer container, int spline)
|
|
{
|
|
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
|
return;
|
|
|
|
foreach (var extrude in m_Components)
|
|
{
|
|
if (extrude.Container == container)
|
|
extrude.Rebuild();
|
|
}
|
|
}
|
|
|
|
void SetShapeType(ShapeType type)
|
|
{
|
|
foreach (var extrude in m_Components)
|
|
{
|
|
if (ShapeTypeUtility.GetShapeType(extrude.Shape) == type)
|
|
continue;
|
|
|
|
Undo.RecordObject(extrude, "Set Extrude Shape");
|
|
|
|
extrude.Shape = ShapeTypeUtility.CreateShape(type);
|
|
m_Shape.isExpanded = true;
|
|
}
|
|
}
|
|
|
|
bool CanCapEnds()
|
|
{
|
|
foreach (var extrude in m_Components)
|
|
{
|
|
if (!extrude.CanCapEnds)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SetRebuildOnSplineChange(bool value)
|
|
{
|
|
foreach (var extrude in m_Components)
|
|
{
|
|
Undo.RecordObject(extrude, "Set Rebuild on Spline Change.");
|
|
|
|
extrude.RebuildOnSplineChange = value;
|
|
}
|
|
}
|
|
|
|
void CreateMeshAssets(SplineExtrude[] components)
|
|
{
|
|
foreach (var extrude in components)
|
|
{
|
|
if (!extrude.TryGetComponent<MeshFilter>(out var filter) || filter.sharedMesh == null)
|
|
filter.sharedMesh = extrude.CreateMeshAsset();
|
|
}
|
|
|
|
m_AnyMissingMesh = false;
|
|
}
|
|
|
|
void Rebuild()
|
|
{
|
|
foreach (var extrude in m_Components)
|
|
extrude.Rebuild();
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
m_AnyMissingMesh = m_Components.Any(x => x.TryGetComponent<MeshFilter>(out var filter) && filter.sharedMesh == null);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
EditorGUILayout.PropertyField(m_Container, new GUIContent(k_SourceSplineContainer, m_Container.tooltip));
|
|
|
|
if (m_Container.objectReferenceValue == null)
|
|
EditorGUILayout.HelpBox(k_Helpbox, MessageType.Warning);
|
|
|
|
// shape section
|
|
m_Shape.isExpanded = Foldout(m_Shape.isExpanded, k_ShapeContent, true);
|
|
|
|
if (m_Shape.isExpanded)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
|
|
EditorGUI.showMixedValue = m_Shape.hasMultipleDifferentValues;
|
|
EditorGUI.BeginChangeCheck();
|
|
var shapeType = ShapeTypeUtility.GetShapeType(m_Shape.managedReferenceValue);
|
|
shapeType = (ShapeType)EditorGUILayout.EnumPopup(L10n.Tr("Type"), shapeType);
|
|
if (EditorGUI.EndChangeCheck())
|
|
SetShapeType(shapeType);
|
|
EditorGUI.showMixedValue = false;
|
|
|
|
if (m_Shape.hasVisibleChildren)
|
|
EditorGUILayout.PropertyField(m_Shape, k_ShapeSettings, true);
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
// https://unityeditordesignsystem.unity.com/patterns/content-organization recommends 8px spacing for
|
|
// vertical groups. padding already adds 4 so just nudge that up for a total of 8
|
|
EditorGUILayout.Space(4);
|
|
|
|
// geometry section
|
|
m_Radius.isExpanded = Foldout(m_Radius.isExpanded, k_GeometryContent, true);
|
|
|
|
if (m_Radius.isExpanded)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(m_RebuildOnSplineChange, new GUIContent(k_AutoRefreshGeneration, m_RebuildOnSplineChange.tooltip));
|
|
if (m_RebuildOnSplineChange.boolValue)
|
|
{
|
|
EditorGUI.BeginDisabledGroup(!m_RebuildOnSplineChange.boolValue);
|
|
using (new LabelWidthScope(80f))
|
|
EditorGUILayout.PropertyField(m_RebuildFrequency, new GUIContent() { text = L10n.Tr("Frequency") });
|
|
EditorGUI.EndDisabledGroup();
|
|
}
|
|
else
|
|
{
|
|
if (GUILayout.Button(new GUIContent(L10n.Tr("Regenerate"))))
|
|
Rebuild();
|
|
}
|
|
|
|
if (EditorGUI.EndChangeCheck() && !m_RebuildOnSplineChange.boolValue)
|
|
{
|
|
// This is needed to set m_RebuildRequested to the appropriate value.
|
|
SetRebuildOnSplineChange(m_RebuildOnSplineChange.boolValue);
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
if (m_AnyMissingMesh)
|
|
{
|
|
GUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel(" ");
|
|
if (GUILayout.Button("Create Mesh Asset"))
|
|
CreateMeshAssets(m_Components);
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(m_Radius);
|
|
if (EditorGUI.EndChangeCheck())
|
|
m_Radius.floatValue = Mathf.Clamp(m_Radius.floatValue, .00001f, 1000f);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(m_SegmentsPerUnit);
|
|
if (EditorGUI.EndChangeCheck())
|
|
m_SegmentsPerUnit.floatValue = Mathf.Clamp(m_SegmentsPerUnit.floatValue, .00001f, 4096f);
|
|
|
|
var canCapEnds = CanCapEnds();
|
|
using (new EditorGUI.DisabledScope(!canCapEnds))
|
|
{
|
|
EditorGUILayout.PropertyField(m_Capped, new GUIContent(k_CapEnds, m_Capped.tooltip));
|
|
if (m_Capped.boolValue && !canCapEnds)
|
|
m_Capped.boolValue = false;
|
|
}
|
|
|
|
EditorGUILayout.PropertyField(m_FlipNormals);
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
// advanced section
|
|
EditorGUILayout.Space(4);
|
|
|
|
m_Range.isExpanded = Foldout(m_Range.isExpanded, k_AdvancedContent, true);
|
|
|
|
if (m_Range.isExpanded)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
|
|
EditorGUI.showMixedValue = m_Range.hasMultipleDifferentValues;
|
|
var range = m_Range.vector2Value;
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.MinMaxSlider(k_RangeContent, ref range.x, ref range.y, 0f, 1f);
|
|
if (EditorGUI.EndChangeCheck())
|
|
m_Range.vector2Value = range;
|
|
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel(k_PercentageContent);
|
|
EditorGUI.indentLevel--;
|
|
|
|
EditorGUI.indentLevel--;
|
|
EditorGUI.BeginChangeCheck();
|
|
var newRange = new Vector2(range.x, range.y);
|
|
using (new LabelWidthScope(30f))
|
|
newRange.x = EditorGUILayout.FloatField(k_From, range.x * 100f) / 100f;
|
|
|
|
using (new LabelWidthScope(15f))
|
|
newRange.y = EditorGUILayout.FloatField(k_To, range.y * 100f) / 100f;
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
newRange.x = Mathf.Min(Mathf.Clamp(newRange.x, 0f, 1f), range.y);
|
|
newRange.y = Mathf.Max(newRange.x, Mathf.Clamp(newRange.y, 0f, 1f));
|
|
m_Range.vector2Value = newRange;
|
|
}
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUI.showMixedValue = false;
|
|
|
|
EditorGUILayout.PropertyField(m_UpdateColliders);
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
foreach (var extrude in m_Components)
|
|
extrude.Rebuild();
|
|
}
|
|
}
|
|
}
|
|
}
|