ObiDistanceFieldRenderer.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. using System;
  2. using UnityEngine;
  3. namespace Obi
  4. {
  5. [ExecuteInEditMode]
  6. [RequireComponent(typeof(ObiCollider))]
  7. public class ObiDistanceFieldRenderer : MonoBehaviour
  8. {
  9. public enum Axis{
  10. X = 0,
  11. Y = 1,
  12. Z = 2,
  13. }
  14. public Axis axis;
  15. [Range(0,1)]
  16. public float slice = 0.25f;
  17. public float maxDistance = 0.5f;
  18. private ObiCollider unityCollider;
  19. private Material material;
  20. private Mesh planeMesh;
  21. private Texture2D cutawayTexture;
  22. private float sampleSize;
  23. private int sampleCount;
  24. private Color boundsColor = new Color(1,1,1,0.5f);
  25. public void Awake(){
  26. unityCollider = GetComponent<ObiCollider>();
  27. }
  28. public void OnEnable(){
  29. material = GameObject.Instantiate(Resources.Load<Material>("ObiMaterials/DistanceFieldRendering"));
  30. material.hideFlags = HideFlags.HideAndDontSave;
  31. }
  32. public void OnDisable(){
  33. Cleanup();
  34. }
  35. private void Cleanup(){
  36. GameObject.DestroyImmediate(cutawayTexture);
  37. GameObject.DestroyImmediate(planeMesh);
  38. GameObject.DestroyImmediate(material);
  39. }
  40. private void ResizeTexture(){
  41. if (cutawayTexture == null){
  42. cutawayTexture = new Texture2D(sampleCount,sampleCount,TextureFormat.RHalf,false);
  43. cutawayTexture.wrapMode = TextureWrapMode.Clamp;
  44. cutawayTexture.hideFlags = HideFlags.HideAndDontSave;
  45. }else
  46. cutawayTexture.Resize(sampleCount,sampleCount);
  47. }
  48. private void CreatePlaneMesh(ObiDistanceField field){
  49. if (field != null && planeMesh == null){
  50. float uvBorder = (1-field.FieldBounds.size[0]/(sampleSize*sampleCount)) * 0.5f;
  51. planeMesh = new Mesh();
  52. planeMesh.vertices = new Vector3[]{new Vector3(-0.5f,-0.5f,0),
  53. new Vector3(0.5f,-0.5f,0),
  54. new Vector3(-0.5f,0.5f,0),
  55. new Vector3(0.5f,0.5f,0)};
  56. planeMesh.uv = new Vector2[]{new Vector2(uvBorder,uvBorder),
  57. new Vector2(1-uvBorder,uvBorder),
  58. new Vector2(uvBorder,1-uvBorder),
  59. new Vector2(1-uvBorder,1-uvBorder)};
  60. planeMesh.normals = new Vector3[]{-Vector3.forward,-Vector3.forward,-Vector3.forward,-Vector3.forward};
  61. planeMesh.triangles = new int[]{0,2,1,2,3,1};
  62. }
  63. }
  64. private void RefreshCutawayTexture (ObiDistanceField field)
  65. {
  66. if (field == null)
  67. return;
  68. Bounds b = field.FieldBounds;
  69. sampleSize = field.EffectiveSampleSize;
  70. sampleCount = (int)(b.size[0] / sampleSize)+1;
  71. CreatePlaneMesh(field);
  72. ResizeTexture();
  73. float sweep = (sampleCount*slice) * sampleSize;
  74. Vector3 origin = b.center - b.extents;
  75. for (int x = 0; x < sampleCount; ++x)
  76. for (int y = 0; y < sampleCount; ++y)
  77. {
  78. Vector3 offset = Vector3.zero;
  79. switch(axis){
  80. case Axis.X: offset = new Vector3(sweep,y * sampleSize,x * sampleSize); break;
  81. case Axis.Y: offset = new Vector3(x * sampleSize,sweep,y * sampleSize); break;
  82. case Axis.Z: offset = new Vector3(x * sampleSize,y * sampleSize,sweep); break;
  83. }
  84. Vector4 position = origin + offset;
  85. float distance = Oni.SampleDistanceField(field.OniDistanceField,position.x,position.y,position.z);
  86. float value = ObiUtils.Remap(distance,-maxDistance,maxDistance,0,1);
  87. cutawayTexture.SetPixel(x,y,new Color(value,0,0));
  88. }
  89. cutawayTexture.Apply();
  90. }
  91. private void DrawCutawayPlane(ObiDistanceField field, Matrix4x4 matrix){
  92. if (field == null)
  93. return;
  94. RefreshCutawayTexture(field);
  95. material.mainTexture = cutawayTexture;
  96. material.SetPass(0);
  97. Quaternion rotation = Quaternion.identity;
  98. Vector3 offset = Vector3.zero;
  99. offset[(int)axis] = field.FieldBounds.size[0];
  100. if (axis == Axis.Y)
  101. rotation = Quaternion.Euler(90,0,0);
  102. else if (axis == Axis.X)
  103. rotation = Quaternion.Euler(0,-90,0);
  104. Matrix4x4 sc = Matrix4x4.TRS(field.FieldBounds.center + offset*(slice-0.5f),rotation,Vector3.one*field.FieldBounds.size[0]);
  105. Graphics.DrawMeshNow(planeMesh,matrix*sc);
  106. }
  107. public void OnDrawGizmos(){
  108. if (unityCollider != null && unityCollider.distanceField != null && unityCollider.distanceField.Initialized && material != null){
  109. DrawCutawayPlane(unityCollider.distanceField,transform.localToWorldMatrix);
  110. Gizmos.color = boundsColor;
  111. Gizmos.DrawWireCube(unityCollider.distanceField.FieldBounds.center,unityCollider.distanceField.FieldBounds.size);
  112. }
  113. }
  114. }
  115. }