SdfBakerPreview.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. using System;
  2. using UnityEngine;
  3. using Transform = UnityEngine.Transform;
  4. using UObject = UnityEngine.Object;
  5. using UnityEditor;
  6. namespace UnityEditor.VFX.SDF
  7. {
  8. class SdfBakerPreview
  9. {
  10. internal static class Styles
  11. {
  12. internal static readonly GUIContent wireframeToggle = EditorGUIUtility.TrTextContent("Wireframe", "Show wireframe");
  13. internal static readonly GUIContent orthographicToggle = EditorGUIUtility.TrTextContent("Orthographic view");
  14. internal static readonly GUIContent showActualBox = EditorGUIUtility.TrTextContent("Show Actual Box");
  15. internal static readonly GUIContent showDesiredBox = EditorGUIUtility.TrTextContent("Show Desired Box");
  16. internal static GUIStyle preSlider = "preSlider";
  17. }
  18. internal class Settings
  19. {
  20. public bool drawWire = true;
  21. public bool showActualBox = true;
  22. public bool showDesiredBox = true;
  23. public Vector3 orthoPosition = new Vector3(0.0f, 0.0f, 0.0f);
  24. public Vector2 previewDir = new Vector2(0, 0);
  25. public Vector2 lightDir = new Vector2(0, 0);
  26. public Vector3 pivotPositionOffset = Vector3.zero;
  27. public float zoomFactor = 1.0f;
  28. public Material shadedPreviewMaterial;
  29. public Material activeMaterial;
  30. public Material wireMaterial;
  31. public Settings()
  32. {
  33. shadedPreviewMaterial = new Material(Shader.Find("Standard"));
  34. shadedPreviewMaterial.hideFlags = HideFlags.DontSave;
  35. wireMaterial = CreateWireframeMaterial();
  36. activeMaterial = shadedPreviewMaterial;
  37. orthoPosition = new Vector3(0.5f, 0.5f, -1);
  38. previewDir = new Vector2(130, 0);
  39. lightDir = new Vector2(-40, -40);
  40. zoomFactor = 1.0f;
  41. }
  42. public void Dispose()
  43. {
  44. if (shadedPreviewMaterial != null)
  45. UObject.DestroyImmediate(shadedPreviewMaterial);
  46. if (wireMaterial != null)
  47. UObject.DestroyImmediate(wireMaterial);
  48. }
  49. }
  50. Mesh m_Target;
  51. internal Mesh mesh
  52. {
  53. get => m_Target;
  54. set => m_Target = value;
  55. }
  56. private Vector3 m_SizeBoxReference = Vector3.one;
  57. private Vector3 m_ActualSizeBox = Vector3.one;
  58. private Vector3 m_CenterBox = Vector3.zero;
  59. private Color m_ActualBoxColor = new Color(0, 255.0f / 255, 70.0f / 255);
  60. internal Vector3 sizeBoxReference
  61. {
  62. get => m_SizeBoxReference;
  63. set => m_SizeBoxReference = value;
  64. }
  65. internal Vector3 actualSizeBox
  66. {
  67. get => m_ActualSizeBox;
  68. set => m_ActualSizeBox = value;
  69. }
  70. internal Vector3 centerBox
  71. {
  72. get => m_CenterBox;
  73. set => m_CenterBox = value;
  74. }
  75. private bool m_Orthographic = false;
  76. internal bool orthographic
  77. {
  78. get => m_Orthographic;
  79. set => m_Orthographic = value;
  80. }
  81. PreviewRenderUtility m_PreviewUtility;
  82. Settings m_Settings;
  83. internal SdfBakerPreview(Mesh target)
  84. {
  85. m_Target = target;
  86. m_PreviewUtility = new PreviewRenderUtility();
  87. m_PreviewUtility.camera.fieldOfView = 30.0f;
  88. m_PreviewUtility.camera.transform.position = new Vector3(5, 5, 0);
  89. m_Settings = new Settings();
  90. }
  91. internal void Dispose()
  92. {
  93. m_PreviewUtility.Cleanup();
  94. m_Settings.Dispose();
  95. }
  96. static Material CreateWireframeMaterial()
  97. {
  98. var shader = Shader.Find("Hidden/Internal-Colored");
  99. if (!shader)
  100. {
  101. Debug.LogWarning("Could not find the built-in Internal-Colored shader");
  102. return null;
  103. }
  104. var mat = new Material(shader);
  105. mat.hideFlags = HideFlags.HideAndDontSave;
  106. mat.SetColor("_Color", new Color(0, 0, 0, 0.3f));
  107. mat.SetFloat("_ZWrite", 0.0f);
  108. mat.SetFloat("_ZBias", -1.0f);
  109. return mat;
  110. }
  111. void ResetView()
  112. {
  113. m_Settings.zoomFactor = 1.0f;
  114. m_Settings.orthoPosition = new Vector3(0.5f, 0.5f, -1);
  115. m_Settings.pivotPositionOffset = Vector3.zero;
  116. }
  117. internal void RenderMeshPreview(
  118. Mesh mesh,
  119. PreviewRenderUtility previewUtility,
  120. Settings settings,
  121. int meshSubset)
  122. {
  123. if (mesh == null || previewUtility == null)
  124. return;
  125. Bounds bounds = mesh.bounds;
  126. UnityEngine.Transform renderCamTransform = previewUtility.camera.GetComponent<UnityEngine.Transform>();
  127. if (m_Orthographic)
  128. {
  129. previewUtility.camera.nearClipPlane = 1;
  130. previewUtility.camera.farClipPlane = 1 + sizeBoxReference.magnitude;
  131. previewUtility.camera.orthographicSize = mesh.bounds.extents.y * 1.1f;
  132. }
  133. else
  134. {
  135. previewUtility.camera.nearClipPlane = 0.0001f;
  136. previewUtility.camera.farClipPlane = 1000f;
  137. }
  138. float halfSize = bounds.extents.magnitude;
  139. float distance = 4.0f * halfSize;
  140. previewUtility.camera.orthographic = m_Orthographic;
  141. Quaternion camRotation = Quaternion.identity;
  142. Vector3 camPosition = camRotation * Vector3.forward * (-distance * settings.zoomFactor) + settings.pivotPositionOffset;
  143. renderCamTransform.position = camPosition;
  144. renderCamTransform.rotation = camRotation;
  145. previewUtility.lights[0].intensity = 1.1f;
  146. previewUtility.lights[0].transform.rotation = Quaternion.Euler(-settings.lightDir.y, -settings.lightDir.x, 0);
  147. previewUtility.lights[1].intensity = 1.1f;
  148. previewUtility.lights[1].transform.rotation = Quaternion.Euler(settings.lightDir.y, settings.lightDir.x, 0);
  149. previewUtility.ambientColor = new Color(.1f, .1f, .1f, 0);
  150. RenderMeshPreviewSkipCameraAndLighting(mesh, bounds, previewUtility, settings, null, meshSubset);
  151. }
  152. internal static Color GetSubMeshTint(int index)
  153. {
  154. // color palette generator based on "golden ratio" idea, like in
  155. // https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
  156. var hue = Mathf.Repeat(index * 0.618f, 1);
  157. var sat = index == 0 ? 0f : 0.3f;
  158. var val = 1f;
  159. return Color.HSVToRGB(hue, sat, val);
  160. }
  161. internal void RenderMeshPreviewSkipCameraAndLighting(
  162. Mesh mesh,
  163. Bounds bounds,
  164. PreviewRenderUtility previewUtility,
  165. Settings settings,
  166. MaterialPropertyBlock customProperties,
  167. int meshSubset) // -1 for whole mesh
  168. {
  169. if (mesh == null || previewUtility == null)
  170. return;
  171. Quaternion rot = Quaternion.Euler(settings.previewDir.y, 0, 0) * Quaternion.Euler(0, settings.previewDir.x, 0);
  172. Vector3 pos = rot * (-bounds.center);
  173. bool oldFog = RenderSettings.fog;
  174. Unsupported.SetRenderSettingsUseFogNoDirty(false);
  175. int submeshes = mesh.subMeshCount;
  176. var tintSubmeshes = false;
  177. var colorPropID = 0;
  178. if (submeshes > 1 && customProperties == null && meshSubset == -1)
  179. {
  180. tintSubmeshes = true;
  181. customProperties = new MaterialPropertyBlock();
  182. colorPropID = Shader.PropertyToID("_Color");
  183. }
  184. if (settings.activeMaterial != null)
  185. {
  186. previewUtility.camera.clearFlags = CameraClearFlags.Nothing;
  187. if (meshSubset < 0 || meshSubset >= submeshes)
  188. {
  189. for (int i = 0; i < submeshes; ++i)
  190. {
  191. if (tintSubmeshes)
  192. customProperties.SetColor(colorPropID, GetSubMeshTint(i));
  193. previewUtility.DrawMesh(mesh, pos, rot, settings.activeMaterial, i, customProperties);
  194. }
  195. }
  196. else
  197. previewUtility.DrawMesh(mesh, pos, rot, settings.activeMaterial, meshSubset, customProperties);
  198. previewUtility.Render(false, false);
  199. }
  200. if (settings.wireMaterial != null && settings.drawWire)
  201. {
  202. previewUtility.camera.clearFlags = CameraClearFlags.Nothing;
  203. GL.wireframe = true;
  204. if (tintSubmeshes)
  205. customProperties.SetColor(colorPropID, settings.wireMaterial.color);
  206. if (meshSubset < 0 || meshSubset >= submeshes)
  207. {
  208. for (int i = 0; i < submeshes; ++i)
  209. {
  210. // lines/points already are wire-like; it does not make sense to overdraw
  211. // them again with dark wireframe color
  212. var topology = mesh.GetTopology(i);
  213. if (topology == MeshTopology.Lines || topology == MeshTopology.LineStrip || topology == MeshTopology.Points)
  214. continue;
  215. previewUtility.DrawMesh(mesh, pos, rot, settings.wireMaterial, i, customProperties);
  216. }
  217. }
  218. else
  219. previewUtility.DrawMesh(mesh, pos, rot, settings.wireMaterial, meshSubset, customProperties);
  220. previewUtility.Render(false, false);
  221. GL.wireframe = false;
  222. }
  223. Unsupported.SetRenderSettingsUseFogNoDirty(oldFog);
  224. }
  225. void DoRenderPreview()
  226. {
  227. RenderMeshPreview(mesh, m_PreviewUtility, m_Settings, -1);
  228. }
  229. internal void OnPreviewGUI(Rect rect, GUIStyle background)
  230. {
  231. var evt = Event.current;
  232. if (!ShaderUtil.hardwareSupportsRectRenderTexture)
  233. {
  234. if (evt.type == EventType.Repaint)
  235. EditorGUI.DropShadowLabel(new Rect(rect.x, rect.y, rect.width, 40),
  236. "Mesh preview requires\nrender texture support");
  237. return;
  238. }
  239. if (evt.button <= 0)
  240. m_Settings.previewDir = PreviewGUI.Drag2D(m_Settings.previewDir, rect);
  241. if (evt.button == 1)
  242. m_Settings.lightDir = PreviewGUI.Drag2D(m_Settings.lightDir, rect);
  243. if (evt.type == EventType.ScrollWheel && rect.Contains(evt.mousePosition))
  244. MeshPreviewZoom(rect, evt);
  245. if (evt.type == EventType.MouseDrag && evt.button == 2 && rect.Contains(evt.mousePosition))
  246. MeshPreviewPan(rect, evt);
  247. if (evt.type != EventType.Repaint)
  248. return;
  249. m_PreviewUtility.BeginPreview(rect, background);
  250. DoRenderPreview();
  251. Handles.EndGUI();
  252. Handles.SetCamera(m_PreviewUtility.camera);
  253. Quaternion rot = Quaternion.Euler(m_Settings.previewDir.y, 0, 0) * Quaternion.Euler(0, m_Settings.previewDir.x, 0);
  254. Vector3 pos = Vector3.zero;
  255. Handles.matrix = Matrix4x4.TRS(pos, rot, Vector3.one);
  256. if (m_Settings.showDesiredBox)
  257. {
  258. Handles.DrawWireCube(m_CenterBox - mesh.bounds.center, m_SizeBoxReference);
  259. }
  260. if (m_Settings.showActualBox)
  261. {
  262. Color prevColor = Handles.color;
  263. Handles.color = m_ActualBoxColor;
  264. Handles.DrawWireCube(m_CenterBox - mesh.bounds.center, m_ActualSizeBox);
  265. Handles.color = prevColor;
  266. }
  267. m_PreviewUtility.EndAndDrawPreview(rect);
  268. EditorGUI.DropShadowLabel(rect, GetInfoString(mesh));
  269. }
  270. internal void OnPreviewSettings()
  271. {
  272. if (!ShaderUtil.hardwareSupportsRectRenderTexture)
  273. return;
  274. GUI.enabled = true;
  275. using (new EditorGUI.DisabledScope(false))
  276. {
  277. m_Settings.showActualBox = GUILayout.Toggle(m_Settings.showActualBox, Styles.showActualBox,
  278. EditorStyles.toolbarButton, GUILayout.MinWidth(100));
  279. m_Settings.showDesiredBox = GUILayout.Toggle(m_Settings.showDesiredBox, Styles.showDesiredBox,
  280. EditorStyles.toolbarButton, GUILayout.MinWidth(105));
  281. m_Settings.drawWire = GUILayout.Toggle(m_Settings.drawWire, Styles.wireframeToggle, EditorStyles.toolbarButton, GUILayout.MaxWidth(70));
  282. }
  283. }
  284. void MeshPreviewZoom(Rect rect, Event evt)
  285. {
  286. float zoomDelta = -(HandleUtility.niceMouseDeltaZoom * 0.5f) * 0.05f;
  287. var newZoom = m_Settings.zoomFactor + m_Settings.zoomFactor * zoomDelta;
  288. newZoom = Mathf.Clamp(newZoom, 0.1f, 10.0f);
  289. // we want to zoom around current mouse position
  290. var mouseViewPos = new Vector2(
  291. evt.mousePosition.x / rect.width,
  292. 1 - evt.mousePosition.y / rect.height);
  293. var mouseWorldPos = m_PreviewUtility.camera.ViewportToWorldPoint(mouseViewPos);
  294. var mouseToCamPos = m_Settings.orthoPosition - mouseWorldPos;
  295. var newCamPos = mouseWorldPos + mouseToCamPos * (newZoom / m_Settings.zoomFactor);
  296. m_Settings.orthoPosition.x = newCamPos.x;
  297. m_Settings.orthoPosition.y = newCamPos.y;
  298. m_Settings.zoomFactor = newZoom;
  299. evt.Use();
  300. }
  301. void MeshPreviewPan(Rect rect, Event evt)
  302. {
  303. var cam = m_PreviewUtility.camera;
  304. // event delta is in "screen" units of the preview rect, but the
  305. // preview camera is rendering into a render target that could
  306. // be different size; have to adjust drag position to match
  307. var delta = new Vector3(
  308. -evt.delta.x * cam.pixelWidth / rect.width,
  309. evt.delta.y * cam.pixelHeight / rect.height,
  310. 0);
  311. Vector3 screenPos;
  312. Vector3 worldPos;
  313. screenPos = cam.WorldToScreenPoint(m_Settings.pivotPositionOffset);
  314. screenPos += delta;
  315. worldPos = cam.ScreenToWorldPoint(screenPos) - m_Settings.pivotPositionOffset;
  316. m_Settings.pivotPositionOffset += worldPos;
  317. evt.Use();
  318. }
  319. static string GetInfoString(Mesh mesh)
  320. {
  321. if (mesh == null)
  322. return "";
  323. string info = $"{mesh.vertexCount} Vertices, {InternalMeshUtil.GetPrimitiveCount(mesh)} Triangles";
  324. int submeshes = mesh.subMeshCount;
  325. if (submeshes > 1)
  326. info += $", {submeshes} Sub Meshes";
  327. int blendShapeCount = mesh.blendShapeCount;
  328. if (blendShapeCount > 0)
  329. info += $", {blendShapeCount} Blend Shapes";
  330. info += " | " + InternalMeshUtil.GetVertexFormat(mesh);
  331. return info;
  332. }
  333. }
  334. }