Skip to content

Instantly share code, notes, and snippets.

@nofishleft
Last active July 25, 2019 03:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nofishleft/663e43c53b35d34a0b63b527b8759503 to your computer and use it in GitHub Desktop.
Save nofishleft/663e43c53b35d34a0b63b527b8759503 to your computer and use it in GitHub Desktop.
Multithreaded PerlinNoise & Runtime Mesh Editing (No coroutines)
//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