Skip to content

Instantly share code, notes, and snippets.

@nezix
Created October 9, 2019 15:38
Show Gist options
  • Save nezix/f5a867175a1ced8f26d80a2239f37cc7 to your computer and use it in GitHub Desktop.
Save nezix/f5a867175a1ced8f26d80a2239f37cc7 to your computer and use it in GitHub Desktop.
Wireframify a mesh using the C# job system + Burst compiler
using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Burst;
public class WireframeGenerator {
public Mesh toWireframe(Mesh inMesh, float wireSize = 0.01f) {
Vector3[] verts = inMesh.vertices;
Vector3[] norms = inMesh.normals;
int[] tris = inMesh.triangles;
Color32[] cols = inMesh.colors32;
NativeArray<float3> InVertices = new NativeArray<float3>(verts.Length, Allocator.TempJob);
NativeArray<float3> InNormals = new NativeArray<float3>(verts.Length, Allocator.TempJob);
NativeArray<Color32> InColors = new NativeArray<Color32>(verts.Length, Allocator.TempJob);
NativeArray<int> InTriangles = new NativeArray<int>(tris.Length, Allocator.TempJob);
int totalVerts = tris.Length * 4;//Each vertex of the triangles generate 2 vertices
int totalTris = tris.Length * 2 * 3 * 2;//4 more triangles per triangle
NativeArray<float3> OutVertices = new NativeArray<float3>(totalVerts, Allocator.TempJob);
NativeArray<float3> OutNormals = new NativeArray<float3>(totalVerts, Allocator.TempJob);
NativeArray<Color32> OutColors = new NativeArray<Color32>(totalVerts, Allocator.TempJob);
NativeArray<int> OutTriangles = new NativeArray<int>(totalTris, Allocator.TempJob);
GetNativeArray(InVertices, verts);
GetNativeArray(InNormals, norms);
GetNativeArray(InTriangles, tris);
GetNativeArray(InColors, cols);
var wireJob = new computeWireframe() {
outVerts = OutVertices,
outNorms = OutNormals,
outTris = OutTriangles,
outCols = OutColors,
verts = InVertices,
norms = InNormals,
tris = InTriangles,
cols = InColors,
size = wireSize
};
var wireJobHandle = wireJob.Schedule(tris.Length / 3, 64);
Mesh newMesh = new Mesh();
newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
verts = new Vector3[OutVertices.Length];
norms = new Vector3[OutNormals.Length];
tris = new int[OutTriangles.Length];
cols = new Color32[OutColors.Length];
wireJobHandle.Complete();
SetNativeArray(verts, OutVertices);
SetNativeArray(tris, OutTriangles);
SetNativeArray(norms, OutNormals);
SetNativeArray(cols, OutColors);
newMesh.vertices = verts;
newMesh.triangles = tris;
newMesh.normals = norms;
newMesh.colors32 = cols;
InVertices.Dispose();
InNormals.Dispose();
InColors.Dispose();
InTriangles.Dispose();
OutVertices.Dispose();
OutNormals.Dispose();
OutColors.Dispose();
OutTriangles.Dispose();
return newMesh;
}
[BurstCompile]
struct computeWireframe : IJobParallelFor
{
[NativeDisableParallelForRestriction]
public NativeArray<float3> outVerts;
[NativeDisableParallelForRestriction]
public NativeArray<float3> outNorms;
[NativeDisableParallelForRestriction]
public NativeArray<int> outTris;
[NativeDisableParallelForRestriction]
public NativeArray<Color32> outCols;
[ReadOnly] public NativeArray<float3> verts;
[ReadOnly] public NativeArray<float3> norms;
[ReadOnly] public NativeArray<int> tris;
[ReadOnly] public NativeArray<Color32> cols;
[ReadOnly] public float size;
void IJobParallelFor.Execute(int index)
{
int t1 = tris[index * 3 + 0];
int t2 = tris[index * 3 + 1];
int t3 = tris[index * 3 + 2];
if(t1 == t2 || t2 == t3 || t3 == t1){
return;
}
float3 v1 = verts[t1];
float3 v2 = verts[t2];
float3 v3 = verts[t3];
float3 v1v2 = v2 - v1;
float3 v2v3 = v3 - v2;
float3 v3v1 = v1 - v3;
// float3 sidev1 = math.normalize(math.cross(v1v2, Vector3.up));
// float3 sidev2 = math.normalize(math.cross(v2v3, Vector3.up));
// float3 sidev3 = math.normalize(math.cross(v3v1, Vector3.up));
float3 sidev1 = math.normalize(math.cross(v1v2, norms[t1]));
float3 sidev2 = math.normalize(math.cross(v2v3, norms[t2]));
float3 sidev3 = math.normalize(math.cross(v3v1, norms[t3]));
int newId = index * 3 * 4;
int newIdT = index * 3 * 6 * 2;
outVerts[newId + 0] = v1 + sidev1 * size;
outVerts[newId + 1] = v1 - sidev1 * size;
outVerts[newId + 2] = v2 + sidev1 * size;
outVerts[newId + 3] = v2 - sidev1 * size;
outVerts[newId + 4] = v2 + sidev2 * size;
outVerts[newId + 5] = v2 - sidev2 * size;
outVerts[newId + 6] = v3 + sidev2 * size;
outVerts[newId + 7] = v3 - sidev2 * size;
outVerts[newId + 8] = v3 + sidev3 * size;
outVerts[newId + 9] = v3 - sidev3 * size;
outVerts[newId + 10] = v1 + sidev3 * size;
outVerts[newId + 11] = v1 - sidev3 * size;
outNorms[newId + 0] = norms[t1]; outCols[newId + 0] = cols[t1];
outNorms[newId + 1] = norms[t1]; outCols[newId + 1] = cols[t1];
outNorms[newId + 2] = norms[t2]; outCols[newId + 2] = cols[t2];
outNorms[newId + 3] = norms[t2]; outCols[newId + 3] = cols[t2];
outNorms[newId + 4] = norms[t2]; outCols[newId + 4] = cols[t2];
outNorms[newId + 5] = norms[t2]; outCols[newId + 5] = cols[t2];
outNorms[newId + 6] = norms[t3]; outCols[newId + 6] = cols[t3];
outNorms[newId + 7] = norms[t3]; outCols[newId + 7] = cols[t3];
outNorms[newId + 8] = norms[t3]; outCols[newId + 8] = cols[t3];
outNorms[newId + 9] = norms[t3]; outCols[newId + 9] = cols[t3];
outNorms[newId + 10] = norms[t1]; outCols[newId + 10] = cols[t1];
outNorms[newId + 11] = norms[t1]; outCols[newId + 11] = cols[t1];
outTris[newIdT + 0] = newId; outTris[newIdT + 18] = newId + 1;
outTris[newIdT + 1] = newId + 1; outTris[newIdT + 19] = newId + 0;
outTris[newIdT + 2] = newId + 2; outTris[newIdT + 20] = newId + 2;
outTris[newIdT + 3] = newId + 1; outTris[newIdT + 21] = newId + 3;
outTris[newIdT + 4] = newId + 3; outTris[newIdT + 22] = newId + 1;
outTris[newIdT + 5] = newId + 2; outTris[newIdT + 23] = newId + 2;
outTris[newIdT + 6] = newId + 4; outTris[newIdT + 24] = newId + 5;
outTris[newIdT + 7] = newId + 5; outTris[newIdT + 25] = newId + 4;
outTris[newIdT + 8] = newId + 6; outTris[newIdT + 26] = newId + 6;
outTris[newIdT + 9] = newId + 5; outTris[newIdT + 27] = newId + 7;
outTris[newIdT + 10] = newId + 7; outTris[newIdT + 28] = newId + 5;
outTris[newIdT + 11] = newId + 6; outTris[newIdT + 29] = newId + 6;
outTris[newIdT + 12] = newId + 8; outTris[newIdT + 30] = newId + 9;
outTris[newIdT + 13] = newId + 9; outTris[newIdT + 31] = newId + 8;
outTris[newIdT + 14] = newId + 10; outTris[newIdT + 32] = newId + 10;
outTris[newIdT + 15] = newId + 9; outTris[newIdT + 33] = newId + 11;
outTris[newIdT + 16] = newId + 11; outTris[newIdT + 34] = newId + 9;
outTris[newIdT + 17] = newId + 10; outTris[newIdT + 35] = newId + 10;
}
}
//From https://gist.github.com/LotteMakesStuff/c2f9b764b15f74d14c00ceb4214356b4
unsafe void GetNativeArray(NativeArray<float3> posNativ, Vector3[] posArray)
{
// pin the buffer in place...
fixed (void* bufferPointer = posArray)
{
// ...and use memcpy to copy the Vector3[] into a NativeArray<floar3> without casting. whould be fast!
UnsafeUtility.MemCpy(NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(posNativ),
bufferPointer, posArray.Length * (long) UnsafeUtility.SizeOf<float3>());
}
// we only have to fix the .net array in place, the NativeArray is allocated in the C++ side of the engine and
// wont move arround unexpectedly. We have a pointer to it not a reference! thats basically what fixed does,
// we create a scope where its 'safe' to get a pointer and directly manipulate the array
}
unsafe void GetNativeArray(NativeArray<int> posNativ, int[] posArray)
{
// pin the buffer in place...
fixed (void* bufferPointer = posArray)
{
// ...and use memcpy to copy the Vector3[] into a NativeArray<floar3> without casting. whould be fast!
UnsafeUtility.MemCpy(NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(posNativ),
bufferPointer, posArray.Length * (long) UnsafeUtility.SizeOf<int>());
}
// we only have to fix the .net array in place, the NativeArray is allocated in the C++ side of the engine and
// wont move arround unexpectedly. We have a pointer to it not a reference! thats basically what fixed does,
// we create a scope where its 'safe' to get a pointer and directly manipulate the array
}
unsafe void GetNativeArray(NativeArray<Color32> posNativ, Color32[] posArray)
{
// pin the buffer in place...
fixed (void* bufferPointer = posArray)
{
// ...and use memcpy to copy the Vector3[] into a NativeArray<floar3> without casting. whould be fast!
UnsafeUtility.MemCpy(NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(posNativ),
bufferPointer, posArray.Length * (long) UnsafeUtility.SizeOf<Color32>());
}
// we only have to fix the .net array in place, the NativeArray is allocated in the C++ side of the engine and
// wont move arround unexpectedly. We have a pointer to it not a reference! thats basically what fixed does,
// we create a scope where its 'safe' to get a pointer and directly manipulate the array
}
unsafe void SetNativeArray(Vector3[] posArray, NativeArray<float3> posNativ)
{
// pin the target array and get a pointer to it
fixed (void* posArrayPointer = posArray)
{
// memcopy the native array over the top
UnsafeUtility.MemCpy(posArrayPointer, NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(posNativ), posArray.Length * (long) UnsafeUtility.SizeOf<float3>());
}
}
unsafe void SetNativeArray(int[] posArray, NativeArray<int> posNativ)
{
// pin the target array and get a pointer to it
fixed (void* posArrayPointer = posArray)
{
// memcopy the native array over the top
UnsafeUtility.MemCpy(posArrayPointer, NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(posNativ), posArray.Length * (long) UnsafeUtility.SizeOf<int>());
}
}
unsafe void SetNativeArray(Color32[] posArray, NativeArray<Color32> posNativ)
{
// pin the target array and get a pointer to it
fixed (void* posArrayPointer = posArray)
{
// memcopy the native array over the top
UnsafeUtility.MemCpy(posArrayPointer, NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(posNativ), posArray.Length * (long) UnsafeUtility.SizeOf<Color32>());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment