Skip to content

Commit 167dd2f

Browse files
AndrewSaraevUnityImpossible
authored andcommitted
Mask Volumes tools update (#22)
* Fixed the issue when the scene view camera moves on selecting the painting tool. * Use normalized values for color and opacity settings. Fixed the effect of opacity and hardness. * Added the brush color inversion while Ctrl is pressed, for quick erasing. * Replaced the Recreate Asset button with Resample Asset. The data is not lost after resizing or changing voxel density now. * Fixed a Mask Volume resampling error when old resolution has 1 on any axis. * Mask Volume: Use resolution from MaskVolumeAsset instead of MaskVolumeArtistParameters for rendering. Resolution in parameters is just for creating new assets or resampling to a new resolution. * Mask Volume: Use normal bias in painting. * Mask Volume: Use input pressure as opacity. * Mask Volume: Added inner radius and normal bias brush gizmos.
1 parent fbb4c9a commit 167dd2f

File tree

7 files changed

+306
-146
lines changed

7 files changed

+306
-146
lines changed

com.unity.render-pipelines.high-definition/Editor/Material/MaskVolume/MaskVolumeBrush.cs

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,25 @@ public class MaskVolumeBrush
99
const float k_Stepping = 0.5f;
1010
const int k_RaycastBufferSize = 128;
1111

12-
public float Radius = 0.5f;
12+
public float OuterRadius = 0.5f;
13+
public float InnerRadius = 1f;
14+
public float NormalBias = 0f;
1315

1416
public bool MeshCollidersOnly = true;
1517
public LayerMask PhysicsLayerMask = ~0;
1618

1719
bool m_Hovering = false;
1820
Vector3 m_Position;
21+
Vector3 m_Normal;
22+
1923
bool m_Applying = false;
2024
Vector3 m_LastApplyPosition;
25+
Vector3 m_LastApplyNormal;
26+
float m_LastApplyPressure;
2127

22-
public event Action<Vector3> OnApply;
28+
public delegate void Apply(Vector3 position, Vector3 normal, float pressure, bool control);
29+
30+
public event Apply OnApply;
2331
public event Action OnStopApplying;
2432

2533
public void OnSceneGUI(SceneView sceneView)
@@ -48,7 +56,7 @@ public void OnSceneGUI(SceneView sceneView)
4856
case EventType.MouseDown:
4957
case EventType.MouseDrag:
5058
UpdateBrush(e.mousePosition);
51-
ApplyBrush();
59+
ApplyBrush(e.pressure, e.control);
5260
break;
5361

5462
case EventType.MouseUp:
@@ -82,13 +90,16 @@ static bool SceneViewInUse(Event e)
8290
void UpdateBrush(Vector2 mousePosition)
8391
{
8492
Ray mouseRay = HandleUtility.GUIPointToWorldRay(mousePosition);
85-
m_Hovering = Raycast(mouseRay, out var hitPosition);
93+
m_Hovering = Raycast(mouseRay, out var hitPosition, out var hitNormal);
8694
if (m_Hovering)
95+
{
8796
m_Position = hitPosition;
97+
m_Normal = hitNormal;
98+
}
8899
}
89100

90101
RaycastHit[] raycastBuffer;
91-
bool Raycast(Ray ray, out Vector3 hitPosition)
102+
bool Raycast(Ray ray, out Vector3 hitPosition, out Vector3 hitNormal)
92103
{
93104
if (MeshCollidersOnly)
94105
{
@@ -97,21 +108,23 @@ bool Raycast(Ray ray, out Vector3 hitPosition)
97108
var hitCount = Physics.RaycastNonAlloc(ray, raycastBuffer, float.MaxValue, PhysicsLayerMask, QueryTriggerInteraction.Ignore);
98109

99110
var minDistance = float.MaxValue;
100-
var nearestPosition = Vector3.zero;
111+
var nearestHit = -1;
101112

102113
for (int i = 0; i < hitCount; i++)
103114
{
104115
var hit = raycastBuffer[i];
105116
if (hit.collider is MeshCollider && hit.distance < minDistance)
106117
{
107118
minDistance = hit.distance;
108-
nearestPosition = hit.point;
119+
nearestHit = i;
109120
}
110121
}
111122

112-
if (minDistance != float.MaxValue)
123+
if (nearestHit != -1)
113124
{
114-
hitPosition = nearestPosition;
125+
var hit = raycastBuffer[nearestHit];
126+
hitPosition = hit.point;
127+
hitNormal = hit.normal;
115128
return true;
116129
}
117130
}
@@ -120,41 +133,50 @@ bool Raycast(Ray ray, out Vector3 hitPosition)
120133
if (Physics.Raycast(ray, out var hit, float.MaxValue, PhysicsLayerMask, QueryTriggerInteraction.Ignore))
121134
{
122135
hitPosition = hit.point;
136+
hitNormal = hit.normal;
123137
return true;
124138
}
125139
}
126140

127141
hitPosition = default;
142+
hitNormal = default;
128143
return false;
129144
}
130145

