diff --git a/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditor.cs b/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditor.cs index 8c40551adc9..9e6660fbde7 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditor.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditor.cs @@ -30,6 +30,10 @@ public class LightAnchorEditor : Editor VisualElement m_GameViewRootElement; VisualElement m_ClickCatcher; + SerializedProperty m_DistanceProperty; + SerializedProperty m_FrameSpaceProperty; + SerializedProperty m_AnchorPositionOverrideProperty; + LightAnchor manipulator { get { return target as LightAnchor; } @@ -62,6 +66,7 @@ public override void OnInspectorGUI() bool pitchChanged = false; bool rollChanged = false; bool distanceChanged = false; + bool positionOverrideChanged = false; bool upChanged = false; using (var change = new EditorGUI.ChangeCheckScope()) @@ -126,14 +131,17 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); EditorGUI.BeginChangeCheck(); - m_Distance = EditorGUILayout.FloatField(LightAnchorStyles.distanceProperty, manipulator.distance); + EditorGUILayout.PropertyField(m_DistanceProperty, LightAnchorStyles.distanceProperty); distanceChanged = EditorGUI.EndChangeCheck(); EditorGUI.BeginChangeCheck(); - var dropRect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight); - m_FrameSpace = (LightAnchor.UpDirection)EditorGUI.EnumPopup(dropRect, LightAnchorStyles.upDirectionProperty, manipulator.frameSpace); + EditorGUILayout.PropertyField(m_FrameSpaceProperty, LightAnchorStyles.upDirectionProperty); upChanged = EditorGUI.EndChangeCheck(); + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(m_AnchorPositionOverrideProperty, LightAnchorStyles.anchorPositionOverrideProperty); + positionOverrideChanged = EditorGUI.EndChangeCheck(); + if (m_FoldoutPreset = EditorGUILayout.Foldout(m_FoldoutPreset, "Common")) { Color cachedColor = GUI.backgroundColor; @@ -266,11 +274,11 @@ public override void OnInspectorGUI() { Undo.RecordObjects(new UnityEngine.Object[] { target, manipulator.transform }, "Light Anchor Change"); - manipulator.frameSpace = m_FrameSpace; + manipulator.frameSpace = (LightAnchor.UpDirection)m_FrameSpaceProperty.intValue; manipulator.SynchronizeOnTransform(camera); UpdateCache(); } - if (yawChanged || pitchChanged || rollChanged || distanceChanged) + if (yawChanged || pitchChanged || rollChanged || distanceChanged || positionOverrideChanged) { Undo.RecordObjects(new UnityEngine.Object[] { target, manipulator.transform }, "Light Anchor Change"); @@ -281,7 +289,32 @@ public override void OnInspectorGUI() if (rollChanged) manipulator.roll = m_Roll; if (distanceChanged) - manipulator.distance = m_Distance; + manipulator.distance = m_DistanceProperty.floatValue; + if (positionOverrideChanged) + { + var newTransform = m_AnchorPositionOverrideProperty.objectReferenceValue as Transform; + + if (newTransform != null) + { + // Check that the assigned transform is not child of the light anchor, otherwise it would cause problems when moving the light position + if (newTransform.IsChildOf(manipulator.transform)) + Debug.LogError($"Can't assign '{newTransform.name}' because it's a child of the Light Anchor component"); + else + { + float newDistance = Vector3.Distance(manipulator.transform.position, newTransform.position); + manipulator.anchorPositionOverride = newTransform; + // Orient the object to face the new override position + manipulator.SynchronizeOnTransform(camera); + // And adjust it's distance to avoid modifying it's position. + manipulator.distance = newDistance; + } + } + else + manipulator.anchorPositionOverride = newTransform; + } + + if (manipulator.anchorPositionOverride != null) + anchor = manipulator.anchorPosition; manipulator.UpdateTransform(camera, anchor); IsCacheInvalid(manipulator); @@ -321,6 +354,10 @@ void OnEnable() EnableClickCatcher(m_EnableClickCatcher); } } + + m_DistanceProperty = serializedObject.FindProperty("m_Distance"); + m_FrameSpaceProperty = serializedObject.FindProperty("m_FrameSpace"); + m_AnchorPositionOverrideProperty = serializedObject.FindProperty("m_AnchorPositionOverride"); } void EditorToolsOnactiveToolChanged() @@ -561,6 +598,7 @@ static class LightAnchorStyles static public GUIContent presetTextureRimRight = EditorGUIUtility.TrTextContent("", "Rim Right", UnityEditor.Rendering.CoreEditorUtils.LoadIcon(LightAnchorStyles.k_IconFolder, "PresetRim_Right", ".png", false)); static public GUIContent distanceProperty = EditorGUIUtility.TrTextContent("Distance", "Controls how far 'back', the light is placed from its anchor"); static public GUIContent upDirectionProperty = EditorGUIUtility.TrTextContent("Up direction", "Specifies the space in which the up direction of the anchor is defined. Local is relative to the camera."); + static public GUIContent anchorPositionOverrideProperty = EditorGUIUtility.TrTextContent("Anchor Position Override", "Specifies the anchor position manually instead of relying on the angles, distance and transform position to compute the anchor position."); static public GUIContent[] angleSubContent = new[] { EditorGUIUtility.TrTextContent("Orbit"), diff --git a/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditorTool.cs b/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditorTool.cs index 4fc2c47dc20..206956d9e3c 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditorTool.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorEditorTool.cs @@ -30,7 +30,10 @@ public override GUIContent toolbarIcon /// Always return true public override bool IsAvailable() { - return true; + var lightAnchor = target as LightAnchor; + + // Hide the transform if a position override object is assigned + return (lightAnchor?.anchorPositionOverride == null); } /// @@ -39,7 +42,7 @@ public override bool IsAvailable() /// The window that is displaying the custom editor tool. public override void OnToolGUI(EditorWindow window) { - if (target == null) + if (target is LightAnchor l && l?.anchorPositionOverride == null) return; DoTargetGUI(target); @@ -112,7 +115,7 @@ LightAnchorHandles GetHandles(UnityObject obj) LightAnchorHandles handles; if (!m_LightAnchorHandles.TryGetValue(obj, out handles)) { - handles = new LightAnchorHandles(); + handles = new LightAnchorHandles(obj as LightAnchor); m_LightAnchorHandles.Add(obj, handles); } diff --git a/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorHandles.cs b/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorHandles.cs index 99bc7dd85e6..f01bc2d0394 100644 --- a/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorHandles.cs +++ b/com.unity.render-pipelines.core/Editor/Lighting/LightAnchorHandles.cs @@ -16,11 +16,15 @@ public class LightAnchorHandles /// public Vector3 anchorPosition { get; set; } + LightAnchor target; + /// /// Initializes and returns an instance of LightAnchorHandles /// - public LightAnchorHandles() + /// Target object + public LightAnchorHandles(LightAnchor target) { + this.target = target; } /// @@ -31,7 +35,12 @@ public void OnGUI() Handles.color = Color.yellow; Handles.DrawDottedLine(lightPosition, anchorPosition, 2f); - anchorPosition = Handles.PositionHandle(anchorPosition, Quaternion.identity); + // Orient the handle rotation depending on the editor pivot rotation mode + var handleRotation = Quaternion.identity; + if (Tools.pivotRotation == PivotRotation.Local && target != null) + handleRotation = target.transform.rotation; + + anchorPosition = Handles.PositionHandle(anchorPosition, handleRotation); } } } diff --git a/com.unity.render-pipelines.core/Runtime/Lights/LightAnchor.cs b/com.unity.render-pipelines.core/Runtime/Lights/LightAnchor.cs index df00a3ab726..e8a88932005 100644 --- a/com.unity.render-pipelines.core/Runtime/Lights/LightAnchor.cs +++ b/com.unity.render-pipelines.core/Runtime/Lights/LightAnchor.cs @@ -10,7 +10,6 @@ namespace UnityEngine /// Represents camera-space light controls around a virtual pivot point. /// [AddComponentMenu("Rendering/Light Anchor")] - [RequireComponent(typeof(Light))] [ExecuteInEditMode] [DisallowMultipleComponent] [CoreRPHelpURLAttribute("View-Lighting-Tool")] @@ -19,13 +18,18 @@ public class LightAnchor : MonoBehaviour const float k_ArcRadius = 5; const float k_AxisLength = 10; - [SerializeField] + [SerializeField, Min(0)] float m_Distance = 0f; [SerializeField] UpDirection m_FrameSpace = UpDirection.World; + [SerializeField] + Transform m_AnchorPositionOverride; + [SerializeField] float m_Yaw; + [SerializeField] float m_Pitch; + [SerializeField] float m_Roll; /// @@ -69,8 +73,8 @@ public float roll /// public float distance { - get { return m_Distance; } - set { m_Distance = Mathf.Max(value, .01f); } + get => m_Distance; + set => m_Distance = Mathf.Max(0, value); } /// @@ -102,7 +106,13 @@ public UpDirection frameSpace /// public Vector3 anchorPosition { - get { return transform.position + transform.forward * m_Distance; } + get + { + if (anchorPositionOverride != null) + return anchorPositionOverride.position; + else + return transform.position + transform.forward * distance; + } } struct Axes @@ -112,6 +122,16 @@ struct Axes public Vector3 forward; } + /// + /// Overrides the pivot of used to compute the light position. This is useful to track an existing object in the scene. + /// The transform of the light will be automatically updated by the Update() method of the LightAnchor. + /// + public Transform anchorPositionOverride + { + get => m_AnchorPositionOverride; + set => m_AnchorPositionOverride = value; + } + /// /// Normalizes the input angle to be in the range of -180 and 180. /// @@ -136,6 +156,10 @@ public void SynchronizeOnTransform(Camera camera) Vector3 worldAnchorToLight = transform.position - anchorPosition; + // In case the distance is 0 or the anchor override is at the same position than the light anchor + if (worldAnchorToLight.magnitude == 0) + worldAnchorToLight = -transform.forward; + Vector3 projectOnGround = Vector3.ProjectOnPlane(worldAnchorToLight, axes.up); projectOnGround.Normalize(); @@ -162,7 +186,28 @@ public void UpdateTransform(Camera camera, Vector3 anchor) Axes GetWorldSpaceAxes(Camera camera) { + // Fallback when the light anchor object is child of the camera (bad setup) + if (transform.IsChildOf(camera.transform)) + { + return new Axes + { + up = Vector3.up, + right = Vector3.right, + forward = Vector3.forward, + }; + } + Matrix4x4 viewToWorld = camera.cameraToWorldMatrix; + + // Correct view to world for perspective + if (!camera.orthographic && camera.transform.position != anchorPosition) + { + var d = (anchorPosition - camera.transform.position).normalized; + var f = Quaternion.LookRotation(d); + viewToWorld = Matrix4x4.Scale(new Vector3(1, 1, -1)) * Matrix4x4.TRS(camera.transform.position, f, Vector3.one).inverse; + viewToWorld = viewToWorld.inverse; + } + if (m_FrameSpace == UpDirection.World) { Vector3 viewUp = (Vector3)(Camera.main.worldToCameraMatrix * Vector3.up); @@ -182,6 +227,15 @@ Axes GetWorldSpaceAxes(Camera camera) }; } + void Update() + { + if (anchorPositionOverride == null || Camera.main == null) + return; + + if (anchorPositionOverride.hasChanged || Camera.main.transform.hasChanged) + UpdateTransform(Camera.main, anchorPosition); + } + void OnDrawGizmosSelected() { var camera = Camera.main; @@ -191,6 +245,8 @@ void OnDrawGizmosSelected() return; } + // TODO: fix light rotated when camera rotates + Axes axes = GetWorldSpaceAxes(camera); Vector3 anchor = anchorPosition; Vector3 d = transform.position - anchor; @@ -229,10 +285,10 @@ void UpdateTransform(Vector3 up, Vector3 right, Vector3 forward, Vector3 anchor) { Quaternion worldYawRot = Quaternion.AngleAxis(m_Yaw, up); Quaternion worldPitchRot = Quaternion.AngleAxis(m_Pitch, right); - Vector3 worldPosition = anchor + (worldYawRot * worldPitchRot) * forward * m_Distance; + Vector3 worldPosition = anchor + (worldYawRot * worldPitchRot) * forward * distance; transform.position = worldPosition; - Vector3 lookAt = (anchor - worldPosition).normalized; + Vector3 lookAt = -((worldYawRot * worldPitchRot) * forward).normalized; Vector3 angles = Quaternion.LookRotation(lookAt, up).eulerAngles; angles.z = m_Roll; transform.eulerAngles = angles; diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 8111f5205bd..bf646ec3074 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed assert failure when enabling the probe volume system for the first time. - Significantly improved performance of APV probe debug. - Removed DLSS keyword in settings search when NVIDIA package is not installed. (case 1358409) +- Fixed light anchor min distance value + properties not working with prefabs (case 1345509). ### changed - Visual Environment ambient mode is now Dynamic by default.