-
Notifications
You must be signed in to change notification settings - Fork 773
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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) 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. Mask Volume: Hotfix: Avoid attempting to upload degenerate data to the atlas by extending IsDataAssigned() check to include a Length > 0 test Fixed the case when a Mask Volume asset is saved to a file empty. The "Resample Asset" button can process an empty asset and fill it with valid data now. (#23) Mask Volumes binary serialization and minor fixes * Fixed the case when the painted Mask Volume asset is not saved. Updated the default brush settings based on the user feedback. * Prefer binary serialization of Mask Volume assets. * Added "Reserialize All" action to context menus of MaskVolumeAsset to be able to convert all assets in a project to binary format. Mask Volume: Fixed minor bug where emptyUAV would silently fail to bind to _MaskVolumeAtlas - using emptyVolumeUAV instead Mask Volume: Mask Volume: Make Mask Volume internals visible to Unity.Entities.Hybrid.HybridComponents Mask Volumes: Set methods for Mask Volume exposed that allows us to use them to do deep copies of Scenes DOTS Mask Volume (#6) Refactored the access to Mask Volume instanced to be able to feed a DOTS version to the system.
- Loading branch information
1 parent
7fa6ad9
commit c2e70d3
Showing
64 changed files
with
4,772 additions
and
13 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
com.unity.render-pipelines.high-definition/Editor/Material/MaskVolume.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
223 changes: 223 additions & 0 deletions
223
com.unity.render-pipelines.high-definition/Editor/Material/MaskVolume/MaskVolumeBrush.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
using System; | ||
using UnityEngine; | ||
|
||
namespace UnityEditor.Rendering.HighDefinition | ||
{ | ||
public class MaskVolumeBrush | ||
{ | ||
const float k_MaxTimeSpentPerEvent = 0.05f; | ||
const float k_Stepping = 0.5f; | ||
const int k_RaycastBufferSize = 128; | ||
|
||
public float OuterRadius = 2f; | ||
public float InnerRadius = 0f; | ||
public float NormalBias = 0f; | ||
|
||
public bool MeshCollidersOnly = true; | ||
public LayerMask PhysicsLayerMask = ~0; | ||
|
||
bool m_Hovering = false; | ||
Vector3 m_Position; | ||
Vector3 m_Normal; | ||
|
||
bool m_Applying = false; | ||
Vector3 m_LastApplyPosition; | ||
Vector3 m_LastApplyNormal; | ||
float m_LastApplyPressure; | ||
|
||
public delegate void Apply(Vector3 position, Vector3 normal, float pressure, bool control); | ||
|
||
public event Apply OnApply; | ||
public event Action OnStopApplying; | ||
|
||
public void OnSceneGUI(SceneView sceneView) | ||
{ | ||
Event e = Event.current; | ||
|
||
if (SceneViewInUse(e)) | ||
{ | ||
// Force exit the current brush if user's mouse left | ||
// the SceneView while a brush was still in use. | ||
StopIfApplying(); | ||
return; | ||
} | ||
|
||
int controlID = GUIUtility.GetControlID(FocusType.Passive); | ||
|
||
HandleUtility.AddDefaultControl(controlID); | ||
|
||
switch (e.GetTypeForControl(controlID)) | ||
{ | ||
case EventType.MouseMove: | ||
StopIfApplying(); | ||
UpdateBrush(e.mousePosition); | ||
break; | ||
|
||
case EventType.MouseDown: | ||
case EventType.MouseDrag: | ||
UpdateBrush(e.mousePosition); | ||
ApplyBrush(e.pressure, e.control); | ||
break; | ||
|
||
case EventType.MouseUp: | ||
StopIfApplying(); | ||
UpdateBrush(e.mousePosition); | ||
break; | ||
} | ||
|
||
DrawGizmo(sceneView); | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if the event is one that should consume the mouse or keyboard. | ||
/// </summary> | ||
/// <param name="e"></param> | ||
/// <returns></returns> | ||
static bool SceneViewInUse(Event e) | ||
{ | ||
return e.alt | ||
|| Tools.current == Tool.View | ||
|| GUIUtility.hotControl > 0 | ||
|| (e.isMouse ? e.button > 1 : false) | ||
|| Tools.viewTool == ViewTool.FPS | ||
|| Tools.viewTool == ViewTool.Orbit; | ||
} | ||
|
||
/// <summary> | ||
/// Update the brush state and position. | ||
/// </summary> | ||
/// <param name="mousePosition">current mouse position (from Event)</param> | ||
void UpdateBrush(Vector2 mousePosition) | ||
{ | ||
Ray mouseRay = HandleUtility.GUIPointToWorldRay(mousePosition); | ||
m_Hovering = Raycast(mouseRay, out var hitPosition, out var hitNormal); | ||
if (m_Hovering) | ||
{ | ||
m_Position = hitPosition; | ||
m_Normal = hitNormal; | ||
} | ||
} | ||
|
||
RaycastHit[] raycastBuffer; | ||
bool Raycast(Ray ray, out Vector3 hitPosition, out Vector3 hitNormal) | ||
{ | ||
if (MeshCollidersOnly) | ||
{ | ||
if (raycastBuffer == null) | ||
raycastBuffer = new RaycastHit[k_RaycastBufferSize]; | ||
var hitCount = Physics.RaycastNonAlloc(ray, raycastBuffer, float.MaxValue, PhysicsLayerMask, QueryTriggerInteraction.Ignore); | ||
|
||
var minDistance = float.MaxValue; | ||
var nearestHit = -1; | ||
|
||
for (int i = 0; i < hitCount; i++) | ||
{ | ||
var hit = raycastBuffer[i]; | ||
if (hit.collider is MeshCollider && hit.distance < minDistance) | ||
{ | ||
minDistance = hit.distance; | ||
nearestHit = i; | ||
} | ||
} | ||
|
||
if (nearestHit != -1) | ||
{ | ||
var hit = raycastBuffer[nearestHit]; | ||
hitPosition = hit.point; | ||
hitNormal = hit.normal; | ||
return true; | ||
} | ||
} | ||
else | ||
{ | ||
if (Physics.Raycast(ray, out var hit, float.MaxValue, PhysicsLayerMask, QueryTriggerInteraction.Ignore)) | ||
{ | ||
hitPosition = hit.point; | ||
hitNormal = hit.normal; | ||
return true; | ||
} | ||
} | ||
|
||
hitPosition = default; | ||
hitNormal = default; | ||
return false; | ||
} | ||
|
||
/// <summary> | ||
/// Apply brush. | ||
/// </summary> | ||
void ApplyBrush(float pressure, bool control) | ||
{ | ||
if (m_Hovering) | ||
{ | ||
if (!m_Applying) | ||
{ | ||
m_Applying = true; | ||
OnApply?.Invoke(m_Position, m_Normal, pressure, control); | ||
m_LastApplyPosition = m_Position; | ||
m_LastApplyNormal = m_Normal; | ||
m_LastApplyPressure = pressure; | ||
} | ||
else | ||
{ | ||
var moveDistance = Vector3.Distance(m_Position, m_LastApplyPosition); | ||
|
||
// If mouse moved too far due to low framerate or high movement speed, fill the gap with more stamps | ||
var maxStep = OuterRadius * k_Stepping; | ||
var steps = (int) (moveDistance / maxStep); | ||
|
||
var maxTime = Time.realtimeSinceStartup + k_MaxTimeSpentPerEvent; | ||
var startPosition = m_LastApplyPosition; | ||
var startNormal = m_LastApplyNormal; | ||
var startPressure = m_LastApplyPressure; | ||
for (int i = 1; i <= steps; i++) | ||
{ | ||
var time = (float) i / steps; | ||
m_LastApplyPosition = Vector3.Lerp(startPosition, m_Position, time); | ||
m_LastApplyNormal = Vector3.Lerp(startNormal, m_Normal, time); | ||
m_LastApplyPressure = Mathf.Lerp(startPressure, pressure, time); | ||
OnApply?.Invoke(m_LastApplyPosition, m_LastApplyNormal, m_LastApplyPressure, control); | ||
if (Time.realtimeSinceStartup > maxTime) | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
void DrawGizmo(SceneView sceneView) | ||
{ | ||
if (m_Hovering) | ||
{ | ||
var oldHandleColor = Handles.color; | ||
|
||
var discNormal = (sceneView.camera.transform.position - m_Position).normalized; | ||
|
||
if (InnerRadius != 1f) | ||
{ | ||
Handles.color = new Color(1f, 1f, 1f, 0.5f); | ||
Handles.DrawWireDisc(m_Position, discNormal, OuterRadius); | ||
} | ||
|
||
Handles.color = Color.white; | ||
Handles.DrawWireDisc(m_Position, discNormal, OuterRadius * InnerRadius); | ||
|
||
if (NormalBias != 0f) | ||
{ | ||
Handles.color = new Color(Mathf.Abs(m_Normal.x), Mathf.Abs(m_Normal.y), Mathf.Abs(m_Normal.z)); | ||
Handles.DrawLine(m_Position, m_Position + m_Normal * NormalBias); | ||
} | ||
|
||
Handles.color = oldHandleColor; | ||
} | ||
} | ||
|
||
public void StopIfApplying() | ||
{ | ||
if (m_Applying) | ||
{ | ||
OnStopApplying?.Invoke(); | ||
m_Applying = false; | ||
} | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...unity.render-pipelines.high-definition/Editor/Material/MaskVolume/MaskVolumeBrush.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.