testss
@@ -0,0 +1,11 @@
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
[CustomEditor(typeof(UnityEngine.Experimental.Rendering.Universal.CinemachineUniversalPixelPerfect)), CanEditMultipleObjects]
|
||||
internal class CinemachineUniversalPixelPerfectEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox("This Cinemachine extension is now deprecated and doesn't function properly. Instead, use the one from Cinemachine v2.4.0 or newer.", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
[CustomEditor(typeof(CompositeShadowCaster2D))]
|
||||
internal class CompositeShadowCaster2DEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
internal static class FreeformPathPresets
|
||||
{
|
||||
public static Vector3[] CreateSquare()
|
||||
{
|
||||
Vector3[] returnPath = new Vector3[4]
|
||||
{
|
||||
new Vector3(-0.5f, -0.5f),
|
||||
new Vector3(0.5f, -0.5f),
|
||||
new Vector3(0.5f, 0.5f),
|
||||
new Vector3(-0.5f, 0.5f)
|
||||
};
|
||||
|
||||
return returnPath;
|
||||
}
|
||||
|
||||
public static Vector3[] CreateIsometricDiamond()
|
||||
{
|
||||
Vector3[] returnPath = new Vector3[4]
|
||||
{
|
||||
new Vector3(-0.5f, 0.0f),
|
||||
new Vector3(0.0f, -0.25f),
|
||||
new Vector3(0.5f, 0.0f),
|
||||
new Vector3(0.0f, 0.25f)
|
||||
};
|
||||
|
||||
return returnPath;
|
||||
}
|
||||
|
||||
private static Vector3[] CreateShape(int vertices, float angleOffset)
|
||||
{
|
||||
Vector3[] returnPath = new Vector3[vertices];
|
||||
const float kRadius = 0.5f;
|
||||
|
||||
for (int i = 0; i < vertices; i++)
|
||||
{
|
||||
float angle = ((float)i * 2 * Mathf.PI / (float)vertices) + angleOffset;
|
||||
float x = kRadius * Mathf.Cos(angle);
|
||||
float y = kRadius * Mathf.Sin(angle);
|
||||
|
||||
returnPath[i] = new Vector3(x, y);
|
||||
}
|
||||
|
||||
return returnPath;
|
||||
}
|
||||
|
||||
public static Vector3[] CreateCircle()
|
||||
{
|
||||
return CreateShape(32, 0);
|
||||
}
|
||||
|
||||
public static Vector3[] CreateHexagonFlatTop()
|
||||
{
|
||||
return CreateShape(6, 0);
|
||||
}
|
||||
|
||||
public static Vector3[] CreateHexagonPointedTop()
|
||||
{
|
||||
return CreateShape(6, 0.5f * Mathf.PI);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,833 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEditor.Rendering.Universal;
|
||||
using UnityEditor.Rendering;
|
||||
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
[CustomEditor(typeof(Light2D))]
|
||||
[CanEditMultipleObjects]
|
||||
internal class Light2DEditor : PathComponentEditor<ScriptablePath>
|
||||
{
|
||||
[EditorTool("Edit Freeform Shape", typeof(Light2D))]
|
||||
class FreeformShapeTool : PathEditorTool<ScriptablePath>
|
||||
{
|
||||
const string k_ShapePath = "m_ShapePath";
|
||||
|
||||
public override bool IsAvailable()
|
||||
{
|
||||
var light = target as Light2D;
|
||||
|
||||
if (light == null)
|
||||
return false;
|
||||
else
|
||||
return base.IsAvailable() && light.lightType == Light2D.LightType.Freeform;
|
||||
}
|
||||
|
||||
protected override IShape GetShape(Object target)
|
||||
{
|
||||
return (target as Light2D).shapePath.ToPolygon(false);
|
||||
}
|
||||
|
||||
protected override void SetShape(ScriptablePath shapeEditor, SerializedObject serializedObject)
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
var pointsProperty = serializedObject.FindProperty(k_ShapePath);
|
||||
pointsProperty.arraySize = shapeEditor.pointCount;
|
||||
|
||||
for (var i = 0; i < shapeEditor.pointCount; ++i)
|
||||
pointsProperty.GetArrayElementAtIndex(i).vector3Value = shapeEditor.GetPoint(i).position;
|
||||
|
||||
((Light2D)(serializedObject.targetObject)).UpdateMesh(true);
|
||||
|
||||
// This is untracked right now...
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Styles
|
||||
{
|
||||
public static readonly GUIContent InnerOuterSpotAngle = EditorGUIUtility.TrTextContent("Inner / Outer Spot Angle", "Adjusts the inner / outer angles of this light to change the angle ranges of this Spot Light’s beam.");
|
||||
|
||||
public static Texture lightCapTopRight = Resources.Load<Texture>("LightCapTopRight");
|
||||
public static Texture lightCapTopLeft = Resources.Load<Texture>("LightCapTopLeft");
|
||||
public static Texture lightCapBottomLeft = Resources.Load<Texture>("LightCapBottomLeft");
|
||||
public static Texture lightCapBottomRight = Resources.Load<Texture>("LightCapBottomRight");
|
||||
public static Texture lightCapUp = Resources.Load<Texture>("LightCapUp");
|
||||
public static Texture lightCapDown = Resources.Load<Texture>("LightCapDown");
|
||||
|
||||
|
||||
public static GUIContent lightTypeFreeform = new GUIContent("Freeform", Resources.Load("InspectorIcons/FreeformLight") as Texture);
|
||||
public static GUIContent lightTypeSprite = new GUIContent("Sprite", Resources.Load("InspectorIcons/SpriteLight") as Texture);
|
||||
public static GUIContent lightTypePoint = new GUIContent("Spot", Resources.Load("InspectorIcons/PointLight") as Texture);
|
||||
public static GUIContent lightTypeGlobal = new GUIContent("Global", Resources.Load("InspectorIcons/GlobalLight") as Texture);
|
||||
public static GUIContent[] lightTypeOptions = new GUIContent[] { lightTypeFreeform, lightTypeSprite, lightTypePoint, lightTypeGlobal };
|
||||
|
||||
|
||||
public static GUIContent blendingSettingsFoldout = EditorGUIUtility.TrTextContent("Blending", "Options used for blending");
|
||||
public static GUIContent shadowsSettingsFoldout = EditorGUIUtility.TrTextContent("Shadows", "Options used for shadows");
|
||||
public static GUIContent volumetricSettingsFoldout = EditorGUIUtility.TrTextContent("Volumetric", "Options used for volumetric lighting");
|
||||
public static GUIContent normalMapsSettingsFoldout = EditorGUIUtility.TrTextContent("Normal Maps", "Options used for normal maps");
|
||||
|
||||
public static GUIContent generalLightType = EditorGUIUtility.TrTextContent("Light Type", "Select the light type. \n\nGlobal Light: For ambient light. \nSpot Light: For a spot light / point light. \nFreeform Light: For a custom shape light. \nSprite Light: For a custom light cookie using Sprites.");
|
||||
|
||||
public static GUIContent generalFalloffSize = EditorGUIUtility.TrTextContent("Falloff", "Adjusts the falloff area of this light. The higher the falloff value, the larger area the falloff spans.");
|
||||
public static GUIContent generalFalloffIntensity = EditorGUIUtility.TrTextContent("Falloff Strength", "Adjusts the falloff curve to control the softness of this light’s edges. The higher the falloff strength, the softer the edges of this light.");
|
||||
public static GUIContent generalLightColor = EditorGUIUtility.TrTextContent("Color", "Adjusts this light’s color.");
|
||||
public static GUIContent generalLightIntensity = EditorGUIUtility.TrTextContent("Intensity", "Adjusts this light’s color intensity by using multiply to brighten the Sprite beyond its original color.");
|
||||
public static GUIContent generalVolumeIntensity = EditorGUIUtility.TrTextContent("Intensity", "Adjusts the intensity of this additional light volume that's additively blended on top of this light. To enable the Volumetric Shadow Strength, increase this Intensity to be greater than 0.");
|
||||
public static GUIContent generalBlendStyle = EditorGUIUtility.TrTextContent("Blend Style", "Adjusts how this light blends with the Sprites on the Target Sorting Layers. Different Blend Styles can be customized in the 2D Renderer Data Asset.");
|
||||
public static GUIContent generalLightOverlapOperation = EditorGUIUtility.TrTextContent("Overlap Operation", "Determines how this light blends with the other lights either through additive or alpha blending.");
|
||||
public static GUIContent generalLightOrder = EditorGUIUtility.TrTextContent("Light Order", "Determines the relative order in which lights of the same Blend Style get rendered. Lights with lower values are rendered first.");
|
||||
public static GUIContent generalShadowIntensity = EditorGUIUtility.TrTextContent("Strength", "Adjusts the amount of light occlusion from the Shadow Caster 2D component(s) when blocking this light.The higher the value, the more opaque the shadow becomes.");
|
||||
public static GUIContent generalShadowVolumeIntensity = EditorGUIUtility.TrTextContent("Shadow Strength", "Adjusts the amount of volume light occlusion from the Shadow Caster 2D component(s) when blocking this light.");
|
||||
public static GUIContent generalSortingLayerPrefixLabel = EditorGUIUtility.TrTextContent("Target Sorting Layers", "Determines which layers this light affects. To optimize performance, minimize the number of layers this light affects.");
|
||||
public static GUIContent generalLightNoLightEnabled = EditorGUIUtility.TrTextContentWithIcon("No valid blend styles are enabled.", MessageType.Error);
|
||||
public static GUIContent generalNormalMapZDistance = EditorGUIUtility.TrTextContent("Distance", "Adjusts the z-axis distance of this light and the lit Sprite(s). Do note that this distance does not Transform the position of this light in the Scene.");
|
||||
public static GUIContent generalNormalMapLightQuality = EditorGUIUtility.TrTextContent("Quality", "Determines the accuracy of the lighting calculations when normal map is used. To optimize for performance, select Fast.");
|
||||
|
||||
public static GUIContent pointLightRadius = EditorGUIUtility.TrTextContent("Radius", "Adjusts the inner / outer radius of this light to change the size of this light.");
|
||||
public static GUIContent pointLightInner = EditorGUIUtility.TrTextContent("Inner", "Specify the inner radius of the light");
|
||||
public static GUIContent pointLightOuter = EditorGUIUtility.TrTextContent("Outer", "Specify the outer radius of the light");
|
||||
public static GUIContent pointLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Specify the sprite (deprecated)");
|
||||
|
||||
public static GUIContent shapeLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Assign a Sprite which acts as a mask to create a light cookie.");
|
||||
|
||||
public static GUIContent deprecatedParametricLightWarningSingle = EditorGUIUtility.TrTextContentWithIcon("Parametic Lights have been deprecated. To continue, upgrade your Parametric Light to a Freeform Light to enjoy similar light functionality.", MessageType.Warning);
|
||||
public static GUIContent deprecatedParametricLightWarningMulti = EditorGUIUtility.TrTextContentWithIcon("Parametic Lights have been deprecated. To continue, upgrade your Parametric Lights to Freeform Lights to enjoy similar light functionality.", MessageType.Warning);
|
||||
public static GUIContent deprecatedParametricLightInstructions = EditorGUIUtility.TrTextContent("Alternatively, you may choose to upgrade from the menu. Edit > Render Pipeline > Universal Render Pipeline > Upgrade Project/Scene Parametric Lights to Freeform");
|
||||
public static GUIContent deprecatedParametricLightButtonSingle = EditorGUIUtility.TrTextContent("Upgrade Parametric Light");
|
||||
public static GUIContent deprecatedParametricLightButtonMulti = EditorGUIUtility.TrTextContent("Upgrade Parametric Lights");
|
||||
|
||||
public static GUIContent renderPipelineUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("Universal scriptable renderpipeline asset must be assigned in Graphics Settings or Quality Settings.", MessageType.Warning);
|
||||
public static GUIContent asset2DUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("2D renderer data must be assigned to your universal render pipeline asset or camera.", MessageType.Warning);
|
||||
|
||||
public static string deprecatedParametricLightDialogTextSingle = "The upgrade will convert the selected parametric light into a freeform light. You can't undo this operation.";
|
||||
public static string deprecatedParametricLightDialogTextMulti = "The upgrade will convert the selected parametric lights into freeform lights. You can't undo this operation.";
|
||||
public static string deprecatedParametricLightDialogTitle = "Parametric Light Upgrader";
|
||||
public static string deprecatedParametricLightDialogProceed = "Proceed";
|
||||
public static string deprecatedParametricLightDialogCancel = "Cancel";
|
||||
}
|
||||
|
||||
const float k_GlobalLightGizmoSize = 1.2f;
|
||||
const float k_AngleCapSize = 0.16f * k_GlobalLightGizmoSize;
|
||||
const float k_AngleCapOffset = 0.08f * k_GlobalLightGizmoSize;
|
||||
const float k_AngleCapOffsetSecondary = -0.05f;
|
||||
const float k_RangeCapSize = 0.025f * k_GlobalLightGizmoSize;
|
||||
const float k_InnerRangeCapSize = 0.08f * k_GlobalLightGizmoSize;
|
||||
|
||||
SerializedProperty m_LightType;
|
||||
SerializedProperty m_LightColor;
|
||||
SerializedProperty m_LightIntensity;
|
||||
SerializedProperty m_UseNormalMap;
|
||||
SerializedProperty m_ShadowIntensity;
|
||||
SerializedProperty m_ShadowIntensityEnabled;
|
||||
SerializedProperty m_ShadowVolumeIntensity;
|
||||
SerializedProperty m_ShadowVolumeIntensityEnabled;
|
||||
SerializedProperty m_ApplyToSortingLayers;
|
||||
SerializedProperty m_VolumetricIntensity;
|
||||
SerializedProperty m_VolumetricIntensityEnabled;
|
||||
SerializedProperty m_BlendStyleIndex;
|
||||
SerializedProperty m_FalloffIntensity;
|
||||
SerializedProperty m_NormalMapZDistance;
|
||||
SerializedProperty m_NormalMapQuality;
|
||||
SerializedProperty m_LightOrder;
|
||||
SerializedProperty m_OverlapOperation;
|
||||
|
||||
// Point Light Properties
|
||||
SerializedProperty m_PointInnerAngle;
|
||||
SerializedProperty m_PointOuterAngle;
|
||||
SerializedProperty m_PointInnerRadius;
|
||||
SerializedProperty m_PointOuterRadius;
|
||||
SerializedProperty m_DeprecatedPointLightSprite;
|
||||
|
||||
// Shape Light Properties
|
||||
SerializedProperty m_ShapeLightParametricRadius;
|
||||
SerializedProperty m_ShapeLightFalloffSize;
|
||||
SerializedProperty m_ShapeLightParametricSides;
|
||||
SerializedProperty m_ShapeLightSprite;
|
||||
|
||||
SavedBool m_BlendingSettingsFoldout;
|
||||
SavedBool m_ShadowsSettingsFoldout;
|
||||
SavedBool m_VolumetricSettingsFoldout;
|
||||
SavedBool m_NormalMapsSettingsFoldout;
|
||||
|
||||
|
||||
int[] m_BlendStyleIndices;
|
||||
GUIContent[] m_BlendStyleNames;
|
||||
bool m_AnyBlendStyleEnabled = false;
|
||||
|
||||
SortingLayerDropDown m_SortingLayerDropDown;
|
||||
|
||||
Light2D lightObject => target as Light2D;
|
||||
|
||||
Analytics.Renderer2DAnalytics m_Analytics;
|
||||
HashSet<Light2D> m_ModifiedLights;
|
||||
|
||||
private void AnalyticsTrackChanges(SerializedObject serializedObject)
|
||||
{
|
||||
if (serializedObject.hasModifiedProperties)
|
||||
{
|
||||
foreach (Object targetObj in serializedObject.targetObjects)
|
||||
{
|
||||
Light2D light2d = (Light2D)targetObj;
|
||||
if (!m_ModifiedLights.Contains(light2d))
|
||||
m_ModifiedLights.Add(light2d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_Analytics = Analytics.Renderer2DAnalytics.instance;
|
||||
m_ModifiedLights = new HashSet<Light2D>();
|
||||
m_SortingLayerDropDown = new SortingLayerDropDown();
|
||||
|
||||
m_BlendingSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPBlendingSettingsFoldout", false);
|
||||
m_ShadowsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPShadowsSettingsFoldout", false);
|
||||
m_VolumetricSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPVolumetricSettingsFoldout", false);
|
||||
m_NormalMapsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPNormalMapsSettingsFoldout", false);
|
||||
|
||||
m_LightType = serializedObject.FindProperty("m_LightType");
|
||||
m_LightColor = serializedObject.FindProperty("m_Color");
|
||||
m_LightIntensity = serializedObject.FindProperty("m_Intensity");
|
||||
m_UseNormalMap = serializedObject.FindProperty("m_UseNormalMap");
|
||||
m_ShadowIntensity = serializedObject.FindProperty("m_ShadowIntensity");
|
||||
m_ShadowIntensityEnabled = serializedObject.FindProperty("m_ShadowIntensityEnabled");
|
||||
m_ShadowVolumeIntensity = serializedObject.FindProperty("m_ShadowVolumeIntensity");
|
||||
m_ShadowVolumeIntensityEnabled = serializedObject.FindProperty("m_ShadowVolumeIntensityEnabled");
|
||||
m_ApplyToSortingLayers = serializedObject.FindProperty("m_ApplyToSortingLayers");
|
||||
m_VolumetricIntensity = serializedObject.FindProperty("m_LightVolumeIntensity");
|
||||
m_VolumetricIntensityEnabled = serializedObject.FindProperty("m_LightVolumeIntensityEnabled");
|
||||
m_BlendStyleIndex = serializedObject.FindProperty("m_BlendStyleIndex");
|
||||
m_FalloffIntensity = serializedObject.FindProperty("m_FalloffIntensity");
|
||||
m_NormalMapZDistance = serializedObject.FindProperty("m_NormalMapDistance");
|
||||
m_NormalMapQuality = serializedObject.FindProperty("m_NormalMapQuality");
|
||||
m_LightOrder = serializedObject.FindProperty("m_LightOrder");
|
||||
m_OverlapOperation = serializedObject.FindProperty("m_OverlapOperation");
|
||||
|
||||
// Point Light
|
||||
m_PointInnerAngle = serializedObject.FindProperty("m_PointLightInnerAngle");
|
||||
m_PointOuterAngle = serializedObject.FindProperty("m_PointLightOuterAngle");
|
||||
m_PointInnerRadius = serializedObject.FindProperty("m_PointLightInnerRadius");
|
||||
m_PointOuterRadius = serializedObject.FindProperty("m_PointLightOuterRadius");
|
||||
m_DeprecatedPointLightSprite = serializedObject.FindProperty("m_DeprecatedPointLightCookieSprite");
|
||||
|
||||
// Shape Light
|
||||
m_ShapeLightParametricRadius = serializedObject.FindProperty("m_ShapeLightParametricRadius");
|
||||
m_ShapeLightFalloffSize = serializedObject.FindProperty("m_ShapeLightFalloffSize");
|
||||
m_ShapeLightParametricSides = serializedObject.FindProperty("m_ShapeLightParametricSides");
|
||||
m_ShapeLightSprite = serializedObject.FindProperty("m_LightCookieSprite");
|
||||
|
||||
m_AnyBlendStyleEnabled = false;
|
||||
var blendStyleIndices = new List<int>();
|
||||
var blendStyleNames = new List<string>();
|
||||
|
||||
var rendererData = Light2DEditorUtility.GetRenderer2DData();
|
||||
if (rendererData != null)
|
||||
{
|
||||
for (int i = 0; i < rendererData.lightBlendStyles.Length; ++i)
|
||||
{
|
||||
blendStyleIndices.Add(i);
|
||||
|
||||
ref var blendStyle = ref rendererData.lightBlendStyles[i];
|
||||
|
||||
if (blendStyle.maskTextureChannel == Light2DBlendStyle.TextureChannel.None)
|
||||
blendStyleNames.Add(blendStyle.name);
|
||||
else
|
||||
{
|
||||
var name = string.Format("{0} ({1})", blendStyle.name, blendStyle.maskTextureChannel);
|
||||
blendStyleNames.Add(name);
|
||||
}
|
||||
|
||||
m_AnyBlendStyleEnabled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
blendStyleIndices.Add(i);
|
||||
blendStyleNames.Add("Operation" + i);
|
||||
}
|
||||
}
|
||||
|
||||
m_BlendStyleIndices = blendStyleIndices.ToArray();
|
||||
m_BlendStyleNames = blendStyleNames.Select(x => new GUIContent(x)).ToArray();
|
||||
|
||||
|
||||
m_SortingLayerDropDown.OnEnable(serializedObject, "m_ApplyToSortingLayers");
|
||||
}
|
||||
|
||||
internal void SendModifiedAnalytics(Analytics.Renderer2DAnalytics analytics, Light2D light)
|
||||
{
|
||||
Analytics.Light2DData lightData = new Analytics.Light2DData();
|
||||
lightData.was_create_event = false;
|
||||
lightData.instance_id = light.GetInstanceID();
|
||||
lightData.light_type = light.lightType;
|
||||
Analytics.Renderer2DAnalytics.instance.SendData(Analytics.AnalyticsDataTypes.k_LightDataString, lightData);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (m_ModifiedLights != null && m_ModifiedLights.Count > 0)
|
||||
{
|
||||
foreach (Light2D light in m_ModifiedLights)
|
||||
{
|
||||
SendModifiedAnalytics(m_Analytics, light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBlendingGroup()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter(false);
|
||||
m_BlendingSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.blendingSettingsFoldout, m_BlendingSettingsFoldout.value);
|
||||
if (m_BlendingSettingsFoldout.value)
|
||||
{
|
||||
if (!m_AnyBlendStyleEnabled)
|
||||
EditorGUILayout.HelpBox(Styles.generalLightNoLightEnabled);
|
||||
else
|
||||
EditorGUILayout.IntPopup(m_BlendStyleIndex, m_BlendStyleNames, m_BlendStyleIndices, Styles.generalBlendStyle);
|
||||
|
||||
EditorGUILayout.PropertyField(m_LightOrder, Styles.generalLightOrder);
|
||||
EditorGUILayout.PropertyField(m_OverlapOperation, Styles.generalLightOverlapOperation);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawShadowsGroup()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter(false);
|
||||
m_ShadowsSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.shadowsSettingsFoldout, m_ShadowsSettingsFoldout.value);
|
||||
if (m_ShadowsSettingsFoldout.value)
|
||||
{
|
||||
DrawToggleProperty(Styles.generalShadowIntensity, m_ShadowIntensityEnabled, m_ShadowIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawVolumetricGroup()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter(false);
|
||||
m_VolumetricSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.volumetricSettingsFoldout, m_VolumetricSettingsFoldout.value);
|
||||
if (m_VolumetricSettingsFoldout.value)
|
||||
{
|
||||
DrawToggleProperty(Styles.generalVolumeIntensity, m_VolumetricIntensityEnabled, m_VolumetricIntensity);
|
||||
if (m_VolumetricIntensity.floatValue < 0)
|
||||
m_VolumetricIntensity.floatValue = 0;
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!m_VolumetricIntensityEnabled.boolValue);
|
||||
DrawToggleProperty(Styles.generalShadowVolumeIntensity, m_ShadowVolumeIntensityEnabled, m_ShadowVolumeIntensity);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawNormalMapGroup()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter(false);
|
||||
m_NormalMapsSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.normalMapsSettingsFoldout, m_NormalMapsSettingsFoldout.value);
|
||||
if (m_NormalMapsSettingsFoldout.value)
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_NormalMapQuality, Styles.generalNormalMapLightQuality);
|
||||
|
||||
EditorGUI.BeginDisabledGroup(m_NormalMapQuality.intValue == (int)Light2D.NormalMapQuality.Disabled);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(m_NormalMapZDistance, Styles.generalNormalMapZDistance);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
m_NormalMapZDistance.floatValue = Mathf.Max(0.0f, m_NormalMapZDistance.floatValue);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawFoldouts()
|
||||
{
|
||||
DrawBlendingGroup();
|
||||
DrawShadowsGroup();
|
||||
DrawVolumetricGroup();
|
||||
DrawNormalMapGroup();
|
||||
}
|
||||
|
||||
void DrawRadiusProperties(GUIContent label, SerializedProperty innerRadius, GUIContent content1, SerializedProperty outerRadius, GUIContent content2)
|
||||
{
|
||||
GUIStyle style = GUI.skin.box;
|
||||
|
||||
float savedLabelWidth = EditorGUIUtility.labelWidth;
|
||||
int savedIndentLevel = EditorGUI.indentLevel;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel(label);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUI.indentLevel = 0;
|
||||
EditorGUIUtility.labelWidth = style.CalcSize(content1).x;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(innerRadius, content1);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (innerRadius.floatValue > outerRadius.floatValue)
|
||||
innerRadius.floatValue = outerRadius.floatValue;
|
||||
else if (innerRadius.floatValue < 0)
|
||||
innerRadius.floatValue = 0;
|
||||
}
|
||||
|
||||
EditorGUIUtility.labelWidth = style.CalcSize(content2).x;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(outerRadius, content2);
|
||||
if (EditorGUI.EndChangeCheck() && outerRadius.floatValue < innerRadius.floatValue)
|
||||
outerRadius.floatValue = innerRadius.floatValue;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUIUtility.labelWidth = savedLabelWidth;
|
||||
EditorGUI.indentLevel = savedIndentLevel;
|
||||
}
|
||||
|
||||
void DrawToggleProperty(GUIContent label, SerializedProperty boolProperty, SerializedProperty property)
|
||||
{
|
||||
int savedIndentLevel = EditorGUI.indentLevel;
|
||||
float savedLabelWidth = EditorGUIUtility.labelWidth;
|
||||
const int kCheckboxWidth = 20;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(boolProperty, GUIContent.none, GUILayout.MaxWidth(kCheckboxWidth));
|
||||
|
||||
EditorGUIUtility.labelWidth = EditorGUIUtility.labelWidth - kCheckboxWidth;
|
||||
EditorGUI.BeginDisabledGroup(!boolProperty.boolValue);
|
||||
EditorGUILayout.PropertyField(property, label);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.indentLevel = savedIndentLevel;
|
||||
|
||||
EditorGUIUtility.labelWidth = savedLabelWidth;
|
||||
}
|
||||
|
||||
public void DrawInnerAndOuterSpotAngle(SerializedProperty minProperty, SerializedProperty maxProperty, GUIContent label)
|
||||
{
|
||||
float textFieldWidth = 45f;
|
||||
|
||||
float min = minProperty.floatValue;
|
||||
float max = maxProperty.floatValue;
|
||||
|
||||
var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
|
||||
// This widget is a little bit of a special case.
|
||||
// The right hand side of the min max slider will control the reset of the max value
|
||||
// The left hand side of the min max slider will control the reset of the min value
|
||||
// The label itself will not have a right click and reset value.
|
||||
|
||||
rect = EditorGUI.PrefixLabel(rect, label);
|
||||
EditorGUI.BeginProperty(new Rect(rect) { width = rect.width * 0.5f }, label, minProperty);
|
||||
EditorGUI.BeginProperty(new Rect(rect) { xMin = rect.x + rect.width * 0.5f }, GUIContent.none, maxProperty);
|
||||
|
||||
var minRect = new Rect(rect) { width = textFieldWidth };
|
||||
var maxRect = new Rect(rect) { xMin = rect.xMax - textFieldWidth };
|
||||
var sliderRect = new Rect(rect) { xMin = minRect.xMax + 4, xMax = maxRect.xMin - 4 };
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUI.DelayedFloatField(minRect, minProperty, GUIContent.none);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (minProperty.floatValue > maxProperty.floatValue)
|
||||
minProperty.floatValue = maxProperty.floatValue;
|
||||
else if (minProperty.floatValue < 0)
|
||||
minProperty.floatValue = 0;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUI.MinMaxSlider(sliderRect, ref min, ref max, 0f, 360f);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
minProperty.floatValue = min;
|
||||
maxProperty.floatValue = max;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUI.DelayedFloatField(maxRect, m_PointOuterAngle, GUIContent.none);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (minProperty.floatValue > maxProperty.floatValue)
|
||||
maxProperty.floatValue = minProperty.floatValue;
|
||||
else if (maxProperty.floatValue > 360)
|
||||
maxProperty.floatValue = 360;
|
||||
}
|
||||
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
void DrawGlobalLight(SerializedObject serializedObject)
|
||||
{
|
||||
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
|
||||
DrawBlendingGroup();
|
||||
}
|
||||
|
||||
void DrawParametricDeprecated(SerializedObject serializedObject)
|
||||
{
|
||||
GUIContent buttonText = targets.Length > 1 ? Styles.deprecatedParametricLightButtonMulti : Styles.deprecatedParametricLightButtonSingle;
|
||||
GUIContent helpText = targets.Length > 1 ? Styles.deprecatedParametricLightWarningMulti : Styles.deprecatedParametricLightWarningSingle;
|
||||
string dialogText = targets.Length > 1 ? Styles.deprecatedParametricLightDialogTextMulti : Styles.deprecatedParametricLightDialogTextSingle;
|
||||
|
||||
EditorGUILayout.HelpBox(helpText);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
|
||||
if (GUILayout.Button(buttonText))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog(Styles.deprecatedParametricLightDialogTitle, dialogText, Styles.deprecatedParametricLightDialogProceed, Styles.deprecatedParametricLightDialogCancel))
|
||||
{
|
||||
for (int i = 0; i < targets.Length; i++)
|
||||
{
|
||||
Light2D light = (Light2D)targets[i];
|
||||
|
||||
if (light.lightType == (Light2D.LightType)Light2D.DeprecatedLightType.Parametric)
|
||||
Renderer2DUpgrader.UpgradeParametricLight(light);
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.HelpBox(Styles.deprecatedParametricLightInstructions);
|
||||
}
|
||||
|
||||
bool DrawLightCommon()
|
||||
{
|
||||
var meshChanged = false;
|
||||
Rect lightTypeRect = EditorGUILayout.GetControlRect();
|
||||
EditorGUI.BeginProperty(lightTypeRect, GUIContent.none, m_LightType);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int newLightType = EditorGUI.Popup(lightTypeRect, Styles.generalLightType, m_LightType.intValue - 1, Styles.lightTypeOptions); // -1 is a bit hacky its to support compatibiltiy. We need something better.
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
m_LightType.intValue = newLightType + 1; // -1 is a bit hacky its to support compatibiltiy. We need something better.
|
||||
meshChanged = true;
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
|
||||
// Color and intensity
|
||||
EditorGUILayout.PropertyField(m_LightColor, Styles.generalLightColor);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(m_LightIntensity, Styles.generalLightIntensity);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
m_LightIntensity.floatValue = Mathf.Max(m_LightIntensity.floatValue, 0);
|
||||
|
||||
return meshChanged;
|
||||
}
|
||||
|
||||
void DrawSpotLight(SerializedObject serializedObject)
|
||||
{
|
||||
DrawRadiusProperties(Styles.pointLightRadius, m_PointInnerRadius, Styles.pointLightInner, m_PointOuterRadius, Styles.pointLightOuter);
|
||||
DrawInnerAndOuterSpotAngle(m_PointInnerAngle, m_PointOuterAngle, Styles.InnerOuterSpotAngle);
|
||||
EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
|
||||
|
||||
if (m_DeprecatedPointLightSprite.objectReferenceValue != null)
|
||||
EditorGUILayout.PropertyField(m_DeprecatedPointLightSprite, Styles.pointLightSprite);
|
||||
|
||||
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
|
||||
|
||||
DrawFoldouts();
|
||||
}
|
||||
|
||||
void DrawSpriteLight(SerializedObject serializedObject)
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_ShapeLightSprite, Styles.shapeLightSprite);
|
||||
|
||||
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
|
||||
DrawFoldouts();
|
||||
}
|
||||
|
||||
void DrawShapeLight(SerializedObject serializedObject)
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_ShapeLightFalloffSize, Styles.generalFalloffSize);
|
||||
if (m_ShapeLightFalloffSize.floatValue < 0)
|
||||
m_ShapeLightFalloffSize.floatValue = 0;
|
||||
|
||||
EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
|
||||
|
||||
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
|
||||
|
||||
if (m_LightType.intValue == (int)Light2D.LightType.Freeform)
|
||||
{
|
||||
DoEditButton<FreeformShapeTool>(PathEditorToolContents.icon, "Edit Shape");
|
||||
DoPathInspector<FreeformShapeTool>();
|
||||
DoSnappingInspector<FreeformShapeTool>();
|
||||
}
|
||||
|
||||
DrawFoldouts();
|
||||
}
|
||||
|
||||
Vector3 DrawAngleSlider2D(Transform transform, Quaternion rotation, float radius, float offset, Handles.CapFunction capFunc, float capSize, bool leftAngle, bool drawLine, bool useCapOffset, ref float angle)
|
||||
{
|
||||
float oldAngle = angle;
|
||||
|
||||
float angleBy2 = (angle / 2) * (leftAngle ? -1.0f : 1.0f);
|
||||
Vector3 trcwPos = Quaternion.AngleAxis(angleBy2, -transform.forward) * (transform.up);
|
||||
Vector3 cwPos = transform.position + trcwPos * (radius + offset);
|
||||
|
||||
float direction = leftAngle ? 1 : -1;
|
||||
|
||||
// Offset the handle
|
||||
float size = .25f * capSize;
|
||||
|
||||
Vector3 handleOffset = useCapOffset ? rotation * new Vector3(direction * size, 0, 0) : Vector3.zero;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var id = GUIUtility.GetControlID("AngleSlider".GetHashCode(), FocusType.Passive);
|
||||
Vector3 cwHandle = Handles.Slider2D(id, cwPos, handleOffset, Vector3.forward, rotation * Vector3.up, rotation * Vector3.right, capSize, capFunc, Vector3.zero);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Vector3 toCwHandle = (transform.position - cwHandle).normalized;
|
||||
|
||||
angle = 360 - 2 * Quaternion.Angle(Quaternion.FromToRotation(transform.up, toCwHandle), Quaternion.identity);
|
||||
angle = Mathf.Round(angle * 100) / 100f;
|
||||
|
||||
float side = Vector3.Dot(direction * transform.right, toCwHandle);
|
||||
if (side < 0)
|
||||
{
|
||||
if (oldAngle < 180)
|
||||
angle = 0;
|
||||
else
|
||||
angle = 360;
|
||||
}
|
||||
}
|
||||
|
||||
if (drawLine)
|
||||
Handles.DrawLine(transform.position, cwHandle);
|
||||
|
||||
return cwHandle;
|
||||
}
|
||||
|
||||
private float DrawAngleHandle(Transform transform, float radius, float offset, Handles.CapFunction capLeft, Handles.CapFunction capRight, ref float angle)
|
||||
{
|
||||
float old = angle;
|
||||
float handleOffset = HandleUtility.GetHandleSize(transform.position) * offset;
|
||||
float handleSize = HandleUtility.GetHandleSize(transform.position) * k_AngleCapSize;
|
||||
|
||||
Quaternion rotLt = Quaternion.AngleAxis(-angle / 2, -transform.forward) * transform.rotation;
|
||||
DrawAngleSlider2D(transform, rotLt, radius, handleOffset, capLeft, handleSize, true, true, true, ref angle);
|
||||
|
||||
Quaternion rotRt = Quaternion.AngleAxis(angle / 2, -transform.forward) * transform.rotation;
|
||||
DrawAngleSlider2D(transform, rotRt, radius, handleOffset, capRight, handleSize, false, true, true, ref angle);
|
||||
|
||||
return angle - old;
|
||||
}
|
||||
|
||||
private void DrawRadiusArc(Transform transform, float radius, float angle, int steps, Handles.CapFunction capFunc, float capSize, bool even)
|
||||
{
|
||||
Handles.DrawWireArc(transform.position, transform.forward, Quaternion.AngleAxis(180 - angle / 2, transform.forward) * -transform.up, angle, radius);
|
||||
}
|
||||
|
||||
Handles.CapFunction GetCapFunc(Texture texture, bool isAngleHandle)
|
||||
{
|
||||
return (controlID, position, rotation, size, eventType) => Light2DEditorUtility.GUITextureCap(controlID, texture, position, rotation, size, eventType, isAngleHandle);
|
||||
}
|
||||
|
||||
private void DrawAngleHandles(Light2D light)
|
||||
{
|
||||
var oldColor = Handles.color;
|
||||
Handles.color = Color.yellow;
|
||||
|
||||
float outerAngle = light.pointLightOuterAngle;
|
||||
float diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, k_AngleCapOffset, GetCapFunc(Styles.lightCapTopRight, true), GetCapFunc(Styles.lightCapBottomRight, true), ref outerAngle);
|
||||
light.pointLightOuterAngle = outerAngle;
|
||||
|
||||
if (diff != 0.0f)
|
||||
light.pointLightInnerAngle = Mathf.Max(0.0f, light.pointLightInnerAngle + diff);
|
||||
|
||||
float innerAngle = light.pointLightInnerAngle;
|
||||
diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, -k_AngleCapOffset, GetCapFunc(Styles.lightCapTopLeft, true), GetCapFunc(Styles.lightCapBottomLeft, true), ref innerAngle);
|
||||
light.pointLightInnerAngle = innerAngle;
|
||||
|
||||
if (diff != 0.0f)
|
||||
light.pointLightInnerAngle = light.pointLightInnerAngle < light.pointLightOuterAngle ? light.pointLightInnerAngle : light.pointLightOuterAngle;
|
||||
|
||||
light.pointLightInnerAngle = Mathf.Min(light.pointLightInnerAngle, light.pointLightOuterAngle);
|
||||
|
||||
Handles.color = oldColor;
|
||||
}
|
||||
|
||||
private void DrawRangeHandles(Light2D light)
|
||||
{
|
||||
var dummy = 0.0f;
|
||||
bool radiusChanged = false;
|
||||
Vector3 handlePos = Vector3.zero;
|
||||
Quaternion rotLeft = Quaternion.AngleAxis(0, -light.transform.forward) * light.transform.rotation;
|
||||
float handleOffset = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapOffsetSecondary;
|
||||
float handleSize = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapSize;
|
||||
|
||||
var oldColor = Handles.color;
|
||||
Handles.color = Color.yellow;
|
||||
|
||||
float outerRadius = light.pointLightOuterRadius;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Vector3 returnPos = DrawAngleSlider2D(light.transform, rotLeft, outerRadius, -handleOffset, GetCapFunc(Styles.lightCapUp, false), handleSize, false, false, false, ref dummy);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
var vec = (returnPos - light.transform.position).normalized;
|
||||
light.transform.up = new Vector3(vec.x, vec.y, 0);
|
||||
outerRadius = (returnPos - light.transform.position).magnitude;
|
||||
outerRadius = outerRadius + handleOffset;
|
||||
radiusChanged = true;
|
||||
}
|
||||
DrawRadiusArc(light.transform, light.pointLightOuterRadius, light.pointLightOuterAngle, 0, Handles.DotHandleCap, k_RangeCapSize, false);
|
||||
|
||||
Handles.color = Color.gray;
|
||||
float innerRadius = light.pointLightInnerRadius;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
returnPos = DrawAngleSlider2D(light.transform, rotLeft, innerRadius, handleOffset, GetCapFunc(Styles.lightCapDown, false), handleSize, true, false, false, ref dummy);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
innerRadius = (returnPos - light.transform.position).magnitude;
|
||||
innerRadius = innerRadius - handleOffset;
|
||||
radiusChanged = true;
|
||||
}
|
||||
DrawRadiusArc(light.transform, light.pointLightInnerRadius, light.pointLightOuterAngle, 0, Handles.SphereHandleCap, k_InnerRangeCapSize, false);
|
||||
|
||||
Handles.color = oldColor;
|
||||
|
||||
if (radiusChanged)
|
||||
{
|
||||
light.pointLightInnerRadius = (outerRadius < innerRadius) ? outerRadius : innerRadius;
|
||||
light.pointLightOuterRadius = (innerRadius > outerRadius) ? innerRadius : outerRadius;
|
||||
}
|
||||
}
|
||||
|
||||
void OnSceneGUI()
|
||||
{
|
||||
var light = target as Light2D;
|
||||
if (light == null)
|
||||
return;
|
||||
|
||||
Transform t = light.transform;
|
||||
switch (light.lightType)
|
||||
{
|
||||
case Light2D.LightType.Point:
|
||||
{
|
||||
Undo.RecordObject(light.transform, "Edit Point Light Transform");
|
||||
Undo.RecordObject(light, "Edit Point Light");
|
||||
|
||||
DrawRangeHandles(light);
|
||||
DrawAngleHandles(light);
|
||||
|
||||
if (GUI.changed)
|
||||
EditorUtility.SetDirty(light);
|
||||
}
|
||||
break;
|
||||
case Light2D.LightType.Sprite:
|
||||
{
|
||||
var cookieSprite = light.lightCookieSprite;
|
||||
if (cookieSprite != null)
|
||||
{
|
||||
Vector3 min = cookieSprite.bounds.min;
|
||||
Vector3 max = cookieSprite.bounds.max;
|
||||
|
||||
Vector3 v0 = t.TransformPoint(new Vector3(min.x, min.y));
|
||||
Vector3 v1 = t.TransformPoint(new Vector3(max.x, min.y));
|
||||
Vector3 v2 = t.TransformPoint(new Vector3(max.x, max.y));
|
||||
Vector3 v3 = t.TransformPoint(new Vector3(min.x, max.y));
|
||||
|
||||
Handles.DrawLine(v0, v1);
|
||||
Handles.DrawLine(v1, v2);
|
||||
Handles.DrawLine(v2, v3);
|
||||
Handles.DrawLine(v3, v0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Light2D.LightType.Freeform:
|
||||
{
|
||||
// Draw the falloff shape's outline
|
||||
List<Vector2> falloffShape = light.GetFalloffShape();
|
||||
Handles.color = Color.white;
|
||||
|
||||
for (int i = 0; i < falloffShape.Count - 1; ++i)
|
||||
{
|
||||
Handles.DrawLine(t.TransformPoint(falloffShape[i]), t.TransformPoint(falloffShape[i + 1]));
|
||||
}
|
||||
|
||||
Handles.DrawLine(t.TransformPoint(falloffShape[falloffShape.Count - 1]), t.TransformPoint(falloffShape[0]));
|
||||
|
||||
for (int i = 0; i < light.shapePath.Length - 1; ++i)
|
||||
{
|
||||
Handles.DrawLine(t.TransformPoint(light.shapePath[i]),
|
||||
t.TransformPoint(light.shapePath[i + 1]));
|
||||
}
|
||||
|
||||
Handles.DrawLine(t.TransformPoint(light.shapePath[light.shapePath.Length - 1]), t.TransformPoint(light.shapePath[0]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
var meshChanged = false;
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
UniversalRenderPipelineAsset asset = UniversalRenderPipeline.asset;
|
||||
if (asset != null)
|
||||
{
|
||||
if (!Light2DEditorUtility.IsUsing2DRenderer())
|
||||
{
|
||||
EditorGUILayout.HelpBox(Styles.asset2DUnassignedWarning);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_LightType.intValue != (int)Light2D.DeprecatedLightType.Parametric)
|
||||
meshChanged = DrawLightCommon();
|
||||
|
||||
switch (m_LightType.intValue)
|
||||
{
|
||||
case (int)Light2D.LightType.Point:
|
||||
{
|
||||
DrawSpotLight(serializedObject);
|
||||
}
|
||||
break;
|
||||
case (int)Light2D.LightType.Freeform:
|
||||
{
|
||||
DrawShapeLight(serializedObject);
|
||||
}
|
||||
break;
|
||||
case (int)Light2D.LightType.Sprite:
|
||||
{
|
||||
DrawSpriteLight(serializedObject);
|
||||
}
|
||||
break;
|
||||
case (int)Light2D.LightType.Global:
|
||||
{
|
||||
DrawGlobalLight(serializedObject);
|
||||
}
|
||||
break;
|
||||
case (int)Light2D.DeprecatedLightType.Parametric:
|
||||
{
|
||||
DrawParametricDeprecated(serializedObject);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
AnalyticsTrackChanges(serializedObject);
|
||||
if (serializedObject.ApplyModifiedProperties())
|
||||
{
|
||||
if (meshChanged)
|
||||
lightObject.UpdateMesh(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox(Styles.renderPipelineUnassignedWarning);
|
||||
|
||||
if (meshChanged)
|
||||
lightObject.UpdateMesh(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
internal static class Light2DEditorUtility
|
||||
{
|
||||
static Material s_TexCapMaterial = CoreUtils.CreateEngineMaterial(Shader.Find("Hidden/Internal-GUITexture"));
|
||||
|
||||
static internal void GUITextureCap(int controlID, Texture texture, Vector3 position, Quaternion rotation, float size, EventType eventType, bool isAngleHandle)
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case (EventType.Layout):
|
||||
{
|
||||
Vector2 size2 = Vector2.one * size * 0.5f;
|
||||
if (isAngleHandle)
|
||||
size2.x = 0.0f;
|
||||
|
||||
HandleUtility.AddControl(controlID, DistanceToRectangle(position, rotation, size2));
|
||||
break;
|
||||
}
|
||||
|
||||
case (EventType.Repaint):
|
||||
{
|
||||
s_TexCapMaterial.mainTexture = texture;
|
||||
s_TexCapMaterial.SetPass(0);
|
||||
|
||||
float w = texture.width;
|
||||
float h = texture.height;
|
||||
float max = Mathf.Max(w, h);
|
||||
Vector3 scale = new Vector2(w / max, h / max) * size * 0.5f;
|
||||
|
||||
if (Camera.current == null)
|
||||
scale.y *= -1f;
|
||||
|
||||
Matrix4x4 matrix = new Matrix4x4();
|
||||
matrix.SetTRS(position, rotation, scale);
|
||||
|
||||
Graphics.DrawMeshNow(RenderingUtils.fullscreenMesh, matrix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static float DistanceToRectangle(Vector3 position, Quaternion rotation, Vector2 size)
|
||||
{
|
||||
Vector3[] points = { Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero };
|
||||
Vector3 sideways = rotation * new Vector3(size.x, 0, 0);
|
||||
Vector3 up = rotation * new Vector3(0, size.y, 0);
|
||||
|
||||
points[0] = HandleUtility.WorldToGUIPoint(position + sideways + up);
|
||||
points[1] = HandleUtility.WorldToGUIPoint(position + sideways - up);
|
||||
points[2] = HandleUtility.WorldToGUIPoint(position - sideways - up);
|
||||
points[3] = HandleUtility.WorldToGUIPoint(position - sideways + up);
|
||||
points[4] = points[0];
|
||||
|
||||
Vector2 pos = Event.current.mousePosition;
|
||||
bool oddNodes = false;
|
||||
int j = 4;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
if ((points[i].y > pos.y) != (points[j].y > pos.y))
|
||||
{
|
||||
if (pos.x < (points[j].x - points[i].x) * (pos.y - points[i].y) / (points[j].y - points[i].y) + points[i].x)
|
||||
oddNodes = !oddNodes;
|
||||
}
|
||||
|
||||
j = i;
|
||||
}
|
||||
|
||||
if (!oddNodes)
|
||||
{
|
||||
// Distance to closest edge (not so fast)
|
||||
float dist, closestDist = -1f;
|
||||
j = 1;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
dist = HandleUtility.DistancePointToLineSegment(pos, points[i], points[j++]);
|
||||
if (dist < closestDist || closestDist < 0)
|
||||
closestDist = dist;
|
||||
}
|
||||
|
||||
return closestDist;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static Renderer2DData GetRenderer2DData()
|
||||
{
|
||||
UniversalRenderPipelineAsset pipelineAsset = UniversalRenderPipeline.asset;
|
||||
if (pipelineAsset == null)
|
||||
return null;
|
||||
|
||||
// try get the default
|
||||
Renderer2DData rendererData = pipelineAsset.scriptableRendererData as Renderer2DData;
|
||||
if (rendererData == null)
|
||||
{
|
||||
foreach (Camera camera in Camera.allCameras)
|
||||
{
|
||||
UniversalAdditionalCameraData additionalCameraData = camera.GetComponent<UniversalAdditionalCameraData>();
|
||||
ScriptableRenderer renderer = additionalCameraData?.scriptableRenderer;
|
||||
Renderer2D renderer2D = renderer as Renderer2D;
|
||||
if (renderer2D != null)
|
||||
return renderer2D.GetRenderer2DData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rendererData;
|
||||
}
|
||||
|
||||
public static bool IsUsing2DRenderer()
|
||||
{
|
||||
return GetRenderer2DData() != null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,280 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
[CustomEditor(typeof(PixelPerfectCamera))]
|
||||
class PixelPerfectCameraEditor : Editor
|
||||
{
|
||||
private class Style
|
||||
{
|
||||
public GUIContent x = new GUIContent("X");
|
||||
public GUIContent y = new GUIContent("Y");
|
||||
public GUIContent assetsPPU = new GUIContent("Assets Pixels Per Unit", "The amount of pixels that make up one unit of the Scene. Set this value to match the PPU value of Sprites in the Scene.");
|
||||
public GUIContent refRes = new GUIContent("Reference Resolution", "The original resolution the Assets are designed for.");
|
||||
public GUIContent upscaleRT = new GUIContent("Upscale Render Texture", "If enabled, the Scene is rendered as close as possible to the Reference Resolution while maintaining the screen aspect ratio, then upscaled to fit the full screen.");
|
||||
public GUIContent pixelSnapping = new GUIContent("Pixel Snapping", "If enabled, Sprite Renderers are snapped to a grid in world space at render-time. Grid size is based on the Assets Pixels Per Unit value. This does not affect GameObjects' Transform positions.");
|
||||
public GUIContent cropFrame = new GUIContent("Crop Frame", "Crops the viewport to match the Reference Resolution, along the checked axis. Black bars will be added to fit the screen aspect ratio.");
|
||||
public GUIContent stretchFill = new GUIContent("Stretch Fill", "If enabled, expands the viewport to fit the screen resolution while maintaining the viewport aspect ratio.");
|
||||
public GUIContent currentPixelRatio = new GUIContent("Current Pixel Ratio", "Ratio of the rendered Sprites compared to their original size.");
|
||||
public GUIContent runInEditMode = new GUIContent("Run In Edit Mode", "Enable this to preview Camera setting changes in Edit Mode. This will cause constant changes to the Scene while active.");
|
||||
public const string cameraStackingWarning = "Pixel Perfect Camera won't function properly if stacked with another camera.";
|
||||
|
||||
public GUIStyle centeredLabel;
|
||||
|
||||
public Style()
|
||||
{
|
||||
centeredLabel = new GUIStyle(EditorStyles.label);
|
||||
centeredLabel.alignment = TextAnchor.MiddleCenter;
|
||||
}
|
||||
}
|
||||
|
||||
private static Style m_Style;
|
||||
|
||||
private const float k_SingleLetterLabelWidth = 15.0f;
|
||||
private const float k_DottedLineSpacing = 2.5f;
|
||||
|
||||
private SerializedProperty m_AssetsPPU;
|
||||
private SerializedProperty m_RefResX;
|
||||
private SerializedProperty m_RefResY;
|
||||
private SerializedProperty m_UpscaleRT;
|
||||
private SerializedProperty m_PixelSnapping;
|
||||
private SerializedProperty m_CropFrameY;
|
||||
private SerializedProperty m_CropFrameX;
|
||||
private SerializedProperty m_StretchFill;
|
||||
|
||||
private Vector2 m_GameViewSize = Vector2.zero;
|
||||
private GUIContent m_CurrentPixelRatioValue;
|
||||
bool m_CameraStacking;
|
||||
|
||||
private void LazyInit()
|
||||
{
|
||||
if (m_Style == null)
|
||||
m_Style = new Style();
|
||||
|
||||
if (m_CurrentPixelRatioValue == null)
|
||||
m_CurrentPixelRatioValue = new GUIContent();
|
||||
}
|
||||
|
||||
void CheckForCameraStacking()
|
||||
{
|
||||
m_CameraStacking = false;
|
||||
|
||||
PixelPerfectCamera obj = target as PixelPerfectCamera;
|
||||
UniversalAdditionalCameraData cameraData = null;
|
||||
obj?.TryGetComponent(out cameraData);
|
||||
|
||||
if (cameraData == null)
|
||||
return;
|
||||
|
||||
if (cameraData.renderType == CameraRenderType.Base)
|
||||
{
|
||||
var cameraStack = cameraData.cameraStack;
|
||||
m_CameraStacking = cameraStack != null ? cameraStack.Count > 0 : false;
|
||||
}
|
||||
else if (cameraData.renderType == CameraRenderType.Overlay)
|
||||
m_CameraStacking = true;
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
m_AssetsPPU = serializedObject.FindProperty("m_AssetsPPU");
|
||||
m_RefResX = serializedObject.FindProperty("m_RefResolutionX");
|
||||
m_RefResY = serializedObject.FindProperty("m_RefResolutionY");
|
||||
m_UpscaleRT = serializedObject.FindProperty("m_UpscaleRT");
|
||||
m_PixelSnapping = serializedObject.FindProperty("m_PixelSnapping");
|
||||
m_CropFrameY = serializedObject.FindProperty("m_CropFrameY");
|
||||
m_CropFrameX = serializedObject.FindProperty("m_CropFrameX");
|
||||
m_StretchFill = serializedObject.FindProperty("m_StretchFill");
|
||||
}
|
||||
|
||||
public override bool RequiresConstantRepaint()
|
||||
{
|
||||
PixelPerfectCamera obj = target as PixelPerfectCamera;
|
||||
if (obj == null || !obj.enabled)
|
||||
return false;
|
||||
|
||||
// If game view size changes, we need to force a repaint of the inspector as the pixel ratio value may change accordingly.
|
||||
Vector2 gameViewSize = Handles.GetMainGameViewSize();
|
||||
if (gameViewSize != m_GameViewSize)
|
||||
{
|
||||
m_GameViewSize = gameViewSize;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
float originalLabelWidth = EditorGUIUtility.labelWidth;
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
if (Event.current.type == EventType.Layout)
|
||||
CheckForCameraStacking();
|
||||
|
||||
if (m_CameraStacking)
|
||||
EditorGUILayout.HelpBox(Style.cameraStackingWarning, MessageType.Warning);
|
||||
|
||||
EditorGUILayout.PropertyField(m_AssetsPPU, m_Style.assetsPPU);
|
||||
if (m_AssetsPPU.intValue <= 0)
|
||||
m_AssetsPPU.intValue = 1;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
{
|
||||
EditorGUILayout.PrefixLabel(m_Style.refRes);
|
||||
|
||||
EditorGUIUtility.labelWidth = k_SingleLetterLabelWidth * (EditorGUI.indentLevel + 1);
|
||||
|
||||
EditorGUILayout.PropertyField(m_RefResX, m_Style.x);
|
||||
if (m_RefResX.intValue <= 0)
|
||||
m_RefResX.intValue = 1;
|
||||
|
||||
EditorGUILayout.PropertyField(m_RefResY, m_Style.y);
|
||||
if (m_RefResY.intValue <= 0)
|
||||
m_RefResY.intValue = 1;
|
||||
|
||||
EditorGUIUtility.labelWidth = originalLabelWidth;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.PropertyField(m_UpscaleRT, m_Style.upscaleRT);
|
||||
if (!m_UpscaleRT.boolValue)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(m_PixelSnapping, m_Style.pixelSnapping);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
{
|
||||
EditorGUILayout.PrefixLabel(m_Style.cropFrame);
|
||||
|
||||
EditorGUIUtility.labelWidth = k_SingleLetterLabelWidth * (EditorGUI.indentLevel + 1);
|
||||
EditorGUILayout.PropertyField(m_CropFrameX, m_Style.x, GUILayout.MaxWidth(40.0f));
|
||||
EditorGUILayout.PropertyField(m_CropFrameY, m_Style.y);
|
||||
EditorGUIUtility.labelWidth = originalLabelWidth;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (m_CropFrameY.boolValue && m_CropFrameX.boolValue)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(m_StretchFill, m_Style.stretchFill);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
PixelPerfectCamera obj = target as PixelPerfectCamera;
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying || !obj.isActiveAndEnabled);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
bool runInEditMode = EditorGUILayout.Toggle(obj.runInEditMode, GUI.skin.button, GUILayout.Width(110.0f));
|
||||
GUI.Label(GUILayoutUtility.GetLastRect(), m_Style.runInEditMode, m_Style.centeredLabel);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
obj.runInEditMode = runInEditMode;
|
||||
|
||||
if (runInEditMode)
|
||||
obj.GetComponent<Camera>().Render();
|
||||
else
|
||||
obj.OnDisable();
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if (obj.isActiveAndEnabled && (EditorApplication.isPlaying || obj.runInEditMode))
|
||||
{
|
||||
if (Event.current.type == EventType.Layout)
|
||||
m_CurrentPixelRatioValue.text = string.Format("{0}:1", obj.pixelRatio);
|
||||
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.LabelField(m_Style.currentPixelRatio, m_CurrentPixelRatioValue);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnSceneGUI()
|
||||
{
|
||||
PixelPerfectCamera obj = target as PixelPerfectCamera;
|
||||
if (obj == null)
|
||||
return;
|
||||
|
||||
Camera camera = obj.GetComponent<Camera>();
|
||||
|
||||
// Show a green rect in scene view that represents the visible area when the pixel perfect correction takes effect in play mode.
|
||||
Vector2 gameViewSize = Handles.GetMainGameViewSize();
|
||||
int gameViewWidth = (int)gameViewSize.x;
|
||||
int gameViewHeight = (int)gameViewSize.y;
|
||||
int zoom = Math.Max(1, Math.Min(gameViewHeight / obj.refResolutionY, gameViewWidth / obj.refResolutionX));
|
||||
|
||||
float verticalOrthoSize;
|
||||
float horizontalOrthoSize;
|
||||
|
||||
if (obj.cropFrameY && obj.cropFrameX)
|
||||
{
|
||||
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
|
||||
horizontalOrthoSize = verticalOrthoSize * ((float)obj.refResolutionX / obj.refResolutionY);
|
||||
}
|
||||
else if (obj.cropFrameY)
|
||||
{
|
||||
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
|
||||
horizontalOrthoSize = verticalOrthoSize * ((float)gameViewWidth / (zoom * obj.refResolutionY));
|
||||
}
|
||||
else if (obj.cropFrameX)
|
||||
{
|
||||
horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
|
||||
verticalOrthoSize = horizontalOrthoSize / (zoom * obj.refResolutionX / (float)gameViewHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
verticalOrthoSize = gameViewHeight * 0.5f / (zoom * obj.assetsPPU);
|
||||
horizontalOrthoSize = verticalOrthoSize * camera.aspect;
|
||||
}
|
||||
|
||||
Handles.color = Color.green;
|
||||
|
||||
Vector3 cameraPosition = camera.transform.position;
|
||||
Vector3 p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
|
||||
Vector3 p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
|
||||
Handles.DrawLine(p1, p2);
|
||||
|
||||
p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
|
||||
Handles.DrawLine(p2, p1);
|
||||
|
||||
p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
|
||||
Handles.DrawLine(p1, p2);
|
||||
|
||||
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
|
||||
Handles.DrawLine(p2, p1);
|
||||
|
||||
// Show a green dotted rect in scene view that represents the area defined by the reference resolution.
|
||||
horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
|
||||
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
|
||||
|
||||
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
|
||||
p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
|
||||
Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
|
||||
|
||||
p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
|
||||
Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
|
||||
|
||||
p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
|
||||
Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
|
||||
|
||||
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
|
||||
Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Analytics;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Analytics
|
||||
{
|
||||
struct AnalyticsDataTypes
|
||||
{
|
||||
public const string k_LightDataString = "u2drendererlights";
|
||||
public const string k_Renderer2DDataString = "u2drendererdata";
|
||||
}
|
||||
|
||||
internal interface IAnalyticsData {};
|
||||
|
||||
[Serializable]
|
||||
internal struct Light2DData : IAnalyticsData
|
||||
{
|
||||
[SerializeField]
|
||||
public bool was_create_event;
|
||||
[SerializeField]
|
||||
public int instance_id;
|
||||
[SerializeField]
|
||||
public Light2D.LightType light_type;
|
||||
};
|
||||
|
||||
|
||||
[Serializable]
|
||||
internal struct RendererAssetData : IAnalyticsData
|
||||
{
|
||||
[SerializeField]
|
||||
public bool was_create_event;
|
||||
[SerializeField]
|
||||
public int instance_id;
|
||||
[SerializeField]
|
||||
public int blending_layers_count;
|
||||
[SerializeField]
|
||||
public int blending_modes_used;
|
||||
}
|
||||
|
||||
|
||||
interface IAnalytics
|
||||
{
|
||||
AnalyticsResult SendData(string eventString, IAnalyticsData data);
|
||||
}
|
||||
|
||||
[InitializeOnLoad]
|
||||
internal class Renderer2DAnalytics : IAnalytics
|
||||
{
|
||||
const int k_MaxEventsPerHour = 1000;
|
||||
const int k_MaxNumberOfElements = 1000;
|
||||
const string k_VendorKey = "unity.renderpipelines.universal.editor";
|
||||
const int k_Version = 1;
|
||||
static Renderer2DAnalytics m_Instance = new Renderer2DAnalytics();
|
||||
static bool s_Initialize = false;
|
||||
public static Renderer2DAnalytics instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Instance == null)
|
||||
m_Instance = new Renderer2DAnalytics();
|
||||
|
||||
return m_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public AnalyticsResult SendData(string eventString, IAnalyticsData data)
|
||||
{
|
||||
//Debug.Log("Sent Data " + JsonUtility.ToJson(data));
|
||||
if (false == s_Initialize)
|
||||
{
|
||||
EditorAnalytics.RegisterEventWithLimit(AnalyticsDataTypes.k_LightDataString, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey, k_Version);
|
||||
EditorAnalytics.RegisterEventWithLimit(AnalyticsDataTypes.k_Renderer2DDataString, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey, k_Version);
|
||||
s_Initialize = true;
|
||||
}
|
||||
|
||||
return EditorAnalytics.SendEventWithLimit(eventString, data, k_Version);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,275 @@
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEditor.Rendering.Universal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
[CustomEditor(typeof(Renderer2DData), true)]
|
||||
internal class Renderer2DDataEditor : Editor
|
||||
{
|
||||
class Styles
|
||||
{
|
||||
public static readonly GUIContent generalHeader = EditorGUIUtility.TrTextContent("General");
|
||||
public static readonly GUIContent lightRenderTexturesHeader = EditorGUIUtility.TrTextContent("Light Render Textures");
|
||||
public static readonly GUIContent lightBlendStylesHeader = EditorGUIUtility.TrTextContent("Light Blend Styles", "A Light Blend Style is a collection of properties that describe a particular way of applying lighting.");
|
||||
public static readonly GUIContent postProcessHeader = EditorGUIUtility.TrTextContent("Post-processing");
|
||||
|
||||
public static readonly GUIContent transparencySortMode = EditorGUIUtility.TrTextContent("Transparency Sort Mode", "Default sorting mode used for transparent objects");
|
||||
public static readonly GUIContent transparencySortAxis = EditorGUIUtility.TrTextContent("Transparency Sort Axis", "Axis used for custom axis sorting mode");
|
||||
public static readonly GUIContent hdrEmulationScale = EditorGUIUtility.TrTextContent("HDR Emulation Scale", "Describes the scaling used by lighting to remap dynamic range between LDR and HDR");
|
||||
public static readonly GUIContent lightRTScale = EditorGUIUtility.TrTextContent("Render Scale", "The resolution of intermediate light render textures, in relation to the screen resolution. 1.0 means full-screen size.");
|
||||
public static readonly GUIContent maxLightRTCount = EditorGUIUtility.TrTextContent("Max Light Render Textures", "How many intermediate light render textures can be created and utilized concurrently. Higher value usually leads to better performance on mobile hardware at the cost of more memory.");
|
||||
public static readonly GUIContent maxShadowRTCount = EditorGUIUtility.TrTextContent("Max Shadow Render Textures", "How many intermediate shadow render textures can be created and utilized concurrently. Higher value usually leads to better performance on mobile hardware at the cost of more memory.");
|
||||
public static readonly GUIContent defaultMaterialType = EditorGUIUtility.TrTextContent("Default Material Type", "Material to use when adding new objects to a scene");
|
||||
public static readonly GUIContent defaultCustomMaterial = EditorGUIUtility.TrTextContent("Default Custom Material", "Material to use when adding new objects to a scene");
|
||||
|
||||
public static readonly GUIContent name = EditorGUIUtility.TrTextContent("Name");
|
||||
public static readonly GUIContent maskTextureChannel = EditorGUIUtility.TrTextContent("Mask Texture Channel", "Which channel of the mask texture will affect this Light Blend Style.");
|
||||
public static readonly GUIContent blendMode = EditorGUIUtility.TrTextContent("Blend Mode", "How the lighting should be blended with the main color of the objects.");
|
||||
public static readonly GUIContent useDepthStencilBuffer = EditorGUIUtility.TrTextContent("Depth/Stencil Buffer", "Uncheck this when you are certain you don't use any feature that requires the depth/stencil buffer (e.g. Sprite Mask). Not using the depth/stencil buffer may improve performance, especially on mobile platforms.");
|
||||
public static readonly GUIContent postProcessIncluded = EditorGUIUtility.TrTextContent("Enabled", "Turns post-processing on (check box selected) or off (check box cleared). If you clear this check box, Unity excludes post-processing render Passes, shaders, and textures from the build.");
|
||||
public static readonly GUIContent postProcessData = EditorGUIUtility.TrTextContent("Data", "The asset containing references to shaders and Textures that the Renderer uses for post-processing.");
|
||||
|
||||
public static readonly GUIContent cameraSortingLayerTextureHeader = EditorGUIUtility.TrTextContent("Camera Sorting Layers Texture", "Layers from back most to selected bounds will be rendered to _CameraSortingLayersTexture");
|
||||
public static readonly GUIContent cameraSortingLayerTextureBound = EditorGUIUtility.TrTextContent("Bound", "Layers from back most to selected bounds will be rendered to _CameraSortingLayersTexture");
|
||||
public static readonly GUIContent cameraSortingLayerDownsampling = EditorGUIUtility.TrTextContent("Downsampling Method", "Method used to copy _CameraSortingLayersTexture");
|
||||
}
|
||||
|
||||
struct LightBlendStyleProps
|
||||
{
|
||||
public SerializedProperty name;
|
||||
public SerializedProperty maskTextureChannel;
|
||||
public SerializedProperty blendMode;
|
||||
public SerializedProperty blendFactorMultiplicative;
|
||||
public SerializedProperty blendFactorAdditive;
|
||||
}
|
||||
|
||||
SerializedProperty m_TransparencySortMode;
|
||||
SerializedProperty m_TransparencySortAxis;
|
||||
SerializedProperty m_HDREmulationScale;
|
||||
SerializedProperty m_LightRenderTextureScale;
|
||||
SerializedProperty m_LightBlendStyles;
|
||||
LightBlendStyleProps[] m_LightBlendStylePropsArray;
|
||||
SerializedProperty m_UseDepthStencilBuffer;
|
||||
SerializedProperty m_DefaultMaterialType;
|
||||
SerializedProperty m_DefaultCustomMaterial;
|
||||
SerializedProperty m_MaxLightRenderTextureCount;
|
||||
SerializedProperty m_MaxShadowRenderTextureCount;
|
||||
SerializedProperty m_PostProcessData;
|
||||
|
||||
SerializedProperty m_UseCameraSortingLayersTexture;
|
||||
SerializedProperty m_CameraSortingLayersTextureBound;
|
||||
SerializedProperty m_CameraSortingLayerDownsamplingMethod;
|
||||
|
||||
SavedBool m_GeneralFoldout;
|
||||
SavedBool m_LightRenderTexturesFoldout;
|
||||
SavedBool m_LightBlendStylesFoldout;
|
||||
SavedBool m_CameraSortingLayerTextureFoldout;
|
||||
SavedBool m_PostProcessingFoldout;
|
||||
|
||||
Analytics.Renderer2DAnalytics m_Analytics = Analytics.Renderer2DAnalytics.instance;
|
||||
Renderer2DData m_Renderer2DData;
|
||||
bool m_WasModified;
|
||||
|
||||
void SendModifiedAnalytics(Analytics.IAnalytics analytics)
|
||||
{
|
||||
if (m_WasModified)
|
||||
{
|
||||
Analytics.RendererAssetData modifiedData = new Analytics.RendererAssetData();
|
||||
modifiedData.instance_id = m_Renderer2DData.GetInstanceID();
|
||||
modifiedData.was_create_event = false;
|
||||
modifiedData.blending_layers_count = 0;
|
||||
modifiedData.blending_modes_used = 0;
|
||||
analytics.SendData(Analytics.AnalyticsDataTypes.k_Renderer2DDataString, modifiedData);
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_WasModified = false;
|
||||
m_Renderer2DData = (Renderer2DData)serializedObject.targetObject;
|
||||
|
||||
m_TransparencySortMode = serializedObject.FindProperty("m_TransparencySortMode");
|
||||
m_TransparencySortAxis = serializedObject.FindProperty("m_TransparencySortAxis");
|
||||
m_HDREmulationScale = serializedObject.FindProperty("m_HDREmulationScale");
|
||||
m_LightRenderTextureScale = serializedObject.FindProperty("m_LightRenderTextureScale");
|
||||
m_LightBlendStyles = serializedObject.FindProperty("m_LightBlendStyles");
|
||||
m_MaxLightRenderTextureCount = serializedObject.FindProperty("m_MaxLightRenderTextureCount");
|
||||
m_MaxShadowRenderTextureCount = serializedObject.FindProperty("m_MaxShadowRenderTextureCount");
|
||||
m_PostProcessData = serializedObject.FindProperty("m_PostProcessData");
|
||||
|
||||
m_CameraSortingLayersTextureBound = serializedObject.FindProperty("m_CameraSortingLayersTextureBound");
|
||||
m_UseCameraSortingLayersTexture = serializedObject.FindProperty("m_UseCameraSortingLayersTexture");
|
||||
m_CameraSortingLayerDownsamplingMethod = serializedObject.FindProperty("m_CameraSortingLayerDownsamplingMethod");
|
||||
|
||||
int numBlendStyles = m_LightBlendStyles.arraySize;
|
||||
m_LightBlendStylePropsArray = new LightBlendStyleProps[numBlendStyles];
|
||||
|
||||
for (int i = 0; i < numBlendStyles; ++i)
|
||||
{
|
||||
SerializedProperty blendStyleProp = m_LightBlendStyles.GetArrayElementAtIndex(i);
|
||||
ref LightBlendStyleProps props = ref m_LightBlendStylePropsArray[i];
|
||||
|
||||
props.name = blendStyleProp.FindPropertyRelative("name");
|
||||
props.maskTextureChannel = blendStyleProp.FindPropertyRelative("maskTextureChannel");
|
||||
props.blendMode = blendStyleProp.FindPropertyRelative("blendMode");
|
||||
props.blendFactorMultiplicative = blendStyleProp.FindPropertyRelative("customBlendFactors.multiplicative");
|
||||
props.blendFactorAdditive = blendStyleProp.FindPropertyRelative("customBlendFactors.additive");
|
||||
|
||||
if (props.blendFactorMultiplicative == null)
|
||||
props.blendFactorMultiplicative = blendStyleProp.FindPropertyRelative("customBlendFactors.modulate");
|
||||
if (props.blendFactorAdditive == null)
|
||||
props.blendFactorAdditive = blendStyleProp.FindPropertyRelative("customBlendFactors.additve");
|
||||
}
|
||||
|
||||
m_UseDepthStencilBuffer = serializedObject.FindProperty("m_UseDepthStencilBuffer");
|
||||
m_DefaultMaterialType = serializedObject.FindProperty("m_DefaultMaterialType");
|
||||
m_DefaultCustomMaterial = serializedObject.FindProperty("m_DefaultCustomMaterial");
|
||||
|
||||
m_GeneralFoldout = new SavedBool($"{target.GetType()}.GeneralFoldout", true);
|
||||
m_LightRenderTexturesFoldout = new SavedBool($"{target.GetType()}.LightRenderTexturesFoldout", true);
|
||||
m_LightBlendStylesFoldout = new SavedBool($"{target.GetType()}.LightBlendStylesFoldout", true);
|
||||
m_CameraSortingLayerTextureFoldout = new SavedBool($"{target.GetType()}.CameraSortingLayerTextureFoldout", true);
|
||||
m_PostProcessingFoldout = new SavedBool($"{target.GetType()}.PostProcessingFoldout", true);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
SendModifiedAnalytics(m_Analytics);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
DrawGeneral();
|
||||
DrawLightRenderTextures();
|
||||
DrawLightBlendStyles();
|
||||
DrawCameraSortingLayerTexture();
|
||||
DrawPostProcessing();
|
||||
|
||||
m_WasModified |= serializedObject.hasModifiedProperties;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public void DrawCameraSortingLayerTexture()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter();
|
||||
m_CameraSortingLayerTextureFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.cameraSortingLayerTextureHeader, m_CameraSortingLayerTextureFoldout.value);
|
||||
if (!m_CameraSortingLayerTextureFoldout.value)
|
||||
return;
|
||||
|
||||
SortingLayer[] sortingLayers = SortingLayer.layers;
|
||||
string[] optionNames = new string[sortingLayers.Length + 1];
|
||||
int[] optionIds = new int[sortingLayers.Length + 1];
|
||||
optionNames[0] = "Disabled";
|
||||
optionIds[0] = -1;
|
||||
|
||||
int currentOptionIndex = 0;
|
||||
for (int i = 0; i < sortingLayers.Length; i++)
|
||||
{
|
||||
optionNames[i + 1] = sortingLayers[i].name;
|
||||
optionIds[i + 1] = sortingLayers[i].id;
|
||||
if (sortingLayers[i].id == m_CameraSortingLayersTextureBound.intValue)
|
||||
currentOptionIndex = i + 1;
|
||||
}
|
||||
|
||||
|
||||
int selectedOptionIndex = !m_UseCameraSortingLayersTexture.boolValue ? 0 : currentOptionIndex;
|
||||
selectedOptionIndex = EditorGUILayout.Popup(Styles.cameraSortingLayerTextureBound, selectedOptionIndex, optionNames);
|
||||
|
||||
m_UseCameraSortingLayersTexture.boolValue = selectedOptionIndex != 0;
|
||||
m_CameraSortingLayersTextureBound.intValue = optionIds[selectedOptionIndex];
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!m_UseCameraSortingLayersTexture.boolValue);
|
||||
EditorGUILayout.PropertyField(m_CameraSortingLayerDownsamplingMethod, Styles.cameraSortingLayerDownsampling);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private void DrawGeneral()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter();
|
||||
m_GeneralFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.generalHeader, m_GeneralFoldout.value);
|
||||
if (!m_GeneralFoldout.value)
|
||||
return;
|
||||
|
||||
EditorGUILayout.PropertyField(m_TransparencySortMode, Styles.transparencySortMode);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(m_TransparencySortMode.intValue != (int)TransparencySortMode.CustomAxis))
|
||||
EditorGUILayout.PropertyField(m_TransparencySortAxis, Styles.transparencySortAxis);
|
||||
|
||||
EditorGUILayout.PropertyField(m_DefaultMaterialType, Styles.defaultMaterialType);
|
||||
if (m_DefaultMaterialType.intValue == (int)Renderer2DData.Renderer2DDefaultMaterialType.Custom)
|
||||
EditorGUILayout.PropertyField(m_DefaultCustomMaterial, Styles.defaultCustomMaterial);
|
||||
|
||||
EditorGUILayout.PropertyField(m_UseDepthStencilBuffer, Styles.useDepthStencilBuffer);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(m_HDREmulationScale, Styles.hdrEmulationScale);
|
||||
if (EditorGUI.EndChangeCheck() && m_HDREmulationScale.floatValue < 1.0f)
|
||||
m_HDREmulationScale.floatValue = 1.0f;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
private void DrawLightRenderTextures()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter();
|
||||
m_LightRenderTexturesFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.lightRenderTexturesHeader, m_LightRenderTexturesFoldout.value);
|
||||
if (!m_LightRenderTexturesFoldout.value)
|
||||
return;
|
||||
|
||||
EditorGUILayout.PropertyField(m_LightRenderTextureScale, Styles.lightRTScale);
|
||||
EditorGUILayout.PropertyField(m_MaxLightRenderTextureCount, Styles.maxLightRTCount);
|
||||
EditorGUILayout.PropertyField(m_MaxShadowRenderTextureCount, Styles.maxShadowRTCount);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
private void DrawLightBlendStyles()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter();
|
||||
m_LightBlendStylesFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.lightBlendStylesHeader, m_LightBlendStylesFoldout.value);
|
||||
if (!m_LightBlendStylesFoldout.value)
|
||||
return;
|
||||
|
||||
int numBlendStyles = m_LightBlendStyles.arraySize;
|
||||
for (int i = 0; i < numBlendStyles; ++i)
|
||||
{
|
||||
ref LightBlendStyleProps props = ref m_LightBlendStylePropsArray[i];
|
||||
|
||||
EditorGUILayout.PropertyField(props.name, Styles.name);
|
||||
EditorGUILayout.PropertyField(props.maskTextureChannel, Styles.maskTextureChannel);
|
||||
EditorGUILayout.PropertyField(props.blendMode, Styles.blendMode);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
private void DrawPostProcessing()
|
||||
{
|
||||
CoreEditorUtils.DrawSplitter();
|
||||
m_PostProcessingFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.postProcessHeader, m_PostProcessingFoldout.value);
|
||||
if (!m_PostProcessingFoldout.value)
|
||||
return;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var postProcessIncluded = EditorGUILayout.Toggle(Styles.postProcessIncluded, m_PostProcessData.objectReferenceValue != null);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
m_PostProcessData.objectReferenceValue = postProcessIncluded ? UnityEngine.Rendering.Universal.PostProcessData.GetDefaultPostProcessData() : null;
|
||||
}
|
||||
|
||||
// this field is no longer hidden by the checkbox. It is bad UX to begin with
|
||||
// also, if the field is hidden, the user could still use Asset Selector to set the value, but it won't stick
|
||||
// making it look like a bug(1307128)
|
||||
EditorGUILayout.PropertyField(m_PostProcessData, Styles.postProcessData);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEditor.ProjectWindowCallback;
|
||||
using UnityEditorInternal;
|
||||
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
static class Renderer2DMenus
|
||||
{
|
||||
const int k_MenuPriority = 50;
|
||||
|
||||
static void Create2DRendererData(Action<Renderer2DData> onCreatedCallback)
|
||||
{
|
||||
var instance = ScriptableObject.CreateInstance<Create2DRendererDataAsset>();
|
||||
instance.onCreated += onCreatedCallback;
|
||||
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, instance, "New 2D Renderer Data.asset", null, null);
|
||||
}
|
||||
|
||||
class Create2DRendererDataAsset : EndNameEditAction
|
||||
{
|
||||
public event Action<Renderer2DData> onCreated;
|
||||
|
||||
public override void Action(int instanceId, string pathName, string resourceFile)
|
||||
{
|
||||
var instance = CreateInstance<Renderer2DData>();
|
||||
instance.postProcessData = PostProcessData.GetDefaultPostProcessData();
|
||||
AssetDatabase.CreateAsset(instance, pathName);
|
||||
Selection.activeObject = instance;
|
||||
|
||||
onCreated?.Invoke(instance);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void PlaceGameObjectInFrontOfSceneView(GameObject go)
|
||||
{
|
||||
var sceneViews = SceneView.sceneViews;
|
||||
if (sceneViews.Count >= 1)
|
||||
{
|
||||
SceneView view = SceneView.lastActiveSceneView;
|
||||
if (!view)
|
||||
view = sceneViews[0] as SceneView;
|
||||
|
||||
if (view)
|
||||
view.MoveToView(go.transform);
|
||||
}
|
||||
}
|
||||
|
||||
// This is from GOCreationCommands
|
||||
internal static void Place(GameObject go, GameObject parent)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
var transform = go.transform;
|
||||
Undo.SetTransformParent(transform, parent.transform, "Reparenting");
|
||||
transform.localPosition = Vector3.zero;
|
||||
transform.localRotation = Quaternion.identity;
|
||||
transform.localScale = Vector3.one;
|
||||
go.layer = parent.layer;
|
||||
|
||||
if (parent.GetComponent<RectTransform>())
|
||||
ObjectFactory.AddComponent<RectTransform>(go);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaceGameObjectInFrontOfSceneView(go);
|
||||
StageUtility.PlaceGameObjectInCurrentStage(go); // may change parent
|
||||
go.transform.position = new Vector3(go.transform.position.x, go.transform.position.y, 0);
|
||||
}
|
||||
|
||||
// Only at this point do we know the actual parent of the object and can modify its name accordingly.
|
||||
GameObjectUtility.EnsureUniqueNameForSibling(go);
|
||||
Undo.SetCurrentGroupName("Create " + go.name);
|
||||
|
||||
//EditorWindow.FocusWindowIfItsOpen<SceneHierarchyWindow>();
|
||||
Selection.activeGameObject = go;
|
||||
}
|
||||
|
||||
static void CreateLight(MenuCommand menuCommand, Light2D.LightType type, Vector3[] shapePath = null)
|
||||
{
|
||||
GameObject go = ObjectFactory.CreateGameObject("Light 2D", typeof(Light2D));
|
||||
Light2D light2D = go.GetComponent<Light2D>();
|
||||
light2D.lightType = type;
|
||||
|
||||
if (shapePath != null && shapePath.Length > 0)
|
||||
light2D.shapePath = shapePath;
|
||||
|
||||
var parent = menuCommand.context as GameObject;
|
||||
Place(go, parent);
|
||||
|
||||
Analytics.Light2DData lightData = new Analytics.Light2DData();
|
||||
lightData.was_create_event = true;
|
||||
lightData.instance_id = light2D.GetInstanceID();
|
||||
lightData.light_type = light2D.lightType;
|
||||
Analytics.Renderer2DAnalytics.instance.SendData(Analytics.AnalyticsDataTypes.k_LightDataString, lightData);
|
||||
}
|
||||
|
||||
static bool CreateLightValidation()
|
||||
{
|
||||
return Light2DEditorUtility.IsUsing2DRenderer();
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Square", false, k_MenuPriority)]
|
||||
static void CreateSquareFreeformLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateSquare());
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Circle", false, k_MenuPriority)]
|
||||
static void CreateCircleFreeformLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateCircle());
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Isometric Diamond", false, k_MenuPriority)]
|
||||
static void CreateIsometricDiamondFreeformLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateIsometricDiamond());
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Flat Top", false, k_MenuPriority)]
|
||||
static void CreateHexagonFlatTopFreeformLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateHexagonFlatTop());
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Pointed Top", false, k_MenuPriority)]
|
||||
static void CreateHexagonPointedTopFreeformLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateHexagonPointedTop());
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Sprite Light 2D", false, k_MenuPriority)]
|
||||
static void CreateSpriteLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Sprite);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Spot Light 2D", false, k_MenuPriority)]
|
||||
static void CreatePointLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Point);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Global Light 2D", false, k_MenuPriority)]
|
||||
static void CreateGlobalLight2D(MenuCommand menuCommand)
|
||||
{
|
||||
CreateLight(menuCommand, Light2D.LightType.Global);
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Isometric Diamond", true, k_MenuPriority)]
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Square", true, k_MenuPriority)]
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Circle", true, k_MenuPriority)]
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Flat Top", true, k_MenuPriority)]
|
||||
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Pointed Top", true, k_MenuPriority)]
|
||||
[MenuItem("GameObject/Light/Sprite Light 2D", true, k_MenuPriority)]
|
||||
[MenuItem("GameObject/Light/Spot Light 2D", true, k_MenuPriority)]
|
||||
[MenuItem("GameObject/Light/Global Light 2D", true, k_MenuPriority)]
|
||||
static bool CreateLight2DValidation()
|
||||
{
|
||||
return CreateLightValidation();
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812")]
|
||||
internal class CreateUniversalPipelineAsset : EndNameEditAction
|
||||
{
|
||||
public override void Action(int instanceId, string pathName, string resourceFile)
|
||||
{
|
||||
//Create asset
|
||||
AssetDatabase.CreateAsset(UniversalRenderPipelineAsset.Create(UniversalRenderPipelineAsset.CreateRendererAsset(pathName, RendererType._2DRenderer)), pathName);
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/Rendering/Universal Render Pipeline/Pipeline Asset (2D Renderer)", priority = CoreUtils.assetCreateMenuPriority1 + 1)]
|
||||
static void CreateUniversalPipeline()
|
||||
{
|
||||
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, UniversalRenderPipelineAsset.CreateInstance<CreateUniversalPipelineAsset>(),
|
||||
"UniversalRenderPipelineAsset.asset", null, null);
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/Rendering/Universal Render Pipeline/2D Renderer", priority = CoreUtils.assetCreateMenuPriority2 + 1)]
|
||||
static void Create2DRendererData()
|
||||
{
|
||||
Renderer2DMenus.Create2DRendererData((instance) =>
|
||||
{
|
||||
Analytics.RendererAssetData modifiedData = new Analytics.RendererAssetData();
|
||||
modifiedData.instance_id = instance.GetInstanceID();
|
||||
modifiedData.was_create_event = true;
|
||||
modifiedData.blending_layers_count = 1;
|
||||
modifiedData.blending_modes_used = 2;
|
||||
Analytics.Renderer2DAnalytics.instance.SendData(Analytics.AnalyticsDataTypes.k_Renderer2DDataString, modifiedData);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,215 @@
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
internal static class Renderer2DUpgrader
|
||||
{
|
||||
delegate void Upgrader<T>(T toUpgrade) where T : Object;
|
||||
|
||||
static void ProcessAssetDatabaseObjects<T>(string searchString, Upgrader<T> upgrader) where T : Object
|
||||
{
|
||||
string[] prefabNames = AssetDatabase.FindAssets(searchString);
|
||||
foreach (string prefabName in prefabNames)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(prefabName);
|
||||
if (path.StartsWith("Assets"))
|
||||
{
|
||||
T obj = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
if (obj != null)
|
||||
{
|
||||
upgrader(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpgradeObjectWithParametricLights(GameObject obj)
|
||||
{
|
||||
Light2D[] lights = obj.GetComponents<Light2D>();
|
||||
if (lights.Length > 0)
|
||||
{
|
||||
foreach (var light in lights)
|
||||
UpgradeParametricLight(light);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpgradeParametricLight(Light2D light)
|
||||
{
|
||||
if (light.lightType == (Light2D.LightType)Light2D.DeprecatedLightType.Parametric)
|
||||
{
|
||||
light.lightType = Light2D.LightType.Freeform;
|
||||
|
||||
float radius = light.shapeLightParametricRadius;
|
||||
float angle = light.shapeLightParametricAngleOffset;
|
||||
int sides = light.shapeLightParametricSides;
|
||||
|
||||
var angleOffset = Mathf.PI / 2.0f + Mathf.Deg2Rad * angle;
|
||||
if (sides < 3)
|
||||
{
|
||||
radius = 0.70710678118654752440084436210485f * radius;
|
||||
sides = 4;
|
||||
}
|
||||
|
||||
if (sides == 4)
|
||||
{
|
||||
angleOffset = Mathf.PI / 4.0f + Mathf.Deg2Rad * angle;
|
||||
}
|
||||
|
||||
var radiansPerSide = 2 * Mathf.PI / sides;
|
||||
var min = new Vector3(float.MaxValue, float.MaxValue, 0);
|
||||
var max = new Vector3(float.MinValue, float.MinValue, 0);
|
||||
|
||||
|
||||
Vector3[] shapePath = new Vector3[sides];
|
||||
for (var i = 0; i < sides; i++)
|
||||
{
|
||||
var endAngle = (i + 1) * radiansPerSide;
|
||||
var extrudeDir = new Vector3(Mathf.Cos(endAngle + angleOffset), Mathf.Sin(endAngle + angleOffset), 0);
|
||||
var endPoint = radius * extrudeDir;
|
||||
|
||||
shapePath[i] = endPoint;
|
||||
}
|
||||
|
||||
light.shapePath = shapePath;
|
||||
light.UpdateMesh(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpgradeGameObject(GameObject go)
|
||||
{
|
||||
Renderer[] spriteRenderers = go.GetComponentsInChildren<Renderer>(true);
|
||||
Renderer2DData data = Light2DEditorUtility.GetRenderer2DData();
|
||||
if (data != null)
|
||||
{
|
||||
Material defaultMat = data.GetDefaultMaterial(DefaultMaterialType.Sprite);
|
||||
|
||||
bool upgraded = false;
|
||||
foreach (Renderer renderer in spriteRenderers)
|
||||
{
|
||||
int materialCount = renderer.sharedMaterials.Length;
|
||||
Material[] newMaterials = new Material[materialCount];
|
||||
|
||||
for (int i = 0; i < materialCount; i++)
|
||||
{
|
||||
Material mat = renderer.sharedMaterials[i];
|
||||
|
||||
if (mat != null && mat.shader.name == "Sprites/Default")
|
||||
{
|
||||
newMaterials[i] = defaultMat;
|
||||
upgraded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
newMaterials[i] = renderer.sharedMaterials[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (upgraded)
|
||||
renderer.sharedMaterials = newMaterials;
|
||||
}
|
||||
|
||||
if (upgraded)
|
||||
{
|
||||
Debug.Log(go.name + " was upgraded.", go);
|
||||
EditorSceneManager.MarkSceneDirty(go.scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UpgradeMaterial(Material mat)
|
||||
{
|
||||
Renderer2DData data = Light2DEditorUtility.GetRenderer2DData();
|
||||
if (data != null)
|
||||
{
|
||||
Material defaultMat = data.GetDefaultMaterial(DefaultMaterialType.Sprite);
|
||||
|
||||
if (mat.shader.name == "Sprites/Default")
|
||||
{
|
||||
mat.shader = defaultMat.shader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Project Materials to 2D Renderer Materials", false)]
|
||||
static void UpgradeProjectTo2DRenderer()
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("2D Renderer Upgrader", "The upgrade will search for all prefabs in your project that use Sprite Renderers and change the material references of those Sprite Renderers to a lit material. You can't undo this operation. It's highly recommended to backup your project before proceeding.", "Proceed", "Cancel"))
|
||||
return;
|
||||
|
||||
ProcessAssetDatabaseObjects<GameObject>("t: Prefab", UpgradeGameObject);
|
||||
AssetDatabase.SaveAssets();
|
||||
Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Scene Materials to 2D Renderer Materials", false)]
|
||||
static void UpgradeSceneTo2DRenderer()
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("2D Renderer Upgrader", "The upgrade will change the material references of Sprite Renderers in currently open scene(s) to a lit material. You can't undo this operation. Make sure you save the scene(s) before proceeding.", "Proceed", "Cancel"))
|
||||
return;
|
||||
|
||||
GameObject[] gameObjects = Object.FindObjectsOfType<GameObject>();
|
||||
if (gameObjects != null && gameObjects.Length > 0)
|
||||
{
|
||||
foreach (GameObject go in gameObjects)
|
||||
{
|
||||
UpgradeGameObject(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Project Materials to 2D Renderer Materials", true)]
|
||||
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Scene Materials to 2D Renderer Materials", true)]
|
||||
static bool MenuValidation()
|
||||
{
|
||||
return Light2DEditorUtility.IsUsing2DRenderer();
|
||||
}
|
||||
|
||||
public static void UpgradeParametricLightsInScene(bool prompt)
|
||||
{
|
||||
if (prompt)
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("Parametric Light Upgrader", "The upgrade will change all game objects which use Parametric Light2D to Freeform Light2D in currently open scene(s). You can't undo this operation. Make sure you save the scene(s) before proceeding.", "Proceed", "Cancel"))
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject[] gameObjects = Object.FindObjectsOfType<GameObject>();
|
||||
if (gameObjects != null && gameObjects.Length > 0)
|
||||
{
|
||||
foreach (GameObject go in gameObjects)
|
||||
{
|
||||
UpgradeObjectWithParametricLights(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpgradeParametricLightsInProject(bool prompt = true)
|
||||
{
|
||||
if (prompt)
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("Parametric Light Upgrader", "The upgrade will search for all prefabs in your project that use Parametric Light2D and change them to Freeform Light2D. You can't undo this operation. It's highly recommended to backup your project before proceeding.", "Proceed", "Cancel"))
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessAssetDatabaseObjects<GameObject>("t: Prefab", UpgradeObjectWithParametricLights);
|
||||
AssetDatabase.SaveAssets();
|
||||
Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
// Set priority to get these to the bottom. Add test for pipeline enabled
|
||||
|
||||
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Project Parametric Lights to Freeform", false)]
|
||||
public static void UpgradeParametricLightsInProject()
|
||||
{
|
||||
UpgradeParametricLightsInProject(true);
|
||||
}
|
||||
|
||||
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Scene Parametric Lights to Freeform", false)]
|
||||
public static void UpgradeParametricLightsInScene()
|
||||
{
|
||||
UpgradeParametricLightsInScene(true);
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 801 B |
After Width: | Height: | Size: 844 B |
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 722 B |
After Width: | Height: | Size: 777 B |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,147 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
internal class ShadowCasterPath : ScriptablePath
|
||||
{
|
||||
internal Bounds GetBounds()
|
||||
{
|
||||
ShadowCaster2D shadowCaster = (ShadowCaster2D)owner;
|
||||
Renderer m_Renderer = shadowCaster.GetComponent<Renderer>();
|
||||
if (m_Renderer != null)
|
||||
{
|
||||
return m_Renderer.bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collider2D collider = shadowCaster.GetComponent<Collider2D>();
|
||||
if (collider != null)
|
||||
return collider.bounds;
|
||||
}
|
||||
|
||||
return new Bounds(shadowCaster.transform.position, shadowCaster.transform.lossyScale);
|
||||
}
|
||||
|
||||
public override void SetDefaultShape()
|
||||
{
|
||||
Clear();
|
||||
Bounds bounds = GetBounds();
|
||||
|
||||
AddPoint(new ControlPoint(bounds.min));
|
||||
AddPoint(new ControlPoint(new Vector3(bounds.min.x, bounds.max.y)));
|
||||
AddPoint(new ControlPoint(bounds.max));
|
||||
AddPoint(new ControlPoint(new Vector3(bounds.max.x, bounds.min.y)));
|
||||
|
||||
base.SetDefaultShape();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[CustomEditor(typeof(ShadowCaster2D))]
|
||||
[CanEditMultipleObjects]
|
||||
internal class ShadowCaster2DEditor : PathComponentEditor<ShadowCasterPath>
|
||||
{
|
||||
[EditorTool("Edit Shadow Caster Shape", typeof(ShadowCaster2D))]
|
||||
class ShadowCaster2DShadowCasterShapeTool : ShadowCaster2DShapeTool {};
|
||||
|
||||
private static class Styles
|
||||
{
|
||||
public static GUIContent shadowMode = EditorGUIUtility.TrTextContent("Use Renderer Silhouette", "When this and Self Shadows are enabled, the Renderer's silhouette is considered part of the shadow. When this is enabled and Self Shadows disabled, the Renderer's silhouette is excluded from the shadow.");
|
||||
public static GUIContent selfShadows = EditorGUIUtility.TrTextContent("Self Shadows", "When enabled, the Renderer casts shadows on itself.");
|
||||
public static GUIContent castsShadows = EditorGUIUtility.TrTextContent("Casts Shadows", "Specifies if this renderer will cast shadows");
|
||||
public static GUIContent sortingLayerPrefixLabel = EditorGUIUtility.TrTextContent("Target Sorting Layers", "Apply shadows to the specified sorting layers.");
|
||||
}
|
||||
|
||||
SerializedProperty m_UseRendererSilhouette;
|
||||
SerializedProperty m_CastsShadows;
|
||||
SerializedProperty m_SelfShadows;
|
||||
SerializedProperty m_ReceivesShadows;
|
||||
|
||||
|
||||
SortingLayerDropDown m_SortingLayerDropDown;
|
||||
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
m_UseRendererSilhouette = serializedObject.FindProperty("m_UseRendererSilhouette");
|
||||
m_SelfShadows = serializedObject.FindProperty("m_SelfShadows");
|
||||
m_CastsShadows = serializedObject.FindProperty("m_CastsShadows");
|
||||
|
||||
m_SortingLayerDropDown = new SortingLayerDropDown();
|
||||
m_SortingLayerDropDown.OnEnable(serializedObject, "m_ApplyToSortingLayers");
|
||||
}
|
||||
|
||||
public void ShadowCaster2DSceneGUI()
|
||||
{
|
||||
ShadowCaster2D shadowCaster = target as ShadowCaster2D;
|
||||
|
||||
Transform t = shadowCaster.transform;
|
||||
Vector3[] shape = shadowCaster.shapePath;
|
||||
Handles.color = Color.white;
|
||||
|
||||
for (int i = 0; i < shape.Length - 1; ++i)
|
||||
{
|
||||
Handles.DrawAAPolyLine(4, new Vector3[] { t.TransformPoint(shape[i]), t.TransformPoint(shape[i + 1]) });
|
||||
}
|
||||
|
||||
if (shape.Length > 1)
|
||||
Handles.DrawAAPolyLine(4, new Vector3[] { t.TransformPoint(shape[shape.Length - 1]), t.TransformPoint(shape[0]) });
|
||||
}
|
||||
|
||||
public void ShadowCaster2DInspectorGUI<T>() where T : ShadowCaster2DShapeTool
|
||||
{
|
||||
DoEditButton<T>(PathEditorToolContents.icon, "Edit Shape");
|
||||
DoPathInspector<T>();
|
||||
DoSnappingInspector<T>();
|
||||
}
|
||||
|
||||
public void OnSceneGUI()
|
||||
{
|
||||
if (m_CastsShadows.boolValue)
|
||||
ShadowCaster2DSceneGUI();
|
||||
}
|
||||
|
||||
public bool HasRenderer()
|
||||
{
|
||||
if (targets != null)
|
||||
{
|
||||
for (int i = 0; i < targets.Length; i++)
|
||||
{
|
||||
ShadowCaster2D shadowCaster = (ShadowCaster2D)targets[i];
|
||||
Renderer renderer = shadowCaster.GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
using (new EditorGUI.DisabledScope(!HasRenderer())) // Done to support multiedit
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_UseRendererSilhouette, Styles.shadowMode);
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(m_CastsShadows, Styles.castsShadows);
|
||||
EditorGUILayout.PropertyField(m_SelfShadows, Styles.selfShadows);
|
||||
|
||||
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.sortingLayerPrefixLabel, null);
|
||||
|
||||
if (m_CastsShadows.boolValue)
|
||||
ShadowCaster2DInspectorGUI<ShadowCaster2DShadowCasterShapeTool>();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
class ShadowCaster2DShapeTool : PathEditorTool<ShadowCasterPath>
|
||||
{
|
||||
const string k_ShapePath = "m_ShapePath";
|
||||
|
||||
protected override IShape GetShape(Object target)
|
||||
{
|
||||
return (target as ShadowCaster2D).shapePath.ToPolygon(false);
|
||||
}
|
||||
|
||||
protected override void SetShape(ShadowCasterPath shapeEditor, SerializedObject serializedObject)
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
var pointsProperty = serializedObject.FindProperty(k_ShapePath);
|
||||
pointsProperty.arraySize = shapeEditor.pointCount;
|
||||
|
||||
for (var i = 0; i < shapeEditor.pointCount; ++i)
|
||||
pointsProperty.GetArrayElementAtIndex(i).vector3Value = shapeEditor.GetPoint(i).position;
|
||||
|
||||
// This is untracked right now...
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
ShadowCaster2D shadowCaster = target as ShadowCaster2D;
|
||||
if (shadowCaster != null)
|
||||
{
|
||||
int hash = LightUtility.GetShapePathHash(shadowCaster.shapePath);
|
||||
shadowCaster.shapePathHash = hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,223 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal static class BezierUtility
|
||||
{
|
||||
static Vector3[] s_TempPoints = new Vector3[3];
|
||||
|
||||
public static Vector3 BezierPoint(Vector3 startPosition, Vector3 startTangent, Vector3 endTangent, Vector3 endPosition, float t)
|
||||
{
|
||||
float s = 1.0f - t;
|
||||
return startPosition * s * s * s + startTangent * s * s * t * 3.0f + endTangent * s * t * t * 3.0f + endPosition * t * t * t;
|
||||
}
|
||||
|
||||
public static Vector3 ClosestPointOnCurve(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, out float t)
|
||||
{
|
||||
Vector3 startToEnd = endPosition - startPosition;
|
||||
Vector3 startToTangent = (startTangent - startPosition);
|
||||
Vector3 endToTangent = (endTangent - endPosition);
|
||||
|
||||
float sqrError = 0.001f;
|
||||
|
||||
if (Colinear(startToTangent, startToEnd, sqrError) && Colinear(endToTangent, startToEnd, sqrError))
|
||||
return ClosestPointToSegment(point, startPosition, endPosition, out t);
|
||||
|
||||
Vector3 leftStartPosition;
|
||||
Vector3 leftEndPosition;
|
||||
Vector3 leftStartTangent;
|
||||
Vector3 leftEndTangent;
|
||||
|
||||
Vector3 rightStartPosition;
|
||||
Vector3 rightEndPosition;
|
||||
Vector3 rightStartTangent;
|
||||
Vector3 rightEndTangent;
|
||||
|
||||
float leftStartT = 0f;
|
||||
float leftEndT = 0.5f;
|
||||
float rightStartT = 0.5f;
|
||||
float rightEndT = 1f;
|
||||
|
||||
SplitBezier(0.5f, startPosition, endPosition, startTangent, endTangent,
|
||||
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
|
||||
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
|
||||
|
||||
Vector3 pointLeft = ClosestPointOnCurveIterative(point, leftStartPosition, leftEndPosition, leftStartTangent, leftEndTangent, sqrError, ref leftStartT, ref leftEndT);
|
||||
Vector3 pointRight = ClosestPointOnCurveIterative(point, rightStartPosition, rightEndPosition, rightStartTangent, rightEndTangent, sqrError, ref rightStartT, ref rightEndT);
|
||||
|
||||
if ((point - pointLeft).sqrMagnitude < (point - pointRight).sqrMagnitude)
|
||||
{
|
||||
t = leftStartT;
|
||||
return pointLeft;
|
||||
}
|
||||
|
||||
t = rightStartT;
|
||||
return pointRight;
|
||||
}
|
||||
|
||||
public static Vector3 ClosestPointOnCurveFast(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, out float t)
|
||||
{
|
||||
float sqrError = 0.001f;
|
||||
float startT = 0f;
|
||||
float endT = 1f;
|
||||
|
||||
Vector3 closestPoint = ClosestPointOnCurveIterative(point, startPosition, endPosition, startTangent, endTangent, sqrError, ref startT, ref endT);
|
||||
|
||||
t = startT;
|
||||
|
||||
return closestPoint;
|
||||
}
|
||||
|
||||
private static Vector3 ClosestPointOnCurveIterative(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, float sqrError, ref float startT, ref float endT)
|
||||
{
|
||||
while ((startPosition - endPosition).sqrMagnitude > sqrError)
|
||||
{
|
||||
Vector3 startToEnd = endPosition - startPosition;
|
||||
Vector3 startToTangent = (startTangent - startPosition);
|
||||
Vector3 endToTangent = (endTangent - endPosition);
|
||||
|
||||
if (Colinear(startToTangent, startToEnd, sqrError) && Colinear(endToTangent, startToEnd, sqrError))
|
||||
{
|
||||
float t;
|
||||
Vector3 closestPoint = ClosestPointToSegment(point, startPosition, endPosition, out t);
|
||||
t *= (endT - startT);
|
||||
startT += t;
|
||||
endT -= t;
|
||||
return closestPoint;
|
||||
}
|
||||
|
||||
Vector3 leftStartPosition;
|
||||
Vector3 leftEndPosition;
|
||||
Vector3 leftStartTangent;
|
||||
Vector3 leftEndTangent;
|
||||
|
||||
Vector3 rightStartPosition;
|
||||
Vector3 rightEndPosition;
|
||||
Vector3 rightStartTangent;
|
||||
Vector3 rightEndTangent;
|
||||
|
||||
SplitBezier(0.5f, startPosition, endPosition, startTangent, endTangent,
|
||||
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
|
||||
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
|
||||
|
||||
s_TempPoints[0] = leftStartPosition;
|
||||
s_TempPoints[1] = leftStartTangent;
|
||||
s_TempPoints[2] = leftEndTangent;
|
||||
|
||||
float sqrDistanceLeft = SqrDistanceToPolyLine(point, s_TempPoints);
|
||||
|
||||
s_TempPoints[0] = rightEndPosition;
|
||||
s_TempPoints[1] = rightEndTangent;
|
||||
s_TempPoints[2] = rightStartTangent;
|
||||
|
||||
float sqrDistanceRight = SqrDistanceToPolyLine(point, s_TempPoints);
|
||||
|
||||
if (sqrDistanceLeft < sqrDistanceRight)
|
||||
{
|
||||
startPosition = leftStartPosition;
|
||||
endPosition = leftEndPosition;
|
||||
startTangent = leftStartTangent;
|
||||
endTangent = leftEndTangent;
|
||||
|
||||
endT -= (endT - startT) * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
startPosition = rightStartPosition;
|
||||
endPosition = rightEndPosition;
|
||||
startTangent = rightStartTangent;
|
||||
endTangent = rightEndTangent;
|
||||
|
||||
startT += (endT - startT) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
return endPosition;
|
||||
}
|
||||
|
||||
public static void SplitBezier(float t, Vector3 startPosition, Vector3 endPosition, Vector3 startRightTangent, Vector3 endLeftTangent,
|
||||
out Vector3 leftStartPosition, out Vector3 leftEndPosition, out Vector3 leftStartTangent, out Vector3 leftEndTangent,
|
||||
out Vector3 rightStartPosition, out Vector3 rightEndPosition, out Vector3 rightStartTangent, out Vector3 rightEndTangent)
|
||||
{
|
||||
Vector3 tangent0 = (startRightTangent - startPosition);
|
||||
Vector3 tangent1 = (endLeftTangent - endPosition);
|
||||
Vector3 tangentEdge = (endLeftTangent - startRightTangent);
|
||||
|
||||
Vector3 tangentPoint0 = startPosition + tangent0 * t;
|
||||
Vector3 tangentPoint1 = endPosition + tangent1 * (1f - t);
|
||||
Vector3 tangentEdgePoint = startRightTangent + tangentEdge * t;
|
||||
|
||||
Vector3 newTangent0 = tangentPoint0 + (tangentEdgePoint - tangentPoint0) * t;
|
||||
Vector3 newTangent1 = tangentPoint1 + (tangentEdgePoint - tangentPoint1) * (1f - t);
|
||||
Vector3 newTangentEdge = newTangent1 - newTangent0;
|
||||
|
||||
Vector3 bezierPoint = newTangent0 + newTangentEdge * t;
|
||||
|
||||
leftStartPosition = startPosition;
|
||||
leftEndPosition = bezierPoint;
|
||||
leftStartTangent = tangentPoint0;
|
||||
leftEndTangent = newTangent0;
|
||||
|
||||
rightStartPosition = bezierPoint;
|
||||
rightEndPosition = endPosition;
|
||||
rightStartTangent = newTangent1;
|
||||
rightEndTangent = tangentPoint1;
|
||||
}
|
||||
|
||||
private static Vector3 ClosestPointToSegment(Vector3 point, Vector3 segmentStart, Vector3 segmentEnd, out float t)
|
||||
{
|
||||
Vector3 relativePoint = point - segmentStart;
|
||||
Vector3 segment = (segmentEnd - segmentStart);
|
||||
Vector3 segmentDirection = segment.normalized;
|
||||
float length = segment.magnitude;
|
||||
|
||||
float dot = Vector3.Dot(relativePoint, segmentDirection);
|
||||
|
||||
if (dot <= 0f)
|
||||
dot = 0f;
|
||||
else if (dot >= length)
|
||||
dot = length;
|
||||
|
||||
t = dot / length;
|
||||
|
||||
return segmentStart + segment * t;
|
||||
}
|
||||
|
||||
private static float SqrDistanceToPolyLine(Vector3 point, Vector3[] points)
|
||||
{
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < points.Length - 1; ++i)
|
||||
{
|
||||
float distance = SqrDistanceToSegment(point, points[i], points[i + 1]);
|
||||
|
||||
if (distance < minDistance)
|
||||
minDistance = distance;
|
||||
}
|
||||
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
private static float SqrDistanceToSegment(Vector3 point, Vector3 segmentStart, Vector3 segmentEnd)
|
||||
{
|
||||
Vector3 relativePoint = point - segmentStart;
|
||||
Vector3 segment = (segmentEnd - segmentStart);
|
||||
Vector3 segmentDirection = segment.normalized;
|
||||
float length = segment.magnitude;
|
||||
|
||||
float dot = Vector3.Dot(relativePoint, segmentDirection);
|
||||
|
||||
if (dot <= 0f)
|
||||
return (point - segmentStart).sqrMagnitude;
|
||||
else if (dot >= length)
|
||||
return (point - segmentEnd).sqrMagnitude;
|
||||
|
||||
return Vector3.Cross(relativePoint, segmentDirection).sqrMagnitude;
|
||||
}
|
||||
|
||||
private static bool Colinear(Vector3 v1, Vector3 v2, float error = 0.0001f)
|
||||
{
|
||||
return Mathf.Abs(v1.x * v2.y - v1.y * v2.x + v1.x * v2.z - v1.z * v2.x + v1.y * v2.z - v1.z * v2.y) < error;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal enum TangentMode
|
||||
{
|
||||
Linear = 0,
|
||||
Continuous = 1,
|
||||
Broken = 2
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal struct TangentCache
|
||||
{
|
||||
public Vector3 leftTangent;
|
||||
public Vector3 rightTangent;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal struct ControlPoint
|
||||
{
|
||||
public Vector3 position;
|
||||
public Vector3 localLeftTangent;
|
||||
public Vector3 localRightTangent;
|
||||
public TangentMode tangentMode;
|
||||
public TangentCache continuousCache;
|
||||
public TangentCache brokenCache;
|
||||
public bool mirrorLeft;
|
||||
|
||||
public ControlPoint(Vector3 pos)
|
||||
{
|
||||
position = pos;
|
||||
localLeftTangent = Vector3.zero;
|
||||
localRightTangent = Vector3.zero;
|
||||
tangentMode = TangentMode.Linear;
|
||||
continuousCache = default(TangentCache);
|
||||
brokenCache = default(TangentCache);
|
||||
mirrorLeft = false;
|
||||
}
|
||||
|
||||
public Vector3 leftTangent
|
||||
{
|
||||
get { return localLeftTangent + position; }
|
||||
set { localLeftTangent = value - position; }
|
||||
}
|
||||
|
||||
public Vector3 rightTangent
|
||||
{
|
||||
get { return localRightTangent + position; }
|
||||
set { localRightTangent = value - position; }
|
||||
}
|
||||
|
||||
public void StoreTangents()
|
||||
{
|
||||
if (tangentMode == TangentMode.Continuous)
|
||||
{
|
||||
continuousCache.leftTangent = localLeftTangent;
|
||||
continuousCache.rightTangent = localRightTangent;
|
||||
}
|
||||
else if (tangentMode == TangentMode.Broken)
|
||||
{
|
||||
brokenCache.leftTangent = localLeftTangent;
|
||||
brokenCache.rightTangent = localRightTangent;
|
||||
}
|
||||
}
|
||||
|
||||
public void RestoreTangents()
|
||||
{
|
||||
if (tangentMode == TangentMode.Continuous)
|
||||
{
|
||||
localLeftTangent = continuousCache.leftTangent;
|
||||
localRightTangent = continuousCache.rightTangent;
|
||||
}
|
||||
else if (tangentMode == TangentMode.Broken)
|
||||
{
|
||||
localLeftTangent = brokenCache.leftTangent;
|
||||
localRightTangent = brokenCache.rightTangent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
[Serializable]
|
||||
internal class EditablePath : IEditablePath
|
||||
{
|
||||
[SerializeField]
|
||||
private ShapeType m_ShapeType;
|
||||
[SerializeField]
|
||||
private IndexedSelection m_Selection = new IndexedSelection();
|
||||
[SerializeField]
|
||||
private List<ControlPoint> m_ControlPoints = new List<ControlPoint>();
|
||||
[SerializeField]
|
||||
private bool m_IsOpenEnded;
|
||||
private Matrix4x4 m_LocalToWorldMatrix = Matrix4x4.identity;
|
||||
private Matrix4x4 m_WorldToLocalMatrix = Matrix4x4.identity;
|
||||
private Vector3 m_Forward = Vector3.forward;
|
||||
private Vector3 m_Up = Vector3.up;
|
||||
private Vector3 m_Right = Vector3.right;
|
||||
|
||||
public ShapeType shapeType
|
||||
{
|
||||
get { return m_ShapeType; }
|
||||
set { m_ShapeType = value; }
|
||||
}
|
||||
|
||||
public IUndoObject undoObject { get; set; }
|
||||
|
||||
public Matrix4x4 localToWorldMatrix
|
||||
{
|
||||
get { return m_LocalToWorldMatrix; }
|
||||
set
|
||||
{
|
||||
m_LocalToWorldMatrix = value;
|
||||
m_WorldToLocalMatrix = value.inverse;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 forward
|
||||
{
|
||||
get { return m_Forward; }
|
||||
set { m_Forward = value; }
|
||||
}
|
||||
|
||||
public Vector3 up
|
||||
{
|
||||
get { return m_Up; }
|
||||
set { m_Up = value; }
|
||||
}
|
||||
|
||||
public Vector3 right
|
||||
{
|
||||
get { return m_Right; }
|
||||
set { m_Right = value; }
|
||||
}
|
||||
|
||||
public Matrix4x4 worldToLocalMatrix
|
||||
{
|
||||
get { return m_WorldToLocalMatrix; }
|
||||
}
|
||||
|
||||
public bool isOpenEnded
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pointCount < 3)
|
||||
return true;
|
||||
|
||||
return m_IsOpenEnded;
|
||||
}
|
||||
set { m_IsOpenEnded = value; }
|
||||
}
|
||||
|
||||
public ISelection<int> selection
|
||||
{
|
||||
get { return m_Selection; }
|
||||
}
|
||||
|
||||
public int pointCount
|
||||
{
|
||||
get { return m_ControlPoints.Count; }
|
||||
}
|
||||
|
||||
public ControlPoint GetPoint(int index)
|
||||
{
|
||||
return TransformPoint(localToWorldMatrix, m_ControlPoints[index]);
|
||||
}
|
||||
|
||||
public void SetPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
m_ControlPoints[index] = TransformPoint(worldToLocalMatrix, controlPoint);
|
||||
}
|
||||
|
||||
public void AddPoint(ControlPoint controlPoint)
|
||||
{
|
||||
m_ControlPoints.Insert(pointCount, TransformPoint(worldToLocalMatrix, controlPoint));
|
||||
}
|
||||
|
||||
public void InsertPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
m_ControlPoints.Insert(index, TransformPoint(worldToLocalMatrix, controlPoint));
|
||||
}
|
||||
|
||||
public void RemovePoint(int index)
|
||||
{
|
||||
m_ControlPoints.RemoveAt(index);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_ControlPoints.Clear();
|
||||
}
|
||||
|
||||
private ControlPoint TransformPoint(Matrix4x4 transformMatrix, ControlPoint controlPoint)
|
||||
{
|
||||
if (transformMatrix == Matrix4x4.identity)
|
||||
return controlPoint;
|
||||
|
||||
var newControlPoint = new ControlPoint()
|
||||
{
|
||||
position = transformMatrix.MultiplyPoint3x4(controlPoint.position),
|
||||
tangentMode = controlPoint.tangentMode,
|
||||
continuousCache = controlPoint.continuousCache,
|
||||
brokenCache = controlPoint.brokenCache,
|
||||
mirrorLeft = controlPoint.mirrorLeft
|
||||
};
|
||||
|
||||
newControlPoint.rightTangent = transformMatrix.MultiplyPoint3x4(controlPoint.rightTangent);
|
||||
newControlPoint.leftTangent = transformMatrix.MultiplyPoint3x4(controlPoint.leftTangent);
|
||||
|
||||
return newControlPoint;
|
||||
}
|
||||
|
||||
public bool Select(ISelector<Vector3> selector)
|
||||
{
|
||||
var changed = false;
|
||||
|
||||
for (var i = 0; i < pointCount; ++i)
|
||||
changed |= selection.Select(i, selector.Select(GetPoint(i).position));
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public virtual void SetDefaultShape()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,264 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class EditablePathController : IEditablePathController
|
||||
{
|
||||
private ISnapping<Vector3> m_Snapping = new Snapping();
|
||||
|
||||
public IEditablePath editablePath { get; set; }
|
||||
public IEditablePath closestEditablePath { get { return editablePath; } }
|
||||
|
||||
public ISnapping<Vector3> snapping
|
||||
{
|
||||
get { return m_Snapping; }
|
||||
set { m_Snapping = value; }
|
||||
}
|
||||
|
||||
public bool enableSnapping { get; set; }
|
||||
|
||||
public void RegisterUndo(string name)
|
||||
{
|
||||
if (editablePath.undoObject != null)
|
||||
editablePath.undoObject.RegisterUndo(name);
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
editablePath.selection.Clear();
|
||||
}
|
||||
|
||||
public void SelectPoint(int index, bool select)
|
||||
{
|
||||
editablePath.selection.Select(index, select);
|
||||
}
|
||||
|
||||
public void CreatePoint(int index, Vector3 position)
|
||||
{
|
||||
ClearSelection();
|
||||
|
||||
if (editablePath.shapeType == ShapeType.Polygon)
|
||||
{
|
||||
editablePath.InsertPoint(index + 1, new ControlPoint() { position = position });
|
||||
}
|
||||
else if (editablePath.shapeType == ShapeType.Spline)
|
||||
{
|
||||
var nextIndex = NextIndex(index);
|
||||
var currentPoint = editablePath.GetPoint(index);
|
||||
var nextPoint = editablePath.GetPoint(nextIndex);
|
||||
|
||||
float t;
|
||||
var closestPoint = BezierUtility.ClosestPointOnCurve(
|
||||
position,
|
||||
currentPoint.position,
|
||||
nextPoint.position,
|
||||
GetRightTangentPosition(index),
|
||||
GetLeftTangentPosition(nextIndex),
|
||||
out t);
|
||||
|
||||
Vector3 leftStartPosition;
|
||||
Vector3 leftEndPosition;
|
||||
Vector3 leftStartTangent;
|
||||
Vector3 leftEndTangent;
|
||||
|
||||
Vector3 rightStartPosition;
|
||||
Vector3 rightEndPosition;
|
||||
Vector3 rightStartTangent;
|
||||
Vector3 rightEndTangent;
|
||||
|
||||
BezierUtility.SplitBezier(t, currentPoint.position, nextPoint.position, GetRightTangentPosition(index), GetLeftTangentPosition(nextIndex),
|
||||
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
|
||||
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
|
||||
|
||||
var newPointIndex = index + 1;
|
||||
var newPoint = new ControlPoint()
|
||||
{
|
||||
position = closestPoint,
|
||||
leftTangent = leftEndTangent,
|
||||
rightTangent = rightStartTangent,
|
||||
tangentMode = TangentMode.Continuous
|
||||
};
|
||||
|
||||
currentPoint.rightTangent = leftStartTangent;
|
||||
nextPoint.leftTangent = rightEndTangent;
|
||||
|
||||
if (currentPoint.tangentMode == TangentMode.Linear && nextPoint.tangentMode == TangentMode.Linear)
|
||||
{
|
||||
newPoint.tangentMode = TangentMode.Linear;
|
||||
newPoint.localLeftTangent = Vector3.zero;
|
||||
newPoint.localRightTangent = Vector3.zero;
|
||||
currentPoint.localRightTangent = Vector3.zero;
|
||||
nextPoint.localLeftTangent = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentPoint.tangentMode == TangentMode.Linear)
|
||||
currentPoint.tangentMode = TangentMode.Broken;
|
||||
|
||||
if (nextPoint.tangentMode == TangentMode.Linear)
|
||||
nextPoint.tangentMode = TangentMode.Broken;
|
||||
}
|
||||
|
||||
editablePath.SetPoint(index, currentPoint);
|
||||
editablePath.SetPoint(nextIndex, nextPoint);
|
||||
editablePath.InsertPoint(newPointIndex, newPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveSelectedPoints()
|
||||
{
|
||||
var minPointCount = editablePath.isOpenEnded ? 2 : 3;
|
||||
int pointsCountToRemove = editablePath.selection.Count;
|
||||
|
||||
if (editablePath.pointCount != pointsCountToRemove)
|
||||
{
|
||||
var indices = editablePath.selection.elements.OrderByDescending(i => i);
|
||||
|
||||
foreach (var index in indices)
|
||||
{
|
||||
if (editablePath.pointCount > minPointCount)
|
||||
{
|
||||
editablePath.RemovePoint(index);
|
||||
}
|
||||
}
|
||||
ClearSelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
editablePath.SetDefaultShape();
|
||||
ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveSelectedPoints(Vector3 delta)
|
||||
{
|
||||
delta = Vector3.ProjectOnPlane(delta, editablePath.forward);
|
||||
|
||||
for (var i = 0; i < editablePath.pointCount; ++i)
|
||||
{
|
||||
if (editablePath.selection.Contains(i))
|
||||
{
|
||||
var controlPoint = editablePath.GetPoint(i);
|
||||
controlPoint.position += delta;
|
||||
editablePath.SetPoint(i, controlPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveEdge(int index, Vector3 delta)
|
||||
{
|
||||
if (editablePath.isOpenEnded && index == editablePath.pointCount - 1)
|
||||
return;
|
||||
|
||||
var controlPoint = editablePath.GetPoint(index);
|
||||
controlPoint.position += delta;
|
||||
editablePath.SetPoint(index, controlPoint);
|
||||
controlPoint = NextControlPoint(index);
|
||||
controlPoint.position += delta;
|
||||
editablePath.SetPoint(NextIndex(index), controlPoint);
|
||||
}
|
||||
|
||||
public void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent)
|
||||
{
|
||||
var controlPoint = editablePath.GetPoint(index);
|
||||
controlPoint.leftTangent = position;
|
||||
controlPoint.mirrorLeft = false;
|
||||
|
||||
if (setToLinear)
|
||||
{
|
||||
controlPoint.leftTangent = controlPoint.position;
|
||||
controlPoint.rightTangent = cachedRightTangent;
|
||||
}
|
||||
else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
|
||||
{
|
||||
var magnitude = controlPoint.localRightTangent.magnitude;
|
||||
|
||||
if (mirror)
|
||||
magnitude = controlPoint.localLeftTangent.magnitude;
|
||||
|
||||
controlPoint.localRightTangent = magnitude * -controlPoint.localLeftTangent.normalized;
|
||||
}
|
||||
|
||||
editablePath.SetPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
public void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent)
|
||||
{
|
||||
var controlPoint = editablePath.GetPoint(index);
|
||||
controlPoint.rightTangent = position;
|
||||
controlPoint.mirrorLeft = true;
|
||||
|
||||
if (setToLinear)
|
||||
{
|
||||
controlPoint.rightTangent = controlPoint.position;
|
||||
controlPoint.leftTangent = cachedLeftTangent;
|
||||
}
|
||||
else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
|
||||
{
|
||||
var magnitude = controlPoint.localLeftTangent.magnitude;
|
||||
|
||||
if (mirror)
|
||||
magnitude = controlPoint.localRightTangent.magnitude;
|
||||
|
||||
controlPoint.localLeftTangent = magnitude * -controlPoint.localRightTangent.normalized;
|
||||
}
|
||||
|
||||
editablePath.SetPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
public void ClearClosestPath() {}
|
||||
public void AddClosestPath(float distance) {}
|
||||
|
||||
private Vector3 GetLeftTangentPosition(int index)
|
||||
{
|
||||
var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localLeftTangent.sqrMagnitude, 0f);
|
||||
|
||||
if (isLinear)
|
||||
{
|
||||
var position = editablePath.GetPoint(index).position;
|
||||
var prevPosition = PrevControlPoint(index).position;
|
||||
|
||||
return (1f / 3f) * (prevPosition - position) + position;
|
||||
}
|
||||
|
||||
return editablePath.GetPoint(index).leftTangent;
|
||||
}
|
||||
|
||||
private Vector3 GetRightTangentPosition(int index)
|
||||
{
|
||||
var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localRightTangent.sqrMagnitude, 0f);
|
||||
|
||||
if (isLinear)
|
||||
{
|
||||
var position = editablePath.GetPoint(index).position;
|
||||
var nextPosition = NextControlPoint(index).position;
|
||||
|
||||
return (1f / 3f) * (nextPosition - position) + position;
|
||||
}
|
||||
|
||||
return editablePath.GetPoint(index).rightTangent;
|
||||
}
|
||||
|
||||
private int NextIndex(int index)
|
||||
{
|
||||
return EditablePathUtility.Mod(index + 1, editablePath.pointCount);
|
||||
}
|
||||
|
||||
private ControlPoint NextControlPoint(int index)
|
||||
{
|
||||
return editablePath.GetPoint(NextIndex(index));
|
||||
}
|
||||
|
||||
private int PrevIndex(int index)
|
||||
{
|
||||
return EditablePathUtility.Mod(index - 1, editablePath.pointCount);
|
||||
}
|
||||
|
||||
private ControlPoint PrevControlPoint(int index)
|
||||
{
|
||||
return editablePath.GetPoint(PrevIndex(index));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,275 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal static class EditablePathExtensions
|
||||
{
|
||||
public static Polygon ToPolygon(this IEditablePath path)
|
||||
{
|
||||
var polygon = new Polygon()
|
||||
{
|
||||
isOpenEnded = path.isOpenEnded,
|
||||
points = new Vector3[path.pointCount]
|
||||
};
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
polygon.points[i] = path.GetPoint(i).position;
|
||||
|
||||
return polygon;
|
||||
}
|
||||
|
||||
public static Spline ToSpline(this IEditablePath path)
|
||||
{
|
||||
var count = path.pointCount * 3;
|
||||
|
||||
if (path.isOpenEnded)
|
||||
count -= 2;
|
||||
|
||||
var spline = new Spline()
|
||||
{
|
||||
isOpenEnded = path.isOpenEnded,
|
||||
points = new Vector3[count]
|
||||
};
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
var point = path.GetPoint(i);
|
||||
|
||||
spline.points[i * 3] = point.position;
|
||||
|
||||
if (i * 3 + 1 < count)
|
||||
{
|
||||
var nextIndex = EditablePathUtility.Mod(i + 1, path.pointCount);
|
||||
|
||||
spline.points[i * 3 + 1] = path.CalculateRightTangent(i);
|
||||
spline.points[i * 3 + 2] = path.CalculateLeftTangent(nextIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return spline;
|
||||
}
|
||||
|
||||
public static Vector3 CalculateLocalLeftTangent(this IEditablePath path, int index)
|
||||
{
|
||||
return path.CalculateLeftTangent(index) - path.GetPoint(index).position;
|
||||
}
|
||||
|
||||
public static Vector3 CalculateLeftTangent(this IEditablePath path, int index)
|
||||
{
|
||||
var point = path.GetPoint(index);
|
||||
var isTangentLinear = point.localLeftTangent == Vector3.zero;
|
||||
var isEndpoint = path.isOpenEnded && index == 0;
|
||||
var tangent = point.leftTangent;
|
||||
|
||||
if (isEndpoint)
|
||||
return point.position;
|
||||
|
||||
if (isTangentLinear)
|
||||
{
|
||||
var prevPoint = path.GetPrevPoint(index);
|
||||
var v = prevPoint.position - point.position;
|
||||
tangent = point.position + v.normalized * (v.magnitude / 3f);
|
||||
}
|
||||
|
||||
return tangent;
|
||||
}
|
||||
|
||||
public static Vector3 CalculateLocalRightTangent(this IEditablePath path, int index)
|
||||
{
|
||||
return path.CalculateRightTangent(index) - path.GetPoint(index).position;
|
||||
}
|
||||
|
||||
public static Vector3 CalculateRightTangent(this IEditablePath path, int index)
|
||||
{
|
||||
var point = path.GetPoint(index);
|
||||
var isTangentLinear = point.localRightTangent == Vector3.zero;
|
||||
var isEndpoint = path.isOpenEnded && index == path.pointCount - 1;
|
||||
var tangent = point.rightTangent;
|
||||
|
||||
if (isEndpoint)
|
||||
return point.position;
|
||||
|
||||
if (isTangentLinear)
|
||||
{
|
||||
var nextPoint = path.GetNextPoint(index);
|
||||
var v = nextPoint.position - point.position;
|
||||
tangent = point.position + v.normalized * (v.magnitude / 3f);
|
||||
}
|
||||
|
||||
return tangent;
|
||||
}
|
||||
|
||||
public static ControlPoint GetPrevPoint(this IEditablePath path, int index)
|
||||
{
|
||||
return path.GetPoint(EditablePathUtility.Mod(index - 1, path.pointCount));
|
||||
}
|
||||
|
||||
public static ControlPoint GetNextPoint(this IEditablePath path, int index)
|
||||
{
|
||||
return path.GetPoint(EditablePathUtility.Mod(index + 1, path.pointCount));
|
||||
}
|
||||
|
||||
public static void UpdateTangentMode(this IEditablePath path, int index)
|
||||
{
|
||||
var localToWorldMatrix = path.localToWorldMatrix;
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
var controlPoint = path.GetPoint(index);
|
||||
var isLeftTangentLinear = controlPoint.localLeftTangent == Vector3.zero;
|
||||
var isRightTangentLinear = controlPoint.localRightTangent == Vector3.zero;
|
||||
|
||||
if (isLeftTangentLinear && isRightTangentLinear)
|
||||
controlPoint.tangentMode = TangentMode.Linear;
|
||||
else if (isLeftTangentLinear || isRightTangentLinear)
|
||||
controlPoint.tangentMode = TangentMode.Broken;
|
||||
else if (controlPoint.tangentMode != TangentMode.Continuous)
|
||||
controlPoint.tangentMode = TangentMode.Broken;
|
||||
|
||||
controlPoint.StoreTangents();
|
||||
path.SetPoint(index, controlPoint);
|
||||
path.localToWorldMatrix = localToWorldMatrix;
|
||||
}
|
||||
|
||||
public static void UpdateTangentsFromMode(this IEditablePath path)
|
||||
{
|
||||
const float kEpsilon = 0.001f;
|
||||
|
||||
var localToWorldMatrix = path.localToWorldMatrix;
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
var controlPoint = path.GetPoint(i);
|
||||
|
||||
if (controlPoint.tangentMode == TangentMode.Linear)
|
||||
{
|
||||
controlPoint.localLeftTangent = Vector3.zero;
|
||||
controlPoint.localRightTangent = Vector3.zero;
|
||||
}
|
||||
else if (controlPoint.tangentMode == TangentMode.Broken)
|
||||
{
|
||||
var isLeftEndpoint = path.isOpenEnded && i == 0;
|
||||
var prevPoint = path.GetPrevPoint(i);
|
||||
var nextPoint = path.GetNextPoint(i);
|
||||
|
||||
var liniarLeftPosition = (prevPoint.position - controlPoint.position) / 3f;
|
||||
var isLeftTangentLinear = isLeftEndpoint || (controlPoint.localLeftTangent - liniarLeftPosition).sqrMagnitude < kEpsilon;
|
||||
|
||||
if (isLeftTangentLinear)
|
||||
controlPoint.localLeftTangent = Vector3.zero;
|
||||
|
||||
var isRightEndpoint = path.isOpenEnded && i == path.pointCount - 1;
|
||||
var liniarRightPosition = (nextPoint.position - controlPoint.position) / 3f;
|
||||
var isRightTangentLinear = isRightEndpoint || (controlPoint.localRightTangent - liniarRightPosition).sqrMagnitude < kEpsilon;
|
||||
|
||||
if (isRightTangentLinear)
|
||||
controlPoint.localRightTangent = Vector3.zero;
|
||||
|
||||
if (isLeftTangentLinear && isRightTangentLinear)
|
||||
controlPoint.tangentMode = TangentMode.Linear;
|
||||
}
|
||||
else if (controlPoint.tangentMode == TangentMode.Continuous)
|
||||
{
|
||||
//TODO: ensure tangent continuity
|
||||
}
|
||||
|
||||
controlPoint.StoreTangents();
|
||||
path.SetPoint(i, controlPoint);
|
||||
}
|
||||
|
||||
path.localToWorldMatrix = localToWorldMatrix;
|
||||
}
|
||||
|
||||
public static void SetTangentMode(this IEditablePath path, int index, TangentMode tangentMode)
|
||||
{
|
||||
var localToWorldMatrix = path.localToWorldMatrix;
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
var controlPoint = path.GetPoint(index);
|
||||
var isEndpoint = path.isOpenEnded && (index == 0 || index == path.pointCount - 1);
|
||||
var oldTangentMode = controlPoint.tangentMode;
|
||||
|
||||
controlPoint.tangentMode = tangentMode;
|
||||
controlPoint.RestoreTangents();
|
||||
|
||||
if (tangentMode == TangentMode.Linear)
|
||||
{
|
||||
controlPoint.localLeftTangent = Vector3.zero;
|
||||
controlPoint.localRightTangent = Vector3.zero;
|
||||
}
|
||||
else if (tangentMode == TangentMode.Continuous && !isEndpoint)
|
||||
{
|
||||
var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
|
||||
var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
|
||||
var tangentDotProduct = Vector3.Dot(controlPoint.localLeftTangent.normalized, controlPoint.localRightTangent.normalized);
|
||||
var isContinous = tangentDotProduct < 0f && (tangentDotProduct + 1) < 0.001f;
|
||||
var isLinear = isLeftLinear && isRightLinear;
|
||||
|
||||
if ((isLinear || oldTangentMode == TangentMode.Broken) && !isContinous)
|
||||
{
|
||||
var prevPoint = path.GetPrevPoint(index);
|
||||
var nextPoint = path.GetNextPoint(index);
|
||||
var vLeft = prevPoint.position - controlPoint.position;
|
||||
var vRight = nextPoint.position - controlPoint.position;
|
||||
var rightDirection = Vector3.Cross(Vector3.Cross(vLeft, vRight), vLeft.normalized + vRight.normalized).normalized;
|
||||
var scale = 1f / 3f;
|
||||
|
||||
if (isLeftLinear)
|
||||
controlPoint.localLeftTangent = vLeft.magnitude * scale * -rightDirection;
|
||||
else
|
||||
controlPoint.localLeftTangent = controlPoint.localLeftTangent.magnitude * -rightDirection;
|
||||
|
||||
if (isRightLinear)
|
||||
controlPoint.localRightTangent = vRight.magnitude * scale * rightDirection;
|
||||
else
|
||||
controlPoint.localRightTangent = controlPoint.localRightTangent.magnitude * rightDirection;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
|
||||
var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
|
||||
|
||||
if (isLeftLinear || isRightLinear)
|
||||
{
|
||||
if (isLeftLinear)
|
||||
controlPoint.localLeftTangent = path.CalculateLocalLeftTangent(index);
|
||||
|
||||
if (isRightLinear)
|
||||
controlPoint.localRightTangent = path.CalculateLocalRightTangent(index);
|
||||
}
|
||||
}
|
||||
|
||||
controlPoint.StoreTangents();
|
||||
path.SetPoint(index, controlPoint);
|
||||
path.localToWorldMatrix = localToWorldMatrix;
|
||||
}
|
||||
|
||||
public static void MirrorTangent(this IEditablePath path, int index)
|
||||
{
|
||||
var localToWorldMatrix = path.localToWorldMatrix;
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
var controlPoint = path.GetPoint(index);
|
||||
|
||||
if (controlPoint.tangentMode == TangentMode.Linear)
|
||||
return;
|
||||
|
||||
if (!Mathf.Approximately((controlPoint.localLeftTangent + controlPoint.localRightTangent).sqrMagnitude, 0f))
|
||||
{
|
||||
if (controlPoint.mirrorLeft)
|
||||
controlPoint.localLeftTangent = -controlPoint.localRightTangent;
|
||||
else
|
||||
controlPoint.localRightTangent = -controlPoint.localLeftTangent;
|
||||
|
||||
controlPoint.StoreTangents();
|
||||
path.SetPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
path.localToWorldMatrix = localToWorldMatrix;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class EditablePathUtility
|
||||
{
|
||||
public static int Mod(int x, int m)
|
||||
{
|
||||
int r = x % m;
|
||||
return r < 0 ? r + m : r;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface IEditablePath : ISelectable<Vector3>
|
||||
{
|
||||
ShapeType shapeType { get; set; }
|
||||
IUndoObject undoObject { get; set; }
|
||||
ISelection<int> selection { get; }
|
||||
Matrix4x4 localToWorldMatrix { get; set; }
|
||||
Vector3 forward { get; set; }
|
||||
Vector3 up { get; set; }
|
||||
Vector3 right { get; set; }
|
||||
bool isOpenEnded { get; set; }
|
||||
int pointCount { get; }
|
||||
ControlPoint GetPoint(int index);
|
||||
void SetPoint(int index, ControlPoint controlPoint);
|
||||
void AddPoint(ControlPoint controlPoint);
|
||||
void InsertPoint(int index, ControlPoint controlPoint);
|
||||
void RemovePoint(int index);
|
||||
void Clear();
|
||||
void SetDefaultShape();
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface IEditablePathController
|
||||
{
|
||||
IEditablePath editablePath { get; set; }
|
||||
IEditablePath closestEditablePath { get; }
|
||||
ISnapping<Vector3> snapping { get; set; }
|
||||
bool enableSnapping { get; set; }
|
||||
void RegisterUndo(string name);
|
||||
void ClearSelection();
|
||||
void SelectPoint(int index, bool select);
|
||||
void CreatePoint(int index, Vector3 position);
|
||||
void RemoveSelectedPoints();
|
||||
void MoveSelectedPoints(Vector3 delta);
|
||||
void MoveEdge(int index, Vector3 delta);
|
||||
void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent);
|
||||
void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent);
|
||||
void ClearClosestPath();
|
||||
void AddClosestPath(float distance);
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface ISnapping<T>
|
||||
{
|
||||
T Snap(T value);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface IUndoObject
|
||||
{
|
||||
void RegisterUndo(string name);
|
||||
}
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class MultipleEditablePathController : IEditablePathController
|
||||
{
|
||||
private IEditablePathController m_Controller = new EditablePathController();
|
||||
private List<IEditablePath> m_Paths = new List<IEditablePath>();
|
||||
private float m_ClosestDistance = float.MaxValue;
|
||||
private IEditablePath m_ClosestPath;
|
||||
|
||||
public IEditablePath editablePath
|
||||
{
|
||||
get { return m_Controller.editablePath; }
|
||||
set { m_Controller.editablePath = value; }
|
||||
}
|
||||
|
||||
public IEditablePath closestEditablePath { get; private set; }
|
||||
|
||||
public ISnapping<Vector3> snapping
|
||||
{
|
||||
get { return m_Controller.snapping; }
|
||||
set { m_Controller.snapping = value; }
|
||||
}
|
||||
|
||||
public bool enableSnapping
|
||||
{
|
||||
get { return m_Controller.enableSnapping; }
|
||||
set { m_Controller.enableSnapping = value; }
|
||||
}
|
||||
|
||||
public void ClearPaths()
|
||||
{
|
||||
m_Paths.Clear();
|
||||
}
|
||||
|
||||
public void AddPath(IEditablePath path)
|
||||
{
|
||||
if (!m_Paths.Contains(path))
|
||||
m_Paths.Add(path);
|
||||
}
|
||||
|
||||
public void RemovePath(IEditablePath path)
|
||||
{
|
||||
m_Paths.Remove(path);
|
||||
}
|
||||
|
||||
public void RegisterUndo(string name)
|
||||
{
|
||||
var current = editablePath;
|
||||
|
||||
ForEach((s) =>
|
||||
{
|
||||
editablePath = s;
|
||||
m_Controller.RegisterUndo(name);
|
||||
});
|
||||
|
||||
editablePath = current;
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
var current = editablePath;
|
||||
|
||||
ForEach((s) =>
|
||||
{
|
||||
editablePath = s;
|
||||
m_Controller.ClearSelection();
|
||||
});
|
||||
|
||||
editablePath = current;
|
||||
}
|
||||
|
||||
public void SelectPoint(int index, bool select)
|
||||
{
|
||||
m_Controller.SelectPoint(index, select);
|
||||
}
|
||||
|
||||
public void CreatePoint(int index, Vector3 position)
|
||||
{
|
||||
m_Controller.CreatePoint(index, position);
|
||||
}
|
||||
|
||||
public void RemoveSelectedPoints()
|
||||
{
|
||||
var current = editablePath;
|
||||
|
||||
ForEach((s) =>
|
||||
{
|
||||
editablePath = s;
|
||||
m_Controller.RemoveSelectedPoints();
|
||||
});
|
||||
|
||||
editablePath = current;
|
||||
}
|
||||
|
||||
public void MoveSelectedPoints(Vector3 delta)
|
||||
{
|
||||
var current = editablePath;
|
||||
|
||||
ForEach((s) =>
|
||||
{
|
||||
editablePath = s;
|
||||
m_Controller.MoveSelectedPoints(delta);
|
||||
});
|
||||
|
||||
editablePath = current;
|
||||
}
|
||||
|
||||
public void MoveEdge(int index, Vector3 delta)
|
||||
{
|
||||
m_Controller.MoveEdge(index, delta);
|
||||
}
|
||||
|
||||
public void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent)
|
||||
{
|
||||
m_Controller.SetLeftTangent(index, position, setToLinear, mirror, cachedRightTangent);
|
||||
}
|
||||
|
||||
public void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent)
|
||||
{
|
||||
m_Controller.SetRightTangent(index, position, setToLinear, mirror, cachedLeftTangent);
|
||||
}
|
||||
|
||||
public void ClearClosestPath()
|
||||
{
|
||||
m_ClosestDistance = float.MaxValue;
|
||||
closestEditablePath = null;
|
||||
}
|
||||
|
||||
public void AddClosestPath(float distance)
|
||||
{
|
||||
if (distance <= m_ClosestDistance)
|
||||
{
|
||||
m_ClosestDistance = distance;
|
||||
closestEditablePath = editablePath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForEach(Action<IEditablePath> action)
|
||||
{
|
||||
foreach (var path in m_Paths)
|
||||
{
|
||||
if (path == null)
|
||||
continue;
|
||||
|
||||
action(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class Snapping : ISnapping<Vector3>
|
||||
{
|
||||
public Vector3 Snap(Vector3 position)
|
||||
{
|
||||
return new Vector3(
|
||||
Snap(position.x, EditorPrefs.GetFloat("MoveSnapX", 1f)),
|
||||
Snap(position.y, EditorPrefs.GetFloat("MoveSnapY", 1f)),
|
||||
position.z);
|
||||
}
|
||||
|
||||
private float Snap(float value, float snap)
|
||||
{
|
||||
return Mathf.Round(value / snap) * snap;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class GenericScriptablePath<T> : ScriptablePath
|
||||
{
|
||||
[SerializeField]
|
||||
private List<T> m_Data = new List<T>();
|
||||
|
||||
public T[] data
|
||||
{
|
||||
get { return m_Data.ToArray(); }
|
||||
set
|
||||
{
|
||||
if (value.Length != pointCount)
|
||||
throw new Exception("Custom data count does not match control point count");
|
||||
|
||||
m_Data.Clear();
|
||||
m_Data.AddRange(value);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
|
||||
m_Data.Clear();
|
||||
}
|
||||
|
||||
public override void AddPoint(ControlPoint controlPoint)
|
||||
{
|
||||
base.AddPoint(controlPoint);
|
||||
|
||||
m_Data.Add(Create());
|
||||
}
|
||||
|
||||
public override void InsertPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
base.InsertPoint(index, controlPoint);
|
||||
|
||||
m_Data.Insert(index, Create());
|
||||
}
|
||||
|
||||
public override void RemovePoint(int index)
|
||||
{
|
||||
base.RemovePoint(index);
|
||||
|
||||
Destroy(m_Data[index]);
|
||||
|
||||
m_Data.RemoveAt(index);
|
||||
}
|
||||
|
||||
public T GetData(int index)
|
||||
{
|
||||
return m_Data[index];
|
||||
}
|
||||
|
||||
public void SetData(int index, T data)
|
||||
{
|
||||
m_Data[index] = data;
|
||||
}
|
||||
|
||||
protected virtual T Create()
|
||||
{
|
||||
return Activator.CreateInstance<T>();
|
||||
}
|
||||
|
||||
protected virtual void Destroy(T data) {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class GenericScriptablePathInspector<U, T> : ScriptablePathInspector where U : ScriptableData<T>
|
||||
{
|
||||
private List<U> m_DataObjects = new List<U>();
|
||||
private List<U> m_SelectedDataObjects = new List<U>();
|
||||
private Editor m_CachedEditor = null;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
PrepareDataObjects();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyDataObjects();
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
|
||||
DoCustomDataInspector();
|
||||
}
|
||||
|
||||
protected void DoCustomDataInspector()
|
||||
{
|
||||
PrepareDataObjects();
|
||||
|
||||
if (m_SelectedDataObjects.Count > 0)
|
||||
{
|
||||
CreateCachedEditor(m_SelectedDataObjects.ToArray(), null, ref m_CachedEditor);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
m_CachedEditor.OnInspectorGUI();
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
SetDataObjects();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareDataObjects()
|
||||
{
|
||||
var elementCount = 0;
|
||||
|
||||
m_SelectedDataObjects.Clear();
|
||||
|
||||
foreach (var path in paths)
|
||||
elementCount += path.pointCount;
|
||||
|
||||
while (m_DataObjects.Count < elementCount)
|
||||
CreateDataObject();
|
||||
|
||||
var index = 0;
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var genericPath = path as GenericScriptablePath<T>;
|
||||
var customDataArray = genericPath.data;
|
||||
var length = customDataArray.Length;
|
||||
|
||||
for (var i = 0; i < length; ++i)
|
||||
{
|
||||
var dataObject = m_DataObjects[index + i];
|
||||
dataObject.data = customDataArray[i];
|
||||
|
||||
if (path.selection.Contains(i))
|
||||
{
|
||||
dataObject.owner = path.owner;
|
||||
dataObject.index = i;
|
||||
m_SelectedDataObjects.Add(dataObject);
|
||||
}
|
||||
}
|
||||
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDataObjects()
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var genericPath = path as GenericScriptablePath<T>;
|
||||
var customDataArray = genericPath.data;
|
||||
var length = customDataArray.Length;
|
||||
|
||||
for (var i = 0; i < length; ++i)
|
||||
customDataArray[i] = m_DataObjects[index + i].data;
|
||||
|
||||
genericPath.data = customDataArray;
|
||||
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
|
||||
private U CreateDataObject()
|
||||
{
|
||||
var dataObject = ScriptableObject.CreateInstance<U>();
|
||||
m_DataObjects.Add(dataObject);
|
||||
return dataObject;
|
||||
}
|
||||
|
||||
private void DestroyDataObjects()
|
||||
{
|
||||
foreach (var customDataObject in m_DataObjects)
|
||||
DestroyImmediate(customDataObject);
|
||||
|
||||
m_DataObjects.Clear();
|
||||
m_SelectedDataObjects.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
#pragma warning disable 0618
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D;
|
||||
|
||||
#if !UNITY_2020_2_OR_NEWER
|
||||
using ToolManager = UnityEditor.EditorTools.EditorTools;
|
||||
#endif
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal abstract class PathComponentEditor<T> : Editor where T : ScriptablePath
|
||||
{
|
||||
private static class Contents
|
||||
{
|
||||
public static readonly GUIContent snappingLabel = new GUIContent("Snapping", "Snap points using the snap settings");
|
||||
}
|
||||
|
||||
private Editor m_CachedEditor = null;
|
||||
|
||||
protected void DoEditButton<U>(GUIContent icon, string label) where U : PathEditorTool<T>
|
||||
{
|
||||
const float kButtonWidth = 33;
|
||||
const float kButtonHeight = 23;
|
||||
const float k_SpaceBetweenLabelAndButton = 5;
|
||||
var buttonStyle = new GUIStyle("EditModeSingleButton");
|
||||
|
||||
var rect = EditorGUILayout.GetControlRect(true, kButtonHeight, buttonStyle);
|
||||
var buttonRect = new Rect(rect.xMin + EditorGUIUtility.labelWidth, rect.yMin, kButtonWidth, kButtonHeight);
|
||||
|
||||
var labelContent = new GUIContent(label);
|
||||
var labelSize = GUI.skin.label.CalcSize(labelContent);
|
||||
|
||||
var labelRect = new Rect(
|
||||
buttonRect.xMax + k_SpaceBetweenLabelAndButton,
|
||||
rect.yMin + (rect.height - labelSize.y) * .5f,
|
||||
labelSize.x,
|
||||
rect.height);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(!EditorToolManager.IsAvailable<U>()))
|
||||
{
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
var isActive = GUI.Toggle(buttonRect, EditorToolManager.IsActiveTool<U>(), icon, buttonStyle);
|
||||
|
||||
GUI.Label(labelRect, label);
|
||||
|
||||
if (check.changed)
|
||||
{
|
||||
if (isActive)
|
||||
ToolManager.SetActiveTool<U>();
|
||||
else
|
||||
ToolManager.RestorePreviousTool();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void DoPathInspector<U>() where U : PathEditorTool<T>
|
||||
{
|
||||
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
|
||||
{
|
||||
var paths = EditorToolManager.GetEditorTool<U>().paths;
|
||||
|
||||
CreateCachedEditor(paths, null, ref m_CachedEditor);
|
||||
|
||||
if (m_CachedEditor == null) //Needed to avoid a nullref on exiting playmode
|
||||
return;
|
||||
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
m_CachedEditor.OnInspectorGUI();
|
||||
|
||||
if (check.changed)
|
||||
EditorToolManager.GetEditorTool<U>().SetShapes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void DoSnappingInspector<U>() where U : PathEditorTool<T>
|
||||
{
|
||||
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
|
||||
{
|
||||
var tool = EditorToolManager.GetEditorTool<U>();
|
||||
tool.enableSnapping = EditorGUILayout.Toggle(Contents.snappingLabel, tool.enableSnapping);
|
||||
}
|
||||
}
|
||||
|
||||
protected void DoOpenEndedInspector<U>(SerializedProperty isOpenEndedProperty) where U : PathEditorTool<T>
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
EditorGUILayout.PropertyField(isOpenEndedProperty);
|
||||
|
||||
if (check.changed)
|
||||
{
|
||||
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
|
||||
{
|
||||
var paths = EditorToolManager.GetEditorTool<U>().paths;
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
path.undoObject.RegisterUndo("Set Open Ended");
|
||||
path.isOpenEnded = isOpenEndedProperty.boolValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 0618
|
@@ -0,0 +1,499 @@
|
||||
#pragma warning disable 0618
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
|
||||
|
||||
using UnityObject = UnityEngine.Object;
|
||||
#if !UNITY_2020_2_OR_NEWER
|
||||
using ToolManager = UnityEditor.EditorTools.EditorTools;
|
||||
#endif
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal static class PathEditorToolContents
|
||||
{
|
||||
internal static readonly GUIContent shapeToolIcon = IconContent("ShapeTool", "Unlocks the shape to allow editing in the Scene View.");
|
||||
internal static readonly GUIContent shapeToolPro = IconContent("ShapeToolPro", "Unlocks the shape to allow editing in the Scene View.");
|
||||
|
||||
internal static GUIContent IconContent(string name, string tooltip = null)
|
||||
{
|
||||
return new GUIContent(Resources.Load<Texture>(name), tooltip);
|
||||
}
|
||||
|
||||
public static GUIContent icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
return shapeToolPro;
|
||||
|
||||
return shapeToolIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IDuringSceneGuiTool
|
||||
{
|
||||
void DuringSceneGui(SceneView sceneView);
|
||||
bool IsAvailable();
|
||||
}
|
||||
|
||||
[InitializeOnLoad]
|
||||
internal class EditorToolManager
|
||||
{
|
||||
private static List<IDuringSceneGuiTool> m_Tools = new List<IDuringSceneGuiTool>();
|
||||
|
||||
static EditorToolManager()
|
||||
{
|
||||
SceneView.duringSceneGui += DuringSceneGui;
|
||||
}
|
||||
|
||||
internal static void Add(IDuringSceneGuiTool tool)
|
||||
{
|
||||
if (!m_Tools.Contains(tool) && tool is EditorTool)
|
||||
m_Tools.Add(tool);
|
||||
}
|
||||
|
||||
internal static void Remove(IDuringSceneGuiTool tool)
|
||||
{
|
||||
if (m_Tools.Contains(tool))
|
||||
m_Tools.Remove(tool);
|
||||
}
|
||||
|
||||
internal static bool IsActiveTool<T>() where T : EditorTool
|
||||
{
|
||||
return ToolManager.activeToolType.Equals(typeof(T));
|
||||
}
|
||||
|
||||
internal static bool IsAvailable<T>() where T : EditorTool
|
||||
{
|
||||
var tool = GetEditorTool<T>();
|
||||
|
||||
if (tool != null)
|
||||
return tool.IsAvailable();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static T GetEditorTool<T>() where T : EditorTool
|
||||
{
|
||||
foreach (var tool in m_Tools)
|
||||
{
|
||||
if (tool.GetType().Equals(typeof(T)))
|
||||
return tool as T;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void DuringSceneGui(SceneView sceneView)
|
||||
{
|
||||
foreach (var tool in m_Tools)
|
||||
{
|
||||
if (tool.IsAvailable() && ToolManager.IsActiveTool(tool as EditorTool))
|
||||
tool.DuringSceneGui(sceneView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class PathEditorTool<T> : EditorTool, IDuringSceneGuiTool where T : ScriptablePath
|
||||
{
|
||||
private Dictionary<UnityObject, T> m_Paths = new Dictionary<UnityObject, T>();
|
||||
private IGUIState m_GUIState = new GUIState();
|
||||
private Dictionary<UnityObject, GUISystem> m_GUISystems = new Dictionary<UnityObject, GUISystem>();
|
||||
private Dictionary<UnityObject, SerializedObject> m_SerializedObjects = new Dictionary<UnityObject, SerializedObject>();
|
||||
private MultipleEditablePathController m_Controller = new MultipleEditablePathController();
|
||||
private PointRectSelector m_RectSelector = new PointRectSelector();
|
||||
private bool m_IsActive = false;
|
||||
|
||||
internal T[] paths
|
||||
{
|
||||
get { return m_Paths.Values.ToArray(); }
|
||||
}
|
||||
|
||||
public bool enableSnapping
|
||||
{
|
||||
get { return m_Controller.enableSnapping; }
|
||||
set { m_Controller.enableSnapping = value; }
|
||||
}
|
||||
|
||||
public override GUIContent toolbarIcon
|
||||
{
|
||||
get { return PathEditorToolContents.icon; }
|
||||
}
|
||||
|
||||
public override bool IsAvailable()
|
||||
{
|
||||
return targets.Count() > 0;
|
||||
}
|
||||
|
||||
public T GetPath(UnityObject targetObject)
|
||||
{
|
||||
var path = default(T);
|
||||
m_Paths.TryGetValue(targetObject, out path);
|
||||
return path;
|
||||
}
|
||||
|
||||
public void SetPath(UnityObject target)
|
||||
{
|
||||
var path = GetPath(target);
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
var undoName = Undo.GetCurrentGroupName();
|
||||
var serializedObject = GetSerializedObject(target);
|
||||
|
||||
serializedObject.UpdateIfRequiredOrScript();
|
||||
|
||||
SetShape(path, serializedObject);
|
||||
|
||||
Undo.SetCurrentGroupName(undoName);
|
||||
}
|
||||
|
||||
private void RepaintInspectors()
|
||||
{
|
||||
var editorWindows = Resources.FindObjectsOfTypeAll<EditorWindow>();
|
||||
|
||||
foreach (var editorWindow in editorWindows)
|
||||
{
|
||||
if (editorWindow.titleContent.text == "Inspector")
|
||||
editorWindow.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_IsActive = false;
|
||||
EditorToolManager.Add(this);
|
||||
|
||||
SetupRectSelector();
|
||||
HandleActivation();
|
||||
|
||||
ToolManager.activeToolChanged += HandleActivation;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
EditorToolManager.Remove(this);
|
||||
|
||||
ToolManager.activeToolChanged -= HandleActivation;
|
||||
UnregisterCallbacks();
|
||||
}
|
||||
|
||||
private void HandleActivation()
|
||||
{
|
||||
if (m_IsActive == false && ToolManager.IsActiveTool(this))
|
||||
Activate();
|
||||
else if (m_IsActive)
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
private void Activate()
|
||||
{
|
||||
m_IsActive = true;
|
||||
RegisterCallbacks();
|
||||
InitializeCache();
|
||||
OnActivate();
|
||||
}
|
||||
|
||||
private void Deactivate()
|
||||
{
|
||||
OnDeactivate();
|
||||
DestroyCache();
|
||||
UnregisterCallbacks();
|
||||
m_IsActive = false;
|
||||
}
|
||||
|
||||
private void RegisterCallbacks()
|
||||
{
|
||||
UnregisterCallbacks();
|
||||
Selection.selectionChanged += SelectionChanged;
|
||||
EditorApplication.playModeStateChanged += PlayModeStateChanged;
|
||||
Undo.undoRedoPerformed += UndoRedoPerformed;
|
||||
}
|
||||
|
||||
private void UnregisterCallbacks()
|
||||
{
|
||||
Selection.selectionChanged -= SelectionChanged;
|
||||
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
|
||||
Undo.undoRedoPerformed -= UndoRedoPerformed;
|
||||
}
|
||||
|
||||
private void DestroyCache()
|
||||
{
|
||||
foreach (var pair in m_Paths)
|
||||
{
|
||||
var path = pair.Value;
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
Undo.ClearUndo(path);
|
||||
UnityObject.DestroyImmediate(path);
|
||||
}
|
||||
}
|
||||
m_Paths.Clear();
|
||||
m_Controller.ClearPaths();
|
||||
m_GUISystems.Clear();
|
||||
m_SerializedObjects.Clear();
|
||||
}
|
||||
|
||||
private void UndoRedoPerformed()
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
|
||||
if (!path.modified)
|
||||
InitializePath(target);
|
||||
});
|
||||
}
|
||||
|
||||
private void SelectionChanged()
|
||||
{
|
||||
InitializeCache();
|
||||
}
|
||||
|
||||
private void PlayModeStateChanged(PlayModeStateChange stateChange)
|
||||
{
|
||||
if (stateChange == PlayModeStateChange.EnteredEditMode)
|
||||
EditorApplication.delayCall += () => { InitializeCache(); }; //HACK: At this point target is null. Let's wait to next frame to refresh.
|
||||
}
|
||||
|
||||
private void SetupRectSelector()
|
||||
{
|
||||
m_RectSelector.onSelectionBegin = BeginSelection;
|
||||
m_RectSelector.onSelectionChanged = UpdateSelection;
|
||||
m_RectSelector.onSelectionEnd = EndSelection;
|
||||
}
|
||||
|
||||
private void ForEachTarget(Action<UnityObject> action)
|
||||
{
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (target == null)
|
||||
continue;
|
||||
|
||||
action(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeCache()
|
||||
{
|
||||
m_Controller.ClearPaths();
|
||||
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetOrCreatePath(target);
|
||||
var pointCount = path.pointCount;
|
||||
|
||||
InitializePath(target);
|
||||
|
||||
if (pointCount != path.pointCount)
|
||||
path.selection.Clear();
|
||||
|
||||
CreateGUISystem(target);
|
||||
|
||||
m_Controller.AddPath(path);
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializePath(UnityObject target)
|
||||
{
|
||||
IShape shape = null;
|
||||
ControlPoint[] controlPoints = null;
|
||||
|
||||
try
|
||||
{
|
||||
shape = GetShape(target);
|
||||
controlPoints = shape.ToControlPoints();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.Message);
|
||||
}
|
||||
|
||||
var path = GetPath(target);
|
||||
path.Clear();
|
||||
|
||||
if (shape != null && controlPoints != null)
|
||||
{
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
path.shapeType = shape.type;
|
||||
path.isOpenEnded = shape.isOpenEnded;
|
||||
|
||||
foreach (var controlPoint in controlPoints)
|
||||
path.AddPoint(controlPoint);
|
||||
}
|
||||
|
||||
Initialize(path, GetSerializedObject(target));
|
||||
}
|
||||
|
||||
private T GetOrCreatePath(UnityObject targetObject)
|
||||
{
|
||||
var path = GetPath(targetObject);
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
path = ScriptableObject.CreateInstance<T>();
|
||||
path.owner = targetObject;
|
||||
m_Paths[targetObject] = path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private GUISystem GetGUISystem(UnityObject target)
|
||||
{
|
||||
GUISystem guiSystem;
|
||||
m_GUISystems.TryGetValue(target, out guiSystem);
|
||||
return guiSystem;
|
||||
}
|
||||
|
||||
private void CreateGUISystem(UnityObject target)
|
||||
{
|
||||
var guiSystem = new GUISystem(m_GUIState);
|
||||
var view = new EditablePathView();
|
||||
|
||||
view.controller = m_Controller;
|
||||
view.Install(guiSystem);
|
||||
|
||||
m_GUISystems[target] = guiSystem;
|
||||
}
|
||||
|
||||
private SerializedObject GetSerializedObject(UnityObject target)
|
||||
{
|
||||
var serializedObject = default(SerializedObject);
|
||||
|
||||
if (!m_SerializedObjects.TryGetValue(target, out serializedObject))
|
||||
{
|
||||
serializedObject = new SerializedObject(target);
|
||||
m_SerializedObjects[target] = serializedObject;
|
||||
}
|
||||
|
||||
return serializedObject;
|
||||
}
|
||||
|
||||
void IDuringSceneGuiTool.DuringSceneGui(SceneView sceneView)
|
||||
{
|
||||
if (m_GUIState.eventType == EventType.Layout)
|
||||
m_Controller.ClearClosestPath();
|
||||
|
||||
m_RectSelector.OnGUI();
|
||||
|
||||
bool changed = false;
|
||||
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
path.localToWorldMatrix = GetLocalToWorldMatrix(target);
|
||||
path.forward = GetForward(target);
|
||||
path.up = GetUp(target);
|
||||
path.right = GetRight(target);
|
||||
m_Controller.editablePath = path;
|
||||
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
GetGUISystem(target).OnGUI();
|
||||
OnCustomGUI(path);
|
||||
changed |= check.changed;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (changed)
|
||||
{
|
||||
SetShapes();
|
||||
RepaintInspectors();
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginSelection(ISelector<Vector3> selector, bool isAdditive)
|
||||
{
|
||||
m_Controller.RegisterUndo("Selection");
|
||||
|
||||
if (isAdditive)
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
path.selection.BeginSelection();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateSelection(selector);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSelection(ISelector<Vector3> selector)
|
||||
{
|
||||
var repaintInspectors = false;
|
||||
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
|
||||
repaintInspectors |= path.Select(selector);
|
||||
});
|
||||
|
||||
if (repaintInspectors)
|
||||
RepaintInspectors();
|
||||
}
|
||||
|
||||
private void EndSelection(ISelector<Vector3> selector)
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
var path = GetPath(target);
|
||||
path.selection.EndSelection(true);
|
||||
});
|
||||
}
|
||||
|
||||
internal void SetShapes()
|
||||
{
|
||||
ForEachTarget((target) =>
|
||||
{
|
||||
SetPath(target);
|
||||
});
|
||||
}
|
||||
|
||||
private Transform GetTransform(UnityObject target)
|
||||
{
|
||||
return (target as Component).transform;
|
||||
}
|
||||
|
||||
private Matrix4x4 GetLocalToWorldMatrix(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).localToWorldMatrix;
|
||||
}
|
||||
|
||||
private Vector3 GetForward(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).forward;
|
||||
}
|
||||
|
||||
private Vector3 GetUp(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).up;
|
||||
}
|
||||
|
||||
private Vector3 GetRight(UnityObject target)
|
||||
{
|
||||
return GetTransform(target).right;
|
||||
}
|
||||
|
||||
protected abstract IShape GetShape(UnityObject target);
|
||||
protected virtual void Initialize(T path, SerializedObject serializedObject) {}
|
||||
protected abstract void SetShape(T path, SerializedObject serializedObject);
|
||||
protected virtual void OnActivate() {}
|
||||
protected virtual void OnDeactivate() {}
|
||||
protected virtual void OnCustomGUI(T path) {}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 0618
|
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal static class PathEditorToolExtensions
|
||||
{
|
||||
public static void CycleTangentMode<T>(this PathEditorTool<T> pathEditorTool) where T : ScriptablePath
|
||||
{
|
||||
var first = true;
|
||||
var mixed = false;
|
||||
var tangentMode = TangentMode.Linear;
|
||||
var targets = pathEditorTool.targets;
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var path = pathEditorTool.GetPath(target);
|
||||
|
||||
if (path.selection.Count == 0)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
if (!path.selection.Contains(i))
|
||||
continue;
|
||||
|
||||
var point = path.GetPoint(i);
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
tangentMode = point.tangentMode;
|
||||
}
|
||||
else if (point.tangentMode != tangentMode)
|
||||
{
|
||||
mixed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mixed)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mixed)
|
||||
tangentMode = TangentMode.Linear;
|
||||
else
|
||||
tangentMode = GetNextTangentMode(tangentMode);
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var path = pathEditorTool.GetPath(target);
|
||||
|
||||
if (path.selection.Count == 0)
|
||||
continue;
|
||||
|
||||
path.undoObject.RegisterUndo("Cycle Tangent Mode");
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
if (!path.selection.Contains(i))
|
||||
continue;
|
||||
|
||||
path.SetTangentMode(i, tangentMode);
|
||||
}
|
||||
|
||||
pathEditorTool.SetPath(target);
|
||||
}
|
||||
}
|
||||
|
||||
public static void MirrorTangent<T>(this PathEditorTool<T> pathEditorTool) where T : ScriptablePath
|
||||
{
|
||||
var targets = pathEditorTool.targets;
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var path = pathEditorTool.GetPath(target);
|
||||
|
||||
if (path.selection.Count == 0)
|
||||
continue;
|
||||
|
||||
path.undoObject.RegisterUndo("Mirror Tangents");
|
||||
|
||||
for (var i = 0; i < path.pointCount; ++i)
|
||||
{
|
||||
if (!path.selection.Contains(i))
|
||||
continue;
|
||||
|
||||
path.MirrorTangent(i);
|
||||
}
|
||||
|
||||
pathEditorTool.SetPath(target);
|
||||
}
|
||||
}
|
||||
|
||||
private static TangentMode GetNextTangentMode(TangentMode tangentMode)
|
||||
{
|
||||
return (TangentMode)((((int)tangentMode) + 1) % Enum.GetValues(typeof(TangentMode)).Length);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class ScriptableData<T> : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
private T m_Data;
|
||||
public UnityObject owner { get; set; }
|
||||
public int index { get; set; }
|
||||
|
||||
public T data
|
||||
{
|
||||
get { return m_Data; }
|
||||
set { m_Data = value; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class ScriptablePath : ScriptableObject, IEditablePath, IUndoObject
|
||||
{
|
||||
[SerializeField]
|
||||
private EditablePath m_EditablePath = new EditablePath();
|
||||
[SerializeField]
|
||||
private bool m_Modified = false;
|
||||
|
||||
internal bool modified
|
||||
{
|
||||
get { return m_Modified; }
|
||||
}
|
||||
|
||||
internal UnityObject owner { get; set; }
|
||||
|
||||
public ShapeType shapeType
|
||||
{
|
||||
get { return m_EditablePath.shapeType; }
|
||||
set { m_EditablePath.shapeType = value; }
|
||||
}
|
||||
|
||||
public IUndoObject undoObject
|
||||
{
|
||||
get { return this; }
|
||||
set {}
|
||||
}
|
||||
|
||||
public ISelection<int> selection
|
||||
{
|
||||
get { return m_EditablePath.selection; }
|
||||
}
|
||||
|
||||
public Matrix4x4 localToWorldMatrix
|
||||
{
|
||||
get { return m_EditablePath.localToWorldMatrix; }
|
||||
set { m_EditablePath.localToWorldMatrix = value; }
|
||||
}
|
||||
|
||||
public Vector3 forward
|
||||
{
|
||||
get { return m_EditablePath.forward; }
|
||||
set { m_EditablePath.forward = value; }
|
||||
}
|
||||
|
||||
public Vector3 up
|
||||
{
|
||||
get { return m_EditablePath.up; }
|
||||
set { m_EditablePath.up = value; }
|
||||
}
|
||||
|
||||
public Vector3 right
|
||||
{
|
||||
get { return m_EditablePath.right; }
|
||||
set { m_EditablePath.right = value; }
|
||||
}
|
||||
|
||||
public bool isOpenEnded
|
||||
{
|
||||
get { return m_EditablePath.isOpenEnded; }
|
||||
set { m_EditablePath.isOpenEnded = value; }
|
||||
}
|
||||
|
||||
public int pointCount
|
||||
{
|
||||
get { return m_EditablePath.pointCount; }
|
||||
}
|
||||
|
||||
public bool Select(ISelector<Vector3> selector)
|
||||
{
|
||||
return m_EditablePath.Select(selector);
|
||||
}
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
m_EditablePath.Clear();
|
||||
}
|
||||
|
||||
public virtual ControlPoint GetPoint(int index)
|
||||
{
|
||||
return m_EditablePath.GetPoint(index);
|
||||
}
|
||||
|
||||
public virtual void SetPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
m_EditablePath.SetPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
public virtual void AddPoint(ControlPoint controlPoint)
|
||||
{
|
||||
m_EditablePath.AddPoint(controlPoint);
|
||||
}
|
||||
|
||||
public virtual void InsertPoint(int index, ControlPoint controlPoint)
|
||||
{
|
||||
m_EditablePath.InsertPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
public virtual void RemovePoint(int index)
|
||||
{
|
||||
m_EditablePath.RemovePoint(index);
|
||||
}
|
||||
|
||||
void IUndoObject.RegisterUndo(string name)
|
||||
{
|
||||
Undo.RegisterCompleteObjectUndo(this, name);
|
||||
m_Modified = true;
|
||||
}
|
||||
|
||||
public virtual void SetDefaultShape()
|
||||
{
|
||||
m_Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(ScriptablePath), true)]
|
||||
internal class ScriptablePathInspector : Editor
|
||||
{
|
||||
private static class Contents
|
||||
{
|
||||
public static readonly GUIContent linearIcon = IconContent("TangentLinear", "TangentLinearPro", "Linear");
|
||||
public static readonly GUIContent continuousIcon = IconContent("TangentContinuous", "TangentContinuousPro", "Continuous");
|
||||
public static readonly GUIContent brokenIcon = IconContent("TangentBroken", "TangentBrokenPro", "Broken");
|
||||
public static readonly GUIContent positionLabel = new GUIContent("Position", "Position of the Control Point");
|
||||
public static readonly GUIContent enableSnapLabel = new GUIContent("Snapping", "Snap points using the snap settings");
|
||||
public static readonly GUIContent tangentModeLabel = new GUIContent("Tangent Mode");
|
||||
public static readonly GUIContent pointLabel = new GUIContent("Point");
|
||||
|
||||
|
||||
private static GUIContent IconContent(string name, string tooltip = null)
|
||||
{
|
||||
return new GUIContent(Resources.Load<Texture>(name), tooltip);
|
||||
}
|
||||
|
||||
private static GUIContent IconContent(string personal, string pro, string tooltip)
|
||||
{
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
return IconContent(pro, tooltip);
|
||||
|
||||
return IconContent(personal, tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ScriptablePath> m_Paths = null;
|
||||
private bool m_Dragged = false;
|
||||
|
||||
protected List<ScriptablePath> paths
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Paths == null)
|
||||
m_Paths = targets.Select(t => t as ScriptablePath).ToList();
|
||||
|
||||
return m_Paths;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DoTangentModeInspector();
|
||||
DoPositionInspector();
|
||||
}
|
||||
|
||||
protected void DoTangentModeInspector()
|
||||
{
|
||||
if (!IsAnyShapeType(ShapeType.Spline))
|
||||
return;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel(Contents.tangentModeLabel);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(!IsAnyPointSelected()))
|
||||
{
|
||||
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Linear), Contents.linearIcon))
|
||||
SetMixedTangentMode(TangentMode.Linear);
|
||||
|
||||
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Continuous), Contents.continuousIcon))
|
||||
SetMixedTangentMode(TangentMode.Continuous);
|
||||
|
||||
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Broken), Contents.brokenIcon))
|
||||
SetMixedTangentMode(TangentMode.Broken);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
protected void DoPositionInspector()
|
||||
{
|
||||
var showMixedValue = EditorGUI.showMixedValue;
|
||||
var wideMode = EditorGUIUtility.wideMode;
|
||||
|
||||
var position = Vector3.zero;
|
||||
var isMixed = GetMixedPosition(out position);
|
||||
|
||||
EditorGUI.showMixedValue = isMixed;
|
||||
EditorGUIUtility.wideMode = true;
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(!IsAnyPointSelected()))
|
||||
{
|
||||
if (GUIUtility.hotControl == 0)
|
||||
m_Dragged = false;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var delta = EditorGUILayout.Vector2Field(Contents.positionLabel, position) - (Vector2)position;
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (m_Dragged == false)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
path.undoObject.RegisterUndo("Point Position");
|
||||
|
||||
m_Dragged = true;
|
||||
}
|
||||
|
||||
SetMixedDeltaPosition(delta);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = showMixedValue;
|
||||
EditorGUIUtility.wideMode = wideMode;
|
||||
}
|
||||
|
||||
private bool DoToggle(bool value, GUIContent icon)
|
||||
{
|
||||
const float kButtonWidth = 33f;
|
||||
const float kButtonHeight = 23f;
|
||||
var buttonStyle = new GUIStyle("EditModeSingleButton");
|
||||
|
||||
var changed = false;
|
||||
using (var check = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
value = GUILayout.Toggle(value, icon, buttonStyle, GUILayout.Width(kButtonWidth), GUILayout.Height(kButtonHeight));
|
||||
changed = check.changed;
|
||||
}
|
||||
|
||||
return value && changed;
|
||||
}
|
||||
|
||||
private bool GetToggleStateFromTangentMode(TangentMode mode)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var selection = path.selection;
|
||||
|
||||
foreach (var index in selection.elements)
|
||||
if (path.GetPoint(index).tangentMode != mode)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetMixedTangentMode(TangentMode tangentMode)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
path.undoObject.RegisterUndo("Tangent Mode");
|
||||
|
||||
foreach (var index in path.selection.elements)
|
||||
path.SetTangentMode(index, tangentMode);
|
||||
}
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
private bool GetMixedPosition(out Vector3 position)
|
||||
{
|
||||
var first = true;
|
||||
position = Vector3.zero;
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var selection = path.selection;
|
||||
var matrix = path.localToWorldMatrix;
|
||||
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
foreach (var index in selection.elements)
|
||||
{
|
||||
var controlPoint = path.GetPoint(index);
|
||||
|
||||
if (first)
|
||||
{
|
||||
position = controlPoint.position;
|
||||
first = false;
|
||||
}
|
||||
else if (position != controlPoint.position)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
path.localToWorldMatrix = matrix;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetMixedDeltaPosition(Vector3 delta)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var selection = path.selection;
|
||||
var matrix = path.localToWorldMatrix;
|
||||
|
||||
path.localToWorldMatrix = Matrix4x4.identity;
|
||||
|
||||
foreach (var index in selection.elements)
|
||||
{
|
||||
var controlPoint = path.GetPoint(index);
|
||||
controlPoint.position += delta;
|
||||
path.SetPoint(index, controlPoint);
|
||||
}
|
||||
|
||||
path.localToWorldMatrix = matrix;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAnyShapeType(ShapeType shapeType)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
if (path.shapeType == shapeType)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool IsAnyPointSelected()
|
||||
{
|
||||
foreach (var path in paths)
|
||||
if (path.selection.Count > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal class ClickAction : HoveredControlAction
|
||||
{
|
||||
private int m_Button;
|
||||
private bool m_UseEvent;
|
||||
|
||||
public Action<IGUIState, Control> onClick;
|
||||
|
||||
public ClickAction(Control control, int button, bool useEvent = true) : base(control)
|
||||
{
|
||||
m_Button = button;
|
||||
m_UseEvent = useEvent;
|
||||
}
|
||||
|
||||
protected override bool GetTriggerContidtion(IGUIState guiState)
|
||||
{
|
||||
return guiState.mouseButton == m_Button && guiState.eventType == EventType.MouseDown;
|
||||
}
|
||||
|
||||
protected override void OnTrigger(IGUIState guiState)
|
||||
{
|
||||
base.OnTrigger(guiState);
|
||||
|
||||
if (onClick != null)
|
||||
onClick(guiState, hoveredControl);
|
||||
|
||||
if (m_UseEvent)
|
||||
guiState.UseCurrentEvent();
|
||||
}
|
||||
|
||||
protected override bool GetFinishContidtion(IGUIState guiState)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal class CommandAction : GUIAction
|
||||
{
|
||||
private string m_CommandName;
|
||||
|
||||
public Action<IGUIState> onCommand;
|
||||
|
||||
public CommandAction(string commandName)
|
||||
{
|
||||
m_CommandName = commandName;
|
||||
}
|
||||
|
||||
protected override bool GetTriggerContidtion(IGUIState guiState)
|
||||
{
|
||||
if (guiState.eventType == EventType.ValidateCommand && guiState.commandName == m_CommandName)
|
||||
{
|
||||
guiState.UseCurrentEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool GetFinishContidtion(IGUIState guiState)
|
||||
{
|
||||
if (guiState.eventType == EventType.ExecuteCommand && guiState.commandName == m_CommandName)
|
||||
{
|
||||
guiState.UseCurrentEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnFinish(IGUIState guiState)
|
||||
{
|
||||
if (onCommand != null)
|
||||
onCommand(guiState);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal abstract class Control
|
||||
{
|
||||
private string m_Name;
|
||||
private int m_NameHashCode;
|
||||
private int m_ID;
|
||||
private LayoutData m_LayoutData;
|
||||
private int m_ActionID = -1;
|
||||
private LayoutData m_HotLayoutData;
|
||||
|
||||
public string name
|
||||
{
|
||||
get { return m_Name; }
|
||||
}
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return m_ID; }
|
||||
}
|
||||
|
||||
public int actionID
|
||||
{
|
||||
get { return m_ActionID; }
|
||||
}
|
||||
|
||||
public LayoutData layoutData
|
||||
{
|
||||
get { return m_LayoutData; }
|
||||
set { m_LayoutData = value; }
|
||||
}
|
||||
|
||||
public LayoutData hotLayoutData
|
||||
{
|
||||
get { return m_HotLayoutData; }
|
||||
}
|
||||
|
||||
public Control(string name)
|
||||
{
|
||||
m_Name = name;
|
||||
m_NameHashCode = name.GetHashCode();
|
||||
}
|
||||
|
||||
public void GetControl(IGUIState guiState)
|
||||
{
|
||||
m_ID = guiState.GetControlID(m_NameHashCode, FocusType.Passive);
|
||||
}
|
||||
|
||||
internal void SetActionID(int actionID)
|
||||
{
|
||||
m_ActionID = actionID;
|
||||
m_HotLayoutData = m_LayoutData;
|
||||
}
|
||||
|
||||
public void BeginLayout(IGUIState guiState)
|
||||
{
|
||||
Debug.Assert(guiState.eventType == EventType.Layout);
|
||||
|
||||
m_LayoutData = OnBeginLayout(LayoutData.zero, guiState);
|
||||
}
|
||||
|
||||
public void Layout(IGUIState guiState)
|
||||
{
|
||||
Debug.Assert(guiState.eventType == EventType.Layout);
|
||||
|
||||
for (var i = 0; i < GetCount(); ++i)
|
||||
{
|
||||
if (guiState.hotControl == actionID && hotLayoutData.index == i)
|
||||
continue;
|
||||
|
||||
var layoutData = new LayoutData()
|
||||
{
|
||||
index = i,
|
||||
position = GetPosition(guiState, i),
|
||||
distance = GetDistance(guiState, i),
|
||||
forward = GetForward(guiState, i),
|
||||
up = GetUp(guiState, i),
|
||||
right = GetRight(guiState, i),
|
||||
userData = GetUserData(guiState, i)
|
||||
};
|
||||
|
||||
m_LayoutData = LayoutData.Nearest(m_LayoutData, layoutData);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndLayout(IGUIState guiState)
|
||||
{
|
||||
Debug.Assert(guiState.eventType == EventType.Layout);
|
||||
|
||||
OnEndLayout(guiState);
|
||||
}
|
||||
|
||||
public void Repaint(IGUIState guiState)
|
||||
{
|
||||
for (var i = 0; i < GetCount(); ++i)
|
||||
OnRepaint(guiState, i);
|
||||
}
|
||||
|
||||
protected virtual LayoutData OnBeginLayout(LayoutData data, IGUIState guiState)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
protected virtual void OnEndLayout(IGUIState guiState)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnRepaint(IGUIState guiState, int index)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual int GetCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected virtual Vector3 GetPosition(IGUIState guiState, int index)
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
protected virtual Vector3 GetForward(IGUIState guiState, int index)
|
||||
{
|
||||
return Vector3.forward;
|
||||
}
|
||||
|
||||
protected virtual Vector3 GetUp(IGUIState guiState, int index)
|
||||
{
|
||||
return Vector3.up;
|
||||
}
|
||||
|
||||
protected virtual Vector3 GetRight(IGUIState guiState, int index)
|
||||
{
|
||||
return Vector3.right;
|
||||
}
|
||||
|
||||
protected virtual float GetDistance(IGUIState guiState, int index)
|
||||
{
|
||||
return layoutData.distance;
|
||||
}
|
||||
|
||||
protected virtual object GetUserData(IGUIState guiState, int index)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal abstract class DefaultControl : Control
|
||||
{
|
||||
public static readonly float kPickDistance = 5f;
|
||||
|
||||
public DefaultControl(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
protected override LayoutData OnBeginLayout(LayoutData data, IGUIState guiState)
|
||||
{
|
||||
data.distance = kPickDistance;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal abstract class GUIAction
|
||||
{
|
||||
private int m_ID = -1;
|
||||
|
||||
public Func<IGUIState, GUIAction, bool> enable;
|
||||
public Func<IGUIState, GUIAction, bool> enableRepaint;
|
||||
public Func<IGUIState, GUIAction, bool> repaintOnMouseMove;
|
||||
public Action<IGUIState, GUIAction> onPreRepaint;
|
||||
public Action<IGUIState, GUIAction> onRepaint;
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return m_ID; }
|
||||
}
|
||||
|
||||
public void OnGUI(IGUIState guiState)
|
||||
{
|
||||
m_ID = guiState.GetControlID(GetType().GetHashCode(), FocusType.Passive);
|
||||
|
||||
if (guiState.hotControl == 0 && IsEnabled(guiState) && CanTrigger(guiState) && GetTriggerContidtion(guiState))
|
||||
{
|
||||
guiState.hotControl = ID;
|
||||
OnTrigger(guiState);
|
||||
}
|
||||
|
||||
if (guiState.hotControl == ID)
|
||||
{
|
||||
if (GetFinishContidtion(guiState))
|
||||
{
|
||||
OnFinish(guiState);
|
||||
guiState.hotControl = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnPerform(guiState);
|
||||
}
|
||||
}
|
||||
|
||||
if (guiState.eventType == EventType.Repaint && IsRepaintEnabled(guiState))
|
||||
Repaint(guiState);
|
||||
}
|
||||
|
||||
public bool IsEnabled(IGUIState guiState)
|
||||
{
|
||||
if (enable != null)
|
||||
return enable(guiState, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsRepaintEnabled(IGUIState guiState)
|
||||
{
|
||||
if (!IsEnabled(guiState))
|
||||
return false;
|
||||
|
||||
if (enableRepaint != null)
|
||||
return enableRepaint(guiState, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void PreRepaint(IGUIState guiState)
|
||||
{
|
||||
Debug.Assert(guiState.eventType == EventType.Repaint);
|
||||
|
||||
if (IsEnabled(guiState) && onPreRepaint != null)
|
||||
onPreRepaint(guiState, this);
|
||||
}
|
||||
|
||||
private void Repaint(IGUIState guiState)
|
||||
{
|
||||
Debug.Assert(guiState.eventType == EventType.Repaint);
|
||||
|
||||
if (onRepaint != null)
|
||||
onRepaint(guiState, this);
|
||||
}
|
||||
|
||||
internal bool IsRepaintOnMouseMoveEnabled(IGUIState guiState)
|
||||
{
|
||||
if (!IsEnabled(guiState) || !IsRepaintEnabled(guiState))
|
||||
return false;
|
||||
|
||||
if (repaintOnMouseMove != null)
|
||||
return repaintOnMouseMove(guiState, this);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract bool GetFinishContidtion(IGUIState guiState);
|
||||
protected abstract bool GetTriggerContidtion(IGUIState guiState);
|
||||
protected virtual bool CanTrigger(IGUIState guiState) { return true; }
|
||||
protected virtual void OnTrigger(IGUIState guiState)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnPerform(IGUIState guiState)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnFinish(IGUIState guiState)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,165 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal class GUIState : IGUIState
|
||||
{
|
||||
private Handles.CapFunction nullCap = (int c, Vector3 p , Quaternion r, float s, EventType ev) => {};
|
||||
|
||||
public Vector2 mousePosition
|
||||
{
|
||||
get { return Event.current.mousePosition; }
|
||||
}
|
||||
|
||||
public int mouseButton
|
||||
{
|
||||
get { return Event.current.button; }
|
||||
}
|
||||
|
||||
public int clickCount
|
||||
{
|
||||
get { return Event.current.clickCount; }
|
||||
}
|
||||
|
||||
public bool isShiftDown
|
||||
{
|
||||
get { return Event.current.shift; }
|
||||
}
|
||||
|
||||
public bool isAltDown
|
||||
{
|
||||
get { return Event.current.alt; }
|
||||
}
|
||||
|
||||
public bool isActionKeyDown
|
||||
{
|
||||
get { return EditorGUI.actionKey; }
|
||||
}
|
||||
|
||||
public KeyCode keyCode
|
||||
{
|
||||
get { return Event.current.keyCode; }
|
||||
}
|
||||
|
||||
public EventType eventType
|
||||
{
|
||||
get { return Event.current.type; }
|
||||
}
|
||||
|
||||
public string commandName
|
||||
{
|
||||
get { return Event.current.commandName; }
|
||||
}
|
||||
|
||||
public int nearestControl
|
||||
{
|
||||
get { return HandleUtility.nearestControl; }
|
||||
set { HandleUtility.nearestControl = value; }
|
||||
}
|
||||
|
||||
public int hotControl
|
||||
{
|
||||
get { return GUIUtility.hotControl; }
|
||||
set { GUIUtility.hotControl = value; }
|
||||
}
|
||||
|
||||
public bool changed
|
||||
{
|
||||
get { return GUI.changed; }
|
||||
set { GUI.changed = value; }
|
||||
}
|
||||
|
||||
public int GetControlID(int hint, FocusType focusType)
|
||||
{
|
||||
return GUIUtility.GetControlID(hint, focusType);
|
||||
}
|
||||
|
||||
public void AddControl(int controlID, float distance)
|
||||
{
|
||||
HandleUtility.AddControl(controlID, distance);
|
||||
}
|
||||
|
||||
public bool Slider(int id, SliderData sliderData, out Vector3 newPosition)
|
||||
{
|
||||
if (mouseButton == 0 && eventType == EventType.MouseDown)
|
||||
{
|
||||
hotControl = 0;
|
||||
nearestControl = id;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
newPosition = Handles.Slider2D(id, sliderData.position, sliderData.forward, sliderData.right, sliderData.up, 1f, nullCap, Vector2.zero);
|
||||
return EditorGUI.EndChangeCheck();
|
||||
}
|
||||
|
||||
public void UseCurrentEvent()
|
||||
{
|
||||
Event.current.Use();
|
||||
}
|
||||
|
||||
public void Repaint()
|
||||
{
|
||||
HandleUtility.Repaint();
|
||||
}
|
||||
|
||||
public bool IsEventOutsideWindow()
|
||||
{
|
||||
return Event.current.type == EventType.Ignore;
|
||||
}
|
||||
|
||||
public bool IsViewToolActive()
|
||||
{
|
||||
return UnityEditor.Tools.current == Tool.View || isAltDown || mouseButton == 1 || mouseButton == 2;
|
||||
}
|
||||
|
||||
public bool HasCurrentCamera()
|
||||
{
|
||||
return Camera.current != null;
|
||||
}
|
||||
|
||||
public float GetHandleSize(Vector3 position)
|
||||
{
|
||||
var scale = HasCurrentCamera() ? 0.01f : 0.05f;
|
||||
return HandleUtility.GetHandleSize(position) * scale;
|
||||
}
|
||||
|
||||
public float DistanceToSegment(Vector3 p1, Vector3 p2)
|
||||
{
|
||||
p1 = HandleUtility.WorldToGUIPoint(p1);
|
||||
p2 = HandleUtility.WorldToGUIPoint(p2);
|
||||
|
||||
return HandleUtility.DistancePointToLineSegment(Event.current.mousePosition, p1, p2);
|
||||
}
|
||||
|
||||
public float DistanceToCircle(Vector3 center, float radius)
|
||||
{
|
||||
return HandleUtility.DistanceToCircle(center, radius);
|
||||
}
|
||||
|
||||
public Vector3 GUIToWorld(Vector2 guiPosition, Vector3 planeNormal, Vector3 planePos)
|
||||
{
|
||||
Vector3 worldPos = Handles.inverseMatrix.MultiplyPoint(guiPosition);
|
||||
|
||||
if (Camera.current)
|
||||
{
|
||||
Ray ray = HandleUtility.GUIPointToWorldRay(guiPosition);
|
||||
|
||||
planeNormal = Handles.matrix.MultiplyVector(planeNormal);
|
||||
|
||||
planePos = Handles.matrix.MultiplyPoint(planePos);
|
||||
|
||||
Plane plane = new Plane(planeNormal, planePos);
|
||||
|
||||
float distance = 0f;
|
||||
|
||||
if (plane.Raycast(ray, out distance))
|
||||
{
|
||||
worldPos = Handles.inverseMatrix.MultiplyPoint(ray.GetPoint(distance));
|
||||
}
|
||||
}
|
||||
|
||||
return worldPos;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal class GUISystem
|
||||
{
|
||||
private readonly int kControlIDCheckHashCode = "ControlIDCheckHashCode".GetHashCode();
|
||||
|
||||
private List<Control> m_Controls = new List<Control>();
|
||||
private List<GUIAction> m_Actions = new List<GUIAction>();
|
||||
private IGUIState m_GUIState;
|
||||
private int m_PrevNearestControl = -1;
|
||||
private LayoutData m_PrevNearestLayoutData = LayoutData.zero;
|
||||
private int m_ControlIDCheck = -1;
|
||||
|
||||
public GUISystem(IGUIState guiState)
|
||||
{
|
||||
m_GUIState = guiState;
|
||||
}
|
||||
|
||||
public void AddControl(Control control)
|
||||
{
|
||||
if (control == null)
|
||||
throw new NullReferenceException("Control is null");
|
||||
|
||||
m_Controls.Add(control);
|
||||
}
|
||||
|
||||
public void RemoveControl(Control control)
|
||||
{
|
||||
m_Controls.Remove(control);
|
||||
}
|
||||
|
||||
public void AddAction(GUIAction action)
|
||||
{
|
||||
if (action == null)
|
||||
throw new NullReferenceException("Action is null");
|
||||
|
||||
m_Actions.Add(action);
|
||||
}
|
||||
|
||||
public void RemoveAction(GUIAction action)
|
||||
{
|
||||
m_Actions.Remove(action);
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
var controlIDCheck = m_GUIState.GetControlID(kControlIDCheckHashCode, FocusType.Passive);
|
||||
|
||||
if (m_GUIState.eventType == EventType.Layout)
|
||||
m_ControlIDCheck = controlIDCheck;
|
||||
else if (m_GUIState.eventType != EventType.Used && m_ControlIDCheck != controlIDCheck)
|
||||
Debug.LogWarning("GetControlID at event " + m_GUIState.eventType + " returns a controlID different from the one in Layout event");
|
||||
|
||||
var nearestLayoutData = LayoutData.zero;
|
||||
|
||||
foreach (var control in m_Controls)
|
||||
control.GetControl(m_GUIState);
|
||||
|
||||
if (m_GUIState.eventType == EventType.Layout)
|
||||
{
|
||||
foreach (var control in m_Controls)
|
||||
control.BeginLayout(m_GUIState);
|
||||
|
||||
foreach (var control in m_Controls)
|
||||
{
|
||||
control.Layout(m_GUIState);
|
||||
nearestLayoutData = LayoutData.Nearest(nearestLayoutData, control.layoutData);
|
||||
}
|
||||
|
||||
foreach (var control in m_Controls)
|
||||
m_GUIState.AddControl(control.ID, control.layoutData.distance);
|
||||
|
||||
foreach (var control in m_Controls)
|
||||
control.EndLayout(m_GUIState);
|
||||
|
||||
if (m_PrevNearestControl == m_GUIState.nearestControl)
|
||||
{
|
||||
if (nearestLayoutData.index != m_PrevNearestLayoutData.index)
|
||||
m_GUIState.Repaint();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PrevNearestControl = m_GUIState.nearestControl;
|
||||
m_GUIState.Repaint();
|
||||
}
|
||||
|
||||
m_PrevNearestLayoutData = nearestLayoutData;
|
||||
}
|
||||
|
||||
if (m_GUIState.eventType == EventType.Repaint)
|
||||
{
|
||||
foreach (var action in m_Actions)
|
||||
if (action.IsRepaintEnabled(m_GUIState))
|
||||
action.PreRepaint(m_GUIState);
|
||||
|
||||
foreach (var control in m_Controls)
|
||||
control.Repaint(m_GUIState);
|
||||
}
|
||||
|
||||
var repaintOnMouseMove = false;
|
||||
|
||||
foreach (var action in m_Actions)
|
||||
{
|
||||
if (IsMouseMoveEvent())
|
||||
repaintOnMouseMove |= action.IsRepaintOnMouseMoveEnabled(m_GUIState);
|
||||
|
||||
action.OnGUI(m_GUIState);
|
||||
}
|
||||
|
||||
if (repaintOnMouseMove)
|
||||
m_GUIState.UseCurrentEvent();
|
||||
}
|
||||
|
||||
private bool IsMouseMoveEvent()
|
||||
{
|
||||
return m_GUIState.eventType == EventType.MouseMove || m_GUIState.eventType == EventType.MouseDrag;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal class GenericControl : Control
|
||||
{
|
||||
public Func<IGUIState, LayoutData> onBeginLayout = null;
|
||||
public Action<IGUIState> onEndLayout = null;
|
||||
public Action<IGUIState, Control, int> onRepaint;
|
||||
public Func<int> count;
|
||||
public Func<int, Vector3> position;
|
||||
public Func<IGUIState, int, float> distance;
|
||||
public Func<int, Vector3> forward;
|
||||
public Func<int, Vector3> up;
|
||||
public Func<int, Vector3> right;
|
||||
public Func<int, object> userData = null;
|
||||
|
||||
public GenericControl(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
protected override int GetCount()
|
||||
{
|
||||
if (count != null)
|
||||
return count();
|
||||
|
||||
return base.GetCount();
|
||||
}
|
||||
|
||||
protected override void OnEndLayout(IGUIState guiState)
|
||||
{
|
||||
if (onEndLayout != null)
|
||||
onEndLayout(guiState);
|
||||
}
|
||||
|
||||
protected override void OnRepaint(IGUIState guiState, int index)
|
||||
{
|
||||
if (onRepaint != null)
|
||||
onRepaint(guiState, this, index);
|
||||
}
|
||||
|
||||
protected override LayoutData OnBeginLayout(LayoutData data, IGUIState guiState)
|
||||
{
|
||||
if (onBeginLayout != null)
|
||||
return onBeginLayout(guiState);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
protected override Vector3 GetPosition(IGUIState guiState, int index)
|
||||
{
|
||||
if (position != null)
|
||||
return position(index);
|
||||
|
||||
return base.GetPosition(guiState, index);
|
||||
}
|
||||
|
||||
protected override float GetDistance(IGUIState guiState, int index)
|
||||
{
|
||||
if (distance != null)
|
||||
return distance(guiState, index);
|
||||
|
||||
return base.GetDistance(guiState, index);
|
||||
}
|
||||
|
||||
protected override Vector3 GetForward(IGUIState guiState, int index)
|
||||
{
|
||||
if (forward != null)
|
||||
return forward(index);
|
||||
|
||||
return base.GetForward(guiState, index);
|
||||
}
|
||||
|
||||
protected override Vector3 GetUp(IGUIState guiState, int index)
|
||||
{
|
||||
if (up != null)
|
||||
return up(index);
|
||||
|
||||
return base.GetUp(guiState, index);
|
||||
}
|
||||
|
||||
protected override Vector3 GetRight(IGUIState guiState, int index)
|
||||
{
|
||||
if (right != null)
|
||||
return right(index);
|
||||
|
||||
return base.GetRight(guiState, index);
|
||||
}
|
||||
|
||||
protected override object GetUserData(IGUIState guiState, int index)
|
||||
{
|
||||
if (userData != null)
|
||||
return userData(index);
|
||||
|
||||
return base.GetUserData(guiState, index);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal class GenericDefaultControl : DefaultControl
|
||||
{
|
||||
public Func<IGUIState, Vector3> position;
|
||||
public Func<IGUIState, Vector3> forward;
|
||||
public Func<IGUIState, Vector3> up;
|
||||
public Func<IGUIState, Vector3> right;
|
||||
public Func<IGUIState, object> userData = null;
|
||||
|
||||
public GenericDefaultControl(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Vector3 GetPosition(IGUIState guiState, int index)
|
||||
{
|
||||
if (position != null)
|
||||
return position(guiState);
|
||||
|
||||
return base.GetPosition(guiState, index);
|
||||
}
|
||||
|
||||
protected override Vector3 GetForward(IGUIState guiState, int index)
|
||||
{
|
||||
if (forward != null)
|
||||
return forward(guiState);
|
||||
|
||||
return base.GetForward(guiState, index);
|
||||
}
|
||||
|
||||
protected override Vector3 GetUp(IGUIState guiState, int index)
|
||||
{
|
||||
if (up != null)
|
||||
return up(guiState);
|
||||
|
||||
return base.GetUp(guiState, index);
|
||||
}
|
||||
|
||||
protected override Vector3 GetRight(IGUIState guiState, int index)
|
||||
{
|
||||
if (right != null)
|
||||
return right(guiState);
|
||||
|
||||
return base.GetRight(guiState, index);
|
||||
}
|
||||
|
||||
protected override object GetUserData(IGUIState guiState, int index)
|
||||
{
|
||||
if (userData != null)
|
||||
return userData(guiState);
|
||||
|
||||
return base.GetUserData(guiState, index);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal abstract class HoveredControlAction : GUIAction
|
||||
{
|
||||
private Control m_HoveredControl;
|
||||
|
||||
public Control hoveredControl
|
||||
{
|
||||
get { return m_HoveredControl; }
|
||||
}
|
||||
|
||||
public HoveredControlAction(Control control)
|
||||
{
|
||||
m_HoveredControl = control;
|
||||
}
|
||||
|
||||
protected override bool CanTrigger(IGUIState guiState)
|
||||
{
|
||||
return guiState.nearestControl == hoveredControl.ID;
|
||||
}
|
||||
|
||||
protected override void OnTrigger(IGUIState guiState)
|
||||
{
|
||||
m_HoveredControl.SetActionID(ID);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal struct SliderData
|
||||
{
|
||||
public Vector3 position;
|
||||
public Vector3 forward;
|
||||
public Vector3 up;
|
||||
public Vector3 right;
|
||||
|
||||
public static readonly SliderData zero = new SliderData() { position = Vector3.zero, forward = Vector3.forward, up = Vector3.up, right = Vector3.right };
|
||||
}
|
||||
|
||||
internal interface IGUIState
|
||||
{
|
||||
Vector2 mousePosition { get; }
|
||||
int mouseButton { get; }
|
||||
int clickCount { get; }
|
||||
bool isShiftDown { get; }
|
||||
bool isAltDown { get; }
|
||||
bool isActionKeyDown { get; }
|
||||
KeyCode keyCode { get; }
|
||||
EventType eventType { get; }
|
||||
string commandName { get; }
|
||||
int nearestControl { get; set; }
|
||||
int hotControl { get; set; }
|
||||
bool changed { get; set; }
|
||||
int GetControlID(int hint, FocusType focusType);
|
||||
void AddControl(int controlID, float distance);
|
||||
bool Slider(int id, SliderData sliderData, out Vector3 newPosition);
|
||||
void UseCurrentEvent();
|
||||
void Repaint();
|
||||
bool IsEventOutsideWindow();
|
||||
bool IsViewToolActive();
|
||||
bool HasCurrentCamera();
|
||||
float GetHandleSize(Vector3 position);
|
||||
float DistanceToSegment(Vector3 p1, Vector3 p2);
|
||||
float DistanceToCircle(Vector3 center, float radius);
|
||||
Vector3 GUIToWorld(Vector2 guiPosition, Vector3 planeNormal, Vector3 planePos);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal struct LayoutData
|
||||
{
|
||||
public int index;
|
||||
public float distance;
|
||||
public Vector3 position;
|
||||
public Vector3 forward;
|
||||
public Vector3 up;
|
||||
public Vector3 right;
|
||||
public object userData;
|
||||
|
||||
public static readonly LayoutData zero = new LayoutData() { index = 0, distance = float.MaxValue, position = Vector3.zero, forward = Vector3.forward, up = Vector3.up, right = Vector3.right };
|
||||
|
||||
public static LayoutData Nearest(LayoutData currentData, LayoutData newData)
|
||||
{
|
||||
if (newData.distance <= currentData.distance)
|
||||
return newData;
|
||||
|
||||
return currentData;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
|
||||
{
|
||||
internal class SliderAction : ClickAction
|
||||
{
|
||||
private SliderData m_SliderData;
|
||||
|
||||
public Action<IGUIState, Control, Vector3> onSliderBegin;
|
||||
public Action<IGUIState, Control, Vector3> onSliderChanged;
|
||||
public Action<IGUIState, Control, Vector3> onSliderEnd;
|
||||
|
||||
public SliderAction(Control control) : base(control, 0, false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool GetFinishContidtion(IGUIState guiState)
|
||||
{
|
||||
return guiState.eventType == EventType.MouseUp && guiState.mouseButton == 0;
|
||||
}
|
||||
|
||||
protected override void OnTrigger(IGUIState guiState)
|
||||
{
|
||||
base.OnTrigger(guiState);
|
||||
|
||||
m_SliderData.position = hoveredControl.hotLayoutData.position;
|
||||
m_SliderData.forward = hoveredControl.hotLayoutData.forward;
|
||||
m_SliderData.right = hoveredControl.hotLayoutData.right;
|
||||
m_SliderData.up = hoveredControl.hotLayoutData.up;
|
||||
|
||||
if (onSliderBegin != null)
|
||||
onSliderBegin(guiState, hoveredControl, m_SliderData.position);
|
||||
}
|
||||
|
||||
protected override void OnFinish(IGUIState guiState)
|
||||
{
|
||||
if (onSliderEnd != null)
|
||||
onSliderEnd(guiState, hoveredControl, m_SliderData.position);
|
||||
|
||||
guiState.UseCurrentEvent();
|
||||
guiState.Repaint();
|
||||
}
|
||||
|
||||
protected override void OnPerform(IGUIState guiState)
|
||||
{
|
||||
Vector3 newPosition;
|
||||
var changed = guiState.Slider(ID, m_SliderData, out newPosition);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
m_SliderData.position = newPosition;
|
||||
|
||||
if (onSliderChanged != null)
|
||||
onSliderChanged(guiState, hoveredControl, newPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 444 B |
After Width: | Height: | Size: 507 B |
After Width: | Height: | Size: 414 B |
After Width: | Height: | Size: 474 B |
After Width: | Height: | Size: 403 B |
After Width: | Height: | Size: 326 B |
After Width: | Height: | Size: 363 B |
After Width: | Height: | Size: 373 B |
After Width: | Height: | Size: 366 B |
After Width: | Height: | Size: 322 B |
After Width: | Height: | Size: 325 B |
After Width: | Height: | Size: 351 B |
After Width: | Height: | Size: 381 B |
@@ -0,0 +1,7 @@
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface ISelectable<T>
|
||||
{
|
||||
bool Select(ISelector<T> selector);
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface ISelection<T>
|
||||
{
|
||||
int Count { get; }
|
||||
T activeElement { get; set; }
|
||||
T[] elements { get; set; }
|
||||
void Clear();
|
||||
void BeginSelection();
|
||||
void EndSelection(bool select);
|
||||
bool Select(T element, bool select);
|
||||
bool Contains(T element);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface ISelector<T>
|
||||
{
|
||||
bool Select(T element);
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
[Serializable]
|
||||
internal class IndexedSelection : SerializableSelection<int>
|
||||
{
|
||||
protected override int GetInvalidElement() { return -1; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class PointRectSelector : RectSelector<Vector3>
|
||||
{
|
||||
protected override bool Select(Vector3 element)
|
||||
{
|
||||
return guiRect.Contains(HandleUtility.WorldToGUIPoint(element), true);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal abstract class RectSelector<T> : ISelector<T>
|
||||
{
|
||||
public Action<ISelector<T>, bool> onSelectionBegin;
|
||||
public Action<ISelector<T>> onSelectionChanged;
|
||||
public Action<ISelector<T>> onSelectionEnd = null;
|
||||
|
||||
private GUISystem m_GUISystem;
|
||||
private Control m_RectSelectorControl;
|
||||
private GUIAction m_RectSelectAction;
|
||||
private Vector3 m_RectStart;
|
||||
private Vector3 m_RectEnd;
|
||||
private Rect m_GUIRect;
|
||||
private IDrawer m_Drawer = new Drawer();
|
||||
|
||||
public Rect guiRect
|
||||
{
|
||||
get { return m_GUIRect; }
|
||||
}
|
||||
|
||||
public RectSelector() : this(new GUISystem(new GUIState())) {}
|
||||
|
||||
public RectSelector(GUISystem guiSystem)
|
||||
{
|
||||
m_GUISystem = guiSystem;
|
||||
|
||||
m_RectSelectorControl = new GenericDefaultControl("RectSelector")
|
||||
{
|
||||
position = (guiState) =>
|
||||
{
|
||||
return GUIToWorld(guiState, guiState.mousePosition);
|
||||
},
|
||||
forward = (guiState) =>
|
||||
{
|
||||
if (Camera.current)
|
||||
return Camera.current.transform.forward;
|
||||
|
||||
return Vector3.forward;
|
||||
},
|
||||
right = (guiState) =>
|
||||
{
|
||||
if (Camera.current)
|
||||
return Camera.current.transform.right;
|
||||
|
||||
return Vector3.right;
|
||||
},
|
||||
up = (guiState) =>
|
||||
{
|
||||
if (Camera.current)
|
||||
return Camera.current.transform.up;
|
||||
|
||||
return Vector3.up;
|
||||
}
|
||||
};
|
||||
|
||||
m_RectSelectAction = new SliderAction(m_RectSelectorControl)
|
||||
{
|
||||
enableRepaint = (guiState, action) =>
|
||||
{
|
||||
var size = m_RectStart - m_RectEnd;
|
||||
return size != Vector3.zero && guiState.hotControl == action.ID;
|
||||
},
|
||||
onClick = (guiState, control) =>
|
||||
{
|
||||
m_RectStart = GUIToWorld(guiState, guiState.mousePosition);
|
||||
m_RectEnd = m_RectStart;
|
||||
m_GUIRect = CalculateGUIRect();
|
||||
},
|
||||
onSliderBegin = (guiState, control, position) =>
|
||||
{
|
||||
m_RectEnd = position;
|
||||
m_GUIRect = CalculateGUIRect();
|
||||
|
||||
if (onSelectionBegin != null)
|
||||
onSelectionBegin(this, guiState.isShiftDown);
|
||||
},
|
||||
onSliderChanged = (guiState, control, position) =>
|
||||
{
|
||||
m_RectEnd = position;
|
||||
m_GUIRect = CalculateGUIRect();
|
||||
|
||||
if (onSelectionChanged != null)
|
||||
onSelectionChanged(this);
|
||||
},
|
||||
onSliderEnd = (guiState, control, position) =>
|
||||
{
|
||||
if (onSelectionEnd != null)
|
||||
onSelectionEnd(this);
|
||||
},
|
||||
onRepaint = (guiState, action) =>
|
||||
{
|
||||
m_Drawer.DrawSelectionRect(m_GUIRect);
|
||||
}
|
||||
};
|
||||
|
||||
m_GUISystem.AddControl(m_RectSelectorControl);
|
||||
m_GUISystem.AddAction(m_RectSelectAction);
|
||||
}
|
||||
|
||||
private Vector3 GUIToWorld(IGUIState guiState, Vector2 guiPosition)
|
||||
{
|
||||
var forward = Vector3.forward;
|
||||
|
||||
if (guiState.HasCurrentCamera())
|
||||
forward = Camera.current.transform.forward;
|
||||
|
||||
return guiState.GUIToWorld(guiPosition, forward, Vector3.zero);
|
||||
}
|
||||
|
||||
private Rect CalculateGUIRect()
|
||||
{
|
||||
return FromToRect(HandleUtility.WorldToGUIPoint(m_RectStart), HandleUtility.WorldToGUIPoint(m_RectEnd));
|
||||
}
|
||||
|
||||
private Rect FromToRect(Vector2 start, Vector2 end)
|
||||
{
|
||||
Rect r = new Rect(start.x, start.y, end.x - start.x, end.y - start.y);
|
||||
if (r.width < 0)
|
||||
{
|
||||
r.x += r.width;
|
||||
r.width = -r.width;
|
||||
}
|
||||
if (r.height < 0)
|
||||
{
|
||||
r.y += r.height;
|
||||
r.height = -r.height;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
m_GUISystem.OnGUI();
|
||||
}
|
||||
|
||||
bool ISelector<T>.Select(T element)
|
||||
{
|
||||
return Select(element);
|
||||
}
|
||||
|
||||
protected abstract bool Select(T element);
|
||||
}
|
||||
}
|
@@ -0,0 +1,143 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
[Serializable]
|
||||
internal abstract class SerializableSelection<T> : ISelection<T>, ISerializationCallbackReceiver
|
||||
{
|
||||
internal readonly static int kInvalidID = -1;
|
||||
|
||||
[SerializeField]
|
||||
private T[] m_Keys = new T[0];
|
||||
|
||||
private HashSet<T> m_Selection = new HashSet<T>();
|
||||
private HashSet<T> m_TemporalSelection = new HashSet<T>();
|
||||
private bool m_SelectionInProgress = false;
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_Selection.Count + m_TemporalSelection.Count; }
|
||||
}
|
||||
|
||||
public T activeElement
|
||||
{
|
||||
get { return First(); }
|
||||
set
|
||||
{
|
||||
Clear();
|
||||
Select(value, true);
|
||||
}
|
||||
}
|
||||
|
||||
public T[] elements
|
||||
{
|
||||
get
|
||||
{
|
||||
var set = m_Selection;
|
||||
|
||||
if (m_SelectionInProgress)
|
||||
{
|
||||
var union = new HashSet<T>(m_Selection);
|
||||
union.UnionWith(m_TemporalSelection);
|
||||
set = union;
|
||||
}
|
||||
|
||||
return new List<T>(set).ToArray();
|
||||
}
|
||||
set
|
||||
{
|
||||
Clear();
|
||||
foreach (var element in value)
|
||||
Select(element, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract T GetInvalidElement();
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
GetSelection().Clear();
|
||||
}
|
||||
|
||||
public void BeginSelection()
|
||||
{
|
||||
m_SelectionInProgress = true;
|
||||
Clear();
|
||||
}
|
||||
|
||||
public void EndSelection(bool select)
|
||||
{
|
||||
m_SelectionInProgress = false;
|
||||
|
||||
if (select)
|
||||
m_Selection.UnionWith(m_TemporalSelection);
|
||||
else
|
||||
m_Selection.ExceptWith(m_TemporalSelection);
|
||||
|
||||
m_TemporalSelection.Clear();
|
||||
}
|
||||
|
||||
public bool Select(T element, bool select)
|
||||
{
|
||||
var changed = false;
|
||||
|
||||
if (EqualityComparer<T>.Default.Equals(element, GetInvalidElement()))
|
||||
return changed;
|
||||
|
||||
if (select)
|
||||
changed = GetSelection().Add(element);
|
||||
else if (Contains(element))
|
||||
changed = GetSelection().Remove(element);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public bool Contains(T element)
|
||||
{
|
||||
return m_Selection.Contains(element) || m_TemporalSelection.Contains(element);
|
||||
}
|
||||
|
||||
private HashSet<T> GetSelection()
|
||||
{
|
||||
if (m_SelectionInProgress)
|
||||
return m_TemporalSelection;
|
||||
|
||||
return m_Selection;
|
||||
}
|
||||
|
||||
private T First()
|
||||
{
|
||||
T element = First(m_Selection);
|
||||
|
||||
if (EqualityComparer<T>.Default.Equals(element, GetInvalidElement()))
|
||||
element = First(m_TemporalSelection);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private T First(HashSet<T> set)
|
||||
{
|
||||
if (set.Count == 0)
|
||||
return GetInvalidElement();
|
||||
|
||||
using (var enumerator = set.GetEnumerator())
|
||||
{
|
||||
Debug.Assert(enumerator.MoveNext());
|
||||
return enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{
|
||||
m_Keys = new List<T>(m_Selection).ToArray();
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
elements = m_Keys;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal enum ShapeType
|
||||
{
|
||||
Polygon,
|
||||
Spline
|
||||
}
|
||||
|
||||
internal interface IShape
|
||||
{
|
||||
ShapeType type { get; }
|
||||
bool isOpenEnded { get; }
|
||||
ControlPoint[] ToControlPoints();
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal struct Polygon : IShape
|
||||
{
|
||||
public bool isOpenEnded;
|
||||
|
||||
public Vector3[] points;
|
||||
|
||||
ShapeType IShape.type => ShapeType.Polygon;
|
||||
|
||||
bool IShape.isOpenEnded => isOpenEnded;
|
||||
|
||||
ControlPoint[] IShape.ToControlPoints()
|
||||
{
|
||||
if (points == null)
|
||||
throw new NullReferenceException("Points array is null");
|
||||
|
||||
var controlPoints = new List<ControlPoint>();
|
||||
|
||||
foreach (var point in points)
|
||||
{
|
||||
controlPoints.Add(new ControlPoint() { position = point });
|
||||
}
|
||||
|
||||
return controlPoints.ToArray();
|
||||
}
|
||||
|
||||
public static Polygon empty = new Polygon() { isOpenEnded = true, points = new Vector3[0] };
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal static class ShapeExtensions
|
||||
{
|
||||
public static Polygon ToPolygon(this Vector3[] points, bool isOpenEnded)
|
||||
{
|
||||
return new Polygon()
|
||||
{
|
||||
isOpenEnded = isOpenEnded,
|
||||
points = points
|
||||
};
|
||||
}
|
||||
|
||||
public static Spline ToSpline(this Vector3[] points, bool isOpenEnded)
|
||||
{
|
||||
if (!points.IsSpline(isOpenEnded) && points.IsSpline(!isOpenEnded))
|
||||
{
|
||||
var pointList = new List<Vector3>(points);
|
||||
|
||||
if (isOpenEnded)
|
||||
{
|
||||
while (pointList.Count % 3 != 1)
|
||||
pointList.RemoveAt(pointList.Count - 1);
|
||||
|
||||
points = pointList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
var last = pointList[pointList.Count - 1];
|
||||
var first = pointList[0];
|
||||
var v = first - last;
|
||||
|
||||
pointList.Add(last + v.normalized * (v.magnitude / 3f));
|
||||
pointList.Add(first - v.normalized * (v.magnitude / 3f));
|
||||
|
||||
points = pointList.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
if (!points.IsSpline(isOpenEnded))
|
||||
throw new Exception("The provided control point array can't conform a Spline.");
|
||||
|
||||
return new Spline()
|
||||
{
|
||||
isOpenEnded = isOpenEnded,
|
||||
points = points
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsSpline(this Vector3[] points, bool isOpenEnded)
|
||||
{
|
||||
if (points.Length < 4)
|
||||
return false;
|
||||
|
||||
if (isOpenEnded && points.Length % 3 != 1)
|
||||
return false;
|
||||
|
||||
if (!isOpenEnded && points.Length % 3 != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Spline ToSpline(this Polygon polygon)
|
||||
{
|
||||
var newPointCount = polygon.points.Length * 3;
|
||||
|
||||
if (polygon.isOpenEnded)
|
||||
newPointCount = (polygon.points.Length - 1) * 3 + 1;
|
||||
|
||||
var newPoints = new Vector3[newPointCount];
|
||||
var controlPoints = polygon.points;
|
||||
var pointCount = controlPoints.Length;
|
||||
|
||||
for (var i = 0; i < pointCount; ++i)
|
||||
{
|
||||
var nextIndex = (i + 1) % pointCount;
|
||||
var point = controlPoints[i];
|
||||
var v = controlPoints[nextIndex] - point;
|
||||
|
||||
newPoints[i * 3] = point;
|
||||
|
||||
if (i * 3 + 2 < newPointCount)
|
||||
{
|
||||
newPoints[i * 3 + 1] = point + v / 3f;
|
||||
newPoints[i * 3 + 2] = point + v * 2f / 3f;
|
||||
}
|
||||
}
|
||||
|
||||
return new Spline()
|
||||
{
|
||||
isOpenEnded = polygon.isOpenEnded,
|
||||
points = newPoints
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal struct Spline : IShape
|
||||
{
|
||||
public bool isOpenEnded;
|
||||
|
||||
public Vector3[] points;
|
||||
|
||||
ShapeType IShape.type => ShapeType.Spline;
|
||||
|
||||
bool IShape.isOpenEnded => isOpenEnded;
|
||||
|
||||
ControlPoint[] IShape.ToControlPoints()
|
||||
{
|
||||
if (points == null)
|
||||
throw new NullReferenceException("Points array is null");
|
||||
|
||||
if (!points.IsSpline(isOpenEnded))
|
||||
throw new Exception("The provided control point array can't conform a Spline.");
|
||||
|
||||
var controlPoints = new List<ControlPoint>();
|
||||
var leftTangent = Vector3.zero;
|
||||
var rightTangent = Vector3.zero;
|
||||
var pointCount = points.Length;
|
||||
|
||||
for (var i = 0; i < pointCount; i += 3)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if (isOpenEnded)
|
||||
leftTangent = points[0];
|
||||
else
|
||||
leftTangent = points[EditablePathUtility.Mod(-1, pointCount)];
|
||||
}
|
||||
|
||||
if (i == pointCount - 1 && isOpenEnded)
|
||||
rightTangent = points[i];
|
||||
else
|
||||
rightTangent = points[i + 1];
|
||||
|
||||
|
||||
controlPoints.Add(
|
||||
new ControlPoint()
|
||||
{
|
||||
position = points[i],
|
||||
leftTangent = leftTangent,
|
||||
rightTangent = rightTangent,
|
||||
tangentMode = TangentMode.Broken
|
||||
});
|
||||
|
||||
if (i == pointCount - 1 && isOpenEnded)
|
||||
leftTangent = Vector3.zero;
|
||||
else
|
||||
leftTangent = points[i + 2];
|
||||
}
|
||||
|
||||
pointCount = controlPoints.Count;
|
||||
|
||||
for (var i = 0; i < pointCount; ++i)
|
||||
{
|
||||
var prevIndex = EditablePathUtility.Mod(i - 1, pointCount);
|
||||
var nextIndex = EditablePathUtility.Mod(i + 1, pointCount);
|
||||
var controlPoint = controlPoints[i];
|
||||
var prevControlPoint = controlPoints[prevIndex];
|
||||
var nextControlPoint = controlPoints[nextIndex];
|
||||
|
||||
var liniarLeftPosition = (prevControlPoint.position - controlPoint.position) / 3f;
|
||||
var isLeftTangentLinear = (controlPoint.localLeftTangent - liniarLeftPosition).sqrMagnitude < 0.001f;
|
||||
|
||||
if (isLeftTangentLinear)
|
||||
controlPoint.localLeftTangent = Vector3.zero;
|
||||
|
||||
var liniarRightPosition = (nextControlPoint.position - controlPoint.position) / 3f;
|
||||
var isRightTangentLinear = (controlPoint.localRightTangent - liniarRightPosition).sqrMagnitude < 0.001f;
|
||||
|
||||
if (isRightTangentLinear)
|
||||
controlPoint.localRightTangent = Vector3.zero;
|
||||
|
||||
var tangentDotProduct = Vector3.Dot(controlPoint.localLeftTangent.normalized, controlPoint.localRightTangent.normalized);
|
||||
var isContinous = tangentDotProduct < 0f && (tangentDotProduct + 1) * (tangentDotProduct + 1) < 0.001f;
|
||||
|
||||
if (isLeftTangentLinear && isRightTangentLinear)
|
||||
controlPoint.tangentMode = TangentMode.Linear;
|
||||
else if (isLeftTangentLinear || isRightTangentLinear)
|
||||
controlPoint.tangentMode = TangentMode.Broken;
|
||||
else if (isContinous)
|
||||
controlPoint.tangentMode = TangentMode.Continuous;
|
||||
|
||||
controlPoints[i] = controlPoint;
|
||||
}
|
||||
|
||||
return controlPoints.ToArray();
|
||||
}
|
||||
|
||||
public static Spline empty = new Spline() { isOpenEnded = true, points = new Vector3[0] };
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class CreatePointAction : ClickAction
|
||||
{
|
||||
private Control m_PointControl;
|
||||
|
||||
public Func<IGUIState, Vector2, Vector3> guiToWorld;
|
||||
public Action<int, Vector3> onCreatePoint;
|
||||
public CreatePointAction(Control pointControl, Control edgeControl) : base(edgeControl, 0, false)
|
||||
{
|
||||
m_PointControl = pointControl;
|
||||
}
|
||||
|
||||
protected override void OnTrigger(IGUIState guiState)
|
||||
{
|
||||
base.OnTrigger(guiState);
|
||||
|
||||
var index = hoveredControl.layoutData.index;
|
||||
var position = GetMousePositionWorld(guiState);
|
||||
|
||||
if (onCreatePoint != null)
|
||||
onCreatePoint(index, position);
|
||||
|
||||
guiState.nearestControl = m_PointControl.ID;
|
||||
|
||||
var data = m_PointControl.layoutData;
|
||||
data.index = index + 1;
|
||||
data.position = position;
|
||||
data.distance = 0f;
|
||||
|
||||
m_PointControl.layoutData = data;
|
||||
|
||||
guiState.changed = true;
|
||||
}
|
||||
|
||||
private Vector3 GetMousePositionWorld(IGUIState guiState)
|
||||
{
|
||||
if (guiToWorld != null)
|
||||
return guiToWorld(guiState, guiState.mousePosition);
|
||||
|
||||
return guiState.GUIToWorld(guiState.mousePosition, hoveredControl.layoutData.forward, hoveredControl.layoutData.position);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class DefaultStyles
|
||||
{
|
||||
public readonly GUIStyle pointNormalStyle;
|
||||
public readonly GUIStyle pointHoveredStyle;
|
||||
public readonly GUIStyle pointSelectedStyle;
|
||||
public readonly GUIStyle pointPreviewStyle;
|
||||
public readonly GUIStyle pointRemovePreviewStyle;
|
||||
public readonly GUIStyle tangentNormalStyle;
|
||||
public readonly GUIStyle tangentHoveredStyle;
|
||||
public readonly GUIStyle selectionRectStyle;
|
||||
|
||||
public DefaultStyles()
|
||||
{
|
||||
var pointNormal = Resources.Load<Texture2D>("Path/pointNormal");
|
||||
var pointHovered = Resources.Load<Texture2D>("Path/pointHovered");
|
||||
var pointSelected = Resources.Load<Texture2D>("Path/pointSelected");
|
||||
var pointPreview = Resources.Load<Texture2D>("Path/pointPreview");
|
||||
var pointRemovePreview = Resources.Load<Texture2D>("Path/pointRemovePreview");
|
||||
var tangentNormal = Resources.Load<Texture2D>("Path/tangentNormal");
|
||||
|
||||
pointNormalStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointNormal"), Vector2.one * 12f);
|
||||
pointHoveredStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointHovered"), Vector2.one * 12f);
|
||||
pointSelectedStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointSelected"), Vector2.one * 12f);
|
||||
pointPreviewStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointPreview"), Vector2.one * 12f);
|
||||
pointRemovePreviewStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointRemovePreview"), Vector2.one * 12f);
|
||||
tangentNormalStyle = CreateStyle(Resources.Load<Texture2D>("Path/tangentNormal"), Vector2.one * 8f);
|
||||
tangentHoveredStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointHovered"), Vector2.one * 10f);
|
||||
|
||||
selectionRectStyle = GUI.skin.FindStyle("selectionRect");
|
||||
}
|
||||
|
||||
private GUIStyle CreateStyle(Texture2D texture, Vector2 size)
|
||||
{
|
||||
var guiStyle = new GUIStyle();
|
||||
guiStyle.normal.background = texture;
|
||||
guiStyle.fixedWidth = size.x;
|
||||
guiStyle.fixedHeight = size.y;
|
||||
|
||||
return guiStyle;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Drawer : IDrawer
|
||||
{
|
||||
private IGUIState m_GUIState = new GUIState();
|
||||
private DefaultStyles m_Styles;
|
||||
protected DefaultStyles styles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Styles == null)
|
||||
m_Styles = new DefaultStyles();
|
||||
|
||||
return m_Styles;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSelectionRect(Rect rect)
|
||||
{
|
||||
Handles.BeginGUI();
|
||||
styles.selectionRectStyle.Draw(rect, GUIContent.none, false, false, false, false);
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
public void DrawCreatePointPreview(Vector3 position)
|
||||
{
|
||||
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointPreviewStyle);
|
||||
}
|
||||
|
||||
public void DrawRemovePointPreview(Vector3 position)
|
||||
{
|
||||
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointRemovePreviewStyle);
|
||||
}
|
||||
|
||||
public void DrawPoint(Vector3 position)
|
||||
{
|
||||
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointNormalStyle);
|
||||
}
|
||||
|
||||
public void DrawPointHovered(Vector3 position)
|
||||
{
|
||||
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointHoveredStyle);
|
||||
}
|
||||
|
||||
public void DrawPointSelected(Vector3 position)
|
||||
{
|
||||
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointSelectedStyle);
|
||||
}
|
||||
|
||||
public void DrawLine(Vector3 p1, Vector3 p2, float width, Color color)
|
||||
{
|
||||
Handles.color = color;
|
||||
Handles.DrawAAPolyLine(width, new Vector3[] { p1, p2 });
|
||||
}
|
||||
|
||||
public void DrawDottedLine(Vector3 p1, Vector3 p2, float width, Color color)
|
||||
{
|
||||
Handles.color = color;
|
||||
Handles.DrawDottedLine(p1, p2, width);
|
||||
}
|
||||
|
||||
public void DrawBezier(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, float width, Color color)
|
||||
{
|
||||
Handles.color = color;
|
||||
Handles.DrawBezier(p1, p4, p2, p3, color, null, width);
|
||||
}
|
||||
|
||||
public void DrawTangent(Vector3 position, Vector3 tangent)
|
||||
{
|
||||
DrawLine(position, tangent, 3f, Color.yellow);
|
||||
DrawGUIStyleCap(0, tangent, Quaternion.identity, m_GUIState.GetHandleSize(tangent), styles.tangentNormalStyle);
|
||||
}
|
||||
|
||||
private void DrawGUIStyleCap(int controlID, Vector3 position, Quaternion rotation, float size, GUIStyle guiStyle)
|
||||
{
|
||||
if (Camera.current && Vector3.Dot(position - Camera.current.transform.position, Camera.current.transform.forward) < 0f)
|
||||
return;
|
||||
|
||||
Handles.BeginGUI();
|
||||
guiStyle.Draw(GetGUIStyleRect(guiStyle, position), GUIContent.none, controlID);
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
private Rect GetGUIStyleRect(GUIStyle style, Vector3 position)
|
||||
{
|
||||
Vector2 vector = HandleUtility.WorldToGUIPoint(position);
|
||||
|
||||
float fixedWidth = style.fixedWidth;
|
||||
float fixedHeight = style.fixedHeight;
|
||||
|
||||
return new Rect(vector.x - fixedWidth / 2f, vector.y - fixedHeight / 2f, fixedWidth, fixedHeight);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,489 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal class EditablePathView : IEditablePathView
|
||||
{
|
||||
const float kSnappingDistance = 15f;
|
||||
const string kDeleteCommandName = "Delete";
|
||||
const string kSoftDeleteCommandName = "SoftDelete";
|
||||
public IEditablePathController controller { get; set; }
|
||||
private Control m_PointControl;
|
||||
private Control m_EdgeControl;
|
||||
private Control m_LeftTangentControl;
|
||||
private Control m_RightTangentControl;
|
||||
private GUIAction m_MovePointAction;
|
||||
private GUIAction m_MoveEdgeAction;
|
||||
private GUIAction m_CreatePointAction;
|
||||
private GUIAction m_RemovePointAction1;
|
||||
private GUIAction m_RemovePointAction2;
|
||||
private GUIAction m_MoveLeftTangentAction;
|
||||
private GUIAction m_MoveRightTangentAction;
|
||||
private IDrawer m_Drawer;
|
||||
|
||||
public EditablePathView() : this(new Drawer()) {}
|
||||
|
||||
public EditablePathView(IDrawer drawer)
|
||||
{
|
||||
m_Drawer = drawer;
|
||||
|
||||
m_PointControl = new GenericControl("Point")
|
||||
{
|
||||
count = GetPointCount,
|
||||
distance = (guiState, i) =>
|
||||
{
|
||||
var position = GetPoint(i).position;
|
||||
return guiState.DistanceToCircle(position, guiState.GetHandleSize(position) * 10f);
|
||||
},
|
||||
position = (i) => { return GetPoint(i).position; },
|
||||
forward = (i) => { return GetForward(); },
|
||||
up = (i) => { return GetUp(); },
|
||||
right = (i) => { return GetRight(); },
|
||||
onRepaint = DrawPoint
|
||||
};
|
||||
|
||||
m_EdgeControl = new GenericControl("Edge")
|
||||
{
|
||||
onEndLayout = (guiState) => { controller.AddClosestPath(m_EdgeControl.layoutData.distance); },
|
||||
count = GetEdgeCount,
|
||||
distance = DistanceToEdge,
|
||||
position = (i) => { return GetPoint(i).position; },
|
||||
forward = (i) => { return GetForward(); },
|
||||
up = (i) => { return GetUp(); },
|
||||
right = (i) => { return GetRight(); },
|
||||
onRepaint = DrawEdge
|
||||
};
|
||||
|
||||
m_LeftTangentControl = new GenericControl("LeftTangent")
|
||||
{
|
||||
count = () =>
|
||||
{
|
||||
if (GetShapeType() != ShapeType.Spline)
|
||||
return 0;
|
||||
|
||||
return GetPointCount();
|
||||
},
|
||||
distance = (guiState, i) =>
|
||||
{
|
||||
if (!IsSelected(i) || IsOpenEnded() && i == 0)
|
||||
return float.MaxValue;
|
||||
|
||||
var position = GetLeftTangent(i);
|
||||
return guiState.DistanceToCircle(position, guiState.GetHandleSize(position) * 10f);
|
||||
},
|
||||
position = (i) => { return GetLeftTangent(i); },
|
||||
forward = (i) => { return GetForward(); },
|
||||
up = (i) => { return GetUp(); },
|
||||
right = (i) => { return GetRight(); },
|
||||
onRepaint = (guiState, control, i) =>
|
||||
{
|
||||
if (!IsSelected(i) || IsOpenEnded() && i == 0)
|
||||
return;
|
||||
|
||||
var position = GetPoint(i).position;
|
||||
var leftTangent = GetLeftTangent(i);
|
||||
|
||||
m_Drawer.DrawTangent(position, leftTangent);
|
||||
}
|
||||
};
|
||||
|
||||
m_RightTangentControl = new GenericControl("RightTangent")
|
||||
{
|
||||
count = () =>
|
||||
{
|
||||
if (GetShapeType() != ShapeType.Spline)
|
||||
return 0;
|
||||
|
||||
return GetPointCount();
|
||||
},
|
||||
distance = (guiState, i) =>
|
||||
{
|
||||
if (!IsSelected(i) || IsOpenEnded() && i == GetPointCount() - 1)
|
||||
return float.MaxValue;
|
||||
|
||||
var position = GetRightTangent(i);
|
||||
return guiState.DistanceToCircle(position, guiState.GetHandleSize(position) * 10f);
|
||||
},
|
||||
position = (i) => { return GetRightTangent(i); },
|
||||
forward = (i) => { return GetForward(); },
|
||||
up = (i) => { return GetUp(); },
|
||||
right = (i) => { return GetRight(); },
|
||||
onRepaint = (guiState, control, i) =>
|
||||
{
|
||||
if (!IsSelected(i) || IsOpenEnded() && i == GetPointCount() - 1)
|
||||
return;
|
||||
|
||||
var position = GetPoint(i).position;
|
||||
var rightTangent = GetRightTangent(i);
|
||||
|
||||
m_Drawer.DrawTangent(position, rightTangent);
|
||||
}
|
||||
};
|
||||
|
||||
m_CreatePointAction = new CreatePointAction(m_PointControl, m_EdgeControl)
|
||||
{
|
||||
enable = (guiState, action) => { return !guiState.isShiftDown && controller.closestEditablePath == controller.editablePath; },
|
||||
enableRepaint = EnableCreatePointRepaint,
|
||||
repaintOnMouseMove = (guiState, action) => { return true; },
|
||||
guiToWorld = GUIToWorld,
|
||||
onCreatePoint = (index, position) =>
|
||||
{
|
||||
controller.RegisterUndo("Create Point");
|
||||
controller.CreatePoint(index, position);
|
||||
},
|
||||
onPreRepaint = (guiState, action) =>
|
||||
{
|
||||
if (GetPointCount() > 0)
|
||||
{
|
||||
var position = ClosestPointInEdge(guiState, guiState.mousePosition, m_EdgeControl.layoutData.index);
|
||||
m_Drawer.DrawCreatePointPreview(position);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Action<IGUIState> removePoints = (guiState) =>
|
||||
{
|
||||
controller.RegisterUndo("Remove Point");
|
||||
controller.RemoveSelectedPoints();
|
||||
guiState.changed = true;
|
||||
};
|
||||
|
||||
m_RemovePointAction1 = new CommandAction(kDeleteCommandName)
|
||||
{
|
||||
enable = (guiState, action) => { return GetSelectedPointCount() > 0; },
|
||||
onCommand = removePoints
|
||||
};
|
||||
|
||||
m_RemovePointAction2 = new CommandAction(kSoftDeleteCommandName)
|
||||
{
|
||||
enable = (guiState, action) => { return GetSelectedPointCount() > 0; },
|
||||
onCommand = removePoints
|
||||
};
|
||||
|
||||
m_MovePointAction = new SliderAction(m_PointControl)
|
||||
{
|
||||
onClick = (guiState, control) =>
|
||||
{
|
||||
var index = control.layoutData.index;
|
||||
|
||||
if (!guiState.isActionKeyDown && !IsSelected(index))
|
||||
controller.ClearSelection();
|
||||
|
||||
controller.SelectPoint(index, true);
|
||||
guiState.changed = true;
|
||||
},
|
||||
onSliderBegin = (guiState, control, position) =>
|
||||
{
|
||||
controller.RegisterUndo("Move Point");
|
||||
},
|
||||
onSliderChanged = (guiState, control, position) =>
|
||||
{
|
||||
var index = control.hotLayoutData.index;
|
||||
var delta = SnapIfNeeded(position) - GetPoint(index).position;
|
||||
|
||||
controller.MoveSelectedPoints(delta);
|
||||
}
|
||||
};
|
||||
|
||||
m_MoveEdgeAction = new SliderAction(m_EdgeControl)
|
||||
{
|
||||
enable = (guiState, action) => { return guiState.isShiftDown; },
|
||||
onSliderBegin = (guiState, control, position) =>
|
||||
{
|
||||
controller.RegisterUndo("Move Edge");
|
||||
},
|
||||
onSliderChanged = (guiState, control, position) =>
|
||||
{
|
||||
var index = control.hotLayoutData.index;
|
||||
var delta = position - GetPoint(index).position;
|
||||
|
||||
controller.MoveEdge(index, delta);
|
||||
}
|
||||
};
|
||||
|
||||
var cachedRightTangent = Vector3.zero;
|
||||
var cachedLeftTangent = Vector3.zero;
|
||||
|
||||
m_MoveLeftTangentAction = new SliderAction(m_LeftTangentControl)
|
||||
{
|
||||
onSliderBegin = (guiState, control, position) =>
|
||||
{
|
||||
controller.RegisterUndo("Move Tangent");
|
||||
cachedRightTangent = GetPoint(control.hotLayoutData.index).rightTangent;
|
||||
},
|
||||
onSliderChanged = (guiState, control, position) =>
|
||||
{
|
||||
var index = control.hotLayoutData.index;
|
||||
var setToLinear = guiState.nearestControl == m_PointControl.ID && m_PointControl.layoutData.index == index;
|
||||
|
||||
controller.SetLeftTangent(index, position, setToLinear, guiState.isShiftDown, cachedRightTangent);
|
||||
},
|
||||
onSliderEnd = (guiState, control, position) =>
|
||||
{
|
||||
controller.editablePath.UpdateTangentMode(control.hotLayoutData.index);
|
||||
guiState.changed = true;
|
||||
}
|
||||
};
|
||||
|
||||
m_MoveRightTangentAction = new SliderAction(m_RightTangentControl)
|
||||
{
|
||||
onSliderBegin = (guiState, control, position) =>
|
||||
{
|
||||
controller.RegisterUndo("Move Tangent");
|
||||
cachedLeftTangent = GetPoint(control.hotLayoutData.index).leftTangent;
|
||||
},
|
||||
onSliderChanged = (guiState, control, position) =>
|
||||
{
|
||||
var index = control.hotLayoutData.index;
|
||||
var setToLinear = guiState.nearestControl == m_PointControl.ID && m_PointControl.layoutData.index == index;
|
||||
|
||||
controller.SetRightTangent(index, position, setToLinear, guiState.isShiftDown, cachedLeftTangent);
|
||||
},
|
||||
onSliderEnd = (guiState, control, position) =>
|
||||
{
|
||||
controller.editablePath.UpdateTangentMode(control.hotLayoutData.index);
|
||||
guiState.changed = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Install(GUISystem guiSystem)
|
||||
{
|
||||
guiSystem.AddControl(m_EdgeControl);
|
||||
guiSystem.AddControl(m_PointControl);
|
||||
guiSystem.AddControl(m_LeftTangentControl);
|
||||
guiSystem.AddControl(m_RightTangentControl);
|
||||
guiSystem.AddAction(m_CreatePointAction);
|
||||
guiSystem.AddAction(m_RemovePointAction1);
|
||||
guiSystem.AddAction(m_RemovePointAction2);
|
||||
guiSystem.AddAction(m_MovePointAction);
|
||||
guiSystem.AddAction(m_MoveEdgeAction);
|
||||
guiSystem.AddAction(m_MoveLeftTangentAction);
|
||||
guiSystem.AddAction(m_MoveRightTangentAction);
|
||||
}
|
||||
|
||||
public void Uninstall(GUISystem guiSystem)
|
||||
{
|
||||
guiSystem.RemoveControl(m_EdgeControl);
|
||||
guiSystem.RemoveControl(m_PointControl);
|
||||
guiSystem.RemoveControl(m_LeftTangentControl);
|
||||
guiSystem.RemoveControl(m_RightTangentControl);
|
||||
guiSystem.RemoveAction(m_CreatePointAction);
|
||||
guiSystem.RemoveAction(m_RemovePointAction1);
|
||||
guiSystem.RemoveAction(m_RemovePointAction2);
|
||||
guiSystem.RemoveAction(m_MovePointAction);
|
||||
guiSystem.RemoveAction(m_MoveEdgeAction);
|
||||
guiSystem.RemoveAction(m_MoveLeftTangentAction);
|
||||
guiSystem.RemoveAction(m_MoveRightTangentAction);
|
||||
}
|
||||
|
||||
private ControlPoint GetPoint(int index)
|
||||
{
|
||||
return controller.editablePath.GetPoint(index);
|
||||
}
|
||||
|
||||
private int GetPointCount()
|
||||
{
|
||||
return controller.editablePath.pointCount;
|
||||
}
|
||||
|
||||
private int GetEdgeCount()
|
||||
{
|
||||
if (controller.editablePath.isOpenEnded)
|
||||
return controller.editablePath.pointCount - 1;
|
||||
|
||||
return controller.editablePath.pointCount;
|
||||
}
|
||||
|
||||
private int GetSelectedPointCount()
|
||||
{
|
||||
return controller.editablePath.selection.Count;
|
||||
}
|
||||
|
||||
private bool IsSelected(int index)
|
||||
{
|
||||
return controller.editablePath.selection.Contains(index);
|
||||
}
|
||||
|
||||
private Vector3 GetForward()
|
||||
{
|
||||
return controller.editablePath.forward;
|
||||
}
|
||||
|
||||
private Vector3 GetUp()
|
||||
{
|
||||
return controller.editablePath.up;
|
||||
}
|
||||
|
||||
private Vector3 GetRight()
|
||||
{
|
||||
return controller.editablePath.right;
|
||||
}
|
||||
|
||||
private Matrix4x4 GetLocalToWorldMatrix()
|
||||
{
|
||||
return controller.editablePath.localToWorldMatrix;
|
||||
}
|
||||
|
||||
private ShapeType GetShapeType()
|
||||
{
|
||||
return controller.editablePath.shapeType;
|
||||
}
|
||||
|
||||
private bool IsOpenEnded()
|
||||
{
|
||||
return controller.editablePath.isOpenEnded;
|
||||
}
|
||||
|
||||
private Vector3 GetLeftTangent(int index)
|
||||
{
|
||||
return controller.editablePath.CalculateLeftTangent(index);
|
||||
}
|
||||
|
||||
private Vector3 GetRightTangent(int index)
|
||||
{
|
||||
return controller.editablePath.CalculateRightTangent(index);
|
||||
}
|
||||
|
||||
private int NextIndex(int index)
|
||||
{
|
||||
return EditablePathUtility.Mod(index + 1, GetPointCount());
|
||||
}
|
||||
|
||||
private ControlPoint NextControlPoint(int index)
|
||||
{
|
||||
return GetPoint(NextIndex(index));
|
||||
}
|
||||
|
||||
private int PrevIndex(int index)
|
||||
{
|
||||
return EditablePathUtility.Mod(index - 1, GetPointCount());
|
||||
}
|
||||
|
||||
private ControlPoint PrevControlPoint(int index)
|
||||
{
|
||||
return GetPoint(PrevIndex(index));
|
||||
}
|
||||
|
||||
private Vector3 ClosestPointInEdge(IGUIState guiState, Vector2 mousePosition, int index)
|
||||
{
|
||||
if (GetShapeType() == ShapeType.Polygon)
|
||||
{
|
||||
var p0 = GetPoint(index).position;
|
||||
var p1 = NextControlPoint(index).position;
|
||||
var mouseWorldPosition = GUIToWorld(guiState, mousePosition);
|
||||
|
||||
var dir1 = (mouseWorldPosition - p0);
|
||||
var dir2 = (p1 - p0);
|
||||
|
||||
return Mathf.Clamp01(Vector3.Dot(dir1, dir2.normalized) / dir2.magnitude) * dir2 + p0;
|
||||
}
|
||||
else if (GetShapeType() == ShapeType.Spline)
|
||||
{
|
||||
var nextIndex = NextIndex(index);
|
||||
float t;
|
||||
return BezierUtility.ClosestPointOnCurve(
|
||||
GUIToWorld(guiState, mousePosition),
|
||||
GetPoint(index).position,
|
||||
GetPoint(nextIndex).position,
|
||||
GetRightTangent(index),
|
||||
GetLeftTangent(nextIndex),
|
||||
out t);
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
private float DistanceToEdge(IGUIState guiState, int index)
|
||||
{
|
||||
if (GetShapeType() == ShapeType.Polygon)
|
||||
{
|
||||
return guiState.DistanceToSegment(GetPoint(index).position, NextControlPoint(index).position);
|
||||
}
|
||||
else if (GetShapeType() == ShapeType.Spline)
|
||||
{
|
||||
var closestPoint = ClosestPointInEdge(guiState, guiState.mousePosition, index);
|
||||
var closestPoint2 = HandleUtility.WorldToGUIPoint(closestPoint);
|
||||
|
||||
return (closestPoint2 - guiState.mousePosition).magnitude;
|
||||
}
|
||||
|
||||
return float.MaxValue;
|
||||
}
|
||||
|
||||
private Vector3 GUIToWorld(IGUIState guiState, Vector2 position)
|
||||
{
|
||||
return guiState.GUIToWorld(position, GetForward(), GetLocalToWorldMatrix().MultiplyPoint3x4(Vector3.zero));
|
||||
}
|
||||
|
||||
private void DrawPoint(IGUIState guiState, Control control, int index)
|
||||
{
|
||||
var position = GetPoint(index).position;
|
||||
|
||||
if (guiState.hotControl == control.actionID && control.hotLayoutData.index == index || IsSelected(index))
|
||||
m_Drawer.DrawPointSelected(position);
|
||||
else if (guiState.hotControl == 0 && guiState.nearestControl == control.ID && control.layoutData.index == index)
|
||||
m_Drawer.DrawPointHovered(position);
|
||||
else
|
||||
m_Drawer.DrawPoint(position);
|
||||
}
|
||||
|
||||
private void DrawEdge(IGUIState guiState, Control control, int index)
|
||||
{
|
||||
if (GetShapeType() == ShapeType.Polygon)
|
||||
{
|
||||
var nextIndex = NextIndex(index);
|
||||
var color = Color.white;
|
||||
|
||||
if (guiState.nearestControl == control.ID && control.layoutData.index == index && guiState.hotControl == 0)
|
||||
color = Color.yellow;
|
||||
|
||||
m_Drawer.DrawLine(GetPoint(index).position, GetPoint(nextIndex).position, 5f, color);
|
||||
}
|
||||
else if (GetShapeType() == ShapeType.Spline)
|
||||
{
|
||||
var nextIndex = NextIndex(index);
|
||||
var color = Color.white;
|
||||
|
||||
if (guiState.nearestControl == control.ID && control.layoutData.index == index && guiState.hotControl == 0)
|
||||
color = Color.yellow;
|
||||
|
||||
m_Drawer.DrawBezier(
|
||||
GetPoint(index).position,
|
||||
GetRightTangent(index),
|
||||
GetLeftTangent(nextIndex),
|
||||
GetPoint(nextIndex).position,
|
||||
5f,
|
||||
color);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnableCreatePointRepaint(IGUIState guiState, GUIAction action)
|
||||
{
|
||||
return guiState.nearestControl != m_PointControl.ID &&
|
||||
guiState.hotControl == 0 &&
|
||||
(guiState.nearestControl != m_LeftTangentControl.ID) &&
|
||||
(guiState.nearestControl != m_RightTangentControl.ID);
|
||||
}
|
||||
|
||||
private Vector3 SnapIfNeeded(Vector3 position)
|
||||
{
|
||||
if (!controller.enableSnapping || controller.snapping == null)
|
||||
return position;
|
||||
|
||||
var guiPosition = HandleUtility.WorldToGUIPoint(position);
|
||||
var snappedGuiPosition = HandleUtility.WorldToGUIPoint(controller.snapping.Snap(position));
|
||||
var sqrDistance = (guiPosition - snappedGuiPosition).sqrMagnitude;
|
||||
|
||||
if (sqrDistance < kSnappingDistance * kSnappingDistance)
|
||||
position = controller.snapping.Snap(position);
|
||||
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface IDrawer
|
||||
{
|
||||
void DrawSelectionRect(Rect rect);
|
||||
void DrawCreatePointPreview(Vector3 position);
|
||||
void DrawRemovePointPreview(Vector3 position);
|
||||
void DrawPoint(Vector3 position);
|
||||
void DrawPointHovered(Vector3 position);
|
||||
void DrawPointSelected(Vector3 position);
|
||||
void DrawLine(Vector3 p1, Vector3 p2, float width, Color color);
|
||||
void DrawDottedLine(Vector3 p1, Vector3 p2, float width, Color color);
|
||||
void DrawBezier(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, float width, Color color);
|
||||
void DrawTangent(Vector3 position, Vector3 tangent);
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
|
||||
{
|
||||
internal interface IEditablePathView
|
||||
{
|
||||
IEditablePathController controller { get; set; }
|
||||
void Install(GUISystem guiSystem);
|
||||
void Uninstall(GUISystem guiSystem);
|
||||
}
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Experimental.Rendering.Universal
|
||||
{
|
||||
internal class SortingLayerDropDown
|
||||
{
|
||||
private class LayerSelectionData
|
||||
{
|
||||
public SerializedObject serializedObject;
|
||||
public Object[] targets;
|
||||
public int layerID;
|
||||
public System.Action<SerializedObject> onSelectionChanged;
|
||||
|
||||
public LayerSelectionData(SerializedObject so, int lid, Object[] tgts, System.Action<SerializedObject> selectionChangedCallback)
|
||||
{
|
||||
serializedObject = so;
|
||||
layerID = lid;
|
||||
targets = tgts;
|
||||
onSelectionChanged = selectionChangedCallback;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Styles
|
||||
{
|
||||
public static GUIContent sortingLayerAll = EditorGUIUtility.TrTextContent("All");
|
||||
public static GUIContent sortingLayerNone = EditorGUIUtility.TrTextContent("None");
|
||||
public static GUIContent sortingLayerMixed = EditorGUIUtility.TrTextContent("Mixed...");
|
||||
}
|
||||
|
||||
SortingLayer[] m_AllSortingLayers;
|
||||
GUIContent[] m_AllSortingLayerNames;
|
||||
List<int> m_ApplyToSortingLayersList;
|
||||
SerializedProperty m_ApplyToSortingLayers;
|
||||
|
||||
public void OnEnable(SerializedObject serializedObject, string propertyName)
|
||||
{
|
||||
m_ApplyToSortingLayers = serializedObject.FindProperty(propertyName);
|
||||
m_ApplyToSortingLayersList = new List<int>(m_ApplyToSortingLayers.arraySize);
|
||||
|
||||
m_AllSortingLayers = SortingLayer.layers;
|
||||
m_AllSortingLayerNames = m_AllSortingLayers.Select(x => new GUIContent(x.name)).ToArray();
|
||||
}
|
||||
|
||||
void UpdateApplyToSortingLayersArray(object layerSelectionDataObject)
|
||||
{
|
||||
LayerSelectionData layerSelectionData = (LayerSelectionData)layerSelectionDataObject;
|
||||
|
||||
m_ApplyToSortingLayers.ClearArray();
|
||||
for (int i = 0; i < m_ApplyToSortingLayersList.Count; ++i)
|
||||
{
|
||||
m_ApplyToSortingLayers.InsertArrayElementAtIndex(i);
|
||||
m_ApplyToSortingLayers.GetArrayElementAtIndex(i).intValue = m_ApplyToSortingLayersList[i];
|
||||
}
|
||||
|
||||
if (layerSelectionData.onSelectionChanged != null)
|
||||
layerSelectionData.onSelectionChanged(layerSelectionData.serializedObject);
|
||||
|
||||
layerSelectionData.serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (layerSelectionData.targets is Light2D[])
|
||||
{
|
||||
foreach (Light2D light in layerSelectionData.targets)
|
||||
{
|
||||
if (light != null && light.lightType == Light2D.LightType.Global)
|
||||
Light2DManager.ErrorIfDuplicateGlobalLight(light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnNoSortingLayerSelected(object selectionData)
|
||||
{
|
||||
m_ApplyToSortingLayersList.Clear();
|
||||
UpdateApplyToSortingLayersArray(selectionData);
|
||||
}
|
||||
|
||||
void OnAllSortingLayersSelected(object selectionData)
|
||||
{
|
||||
m_ApplyToSortingLayersList.Clear();
|
||||
m_ApplyToSortingLayersList.AddRange(m_AllSortingLayers.Select(x => x.id));
|
||||
UpdateApplyToSortingLayersArray(selectionData);
|
||||
}
|
||||
|
||||
void OnSortingLayerSelected(object layerSelectionDataObject)
|
||||
{
|
||||
LayerSelectionData layerSelectionData = (LayerSelectionData)layerSelectionDataObject;
|
||||
|
||||
int layerID = (int)layerSelectionData.layerID;
|
||||
|
||||
if (m_ApplyToSortingLayersList.Contains(layerID))
|
||||
m_ApplyToSortingLayersList.RemoveAll(id => id == layerID);
|
||||
else
|
||||
m_ApplyToSortingLayersList.Add(layerID);
|
||||
|
||||
UpdateApplyToSortingLayersArray(layerSelectionDataObject);
|
||||
}
|
||||
|
||||
public void OnTargetSortingLayers(SerializedObject serializedObject, Object[] targets, GUIContent labelContent, System.Action<SerializedObject> selectionChangedCallback)
|
||||
{
|
||||
Rect totalPosition = EditorGUILayout.GetControlRect();
|
||||
GUIContent actualLabel = EditorGUI.BeginProperty(totalPosition, labelContent, m_ApplyToSortingLayers);
|
||||
Rect position = EditorGUI.PrefixLabel(totalPosition, actualLabel);
|
||||
|
||||
m_ApplyToSortingLayersList.Clear();
|
||||
int applyToSortingLayersSize = m_ApplyToSortingLayers.arraySize;
|
||||
for (int i = 0; i < applyToSortingLayersSize; ++i)
|
||||
{
|
||||
int layerID = m_ApplyToSortingLayers.GetArrayElementAtIndex(i).intValue;
|
||||
if (SortingLayer.IsValid(layerID))
|
||||
m_ApplyToSortingLayersList.Add(layerID);
|
||||
}
|
||||
|
||||
GUIContent selectedLayers;
|
||||
if (m_ApplyToSortingLayersList.Count == 1)
|
||||
selectedLayers = new GUIContent(SortingLayer.IDToName(m_ApplyToSortingLayersList[0]));
|
||||
else if (m_ApplyToSortingLayersList.Count == m_AllSortingLayers.Length)
|
||||
selectedLayers = Styles.sortingLayerAll;
|
||||
else if (m_ApplyToSortingLayersList.Count == 0)
|
||||
selectedLayers = Styles.sortingLayerNone;
|
||||
else
|
||||
selectedLayers = Styles.sortingLayerMixed;
|
||||
|
||||
if (EditorGUI.DropdownButton(position, selectedLayers, FocusType.Keyboard, EditorStyles.popup))
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.allowDuplicateNames = true;
|
||||
|
||||
LayerSelectionData layerSelectionData = new LayerSelectionData(serializedObject, 0, targets, selectionChangedCallback);
|
||||
menu.AddItem(Styles.sortingLayerNone, m_ApplyToSortingLayersList.Count == 0, OnNoSortingLayerSelected, layerSelectionData);
|
||||
menu.AddItem(Styles.sortingLayerAll, m_ApplyToSortingLayersList.Count == m_AllSortingLayers.Length, OnAllSortingLayersSelected, layerSelectionData);
|
||||
menu.AddSeparator("");
|
||||
|
||||
for (int i = 0; i < m_AllSortingLayers.Length; ++i)
|
||||
{
|
||||
var sortingLayer = m_AllSortingLayers[i];
|
||||
layerSelectionData = new LayerSelectionData(serializedObject, sortingLayer.id, targets, selectionChangedCallback);
|
||||
menu.AddItem(m_AllSortingLayerNames[i], m_ApplyToSortingLayersList.Contains(sortingLayer.id), OnSortingLayerSelected, layerSelectionData);
|
||||
}
|
||||
|
||||
menu.DropDown(position);
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("UniversalGraphicsTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Editor.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.GraphicTests.Performance.Universal.Editor")]
|
@@ -0,0 +1,342 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor.AssetImporters;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Rendering.Universal
|
||||
{
|
||||
class AutodeskInteractiveMaterialImport : AssetPostprocessor
|
||||
{
|
||||
static readonly uint k_Version = 1;
|
||||
static readonly int k_Order = 3;
|
||||
public override uint GetVersion()
|
||||
{
|
||||
return k_Version;
|
||||
}
|
||||
|
||||
public override int GetPostprocessOrder()
|
||||
{
|
||||
return k_Order;
|
||||
}
|
||||
|
||||
public void OnPreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] clips)
|
||||
{
|
||||
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
|
||||
if (!pipelineAsset || pipelineAsset.GetType() != typeof(UniversalRenderPipelineAsset))
|
||||
return;
|
||||
|
||||
if (IsAutodeskInteractiveMaterial(description))
|
||||
{
|
||||
float floatProperty;
|
||||
Vector4 vectorProperty;
|
||||
TexturePropertyDescription textureProperty;
|
||||
|
||||
bool isMasked = description.TryGetProperty("mask_threshold", out floatProperty);
|
||||
bool isTransparent = description.TryGetProperty("opacity", out floatProperty);
|
||||
|
||||
Shader shader;
|
||||
if (isMasked)
|
||||
shader = GraphicsSettings.currentRenderPipeline.autodeskInteractiveMaskedShader;
|
||||
else if (isTransparent)
|
||||
shader = GraphicsSettings.currentRenderPipeline.autodeskInteractiveTransparentShader;
|
||||
else
|
||||
shader = GraphicsSettings.currentRenderPipeline.autodeskInteractiveShader;
|
||||
|
||||
if (shader == null)
|
||||
return;
|
||||
|
||||
material.shader = shader;
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
clip.ClearCurves();
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("base_color", out vectorProperty))
|
||||
material.SetColor("_Color", vectorProperty);
|
||||
if (description.TryGetProperty("emissive", out vectorProperty))
|
||||
material.SetColor("_EmissionColor", vectorProperty);
|
||||
|
||||
if (description.TryGetProperty("roughness", out floatProperty))
|
||||
material.SetFloat("_Roughness", floatProperty);
|
||||
|
||||
if (description.TryGetProperty("metallic", out floatProperty))
|
||||
material.SetFloat("_Metallic", floatProperty);
|
||||
|
||||
if (description.TryGetProperty("uvTransform", out vectorProperty))
|
||||
{
|
||||
material.SetVector("_UvOffset", new Vector4(vectorProperty.x, vectorProperty.y, .0f, .0f));
|
||||
material.SetVector("_UvTiling", new Vector4(vectorProperty.w, vectorProperty.z, .0f, .0f));
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("TEX_color_map", out textureProperty))
|
||||
{
|
||||
material.SetTexture("_MainTex", textureProperty.texture);
|
||||
material.SetFloat("_UseColorMap", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_UseColorMap", 0.0f);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("TEX_normal_map", out textureProperty))
|
||||
{
|
||||
material.SetTexture("_BumpMap", textureProperty.texture);
|
||||
material.SetFloat("_UseNormalMap", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_UseNormalMap", 0.0f);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("TEX_roughness_map", out textureProperty))
|
||||
{
|
||||
material.SetTexture("RoughnessMap", textureProperty.texture);
|
||||
material.SetFloat("_UseRoughnessMap", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_UseRoughnessMap", 0.0f);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("TEX_metallic_map", out textureProperty))
|
||||
{
|
||||
material.SetTexture("_MetallicMap", textureProperty.texture);
|
||||
material.SetFloat("_UseMetallicMap", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_UseMetallicMap", 0.0f);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("TEX_emissive_map", out textureProperty))
|
||||
{
|
||||
material.SetTexture("_EmissionMap", textureProperty.texture);
|
||||
material.SetFloat("_UseEmissiveMap", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_UseEmissiveMap", 0.0f);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("hasTransparencyTexture", out floatProperty))
|
||||
material.SetFloat("_UseOpacityMap", floatProperty);
|
||||
|
||||
if (description.TryGetProperty("transparencyMaskThreshold", out floatProperty))
|
||||
material.SetFloat("_OpacityThreshold", floatProperty);
|
||||
|
||||
if (description.TryGetProperty("TEX_ao_map", out textureProperty))
|
||||
{
|
||||
var tex = AssetDatabase.LoadAssetAtPath<Texture>(textureProperty.relativePath);
|
||||
material.SetTexture("AoMap", tex);
|
||||
material.SetFloat("UseAoMap", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("UseAoMap", 0.0f);
|
||||
}
|
||||
|
||||
RemapColorCurves(description, clips, "base_color", "_Color");
|
||||
RemapCurve(description, clips, "mask_threshold", "_Cutoff");
|
||||
RemapCurve(description, clips, "metallic", "_Metallic");
|
||||
RemapCurve(description, clips, "roughness", "_Glossiness");
|
||||
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
if (description.HasAnimationCurveInClip(clips[i].name, "uv_scale.x") || description.HasAnimationCurveInClip(clips[i].name, "uv_scale.y"))
|
||||
{
|
||||
AnimationCurve curve;
|
||||
if (description.TryGetAnimationCurve(clips[i].name, "uv_scale.x", out curve))
|
||||
clips[i].SetCurve("", typeof(Material), "_UvTiling.x", curve);
|
||||
else
|
||||
clips[i].SetCurve("", typeof(Material), "_UvTiling.x", AnimationCurve.Constant(0.0f, 1.0f, 1.0f));
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, "uv_scale.y", out curve))
|
||||
clips[i].SetCurve("", typeof(Material), "_UvTiling.y", curve);
|
||||
else
|
||||
clips[i].SetCurve("", typeof(Material), "_UvTiling.y", AnimationCurve.Constant(0.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
||||
if (description.HasAnimationCurveInClip(clips[i].name, "uv_offset.x") || description.HasAnimationCurveInClip(clips[i].name, "uv_offset.y"))
|
||||
{
|
||||
AnimationCurve curve;
|
||||
if (description.TryGetAnimationCurve(clips[i].name, "uv_offset.x", out curve))
|
||||
clips[i].SetCurve("", typeof(Material), "_UvOffset.x", curve);
|
||||
else
|
||||
clips[i].SetCurve("", typeof(Material), "_UvOffset.x", AnimationCurve.Constant(0.0f, 1.0f, 0.0f));
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, "uv_offset.y", out curve))
|
||||
{
|
||||
ConvertKeys(curve, ConvertFloatNegate);
|
||||
clips[i].SetCurve("", typeof(Material), "_UvOffset.y", curve);
|
||||
}
|
||||
else
|
||||
clips[i].SetCurve("", typeof(Material), "_UvOffset.y", AnimationCurve.Constant(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (description.HasAnimationCurve("emissive_intensity"))
|
||||
{
|
||||
Vector4 emissiveColor;
|
||||
description.TryGetProperty("emissive", out emissiveColor);
|
||||
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
AnimationCurve curve;
|
||||
description.TryGetAnimationCurve(clips[i].name, "emissive_intensity", out curve);
|
||||
// remap emissive intensity to emission color
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.r", curve);
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.g", curve);
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.b", curve);
|
||||
}
|
||||
}
|
||||
else if (description.TryGetProperty("emissive", out vectorProperty))
|
||||
{
|
||||
if (vectorProperty.x > 0.0f || vectorProperty.y > 0.0f || vectorProperty.z > 0.0f)
|
||||
{
|
||||
material.globalIlluminationFlags |= MaterialGlobalIlluminationFlags.RealtimeEmissive;
|
||||
material.EnableKeyword("_EMISSION");
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("emissive_intensity", out floatProperty))
|
||||
{
|
||||
vectorProperty *= floatProperty;
|
||||
}
|
||||
|
||||
material.SetColor("_EmissionColor", vectorProperty);
|
||||
|
||||
|
||||
if (description.HasAnimationCurve("emissive.x"))
|
||||
{
|
||||
if (description.HasAnimationCurve("emissive_intensity"))
|
||||
{
|
||||
// combine color and intensity.
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
AnimationCurve curve;
|
||||
AnimationCurve intensityCurve;
|
||||
description.TryGetAnimationCurve(clips[i].name, "emissive_intensity", out intensityCurve);
|
||||
|
||||
description.TryGetAnimationCurve(clips[i].name, "emissive.x", out curve);
|
||||
MultiplyCurves(curve, intensityCurve);
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.r", curve);
|
||||
|
||||
description.TryGetAnimationCurve(clips[i].name, "emissive.y", out curve);
|
||||
MultiplyCurves(curve, intensityCurve);
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.g", curve);
|
||||
|
||||
description.TryGetAnimationCurve(clips[i].name, "emissive.z", out curve);
|
||||
MultiplyCurves(curve, intensityCurve);
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.b", curve);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RemapColorCurves(description, clips, "emissive", "_EmissionColor");
|
||||
}
|
||||
}
|
||||
else if (description.HasAnimationCurve("emissive_intensity"))
|
||||
{
|
||||
Vector4 emissiveColor;
|
||||
description.TryGetProperty("emissive", out emissiveColor);
|
||||
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
AnimationCurve curve;
|
||||
description.TryGetAnimationCurve(clips[i].name, "emissive_intensity", out curve);
|
||||
// remap emissive intensity to emission color
|
||||
AnimationCurve curveR = new AnimationCurve();
|
||||
ConvertAndCopyKeys(curveR, curve, value => ConvertFloatMultiply(emissiveColor.x, value));
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.r", curveR);
|
||||
|
||||
AnimationCurve curveG = new AnimationCurve();
|
||||
ConvertAndCopyKeys(curveG, curve, value => ConvertFloatMultiply(emissiveColor.y, value));
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.g", curveG);
|
||||
|
||||
AnimationCurve curveB = new AnimationCurve();
|
||||
ConvertAndCopyKeys(curveB, curve, value => ConvertFloatMultiply(emissiveColor.z, value));
|
||||
clips[i].SetCurve("", typeof(Material), "_EmissionColor.b", curveB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsAutodeskInteractiveMaterial(MaterialDescription description)
|
||||
{
|
||||
return description.TryGetProperty("renderAPI", out string stringValue) && stringValue == "SFX_PBS_SHADER";
|
||||
}
|
||||
|
||||
static void ConvertKeys(AnimationCurve curve, System.Func<float, float> convertionDelegate)
|
||||
{
|
||||
Keyframe[] keyframes = curve.keys;
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
keyframes[i].value = convertionDelegate(keyframes[i].value);
|
||||
}
|
||||
curve.keys = keyframes;
|
||||
}
|
||||
|
||||
static void ConvertAndCopyKeys(AnimationCurve curveDest, AnimationCurve curveSource, System.Func<float, float> convertionDelegate)
|
||||
{
|
||||
for (int i = 0; i < curveSource.keys.Length; i++)
|
||||
{
|
||||
var sourceKey = curveSource.keys[i];
|
||||
curveDest.AddKey(new Keyframe(sourceKey.time, convertionDelegate(sourceKey.value), sourceKey.inTangent, sourceKey.outTangent, sourceKey.inWeight, sourceKey.outWeight));
|
||||
}
|
||||
}
|
||||
|
||||
static float ConvertFloatNegate(float value)
|
||||
{
|
||||
return -value;
|
||||
}
|
||||
|
||||
static float ConvertFloatMultiply(float value, float multiplier)
|
||||
{
|
||||
return value * multiplier;
|
||||
}
|
||||
|
||||
static void MultiplyCurves(AnimationCurve curve, AnimationCurve curveMultiplier)
|
||||
{
|
||||
Keyframe[] keyframes = curve.keys;
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
keyframes[i].value *= curveMultiplier.Evaluate(keyframes[i].time);
|
||||
}
|
||||
curve.keys = keyframes;
|
||||
}
|
||||
|
||||
static void RemapCurve(MaterialDescription description, AnimationClip[] clips, string originalPropertyName, string newPropertyName)
|
||||
{
|
||||
AnimationCurve curve;
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName, out curve))
|
||||
{
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName, curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RemapColorCurves(MaterialDescription description, AnimationClip[] clips, string originalPropertyName, string newPropertyName)
|
||||
{
|
||||
AnimationCurve curve;
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".x", out curve))
|
||||
{
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".r", curve);
|
||||
}
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".y", out curve))
|
||||
{
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".g", curve);
|
||||
}
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".z", out curve))
|
||||
{
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".b", curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,317 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
using UnityEditor.AssetImporters;
|
||||
#else
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#endif
|
||||
|
||||
namespace UnityEditor.Rendering.Universal
|
||||
{
|
||||
class FBXArnoldSurfaceMaterialDescriptionPreprocessor : AssetPostprocessor
|
||||
{
|
||||
static readonly uint k_Version = 2;
|
||||
static readonly int k_Order = 4;
|
||||
|
||||
static readonly string k_ShaderPath = "Packages/com.unity.render-pipelines.universal/Runtime/Materials/ArnoldStandardSurface/ArnoldStandardSurface.shadergraph";
|
||||
static readonly string k_ShaderTransparentPath = "Packages/com.unity.render-pipelines.universal/Runtime/Materials/ArnoldStandardSurface/ArnoldStandardSurfaceTransparent.shadergraph";
|
||||
|
||||
public override uint GetVersion()
|
||||
{
|
||||
return k_Version;
|
||||
}
|
||||
|
||||
public override int GetPostprocessOrder()
|
||||
{
|
||||
return k_Order;
|
||||
}
|
||||
|
||||
static bool IsMayaArnoldStandardSurfaceMaterial(MaterialDescription description)
|
||||
{
|
||||
float typeId;
|
||||
description.TryGetProperty("TypeId", out typeId);
|
||||
return typeId == 1138001;
|
||||
}
|
||||
|
||||
static bool Is3DsMaxArnoldStandardSurfaceMaterial(MaterialDescription description)
|
||||
{
|
||||
float classIdA;
|
||||
float classIdB;
|
||||
string originalMtl;
|
||||
description.TryGetProperty("ClassIDa", out classIdA);
|
||||
description.TryGetProperty("ClassIDb", out classIdB);
|
||||
description.TryGetProperty("ORIGINAL_MTL", out originalMtl);
|
||||
return classIdA == 2121471519 && classIdB == 1660373836 && originalMtl != "PHYSICAL_MTL";
|
||||
}
|
||||
|
||||
public void OnPreprocessMaterialDescription(MaterialDescription description, Material material,
|
||||
AnimationClip[] clips)
|
||||
{
|
||||
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
|
||||
if (!pipelineAsset || pipelineAsset.GetType() != typeof(UniversalRenderPipelineAsset))
|
||||
return;
|
||||
|
||||
var lowerCasePath = Path.GetExtension(assetPath).ToLower();
|
||||
if (lowerCasePath == ".fbx")
|
||||
{
|
||||
if (IsMayaArnoldStandardSurfaceMaterial(description))
|
||||
CreateFromMayaArnoldStandardSurfaceMaterial(description, material, clips);
|
||||
else if (Is3DsMaxArnoldStandardSurfaceMaterial(description))
|
||||
CreateFrom3DsMaxArnoldStandardSurfaceMaterial(description, material, clips);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateFromMayaArnoldStandardSurfaceMaterial(MaterialDescription description, Material material,
|
||||
AnimationClip[] clips)
|
||||
{
|
||||
float floatProperty;
|
||||
Vector4 vectorProperty;
|
||||
TexturePropertyDescription textureProperty;
|
||||
Shader shader;
|
||||
|
||||
float opacity = 1.0f;
|
||||
Vector4 opacityColor;
|
||||
TexturePropertyDescription opacityMap;
|
||||
description.TryGetProperty("opacity", out opacityColor);
|
||||
bool hasOpacityMap = description.TryGetProperty("opacity", out opacityMap);
|
||||
opacity = Mathf.Min(Mathf.Min(opacityColor.x, opacityColor.y), opacityColor.z);
|
||||
|
||||
float transmission;
|
||||
description.TryGetProperty("transmission", out transmission);
|
||||
if (opacity == 1.0f && !hasOpacityMap)
|
||||
{
|
||||
opacity = 1.0f - transmission;
|
||||
}
|
||||
|
||||
if (opacity < 1.0f || hasOpacityMap)
|
||||
{
|
||||
shader = AssetDatabase.LoadAssetAtPath<Shader>(k_ShaderTransparentPath);
|
||||
if (shader == null)
|
||||
return;
|
||||
|
||||
material.shader = shader;
|
||||
if (hasOpacityMap)
|
||||
{
|
||||
material.SetTexture("_OPACITY_MAP", opacityMap.texture);
|
||||
material.SetFloat("_OPACITY", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_OPACITY", opacity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shader = AssetDatabase.LoadAssetAtPath<Shader>(k_ShaderPath);
|
||||
if (shader == null)
|
||||
return;
|
||||
|
||||
material.shader = shader;
|
||||
}
|
||||
|
||||
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
clip.ClearCurves();
|
||||
}
|
||||
|
||||
description.TryGetProperty("base", out floatProperty);
|
||||
|
||||
if (description.TryGetProperty("baseColor", out textureProperty))
|
||||
{
|
||||
SetMaterialTextureProperty("_BASE_COLOR_MAP", material, textureProperty);
|
||||
material.SetColor("_BASE_COLOR", Color.white * floatProperty);
|
||||
}
|
||||
else if (description.TryGetProperty("baseColor", out vectorProperty))
|
||||
{
|
||||
if (QualitySettings.activeColorSpace == ColorSpace.Gamma)
|
||||
{
|
||||
vectorProperty.x = Mathf.LinearToGammaSpace(vectorProperty.x);
|
||||
vectorProperty.y = Mathf.LinearToGammaSpace(vectorProperty.y);
|
||||
vectorProperty.z = Mathf.LinearToGammaSpace(vectorProperty.z);
|
||||
vectorProperty *= floatProperty;
|
||||
}
|
||||
|
||||
material.SetColor("_BASE_COLOR", vectorProperty * floatProperty);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("emission", out floatProperty) && floatProperty > 0.0f)
|
||||
{
|
||||
remapPropertyColorOrTexture(description, material, "emissionColor", "_EMISSION_COLOR", floatProperty);
|
||||
}
|
||||
|
||||
remapPropertyFloatOrTexture(description, material, "metalness", "_METALNESS");
|
||||
|
||||
description.TryGetProperty("specular", out floatProperty);
|
||||
|
||||
remapPropertyColorOrTexture(description, material, "specularColor", "_SPECULAR_COLOR", floatProperty);
|
||||
remapPropertyFloatOrTexture(description, material, "specularRoughness", "_SPECULAR_ROUGHNESS");
|
||||
remapPropertyFloatOrTexture(description, material, "specularIOR", "_SPECULAR_IOR");
|
||||
|
||||
remapPropertyTexture(description, material, "normalCamera", "_NORMAL_MAP");
|
||||
}
|
||||
|
||||
void CreateFrom3DsMaxArnoldStandardSurfaceMaterial(MaterialDescription description, Material material,
|
||||
AnimationClip[] clips)
|
||||
{
|
||||
float floatProperty;
|
||||
Vector4 vectorProperty;
|
||||
TexturePropertyDescription textureProperty;
|
||||
|
||||
var shader = AssetDatabase.LoadAssetAtPath<Shader>(k_ShaderPath);
|
||||
if (shader == null)
|
||||
return;
|
||||
|
||||
|
||||
material.shader = shader;
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
clip.ClearCurves();
|
||||
}
|
||||
|
||||
float opacity = 1.0f;
|
||||
Vector4 opacityColor;
|
||||
TexturePropertyDescription opacityMap;
|
||||
description.TryGetProperty("opacity", out opacityColor);
|
||||
bool hasOpacityMap = description.TryGetProperty("opacity", out opacityMap);
|
||||
opacity = Mathf.Min(Mathf.Min(opacityColor.x, opacityColor.y), opacityColor.z);
|
||||
|
||||
if (opacity < 1.0f || hasOpacityMap)
|
||||
{
|
||||
if (hasOpacityMap)
|
||||
{
|
||||
material.SetTexture("_OPACITY_MAP", opacityMap.texture);
|
||||
material.SetFloat("_OPACITY", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_OPACITY", opacity);
|
||||
}
|
||||
}
|
||||
|
||||
description.TryGetProperty("base", out floatProperty);
|
||||
|
||||
if (description.TryGetProperty("base_color.shader", out textureProperty))
|
||||
{
|
||||
SetMaterialTextureProperty("_BASE_COLOR_MAP", material, textureProperty);
|
||||
material.SetColor("_BASE_COLOR", Color.white * floatProperty);
|
||||
}
|
||||
else if (description.TryGetProperty("base_color", out vectorProperty))
|
||||
{
|
||||
if (QualitySettings.activeColorSpace == ColorSpace.Gamma)
|
||||
{
|
||||
vectorProperty.x = Mathf.LinearToGammaSpace(vectorProperty.x);
|
||||
vectorProperty.y = Mathf.LinearToGammaSpace(vectorProperty.y);
|
||||
vectorProperty.z = Mathf.LinearToGammaSpace(vectorProperty.z);
|
||||
}
|
||||
|
||||
material.SetColor("_BASE_COLOR", vectorProperty * floatProperty);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("emission", out floatProperty) && floatProperty > 0.0f)
|
||||
{
|
||||
remapPropertyColorOrTexture3DsMax(description, material, "emission_color", "_EMISSION_COLOR",
|
||||
floatProperty);
|
||||
}
|
||||
|
||||
remapPropertyFloatOrTexture3DsMax(description, material, "metalness", "_METALNESS");
|
||||
|
||||
description.TryGetProperty("specular", out float specularFactor);
|
||||
|
||||
remapPropertyColorOrTexture3DsMax(description, material, "specular_color", "_SPECULAR_COLOR",
|
||||
specularFactor);
|
||||
remapPropertyFloatOrTexture3DsMax(description, material, "specular_roughness", "_SPECULAR_ROUGHNESS");
|
||||
remapPropertyFloatOrTexture3DsMax(description, material, "specular_IOR", "_SPECULAR_IOR");
|
||||
|
||||
remapPropertyTexture(description, material, "normal_camera", "_NORMAL_MAP");
|
||||
}
|
||||
|
||||
static void SetMaterialTextureProperty(string propertyName, Material material,
|
||||
TexturePropertyDescription textureProperty)
|
||||
{
|
||||
material.SetTexture(propertyName, textureProperty.texture);
|
||||
material.SetTextureOffset(propertyName, textureProperty.offset);
|
||||
material.SetTextureScale(propertyName, textureProperty.scale);
|
||||
}
|
||||
|
||||
static void remapPropertyFloat(MaterialDescription description, Material material, string inPropName,
|
||||
string outPropName)
|
||||
{
|
||||
if (description.TryGetProperty(inPropName, out float floatProperty))
|
||||
{
|
||||
material.SetFloat(outPropName, floatProperty);
|
||||
}
|
||||
}
|
||||
|
||||
static void remapPropertyTexture(MaterialDescription description, Material material, string inPropName,
|
||||
string outPropName)
|
||||
{
|
||||
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
|
||||
{
|
||||
material.SetTexture(outPropName, textureProperty.texture);
|
||||
}
|
||||
}
|
||||
|
||||
static void remapPropertyColorOrTexture3DsMax(MaterialDescription description, Material material,
|
||||
string inPropName, string outPropName, float multiplier = 1.0f)
|
||||
{
|
||||
if (description.TryGetProperty(inPropName + ".shader", out TexturePropertyDescription textureProperty))
|
||||
{
|
||||
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
|
||||
material.SetColor(outPropName, Color.white * multiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
description.TryGetProperty(inPropName, out Vector4 vectorProperty);
|
||||
material.SetColor(outPropName, vectorProperty * multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
static void remapPropertyFloatOrTexture3DsMax(MaterialDescription description, Material material,
|
||||
string inPropName, string outPropName)
|
||||
{
|
||||
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
|
||||
{
|
||||
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
|
||||
material.SetFloat(outPropName, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
description.TryGetProperty(inPropName, out float floatProperty);
|
||||
material.SetFloat(outPropName, floatProperty);
|
||||
}
|
||||
}
|
||||
|
||||
static void remapPropertyColorOrTexture(MaterialDescription description, Material material, string inPropName,
|
||||
string outPropName, float multiplier = 1.0f)
|
||||
{
|
||||
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
|
||||
{
|
||||
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
|
||||
material.SetColor(outPropName, Color.white * multiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
description.TryGetProperty(inPropName, out Vector4 vectorProperty);
|
||||
material.SetColor(outPropName, vectorProperty * multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
static void remapPropertyFloatOrTexture(MaterialDescription description, Material material, string inPropName,
|
||||
string outPropName)
|
||||
{
|
||||
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
|
||||
{
|
||||
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
|
||||
material.SetFloat(outPropName, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
description.TryGetProperty(inPropName, out float floatProperty);
|
||||
material.SetFloat(outPropName, floatProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,273 @@
|
||||
using System.IO;
|
||||
using UnityEditor.AssetImporters;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Rendering.Universal
|
||||
{
|
||||
class FBXMaterialDescriptionPreprocessor : AssetPostprocessor
|
||||
{
|
||||
static readonly uint k_Version = 1;
|
||||
static readonly int k_Order = 2;
|
||||
public override uint GetVersion()
|
||||
{
|
||||
return k_Version;
|
||||
}
|
||||
|
||||
public override int GetPostprocessOrder()
|
||||
{
|
||||
return k_Order;
|
||||
}
|
||||
|
||||
public void OnPreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] clips)
|
||||
{
|
||||
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
|
||||
if (!pipelineAsset || pipelineAsset.GetType() != typeof(UniversalRenderPipelineAsset))
|
||||
return;
|
||||
|
||||
var lowerCaseExtension = Path.GetExtension(assetPath).ToLower();
|
||||
if (lowerCaseExtension != ".fbx" && lowerCaseExtension != ".obj" && lowerCaseExtension != ".blend" && lowerCaseExtension != ".mb" && lowerCaseExtension != ".ma" && lowerCaseExtension != ".max")
|
||||
return;
|
||||
|
||||
string path = AssetDatabase.GUIDToAssetPath(ShaderUtils.GetShaderGUID(ShaderPathID.Lit));
|
||||
var shader = AssetDatabase.LoadAssetAtPath<Shader>(path);
|
||||
if (shader == null)
|
||||
return;
|
||||
|
||||
|
||||
material.shader = shader;
|
||||
|
||||
Vector4 vectorProperty;
|
||||
float floatProperty;
|
||||
TexturePropertyDescription textureProperty;
|
||||
|
||||
bool isTransparent = false;
|
||||
|
||||
float opacity;
|
||||
float transparencyFactor;
|
||||
if (!description.TryGetProperty("Opacity", out opacity))
|
||||
{
|
||||
if (description.TryGetProperty("TransparencyFactor", out transparencyFactor))
|
||||
{
|
||||
opacity = transparencyFactor == 1.0f ? 1.0f : 1.0f - transparencyFactor;
|
||||
}
|
||||
if (opacity == 1.0f && description.TryGetProperty("TransparentColor", out vectorProperty))
|
||||
{
|
||||
opacity = vectorProperty.x == 1.0f ? 1.0f : 1.0f - vectorProperty.x;
|
||||
}
|
||||
}
|
||||
if (opacity < 1.0f || (opacity == 1.0f && description.TryGetProperty("TransparentColor", out textureProperty)))
|
||||
{
|
||||
isTransparent = true;
|
||||
}
|
||||
else if (description.HasAnimationCurve("TransparencyFactor") || description.HasAnimationCurve("TransparentColor"))
|
||||
{
|
||||
isTransparent = true;
|
||||
}
|
||||
|
||||
if (isTransparent)
|
||||
{
|
||||
material.SetFloat("_Mode", 3.0f);
|
||||
material.SetOverrideTag("RenderType", "Transparent");
|
||||
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
||||
material.SetFloat("_ZWrite", 0.0f);
|
||||
material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
|
||||
material.SetFloat("_Surface", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_Mode", 0.0f);
|
||||
material.SetOverrideTag("RenderType", "");
|
||||
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.Zero);
|
||||
material.SetFloat("_ZWrite", 1.0f);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.DisableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = -1;
|
||||
material.SetFloat("_Surface", 0.0f);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("DiffuseColor", out textureProperty) && textureProperty.texture != null)
|
||||
{
|
||||
Color diffuseColor = new Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
if (description.TryGetProperty("DiffuseFactor", out floatProperty))
|
||||
diffuseColor *= floatProperty;
|
||||
diffuseColor.a = opacity;
|
||||
|
||||
SetMaterialTextureProperty("_BaseMap", material, textureProperty);
|
||||
material.SetColor("_BaseColor", diffuseColor);
|
||||
}
|
||||
else if (description.TryGetProperty("DiffuseColor", out vectorProperty))
|
||||
{
|
||||
Color diffuseColor = vectorProperty;
|
||||
diffuseColor.a = opacity;
|
||||
material.SetColor("_BaseColor", PlayerSettings.colorSpace == ColorSpace.Linear ? diffuseColor.gamma : diffuseColor);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("Bump", out textureProperty))
|
||||
{
|
||||
SetMaterialTextureProperty("_BumpMap", material, textureProperty);
|
||||
material.EnableKeyword("_NORMALMAP");
|
||||
|
||||
if (description.TryGetProperty("BumpFactor", out floatProperty))
|
||||
material.SetFloat("_BumpScale", floatProperty);
|
||||
}
|
||||
else if (description.TryGetProperty("NormalMap", out textureProperty))
|
||||
{
|
||||
SetMaterialTextureProperty("_BumpMap", material, textureProperty);
|
||||
material.EnableKeyword("_NORMALMAP");
|
||||
|
||||
if (description.TryGetProperty("BumpFactor", out floatProperty))
|
||||
material.SetFloat("_BumpScale", floatProperty);
|
||||
}
|
||||
|
||||
if (description.TryGetProperty("EmissiveColor", out textureProperty))
|
||||
{
|
||||
Color emissiveColor = new Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
material.SetColor("_EmissionColor", emissiveColor);
|
||||
SetMaterialTextureProperty("_EmissionMap", material, textureProperty);
|
||||
|
||||
if (description.TryGetProperty("EmissiveFactor", out floatProperty) && floatProperty > 0.0f)
|
||||
{
|
||||
material.EnableKeyword("_EMISSION");
|
||||
material.globalIlluminationFlags |= MaterialGlobalIlluminationFlags.RealtimeEmissive;
|
||||
}
|
||||
}
|
||||
else if (
|
||||
description.TryGetProperty("EmissiveColor", out vectorProperty) && vectorProperty.magnitude > vectorProperty.w
|
||||
|| description.HasAnimationCurve("EmissiveColor.x"))
|
||||
{
|
||||
if (description.TryGetProperty("EmissiveFactor", out floatProperty))
|
||||
vectorProperty *= floatProperty;
|
||||
|
||||
material.SetColor("_EmissionColor", vectorProperty);
|
||||
if (floatProperty > 0.0f)
|
||||
{
|
||||
material.EnableKeyword("_EMISSION");
|
||||
material.globalIlluminationFlags |= MaterialGlobalIlluminationFlags.RealtimeEmissive;
|
||||
}
|
||||
}
|
||||
|
||||
material.SetFloat("_Glossiness", 0.0f);
|
||||
|
||||
if (PlayerSettings.colorSpace == ColorSpace.Linear)
|
||||
RemapAndTransformColorCurves(description, clips, "DiffuseColor", "_BaseColor", ConvertFloatLinearToGamma);
|
||||
else
|
||||
RemapColorCurves(description, clips, "DiffuseColor", "_BaseColor");
|
||||
|
||||
RemapTransparencyCurves(description, clips);
|
||||
|
||||
RemapColorCurves(description, clips, "EmissiveColor", "_EmissionColor");
|
||||
}
|
||||
|
||||
static void RemapTransparencyCurves(MaterialDescription description, AnimationClip[] clips)
|
||||
{
|
||||
// For some reason, Opacity is never animated, we have to use TransparencyFactor and TransparentColor
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
bool foundTransparencyCurve = false;
|
||||
AnimationCurve curve;
|
||||
if (description.TryGetAnimationCurve(clips[i].name, "TransparencyFactor", out curve))
|
||||
{
|
||||
ConvertKeys(curve, ConvertFloatOneMinus);
|
||||
clips[i].SetCurve("", typeof(Material), "_BaseColor.a", curve);
|
||||
foundTransparencyCurve = true;
|
||||
}
|
||||
else if (description.TryGetAnimationCurve(clips[i].name, "TransparentColor.x", out curve))
|
||||
{
|
||||
ConvertKeys(curve, ConvertFloatOneMinus);
|
||||
clips[i].SetCurve("", typeof(Material), "_BaseColor.a", curve);
|
||||
foundTransparencyCurve = true;
|
||||
}
|
||||
|
||||
if (foundTransparencyCurve && !description.HasAnimationCurveInClip(clips[i].name, "DiffuseColor"))
|
||||
{
|
||||
Vector4 diffuseColor;
|
||||
description.TryGetProperty("DiffuseColor", out diffuseColor);
|
||||
clips[i].SetCurve("", typeof(Material), "_BaseColor.r", AnimationCurve.Constant(0.0f, 1.0f, diffuseColor.x));
|
||||
clips[i].SetCurve("", typeof(Material), "_BaseColor.g", AnimationCurve.Constant(0.0f, 1.0f, diffuseColor.y));
|
||||
clips[i].SetCurve("", typeof(Material), "_BaseColor.b", AnimationCurve.Constant(0.0f, 1.0f, diffuseColor.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RemapColorCurves(MaterialDescription description, AnimationClip[] clips, string originalPropertyName, string newPropertyName)
|
||||
{
|
||||
AnimationCurve curve;
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".x", out curve))
|
||||
{
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".r", curve);
|
||||
}
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".y", out curve))
|
||||
{
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".g", curve);
|
||||
}
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".z", out curve))
|
||||
{
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".b", curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RemapAndTransformColorCurves(MaterialDescription description, AnimationClip[] clips, string originalPropertyName, string newPropertyName, System.Func<float, float> converter)
|
||||
{
|
||||
AnimationCurve curve;
|
||||
for (int i = 0; i < clips.Length; i++)
|
||||
{
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".x", out curve))
|
||||
{
|
||||
ConvertKeys(curve, converter);
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".r", curve);
|
||||
}
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".y", out curve))
|
||||
{
|
||||
ConvertKeys(curve, converter);
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".g", curve);
|
||||
}
|
||||
|
||||
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".z", out curve))
|
||||
{
|
||||
ConvertKeys(curve, converter);
|
||||
clips[i].SetCurve("", typeof(Material), newPropertyName + ".b", curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float ConvertFloatLinearToGamma(float value)
|
||||
{
|
||||
return Mathf.LinearToGammaSpace(value);
|
||||
}
|
||||
|
||||
static float ConvertFloatOneMinus(float value)
|
||||
{
|
||||
return 1.0f - value;
|
||||
}
|
||||
|
||||
static void ConvertKeys(AnimationCurve curve, System.Func<float, float> convertionDelegate)
|
||||
{
|
||||
Keyframe[] keyframes = curve.keys;
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
keyframes[i].value = convertionDelegate(keyframes[i].value);
|
||||
}
|
||||
curve.keys = keyframes;
|
||||
}
|
||||
|
||||
static void SetMaterialTextureProperty(string propertyName, Material material, TexturePropertyDescription textureProperty)
|
||||
{
|
||||
material.SetTexture(propertyName, textureProperty.texture);
|
||||
material.SetTextureOffset(propertyName, textureProperty.offset);
|
||||
material.SetTextureScale(propertyName, textureProperty.scale);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,390 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.Rendering.Universal.ShaderGUI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace UnityEditor.Rendering.Universal
|
||||
{
|
||||
class MaterialModificationProcessor : AssetModificationProcessor
|
||||
{
|
||||
static void OnWillCreateAsset(string asset)
|
||||
{
|
||||
if (!asset.ToLowerInvariant().EndsWith(".mat"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
MaterialPostprocessor.s_CreatedAssets.Add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
class MaterialReimporter : Editor
|
||||
{
|
||||
static bool s_NeedToCheckProjSettingExistence = true;
|
||||
|
||||
static void ReimportAllMaterials()
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:material", null);
|
||||
// There can be several materials subAssets per guid ( ie : FBX files ), remove duplicate guids.
|
||||
var distinctGuids = guids.Distinct();
|
||||
|
||||
int materialIdx = 0;
|
||||
int totalMaterials = distinctGuids.Count();
|
||||
foreach (var asset in distinctGuids)
|
||||
{
|
||||
materialIdx++;
|
||||
var path = AssetDatabase.GUIDToAssetPath(asset);
|
||||
EditorUtility.DisplayProgressBar("Material Upgrader re-import", string.Format("({0} of {1}) {2}", materialIdx, totalMaterials, path), (float)materialIdx / (float)totalMaterials);
|
||||
AssetDatabase.ImportAsset(path);
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
MaterialPostprocessor.s_NeedsSavingAssets = true;
|
||||
}
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
static void RegisterUpgraderReimport()
|
||||
{
|
||||
EditorApplication.update += () =>
|
||||
{
|
||||
if (Time.renderedFrameCount > 0)
|
||||
{
|
||||
bool fileExist = true;
|
||||
// We check the file existence only once to avoid IO operations every frame.
|
||||
if (s_NeedToCheckProjSettingExistence)
|
||||
{
|
||||
fileExist = System.IO.File.Exists(UniversalProjectSettings.filePath);
|
||||
s_NeedToCheckProjSettingExistence = false;
|
||||
}
|
||||
|
||||
//This method is called at opening and when URP package change (update of manifest.json)
|
||||
var curUpgradeVersion = UniversalProjectSettings.materialVersionForUpgrade;
|
||||
|
||||
if (curUpgradeVersion != MaterialPostprocessor.k_Upgraders.Length)
|
||||
{
|
||||
string commandLineOptions = Environment.CommandLine;
|
||||
bool inTestSuite = commandLineOptions.Contains("-testResults");
|
||||
if (!inTestSuite && fileExist)
|
||||
{
|
||||
EditorUtility.DisplayDialog("URP Material upgrade", "The Materials in your Project were created using an older version of the Universal Render Pipeline (URP)." +
|
||||
" Unity must upgrade them to be compatible with your current version of URP. \n" +
|
||||
" Unity will re-import all of the Materials in your project, save the upgraded Materials to disk, and check them out in source control if needed.\n" +
|
||||
" Please see the Material upgrade guide in the URP documentation for more information.", "Ok");
|
||||
}
|
||||
|
||||
ReimportAllMaterials();
|
||||
}
|
||||
|
||||
if (MaterialPostprocessor.s_NeedsSavingAssets)
|
||||
MaterialPostprocessor.SaveAssetsToDisk();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class MaterialPostprocessor : AssetPostprocessor
|
||||
{
|
||||
public static List<string> s_CreatedAssets = new List<string>();
|
||||
internal static List<string> s_ImportedAssetThatNeedSaving = new List<string>();
|
||||
internal static bool s_NeedsSavingAssets = false;
|
||||
|
||||
internal static readonly Action<Material, ShaderPathID>[] k_Upgraders = { UpgradeV1, UpgradeV2, UpgradeV3, UpgradeV4 };
|
||||
|
||||
static internal void SaveAssetsToDisk()
|
||||
{
|
||||
string commandLineOptions = System.Environment.CommandLine;
|
||||
bool inTestSuite = commandLineOptions.Contains("-testResults");
|
||||
if (inTestSuite)
|
||||
{
|
||||
// Need to update material version to prevent infinite loop in the upgrader
|
||||
// when running tests.
|
||||
UniversalProjectSettings.materialVersionForUpgrade = k_Upgraders.Length;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var asset in s_ImportedAssetThatNeedSaving)
|
||||
{
|
||||
AssetDatabase.MakeEditable(asset);
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
//to prevent data loss, only update the saved version if user applied change and assets are written to
|
||||
UniversalProjectSettings.materialVersionForUpgrade = k_Upgraders.Length;
|
||||
UniversalProjectSettings.Save();
|
||||
|
||||
s_ImportedAssetThatNeedSaving.Clear();
|
||||
s_NeedsSavingAssets = false;
|
||||
}
|
||||
|
||||
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
var upgradeLog = "UniversalRP Material log:";
|
||||
var upgradeCount = 0;
|
||||
|
||||
foreach (var asset in importedAssets)
|
||||
{
|
||||
if (!asset.EndsWith(".mat", StringComparison.InvariantCultureIgnoreCase))
|
||||
continue;
|
||||
|
||||
var material = (Material)AssetDatabase.LoadAssetAtPath(asset, typeof(Material));
|
||||
if (!ShaderUtils.IsLWShader(material.shader))
|
||||
continue;
|
||||
|
||||
ShaderPathID id = ShaderUtils.GetEnumFromPath(material.shader.name);
|
||||
var wasUpgraded = false;
|
||||
|
||||
var debug = "\n" + material.name;
|
||||
|
||||
AssetVersion assetVersion = null;
|
||||
var allAssets = AssetDatabase.LoadAllAssetsAtPath(asset);
|
||||
foreach (var subAsset in allAssets)
|
||||
{
|
||||
if (subAsset is AssetVersion sub)
|
||||
{
|
||||
assetVersion = sub;
|
||||
}
|
||||
}
|
||||
|
||||
if (!assetVersion)
|
||||
{
|
||||
wasUpgraded = true;
|
||||
assetVersion = ScriptableObject.CreateInstance<AssetVersion>();
|
||||
if (s_CreatedAssets.Contains(asset))
|
||||
{
|
||||
assetVersion.version = k_Upgraders.Length;
|
||||
s_CreatedAssets.Remove(asset);
|
||||
InitializeLatest(material, id);
|
||||
debug += " initialized.";
|
||||
}
|
||||
else
|
||||
{
|
||||
assetVersion.version = UniversalProjectSettings.materialVersionForUpgrade;
|
||||
debug += $" assumed to be version {UniversalProjectSettings.materialVersionForUpgrade} due to missing version.";
|
||||
}
|
||||
|
||||
assetVersion.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector | HideFlags.NotEditable;
|
||||
AssetDatabase.AddObjectToAsset(assetVersion, asset);
|
||||
}
|
||||
|
||||
while (assetVersion.version < k_Upgraders.Length)
|
||||
{
|
||||
k_Upgraders[assetVersion.version](material, id);
|
||||
debug += $" upgrading:v{assetVersion.version} to v{assetVersion.version + 1}";
|
||||
assetVersion.version++;
|
||||
wasUpgraded = true;
|
||||
}
|
||||
|
||||
if (wasUpgraded)
|
||||
{
|
||||
upgradeLog += debug;
|
||||
upgradeCount++;
|
||||
EditorUtility.SetDirty(assetVersion);
|
||||
s_ImportedAssetThatNeedSaving.Add(asset);
|
||||
s_NeedsSavingAssets = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void InitializeLatest(Material material, ShaderPathID id)
|
||||
{
|
||||
}
|
||||
|
||||
static void UpgradeV1(Material material, ShaderPathID shaderID)
|
||||
{
|
||||
var shaderPath = ShaderUtils.GetShaderPath(shaderID);
|
||||
var upgradeFlag = MaterialUpgrader.UpgradeFlags.LogMessageWhenNoUpgraderFound;
|
||||
|
||||
switch (shaderID)
|
||||
{
|
||||
case ShaderPathID.Unlit:
|
||||
MaterialUpgrader.Upgrade(material, new UnlitUpdaterV1(shaderPath), upgradeFlag);
|
||||
UnlitShader.SetMaterialKeywords(material);
|
||||
break;
|
||||
case ShaderPathID.SimpleLit:
|
||||
MaterialUpgrader.Upgrade(material, new SimpleLitUpdaterV1(shaderPath), upgradeFlag);
|
||||
SimpleLitShader.SetMaterialKeywords(material, SimpleLitGUI.SetMaterialKeywords);
|
||||
break;
|
||||
case ShaderPathID.Lit:
|
||||
MaterialUpgrader.Upgrade(material, new LitUpdaterV1(shaderPath), upgradeFlag);
|
||||
LitShader.SetMaterialKeywords(material, LitGUI.SetMaterialKeywords);
|
||||
break;
|
||||
case ShaderPathID.ParticlesLit:
|
||||
MaterialUpgrader.Upgrade(material, new ParticleUpdaterV1(shaderPath), upgradeFlag);
|
||||
ParticlesLitShader.SetMaterialKeywords(material, LitGUI.SetMaterialKeywords, ParticleGUI.SetMaterialKeywords);
|
||||
break;
|
||||
case ShaderPathID.ParticlesSimpleLit:
|
||||
MaterialUpgrader.Upgrade(material, new ParticleUpdaterV1(shaderPath), upgradeFlag);
|
||||
ParticlesSimpleLitShader.SetMaterialKeywords(material, SimpleLitGUI.SetMaterialKeywords, ParticleGUI.SetMaterialKeywords);
|
||||
break;
|
||||
case ShaderPathID.ParticlesUnlit:
|
||||
MaterialUpgrader.Upgrade(material, new ParticleUpdaterV1(shaderPath), upgradeFlag);
|
||||
ParticlesUnlitShader.SetMaterialKeywords(material, null, ParticleGUI.SetMaterialKeywords);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void UpgradeV2(Material material, ShaderPathID shaderID)
|
||||
{
|
||||
// fix 50 offset on shaders
|
||||
if (material.HasProperty("_QueueOffset"))
|
||||
BaseShaderGUI.SetupMaterialBlendMode(material);
|
||||
}
|
||||
|
||||
static void UpgradeV3(Material material, ShaderPathID shaderID)
|
||||
{
|
||||
switch (shaderID)
|
||||
{
|
||||
case ShaderPathID.Lit:
|
||||
case ShaderPathID.SimpleLit:
|
||||
case ShaderPathID.ParticlesLit:
|
||||
case ShaderPathID.ParticlesSimpleLit:
|
||||
case ShaderPathID.ParticlesUnlit:
|
||||
var propertyID = Shader.PropertyToID("_EmissionColor");
|
||||
if (material.HasProperty(propertyID))
|
||||
{
|
||||
// In older version there was a bug that these shaders did not had HDR attribute on emission property.
|
||||
// This caused emission color to be converted from gamma to linear space.
|
||||
// In order to avoid visual regression on older projects we will do gamma to linear conversion here.
|
||||
var emissionGamma = material.GetColor(propertyID);
|
||||
var emissionLinear = emissionGamma.linear;
|
||||
material.SetColor(propertyID, emissionLinear);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void UpgradeV4(Material material, ShaderPathID shaderID)
|
||||
{}
|
||||
}
|
||||
|
||||
// Upgraders v1
|
||||
#region UpgradersV1
|
||||
|
||||
internal class LitUpdaterV1 : MaterialUpgrader
|
||||
{
|
||||
public static void UpdateLitDetails(Material material)
|
||||
{
|
||||
if (material == null)
|
||||
throw new ArgumentNullException("material");
|
||||
|
||||
if (material.GetTexture("_MetallicGlossMap") || material.GetTexture("_SpecGlossMap") || material.GetFloat("_SmoothnessTextureChannel") >= 0.5f)
|
||||
material.SetFloat("_Smoothness", material.GetFloat("_GlossMapScale"));
|
||||
else
|
||||
material.SetFloat("_Smoothness", material.GetFloat("_Glossiness"));
|
||||
}
|
||||
|
||||
public LitUpdaterV1(string oldShaderName)
|
||||
{
|
||||
if (oldShaderName == null)
|
||||
throw new ArgumentNullException("oldShaderName");
|
||||
|
||||
string standardShaderPath = ShaderUtils.GetShaderPath(ShaderPathID.Lit);
|
||||
|
||||
RenameShader(oldShaderName, standardShaderPath, UpdateLitDetails);
|
||||
|
||||
RenameTexture("_MainTex", "_BaseMap");
|
||||
RenameColor("_Color", "_BaseColor");
|
||||
RenameFloat("_GlossyReflections", "_EnvironmentReflections");
|
||||
}
|
||||
}
|
||||
|
||||
internal class UnlitUpdaterV1 : MaterialUpgrader
|
||||
{
|
||||
static Shader bakedLit = Shader.Find(ShaderUtils.GetShaderPath(ShaderPathID.BakedLit));
|
||||
|
||||
public static void UpgradeToUnlit(Material material)
|
||||
{
|
||||
if (material == null)
|
||||
throw new ArgumentNullException("material");
|
||||
|
||||
if (material.GetFloat("_SampleGI") != 0)
|
||||
{
|
||||
material.shader = bakedLit;
|
||||
material.EnableKeyword("_NORMALMAP");
|
||||
}
|
||||
}
|
||||
|
||||
public UnlitUpdaterV1(string oldShaderName)
|
||||
{
|
||||
if (oldShaderName == null)
|
||||
throw new ArgumentNullException("oldShaderName");
|
||||
|
||||
RenameShader(oldShaderName, ShaderUtils.GetShaderPath(ShaderPathID.Unlit), UpgradeToUnlit);
|
||||
|
||||
RenameTexture("_MainTex", "_BaseMap");
|
||||
RenameColor("_Color", "_BaseColor");
|
||||
}
|
||||
}
|
||||
|
||||
internal class SimpleLitUpdaterV1 : MaterialUpgrader
|
||||
{
|
||||
public SimpleLitUpdaterV1(string oldShaderName)
|
||||
{
|
||||
if (oldShaderName == null)
|
||||
throw new ArgumentNullException("oldShaderName");
|
||||
|
||||
RenameShader(oldShaderName, ShaderUtils.GetShaderPath(ShaderPathID.SimpleLit), UpgradeToSimpleLit);
|
||||
|
||||
RenameTexture("_MainTex", "_BaseMap");
|
||||
RenameColor("_Color", "_BaseColor");
|
||||
RenameFloat("_SpecSource", "_SpecularHighlights");
|
||||
RenameFloat("_Shininess", "_Smoothness");
|
||||
}
|
||||
|
||||
public static void UpgradeToSimpleLit(Material material)
|
||||
{
|
||||
if (material == null)
|
||||
throw new ArgumentNullException("material");
|
||||
|
||||
var smoothnessSource = 1 - (int)material.GetFloat("_GlossinessSource");
|
||||
material.SetFloat("_SmoothnessSource" , smoothnessSource);
|
||||
if (material.GetTexture("_SpecGlossMap") == null)
|
||||
{
|
||||
var col = material.GetColor("_SpecColor");
|
||||
var colBase = material.GetColor("_Color");
|
||||
var smoothness = material.GetFloat("_Shininess");
|
||||
|
||||
if (material.GetFloat("_Surface") == 0)
|
||||
{
|
||||
if (smoothnessSource == 1)
|
||||
colBase.a = smoothness;
|
||||
else
|
||||
col.a = smoothness;
|
||||
material.SetColor("_BaseColor", colBase);
|
||||
}
|
||||
|
||||
material.SetColor("_BaseColor", colBase);
|
||||
material.SetColor("_SpecColor", col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ParticleUpdaterV1 : MaterialUpgrader
|
||||
{
|
||||
public ParticleUpdaterV1(string shaderName)
|
||||
{
|
||||
if (shaderName == null)
|
||||
throw new ArgumentNullException("oldShaderName");
|
||||
|
||||
RenameShader(shaderName, shaderName, ParticleUpgrader.UpdateSurfaceBlendModes);
|
||||
|
||||
RenameTexture("_MainTex", "_BaseMap");
|
||||
RenameColor("_Color", "_BaseColor");
|
||||
RenameFloat("_FlipbookMode", "_FlipbookBlending");
|
||||
|
||||
switch (ShaderUtils.GetEnumFromPath(shaderName))
|
||||
{
|
||||
case ShaderPathID.ParticlesLit:
|
||||
RenameFloat("_Glossiness", "_Smoothness");
|
||||
break;
|
||||
case ShaderPathID.ParticlesSimpleLit:
|
||||
RenameFloat("_Glossiness", "_Smoothness");
|
||||
break;
|
||||
case ShaderPathID.ParticlesUnlit:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|