131146
/// <summary>
132147
/// Apply brush.
133148
/// </summary>
134-
void ApplyBrush()
149+
void ApplyBrush(float pressure, bool control)
135150
{
136151
if (m_Hovering)
137152
{
138153
if (!m_Applying)
139154
{
140155
m_Applying = true;
141-
OnApply?.Invoke(m_Position);
156+
OnApply?.Invoke(m_Position, m_Normal, pressure, control);
142157
m_LastApplyPosition = m_Position;
158+
m_LastApplyNormal = m_Normal;
159+
m_LastApplyPressure = pressure;
143160
}
144161
else
145162
{
146163
var moveDistance = Vector3.Distance(m_Position, m_LastApplyPosition);
147164

148165
// If mouse moved too far due to low framerate or high movement speed, fill the gap with more stamps
149-
var maxStep = Radius * k_Stepping;
166+
var maxStep = OuterRadius * k_Stepping;
150167
var steps = (int) (moveDistance / maxStep);
151168

152169
var maxTime = Time.realtimeSinceStartup + k_MaxTimeSpentPerEvent;
153170
var startPosition = m_LastApplyPosition;
171+
var startNormal = m_LastApplyNormal;
172+
var startPressure = m_LastApplyPressure;
154173
for (int i = 1; i <= steps; i++)
155174
{
156-
m_LastApplyPosition = Vector3.Lerp(startPosition, m_Position, (float) i / steps);
157-
OnApply?.Invoke(m_LastApplyPosition);
175+
var time = (float) i / steps;
176+
m_LastApplyPosition = Vector3.Lerp(startPosition, m_Position, time);
177+
m_LastApplyNormal = Vector3.Lerp(startNormal, m_Normal, time);
178+
m_LastApplyPressure = Mathf.Lerp(startPressure, pressure, time);
179+
OnApply?.Invoke(m_LastApplyPosition, m_LastApplyNormal, m_LastApplyPressure, control);
158180
if (Time.realtimeSinceStartup > maxTime)
159181
break;
160182
}
@@ -165,7 +187,28 @@ void ApplyBrush()
165187
void DrawGizmo(SceneView sceneView)
166188
{
167189
if (m_Hovering)
168-
Handles.DrawWireDisc(m_Position, (sceneView.camera.transform.position - m_Position).normalized, Radius);
190+
{
191+
var oldHandleColor = Handles.color;
192+
193+
var discNormal = (sceneView.camera.transform.position - m_Position).normalized;
194+
195+
if (InnerRadius != 1f)
196+
{
197+
Handles.color = new Color(1f, 1f, 1f, 0.5f);
198+
Handles.DrawWireDisc(m_Position, discNormal, OuterRadius);
199+
}
200+
201+
Handles.color = Color.white;
202+
Handles.DrawWireDisc(m_Position, discNormal, OuterRadius * InnerRadius);
203+
204+
if (NormalBias != 0f)
205+
{
206+
Handles.color = new Color(Mathf.Abs(m_Normal.x), Mathf.Abs(m_Normal.y), Mathf.Abs(m_Normal.z));
207+
Handles.DrawLine(m_Position, m_Position + m_Normal * NormalBias);
208+
}
209+
210+
Handles.color = oldHandleColor;
211+
}
169212
}
170213

171214
public void StopIfApplying()

com.unity.render-pipelines.high-definition/Editor/Material/MaskVolume/MaskVolumeEditor.cs

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ internal class MaskVolumeEditor : Editor
1616
static Dictionary<MaskVolume, HierarchicalBox> shapeBoxes = new Dictionary<MaskVolume, HierarchicalBox>();
1717
internal static Dictionary<MaskVolume, HierarchicalBox> blendBoxes = new Dictionary<MaskVolume, HierarchicalBox>();
1818

19-
internal static Color32 BrushColor = new Color32(255, 0, 0, 255);
19+
internal static Color BrushColor = new Color(1f, 0f, 0f, 1f);
2020
internal static bool BrushApplyRed = true;
2121
internal static bool BrushApplyGreen = true;
2222
internal static bool BrushApplyBlue = true;
23-
internal static float BrushHardness = 1f;
2423

2524
SerializedMaskVolume m_SerializedMaskVolume;
2625

@@ -52,7 +51,12 @@ void OnDisable()
5251
Brush.OnStopApplying -= OnStopApplyingBrush;
5352
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
5453
}
55-
54+
55+
public override bool RequiresConstantRepaint()
56+
{
57+
return EditMode.editMode == k_EditPaint;
58+
}
59+
5660
public override void OnInspectorGUI()
5761
{
5862
serializedObject.Update();
@@ -119,7 +123,7 @@ protected void OnSceneGUI()
119123
if (!blendBoxes.TryGetValue(maskVolume, out HierarchicalBox blendBox)) { return; }
120124
if (!shapeBoxes.TryGetValue(maskVolume, out HierarchicalBox shapeBox)) { return; }
121125

122-
if (EditMode.editMode != EditMode.SceneViewEditMode.GridPainting)
126+
if (EditMode.editMode != k_EditPaint)
123127
Brush.StopIfApplying();
124128

125129
switch (EditMode.editMode)
@@ -182,6 +186,7 @@ protected void OnSceneGUI()
182186
break;
183187
case k_EditPaint:
184188
var sceneView = SceneView.currentDrawingSceneView;
189+
Brush.NormalBias = maskVolume.parameters.normalBiasWS;
185190
Brush.OnSceneGUI(sceneView);
186191
sceneView.Repaint();
187192
break;
@@ -190,7 +195,7 @@ protected void OnSceneGUI()
190195

191196
bool m_ApplyingBrush;
192197

193-
void OnApplyBrush(Vector3 position)
198+
void OnApplyBrush(Vector3 position, Vector3 normal, float pressure, bool control)
194199
{
195200
// TODO: Multi-editing.
196201
var maskVolume = (MaskVolume)target;
@@ -212,10 +217,10 @@ void OnApplyBrush(Vector3 position)
212217
var maskTransform = maskVolume.transform;
213218
var localToWorld = Matrix4x4.TRS(maskTransform.position, maskTransform.rotation, Vector3.one);
214219
var worldToLocal = localToWorld.inverse;
215-
var localBrushPosition = worldToLocal.MultiplyPoint3x4(position);
220+
var localBrushPosition = worldToLocal.MultiplyPoint3x4(position + normal * parameters.normalBiasWS);
216221

217-
var minAffectedLocalPosition = localBrushPosition - firstVoxelLocalPosition - new Vector3(Brush.Radius, Brush.Radius, Brush.Radius);
218-
var maxAffectedLocalPosition = localBrushPosition - firstVoxelLocalPosition + new Vector3(Brush.Radius, Brush.Radius, Brush.Radius);
222+
var minAffectedLocalPosition = localBrushPosition - firstVoxelLocalPosition - new Vector3(Brush.OuterRadius, Brush.OuterRadius, Brush.OuterRadius);
223+
var maxAffectedLocalPosition = localBrushPosition - firstVoxelLocalPosition + new Vector3(Brush.OuterRadius, Brush.OuterRadius, Brush.OuterRadius);
219224

220225
var minX = Mathf.Max(Mathf.RoundToInt(minAffectedLocalPosition.x / voxelSize.x), 0);
221226
var minY = Mathf.Max(Mathf.RoundToInt(minAffectedLocalPosition.y / voxelSize.y), 0);
@@ -226,6 +231,17 @@ void OnApplyBrush(Vector3 position)
226231

227232
var dataSHL0 = maskVolumeAsset.payload.dataSHL0;
228233
var strideSHL0 = MaskVolumePayload.GetDataSHL0Stride();
234+
var color32 = (Color32)BrushColor;
235+
236+
// A way of erase quickly
237+
if (control)
238+
{
239+
color32.r = (byte)(255 - color32.r);
240+
color32.g = (byte)(255 - color32.g);
241+
color32.b = (byte)(255 - color32.b);
242+
}
243+
244+
float opacity = BrushColor.a * pressure;
229245

230246
for (int z = minZ; z <= maxZ; z++)
231247
{
@@ -244,22 +260,20 @@ void OnApplyBrush(Vector3 position)
244260
var longestComponent = Mathf.Max(Mathf.Max(Mathf.Abs(toBrush.x), Mathf.Abs(toBrush.y)), Mathf.Abs(toBrush.z));
245261
var halfVoxelLength = Vector3.Scale(toBrush / longestComponent, voxelSize).magnitude * 0.5f;
246262

247-
var outerRadius = Brush.Radius + halfVoxelLength;
248-
var innerRadius = Brush.Radius - halfVoxelLength;
249-
if (innerRadius > 0f)
250-
innerRadius *= BrushHardness;
263+
var outerRadius = Brush.OuterRadius + halfVoxelLength;
264+
var innerRadius = Brush.OuterRadius * Brush.InnerRadius - halfVoxelLength;
251265

252266
var distanceToBrush = toBrush.magnitude;
253-
var opacity = MaskVolumePayload.ToUNormByte(MaskVolumePayload.FromUNormByte(BrushColor.a) * (outerRadius - distanceToBrush) / (outerRadius - innerRadius));
254-
if (opacity > 0)
267+
color32.a = (byte)(opacity * Mathf.Clamp01((outerRadius - distanceToBrush) / (outerRadius - innerRadius)) * 255f);
268+
if (color32.a > 0)
255269
{
256270
var indexDataBaseSHL0 = i * strideSHL0;
257271
if (BrushApplyRed)
258-
dataSHL0[indexDataBaseSHL0 + 0] = ApplyBrushChannel(dataSHL0[indexDataBaseSHL0 + 0], BrushColor.r, opacity); // shAr.w
272+
dataSHL0[indexDataBaseSHL0 + 0] = ApplyBrushChannel(dataSHL0[indexDataBaseSHL0 + 0], color32.r, color32.a); // shAr.w
259273
if (BrushApplyGreen)
260-
dataSHL0[indexDataBaseSHL0 + 1] = ApplyBrushChannel(dataSHL0[indexDataBaseSHL0 + 1], BrushColor.g, opacity); // shAg.w
274+
dataSHL0[indexDataBaseSHL0 + 1] = ApplyBrushChannel(dataSHL0[indexDataBaseSHL0 + 1], color32.g, color32.a); // shAg.w
261275
if (BrushApplyBlue)
262-
dataSHL0[indexDataBaseSHL0 + 2] = ApplyBrushChannel(dataSHL0[indexDataBaseSHL0 + 2], BrushColor.b, opacity); // shAb.w
276+
dataSHL0[indexDataBaseSHL0 + 2] = ApplyBrushChannel(dataSHL0[indexDataBaseSHL0 + 2], color32.b, color32.a); // shAb.w
263277
}
264278
}
265279
}
@@ -271,16 +285,10 @@ void OnApplyBrush(Vector3 position)
271285
static byte ApplyBrushChannel(byte value, byte targetValue, byte opacity)
272286
{
273287
var delta = targetValue - value;
274-
if (delta < 0)
275-
{
276-
if (delta < -opacity)
277-
delta = -opacity;
278-
}
279-
else
280-
{
281-
if (delta > opacity)
282-
delta = opacity;
283-
}
288+
if (delta < -opacity)
289+
delta = -opacity;
290+
else if (delta > opacity)
291+
delta = opacity;
284292
return (byte)(value + delta);
285293
}
286294

0 commit comments

Comments
 (0)