Rasagar/Library/PackageCache/com.unity.visualeffectgraph/Editor/Utilities/SDF/BakeTool/SdfBakerPreview.cs
2024-08-26 23:07:20 +03:00

412 lines
15 KiB
C#

using System;
using UnityEngine;
using Transform = UnityEngine.Transform;
using UObject = UnityEngine.Object;
using UnityEditor;
namespace UnityEditor.VFX.SDF
{
class SdfBakerPreview
{
internal static class Styles
{
internal static readonly GUIContent wireframeToggle = EditorGUIUtility.TrTextContent("Wireframe", "Show wireframe");
internal static readonly GUIContent orthographicToggle = EditorGUIUtility.TrTextContent("Orthographic view");
internal static readonly GUIContent showActualBox = EditorGUIUtility.TrTextContent("Show Actual Box");
internal static readonly GUIContent showDesiredBox = EditorGUIUtility.TrTextContent("Show Desired Box");
internal static GUIStyle preSlider = "preSlider";
}
internal class Settings
{
public bool drawWire = true;
public bool showActualBox = true;
public bool showDesiredBox = true;
public Vector3 orthoPosition = new Vector3(0.0f, 0.0f, 0.0f);
public Vector2 previewDir = new Vector2(0, 0);
public Vector2 lightDir = new Vector2(0, 0);
public Vector3 pivotPositionOffset = Vector3.zero;
public float zoomFactor = 1.0f;
public Material shadedPreviewMaterial;
public Material activeMaterial;
public Material wireMaterial;
public Settings()
{
shadedPreviewMaterial = new Material(Shader.Find("Standard"));
shadedPreviewMaterial.hideFlags = HideFlags.DontSave;
wireMaterial = CreateWireframeMaterial();
activeMaterial = shadedPreviewMaterial;
orthoPosition = new Vector3(0.5f, 0.5f, -1);
previewDir = new Vector2(130, 0);
lightDir = new Vector2(-40, -40);
zoomFactor = 1.0f;
}
public void Dispose()
{
if (shadedPreviewMaterial != null)
UObject.DestroyImmediate(shadedPreviewMaterial);
if (wireMaterial != null)
UObject.DestroyImmediate(wireMaterial);
}
}
Mesh m_Target;
internal Mesh mesh
{
get => m_Target;
set => m_Target = value;
}
private Vector3 m_SizeBoxReference = Vector3.one;
private Vector3 m_ActualSizeBox = Vector3.one;
private Vector3 m_CenterBox = Vector3.zero;
private Color m_ActualBoxColor = new Color(0, 255.0f / 255, 70.0f / 255);
internal Vector3 sizeBoxReference
{
get => m_SizeBoxReference;
set => m_SizeBoxReference = value;
}
internal Vector3 actualSizeBox
{
get => m_ActualSizeBox;
set => m_ActualSizeBox = value;
}
internal Vector3 centerBox
{
get => m_CenterBox;
set => m_CenterBox = value;
}
private bool m_Orthographic = false;
internal bool orthographic
{
get => m_Orthographic;
set => m_Orthographic = value;
}
PreviewRenderUtility m_PreviewUtility;
Settings m_Settings;
internal SdfBakerPreview(Mesh target)
{
m_Target = target;
m_PreviewUtility = new PreviewRenderUtility();
m_PreviewUtility.camera.fieldOfView = 30.0f;
m_PreviewUtility.camera.transform.position = new Vector3(5, 5, 0);
m_Settings = new Settings();
}
internal void Dispose()
{
m_PreviewUtility.Cleanup();
m_Settings.Dispose();
}
static Material CreateWireframeMaterial()
{
var shader = Shader.Find("Hidden/Internal-Colored");
if (!shader)
{
Debug.LogWarning("Could not find the built-in Internal-Colored shader");
return null;
}
var mat = new Material(shader);
mat.hideFlags = HideFlags.HideAndDontSave;
mat.SetColor("_Color", new Color(0, 0, 0, 0.3f));
mat.SetFloat("_ZWrite", 0.0f);
mat.SetFloat("_ZBias", -1.0f);
return mat;
}
void ResetView()
{
m_Settings.zoomFactor = 1.0f;
m_Settings.orthoPosition = new Vector3(0.5f, 0.5f, -1);
m_Settings.pivotPositionOffset = Vector3.zero;
}
internal void RenderMeshPreview(
Mesh mesh,
PreviewRenderUtility previewUtility,
Settings settings,
int meshSubset)
{
if (mesh == null || previewUtility == null)
return;
Bounds bounds = mesh.bounds;
UnityEngine.Transform renderCamTransform = previewUtility.camera.GetComponent<UnityEngine.Transform>();
if (m_Orthographic)
{
previewUtility.camera.nearClipPlane = 1;
previewUtility.camera.farClipPlane = 1 + sizeBoxReference.magnitude;
previewUtility.camera.orthographicSize = mesh.bounds.extents.y * 1.1f;
}
else
{
previewUtility.camera.nearClipPlane = 0.0001f;
previewUtility.camera.farClipPlane = 1000f;
}
float halfSize = bounds.extents.magnitude;
float distance = 4.0f * halfSize;
previewUtility.camera.orthographic = m_Orthographic;
Quaternion camRotation = Quaternion.identity;
Vector3 camPosition = camRotation * Vector3.forward * (-distance * settings.zoomFactor) + settings.pivotPositionOffset;
renderCamTransform.position = camPosition;
renderCamTransform.rotation = camRotation;
previewUtility.lights[0].intensity = 1.1f;
previewUtility.lights[0].transform.rotation = Quaternion.Euler(-settings.lightDir.y, -settings.lightDir.x, 0);
previewUtility.lights[1].intensity = 1.1f;
previewUtility.lights[1].transform.rotation = Quaternion.Euler(settings.lightDir.y, settings.lightDir.x, 0);
previewUtility.ambientColor = new Color(.1f, .1f, .1f, 0);
RenderMeshPreviewSkipCameraAndLighting(mesh, bounds, previewUtility, settings, null, meshSubset);
}
internal static Color GetSubMeshTint(int index)
{
// color palette generator based on "golden ratio" idea, like in
// https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
var hue = Mathf.Repeat(index * 0.618f, 1);
var sat = index == 0 ? 0f : 0.3f;
var val = 1f;
return Color.HSVToRGB(hue, sat, val);
}
internal void RenderMeshPreviewSkipCameraAndLighting(
Mesh mesh,
Bounds bounds,
PreviewRenderUtility previewUtility,
Settings settings,
MaterialPropertyBlock customProperties,
int meshSubset) // -1 for whole mesh
{
if (mesh == null || previewUtility == null)
return;
Quaternion rot = Quaternion.Euler(settings.previewDir.y, 0, 0) * Quaternion.Euler(0, settings.previewDir.x, 0);
Vector3 pos = rot * (-bounds.center);
bool oldFog = RenderSettings.fog;
Unsupported.SetRenderSettingsUseFogNoDirty(false);
int submeshes = mesh.subMeshCount;
var tintSubmeshes = false;
var colorPropID = 0;
if (submeshes > 1 && customProperties == null && meshSubset == -1)
{
tintSubmeshes = true;
customProperties = new MaterialPropertyBlock();
colorPropID = Shader.PropertyToID("_Color");
}
if (settings.activeMaterial != null)
{
previewUtility.camera.clearFlags = CameraClearFlags.Nothing;
if (meshSubset < 0 || meshSubset >= submeshes)
{
for (int i = 0; i < submeshes; ++i)
{
if (tintSubmeshes)
customProperties.SetColor(colorPropID, GetSubMeshTint(i));
previewUtility.DrawMesh(mesh, pos, rot, settings.activeMaterial, i, customProperties);
}
}
else
previewUtility.DrawMesh(mesh, pos, rot, settings.activeMaterial, meshSubset, customProperties);
previewUtility.Render(false, false);
}
if (settings.wireMaterial != null && settings.drawWire)
{
previewUtility.camera.clearFlags = CameraClearFlags.Nothing;
GL.wireframe = true;
if (tintSubmeshes)
customProperties.SetColor(colorPropID, settings.wireMaterial.color);
if (meshSubset < 0 || meshSubset >= submeshes)
{
for (int i = 0; i < submeshes; ++i)
{
// lines/points already are wire-like; it does not make sense to overdraw
// them again with dark wireframe color
var topology = mesh.GetTopology(i);
if (topology == MeshTopology.Lines || topology == MeshTopology.LineStrip || topology == MeshTopology.Points)
continue;
previewUtility.DrawMesh(mesh, pos, rot, settings.wireMaterial, i, customProperties);
}
}
else
previewUtility.DrawMesh(mesh, pos, rot, settings.wireMaterial, meshSubset, customProperties);
previewUtility.Render(false, false);
GL.wireframe = false;
}
Unsupported.SetRenderSettingsUseFogNoDirty(oldFog);
}
void DoRenderPreview()
{
RenderMeshPreview(mesh, m_PreviewUtility, m_Settings, -1);
}
internal void OnPreviewGUI(Rect rect, GUIStyle background)
{
var evt = Event.current;
if (!ShaderUtil.hardwareSupportsRectRenderTexture)
{
if (evt.type == EventType.Repaint)
EditorGUI.DropShadowLabel(new Rect(rect.x, rect.y, rect.width, 40),
"Mesh preview requires\nrender texture support");
return;
}
if (evt.button <= 0)
m_Settings.previewDir = PreviewGUI.Drag2D(m_Settings.previewDir, rect);
if (evt.button == 1)
m_Settings.lightDir = PreviewGUI.Drag2D(m_Settings.lightDir, rect);
if (evt.type == EventType.ScrollWheel && rect.Contains(evt.mousePosition))
MeshPreviewZoom(rect, evt);
if (evt.type == EventType.MouseDrag && evt.button == 2 && rect.Contains(evt.mousePosition))
MeshPreviewPan(rect, evt);
if (evt.type != EventType.Repaint)
return;
m_PreviewUtility.BeginPreview(rect, background);
DoRenderPreview();
Handles.EndGUI();
Handles.SetCamera(m_PreviewUtility.camera);
Quaternion rot = Quaternion.Euler(m_Settings.previewDir.y, 0, 0) * Quaternion.Euler(0, m_Settings.previewDir.x, 0);
Vector3 pos = Vector3.zero;
Handles.matrix = Matrix4x4.TRS(pos, rot, Vector3.one);
if (m_Settings.showDesiredBox)
{
Handles.DrawWireCube(m_CenterBox - mesh.bounds.center, m_SizeBoxReference);
}
if (m_Settings.showActualBox)
{
Color prevColor = Handles.color;
Handles.color = m_ActualBoxColor;
Handles.DrawWireCube(m_CenterBox - mesh.bounds.center, m_ActualSizeBox);
Handles.color = prevColor;
}
m_PreviewUtility.EndAndDrawPreview(rect);
EditorGUI.DropShadowLabel(rect, GetInfoString(mesh));
}
internal void OnPreviewSettings()
{
if (!ShaderUtil.hardwareSupportsRectRenderTexture)
return;
GUI.enabled = true;
using (new EditorGUI.DisabledScope(false))
{
m_Settings.showActualBox = GUILayout.Toggle(m_Settings.showActualBox, Styles.showActualBox,
EditorStyles.toolbarButton, GUILayout.MinWidth(100));
m_Settings.showDesiredBox = GUILayout.Toggle(m_Settings.showDesiredBox, Styles.showDesiredBox,
EditorStyles.toolbarButton, GUILayout.MinWidth(105));
m_Settings.drawWire = GUILayout.Toggle(m_Settings.drawWire, Styles.wireframeToggle, EditorStyles.toolbarButton, GUILayout.MaxWidth(70));
}
}
void MeshPreviewZoom(Rect rect, Event evt)
{
float zoomDelta = -(HandleUtility.niceMouseDeltaZoom * 0.5f) * 0.05f;
var newZoom = m_Settings.zoomFactor + m_Settings.zoomFactor * zoomDelta;
newZoom = Mathf.Clamp(newZoom, 0.1f, 10.0f);
// we want to zoom around current mouse position
var mouseViewPos = new Vector2(
evt.mousePosition.x / rect.width,
1 - evt.mousePosition.y / rect.height);
var mouseWorldPos = m_PreviewUtility.camera.ViewportToWorldPoint(mouseViewPos);
var mouseToCamPos = m_Settings.orthoPosition - mouseWorldPos;
var newCamPos = mouseWorldPos + mouseToCamPos * (newZoom / m_Settings.zoomFactor);
m_Settings.orthoPosition.x = newCamPos.x;
m_Settings.orthoPosition.y = newCamPos.y;
m_Settings.zoomFactor = newZoom;
evt.Use();
}
void MeshPreviewPan(Rect rect, Event evt)
{
var cam = m_PreviewUtility.camera;
// event delta is in "screen" units of the preview rect, but the
// preview camera is rendering into a render target that could
// be different size; have to adjust drag position to match
var delta = new Vector3(
-evt.delta.x * cam.pixelWidth / rect.width,
evt.delta.y * cam.pixelHeight / rect.height,
0);
Vector3 screenPos;
Vector3 worldPos;
screenPos = cam.WorldToScreenPoint(m_Settings.pivotPositionOffset);
screenPos += delta;
worldPos = cam.ScreenToWorldPoint(screenPos) - m_Settings.pivotPositionOffset;
m_Settings.pivotPositionOffset += worldPos;
evt.Use();
}
static string GetInfoString(Mesh mesh)
{
if (mesh == null)
return "";
string info = $"{mesh.vertexCount} Vertices, {InternalMeshUtil.GetPrimitiveCount(mesh)} Triangles";
int submeshes = mesh.subMeshCount;
if (submeshes > 1)
info += $", {submeshes} Sub Meshes";
int blendShapeCount = mesh.blendShapeCount;
if (blendShapeCount > 0)
info += $", {blendShapeCount} Blend Shapes";
info += " | " + InternalMeshUtil.GetVertexFormat(mesh);
return info;
}
}
}