Skip to content

Instantly share code, notes, and snippets.

@nofishleft
Last active July 26, 2019 06:57
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/057170cba135ae9d71d8ce99face1306 to your computer and use it in GitHub Desktop.
Save nofishleft/057170cba135ae9d71d8ce99face1306 to your computer and use it in GitHub Desktop.
Multithreaded PerlinNoise & Runtime Mesh Editing Alternative
//Uses Coroutines & LateUpdate to do processing over a larger percentage of the game loop
//Uses WaitForEndOfFrame (called after rending before next frame) instead of Update
// and LateUpdate (called before rendering)
// see: https://docs.unity3d.com/Manual/ExecutionOrder.html
using System.Collections;
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);
Debug.Log("Starting Coroutine");
StartCoroutine(nameof(EndOfRendering));
Debug.Log("Starting Thread");
thread = new Thread(ThreadLoop);
thread.Start();
}
bool _looping = true;
Thread thread;
//We start processing at the end of rendering of the first frame
//So we initialize true to stop main thread from freezing during the first frame
private readonly EventWaitHandle _processedHandle = new EventWaitHandle(true, EventResetMode.AutoReset);
private readonly EventWaitHandle _startHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
public void ThreadLoop()
{
while (_looping)
{
MeshUpdater();
}
}
IEnumerator EndOfRendering()
{
while (_looping)
{
//Start
pos.x += Time.deltaTime;
pos.z += Time.deltaTime;
//Fetch mesh vertices
vertices = m.vertices;
//Signal thread to start updating mesh vertices
Debug.Log("Set start handle");
_startHandle.Set();
//Waits until Rendering of current frame is finished
// (before physics and update of next frame)
Debug.Log("Waiting for next frame");
yield return new WaitForEndOfFrame();
}
}
Vector3[] vertices;
/// <summary>
/// Updates the mesh vertices in the background.
/// </summary>
///
/// FYI: Should never operate directly on Unity Object's in secondary threads
public void MeshUpdater()
{
Debug.Log("Waiting for start of frame signal");
//Wait for start of frame/update
_startHandle.WaitOne();
Debug.Log("Received start of frame signal");
Parallel.For(0, vertices.Length, (i) => {
vertices[i].y =
Mathf.PerlinNoise(vertices[i].x + pos.x, vertices[i].z + pos.z);
});
Debug.Log("Set processing handle");
//Signal that we are finished updating the vertices in the secondary thread
_processedHandle.Set();
}
//Called just before rendering
void LateUpdate()
{
//Wait for mesh updating to finish
Debug.Log("Waiting for processing signal");
_processedHandle.WaitOne();
Debug.Log("Received processing signal");
//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