Created
September 11, 2012 19:48
-
-
Save speps/3701541 to your computer and use it in GitHub Desktop.
DebugDraw - Unity3D based debug drawing for programmers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
} | |
} |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For Unity 5 change the line that creates the material to:
and change the 2 AddColor lines to SetColor like this:
if you want alpha blending (transparency) to work, add these lines in the DebugDraw constructor:
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 :)