-
-
Save speps/3701541 to your computer and use it in GitHub Desktop.
| // Remi Gillig - http://speps.fr - 2012 - Public domain | |
| using System; | |
| using System.Collections.Generic; | |
| using UnityEngine; | |
| public class DebugDraw | |
| { | |
| static Material material = new Material( | |
| @"Shader ""Custom/DebugDraw"" { | |
| Properties { | |
| _Color (""Main Color"", Color) = (1,1,1,1) | |
| } | |
| SubShader { | |
| Pass { | |
| Color [_Color] | |
| } | |
| } | |
| }"); | |
| static MeshCreator creator = new MeshCreator(); | |
| static Mesh solidSphere; | |
| static Mesh solidCube; | |
| static DebugDraw() | |
| { | |
| solidSphere = creator.CreateSphere(3); | |
| solidCube = creator.CreateCube(0); | |
| } | |
| public static void DrawSphere(Vector3 position, float radius, Color color) | |
| { | |
| Matrix4x4 mat = Matrix4x4.TRS(position, Quaternion.identity, radius * 0.5f * Vector3.one); | |
| MaterialPropertyBlock block = new MaterialPropertyBlock(); | |
| block.AddColor("_Color", color); | |
| Graphics.DrawMesh(solidSphere, mat, material, 0, null, 0, block); | |
| } | |
| public static void DrawCube(Vector3 position, Quaternion rotation, float size, Color color) | |
| { | |
| Matrix4x4 mat = Matrix4x4.TRS(position, rotation, size * Vector3.one); | |
| MaterialPropertyBlock block = new MaterialPropertyBlock(); | |
| block.AddColor("_Color", color); | |
| Graphics.DrawMesh(solidCube, mat, material, 0, null, 0, block); | |
| } | |
| #region MeshCreator | |
| public class MeshCreator | |
| { | |
| private List<Vector3> positions; | |
| private List<Vector2> uvs; | |
| private int index; | |
| private Dictionary<Int64, int> middlePointIndexCache; | |
| // add vertex to mesh, fix position to be on unit sphere, return index | |
| private int addVertex(Vector3 p, Vector2 uv) | |
| { | |
| positions.Add(p); | |
| uvs.Add(uv); | |
| return index++; | |
| } | |
| // return index of point in the middle of p1 and p2 | |
| private int getMiddlePoint(int p1, int p2) | |
| { | |
| // first check if we have it already | |
| bool firstIsSmaller = p1 < p2; | |
| Int64 smallerIndex = firstIsSmaller ? p1 : p2; | |
| Int64 greaterIndex = firstIsSmaller ? p2 : p1; | |
| Int64 key = (smallerIndex << 32) + greaterIndex; | |
| int ret; | |
| if (this.middlePointIndexCache.TryGetValue(key, out ret)) | |
| { | |
| return ret; | |
| } | |
| // not in cache, calculate it | |
| Vector3 point1 = this.positions[p1]; | |
| Vector3 point2 = this.positions[p2]; | |
| Vector3 middle = new Vector3( | |
| (point1.x + point2.x) / 2.0f, | |
| (point1.y + point2.y) / 2.0f, | |
| (point1.z + point2.z) / 2.0f); | |
| Vector2 uv1 = this.uvs[p1]; | |
| Vector2 uv2 = this.uvs[p2]; | |
| Vector2 uvmid = new Vector2( | |
| (uv1.x + uv2.x) / 2.0f, | |
| (uv1.y + uv2.y) / 2.0f); | |
| // add vertex makes sure point is on unit sphere | |
| int i = addVertex(middle, uvmid); | |
| // store it, return index | |
| this.middlePointIndexCache.Add(key, i); | |
| return i; | |
| } | |
| public Mesh CreateCube(int subdivisions) | |
| { | |
| positions = new List<Vector3>(); | |
| uvs = new List<Vector2>(); | |
| middlePointIndexCache = new Dictionary<long, int>(); | |
| index = 0; | |
| var indices = new List<int>(); | |
| // front | |
| addVertex(new Vector3(-1, -1, 1), new Vector2(1, 0)); | |
| addVertex(new Vector3(-1, 1, 1), new Vector2(1, 1)); | |
| addVertex(new Vector3(1, 1, 1), new Vector2(0, 1)); | |
| addVertex(new Vector3(1, -1, 1), new Vector2(0, 0)); | |
| indices.Add(0); indices.Add(3); indices.Add(2); | |
| indices.Add(2); indices.Add(1); indices.Add(0); | |
| // right | |
| addVertex(new Vector3(1, -1, 1), new Vector2(1, 0)); | |
| addVertex(new Vector3(1, 1, 1), new Vector2(1, 1)); | |
| addVertex(new Vector3(1, 1, -1), new Vector2(0, 1)); | |
| addVertex(new Vector3(1, -1, -1), new Vector2(0, 0)); | |
| indices.Add(4); indices.Add(7); indices.Add(6); | |
| indices.Add(6); indices.Add(5); indices.Add(4); | |
| // back | |
| addVertex(new Vector3(1, -1, -1), new Vector2(1, 0)); | |
| addVertex(new Vector3(1, 1, -1), new Vector2(1, 1)); | |
| addVertex(new Vector3(-1, 1, -1), new Vector2(0, 1)); | |
| addVertex(new Vector3(-1, -1, -1), new Vector2(0, 0)); | |
| indices.Add(8); indices.Add(11); indices.Add(10); | |
| indices.Add(10); indices.Add(9); indices.Add(8); | |
| // left | |
| addVertex(new Vector3(-1, -1, -1), new Vector2(1, 0)); | |
| addVertex(new Vector3(-1, 1, -1), new Vector2(1, 1)); | |
| addVertex(new Vector3(-1, 1, 1), new Vector2(0, 1)); | |
| addVertex(new Vector3(-1, -1, 1), new Vector2(0, 0)); | |
| indices.Add(12); indices.Add(15); indices.Add(14); | |
| indices.Add(14); indices.Add(13); indices.Add(12); | |
| // top | |
| addVertex(new Vector3(1, 1, 1), new Vector2(0, 0)); | |
| addVertex(new Vector3(1, 1, -1), new Vector2(0, 1)); | |
| addVertex(new Vector3(-1, 1, -1), new Vector2(1, 1)); | |
| addVertex(new Vector3(-1, 1, 1), new Vector2(1, 0)); | |
| indices.Add(16); indices.Add(17); indices.Add(18); | |
| indices.Add(18); indices.Add(19); indices.Add(16); | |
| // bottom | |
| addVertex(new Vector3(1, -1, 1), new Vector2(1, 0)); | |
| addVertex(new Vector3(1, -1, -1), new Vector2(1, 1)); | |
| addVertex(new Vector3(-1, -1, -1), new Vector2(0, 1)); | |
| addVertex(new Vector3(-1, -1, 1), new Vector2(0, 0)); | |
| indices.Add(21); indices.Add(20); indices.Add(23); | |
| indices.Add(23); indices.Add(22); indices.Add(21); | |
| for (int i = 0; i < subdivisions; i++) | |
| { | |
| var indices2 = new List<int>(); | |
| for (int idx = 0; idx < indices.Count; idx += 3) | |
| { | |
| // replace triangle by 4 triangles | |
| int a = getMiddlePoint(indices[idx + 0], indices[idx + 1]); | |
| int b = getMiddlePoint(indices[idx + 1], indices[idx + 2]); | |
| int c = getMiddlePoint(indices[idx + 2], indices[idx + 0]); | |
| indices2.Add(indices[idx + 0]); indices2.Add(a); indices2.Add(c); | |
| indices2.Add(indices[idx + 1]); indices2.Add(b); indices2.Add(a); | |
| indices2.Add(indices[idx + 2]); indices2.Add(c); indices2.Add(b); | |
| indices2.Add(a); indices2.Add(b); indices2.Add(c); | |
| } | |
| indices = indices2; | |
| } | |
| // done, create the mesh | |
| var mesh = new Mesh(); | |
| mesh.vertices = positions.ToArray(); | |
| mesh.triangles = indices.ToArray(); | |
| mesh.uv = uvs.ToArray(); | |
| mesh.RecalculateNormals(); | |
| var colors = new Color[mesh.vertexCount]; | |
| for (int i = 0; i < colors.Length; i++) | |
| colors[i] = new Color(1.0f, 1.0f, 1.0f); | |
| mesh.colors = colors; | |
| RecalculateTangents(mesh); | |
| return mesh; | |
| } | |
| public Mesh CreateSphere(int subdivisions) | |
| { | |
| var sphere = CreateCube(subdivisions); | |
| var vertices = new List<Vector3>(sphere.vertices); | |
| for (int i = 0; i < vertices.Count; i++) | |
| vertices[i] = vertices[i].normalized; | |
| sphere.vertices = vertices.ToArray(); | |
| sphere.RecalculateNormals(); | |
| RecalculateTangents(sphere); | |
| return sphere; | |
| } | |
| // Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. | |
| // Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html | |
| public static void RecalculateTangents(Mesh mesh) | |
| { | |
| var tan1 = new Vector3[mesh.vertexCount]; | |
| var tan2 = new Vector3[mesh.vertexCount]; | |
| for (int a = 0; a < mesh.triangles.Length; a += 3) | |
| { | |
| int i1 = mesh.triangles[a + 0]; | |
| int i2 = mesh.triangles[a + 1]; | |
| int i3 = mesh.triangles[a + 2]; | |
| Vector3 v1 = mesh.vertices[i1]; | |
| Vector3 v2 = mesh.vertices[i2]; | |
| Vector3 v3 = mesh.vertices[i3]; | |
| Vector2 w1 = mesh.uv[i1]; | |
| Vector2 w2 = mesh.uv[i2]; | |
| Vector2 w3 = mesh.uv[i3]; | |
| float x1 = v2.x - v1.x; | |
| float x2 = v3.x - v1.x; | |
| float y1 = v2.y - v1.y; | |
| float y2 = v3.y - v1.y; | |
| float z1 = v2.z - v1.z; | |
| float z2 = v3.z - v1.z; | |
| float s1 = w2.x - w1.x; | |
| float s2 = w3.x - w1.x; | |
| float t1 = w2.y - w1.y; | |
| float t2 = w3.y - w1.y; | |
| float r = 1.0F / (s1 * t2 - s2 * t1); | |
| var sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, | |
| (t2 * z1 - t1 * z2) * r); | |
| var tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, | |
| (s1 * z2 - s2 * z1) * r); | |
| tan1[i1] += sdir; | |
| tan1[i2] += sdir; | |
| tan1[i3] += sdir; | |
| tan2[i1] += tdir; | |
| tan2[i2] += tdir; | |
| tan2[i3] += tdir; | |
| } | |
| var tangents = new Vector4[mesh.vertexCount]; | |
| for (long a = 0; a < mesh.vertexCount; a++) | |
| { | |
| Vector3 n = mesh.normals[a]; | |
| Vector3 t = tan1[a]; | |
| // Gram-Schmidt orthogonalize | |
| tangents[a] = t - n * Vector3.Dot(n, t); | |
| tangents[a].Normalize(); | |
| // Calculate handedness | |
| tangents[a].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f; | |
| } | |
| mesh.tangents = tangents; | |
| } | |
| #endregion | |
| } | |
| } |
With Unity 5, color no longer appears to work. Any ideas?
For Unity 5 change the line that creates the material to:
static Material material = new Material(Shader.Find("Standard"));
and change the 2 AddColor lines to SetColor like this:
block.SetColor("_Color", color);
if you want alpha blending (transparency) to work, add these lines in the DebugDraw constructor:
material.SetFloat("_Mode", 3);
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 1);
material.DisableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = 3000;
Then use a color with an alpha value between 0 (full transparent) and 1 (opaque).
...and make sure you put a hidden game object in your scene that uses the Standard shader with a transparent material, that way Unity will include the material in the build. As explained here: https://docs.unity3d.com/Manual/MaterialsAccessingViaScript.html
@speps -- Thanks for the code, saved me a lot of time, even if I had to update for Unity 5 :)
To use this in the HDRP do the following:
Create Material:
static Material material = new Material(Shader.Find("HDRP/Lit"));
Set Color (add this line):
material.SetColor("_BaseColor", color);
Also you can't set transparency based upon Unity answer here: https://forum.unity.com/threads/hdrp-make-surface-transparent-in-runtime.663025/#post-4439452
You can get around this by passing a transparent material and then editing it however.
Hey man, thanks for your library; it's great! (clever way of generating the sphere by the way) I've added transparency support in the shader for anyone else who might like it.
I've also added support for cylinders.