Skip to content

Instantly share code, notes, and snippets.

@aras-p
Created November 4, 2019 07:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aras-p/4af04b9c9c1fe1180a0a645d499ad430 to your computer and use it in GitHub Desktop.
Save aras-p/4af04b9c9c1fe1180a0a645d499ad430 to your computer and use it in GitHub Desktop.
Mesh.ReadOnlyMeshData example
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEditor;
using UnityEngine.Rendering;
using Debug = UnityEngine.Debug;
public class CreateSceneMesh : MonoBehaviour
{
[MenuItem("GameObject/Create Scene Mesh %G")]
// ----------------------------------------------------------------------------------------------------------------
// Took 0.50sec for 7259 objects, total 3921994 verts
public static void CreateMesh_01_Initial()
{
var sw = Stopwatch.StartNew();
List<Vector3> allVerts = new List<Vector3>();
List<int> allIndices = new List<int>();
var meshFilters = FindObjectsOfType<MeshFilter>();
foreach (var mf in meshFilters)
{
var go = mf.gameObject;
if (go.CompareTag("EditorOnly"))
{
DestroyImmediate(go);
continue;
}
var tr = go.transform;
var mesh = mf.sharedMesh;
var verts = mesh.vertices;
var tris = mesh.triangles;
for (var i = 0; i < verts.Length; ++i)
{
var pos = verts[i];
pos = tr.TransformPoint(pos);
verts[i] = pos;
}
var baseIdx = allVerts.Count;
for (var i = 0; i < tris.Length; ++i)
tris[i] = tris[i] + baseIdx;
allVerts.AddRange(verts);
allIndices.AddRange(tris);
}
var newMesh = new Mesh();
newMesh.name = "CombinedMesh";
newMesh.indexFormat = IndexFormat.UInt32;
newMesh.SetVertices(allVerts);
newMesh.SetTriangles(allIndices, 0);
var dur = sw.ElapsedMilliseconds;
Debug.Log($"Took {dur/1000.0:F2}sec for {meshFilters.Length} objects, total {allVerts.Count} verts");
var newGo = new GameObject("CombinedMesh", typeof(MeshFilter), typeof(MeshRenderer));
newGo.tag = "EditorOnly";
var newMf = newGo.GetComponent<MeshFilter>();
var newMr = newGo.GetComponent<MeshRenderer>();
newMf.sharedMesh = newMesh;
newMesh.RecalculateNormals();
Selection.activeObject = newGo;
}
}
using System.Diagnostics;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Profiling;
using UnityEngine;
using UnityEditor;
using UnityEngine.Rendering;
using Debug = UnityEngine.Debug;
public class CreateSceneMesh : MonoBehaviour
{
[MenuItem("GameObject/Create Scene Mesh %G")]
// ----------------------------------------------------------------------------------------------------------------
// Took 0.08sec for 7258 objects, total 3921994 verts
// - Create mesh without index validation; calculate bounds manually
public static void CreateMesh()
{
var smp1 = new ProfilerMarker("Find Meshes");
var smp2 = new ProfilerMarker("Prepare Main Thread");
var smp3 = new ProfilerMarker("Create Mesh");
var smp4 = new ProfilerMarker("Cleanup");
var sw = Stopwatch.StartNew();
smp1.Begin();
var meshFilters = FindObjectsOfType<MeshFilter>();
smp1.End();
smp2.Begin();
ProcessMeshDataJob process = new ProcessMeshDataJob();
process.meshDatas = new NativeArray<Mesh.ReadOnlyMeshData>(meshFilters.Length, Allocator.TempJob);
process.vertexStart = new NativeArray<int>(meshFilters.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
process.vertexCount = new NativeArray<int>(meshFilters.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
process.triStart = new NativeArray<int>(meshFilters.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
process.triCount = new NativeArray<int>(meshFilters.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
process.xforms = new NativeArray<float4x4>(meshFilters.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
process.bounds = new NativeArray<float3x2>(meshFilters.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
int vertexStart = 0;
int triStart = 0;
for (var index = 0; index < meshFilters.Length; ++index)
{
var mf = meshFilters[index];
var go = mf.gameObject;
if (go.CompareTag("EditorOnly"))
{
DestroyImmediate(go);
process.vertexCount[index] = 0;
process.triCount[index] = 0;
continue;
}
var mesh = mf.sharedMesh;
var vcount = mesh.vertexCount;
var icount = (int)mesh.GetIndexCount(0);
process.meshDatas[index] = mesh.AcquireReadOnlyMeshData();
process.xforms[index] = go.transform.localToWorldMatrix;
process.vertexCount[index] = vcount;
process.triCount[index] = icount;
process.vertexStart[index] = vertexStart;
process.triStart[index] = triStart;
process.bounds[index] = new float3x2(new float3(Mathf.Infinity), new float3(Mathf.NegativeInfinity));
vertexStart += vcount;
triStart += icount;
}
process.outputVerts = new NativeArray<float3>(vertexStart, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
process.outputTris = new NativeArray<int>(triStart, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
smp2.End();
process.Schedule(process.xforms.Length, 4).Complete();
smp3.Begin();
var newMesh = new Mesh();
newMesh.name = "CombinedMesh";
newMesh.SetIndexBufferParams(process.outputTris.Length, IndexFormat.UInt32);
newMesh.SetVertexBufferParams(process.outputVerts.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3, 0));
newMesh.SetVertexBufferData(process.outputVerts, 0, 0, process.outputVerts.Length, 0, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
newMesh.SetIndexBufferData(process.outputTris, 0, 0, process.outputTris.Length, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
newMesh.subMeshCount = 1;
var sm = new SubMeshDescriptor(0, process.outputTris.Length, MeshTopology.Triangles);
sm.firstVertex = 0;
sm.vertexCount = process.outputVerts.Length;
var bounds = new float3x2(new float3(Mathf.Infinity), new float3(Mathf.NegativeInfinity));
for (var i = 0; i < process.bounds.Length; ++i)
{
if (process.vertexCount[i] == 0)
continue;
bounds.c0 = math.min(bounds.c0, process.bounds[i].c0);
bounds.c1 = math.max(bounds.c1, process.bounds[i].c1);
}
sm.bounds = new Bounds(bounds.c0, bounds.c1);
newMesh.SetSubMesh(0, sm, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers);
newMesh.bounds = sm.bounds;
smp3.End();
smp4.Begin();
process.Dispose();
smp4.End();
var dur = sw.ElapsedMilliseconds;
Debug.Log($"Took {dur/1000.0:F2}sec for {meshFilters.Length} objects, total {vertexStart} verts");
var newGo = new GameObject("CombinedMesh", typeof(MeshFilter), typeof(MeshRenderer));
newGo.tag = "EditorOnly";
var newMf = newGo.GetComponent<MeshFilter>();
var newMr = newGo.GetComponent<MeshRenderer>();
newMr.material = AssetDatabase.LoadAssetAtPath<Material>("Assets/NewMat.mat");
newMf.sharedMesh = newMesh;
newMesh.RecalculateNormals();
Selection.activeObject = newGo;
}
[BurstCompile]
struct ProcessMeshDataJob : IJobParallelFor
{
[ReadOnly] public NativeArray<Mesh.ReadOnlyMeshData> meshDatas;
[ReadOnly] public NativeArray<int> vertexStart;
[ReadOnly] public NativeArray<int> vertexCount;
[ReadOnly] public NativeArray<int> triStart;
[ReadOnly] public NativeArray<int> triCount;
[ReadOnly] public NativeArray<float4x4> xforms;
public NativeArray<float3x2> bounds;
[NativeDisableParallelForRestriction] public NativeArray<float3> outputVerts;
[NativeDisableParallelForRestriction]public NativeArray<int> outputTris;
public void Execute(int index)
{
var vCount = vertexCount[index];
if (vCount == 0)
return;
var mat = xforms[index];
var vStart = vertexStart[index];
var vdata = meshDatas[index];
var verts = new NativeArray<float3>(vCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
vdata.GetVertices(verts.Reinterpret<Vector3>());
var b = bounds[index];
for (var i = 0; i < vCount; ++i)
{
var pos = verts[i];
pos = math.mul(mat, new float4(pos, 1)).xyz;
outputVerts[i+vStart] = pos;
b.c0 = math.min(b.c0, pos);
b.c1 = math.max(b.c1, pos);
}
bounds[index] = b;
verts.Dispose();
var tStart = triStart[index];
var tCount = triCount[index];
if (vdata.indexFormat == IndexFormat.UInt16)
{
var tris = vdata.GetIndexData<ushort>();
for (var i = 0; i < tCount; ++i)
{
int idx = tris[i];
outputTris[i + tStart] = vStart + idx;
}
}
else
{
var tris = vdata.GetIndexData<int>();
for (var i = 0; i < tCount; ++i)
{
int idx = tris[i];
outputTris[i + tStart] = vStart + idx;
}
}
}
public void Dispose()
{
foreach (var d in meshDatas)
d.Dispose();
meshDatas.Dispose();
vertexStart.Dispose();
vertexCount.Dispose();
triStart.Dispose();
triCount.Dispose();
xforms.Dispose();
bounds.Dispose();
outputVerts.Dispose();
outputTris.Dispose();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment