Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
MonoBehaviour * Unity.Jobs = simple and fast dynamic mesh generation method.
// src: https://gist.github.com/andrew-raphael-lukasik/c83f1d9b3a356f36e0c66ddffbae3271
using System.Runtime.CompilerServices;
using UnityEngine;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Jobs;
[RequireComponent( typeof(MeshFilter) , typeof(MeshRenderer) )]
public class NoiseFieldMesh : MonoBehaviour
{
[SerializeField][Range(2,256)] int _size = 10;
[SerializeField] bool _dirty = true;
[SerializeField] bool _everyFrame = true;
bool _scheduled = false;
Mesh _mesh;
NativeArray<float3> _vertices, _normals;
NativeArray<int> _indices;
NativeArray<AABB> _aabb;
JobHandle _dependency;
void OnEnable ()
{
_mesh = new Mesh();
_mesh.MarkDynamic();
GetComponent<MeshFilter>().mesh = _mesh;
GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
_aabb = new NativeArray<AABB>( 1 , Allocator.Persistent );
}
void OnDisable ()
{
Destroy( _mesh );
_dependency.Complete();
if( _vertices.IsCreated ) _vertices.Dispose();
if( _indices.IsCreated ) _indices.Dispose();
if( _normals.IsCreated ) _normals.Dispose();
if( _aabb.IsCreated ) _aabb.Dispose();
}
void Update ()
{
if( _scheduled )
{
_dependency.Complete();
_mesh.SetVertices( _vertices );
_mesh.SetIndices( _indices , MeshTopology.Triangles , 0 );
_mesh.SetNormals( _normals );
_mesh.bounds = _aabb[0].ToBounds();
_scheduled = false;
}
if( _dirty || _everyFrame )
{
float2 noiseOrigin = new float2{ x=Time.time , y=math.sin(Time.time) };// seed
int numVertices = _size*_size;
if( _vertices.Length!=numVertices )
{
if( _vertices.IsCreated ) _vertices.Dispose();
_vertices = new NativeArray<float3>( numVertices , Allocator.Persistent , NativeArrayOptions.UninitializedMemory );
}
var verticesJobHandle = new VerticesJob{
Width = _size ,
NoiseOrigin = noiseOrigin ,
Vertices = _vertices
}.Schedule( numVertices , 32 );
var boundsJobHandle = new BoundsJob{
Vertices = _vertices ,
Aabb = _aabb
}.Schedule( verticesJobHandle );
int numTriangles = (_size-1)*(_size-1)*2;
int numIndices = numTriangles*3;
if( _indices.Length!=numIndices )
{
if( _indices.IsCreated ) _indices.Dispose();
_indices = new NativeArray<int>( numIndices , Allocator.Persistent , NativeArrayOptions.UninitializedMemory );
}
var trianglesJobHandle = new TrianglesJob{
Width = _size ,
Indices = _indices
}.Schedule();
int numNormals = numVertices;
if( _normals.Length!=numNormals )
{
if( _normals.IsCreated ) _normals.Dispose();
_normals = new NativeArray<float3>( numNormals , Allocator.Persistent , NativeArrayOptions.ClearMemory );
}
var normalsJobHandle = new NormalsJob{
Width = _size ,
Vertices = _vertices ,
Indices = _indices ,
Normals = _normals
}.Schedule( numTriangles , 32 , JobHandle.CombineDependencies(verticesJobHandle,trianglesJobHandle) );
var normalizeNormalsJobHandle = new NormalizeJob{
Vectors = _normals
}.Schedule( _normals.Length , 32 , normalsJobHandle );
_dependency = JobHandle.CombineDependencies( boundsJobHandle , normalizeNormalsJobHandle );
_dirty = false;
_scheduled = true;
}
}
[Unity.Burst.BurstCompile]
public struct VerticesJob : IJobParallelFor
{
public int Width;
public float2 NoiseOrigin;
[WriteOnly] public NativeArray<float3> Vertices;
void IJobParallelFor.Execute( int index )
{
Vertices[index] = Vertex( Index1dTo2d(index,Width) , NoiseOrigin );
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Height ( float2 xy ) => noise.snoise( xy );
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 Vertex ( int2 xy , float2 noiseOrigin )
{
float height = VerticesJob.Height( noiseOrigin + xy );
return new float3{ x=xy.x , y=height , z=xy.y };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int2 Index1dTo2d ( int index , int width ) => new int2{ x=index%width , y=index/width };
}
[Unity.Burst.BurstCompile]
public struct TrianglesJob : IJob
{
public int Width;
[NativeDisableParallelForRestriction][WriteOnly] public NativeArray<int> Indices;
void IJob.Execute ()
{
int width = Width, height = Width;
int numQuadRows = width - 1, numQuadCols = height - 1;
for( int Y=0, triangleIndex=0 ; Y<numQuadRows ; Y++ )
for( int X=0 ; X<numQuadCols ; X++)
{
int topLeft = ((Y + 1) * width) + X;
int topRight = ((Y + 1) * width) + (X + 1);
int bottomLeft = (Y * width) + X;
int bottomRight = (Y * width) + (X + 1);
Indices[triangleIndex++] = topLeft;
Indices[triangleIndex++] = topRight;
Indices[triangleIndex++] = bottomLeft;
Indices[triangleIndex++] = topRight;
Indices[triangleIndex++] = bottomRight;
Indices[triangleIndex++] = bottomLeft;
}
}
}
[Unity.Burst.BurstCompile]
public struct NormalsJob : IJobParallelFor
{
public int Width;
[ReadOnly] public NativeArray<float3> Vertices;
[ReadOnly] public NativeArray<int> Indices;
[NativeDisableParallelForRestriction] public NativeArray<float3> Normals;
void IJobParallelFor.Execute( int triangle )
{
int index = triangle * 3;
int ia = Indices[ index ];
int ib = Indices[ index+1 ];
int ic = Indices[ index+2 ];
float3 triangleNormal = TriangleNormal( a:ia , b:ib , c:ic );
Normals[ ia ] += triangleNormal;
Normals[ ib ] += triangleNormal;
Normals[ ic ] += triangleNormal;
}
float3 TriangleNormal ( int a , int b , int c )
{
float3 va = Vertices[a];
float3 vb = Vertices[b];
float3 vc = Vertices[c];
float3 vab = math.normalize( vb - va );
float3 vac = math.normalize( vc - va );
return math.cross( vab , vac );
}
}
[Unity.Burst.BurstCompile]
public struct NormalizeJob : IJobParallelFor
{
public NativeArray<float3> Vectors;
void IJobParallelFor.Execute ( int index )
{
Vectors[index] = math.normalize( Vectors[index] );
}
}
[Unity.Burst.BurstCompile]
public struct BoundsJob : IJob
{
[ReadOnly] public NativeArray<float3> Vertices;
[WriteOnly] public NativeArray<AABB> Aabb;
void IJob.Execute ()
{
MinMaxAABB aabb = MinMaxAABB.Empty;
int numVertices = Vertices.Length;
for( int i=0 ; i<numVertices ; i++ )
aabb.Encapsulate( Vertices[i] );
Aabb[0] = aabb;
}
}
}
@andrew-raphael-lukasik
Copy link
Author

andrew-raphael-lukasik commented Jan 4, 2021

preview

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment