diff --git a/com.unity.render-pipelines.core/Editor/Gizmo/GizmoUtility.cs b/com.unity.render-pipelines.core/Editor/Gizmo/GizmoUtility.cs
new file mode 100644
index 00000000000..2e24de36207
--- /dev/null
+++ b/com.unity.render-pipelines.core/Editor/Gizmo/GizmoUtility.cs
@@ -0,0 +1,26 @@
+using System;
+using UnityEngine;
+
+namespace UnityEditor.Rendering
+{
+ public static class GizmoUtility
+ {
+ public static Color GetHandleColor(Color baseColor)
+ {
+ baseColor.a = 1f;
+ return baseColor;
+ }
+
+ public static Color GetWireframeColor(Color baseColor)
+ {
+ baseColor.a = .7f;
+ return baseColor;
+ }
+
+ public static Color GetWireframeColorBehindObjects(Color baseColor)
+ {
+ baseColor.a = .2f;
+ return baseColor;
+ }
+ }
+}
diff --git a/com.unity.render-pipelines.core/Editor/Gizmo/GizmoUtility.cs.meta b/com.unity.render-pipelines.core/Editor/Gizmo/GizmoUtility.cs.meta
new file mode 100644
index 00000000000..3fb6b736167
--- /dev/null
+++ b/com.unity.render-pipelines.core/Editor/Gizmo/GizmoUtility.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 13d32c9cdf2984447b4802095ce716ae
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.core/Editor/Gizmo/HierarchicalBox.cs b/com.unity.render-pipelines.core/Editor/Gizmo/HierarchicalBox.cs
index e0dbb33c4e8..d2d225dab6b 100644
--- a/com.unity.render-pipelines.core/Editor/Gizmo/HierarchicalBox.cs
+++ b/com.unity.render-pipelines.core/Editor/Gizmo/HierarchicalBox.cs
@@ -115,17 +115,24 @@ public Color baseColor
set
{
value.a = 8f / 255;
- m_MonochromeFillColor = value;
- material.color = m_MonochromeFillColor;
- value.a = 1f;
- m_MonochromeHandleColor = value;
- value.a = 0.7f;
- m_WireframeColor = value;
- value.a = 0.2f;
- m_WireframeColorBehind = value;
+ SetBaseColor(value);
}
}
+ ///
+ /// Set the baseColor used to fill hull. All other colors are deduced from it except specific handle colors.
+ /// Instead of baseColor set, this will not force the opacity and keep what is provided for the filled faces.
+ ///
+ /// The color to use
+ public void SetBaseColor(Color color)
+ {
+ m_MonochromeFillColor = color;
+ material.color = m_MonochromeFillColor;
+ m_MonochromeHandleColor = GizmoUtility.GetHandleColor(color);
+ m_WireframeColor = GizmoUtility.GetWireframeColor(color);
+ m_WireframeColorBehind = GizmoUtility.GetWireframeColorBehindObjects(color);
+ }
+
//Note: Handles.Slider not allow to use a specific ControlID.
//Thus Slider1D is used (with reflection)
static Type k_Slider1D = Type.GetType("UnityEditorInternal.Slider1D, UnityEditor");
diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md
index 25df4a795e1..538ea16409b 100644
--- a/com.unity.render-pipelines.high-definition/CHANGELOG.md
+++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- Added support for the PlayStation 5 platform.
+- Added pivot point manipulation for Decals (inspector and edit mode).
+- Added UV manipulation for Decals (edit mode).
+- Added color and intensity customization for Decals.
### Fixed
- Fixed GC allocations from XR occlusion mesh when using multipass.
@@ -33,6 +36,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixed issues with compositor's undo (cases 1305633, 1307170).
### Changed
+- Removed the material pass probe volumes evaluation mode.
- Change the source value for the ray tracing frame index iterator from m_FrameCount to the camera frame count (case 1301356).
## [11.0.0] - 2020-10-21
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md b/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md
index 52e1275e04f..65b6e212e09 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md
@@ -14,16 +14,19 @@ The Decal Projector includes a Scene view representation of its bounds and proje
* A box that describes the 3D size of the projector; the projector draws its decal on every Material inside the box.
-* An arrow that indicates the direction the projector faces.
+* An arrow that indicates the direction the projector faces. The base of this arrow is on the pivot point.

