You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

451 lines
13 KiB

  1. using UnityEngine;
  2. using UnityEditor;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. namespace ProGrids
  7. {
  8. public class pg_GridRenderer
  9. {
  10. static readonly HideFlags PG_HIDE_FLAGS = HideFlags.HideAndDontSave;
  11. const string PREVIEW_OBJECT_NAME = "ProGridsGridObject";
  12. const string MATERIAL_OBJECT_NAME = "ProGridsMaterialObject";
  13. const string MESH_OBJECT_NAME = "ProGridsMeshObject";
  14. const string GRID_SHADER = "Hidden/ProGrids/pg_GridShader";
  15. const int MAX_LINES = 256;
  16. static GameObject gridObject;
  17. static Mesh gridMesh;
  18. static Material gridMaterial;
  19. public static int majorLineIncrement = 10;
  20. /**
  21. * Destroy any existing render objects, then initialize new ones.
  22. */
  23. public static void Init()
  24. {
  25. Destroy();
  26. gridObject = EditorUtility.CreateGameObjectWithHideFlags(PREVIEW_OBJECT_NAME, PG_HIDE_FLAGS, new System.Type[2]{typeof(MeshFilter), typeof(MeshRenderer)});
  27. majorLineIncrement = EditorPrefs.GetInt(pg_Constant.MajorLineIncrement, 10);
  28. if(majorLineIncrement < 2)
  29. majorLineIncrement = 2;
  30. // Force the mesh to only render in SceneView
  31. pg_SceneMeshRender renderer = gridObject.AddComponent<pg_SceneMeshRender>();
  32. gridMesh = new Mesh();
  33. gridMesh.name = MESH_OBJECT_NAME;
  34. gridMesh.hideFlags = PG_HIDE_FLAGS;
  35. gridMaterial = new Material(Shader.Find(GRID_SHADER));
  36. gridMaterial.name = MATERIAL_OBJECT_NAME;
  37. gridMaterial.hideFlags = PG_HIDE_FLAGS;
  38. renderer.mesh = gridMesh;
  39. renderer.material = gridMaterial;
  40. }
  41. public static void Destroy()
  42. {
  43. DestoryObjectsWithName(MESH_OBJECT_NAME, typeof(Mesh));
  44. DestoryObjectsWithName(MATERIAL_OBJECT_NAME, typeof(Material));
  45. DestoryObjectsWithName(PREVIEW_OBJECT_NAME, typeof(GameObject));
  46. }
  47. static void DestoryObjectsWithName(string Name, System.Type type)
  48. {
  49. IEnumerable go = Resources.FindObjectsOfTypeAll(type).Where(x => x.name.Contains(Name));
  50. foreach(Object t in go)
  51. {
  52. GameObject.DestroyImmediate(t);
  53. }
  54. }
  55. private static int tan_iter, bit_iter, max = MAX_LINES, div = 1;
  56. /**
  57. * Returns the distance this grid is drawing
  58. */
  59. public static float DrawPlane(Camera cam, Vector3 pivot, Vector3 tangent, Vector3 bitangent, float snapValue, Color color, float alphaBump)
  60. {
  61. if(!gridMesh || !gridMaterial || !gridObject)
  62. Init();
  63. gridMaterial.SetFloat("_AlphaCutoff", .1f);
  64. gridMaterial.SetFloat("_AlphaFade", .6f);
  65. pivot = pg_Util.SnapValue(pivot, snapValue);
  66. Vector3 p = cam.WorldToViewportPoint(pivot);
  67. bool inFrustum = (p.x >= 0f && p.x <= 1f) &&
  68. (p.y >= 0f && p.y <= 1f) &&
  69. p.z >= 0f;
  70. float[] distances = GetDistanceToFrustumPlanes(cam, pivot, tangent, bitangent, 24f);
  71. if(inFrustum)
  72. {
  73. tan_iter = (int)(Mathf.Ceil( (Mathf.Abs(distances[0]) + Mathf.Abs(distances[2]))/snapValue ));
  74. bit_iter = (int)(Mathf.Ceil( (Mathf.Abs(distances[1]) + Mathf.Abs(distances[3]))/snapValue ));
  75. max = Mathf.Max( tan_iter, bit_iter );
  76. // if the max is around 3x greater than min, we're probably skewing the camera at near-plane
  77. // angle, so use the min instead.
  78. if(max > Mathf.Min(tan_iter, bit_iter) * 2)
  79. max = (int) Mathf.Min(tan_iter, bit_iter) * 2;
  80. div = 1;
  81. float dot = Vector3.Dot( cam.transform.position-pivot, Vector3.Cross(tangent, bitangent) );
  82. if(max > MAX_LINES)
  83. {
  84. if(Vector3.Distance(cam.transform.position, pivot) > 50f * snapValue && Mathf.Abs(dot) > .8f)
  85. {
  86. while(max/div > MAX_LINES)
  87. div += div;
  88. }
  89. else
  90. {
  91. max = MAX_LINES;
  92. }
  93. }
  94. }
  95. // origin, tan, bitan, increment, iterations, divOffset, color, primary alpha bump
  96. DrawFullGrid(cam, pivot, tangent, bitangent, snapValue*div, max/div, div, color, alphaBump);
  97. return ((snapValue*div)*(max/div));
  98. }
  99. public static void DrawGridPerspective(Camera cam, Vector3 pivot, float snapValue, Color[] colors, float alphaBump)
  100. {
  101. if(!gridMesh || !gridMaterial || !gridObject)
  102. Init();
  103. gridMaterial.SetFloat("_AlphaCutoff", 0f);
  104. gridMaterial.SetFloat("_AlphaFade", 0f);
  105. Vector3 camDir = (pivot - cam.transform.position).normalized;
  106. pivot = pg_Util.SnapValue(pivot, snapValue);
  107. // Used to flip the grid to match whatever direction the cam is currently
  108. // coming at the pivot from
  109. Vector3 right = camDir.x < 0f ? Vector3.right : Vector3.right * -1f;
  110. Vector3 up = camDir.y < 0f ? Vector3.up : Vector3.up * -1f;
  111. Vector3 forward = camDir.z < 0f ? Vector3.forward : Vector3.forward * -1f;
  112. // Get intersecting point for each axis, if it exists
  113. Ray ray_x = new Ray(pivot, right);
  114. Ray ray_y = new Ray(pivot, up);
  115. Ray ray_z = new Ray(pivot, forward);
  116. float x_dist = 10f, y_dist = 10f, z_dist = 10f;
  117. bool x_intersect = false, y_intersect = false, z_intersect = false;
  118. Plane[] planes = GeometryUtility.CalculateFrustumPlanes(cam);
  119. foreach(Plane p in planes)
  120. {
  121. float dist;
  122. float t = 0;
  123. if(p.Raycast(ray_x, out dist))
  124. {
  125. t = Vector3.Distance(pivot, ray_x.GetPoint(dist));
  126. if(t < x_dist || !x_intersect)
  127. {
  128. x_intersect = true;
  129. x_dist = t;
  130. }
  131. }
  132. if(p.Raycast(ray_y, out dist))
  133. {
  134. t = Vector3.Distance(pivot, ray_y.GetPoint(dist));
  135. if(t < y_dist || !y_intersect)
  136. {
  137. y_intersect = true;
  138. y_dist = t;
  139. }
  140. }
  141. if(p.Raycast(ray_z, out dist))
  142. {
  143. t = Vector3.Distance(pivot, ray_z.GetPoint(dist));
  144. if(t < z_dist || !z_intersect)
  145. {
  146. z_intersect = true;
  147. z_dist = t;
  148. }
  149. }
  150. }
  151. int x_iter = (int)(Mathf.Ceil(Mathf.Max(x_dist, y_dist))/snapValue);
  152. int y_iter = (int)(Mathf.Ceil(Mathf.Max(x_dist, z_dist))/snapValue);
  153. int z_iter = (int)(Mathf.Ceil(Mathf.Max(z_dist, y_dist))/snapValue);
  154. int max = Mathf.Max( Mathf.Max(x_iter, y_iter), z_iter );
  155. int div = 1;
  156. while(max/div> MAX_LINES)
  157. {
  158. div++;
  159. }
  160. Vector3[] vertices_t = null;
  161. Vector3[] normals_t = null;
  162. Color[] colors_t = null;
  163. int[] indices_t = null;
  164. List<Vector3> vertices_m = new List<Vector3>();
  165. List<Vector3> normals_m = new List<Vector3>();
  166. List<Color> colors_m = new List<Color>();
  167. List<int> indices_m = new List<int>();
  168. // X plane
  169. DrawHalfGrid(cam, pivot, up, right, snapValue*div, x_iter/div, colors[0], alphaBump, out vertices_t, out normals_t, out colors_t, out indices_t, 0);
  170. vertices_m.AddRange(vertices_t);
  171. normals_m.AddRange(normals_t);
  172. colors_m.AddRange(colors_t);
  173. indices_m.AddRange(indices_t);
  174. // Y plane
  175. DrawHalfGrid(cam, pivot, right, forward, snapValue*div, y_iter/div, colors[1], alphaBump, out vertices_t, out normals_t, out colors_t, out indices_t, vertices_m.Count);
  176. vertices_m.AddRange(vertices_t);
  177. normals_m.AddRange(normals_t);
  178. colors_m.AddRange(colors_t);
  179. indices_m.AddRange(indices_t);
  180. // Z plane
  181. DrawHalfGrid(cam, pivot, forward, up, snapValue*div, z_iter/div, colors[2], alphaBump, out vertices_t, out normals_t, out colors_t, out indices_t, vertices_m.Count);
  182. vertices_m.AddRange(vertices_t);
  183. normals_m.AddRange(normals_t);
  184. colors_m.AddRange(colors_t);
  185. indices_m.AddRange(indices_t);
  186. gridMesh.Clear();
  187. gridMesh.vertices = vertices_m.ToArray();
  188. gridMesh.normals = normals_m.ToArray();
  189. gridMesh.subMeshCount = 1;
  190. gridMesh.uv = new Vector2[vertices_m.Count];
  191. gridMesh.colors = colors_m.ToArray();
  192. gridMesh.SetIndices(indices_m.ToArray(), MeshTopology.Lines, 0);
  193. }
  194. private static void DrawHalfGrid(Camera cam, Vector3 pivot, Vector3 tan, Vector3 bitan, float increment, int iterations, Color secondary, float alphaBump,
  195. out Vector3[] vertices,
  196. out Vector3[] normals,
  197. out Color[] colors,
  198. out int[] indices, int offset)
  199. {
  200. Color primary = secondary;
  201. primary.a += alphaBump;
  202. float len = increment * iterations;
  203. int highlightOffsetTan = (int)((pg_Util.ValueFromMask(pivot, tan) % (increment * majorLineIncrement)) / increment);
  204. int highlightOffsetBitan = (int)((pg_Util.ValueFromMask(pivot, bitan) % (increment * majorLineIncrement)) / increment);
  205. iterations++;
  206. // this could only use 3 verts per line
  207. float fade = .75f;
  208. float fadeDist = len * fade;
  209. Vector3 nrm = Vector3.Cross(tan, bitan);
  210. vertices = new Vector3[iterations*6-3];
  211. normals = new Vector3[iterations*6-3];
  212. indices = new int[iterations*8-4];
  213. colors = new Color[iterations*6-3];
  214. vertices[0] = pivot;
  215. vertices[1] = (pivot + bitan*fadeDist);
  216. vertices[2] = (pivot + bitan*len);
  217. normals[0] = nrm;
  218. normals[1] = nrm;
  219. normals[2] = nrm;
  220. indices[0] = 0 + offset;
  221. indices[1] = 1 + offset;
  222. indices[2] = 1 + offset;
  223. indices[3] = 2 + offset;
  224. colors[0] = primary;
  225. colors[1] = primary;
  226. colors[2] = primary;
  227. colors[2].a = 0f;
  228. int n = 4;
  229. int v = 3;
  230. for(int i = 1; i < iterations; i++)
  231. {
  232. // MeshTopology doesn't exist prior to Unity 4
  233. vertices[v+0] = pivot + i * tan * increment;
  234. vertices[v+1] = (pivot + bitan*fadeDist) + i * tan * increment;
  235. vertices[v+2] = (pivot + bitan*len) + i * tan * increment;
  236. vertices[v+3] = pivot + i * bitan * increment;
  237. vertices[v+4] = (pivot + tan*fadeDist) + i * bitan * increment;
  238. vertices[v+5] = (pivot + tan*len) + i * bitan * increment;
  239. normals[v+0] = nrm;
  240. normals[v+1] = nrm;
  241. normals[v+2] = nrm;
  242. normals[v+3] = nrm;
  243. normals[v+4] = nrm;
  244. normals[v+5] = nrm;
  245. indices[n+0] = v + 0 + offset;
  246. indices[n+1] = v + 1 + offset;
  247. indices[n+2] = v + 1 + offset;
  248. indices[n+3] = v + 2 + offset;
  249. indices[n+4] = v + 3 + offset;
  250. indices[n+5] = v + 4 + offset;
  251. indices[n+6] = v + 4 + offset;
  252. indices[n+7] = v + 5 + offset;
  253. float alpha = (i/(float)iterations);
  254. alpha = alpha < fade ? 1f : 1f - ( (alpha-fade)/(1-fade) );
  255. Color col = (i+highlightOffsetTan) % majorLineIncrement == 0 ? primary : secondary;
  256. col.a *= alpha;
  257. colors[v+0] = col;
  258. colors[v+1] = col;
  259. colors[v+2] = col;
  260. colors[v+2].a = 0f;
  261. col = (i+highlightOffsetBitan) % majorLineIncrement == 0 ? primary : secondary;
  262. col.a *= alpha;
  263. colors[v+3] = col;
  264. colors[v+4] = col;
  265. colors[v+5] = col;
  266. colors[v+5].a = 0f;
  267. n += 8;
  268. v += 6;
  269. }
  270. }
  271. /**
  272. * Draws a plane grid using pivot point, the right and forward directions, and how far each direction should extend
  273. */
  274. private static void DrawFullGrid(Camera cam, Vector3 pivot, Vector3 tan, Vector3 bitan, float increment, int iterations, int div, Color secondary, float alphaBump)
  275. {
  276. Color primary = secondary;
  277. primary.a += alphaBump;
  278. float len = iterations * increment;
  279. iterations++;
  280. Vector3 start = pivot - tan*(len/2f) - bitan*(len/2f);
  281. start = pg_Util.SnapValue(start, bitan+tan, increment);
  282. float inc = increment;
  283. int highlightOffsetTan = (int)((pg_Util.ValueFromMask(start, tan) % (inc*majorLineIncrement)) / inc);
  284. int highlightOffsetBitan = (int)((pg_Util.ValueFromMask(start, bitan) % (inc*majorLineIncrement)) / inc);
  285. Vector3[] lines = new Vector3[iterations * 4];
  286. int[] indices = new int[iterations * 4];
  287. Color[] colors = new Color[iterations * 4];
  288. int v = 0, t = 0;
  289. for(int i = 0; i < iterations; i++)
  290. {
  291. Vector3 a = start + tan * i * increment;
  292. Vector3 b = start + bitan * i * increment;
  293. lines[v+0] = a;
  294. lines[v+1] = a + bitan * len;
  295. lines[v+2] = b;
  296. lines[v+3] = b + tan * len;
  297. indices[t++] = v;
  298. indices[t++] = v+1;
  299. indices[t++] = v+2;
  300. indices[t++] = v+3;
  301. Color col = (i + highlightOffsetTan) % majorLineIncrement == 0 ? primary : secondary;
  302. // tan
  303. colors[v+0] = col;
  304. colors[v+1] = col;
  305. col = (i + highlightOffsetBitan) % majorLineIncrement == 0 ? primary : secondary;
  306. // bitan
  307. colors[v+2] = col;
  308. colors[v+3] = col;
  309. v += 4;
  310. }
  311. Vector3 nrm = Vector3.Cross(tan, bitan);
  312. Vector3[] nrms = new Vector3[lines.Length];
  313. for(int i = 0; i < lines.Length; i++)
  314. nrms[i] = nrm;
  315. gridMesh.Clear();
  316. gridMesh.vertices = lines;
  317. gridMesh.normals = nrms;
  318. gridMesh.subMeshCount = 1;
  319. gridMesh.uv = new Vector2[lines.Length];
  320. gridMesh.colors = colors;
  321. gridMesh.SetIndices(indices, MeshTopology.Lines, 0);
  322. }
  323. /**
  324. * \brief Returns the distance from pivot to frustum plane in the order of
  325. * float[] { tan, bitan, -tan, -bitan }
  326. */
  327. private static float[] GetDistanceToFrustumPlanes(Camera cam, Vector3 pivot, Vector3 tan, Vector3 bitan, float minDist)
  328. {
  329. Ray[] rays = new Ray[4]
  330. {
  331. new Ray(pivot, tan),
  332. new Ray(pivot, bitan),
  333. new Ray(pivot, -tan),
  334. new Ray(pivot, -bitan)
  335. };
  336. float[] intersects = new float[4] { minDist, minDist, minDist, minDist };
  337. bool[] intersection_found = new bool[4] { false, false, false, false };
  338. Plane[] planes = GeometryUtility.CalculateFrustumPlanes(cam);
  339. foreach(Plane p in planes)
  340. {
  341. float dist;
  342. float t = 0;
  343. for(int i = 0; i < 4; i++)
  344. {
  345. if(p.Raycast(rays[i], out dist))
  346. {
  347. t = Vector3.Distance(pivot, rays[i].GetPoint(dist));
  348. if(t < intersects[i] || !intersection_found[i])
  349. {
  350. intersection_found[i] = true;
  351. intersects[i] = Mathf.Max(minDist, t);
  352. }
  353. }
  354. }
  355. }
  356. return intersects;
  357. }
  358. }
  359. }