MonoBehaviour * Unity.Jobs = simple and fast dynamic mesh generation method.
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
| // 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; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
andrew-raphael-lukasik commentedJan 4, 2021