-The decal Projector also includes two gizmos that add handles on every face for you to click and drag to alter the size of the projector's bounds.
+The decal Projector also includes three gizmos. The first two add handles on every face for you to click and drag to alter the size of the projector's bounds.
|**Button**|**Gizmo**|**Description**|
|-----|-----|-----|
-||**Scale**|Scales the decal with the projector box. This changes the UVs of the Material to match the size of the projector box. This stretches the decal.|
-||**Crop**|Crops the decal with the projector box. This changes the size of the projector box but not the UVs of the Material. This crops the decal.|
+||**Scale**|Scales the decal with the projector box. This changes the UVs of the Material to match the size of the projector box. This stretches the decal. The Pivot remains still.|
+||**Crop**|Crops the decal with the projector box. This changes the size of the projector box but not the UVs of the Material. This crops the decal. The Pivot remains still.|
+||**Pivot / UV**|Moves the decal's pivot point without moving the projection box. This changes the transform position.
Note this also sets the UV used on the projected texture.|
+
+The color of the gizmos can be set up in the Preference window inside Color panel.
## Using the Inspector
@@ -31,12 +34,13 @@ Using the Inspector allows you to change all of the Decal Projector properties,
## Properties
-
+
| **Property** | **Description** |
| ----------------------- | ------------------------------------------------------------ |
| **Size** | The size of the projector influence box, and thus the decal along the projected plane. The projector scales the decal to match the **Width** (along the local x-axis) and **Height** (along the local y-axis) components of the **Size**. |
| **Projection Depth** | The depth of the projector influence box. The projector scales the decal to match **Projection Depth**. The Decal Projector component projects decals along the local z-axis. |
+| **Pivot** | The offset position of the transform regarding the projection box. To rotate the projected texture around a specific position, adjust the **X** and **Y** values. To set a depth offset for the projected texture, adjust the **Z** value. |
| **Material** | The decal Material to project. The decal Material must use a HDRP/Decal Shader. |
| **Decal Layer** | The layer that specifies the Materials to project the decal onto. Any Mesh Renderers or Terrain that uses a matching Decal Layer receives the decal. |
| **Draw Distance** | The distance from the Camera to the Decal at which this projector stops projecting the decal and HDRP no longer renders the decal. |
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector3.png b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector3.png
index 1fda03f14a3..f83fb4ea85f 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector3.png
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector3.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6ee48ea03dc20eb5f9795b6373da6f247e7d53659eb3884637d050c1a4fb7208
-size 335
+oid sha256:dc861506e84d5055dc66b6f3cf2f4f2ec2af4903996c2a57a7c01244983784d0
+size 731
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector4.png b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector4.png
index a51f5ee3151..849a223dd73 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector4.png
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector4.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d1dc2574e29399b90a8b376882bd16b3b32f96cc281f172bb6bf4f71594e6037
-size 328
+oid sha256:ddef1d57d861335e487c5ecdaa4ff1530770d65aab69058f3f48675eb671abe6
+size 711
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector5.png b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector5.png
index 4ca38d845d8..321e309a2ba 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector5.png
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector5.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f1521303080bc06ff09dc3e6e67c42afe113dcbca9fd5d708bebe59bf2751a54
-size 21831
+oid sha256:1f682e51dd43fca42cb9592725e476aecdb6677463124b5566c2180923acddbe
+size 881
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector6.png b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector6.png
new file mode 100644
index 00000000000..564e3c78758
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Images/DecalProjector6.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa9f73b741574d9930dd763cae35847a4003d59b24d1997896d26b6f63f50e66
+size 16705
diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.Skin.cs b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.Skin.cs
index 98a337c4a73..0c8adc52a43 100644
--- a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.Skin.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.Skin.cs
@@ -6,7 +6,7 @@ partial class DecalProjectorEditor
{
const string k_EditShapePreservingUVTooltip = "Modifies the projector boundaries and crops/tiles the decal to fill them.";
const string k_EditShapeWithoutPreservingUVTooltip = "Modifies the projector boundaries and stretches the decal to fill them.";
- const string k_EditUVTooltip = "Modify the UV positions only";
+ const string k_EditUVTooltip = "Modify the UV and the pivot position without moving the projection box. It can alter Transform.";
static readonly GUIContent k_SizeContent = EditorGUIUtility.TrTextContent("Size", "Sets the size of the projector.");
static readonly GUIContent[] k_SizeSubContent = new[]
@@ -24,16 +24,8 @@ partial class DecalProjectorEditor
static readonly GUIContent k_UVBiasContent = EditorGUIUtility.TrTextContent("Offset", "Sets the offset for the decal Material. Moves the decal along its UV axes.");
static readonly GUIContent k_FadeFactorContent = EditorGUIUtility.TrTextContent("Fade Factor", "Controls the transparency of the decal.");
static readonly GUIContent k_AffectTransparentContent = EditorGUIUtility.TrTextContent("Affects Transparent", "When enabled, HDRP draws this projector's decal on top of transparent surfaces.");
+ static readonly GUIContent k_Offset = EditorGUIUtility.TrTextContent("Pivot", "Controls the position of the pivot point of the decal.");
- public static readonly Color k_GizmoColorBase = Color.white;
- public static readonly Color[] k_BaseHandlesColor = new Color[]
- {
- Color.white,
- Color.white,
- Color.white,
- Color.white,
- Color.white,
- Color.white
- };
+ public static readonly Color k_GizmoColorBase = new Color(1, 1, 1, 8f / 255);
}
}
diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs
index 89e7d567e57..97396e1feab 100644
--- a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs
@@ -1,7 +1,11 @@
using UnityEngine;
-using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEditor.ShortcutManagement;
+using UnityEditor.IMGUI.Controls;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Linq.Expressions;
using static UnityEditorInternal.EditMode;
namespace UnityEditor.Rendering.HighDefinition
@@ -10,6 +14,48 @@ namespace UnityEditor.Rendering.HighDefinition
[CanEditMultipleObjects]
partial class DecalProjectorEditor : Editor
{
+ const float k_Limit = 100000;
+ const float k_LimitInv = 1 / k_Limit;
+
+ static object s_ColorPref;
+ static Func GetColorPref;
+ static Color fullColor
+ {
+ get
+ {
+ Color c = s_LastColor;
+ c.a = 1;
+ return c;
+ }
+ }
+ static Color s_LastColor;
+ static void UpdateColorsInHandlesIfRequired()
+ {
+ Color c = GetColorPref();
+ if (c != s_LastColor)
+ {
+ if (s_BoxHandle != null && !s_BoxHandle.Equals(null))
+ s_BoxHandle = null;
+
+ if (s_uvHandles != null && !s_uvHandles.Equals(null))
+ s_uvHandles.baseColor = c;
+
+ s_LastColor = c;
+ }
+ }
+
+ static DecalProjectorEditor()
+ {
+ // PrefColor is the type to use to have a Color that is customizable inside the Preference/Colors panel.
+ // Sadly it is internal so we must create it and grab color from it by reflection.
+ Type prefColorType = typeof(Editor).Assembly.GetType("UnityEditor.PrefColor");
+ s_ColorPref = Activator.CreateInstance(prefColorType, new object[] { "Scene/Decal", k_GizmoColorBase.r, k_GizmoColorBase.g, k_GizmoColorBase.b, k_GizmoColorBase.a });
+ PropertyInfo colorInfo = prefColorType.GetProperty("Color");
+ MemberExpression colorProperty = Expression.Property(Expression.Constant(s_ColorPref, prefColorType), colorInfo);
+ Expression> colorLambda = Expression.Lambda>(colorProperty);
+ GetColorPref = colorLambda.Compile();
+ }
+
MaterialEditor m_MaterialEditor = null;
SerializedProperty m_MaterialProperty;
SerializedProperty m_DrawDistanceProperty;
@@ -21,7 +67,8 @@ partial class DecalProjectorEditor : Editor
SerializedProperty m_AffectsTransparencyProperty;
SerializedProperty m_Size;
SerializedProperty[] m_SizeValues;
- SerializedProperty m_OffsetZ;
+ SerializedProperty m_Offset;
+ SerializedProperty[] m_OffsetValues;
SerializedProperty m_FadeFactor;
SerializedProperty m_DecalLayerMask;
@@ -65,34 +112,50 @@ bool showAffectTransparencyHaveMultipleDifferentValue
}
}
- static HierarchicalBox s_Handle;
- static HierarchicalBox handle
+ static HierarchicalBox s_BoxHandle;
+ static HierarchicalBox boxHandle
{
get
{
- if (s_Handle == null || s_Handle.Equals(null))
+ if (s_BoxHandle == null || s_BoxHandle.Equals(null))
{
- s_Handle = new HierarchicalBox(k_GizmoColorBase, k_BaseHandlesColor);
- s_Handle.monoHandle = false;
+ Color c = fullColor;
+ s_BoxHandle = new HierarchicalBox(s_LastColor, new[] { c, c, c, c, c, c });
+ s_BoxHandle.SetBaseColor(s_LastColor);
+ s_BoxHandle.monoHandle = false;
}
- return s_Handle;
+ return s_BoxHandle;
}
}
+ static DisplacableRectHandles s_uvHandles;
+ static DisplacableRectHandles uvHandles
+ {
+ get
+ {
+ if (s_uvHandles == null || s_uvHandles.Equals(null))
+ s_uvHandles = new DisplacableRectHandles(s_LastColor);
+ return s_uvHandles;
+ }
+ }
+
+ static readonly BoxBoundsHandle s_AreaLightHandle =
+ new BoxBoundsHandle { axes = PrimitiveBoundsHandle.Axes.X | PrimitiveBoundsHandle.Axes.Y };
+
const SceneViewEditMode k_EditShapeWithoutPreservingUV = (SceneViewEditMode)90;
const SceneViewEditMode k_EditShapePreservingUV = (SceneViewEditMode)91;
- const SceneViewEditMode k_EditUV = (SceneViewEditMode)92;
+ const SceneViewEditMode k_EditUVAndPivot = (SceneViewEditMode)92;
static readonly SceneViewEditMode[] k_EditVolumeModes = new SceneViewEditMode[]
{
k_EditShapeWithoutPreservingUV,
k_EditShapePreservingUV
};
- static readonly SceneViewEditMode[] k_EditPivotModes = new SceneViewEditMode[]
+ static readonly SceneViewEditMode[] k_EditUVAndPivotModes = new SceneViewEditMode[]
{
- k_EditUV
+ k_EditUVAndPivot
};
- static SceneViewEditMode s_CurrentEditMode;
- static bool s_ModeSwitched;
+
+ static Func s_DrawPivotHandle;
static GUIContent[] k_EditVolumeLabels = null;
static GUIContent[] editVolumeLabels => k_EditVolumeLabels ?? (k_EditVolumeLabels = new GUIContent[]
@@ -106,11 +169,34 @@ static HierarchicalBox handle
EditorGUIUtility.TrIconContent("d_MoveTool", k_EditUVTooltip)
});
- static Editor s_Owner;
+ static List s_Instances = new List();
+
+ static DecalProjectorEditor FindEditorFromSelection()
+ {
+ GameObject[] selection = Selection.gameObjects;
+ DecalProjector[] selectionTargets = Selection.GetFiltered(SelectionMode.Unfiltered);
+
+ foreach (DecalProjectorEditor editor in s_Instances)
+ {
+ if (selectionTargets.Length != editor.targets.Length)
+ continue;
+ bool allOk = true;
+ foreach (DecalProjector selectionTarget in selectionTargets)
+ if (!Array.Find(editor.targets, t => t == selectionTarget))
+ {
+ allOk = false;
+ break;
+ }
+ if (!allOk)
+ continue;
+ return editor;
+ }
+ return null;
+ }
private void OnEnable()
{
- s_Owner = this;
+ s_Instances.Add(this);
// Create an instance of the MaterialEditor
UpdateMaterialEditor();
@@ -135,7 +221,13 @@ private void OnEnable()
m_Size.FindPropertyRelative("y"),
m_Size.FindPropertyRelative("z"),
};
- m_OffsetZ = serializedObject.FindProperty("m_Offset").FindPropertyRelative("z");
+ m_Offset = serializedObject.FindProperty("m_Offset");
+ m_OffsetValues = new[]
+ {
+ m_Offset.FindPropertyRelative("x"),
+ m_Offset.FindPropertyRelative("y"),
+ m_Offset.FindPropertyRelative("z"),
+ };
m_FadeFactor = serializedObject.FindProperty("m_FadeFactor");
m_DecalLayerMask = serializedObject.FindProperty("m_DecalLayerMask");
}
@@ -147,7 +239,8 @@ private void OnDisable()
if (decalProjector != null)
decalProjector.OnMaterialChange -= RequireUpdateMaterialEditor;
}
- s_Owner = null;
+
+ s_Instances.Remove(this);
}
private void OnDestroy() =>
@@ -162,7 +255,7 @@ public Bounds OnGetFrameBounds()
{
DecalProjector decalProjector = target as DecalProjector;
- return new Bounds(decalProjector.transform.position, handle.size);
+ return new Bounds(decalProjector.transform.position, boxHandle.size);
}
private bool m_RequireUpdateMaterialEditor = false;
@@ -197,161 +290,189 @@ void OnSceneGUI()
DrawHandles();
}
- void DrawHandles()
+ void DrawBoxTransformationHandles(DecalProjector decalProjector)
{
- //Note: each target need to be handled individually to allow multi edition
- DecalProjector decalProjector = target as DecalProjector;
-
- if (editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV)
+ using (new Handles.DrawingScope(fullColor, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, Vector3.one)))
{
- using (new Handles.DrawingScope(Color.white, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, Vector3.one)))
+ Vector3 centerStart = decalProjector.pivot;
+ boxHandle.center = centerStart;
+ boxHandle.size = decalProjector.size;
+
+ Vector3 boundsSizePreviousOS = boxHandle.size;
+ Vector3 boundsMinPreviousOS = boxHandle.size * -0.5f + boxHandle.center;
+
+ EditorGUI.BeginChangeCheck();
+ boxHandle.DrawHandle();
+ if (EditorGUI.EndChangeCheck())
{
- bool needToRefreshDecalProjector = false;
+ // Adjust decal transform if handle changed.
+ Undo.RecordObject(decalProjector, "Decal Projector Change");
- handle.center = decalProjector.offset;
- handle.size = decalProjector.size;
+ decalProjector.size = boxHandle.size;
+ decalProjector.pivot += boxHandle.center - centerStart;
- Vector3 boundsSizePreviousOS = handle.size;
- Vector3 boundsMinPreviousOS = handle.size * -0.5f + handle.center;
+ Vector3 boundsSizeCurrentOS = boxHandle.size;
+ Vector3 boundsMinCurrentOS = boxHandle.size * -0.5f + boxHandle.center;
- EditorGUI.BeginChangeCheck();
- handle.DrawHandle();
- if (EditorGUI.EndChangeCheck())
+ if (editMode == k_EditShapePreservingUV)
{
- needToRefreshDecalProjector = true;
-
- // Adjust decal transform if handle changed.
- Undo.RecordObject(decalProjector, "Decal Projector Change");
-
- decalProjector.size = handle.size;
- decalProjector.offset = handle.center;
-
- Vector3 boundsSizeCurrentOS = handle.size;
- Vector3 boundsMinCurrentOS = handle.size * -0.5f + handle.center;
-
- if (editMode == k_EditShapePreservingUV)
- {
- // Treat decal projector bounds as a crop tool, rather than a scale tool.
- // Compute a new uv scale and bias terms to pin decal projection pixels in world space, irrespective of projector bounds.
- Vector2 uvScale = decalProjector.uvScale;
- uvScale.x *= Mathf.Max(1e-5f, boundsSizeCurrentOS.x) / Mathf.Max(1e-5f, boundsSizePreviousOS.x);
- uvScale.y *= Mathf.Max(1e-5f, boundsSizeCurrentOS.y) / Mathf.Max(1e-5f, boundsSizePreviousOS.y);
- decalProjector.uvScale = uvScale;
-
- Vector2 uvBias = decalProjector.uvBias;
- uvBias.x += (boundsMinCurrentOS.x - boundsMinPreviousOS.x) / Mathf.Max(1e-5f, boundsSizeCurrentOS.x) * decalProjector.uvScale.x;
- uvBias.y += (boundsMinCurrentOS.y - boundsMinPreviousOS.y) / Mathf.Max(1e-5f, boundsSizeCurrentOS.y) * decalProjector.uvScale.y;
- decalProjector.uvBias = uvBias;
- }
-
- if (PrefabUtility.IsPartOfNonAssetPrefabInstance(decalProjector))
- {
- PrefabUtility.RecordPrefabInstancePropertyModifications(decalProjector);
- }
+ // Treat decal projector bounds as a crop tool, rather than a scale tool.
+ // Compute a new uv scale and bias terms to pin decal projection pixels in world space, irrespective of projector bounds.
+ Vector2 uvScale = decalProjector.uvScale;
+ uvScale.x *= Mathf.Max(1e-5f, boundsSizeCurrentOS.x) / Mathf.Max(1e-5f, boundsSizePreviousOS.x);
+ uvScale.y *= Mathf.Max(1e-5f, boundsSizeCurrentOS.y) / Mathf.Max(1e-5f, boundsSizePreviousOS.y);
+ decalProjector.uvScale = uvScale;
+
+ Vector2 uvBias = decalProjector.uvBias;
+ uvBias.x += (boundsMinCurrentOS.x - boundsMinPreviousOS.x) / Mathf.Max(1e-5f, boundsSizeCurrentOS.x) * decalProjector.uvScale.x;
+ uvBias.y += (boundsMinCurrentOS.y - boundsMinPreviousOS.y) / Mathf.Max(1e-5f, boundsSizeCurrentOS.y) * decalProjector.uvScale.y;
+ decalProjector.uvBias = uvBias;
}
- // Automatically recenter our transform component if necessary.
- // In order to correctly handle world-space snapping, we only perform this recentering when the user is no longer interacting with the gizmo.
- if ((GUIUtility.hotControl == 0) && (decalProjector.offset != Vector3.zero))
+ if (PrefabUtility.IsPartOfNonAssetPrefabInstance(decalProjector))
{
- needToRefreshDecalProjector = true;
-
- // Both the DecalProjectorComponent, and the transform will be modified.
- // The undo system will automatically group all RecordObject() calls here into a single action.
- Undo.RecordObject(decalProjector.transform, "Decal Projector Change");
-
- // Re-center the transform to the center of the decal projector bounds,
- // while maintaining the world-space coordinates of the decal projector boundings vertices.
- // Center of the decal projector is not the same of the HierarchicalBox as we want it to be on the z face as lights
- decalProjector.transform.Translate(decalProjector.offset + new Vector3(0f, 0f, handle.size.z * -0.5f), Space.Self);
-
- decalProjector.offset = new Vector3(0f, 0f, handle.size.z * 0.5f);
- if (PrefabUtility.IsPartOfNonAssetPrefabInstance(decalProjector))
- {
- PrefabUtility.RecordPrefabInstancePropertyModifications(decalProjector);
- }
+ PrefabUtility.RecordPrefabInstancePropertyModifications(decalProjector);
}
- if (needToRefreshDecalProjector)
+ // Smoothly update the decal image projected
+ DecalSystem.instance.UpdateCachedData(decalProjector.Handle, decalProjector.GetCachedDecalData());
+ }
+ }
+ }
+
+ void DrawPivotHandles(DecalProjector decalProjector)
+ {
+ using (new Handles.DrawingScope(fullColor, Matrix4x4.TRS(Vector3.zero, decalProjector.transform.rotation, Vector3.one)))
+ {
+ EditorGUI.BeginChangeCheck();
+ Vector3 newPosition = ProjectedTransform.DrawHandles(decalProjector.transform.position, .5f * decalProjector.size.z - decalProjector.pivot.z, decalProjector.transform.rotation);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObjects(new UnityEngine.Object[] { decalProjector, decalProjector.transform }, "Decal Projector Change");
+
+ decalProjector.pivot += Quaternion.Inverse(decalProjector.transform.rotation) * (decalProjector.transform.position - newPosition);
+ decalProjector.transform.position = newPosition;
+ }
+ }
+ }
+
+ void DrawUVHandles(DecalProjector decalProjector)
+ {
+ using (new Handles.DrawingScope(Matrix4x4.TRS(decalProjector.transform.position + decalProjector.transform.rotation * (decalProjector.pivot - .5f * decalProjector.size), decalProjector.transform.rotation, Vector3.one)))
+ {
+ Vector2 uvSize = new Vector2(
+ (decalProjector.uvScale.x > k_Limit || decalProjector.uvScale.x < -k_Limit) ? 0f : decalProjector.size.x / decalProjector.uvScale.x,
+ (decalProjector.uvScale.y > k_Limit || decalProjector.uvScale.y < -k_Limit) ? 0f : decalProjector.size.y / decalProjector.uvScale.y
+ );
+ Vector2 uvCenter = uvSize * .5f - new Vector2(decalProjector.uvBias.x * uvSize.x, decalProjector.uvBias.y * uvSize.y);
+
+ uvHandles.center = uvCenter;
+ uvHandles.size = uvSize;
+
+ EditorGUI.BeginChangeCheck();
+ uvHandles.DrawHandle();
+ if (EditorGUI.EndChangeCheck())
+ {
+ Undo.RecordObject(decalProjector, "Decal Projector Change");
+
+ Vector2 limit = new Vector2(Mathf.Abs(decalProjector.size.x * k_LimitInv), Mathf.Abs(decalProjector.size.y * k_LimitInv));
+ Vector2 uvScale = uvHandles.size;
+ for (int channel = 0; channel < 2; channel++)
{
- // Smoothly update the decal image projected
- DecalSystem.instance.UpdateCachedData(decalProjector.Handle, decalProjector.GetCachedDecalData());
+ if (Mathf.Abs(uvScale[channel]) > limit[channel])
+ uvScale[channel] = decalProjector.size[channel] / uvScale[channel];
+ else
+ uvScale[channel] = Mathf.Sign(decalProjector.size[channel]) * Mathf.Sign(uvScale[channel]) * k_Limit;
}
+ decalProjector.uvScale = uvScale;
+
+ var newUVStart = uvHandles.center - .5f * uvHandles.size;
+ decalProjector.uvBias = -new Vector2(
+ (uvHandles.size.x < k_LimitInv) && (uvHandles.size.x > -k_LimitInv) ? k_Limit * newUVStart.x / decalProjector.size.x : newUVStart.x / uvHandles.size.x, //parenthesis to force format tool
+ (uvHandles.size.y < k_LimitInv) && (uvHandles.size.y > -k_LimitInv) ? k_Limit * newUVStart.y / decalProjector.size.y : newUVStart.y / uvHandles.size.y //parenthesis to force format tool
+ );
}
}
+ }
- //[TODO: add editable pivot. Uncomment this when ready]
- //else if (editMode == k_EditUV)
- //{
- // //here should be handles code to manipulate the pivot without changing the UV
- //}
+ void DrawHandles()
+ {
+ DecalProjector decalProjector = target as DecalProjector;
+
+ if (editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV)
+ DrawBoxTransformationHandles(decalProjector);
+ else if (editMode == k_EditUVAndPivot)
+ {
+ DrawPivotHandles(decalProjector);
+ DrawUVHandles(decalProjector);
+ }
}
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void DrawGizmosSelected(DecalProjector decalProjector, GizmoType gizmoType)
{
+ UpdateColorsInHandlesIfRequired();
+
+ const float k_DotLength = 5f;
+
//draw them scale independent
- using (new Handles.DrawingScope(Color.white, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, Vector3.one)))
+ using (new Handles.DrawingScope(fullColor, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, Vector3.one)))
{
- handle.center = decalProjector.offset;
- handle.size = decalProjector.size;
- bool inEditMode = editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV;
- handle.DrawHull(inEditMode);
-
- Quaternion arrowRotation = Quaternion.LookRotation(Vector3.down, Vector3.right);
- float arrowSize = decalProjector.size.z * 0.25f;
- Vector3 pivot = decalProjector.offset;
- Vector3 projectedPivot = pivot + decalProjector.size.z * 0.5f * Vector3.back;
- Handles.ArrowHandleCap(0, projectedPivot, Quaternion.identity, arrowSize, EventType.Repaint);
-
- //[TODO: add editable pivot. Uncomment this when ready]
- //draw pivot
- //Handles.SphereHandleCap(controlID, pivot, Quaternion.identity, 0.02f, EventType.Repaint);
- //Color c = Color.white;
- //c.a = 0.2f;
- //Handles.color = c;
- //Handles.DrawLine(projectedPivot, projectedPivot + decalProjector.m_Size.x * 0.5f * Vector3.right);
- //Handles.DrawLine(projectedPivot, projectedPivot + decalProjector.m_Size.y * 0.5f * Vector3.up);
- //Handles.DrawLine(projectedPivot, projectedPivot + decalProjector.m_Size.z * 0.5f * Vector3.forward);
+ boxHandle.center = decalProjector.pivot;
+ boxHandle.size = decalProjector.size;
+ bool isVolumeEditMode = editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV;
+ bool isPivotEditMode = editMode == k_EditUVAndPivot;
+ boxHandle.DrawHull(isVolumeEditMode);
- //draw UV and bolder edges
- using (new Handles.DrawingScope(Matrix4x4.TRS(decalProjector.transform.position - decalProjector.transform.rotation * (decalProjector.size * 0.5f + decalProjector.offset.z * Vector3.back), decalProjector.transform.rotation, Vector3.one)))
+ Vector3 pivot = Vector3.zero;
+ Vector3 projectedPivot = new Vector3(0, 0, decalProjector.pivot.z - .5f * decalProjector.size.z);
+
+ if (isPivotEditMode)
{
- if (inEditMode)
- {
- Vector2 size = new Vector2(
- (decalProjector.uvScale.x > 100000 || decalProjector.uvScale.x < -100000 ? 0f : 1f / decalProjector.uvScale.x) * decalProjector.size.x,
- (decalProjector.uvScale.y > 100000 || decalProjector.uvScale.y < -100000 ? 0f : 1f / decalProjector.uvScale.y) * decalProjector.size.y
- );
- Vector2 start = (Vector2)projectedPivot - new Vector2(decalProjector.uvBias.x * size.x, decalProjector.uvBias.y * size.y);
- Handles.DrawDottedLines(
- new Vector3[]
- {
- start, start + new Vector2(size.x, 0),
- start + new Vector2(size.x, 0), start + size,
- start + size, start + new Vector2(0, size.y),
- start + new Vector2(0, size.y), start
- },
- 5f);
- }
+ Handles.DrawDottedLines(new[] { projectedPivot, pivot }, k_DotLength);
+ }
+ else
+ {
+ float arrowSize = decalProjector.size.z * 0.25f;
+ Handles.ArrowHandleCap(0, projectedPivot, Quaternion.identity, arrowSize, EventType.Repaint);
+ }
- Vector2 halfSize = decalProjector.size * .5f;
- Vector2 halfSize2 = new Vector2(halfSize.x, -halfSize.y);
- Vector2 center = (Vector2)projectedPivot + halfSize;
- Handles.DrawLine(center - halfSize, center - halfSize2, 3f);
- Handles.DrawLine(center - halfSize2, center + halfSize, 3f);
- Handles.DrawLine(center + halfSize, center + halfSize2, 3f);
- Handles.DrawLine(center + halfSize2, center - halfSize, 3f);
+ //draw UV and bolder edges
+ using (new Handles.DrawingScope(Matrix4x4.TRS(decalProjector.transform.position + decalProjector.transform.rotation * new Vector3(decalProjector.pivot.x, decalProjector.pivot.y, decalProjector.pivot.z - .5f * decalProjector.size.z), decalProjector.transform.rotation, Vector3.one)))
+ {
+ Vector2 UVSize = new Vector2(
+ (decalProjector.uvScale.x > k_Limit || decalProjector.uvScale.x < -k_Limit) ? 0f : decalProjector.size.x / decalProjector.uvScale.x,
+ (decalProjector.uvScale.y > k_Limit || decalProjector.uvScale.y < -k_Limit) ? 0f : decalProjector.size.y / decalProjector.uvScale.y
+ );
+ Vector2 UVCenter = UVSize * .5f - new Vector2(decalProjector.uvBias.x * UVSize.x, decalProjector.uvBias.y * UVSize.y) - (Vector2)decalProjector.size * .5f;
+
+ uvHandles.center = UVCenter;
+ uvHandles.size = UVSize;
+ uvHandles.DrawRect(dottedLine: true, screenSpaceSize: k_DotLength);
+
+ uvHandles.center = default;
+ uvHandles.size = decalProjector.size;
+ uvHandles.DrawRect(dottedLine: false, thickness: 3f);
}
}
}
- Bounds GetBoundsGetter()
+ static Func GetBoundsGetter(DecalProjector decalProjector)
{
- var bounds = new Bounds();
- var decalTransform = ((Component)target).transform;
- bounds.Encapsulate(decalTransform.position);
- return bounds;
+ return () =>
+ {
+ var bounds = new Bounds();
+ var decalTransform = decalProjector.transform;
+ bounds.Encapsulate(decalTransform.position);
+ return bounds;
+ };
+ }
+
+ void UpdateSize(int axe, float newSize, float oldSize)
+ {
+ m_SizeValues[axe].floatValue = newSize;
+ if (oldSize > Mathf.Epsilon)
+ m_OffsetValues[axe].floatValue *= newSize / oldSize;
}
public override void OnInspectorGUI()
@@ -368,10 +489,8 @@ public override void OnInspectorGUI()
{
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
- DoInspectorToolbar(k_EditVolumeModes, editVolumeLabels, GetBoundsGetter, this);
-
- //[TODO: add editable pivot. Uncomment this when ready]
- //DoInspectorToolbar(k_EditPivotModes, editPivotLabels, GetBoundsGetter, this);
+ DoInspectorToolbar(k_EditVolumeModes, editVolumeLabels, GetBoundsGetter(target as DecalProjector), this);
+ DoInspectorToolbar(k_EditUVAndPivotModes, editPivotLabels, GetBoundsGetter(target as DecalProjector), this);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
@@ -385,20 +504,22 @@ public override void OnInspectorGUI()
EditorGUI.MultiFloatField(rect, k_SizeContent, k_SizeSubContent, size);
if (EditorGUI.EndChangeCheck())
{
- m_SizeValues[0].floatValue = Mathf.Max(0, size[0]);
- m_SizeValues[1].floatValue = Mathf.Max(0, size[1]);
+ for (int i = 0; i < 2; ++i)
+ UpdateSize(i, Mathf.Max(0, size[i]), m_SizeValues[i].floatValue);
}
EditorGUI.EndProperty();
EditorGUI.EndProperty();
EditorGUI.BeginChangeCheck();
+ float oldSizeZ = m_SizeValues[2].floatValue;
EditorGUILayout.PropertyField(m_SizeValues[2], k_ProjectionDepthContent);
if (EditorGUI.EndChangeCheck())
{
- m_SizeValues[2].floatValue = Mathf.Max(0, m_SizeValues[2].floatValue);
- m_OffsetZ.floatValue = m_SizeValues[2].floatValue * 0.5f;
+ UpdateSize(2, Mathf.Max(0, m_SizeValues[2].floatValue), oldSizeZ);
}
+ EditorGUILayout.PropertyField(m_Offset, k_Offset);
+
EditorGUILayout.PropertyField(m_MaterialProperty, k_MaterialContent);
bool decalLayerEnabled = false;
@@ -503,34 +624,48 @@ public override void OnInspectorGUI()
static void EnterEditModeWithoutPreservingUV(ShortcutArguments args)
{
//If editor is not there, then the selected GameObject does not contains a DecalProjector
- if (s_Owner == null || s_Owner.Equals(null))
+ DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent();
+ if (activeDecalProjector == null || activeDecalProjector.Equals(null))
return;
- ChangeEditMode(k_EditShapeWithoutPreservingUV, (s_Owner as DecalProjectorEditor).GetBoundsGetter(), s_Owner);
+ ChangeEditMode(k_EditShapeWithoutPreservingUV, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
}
[Shortcut("HDRP/Decal: Handle changing size cropping UV", typeof(SceneView), KeyCode.Keypad2, ShortcutModifiers.Action)]
static void EnterEditModePreservingUV(ShortcutArguments args)
{
//If editor is not there, then the selected GameObject does not contains a DecalProjector
- if (s_Owner == null || s_Owner.Equals(null))
+ DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent();
+ if (activeDecalProjector == null || activeDecalProjector.Equals(null))
return;
- ChangeEditMode(k_EditShapePreservingUV, (s_Owner as DecalProjectorEditor).GetBoundsGetter(), s_Owner);
+ ChangeEditMode(k_EditShapePreservingUV, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
}
- //[TODO: add editable pivot. Uncomment this when ready]
- //[Shortcut("HDRP/Decal: Handle changing pivot position while preserving UV position", typeof(SceneView), KeyCode.Keypad3, ShortcutModifiers.Action)]
- //static void EnterEditModePivotPreservingUV(ShortcutArguments args) =>
- // ChangeEditMode(k_EditUV, (s_Owner as DecalProjectorComponentEditor).GetBoundsGetter(), s_Owner);
+ [Shortcut("HDRP/Decal: Handle changing pivot position and UVs", typeof(SceneView), KeyCode.Keypad3, ShortcutModifiers.Action)]
+ static void EnterEditModePivotPreservingUV(ShortcutArguments args)
+ {
+ //If editor is not there, then the selected GameObject does not contains a DecalProjector
+ DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent();
+ if (activeDecalProjector == null || activeDecalProjector.Equals(null))
+ return;
+
+ ChangeEditMode(k_EditUVAndPivot, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
+ }
[Shortcut("HDRP/Decal: Handle swap between cropping and stretching UV", typeof(SceneView), KeyCode.W, ShortcutModifiers.Action)]
static void SwappingEditUVMode(ShortcutArguments args)
{
+ //If editor is not there, then the selected GameObject does not contains a DecalProjector
+ DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent();
+ if (activeDecalProjector == null || activeDecalProjector.Equals(null))
+ return;
+
SceneViewEditMode targetMode = SceneViewEditMode.None;
switch (editMode)
{
case k_EditShapePreservingUV:
+ case k_EditUVAndPivot:
targetMode = k_EditShapeWithoutPreservingUV;
break;
case k_EditShapeWithoutPreservingUV:
@@ -538,10 +673,18 @@ static void SwappingEditUVMode(ShortcutArguments args)
break;
}
if (targetMode != SceneViewEditMode.None)
- ChangeEditMode(targetMode, (s_Owner as DecalProjectorEditor).GetBoundsGetter(), s_Owner);
+ ChangeEditMode(targetMode, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
}
[Shortcut("HDRP/Decal: Stop Editing", typeof(SceneView), KeyCode.Keypad0, ShortcutModifiers.Action)]
- static void ExitEditMode(ShortcutArguments args) => QuitEditMode();
+ static void ExitEditMode(ShortcutArguments args)
+ {
+ //If editor is not there, then the selected GameObject does not contains a DecalProjector
+ DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent();
+ if (activeDecalProjector == null || activeDecalProjector.Equals(null))
+ return;
+
+ QuitEditMode();
+ }
}
}
diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DisplacableRectHandles.cs b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DisplacableRectHandles.cs
new file mode 100644
index 00000000000..6579041a564
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DisplacableRectHandles.cs
@@ -0,0 +1,296 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
+
+namespace UnityEditor.Rendering.HighDefinition
+{
+ class DisplacableRectHandles
+ {
+ const float k_HandleSizeCoef = 0.05f;
+
+ enum NamedEdge { Right, Top, Left, Bottom, None }
+
+ int[] m_ControlIDs = new int[4] { 0, 0, 0, 0 };
+ Color m_MonochromeHandleColor;
+ Color m_WireframeColor;
+ Color m_WireframeColorBehind;
+
+ /// The position of the center of the box in Handle.matrix space. On plane z=0.
+ public Vector2 center { get; set; }
+
+ /// The size of the box in Handle.matrix space. On plane z=0.
+ public Vector2 size { get; set; }
+
+ //Note: Handles.Slider not allow to use a specific ControlID.
+ //Thus Slider1D is used (with reflection)
+ static Type k_Slider1D = Type.GetType("UnityEditorInternal.Slider1D, UnityEditor");
+ static MethodInfo k_Slider1D_Do = k_Slider1D
+ .GetMethod(
+ "Do",
+ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
+ null,
+ CallingConventions.Any,
+ new[] { typeof(int), typeof(Vector3), typeof(Vector3), typeof(float), typeof(Handles.CapFunction), typeof(float) },
+ null);
+ static void Slider1D(int controlID, ref Vector3 handlePosition, Vector3 handleOrientation, float snapScale)
+ {
+ handlePosition = (Vector3)k_Slider1D_Do.Invoke(null, new object[]
+ {
+ controlID,
+ handlePosition,
+ handleOrientation,
+ HandleUtility.GetHandleSize(handlePosition) * k_HandleSizeCoef,
+ new Handles.CapFunction(Handles.DotHandleCap),
+ snapScale
+ });
+ }
+
+ /// The baseColor used to draw the rect.
+ public Color baseColor
+ {
+ get { return m_MonochromeHandleColor; }
+ set
+ {
+ m_MonochromeHandleColor = GizmoUtility.GetHandleColor(value);
+ m_WireframeColor = GizmoUtility.GetWireframeColor(value);
+ m_WireframeColorBehind = GizmoUtility.GetWireframeColorBehindObjects(value);
+ }
+ }
+
+ public DisplacableRectHandles(Color baseColor)
+ {
+ this.baseColor = baseColor;
+ }
+
+ /// Draw the rect.
+ public void DrawRect(bool dottedLine = false, float thickness = .0f, float screenSpaceSize = 5f)
+ {
+ Vector2 start = center - size * .5f;
+ Vector3[] positions = new Vector3[]
+ {
+ start,
+ start + size * Vector2.right,
+ start + size,
+ start + size * Vector2.up
+ };
+ Vector3[] edges = new Vector3[]
+ {
+ positions[0], positions[1],
+ positions[1], positions[2],
+ positions[2], positions[3],
+ positions[3], positions[0],
+ };
+
+ void Draw()
+ {
+ if (dottedLine)
+ Handles.DrawDottedLines(edges, screenSpaceSize);
+ else
+ {
+ Handles.DrawLine(positions[0], positions[1], thickness);
+ Handles.DrawLine(positions[1], positions[2], thickness);
+ Handles.DrawLine(positions[2], positions[3], thickness);
+ Handles.DrawLine(positions[3], positions[0], thickness);
+ }
+ }
+
+ Color previousColor = Handles.color;
+ Handles.color = m_WireframeColor;
+ Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
+ Draw();
+ Handles.color = m_WireframeColorBehind;
+ Handles.zTest = UnityEngine.Rendering.CompareFunction.Greater;
+ Draw();
+ Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
+ Handles.color = previousColor;
+ }
+
+ NamedEdge DrawSliders(ref Vector3 leftPosition, ref Vector3 rightPosition, ref Vector3 topPosition, ref Vector3 bottomPosition)
+ {
+ NamedEdge theChangedEdge = NamedEdge.None;
+
+ using (new Handles.DrawingScope(m_MonochromeHandleColor))
+ {
+ EditorGUI.BeginChangeCheck();
+ Slider1D(m_ControlIDs[(int)NamedEdge.Left], ref leftPosition, Vector3.left, EditorSnapSettings.scale);
+ if (EditorGUI.EndChangeCheck())
+ theChangedEdge = NamedEdge.Left;
+
+ EditorGUI.BeginChangeCheck();
+ Slider1D(m_ControlIDs[(int)NamedEdge.Right], ref rightPosition, Vector3.right, EditorSnapSettings.scale);
+ if (EditorGUI.EndChangeCheck())
+ theChangedEdge = NamedEdge.Right;
+
+ EditorGUI.BeginChangeCheck();
+ Slider1D(m_ControlIDs[(int)NamedEdge.Top], ref topPosition, Vector3.up, EditorSnapSettings.scale);
+ if (EditorGUI.EndChangeCheck())
+ theChangedEdge = NamedEdge.Top;
+
+ EditorGUI.BeginChangeCheck();
+ Slider1D(m_ControlIDs[(int)NamedEdge.Bottom], ref bottomPosition, Vector3.down, EditorSnapSettings.scale);
+ if (EditorGUI.EndChangeCheck())
+ theChangedEdge = NamedEdge.Bottom;
+ }
+
+ return theChangedEdge;
+ }
+
+ void EnsureEdgeFacesOutsideForHomothety(NamedEdge theChangedEdge, ref Vector3 leftPosition, ref Vector3 rightPosition, ref Vector3 topPosition, ref Vector3 bottomPosition)
+ {
+ switch (theChangedEdge)
+ {
+ case NamedEdge.Left:
+ if (rightPosition.x < leftPosition.x)
+ leftPosition.x = rightPosition.x;
+ if (topPosition.y < bottomPosition.y)
+ topPosition.y = bottomPosition.y = center.y;
+ break;
+ case NamedEdge.Right:
+ if (rightPosition.x < leftPosition.x)
+ rightPosition.x = leftPosition.x;
+ if (topPosition.y < bottomPosition.y)
+ topPosition.y = bottomPosition.y = center.y;
+ break;
+ case NamedEdge.Top:
+ if (topPosition.y < bottomPosition.y)
+ topPosition.y = bottomPosition.y;
+ if (rightPosition.x < leftPosition.x)
+ rightPosition.x = leftPosition.x = center.x;
+ break;
+ case NamedEdge.Bottom:
+ if (topPosition.y < bottomPosition.y)
+ bottomPosition.y = topPosition.y;
+ if (rightPosition.x < leftPosition.x)
+ rightPosition.x = leftPosition.x = center.x;
+ break;
+ }
+ }
+
+ void EnsureEdgeFacesOutsideForSymetry(NamedEdge theChangedEdge, ref Vector3 leftPosition, ref Vector3 rightPosition, ref Vector3 topPosition, ref Vector3 bottomPosition)
+ {
+ switch (theChangedEdge)
+ {
+ case NamedEdge.Left:
+ case NamedEdge.Right:
+ if (rightPosition.x < leftPosition.x)
+ rightPosition.x = leftPosition.x = center.x;
+ break;
+ case NamedEdge.Top:
+ case NamedEdge.Bottom:
+ if (topPosition.y < bottomPosition.y)
+ topPosition.y = bottomPosition.y = center.y;
+ break;
+ }
+ }
+
+ void EnsureEdgeFacesOutsideForOtherTransformation(ref Vector2 max, ref Vector2 min)
+ {
+ for (int axis = 0; axis < 2; ++axis)
+ {
+ if (min[axis] > max[axis])
+ {
+ // Control IDs in m_ControlIDs[0-1] are for positive axes
+ if (GUIUtility.hotControl == m_ControlIDs[axis])
+ max[axis] = min[axis];
+ else
+ min[axis] = max[axis];
+ }
+ }
+ }
+
+ /// Draw the manipulable handles
+ public void DrawHandle()
+ {
+ Event evt = Event.current;
+ bool useHomothety = evt.shift;
+ bool useSymetry = evt.alt || evt.command;
+ // Note: snapping is handled natively on ctrl for each Slider1D
+
+ for (int i = 0, count = m_ControlIDs.Length; i < count; ++i)
+ m_ControlIDs[i] = GUIUtility.GetControlID("DisplacableRectHandles".GetHashCode() + i, FocusType.Passive);
+
+ Vector3 leftPosition = center + size.x * .5f * Vector2.left;
+ Vector3 rightPosition = center + size.x * .5f * Vector2.right;
+ Vector3 topPosition = center + size.y * .5f * Vector2.up;
+ Vector3 bottomPosition = center + size.y * .5f * Vector2.down;
+
+ var theChangedEdge = NamedEdge.None;
+
+ EditorGUI.BeginChangeCheck();
+ theChangedEdge = DrawSliders(ref leftPosition, ref rightPosition, ref topPosition, ref bottomPosition);
+ if (EditorGUI.EndChangeCheck())
+ {
+ float delta = 0f;
+ switch (theChangedEdge)
+ {
+ case NamedEdge.Left: delta = ((Vector2)leftPosition - center - size.x * .5f * Vector2.left).x; break;
+ case NamedEdge.Right: delta = -((Vector2)rightPosition - center - size.x * .5f * Vector2.right).x; break;
+ case NamedEdge.Top: delta = -((Vector2)topPosition - center - size.y * .5f * Vector2.up).y; break;
+ case NamedEdge.Bottom: delta = ((Vector2)bottomPosition - center - size.y * .5f * Vector2.down).y; break;
+ }
+
+ if (useHomothety && useSymetry)
+ {
+ var tempSize = size - Vector2.one * delta;
+
+ //ensure that the rect edges are still facing outside
+ for (int axis = 0; axis < 3; ++axis)
+ {
+ if (tempSize[axis] < 0)
+ {
+ delta += tempSize[axis];
+ tempSize = size - Vector2.one * delta;
+ }
+ }
+
+ size = tempSize;
+ }
+ else
+ {
+ if (useSymetry)
+ {
+ switch (theChangedEdge)
+ {
+ case NamedEdge.Left: rightPosition.x -= delta; break;
+ case NamedEdge.Right: leftPosition.x += delta; break;
+ case NamedEdge.Top: bottomPosition.y += delta; break;
+ case NamedEdge.Bottom: topPosition.y -= delta; break;
+ }
+
+ EnsureEdgeFacesOutsideForSymetry(theChangedEdge, ref leftPosition, ref rightPosition, ref topPosition, ref bottomPosition);
+ }
+
+ if (useHomothety)
+ {
+ float halfDelta = delta * 0.5f;
+ switch (theChangedEdge)
+ {
+ case NamedEdge.Left:
+ case NamedEdge.Right:
+ bottomPosition.y += halfDelta;
+ topPosition.y -= halfDelta;
+ break;
+ case NamedEdge.Top:
+ case NamedEdge.Bottom:
+ rightPosition.x -= halfDelta;
+ leftPosition.x += halfDelta;
+ break;
+ }
+
+ EnsureEdgeFacesOutsideForHomothety(theChangedEdge, ref leftPosition, ref rightPosition, ref topPosition, ref bottomPosition);
+ }
+
+ var max = new Vector2(rightPosition.x, topPosition.y);
+ var min = new Vector2(leftPosition.x, bottomPosition.y);
+
+ if (!useSymetry && !useHomothety)
+ EnsureEdgeFacesOutsideForOtherTransformation(ref max, ref min);
+
+ center = (max + min) * .5f;
+ size = max - min;
+ }
+ }
+ }
+ }
+}
diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DisplacableRectHandles.cs.meta b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DisplacableRectHandles.cs.meta
new file mode 100644
index 00000000000..335e9f208c5
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DisplacableRectHandles.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ec7aa4f206331154bacd45514acb99b1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/ProjectedTransform.cs b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/ProjectedTransform.cs
new file mode 100644
index 00000000000..5b1dcc86277
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/ProjectedTransform.cs
@@ -0,0 +1,246 @@
+using System;
+using System.Linq.Expressions;
+using System.Reflection;
+using UnityEditor;
+
+namespace UnityEngine.Rendering.HighDefinition
+{
+ class ProjectedTransform
+ {
+ struct PositionHandleIds
+ {
+ static int s_xAxisMoveHandleHash = "xAxisDecalPivot".GetHashCode();
+ static int s_yAxisMoveHandleHash = "yAxisDecalPivot".GetHashCode();
+ static int s_zAxisMoveHandleHash = "zAxisDecalPivot".GetHashCode();
+ static int s_xyAxisMoveHandleHash = "xyAxisDecalPivot".GetHashCode();
+
+ public static PositionHandleIds @default
+ {
+ get
+ {
+ return new PositionHandleIds(
+ GUIUtility.GetControlID(s_xAxisMoveHandleHash, FocusType.Passive),
+ GUIUtility.GetControlID(s_yAxisMoveHandleHash, FocusType.Passive),
+ GUIUtility.GetControlID(s_zAxisMoveHandleHash, FocusType.Passive),
+ GUIUtility.GetControlID(s_xyAxisMoveHandleHash, FocusType.Passive)
+ );
+ }
+ }
+
+ public readonly int x, y, z, xy;
+
+ public int this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0: return x;
+ case 1: return y;
+ case 2: return z;
+ case 3: return xy;
+ }
+ return -1;
+ }
+ }
+
+ public bool Has(int id)
+ {
+ return x == id
+ || y == id
+ || z == id
+ || xy == id;
+ }
+
+ public PositionHandleIds(int x, int y, int z, int xy)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.xy = xy;
+ }
+
+ public override int GetHashCode()
+ {
+ return x ^ y ^ z ^ xy;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is PositionHandleIds o))
+ return false;
+
+ return o.x == x && o.y == y && o.z == z && o.xy == xy;
+ }
+ }
+
+ struct PositionHandleParam
+ {
+ public static PositionHandleParam defaultHandleXY = new PositionHandleParam(
+ Handle.X | Handle.Y | Handle.XY,
+ Vector3.zero, Vector3.one, Vector3.zero, Vector3.one * .25f,
+ Orientation.Signed, Orientation.Camera);
+
+ public static PositionHandleParam defaultHandleZ = new PositionHandleParam(
+ Handle.Z,
+ Vector3.zero, Vector3.one, Vector3.zero, Vector3.one * .25f,
+ Orientation.Signed, Orientation.Camera);
+
+ [Flags]
+ public enum Handle
+ {
+ None = 0,
+ X = 1 << 0,
+ Y = 1 << 1,
+ Z = 1 << 2,
+ XY = 1 << 3,
+ All = ~None
+ }
+
+ public enum Orientation
+ {
+ Signed,
+ Camera
+ }
+
+ public readonly Vector3 axisOffset;
+ public readonly Vector3 axisSize;
+ public readonly Vector3 planeOffset;
+ public readonly Vector3 planeSize;
+ public readonly Handle handles;
+ public readonly Orientation axesOrientation;
+ public readonly Orientation planeOrientation;
+
+ public bool ShouldShow(int axis)
+ {
+ return (handles & (Handle)(1 << axis)) != 0;
+ }
+
+ public bool ShouldShow(Handle handle)
+ {
+ return (handles & handle) != 0;
+ }
+
+ public PositionHandleParam(
+ Handle handles,
+ Vector3 axisOffset,
+ Vector3 axisSize,
+ Vector3 planeOffset,
+ Vector3 planeSize,
+ Orientation axesOrientation,
+ Orientation planeOrientation)
+ {
+ this.axisOffset = axisOffset;
+ this.axisSize = axisSize;
+ this.planeOffset = planeOffset;
+ this.planeSize = planeSize;
+ this.handles = handles;
+ this.axesOrientation = axesOrientation;
+ this.planeOrientation = planeOrientation;
+ }
+ }
+
+ static PositionHandleParam paramXY = PositionHandleParam.defaultHandleXY;
+ static PositionHandleParam paramZ = PositionHandleParam.defaultHandleZ;
+ static PositionHandleIds ids = PositionHandleIds.@default;
+
+ static int[] s_DoPositionHandle_Internal_NextIndex = { 1, 2, 0 };
+ static int[] s_DoPositionHandle_Internal_PrevIndex = { 2, 0, 1 };
+ static Vector3[] verts = { Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero };
+
+ static Func s_IsGridSnappingActive;
+
+
+ static ProjectedTransform()
+ {
+ //We need to know if grid snaping is active or not in Editor. Sadly this is internal so we must grab it by reflection.
+ Type gridSnappingType = typeof(Handles).Assembly.GetType("UnityEditor.GridSnapping");
+ PropertyInfo activePropertyInfo = gridSnappingType.GetProperty("active", BindingFlags.Public | BindingFlags.Static);
+ MethodCallExpression activePropertyGetCall = Expression.Call(null, activePropertyInfo.GetGetMethod());
+ var activeGetLambda = Expression.Lambda>(activePropertyGetCall);
+ s_IsGridSnappingActive = activeGetLambda.Compile();
+ }
+
+ static bool IsHovering(int controlID, Event evt)
+ {
+ return controlID == HandleUtility.nearestControl && GUIUtility.hotControl == 0 && !Tools.viewToolActive;
+ }
+
+ public static Vector3 DrawHandles(Vector3 position, float zProjectionDistance, Quaternion rotation)
+ {
+ var isHot = ids.Has(GUIUtility.hotControl);
+ var planeSize = isHot ? paramXY.planeSize + paramXY.planeOffset : paramXY.planeSize;
+ var planarSize = Mathf.Max(planeSize[0], planeSize[s_DoPositionHandle_Internal_NextIndex[0]]);
+ Vector3 sliderRotatedWorldPos = Quaternion.Inverse(rotation) * position;
+ var size1D = HandleUtility.GetHandleSize(sliderRotatedWorldPos);
+ var size2D = HandleUtility.GetHandleSize(sliderRotatedWorldPos - new Vector3(0, 0, zProjectionDistance)) * planarSize * .5f;
+ Vector3 depthSlider = sliderRotatedWorldPos;
+
+ EditorGUI.BeginChangeCheck();
+ {
+ // dot offset = transform position seen as a sphere
+ EditorGUI.BeginChangeCheck();
+ depthSlider = Handles.Slider(depthSlider, Vector3.forward, size1D * .1f, Handles.SphereHandleCap, -1);
+ if (EditorGUI.EndChangeCheck())
+ sliderRotatedWorldPos.z = depthSlider.z;
+
+ // 2D slider: square xy-axis
+ Vector3 sliderFaceProjected = sliderRotatedWorldPos - new Vector3(0, 0, zProjectionDistance);
+ sliderFaceProjected.x += size2D;
+ sliderFaceProjected.y += size2D;
+ using (new Handles.DrawingScope(Handles.zAxisColor))
+ {
+ verts[0] = sliderFaceProjected + (Vector3.right + Vector3.up) * size2D;
+ verts[1] = sliderFaceProjected + (-Vector3.right + Vector3.up) * size2D;
+ verts[2] = sliderFaceProjected + (-Vector3.right - Vector3.up) * size2D;
+ verts[3] = sliderFaceProjected + (Vector3.right - Vector3.up) * size2D;
+ float faceOpacity = 0.8f;
+ if (GUIUtility.hotControl == ids.xy)
+ Handles.color = Handles.selectedColor;
+ else if (IsHovering(ids.xy, Event.current))
+ faceOpacity = 0.4f;
+ else
+ faceOpacity = 0.1f;
+ Color faceColor = new Color(Handles.zAxisColor.r, Handles.zAxisColor.g, Handles.zAxisColor.b, Handles.zAxisColor.a * faceOpacity);
+ Handles.DrawSolidRectangleWithOutline(verts, faceColor, Color.clear);
+ EditorGUI.BeginChangeCheck();
+ sliderFaceProjected = Handles.Slider2D(ids.xy, sliderFaceProjected, Vector3.forward, Vector3.right, Vector3.up, size2D, Handles.RectangleHandleCap, s_IsGridSnappingActive() ? Vector2.zero : new Vector2(EditorSnapSettings.move[0], EditorSnapSettings.move[1]), false);
+ if (EditorGUI.EndChangeCheck())
+ {
+ sliderRotatedWorldPos.x = sliderFaceProjected.x;
+ sliderRotatedWorldPos.y = sliderFaceProjected.y;
+ }
+ }
+ sliderFaceProjected.x -= size2D;
+ sliderFaceProjected.y -= size2D;
+
+ // 2D slider: x-axis
+ EditorGUI.BeginChangeCheck();
+ using (new Handles.DrawingScope(Handles.xAxisColor))
+ sliderFaceProjected = Handles.Slider(sliderFaceProjected, Vector3.right);
+ if (EditorGUI.EndChangeCheck())
+ sliderRotatedWorldPos.x = sliderFaceProjected.x;
+
+ // 2D slider: y-axis
+ EditorGUI.BeginChangeCheck();
+ using (new Handles.DrawingScope(Handles.yAxisColor))
+ sliderFaceProjected = Handles.Slider(sliderFaceProjected, Vector3.up);
+ if (EditorGUI.EndChangeCheck())
+ sliderRotatedWorldPos.y = sliderFaceProjected.y;
+
+ // depth: z-axis
+ EditorGUI.BeginChangeCheck();
+ using (new Handles.DrawingScope(Handles.zAxisColor))
+ depthSlider = Handles.Slider(depthSlider, Vector3.forward);
+ if (EditorGUI.EndChangeCheck())
+ sliderRotatedWorldPos.z = depthSlider.z;
+ }
+ if (EditorGUI.EndChangeCheck())
+ {
+ position = rotation * sliderRotatedWorldPos;
+ }
+
+ return position;
+ }
+ }
+}
diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Decal/ProjectedTransform.cs.meta b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/ProjectedTransform.cs.meta
new file mode 100644
index 00000000000..c25147271e6
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Editor/Material/Decal/ProjectedTransform.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1955ced6df3d87641b9abc7588ca00da
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalProjector.cs b/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalProjector.cs
index 27e9764c59b..ee36a76a410 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalProjector.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalProjector.cs
@@ -182,10 +182,10 @@ public DecalLayerEnum decalLayerMask
[SerializeField]
private Vector3 m_Offset = new Vector3(0, 0, 0.5f);
///
- /// Change the offset position.
- /// Do not expose: Could be changed by the inspector when manipulating the gizmo.
+ /// Change the pivot position.
+ /// It is an offset between the center of the projection and the transform position.
///
- internal Vector3 offset
+ public Vector3 pivot
{
get
{
@@ -202,13 +202,11 @@ internal Vector3 offset
Vector3 m_Size = new Vector3(1, 1, 1);
///
/// The size of the projection volume.
+ /// See also to rescale relatively to the pivot position.
///
public Vector3 size
{
- get
- {
- return m_Size;
- }
+ get => m_Size;
set
{
m_Size = value;
@@ -216,6 +214,18 @@ public Vector3 size
}
}
+ ///
+ /// Update the pivot to resize centered on the pivot position.
+ ///
+ /// The new size.
+ public void ResizeAroundPivot(Vector3 newSize)
+ {
+ for (int axis = 0; axis < 3; ++axis)
+ if (m_Size[axis] > Mathf.Epsilon)
+ m_Offset[axis] *= newSize[axis] / m_Size[axis];
+ size = newSize;
+ }
+
[SerializeField]
[Range(0, 1)]
private float m_FadeFactor = 1.0f;