Skip to content

Commit a1f8e79

Browse files
AndrewSaraevUnitypastasfuture
authored andcommitted
Mask Volumes. An ability to place volumes with 3D textures into the world, paint custom color into them and sample this data in any Shader Graph. To be used as a global volumetric splat map. (#16)
(cherry picked from commit f68dc61b5c92ccea7f249e7f58aab1583799ddcb)
1 parent 3c8449c commit a1f8e79

File tree

59 files changed

+4454
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4454
-11
lines changed

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

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
using System;
2+
using UnityEngine;
3+
4+
namespace UnityEditor.Rendering.HighDefinition
5+
{
6+
public class MaskVolumeBrush
7+
{
8+
const float k_MaxTimeSpentPerEvent = 0.05f;
9+
const float k_Stepping = 0.5f;
10+
const int k_RaycastBufferSize = 128;
11+
12+
public float Radius = 0.5f;
13+
14+
public bool MeshCollidersOnly = true;
15+
public LayerMask PhysicsLayerMask = ~0;
16+
17+
bool m_Hovering = false;
18+
Vector3 m_Position;
19+
bool m_Applying = false;
20+
Vector3 m_LastApplyPosition;
21+
22+
public event Action<Vector3> OnApply;
23+
public event Action OnStopApplying;
24+
25+
public void OnSceneGUI(SceneView sceneView)
26+
{
27+
Event e = Event.current;
28+
29+
if (SceneViewInUse(e))
30+
{
31+
// Force exit the current brush if user's mouse left
32+
// the SceneView while a brush was still in use.
33+
StopIfApplying();
34+
return;
35+
}
36+
37+
int controlID = GUIUtility.GetControlID(FocusType.Passive);
38+
39+
HandleUtility.AddDefaultControl(controlID);
40+
41+
switch (e.GetTypeForControl(controlID))
42+
{
43+
case EventType.MouseMove:
44+
StopIfApplying();
45+
UpdateBrush(e.mousePosition);
46+
break;
47+
48+
case EventType.MouseDown:
49+
case EventType.MouseDrag:
50+
UpdateBrush(e.mousePosition);
51+
ApplyBrush();
52+
break;
53+
54+
case EventType.MouseUp:
55+
StopIfApplying();
56+
UpdateBrush(e.mousePosition);
57+
break;
58+
}
59+
60+
DrawGizmo(sceneView);
61+
}
62+
63+
/// <summary>
64+
/// Returns true if the event is one that should consume the mouse or keyboard.
65+
/// </summary>
66+
/// <param name="e"></param>
67+
/// <returns></returns>
68+
static bool SceneViewInUse(Event e)
69+
{
70+
return e.alt
71+
|| Tools.current == Tool.View
72+
|| GUIUtility.hotControl > 0
73+
|| (e.isMouse ? e.button > 1 : false)
74+
|| Tools.viewTool == ViewTool.FPS
75+
|| Tools.viewTool == ViewTool.Orbit;
76+
}
77+
78+
/// <summary>
79+
/// Update the brush state and position.
80+
/// </summary>
81+
/// <param name="mousePosition">current mouse position (from Event)</param>
82+
void UpdateBrush(Vector2 mousePosition)
83+
{
84+
Ray mouseRay = HandleUtility.GUIPointToWorldRay(mousePosition);
85+
m_Hovering = Raycast(mouseRay, out var hitPosition);
86+
if (m_Hovering)
87+
m_Position = hitPosition;
88+
}
89+
90+
RaycastHit[] raycastBuffer;
91+
bool Raycast(Ray ray, out Vector3 hitPosition)
92+
{
93+
if (MeshCollidersOnly)
94+
{
95+
if (raycastBuffer == null)
96+
raycastBuffer = new RaycastHit[k_RaycastBufferSize];
97+
var hitCount = Physics.RaycastNonAlloc(ray, raycastBuffer, float.MaxValue, PhysicsLayerMask, QueryTriggerInteraction.Ignore);
98+
99+
var minDistance = float.MaxValue;
100+
var nearestPosition = Vector3.zero;
101+
102+
for (int i = 0; i < hitCount; i++)
103+
{
104+
var hit = raycastBuffer[i];
105+
if (hit.collider is MeshCollider && hit.distance < minDistance)
106+
{
107+
minDistance = hit.distance;
108+
nearestPosition = hit.point;
109+
}
110+
}
111+
112+
if (minDistance != float.MaxValue)
113+
{
114+
hitPosition = nearestPosition;
115+
return true;
116+
}
117+
}
118+
else
119+
{
120+
if (Physics.Raycast(ray, out var hit, float.MaxValue, PhysicsLayerMask, QueryTriggerInteraction.Ignore))
121+
{
122+
hitPosition = hit.point;
123+
return true;
124+
}
125+
}
126+
127+
hitPosition = default;
128+
return false;
129+
}
130+
131+
/// <summary>
132+
/// Apply brush.
133+
/// </summary>
134+
void ApplyBrush()
135+
{
136+
if (m_Hovering)
137+
{
138+
if (!m_Applying)
139+
{
140+
m_Applying = true;
141+
OnApply?.Invoke(m_Position);
142+
m_LastApplyPosition = m_Position;
143+
}
144+
else
145+
{
146+
var moveDistance = Vector3.Distance(m_Position, m_LastApplyPosition);
147+
148+
// 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;
150+
var steps = (int) (moveDistance / maxStep);
151+
152+
var maxTime = Time.realtimeSinceStartup + k_MaxTimeSpentPerEvent;
153+
var startPosition = m_LastApplyPosition;
154+
for (int i = 1; i <= steps; i++)
155+
{
156+
m_LastApplyPosition = Vector3.Lerp(startPosition, m_Position, (float) i / steps);
157+
OnApply?.Invoke(m_LastApplyPosition);
158+
if (Time.realtimeSinceStartup > maxTime)
159+
break;
160+
}
161+
}
162+
}
163+
}
164+
165+
void DrawGizmo(SceneView sceneView)
166+
{
167+
if (m_Hovering)
168+
Handles.DrawWireDisc(m_Position, (sceneView.camera.transform.position - m_Position).normalized, Radius);
169+
}
170+
171+
public void StopIfApplying()
172+
{
173+
if (m_Applying)
174+
{
175+
OnStopApplying?.Invoke();
176+
m_Applying = false;
177+
}
178+
}
179+
}
180+
}

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

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)