Last active
July 25, 2019 03:26
-
-
Save nofishleft/663e43c53b35d34a0b63b527b8759503 to your computer and use it in GitHub Desktop.
Multithreaded PerlinNoise & Runtime Mesh Editing (No coroutines)
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
//Uses Update & LateUpdate to synchronize background threads | |
//Uses Update (Called after physics updates) | |
// and LateUpdate (called before rendering) | |
// see: https://docs.unity3d.com/Manual/ExecutionOrder.html | |
using System.Threading; | |
using System.Threading.Tasks; | |
using UnityEngine; | |
[RequireComponent(typeof(MeshFilter))] | |
[RequireComponent(typeof(MeshRenderer))] | |
public class PerlinMesh : MonoBehaviour | |
{ | |
public Vector3 pos; | |
public Mesh m; | |
public MeshFilter f; | |
public MeshRenderer r; | |
public int R; | |
void Start() | |
{ | |
m = new Mesh(); | |
int R1 = R + 1; | |
Vector3[] vertices = new Vector3[R1 * R1]; | |
for (int x = 0; x < R1; ++x) | |
{ | |
for (int y = 0; y < R1; ++y) | |
{ | |
vertices[x * R1 + y] = new Vector3(x - R / 2f, 0, y - R / 2f); | |
} | |
} | |
m.vertices = vertices; | |
int[] triangles = new int[6 * R * R]; | |
for (int x = 0; x < R; ++x) | |
{ | |
for (int y = 0; y < R; ++y) | |
{ | |
triangles[(x * R + y) * 6] = x * R1 + y; | |
triangles[(x * R + y) * 6 + 1] = x * R1 + y + 1; | |
triangles[(x * R + y) * 6 + 2] = (x + 1) * R1 + y; | |
triangles[(x * R + y) * 6 + 3] = (x + 1) * R1 + y; | |
triangles[(x * R + y) * 6 + 4] = x * R1 + y + 1; | |
triangles[(x * R + y) * 6 + 5] = (x + 1) * R1 + y + 1; | |
} | |
} | |
m.triangles = triangles; | |
Vector3[] normals = new Vector3[vertices.Length]; | |
for (int i = 0; i < normals.Length; ++i) | |
{ | |
normals[i] = Vector3.up; | |
} | |
m.normals = normals; | |
if (f == null) f = GetComponent<MeshFilter>() ?? gameObject.AddComponent<MeshFilter>(); | |
if (r == null) r = GetComponent<MeshRenderer>() ?? gameObject.AddComponent<MeshRenderer>(); | |
f.sharedMesh = m; | |
pos.x += Random.Range(0f, 100f); | |
pos.z += Random.Range(0f, 100f); | |
thread = new Thread(ThreadLoop); | |
thread.Start(); | |
} | |
bool _looping = true; | |
Thread thread; | |
private readonly EventWaitHandle _startHandle = new EventWaitHandle(false, EventResetMode.AutoReset); | |
private readonly EventWaitHandle _processedHandle = new EventWaitHandle(false, EventResetMode.AutoReset); | |
public void ThreadLoop() | |
{ | |
while (_looping) | |
{ | |
MeshUpdater(); | |
} | |
} | |
Vector3[] vertices; | |
//Updates the mesh vertices in the background. | |
//FYI Should never operate directly on Unity Object's in secondary threads | |
public void MeshUpdater() | |
{ | |
//Wait for start of frame/update | |
_startHandle.WaitOne(); | |
Parallel.For(0, vertices.Length, (i) => { | |
vertices[i].y = | |
Mathf.PerlinNoise(vertices[i].x + pos.x, vertices[i].z + pos.z); | |
}); | |
//Signal that we are finished updating the vertices in the secondary thread | |
_processedHandle.Set(); | |
} | |
void Update() | |
{ | |
pos.x += Time.deltaTime; | |
pos.z += Time.deltaTime; | |
//Fetch mesh vertices | |
vertices = m.vertices; | |
//Signal thread to start updating mesh vertices | |
_startHandle.Set(); | |
} | |
void LateUpdate() | |
{ | |
//Wait for mesh updating to finish | |
_processedHandle.WaitOne(); | |
//Assign vertices to mesh object | |
m.vertices = vertices; | |
m.RecalculateNormals(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment