Net.Like.Xue.Tokyo/Assets/Plugins/MeshCombineStudio/Scripts/Mesh/MeshCombineJobManager.cs

1631 lines
67 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Threading;
namespace MeshCombineStudio
{
[DefaultExecutionOrder(-94000000)]
[ExecuteInEditMode]
public class MeshCombineJobManager : MonoBehaviour
{
static public MeshCombineJobManager instance;
public JobSettings jobSettings = new JobSettings();
[Serializable]
public class JobSettings
{
public CombineJobMode combineJobMode;
public ThreadAmountMode threadAmountMode;
public int combineMeshesPerFrame = 4;
public bool useMultiThreading = true;
public bool useMainThread = true;
public int customThreadAmount = 1;
public bool showStats;
public void CopySettings(JobSettings source)
{
combineJobMode = source.combineJobMode;
threadAmountMode = source.threadAmountMode;
combineMeshesPerFrame = source.combineMeshesPerFrame;
useMultiThreading = source.useMultiThreading;
useMainThread = source.useMainThread;
customThreadAmount = source.customThreadAmount;
}
public void ReportStatus()
{
Debug.Log("---------------------");
Debug.Log("combineJobMode " + combineJobMode);
Debug.Log("threadAmountMode " + threadAmountMode);
Debug.Log("combineMeshesPerFrame " + combineMeshesPerFrame);
Debug.Log("useMultiThreading " + useMultiThreading);
Debug.Log("useMainThread " + useMainThread);
Debug.Log("customThreadAmount " + customThreadAmount);
}
}
public enum CombineJobMode { CombineAtOnce, CombinePerFrame };
public enum ThreadAmountMode { AllThreads, HalfThreads, Custom };
public enum ThreadState { isFree, isReady, isRunning, hasError };
[NonSerialized] public FastList<NewMeshObject> newMeshObjectsPool = new FastList<NewMeshObject>();
public Dictionary<Mesh, MeshCache> meshCacheDictionary = new Dictionary<Mesh, MeshCache>();
[NonSerialized] public int totalNewMeshObjects;
public Queue<MeshCombineJob> meshCombineJobs = new Queue<MeshCombineJob>();
public MeshCombineJobsThread[] meshCombineJobsThreads;
public CamGeometryCapture camGeometryCapture;
public int cores;
public int threadAmount;
public int startThreadId, endThreadId;
public bool abort;
MeshCache.SubMeshCache tempMeshCache;
Ray ray = new Ray(Vector3.zero, Vector3.down);
RaycastHit hitInfo;
static public MeshCombineJobManager CreateInstance(MeshCombiner meshCombiner, GameObject instantiatePrefab)
{
if (instance != null)
{
instance.camGeometryCapture.computeDepthToArray = meshCombiner.computeDepthToArray;
return instance;
}
GameObject go = new GameObject("MCS Job Manager");
instance = go.AddComponent<MeshCombineJobManager>();
instance.SetJobMode(meshCombiner.jobSettings);
go.AddComponent<Camera>().enabled = false;
instance.camGeometryCapture = go.AddComponent<CamGeometryCapture>();
instance.camGeometryCapture.computeDepthToArray = meshCombiner.computeDepthToArray;
instance.camGeometryCapture.Init();
return instance;
}
static public void ResetMeshCache()
{
if (instance) instance.meshCacheDictionary.Clear();
}
void Awake()
{
instance = this;
}
void OnEnable()
{
instance = this;
gameObject.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
// gameObject.hideFlags = HideFlags.None;
Init();
#if UNITY_EDITOR
if (!Application.isPlaying) UnityEditor.EditorApplication.update += MyUpdate;
#endif
}
public void Init()
{
// Debug.Log("Init");
cores = Environment.ProcessorCount;
if (meshCombineJobsThreads == null || meshCombineJobsThreads.Length != cores)
{
meshCombineJobsThreads = new MeshCombineJobsThread[cores];
for (int i = 0; i < meshCombineJobsThreads.Length; i++) meshCombineJobsThreads[i] = new MeshCombineJobsThread(i);
}
}
void OnDisable()
{
// Debug.Log("Disable");
#if UNITY_EDITOR
if (!Application.isPlaying) UnityEditor.EditorApplication.update -= MyUpdate;
#endif
}
void OnDestroy()
{
AbortJobs();
if (instance == this) instance = null;
}
void Update()
{
if (Application.isPlaying) MyUpdate();
}
void MyUpdate()
{
ExecuteJobs();
}
public void SetJobMode(JobSettings newJobSettings)
{
if (newJobSettings.combineMeshesPerFrame < 1)
{
Debug.LogError("(MeshCombineStudio) => CombineMeshesPerFrame is " + newJobSettings.combineMeshesPerFrame + " and should be 1 or higher.");
return;
}
else if (newJobSettings.combineMeshesPerFrame > 128)
{
Debug.LogError("(MeshCombineStudio) => CombineMeshesPerFrame is " + newJobSettings.combineMeshesPerFrame + " and should be 128 or lower.");
return;
}
if (newJobSettings.customThreadAmount < 1)
{
Debug.LogError("(MeshCombineStudio) => customThreadAmount is " + newJobSettings.combineMeshesPerFrame + " and should be 1 or higher.");
return;
}
else if (newJobSettings.customThreadAmount > cores) newJobSettings.customThreadAmount = cores;
jobSettings.CopySettings(newJobSettings);
if (jobSettings.useMultiThreading)
{
startThreadId = jobSettings.useMainThread ? 0 : 1;
if (jobSettings.threadAmountMode == ThreadAmountMode.Custom)
{
if (jobSettings.customThreadAmount > cores - startThreadId) jobSettings.customThreadAmount = cores - startThreadId;
threadAmount = jobSettings.customThreadAmount;
}
else
{
if (jobSettings.threadAmountMode == ThreadAmountMode.AllThreads) threadAmount = cores;
else threadAmount = cores / 2;
threadAmount -= startThreadId;
}
endThreadId = startThreadId + threadAmount;
}
else
{
startThreadId = 0;
endThreadId = 1;
threadAmount = 1;
}
int totalNewCacheMeshesNeeded;
if (jobSettings.combineJobMode == CombineJobMode.CombinePerFrame) totalNewCacheMeshesNeeded = jobSettings.combineMeshesPerFrame;
else totalNewCacheMeshesNeeded = threadAmount;
while (newMeshObjectsPool.Count > totalNewCacheMeshesNeeded)
{
newMeshObjectsPool.RemoveLast();
// Debug.LogError("Remove!!");
}
}
public void AddJob(MeshCombiner meshCombiner, MeshObjectsHolder meshObjectsHolder, Transform parent, Vector3 position)
{
FastList<MeshObject> meshObjects = meshObjectsHolder.meshObjects;
if (meshObjects.Count == 0) return;
if (meshObjects.Count < 2 && meshObjects.items[0].cachedGO.mr.sharedMaterials.Length == 1 && !meshCombiner.removeTrianglesBelowSurface && !meshCombiner.removeOverlappingTriangles && !meshCombiner.removeBackFaceTriangles)
{
if (meshCombiner.excludeSingleMeshes)
{
for (int i = 0; i < meshObjects.Count; i++)
{
MeshObject meshObject = meshObjects.items[i];
meshObject.cachedGO.excludeCombine = true;
}
meshCombiner.originalDrawCalls++;
meshCombiner.newDrawCalls++;
return;
}
else if (meshObjects.Count == 1 && meshObjectsHolder.lodParent.lodLevels.Length == 1)
{
MeshObject meshObject = meshObjects.items[0];
GameObject go = (GameObject)Instantiate(meshCombiner.instantiatePrefab, meshObject.position, meshObject.rotation, parent);
go.transform.localScale = meshObject.cachedGO.t.lossyScale;
Mesh mesh = meshObject.cachedGO.mf.sharedMesh;
go.name = "SingleMesh " + mesh.name;
var cachedComponents = go.GetComponent<CachedComponents>();
cachedComponents.mf.sharedMesh = mesh;
MeshRenderer mrS = cachedComponents.mr;
MeshRenderer mrD = meshObject.cachedGO.mr;
mrS.sharedMaterials = mrD.sharedMaterials;
mrS.lightmapScaleOffset = mrD.lightmapScaleOffset;
mrS.lightmapIndex = mrD.lightmapIndex;
meshObjectsHolder.combineCondition.WriteToGameObject(go, mrD);
if (meshCombiner.copyBakedLighting)
{
var lightmapSettings = go.AddComponent<LightmapSettings>();
lightmapSettings.mr = mrS;
lightmapSettings.lightmapIndex = mrD.lightmapIndex;
lightmapSettings.setLightmapScaleOffset = true;
lightmapSettings.lightmapScaleOffset = mrD.lightmapScaleOffset;
}
return;
}
}
int totalVertices = 0, totalTriangles = 0;
int startIndex = 0;
int length = 0;
bool firstMesh = true;
bool intersectsSurface = false;
Mesh meshOld = null;
MeshCache meshCache = null;
int maxVertices = meshCombiner.useVertexOutputLimit ? meshCombiner.vertexOutputLimit : 64000;
for (int i = 0; i < meshObjects.Count; i++)
{
MeshObject meshObject = meshObjects.items[i];
meshObject.skip = false;
meshCombiner.originalDrawCalls++;
Mesh mesh = meshObject.cachedGO.mesh;
if (mesh != meshOld)
{
if (!meshCacheDictionary.TryGetValue(mesh, out meshCache))
{
meshCache = new MeshCache(mesh);
meshCacheDictionary.Add(mesh, meshCache);
}
}
meshOld = mesh;
meshObject.meshCache = meshCache;
int vertexCount = meshCache.subMeshCache[meshObject.subMeshIndex].vertexCount;
int triangleCount = meshCache.subMeshCache[meshObject.subMeshIndex].triangleCount;
meshCombiner.originalTotalVertices += vertexCount;
meshCombiner.originalTotalTriangles += triangleCount;
if (totalVertices + vertexCount > maxVertices)
{
// Debug.Log(">AddJob StartIndex " + startIndex + " length " + length);
var meshCombineJob = new MeshCombineJob(meshCombiner, meshObjectsHolder, parent, position, startIndex, length, firstMesh, intersectsSurface);
EnqueueJob(meshCombiner, meshCombineJob);
firstMesh = intersectsSurface = false;
totalVertices = totalTriangles = length = 0;
startIndex = i;
}
if (meshCombiner.removeOverlappingTriangles)
{
meshObject.startNewTriangleIndex = totalTriangles;
meshObject.newTriangleCount = triangleCount;
}
if (meshCombiner.removeTrianglesBelowSurface)
{
int intersect = 0;
if (!meshCombiner.noColliders) intersect = MeshIntersectsSurface(meshCombiner, meshObject.cachedGO);
meshObject.startNewTriangleIndex = totalTriangles;
meshObject.newTriangleCount = triangleCount;
if (intersect == 0)
{
meshObject.intersectsSurface = intersectsSurface = true;
meshObject.skip = false;
}
else
{
meshObject.intersectsSurface = false;
if (intersect == -1) { meshObject.skip = true; ++length; continue; }
else meshObject.skip = false;
}
}
totalVertices += vertexCount;
totalTriangles += triangleCount;
++length;
}
if (totalVertices > 0)
{
// Debug.Log("*AddJob2 StartIndex " + startIndex + " length " + length);
var meshCombineJob = new MeshCombineJob(meshCombiner, meshObjectsHolder, parent, position, startIndex, length, firstMesh, intersectsSurface);
EnqueueJob(meshCombiner, meshCombineJob);
}
}
void EnqueueJob(MeshCombiner meshCombiner, MeshCombineJob meshCombineJob)
{
meshCombiner.meshCombineJobs.Add(meshCombineJob);
meshCombiner.totalMeshCombineJobs++;
meshCombineJobs.Enqueue(meshCombineJob);
}
public int MeshIntersectsSurface(MeshCombiner meshCombiner, CachedGameObject cachedGO)
{
// -1 = below, 0 = intersect, 1 = above
MeshRenderer mr = cachedGO.mr;
LayerMask terrainLayerMask = meshCombiner.surfaceLayerMask;
float maxTerrainHeight = meshCombiner.maxSurfaceHeight;
if (Physics.CheckBox(mr.bounds.center, mr.bounds.extents, Quaternion.identity, terrainLayerMask)) return 0;
Vector3 pos = mr.bounds.min;
float rayLength = meshCombiner.maxSurfaceHeight - pos.y;
ray.origin = new Vector3(pos.x, maxTerrainHeight, pos.z);
if (Physics.Raycast(ray, out hitInfo, rayLength, terrainLayerMask)) if (pos.y < hitInfo.point.y) return -1;
return 1;
}
public void AbortJobs()
{
foreach (MeshCombineJob meshCombineJob in meshCombineJobs)
{
meshCombineJob.meshCombiner.ClearMeshCombineJobs();
}
meshCombineJobs.Clear();
for (int i = 0; i < meshCombineJobsThreads.Length; i++)
{
MeshCombineJobsThread meshCombineJobsThread = meshCombineJobsThreads[i];
lock (meshCombineJobsThread.meshCombineJobs)
{
foreach (MeshCombineJob meshCombineJob in meshCombineJobsThread.meshCombineJobs)
{
meshCombineJob.meshCombiner.ClearMeshCombineJobs();
}
meshCombineJobsThread.meshCombineJobs.Clear();
}
}
// newMeshObjectsPool.Clear();
totalNewMeshObjects = 0;
abort = true;
}
public void ExecuteJobs()
{
while (meshCombineJobs.Count > 0)
{
int min = 999999;
int threadId = 0;
for (int i = startThreadId; i < endThreadId; i++)
{
int count = meshCombineJobsThreads[i].meshCombineJobs.Count;
if (count < min)
{
threadId = i;
min = count;
if (min == 0) break;
}
}
lock (meshCombineJobsThreads[threadId].meshCombineJobs)
{
MeshCombineJob meshCombineJob = meshCombineJobs.Dequeue();
if (!meshCombineJob.abort) meshCombineJobsThreads[threadId].meshCombineJobs.Enqueue(meshCombineJob);
}
}
bool jobsPending;
try
{
do
{
jobsPending = false;
if (jobSettings.useMultiThreading)
{
for (int i = 1; i < endThreadId; i++)
{
MeshCombineJobsThread meshCombineJobsThread = meshCombineJobsThreads[i];
if (meshCombineJobsThread.meshCombineJobs.Count > 0)
{
jobsPending = true;
if (meshCombineJobsThread.threadState == ThreadState.isFree)
{
if (instance.jobSettings.combineJobMode == CombineJobMode.CombinePerFrame)
{
if (instance.totalNewMeshObjects + 1 > instance.jobSettings.combineMeshesPerFrame) break;
}
meshCombineJobsThread.threadState = ThreadState.isRunning;
ThreadPool.QueueUserWorkItem(meshCombineJobsThread.ExecuteJobsThread);
}
if (meshCombineJobsThread.threadState == ThreadState.hasError) { AbortJobs(); goto exitLoop; }
}
}
for (int i = 1; i < endThreadId; i++)
{
MeshCombineJobsThread meshCombineJobsThread = meshCombineJobsThreads[i];
if (meshCombineJobsThread.threadState == ThreadState.isReady) CombineMeshesDone(meshCombineJobsThreads[i]);
}
}
if (!jobSettings.useMultiThreading || jobSettings.useMainThread)
{
MeshCombineJobsThread meshCombineJobsThread = meshCombineJobsThreads[0];
if (meshCombineJobsThread.meshCombineJobs.Count > 0)
{
jobsPending = true;
meshCombineJobsThread.threadState = ThreadState.isRunning;
meshCombineJobsThread.ExecuteJobsThread(null);
if (meshCombineJobsThread.threadState == ThreadState.isReady) CombineMeshesDone(meshCombineJobsThread);
}
}
}
while (jobSettings.combineJobMode == CombineJobMode.CombineAtOnce && jobsPending);
exitLoop:;
}
catch (Exception e)
{
Debug.LogError("(MeshCombineStudio) => " + e.ToString());
AbortJobs();
}
}
public void CombineMeshesDone(MeshCombineJobsThread meshCombineJobThread)
{
var newMeshObjectsDone = meshCombineJobThread.newMeshObjectsDone;
int count = 0;
// Debug.LogError(newMeshObjectsPool.Count);
while(newMeshObjectsDone.Count > 0)
{
NewMeshObject newMeshObject = newMeshObjectsDone.Dequeue();
MeshCombiner meshCombiner = newMeshObject.meshCombineJob.meshCombiner;
if (!abort && !newMeshObject.meshCombineJob.abort)
{
meshCombiner.meshCombineJobs.Remove(newMeshObject.meshCombineJob);
try
{
if (!newMeshObject.allSkipped) newMeshObject.CreateMesh();
if (meshCombiner.meshCombineJobs.Count == 0)
{
if (meshCombiner.addMeshColliders) meshCombiner.AddMeshColliders();
meshCombiner.ExecuteOnCombiningReady();
}
}
catch (Exception e)
{
Debug.LogError("(MeshCombineStudio) => " + e.ToString());
instance.AbortJobs();
}
}
lock (newMeshObjectsPool)
{
newMeshObjectsPool.Add(newMeshObject);
}
Interlocked.Decrement(ref totalNewMeshObjects);
if (jobSettings.combineJobMode == CombineJobMode.CombinePerFrame && ++count > jobSettings.combineMeshesPerFrame && !abort)
{
break;
}
}
// Debug.LogError(newMeshObjectsPool.Count);
meshCombineJobThread.threadState = ThreadState.isFree;
abort = false;
}
public class MeshCombineJobsThread
{
public int threadId;
public ThreadState threadState;
public Queue<MeshCombineJob> meshCombineJobs = new Queue<MeshCombineJob>();
public Queue<NewMeshObject> newMeshObjectsDone = new Queue<NewMeshObject>();
public MeshCombineJobsThread(int threadId)
{
this.threadId = threadId;
}
public void ExecuteJobsThread(object state)
{
NewMeshObject newMeshObject = null;
try
{
newMeshObject = null;
////here lies uselsees work for testing race conditions
//int j = 0;
//for (int i = 0; i < 1000; i++)
//{
// j++;
// for (int k = 0; k < 1000; k++)
// {
// j--;
// }
//}
//// end of useless work
//if (instance.jobSettings.combineJobMode == CombineJobMode.CombinePerFrame)
//{
// if (instance.totalNewMeshObjects + 1 > instance.jobSettings.combineMeshesPerFrame) break;
//}
MeshCombineJob meshCombineJob;
lock (meshCombineJobs)
{
meshCombineJob = meshCombineJobs.Dequeue();
}
Interlocked.Increment(ref instance.totalNewMeshObjects);
lock (instance.newMeshObjectsPool)
{
if (instance.newMeshObjectsPool.Count == 0)
{
newMeshObject = new NewMeshObject();
// Debug.Log("Create newMeshObject");
}
else
{
newMeshObject = instance.newMeshObjectsPool.Dequeue();
}
}
newMeshObject.newPosition = meshCombineJob.position;
newMeshObject.Combine(meshCombineJob);
lock (newMeshObjectsDone)
{
newMeshObjectsDone.Enqueue(newMeshObject);
}
threadState = ThreadState.isReady;
}
catch (Exception e)
{
if (newMeshObject != null)
{
lock (instance.newMeshObjectsPool)
{
instance.newMeshObjectsPool.Add(newMeshObject);
}
Interlocked.Decrement(ref instance.totalNewMeshObjects);
}
lock (meshCombineJobs)
{
meshCombineJobs.Clear();
}
Debug.LogError("(MeshCombineStudio) => Mesh Combine Studio thread error -> " + e.ToString());
threadState = ThreadState.hasError;
return;
}
}
}
public class MeshCombineJob
{
public MeshCombiner meshCombiner;
public MeshObjectsHolder meshObjectsHolder;
public Transform parent;
public Vector3 position;
public int startIndex;
public int endIndex;
public bool firstMesh;
public bool intersectsSurface;
public int backFaceTrianglesRemoved, trianglesRemoved;
public bool abort;
public string name;
public MeshCombineJob(MeshCombiner meshCombiner, MeshObjectsHolder meshObjectsHolder, Transform parent, Vector3 position, int startIndex, int length, bool firstMesh, bool intersectsSurface)
{
this.meshCombiner = meshCombiner;
this.meshObjectsHolder = meshObjectsHolder;
this.parent = parent;
this.position = position;
this.startIndex = startIndex;
this.firstMesh = firstMesh;
this.intersectsSurface = intersectsSurface;
endIndex = startIndex + length;
meshObjectsHolder.lodParent.jobsPending++;
name = GetHashCode().ToString();
}
}
public class NewMeshObject
{
public static FastList<Vector3> weldVertices;
public MeshCombineJob meshCombineJob;
public MeshCache.SubMeshCache newMeshCache = new MeshCache.SubMeshCache();
public bool allSkipped;
public Vector3 newPosition;
byte[] vertexIsBelow;
const byte belowSurface = 1, aboveSurface = 2;
public NewMeshObject()
{
newMeshCache.Init();
}
public void Combine(MeshCombineJob meshCombineJob)
{
BitArray backfaceRemovalExclusions = new BitArray(newMeshCache.triangles.Length, false);
this.meshCombineJob = meshCombineJob;
if (meshCombineJob.abort) return;
int startIndex = meshCombineJob.startIndex;
int endIndex = meshCombineJob.endIndex;
FastList<MeshObject> meshObjects = meshCombineJob.meshObjectsHolder.meshObjects;
newMeshCache.ResetHasBooleans();
int totalVertices = 0, totalTriangles = 0;
int meshCount = endIndex - startIndex;
MeshCombiner meshCombiner = meshCombineJob.meshCombiner;
CombineMode combineMode = meshCombiner.combineMode;
bool copyBakedLighting = meshCombiner.validCopyBakedLighting;
bool rebakeLighting = meshCombiner.validRebakeLighting;
bool regenarateLightingUvs = meshCombiner.rebakeLightingMode == MeshCombiner.RebakeLightingMode.RegenarateLightmapUvs;
int tiles = 0;
int tileX = 0;
int tileY = 0;
float tilesInv = 0;
if (rebakeLighting)
{
tiles = Mathf.CeilToInt(Mathf.Sqrt(meshCount));
tilesInv = 1.0f / tiles;
}
allSkipped = true;
for (int i = startIndex; i < endIndex; i++)
{
MeshObject meshObject = meshObjects.items[i];
int subMeshIndex = meshObject.subMeshIndex;
MeshCache.SubMeshCache subMeshCache = meshObject.meshCache.subMeshCache[subMeshIndex];
int vertexCount = subMeshCache.vertexCount;
HasArray(ref newMeshCache.hasNormals, subMeshCache.hasNormals, ref newMeshCache.normals, subMeshCache.normals, vertexCount, totalVertices);
HasArray(ref newMeshCache.hasTangents, subMeshCache.hasTangents, ref newMeshCache.tangents, subMeshCache.tangents, vertexCount, totalVertices, true, new Vector4(1, 1, 1, 1));
HasArray(ref newMeshCache.hasUv, subMeshCache.hasUv, ref newMeshCache.uv, subMeshCache.uv, vertexCount, totalVertices);
HasArray(ref newMeshCache.hasUv2, subMeshCache.hasUv2, ref newMeshCache.uv2, subMeshCache.uv2, vertexCount, totalVertices);
HasArray(ref newMeshCache.hasUv3, subMeshCache.hasUv3, ref newMeshCache.uv3, subMeshCache.uv3, vertexCount, totalVertices);
HasArray(ref newMeshCache.hasUv4, subMeshCache.hasUv4, ref newMeshCache.uv4, subMeshCache.uv4, vertexCount, totalVertices);
HasArray(ref newMeshCache.hasColors, subMeshCache.hasColors, ref newMeshCache.colors32, subMeshCache.colors32, vertexCount, totalVertices, true, new Color32(1, 1, 1, 1));
totalVertices += vertexCount;
}
totalVertices = 0;
for (int i = startIndex; i < endIndex; i++)
{
MeshObject meshObject = meshObjects.items[i];
if (meshObject.skip) continue;
bool excludeBackfaceRemoval =
meshCombiner.useExcludeBackfaceRemovalTag &&
!string.IsNullOrEmpty(meshCombiner.excludeBackfaceRemovalTag) &&
meshObject.cachedGO.go.CompareTag(meshCombiner.excludeBackfaceRemovalTag);
allSkipped = false;
MeshCache meshCache = meshObject.meshCache;
int subMeshIndex = meshObject.subMeshIndex;
MeshCache.SubMeshCache subMeshCache = meshCache.subMeshCache[subMeshIndex];
Vector3 scale = meshObject.scale;
bool flipTriangles = false;
if (scale.x < 0) flipTriangles = !flipTriangles;
if (scale.y < 0) flipTriangles = !flipTriangles;
if (scale.z < 0) flipTriangles = !flipTriangles;
int flipTangentW = 1;
if (flipTriangles) flipTangentW = -1;
Vector3[] vertices = subMeshCache.vertices;
Vector3[] normals = subMeshCache.normals;
Vector4[] tangents = subMeshCache.tangents;
Vector2[] uv = subMeshCache.uv;
Vector2[] uv2 = subMeshCache.uv2;
Vector2[] uv3 = subMeshCache.uv3;
Vector2[] uv4 = subMeshCache.uv4;
Color32[] colors32 = subMeshCache.colors32;
int[] triangles = subMeshCache.triangles;
int vertexCount = subMeshCache.vertexCount;
int[] newTriangles = newMeshCache.triangles;
Vector3[] newVertices = newMeshCache.vertices;
Vector3[] newNormals = newMeshCache.normals;
Vector4[] newTangents = newMeshCache.tangents;
Vector2[] newUv = newMeshCache.uv;
Vector2[] newUv2 = newMeshCache.uv2;
Vector2[] newUv3 = newMeshCache.uv3;
Vector2[] newUv4 = newMeshCache.uv4;
Color32[] newColors32 = newMeshCache.colors32;
bool hasNormals = subMeshCache.hasNormals;
bool hasTangents = subMeshCache.hasTangents;
Vector3 position = meshCombineJob.position;
Matrix4x4 mt = meshObject.cachedGO.mt;
Matrix4x4 mtNormals = meshObject.cachedGO.mtNormals;
if (combineMode == CombineMode.DynamicObjects)
{
Vector3 rootTScale = meshObject.cachedGO.rootTLossyScale;
rootTScale.x = 1.0f / rootTScale.x;
rootTScale.y = 1.0f / rootTScale.y;
rootTScale.z = 1.0f / rootTScale.z;
for (int j = 0; j < vertices.Length; j++)
{
int vertexIndex = j + totalVertices;
newVertices[vertexIndex] = Vector3.Scale(mt.MultiplyPoint3x4(vertices[j]) - position, rootTScale);
}
}
else
{
for (int j = 0; j < vertices.Length; j++)
{
int vertexIndex = j + totalVertices;
newVertices[vertexIndex] = mt.MultiplyPoint3x4(vertices[j]) - position;
}
}
if (hasNormals)
{
meshCombiner.originalTotalNormalChannels++;
for (int j = 0; j < vertices.Length; j++)
{
int vertexIndex = j + totalVertices;
newNormals[vertexIndex] = mtNormals.MultiplyVector(normals[j]);
}
}
// else if (newMeshCache.hasNormals) PrintMissingArrayWarning(meshCombiner, meshObject.cachedGO.go, meshCache.mesh, "normals");
if (hasTangents)
{
meshCombiner.originalTotalTangentChannels++;
for (int j = 0; j < vertices.Length; j++)
{
int vertexIndex = j + totalVertices;
newTangents[vertexIndex] = mt.MultiplyVector(tangents[j]);
newTangents[vertexIndex].w = tangents[j].w * flipTangentW;
}
}
// else if (newMeshCache.hasTangents) PrintMissingArrayWarning(meshCombiner, meshObject.cachedGO.go, meshCache.mesh, "tangents");
if (subMeshCache.hasUv)
{
meshCombiner.originalTotalUvChannels++;
Array.Copy(uv, 0, newUv, totalVertices, vertexCount);
}
// else if (newMeshCache.hasUv) PrintMissingArrayWarning(meshCombiner, meshObject.cachedGO.go, meshCache.mesh, "uv1");
if (subMeshCache.hasUv2)
{
meshCombiner.originalTotalUv2Channels++;
if (copyBakedLighting)
{
Vector4 lightmapScaleOffset = meshObject.lightmapScaleOffset;
Vector2 uvOffset = new Vector2(lightmapScaleOffset.z, lightmapScaleOffset.w);
Vector2 uvScale = new Vector2(lightmapScaleOffset.x, lightmapScaleOffset.y);
for (int j = 0; j < vertices.Length; j++)
{
int vertexIndex = j + totalVertices;
newUv2[vertexIndex] = new Vector2(uv2[j].x * uvScale.x, uv2[j].y * uvScale.y) + uvOffset;
}
}
else if (rebakeLighting)
{
if (!regenarateLightingUvs)
{
Vector2 uvOffset = new Vector2(tilesInv * tileX, tilesInv * tileY);
for (int j = 0; j < vertices.Length; j++)
{
int vertexIndex = j + totalVertices;
newUv2[vertexIndex] = (uv2[j] * tilesInv) + uvOffset;
}
}
}
else Array.Copy(uv2, 0, newUv2, totalVertices, vertexCount);
}
// else if (newMeshCache.hasUv2) PrintMissingArrayWarning(meshCombiner, meshObject.cachedGO.go, meshCache.mesh, "uv2");
if (subMeshCache.hasUv3)
{
meshCombiner.originalTotalUv3Channels++;
Array.Copy(uv3, 0, newUv3, totalVertices, vertexCount);
}
// else if (newMeshCache.hasUv3) PrintMissingArrayWarning(meshCombiner, meshObject.cachedGO.go, meshCache.mesh, "uv3");
if (subMeshCache.hasUv4)
{
meshCombiner.originalTotalUv4Channels++;
Array.Copy(uv4, 0, newUv4, totalVertices, vertexCount);
}
// else if (newMeshCache.hasUv4) PrintMissingArrayWarning(meshCombiner, meshObject.cachedGO.go, meshCache.mesh, "uv4");
if (subMeshCache.hasColors)
{
meshCombiner.originalTotalColorChannels++;
Array.Copy(colors32, 0, newColors32, totalVertices, vertexCount);
}
else if (newMeshCache.hasColors)
{
int length = totalVertices + vertexCount;
for (int j = totalVertices; j < length; j++)
{
newColors32[j] = new Color32(255, 255, 255, 255);
}
}
if (flipTriangles)
{
for (int j = 0; j < triangles.Length; j += 3)
{
newTriangles[j + totalTriangles] = triangles[j + 2] + totalVertices;
newTriangles[j + totalTriangles + 1] = triangles[j + 1] + totalVertices;
newTriangles[j + totalTriangles + 2] = triangles[j] + totalVertices;
if (excludeBackfaceRemoval)
{
backfaceRemovalExclusions[j + totalTriangles] = true;
backfaceRemovalExclusions[j + totalTriangles + 1] = true;
backfaceRemovalExclusions[j + totalTriangles + 2] = true;
}
}
}
else
{
for (int j = 0; j < triangles.Length; j++)
{
newTriangles[j + totalTriangles] = triangles[j] + totalVertices;
if (excludeBackfaceRemoval)
{
backfaceRemovalExclusions[j + totalTriangles] = true;
}
}
}
totalVertices += vertexCount;
totalTriangles += triangles.Length;
if (++tileX >= tiles) { tileX = 0; ++tileY; }
}
newMeshCache.vertexCount = totalVertices;
newMeshCache.triangleCount = totalTriangles;
if (meshCombiner.removeBackFaceTriangles) RemoveBackFaceTriangles(backfaceRemovalExclusions);
}
void PrintMissingArrayWarning(MeshCombiner meshCombiner, GameObject go, Mesh mesh, string text)
{
Debug.Log("(MeshCombineStudio) => GameObject: " + go.name + " Mesh " + mesh.name + " has missing " + text + " while the other meshes have them. Click the 'Select Meshes in Project' button to change the import settings.");
meshCombiner.selectImportSettingsMeshes.Add(mesh);
}
void HasArray<T>(ref bool hasNewArray, bool hasArray, ref T[] newArray, Array array, int vertexCount, int totalVertices, bool useDefaultValue = false, T defaultValue = default(T))
{
if (hasArray)
{
if (!hasNewArray)
{
if (newArray == null)
{
newArray = new T[65534];
if (useDefaultValue) FillArray(newArray, 0, totalVertices, defaultValue);
}
else
{
if (useDefaultValue) FillArray(newArray, 0, totalVertices, defaultValue);
else Array.Clear(newArray, 0, totalVertices);
}
}
hasNewArray = true;
}
else if (hasNewArray)
{
if (useDefaultValue) FillArray(newArray, totalVertices, vertexCount, defaultValue);
else Array.Clear(newArray, totalVertices, vertexCount);
}
}
void FillArray<T>(T[] array, int offset, int length, T value)
{
length += offset;
for (int i = offset; i < length; i++) array[i] = value;
}
public void RemoveTrianglesBelowSurface(Transform t, MeshCombineJob meshCombineJob)
{
if (vertexIsBelow == null) vertexIsBelow = new byte[65534];
Ray ray = instance.ray;
RaycastHit hitInfo = instance.hitInfo;
Vector3 pos = Vector3.zero;
int layerMask = meshCombineJob.meshCombiner.surfaceLayerMask;
float rayHeight = meshCombineJob.meshCombiner.maxSurfaceHeight;
Vector3[] newVertices = newMeshCache.vertices;
int[] newTriangles = newMeshCache.triangles;
FastList<MeshObject> meshObjects = meshCombineJob.meshObjectsHolder.meshObjects;
int startIndex = meshCombineJob.startIndex;
int endIndex = meshCombineJob.endIndex;
for (int i = startIndex; i < endIndex; i++)
{
MeshObject meshObject = meshObjects.items[i];
if (!meshObject.intersectsSurface) continue;
int startTriangleIndex = meshObject.startNewTriangleIndex;
int endTriangleIndex = meshObject.newTriangleCount + startTriangleIndex;
for (int j = startTriangleIndex; j < endTriangleIndex; j += 3)
{
bool isAboveSurface = false;
for (int k = 0; k < 3; k++)
{
int vertexIndex = newTriangles[j + k];
if (vertexIndex == -1) continue;
byte isBelow = vertexIsBelow[vertexIndex];
if (isBelow == 0)
{
pos = t.TransformPoint(newVertices[vertexIndex]);
ray.origin = new Vector3(pos.x, rayHeight, pos.z);
if (Physics.Raycast(ray, out hitInfo, rayHeight - pos.y, layerMask))
{
if (pos.y < hitInfo.point.y)
{
vertexIsBelow[vertexIndex] = isBelow = belowSurface;
}
else
{
vertexIsBelow[vertexIndex] = isBelow = aboveSurface;
break;
}
// hitPoints.Add(hitInfo.point);
}
else
{
vertexIsBelow[vertexIndex] = aboveSurface;
isAboveSurface = true;
break;
}
}
if (isBelow != belowSurface) { isAboveSurface = true; break; }
}
if (!isAboveSurface)
{
meshCombineJob.trianglesRemoved += 3;
newTriangles[j] = -1;
}
}
}
Array.Clear(vertexIsBelow, 0, newVertices.Length);
}
public void RemoveBackFaceTriangles(BitArray backfaceRemovalExclusions)
{
int[] newTriangles = newMeshCache.triangles;
Vector3[] newNormals = newMeshCache.normals;
int totalTriangles = newMeshCache.triangleCount;
MeshCombiner meshCombiner = meshCombineJob.meshCombiner;
bool useBox = (meshCombiner.backFaceTriangleMode == MeshCombiner.BackFaceTriangleMode.Box);
Bounds backFaceBounds = meshCombiner.backFaceBounds;
Vector3 backFaceBoundsMin = backFaceBounds.min;
Vector3 backFaceBoundsMax = backFaceBounds.max;
Vector3[] newVertices = newMeshCache.vertices;
Vector3 backFaceDirection;
if (meshCombiner.backFaceTriangleMode == MeshCombiner.BackFaceTriangleMode.EulerAngles)
{
backFaceDirection = Quaternion.Euler(meshCombiner.backFaceRotation) * Vector3.forward;
}
else backFaceDirection = meshCombiner.backFaceDirection;
for (int i = 0; i < totalTriangles; i += 3)
{
Vector3 normal = Vector3.zero;
Vector3 vertexPosition = Vector3.zero;
if (backfaceRemovalExclusions[i] == true)
{
continue;
}
for (int j = 0; j < 3; j++)
{
int vertexIndex = newTriangles[i + j];
vertexPosition += newVertices[vertexIndex];
normal += newNormals[vertexIndex];
}
vertexPosition /= 3;
normal /= 3;
if (useBox)
{
Vector3 outerPosition;
outerPosition.x = (normal.x > 0 ? backFaceBoundsMax.x : backFaceBoundsMin.x);
outerPosition.y = (normal.y > 0 ? backFaceBoundsMax.y : backFaceBoundsMin.y);
outerPosition.z = (normal.z > 0 ? backFaceBoundsMax.z : backFaceBoundsMin.z);
backFaceDirection = ((newPosition + vertexPosition) - outerPosition);
}
if (Vector3.Dot(backFaceDirection, normal) >= 0)
{
newTriangles[i] = -1;
meshCombineJob.backFaceTrianglesRemoved += 3;
}
}
}
public void WeldVertices(MeshCombineJob meshCombineJob)
{
if (weldVertices == null) weldVertices = new FastList<Vector3>(65534);
else weldVertices.FastClear();
Vector3[] vertices = newMeshCache.vertices;
int vertexCount = newMeshCache.vertexCount;
int[] triangleMap = new int[vertexCount];
Dictionary<Vector3, int> lookup = new Dictionary<Vector3, int>();
int vertexIndex;
if (meshCombineJob.meshCombiner.weldSnapVertices)
{
float snapSize = meshCombineJob.meshCombiner.weldSnapSize;
if (snapSize < 0.00001f) snapSize = 0.00001f;
for (int i = 0; i < vertexCount; i++)
{
Vector3 vertex = Mathw.SnapRound(vertices[i], snapSize);
if (lookup.TryGetValue(vertex, out vertexIndex)) triangleMap[i] = vertexIndex;
else
{
lookup[vertex] = triangleMap[i] = weldVertices.Count;
weldVertices.Add(vertex);
}
}
}
else
{
for (int i = 0; i < vertexCount; i++)
{
Vector3 vertex = vertices[i];
if (lookup.TryGetValue(vertex, out vertexIndex)) triangleMap[i] = vertexIndex;
else
{
lookup[vertex] = triangleMap[i] = weldVertices.Count;
weldVertices.Add(vertex);
}
}
}
int[] triangles = newMeshCache.triangles;
int triangleCount = newMeshCache.triangleCount;
for (int i = 0; i < triangleCount; i++)
{
if (triangles[i] == -1) continue;
triangles[i] = triangleMap[triangles[i]];
}
Array.Copy(weldVertices.items, newMeshCache.vertices, weldVertices.Count);
newMeshCache.vertexCount = weldVertices.Count;
}
void ArrangeTriangles()
{
int totalTriangles = newMeshCache.triangleCount;
int[] newTriangles = newMeshCache.triangles;
for (int i = 0; i < totalTriangles; i += 3)
{
if (newTriangles[i] == -1)
{
newTriangles[i] = newTriangles[totalTriangles - 3];
newTriangles[i + 1] = newTriangles[totalTriangles - 2];
newTriangles[i + 2] = newTriangles[totalTriangles - 1];
i -= 3;
totalTriangles -= 3;
}
}
newMeshCache.triangleCount = totalTriangles;
}
public void CreateMesh()
{
MeshCombiner meshCombiner = meshCombineJob.meshCombiner;
if (meshCombiner.instantiatePrefab == null)
{
Debug.LogError("(MeshCombineStudio) => Instantiate Prefab = null");
return;
}
CombineMode combineMode = meshCombiner.combineMode;
MeshObjectsHolder meshObjectsHolder = meshCombineJob.meshObjectsHolder;
if (combineMode == CombineMode.DynamicObjects)
{
meshCombineJob.parent = meshCombineJob.meshObjectsHolder.meshObjects.items[0].cachedGO.rootT;
}
else if (meshCombineJob.parent == null) meshCombineJob.parent = meshCombineJob.meshCombiner.transform;
GameObject go = (GameObject)GameObject.Instantiate(meshCombiner.instantiatePrefab, newPosition, Quaternion.identity, meshCombineJob.parent);
meshCombiner.data.combinedGameObjects.Add(go);
//List<MeshCombiner.TypeHolder> addScriptTypes = meshCombiner.addScriptTypes;
//for (int i = 0; i < addScriptTypes.Count; i++)
//{
// Type type = addScriptTypes[i].type;
// if (type != null) go.AddComponent(type);
//}
CachedComponents cachedComponents = go.GetComponent<CachedComponents>();
MeshRenderer mr = cachedComponents.mr;
MeshFilter mf = cachedComponents.mf;
string name = (combineMode == CombineMode.DynamicObjects ? "CombinedMesh" : meshObjectsHolder.mat.name);
go.name = name;
if (meshCombineJob.intersectsSurface)
{
if (meshCombiner.noColliders) instance.camGeometryCapture.RemoveTrianglesBelowSurface(go.transform, meshCombineJob, newMeshCache, ref vertexIsBelow);
else RemoveTrianglesBelowSurface(go.transform, meshCombineJob);
}
#if MCSCaves
if (meshCombiner.removeOverlappingTriangles)
{
// Voxelize.RemoveOverlap(go.transform, meshCombineJob, newMeshCache, ref vertexIsBelow);
RemoveOverlappingTris.RemoveOverlap(
go.transform,
meshCombineJob,
newMeshCache,
ref vertexIsBelow,
meshCombiner.useExcludeOverlapRemovalTag ? meshCombiner.excludeOverlapRemovalTag : null);
}
#endif
if (meshCombiner.weldVertices) WeldVertices(meshCombineJob);
if (meshCombineJob.trianglesRemoved > 0 || meshCombineJob.backFaceTrianglesRemoved > 0 || meshCombiner.weldVertices)
{
ArrangeTriangles();
if (instance.tempMeshCache == null)
{
instance.tempMeshCache = new MeshCache.SubMeshCache();
instance.tempMeshCache.Init(false);
}
instance.tempMeshCache.CopySubMeshCache(newMeshCache);
newMeshCache.RebuildVertexBuffer(instance.tempMeshCache, false);
}
int totalVertices = newMeshCache.vertexCount;
int totalTriangles = newMeshCache.triangleCount;
if (totalVertices == 0)
{
Methods.Destroy(go);
return;
}
Mesh mesh = new Mesh();
mesh.name = name;
meshCombiner.newTotalVertices += totalVertices;
meshCombiner.newTotalTriangles += totalTriangles;
MeshExtension.ApplyVertices(mesh, newMeshCache.vertices, totalVertices);
MeshExtension.ApplyTriangles(mesh, newMeshCache.triangles, totalTriangles);
if (meshCombiner.weldVertices)
{
if (newMeshCache.hasNormals && meshCombiner.weldIncludeNormals) mesh.RecalculateNormals();
// if (newMeshCache.hasTangents) mesh.RecalculateTangents();
}
else
{
if (newMeshCache.hasNormals)
{
meshCombiner.newTotalNormalChannels++;
MeshExtension.ApplyNormals(mesh, newMeshCache.normals, totalVertices);
}
if (newMeshCache.hasTangents)
{
meshCombiner.newTotalTangentChannels++;
MeshExtension.ApplyTangents(mesh, newMeshCache.tangents, totalVertices);
}
if (newMeshCache.hasUv)
{
meshCombiner.newTotalUvChannels++;
MeshExtension.ApplyUvs(mesh, newMeshCache.uv, 0, totalVertices);
}
if (newMeshCache.hasUv2)
{
meshCombiner.newTotalUv2Channels++;
MeshExtension.ApplyUvs(mesh, newMeshCache.uv2, 1, totalVertices);
}
if (newMeshCache.hasUv3)
{
meshCombiner.newTotalUv3Channels++;
MeshExtension.ApplyUvs(mesh, newMeshCache.uv3, 2, totalVertices);
}
if (newMeshCache.hasUv4)
{
meshCombiner.newTotalUv4Channels++;
MeshExtension.ApplyUvs(mesh, newMeshCache.uv4, 3, totalVertices);
}
if (newMeshCache.hasColors)
{
meshCombiner.newTotalColorChannels++;
MeshExtension.ApplyColors32(mesh, newMeshCache.colors32, totalVertices);
}
}
#if UNITY_EDITOR
if (meshCombiner.validRebakeLighting)
{
if (meshCombiner.rebakeLightingMode == MeshCombiner.RebakeLightingMode.RegenarateLightmapUvs)
{
UnityEditor.UnwrapParam unwrapParam = new UnityEditor.UnwrapParam();
UnityEditor.UnwrapParam.SetDefaults(out unwrapParam);
UnityEditor.Unwrapping.GenerateSecondaryUVSet(mesh, unwrapParam);
}
UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(mr);
so.FindProperty("m_ScaleInLightmap").floatValue = meshCombiner.scaleInLightmap;
so.ApplyModifiedProperties();
}
// UnityEditor.GameObjectUtility.SetStaticEditorFlags(go, ((UnityEditor.StaticEditorFlags)meshCombiner.outputStatic) & ~UnityEditor.StaticEditorFlags.BatchingStatic);
if (!Application.isPlaying)
{
if (meshCombiner.validCopyBakedLighting)
{
var lightmapSettings = go.AddComponent<LightmapSettings>();
lightmapSettings.mr = mr;
lightmapSettings.lightmapIndex = meshObjectsHolder.combineCondition.lightmapIndex;
}
}
#endif
if (meshCombiner.addMeshColliders)
{
bool addMeshCollider = true;
if (meshCombiner.addMeshCollidersInRange)
{
if (!meshCombiner.addMeshCollidersBounds.Contains(go.transform.position)) addMeshCollider = false;
}
if (addMeshCollider) meshCombiner.addMeshCollidersList.Add(new MeshColliderAdd(go, mesh));
}
if (meshCombiner.makeMeshesUnreadable) mesh.UploadMeshData(true);
meshCombiner.newDrawCalls++;
mr.sharedMaterial = meshObjectsHolder.mat;
mf.sharedMesh = mesh;
cachedComponents.garbageCollectMesh.mesh = mesh;
meshObjectsHolder.combineCondition.WriteToGameObject(go, mr);
if (meshObjectsHolder.newCachedGOs == null) meshObjectsHolder.newCachedGOs = new FastList<CachedGameObject>();
meshObjectsHolder.newCachedGOs.Add(new CachedGameObject(cachedComponents));
meshObjectsHolder.lodParent.lodLevels[meshObjectsHolder.lodLevel].newMeshRenderers.Add(mr);
if (--meshObjectsHolder.lodParent.jobsPending == 0 && meshObjectsHolder.lodParent.lodLevels.Length > 1) meshObjectsHolder.lodParent.AssignLODGroup(meshCombiner);
}
}
}
public class MeshCache
{
public Mesh mesh;
public SubMeshCache[] subMeshCache;
public int subMeshCount;
public MeshCache(Mesh mesh)
{
this.mesh = mesh;
subMeshCount = mesh.subMeshCount;
subMeshCache = new SubMeshCache[subMeshCount];
if (subMeshCount == 1) subMeshCache[0] = new SubMeshCache(mesh, true);
else
{
SubMeshCache tempMeshCache = new SubMeshCache(mesh, false);
for (int i = 0; i < subMeshCache.Length; i++)
{
subMeshCache[i] = new SubMeshCache(mesh, i);
subMeshCache[i].RebuildVertexBuffer(tempMeshCache, true);
}
}
}
public class SubMeshCache
{
public Vector3[] vertices, normals;
public Vector4[] tangents;
public Vector2[] uv, uv2, uv3, uv4;
public Color32[] colors32;
public int[] triangles;
public bool hasNormals, hasTangents, hasUv, hasUv2, hasUv3, hasUv4, hasColors;
public int vertexCount;
public int triangleCount;
public SubMeshCache() { }
public void CopySubMeshCache(SubMeshCache source)
{
vertexCount = source.vertexCount;
Array.Copy(source.vertices, 0, vertices, 0, vertexCount);
hasNormals = source.hasNormals; hasTangents = source.hasTangents; hasColors = source.hasColors;
hasUv = source.hasUv; hasUv2 = source.hasUv2; hasUv3 = source.hasUv3; hasUv4 = source.hasUv4;
if (source.hasNormals) CopyArray(source.normals, ref normals, vertexCount);
if (source.hasTangents) CopyArray(source.tangents, ref tangents, vertexCount);
if (source.hasUv) CopyArray(source.uv, ref uv, vertexCount);
if (source.hasUv2) CopyArray(source.uv2, ref uv2, vertexCount);
if (source.hasUv3) CopyArray(source.uv3, ref uv3, vertexCount);
if (source.hasUv4) CopyArray(source.uv4, ref uv4, vertexCount);
if (source.hasColors) CopyArray(source.colors32, ref colors32, vertexCount);
}
public void CopyArray<T>(Array sourceArray, ref T[] destinationArray, int vertexCount)
{
if (destinationArray == null) destinationArray = new T[65534];
Array.Copy(sourceArray, 0, destinationArray, 0, vertexCount);
}
public SubMeshCache(Mesh mesh, int subMeshIndex)
{
triangles = mesh.GetTriangles(subMeshIndex);
triangleCount = triangles.Length;
}
public SubMeshCache(Mesh mesh, bool assignTriangles)
{
vertices = mesh.vertices;
normals = mesh.normals;
tangents = mesh.tangents;
uv = mesh.uv;
uv2 = mesh.uv2;
uv3 = mesh.uv3;
uv4 = mesh.uv4;
colors32 = mesh.colors32;
if (assignTriangles)
{
triangles = mesh.triangles;
triangleCount = triangles.Length;
}
CheckHasArrays();
vertexCount = vertices.Length;
}
public void CheckHasArrays()
{
if (normals != null && normals.Length > 0) hasNormals = true;
if (tangents != null && tangents.Length > 0) hasTangents = true;
if (uv != null && uv.Length > 0) hasUv = true;
if (uv2 != null && uv2.Length > 0) hasUv2 = true;
if (uv3 != null && uv3.Length > 0) hasUv3 = true;
if (uv4 != null && uv4.Length > 0) hasUv4 = true;
if (colors32 != null && colors32.Length > 0) hasColors = true;
}
public void ResetHasBooleans()
{
hasNormals = hasTangents = hasUv = hasUv2 = hasUv3 = hasUv4 = hasColors = false;
}
public void Init(bool initTriangles = true)
{
vertices = new Vector3[65534];
if (initTriangles) triangles = new int[786408];
}
// TODO make it possible to do on multi thread
public void RebuildVertexBuffer(SubMeshCache sub, bool resizeArrays)
{
int[] usedVertices = new int[sub.vertices.Length];
int[] subVertexIndices = new int[usedVertices.Length];
vertexCount = 0;
for (int i = 0; i < triangleCount; i++)
{
int vertexIndex = triangles[i];
if (usedVertices[vertexIndex] == 0)
{
usedVertices[vertexIndex] = vertexCount + 1;
subVertexIndices[vertexCount] = vertexIndex;
triangles[i] = vertexCount;
++vertexCount;
}
else triangles[i] = usedVertices[vertexIndex] - 1;
}
if (resizeArrays) vertices = new Vector3[vertexCount];
hasNormals = sub.hasNormals; hasTangents = sub.hasTangents; hasColors = sub.hasColors;
hasUv = sub.hasUv; hasUv2 = sub.hasUv2; hasUv3 = sub.hasUv3; hasUv4 = sub.hasUv4;
if (resizeArrays)
{
// Debug.LogError("vertexCount " + vertexCount);
if (hasNormals) normals = new Vector3[vertexCount];
if (hasTangents) tangents = new Vector4[vertexCount];
if (hasUv) uv = new Vector2[vertexCount];
if (hasUv2) uv2 = new Vector2[vertexCount];
if (hasUv3) uv3 = new Vector2[vertexCount];
if (hasUv4) uv4 = new Vector2[vertexCount];
if (hasColors) colors32 = new Color32[vertexCount];
}
for (int i = 0; i < vertexCount; i++)
{
int vertexIndex = subVertexIndices[i];
vertices[i] = sub.vertices[vertexIndex];
if (hasNormals) normals[i] = sub.normals[vertexIndex];
if (hasTangents) tangents[i] = sub.tangents[vertexIndex];
if (hasUv) uv[i] = sub.uv[vertexIndex];
if (hasUv2) uv2[i] = sub.uv2[vertexIndex];
if (hasUv3) uv3[i] = sub.uv3[vertexIndex];
if (hasUv4) uv4[i] = sub.uv4[vertexIndex];
if (hasColors) colors32[i] = sub.colors32[vertexIndex];
}
}
}
}
static public class MeshExtensionAlloc
{
static public void ApplyVertices(Mesh mesh, Vector3[] vertices, int length)
{
Vector3[] newVertices = new Vector3[length];
Array.Copy(vertices, newVertices, length);
mesh.vertices = newVertices;
}
static public void ApplyNormals(Mesh mesh, Vector3[] normals, int length)
{
Vector3[] newNormals = new Vector3[length];
Array.Copy(normals, newNormals, length);
mesh.normals = newNormals;
}
static public void ApplyTangents(Mesh mesh, Vector4[] tangents, int length)
{
Vector4[] newTangents = new Vector4[length];
Array.Copy(tangents, newTangents, length);
mesh.tangents = newTangents;
}
static public void ApplyUvs(Mesh mesh, int channel, Vector2[] uvs, int length)
{
Vector2[] newUvs = new Vector2[length];
Array.Copy(uvs, newUvs, length);
if (channel == 0) mesh.uv = newUvs;
else if (channel == 1) mesh.uv2 = newUvs;
else if (channel == 2) mesh.uv3 = newUvs;
else mesh.uv4 = newUvs;
}
static public void ApplyColors32(Mesh mesh, Color32[] colors, int length)
{
Color32[] newColors = new Color32[length];
Array.Copy(colors, newColors, length);
mesh.colors32 = newColors;
}
static public void ApplyTriangles(Mesh mesh, int[] triangles, int length)
{
int[] newTriangles = new int[length];
Array.Copy(triangles, newTriangles, length);
mesh.triangles = newTriangles;
}
}
}