-
-
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.