Files
PrinceOfGlory/Assets/Third Party/SimpleLOD/Extensions/GameObjectExtensions.cs
kridoo 6e91a0c7f0 111
2025-09-15 17:32:08 +08:00

1330 lines
54 KiB
C#

/* SimpleLOD 1.5a */
/* By Orbcreation BV */
/* Richard Knol */
/* March 18, 2015 */
/* Note: if you also use other packages by Orbcreation, */
/* you may end up with multiple copies of this file. */
/* In that case, better delete/merge those files into 1. */
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
#if UNITY_WP8
#elif UNITY_WP_8_1
#elif UNITY_WSA
#elif UNITY_WSA_8_0
#elif UNITY_WSA_8_1
#elif UNITY_WINRT
#elif UNITY_WINRT_8_0
#elif UNITY_WINRT_8_1
#elif NETFX_CORE
#else
using System.Threading;
#endif
using LOD = UnityEngine.LOD; // This is needed when SimpleLOD is used in combination with SmartLOD
namespace OrbCreationExtensions
{
public static class GameObjectExtensions
{
public static Bounds GetWorldBounds(this GameObject go) {
if(go.transform == null) return new Bounds();
Bounds goBounds = new Bounds(go.transform.position, Vector3.zero);
Renderer[] renderers = go.GetComponentsInChildren<Renderer>(true);
if(renderers != null && renderers.Length > 0) goBounds = renderers[0].bounds;
foreach (Renderer r in renderers) {
Bounds bounds = r.bounds;
goBounds.Encapsulate(bounds);
}
return goBounds;
}
// Bounds are always in world coordinates
// Bounds always gives a rectangle that is straight with global x,y,z axis.
// So when you rotate a cude with 45 degrees, its bounds will get bigger
// localScale is incorporated, so when you increase the scale, the bounds will increase too
// But watch out:
// When you move or alter a GameObject that is set to inactive (activeSelf == false), the bounds are not updated!!!
public static Vector3[] GetBoundsCorners(this Bounds bounds) {
Vector3[] corners = new Vector3[8];
for(int i=0;i<8;i++) {
corners[i] = bounds.min;
if((i & 1) > 0) corners[i].x += bounds.size.x;
if((i & 2) > 0) corners[i].y += bounds.size.y;
if((i & 4) > 0) corners[i].z += bounds.size.z;
}
return corners;
}
public static Vector3[] GetBoundsCenterAndCorners(this Bounds bounds) {
Vector3[] corners = new Vector3[9];
corners[0] = bounds.center;
for(int i=1;i<9;i++) {
corners[i] = bounds.min;
if((i & 1) > 0) corners[i].x += bounds.size.x;
if((i & 2) > 0) corners[i].y += bounds.size.y;
if((i & 4) > 0) corners[i].z += bounds.size.z;
}
return corners;
}
public static Vector3[] GetWorldBoundsCorners(this GameObject go) {
return GetBoundsCorners(GetWorldBounds(go));
}
// These are in world coordinates
public static Vector3[] GetWorldBoundsCenterAndCorners(this GameObject go) {
return GetBoundsCenterAndCorners(GetWorldBounds(go));
}
public static float GetModelComplexity(this GameObject go) {
float complexity = 0f;
MeshFilter[] meshFilters = go.GetComponentsInChildren<MeshFilter>(true);
foreach (MeshFilter mf in meshFilters) {
Mesh mesh = mf.sharedMesh;
float multiplier = 1f;
for(int i=0;i<mesh.subMeshCount;i++) {
complexity += multiplier * mesh.GetTriangles(i).Length / 3f / 65536f;
// nog iets doen met de shader
multiplier *= 1.1f; // more materials, higher weight
}
}
return complexity; // 1 model with 1 submesh of 64K vertices = 1.0f
}
public static string GetModelInfoString(this GameObject go) {
string infoString = "";
int meshCount = 0;
int subMeshCount = 0;
int vertexCount = 0;
int triangleCount = 0;
Bounds bounds = GetWorldBounds(go);
MeshFilter[] meshFilters = go.GetComponentsInChildren<MeshFilter>(true);
foreach (MeshFilter mf in meshFilters) {
Mesh mesh = mf.sharedMesh;
meshCount++;
subMeshCount += mesh.subMeshCount;
vertexCount += mesh.vertices.Length;
triangleCount += mesh.triangles.Length / 3;
}
infoString = infoString + meshCount + " meshes\n";
infoString = infoString + subMeshCount + " submeshes\n";
infoString = infoString + vertexCount + " vertices\n";
infoString = infoString + triangleCount + " triangles\n";
infoString = infoString + bounds.size + " meters";
return infoString;
}
public static GameObject TopParent(this GameObject go) {
Transform parentTransform = go.transform.parent;
if(parentTransform == null) return go;
return TopParent(parentTransform.gameObject);
}
public static GameObject FindParentWithName(this GameObject go, string parentName) {
if(go.name == parentName) return go;
Transform parentTransform = go.transform.parent;
if(parentTransform == null) return null;
return FindParentWithName(parentTransform.gameObject, parentName);
}
public static GameObject FindMutualParent(this GameObject go1, GameObject go2) {
if(go2 == null || go1 == go2) return null;
Transform checkTrans = go2.transform;
while(checkTrans != null) {
if(go1 == checkTrans.gameObject) return go1;
checkTrans = checkTrans.parent;
}
Transform parent1 = go1.transform.parent;
if(parent1 == null) return null;
return FindMutualParent(parent1.gameObject, go2);
}
public static GameObject FindFirstChildWithName(this GameObject go, string childName) {
if(go.name == childName) return go;
foreach(Transform child in go.transform) {
GameObject childWithName = FindFirstChildWithName(child.gameObject, childName);
if(childWithName != null) return childWithName;
}
return null;
}
public static bool IsChildWithNameUnique(this GameObject go, string childName) {
int total = 0;
CountChildrenWithName(go, childName, ref total);
return (total <= 1);
}
public static void CountChildrenWithName(this GameObject go, string childName, ref int total) {
if(go.name == childName) total++;
foreach(Transform child in go.transform) {
CountChildrenWithName(child.gameObject, childName, ref total);
}
}
public static void DestroyChildren(this GameObject go, bool disabledOnly) {
List<Transform> children = new List<Transform>();
foreach(Transform child in go.transform) {
if((!child.gameObject.activeSelf) || (!disabledOnly)) children.Add(child);
}
// we do it this way because you cant alter the array in a foreach loop
for(int i=children.Count-1;i>=0;i--) {
#if UNITY_4_3
children[i].parent = null;
#elif UNITY_4_4
children[i].parent = null;
#elif UNITY_4_5
children[i].parent = null;
#else
children[i].SetParent(null);
#endif
GameObject.Destroy(children[i].gameObject);
}
}
public static T GetFirstComponentInParents<T>(this GameObject go) where T : Component {
T component = go.GetComponent<T>();
if(component != null) return component;
if(go.transform.parent != null && go.transform.parent.gameObject != go) return GetFirstComponentInParents<T>(go.transform.parent.gameObject);
return null;
}
public static T GetFirstComponentInChildren<T>(this GameObject go) where T : Component {
T[] components = go.GetComponentsInChildren<T>();
if(components != null && components.Length>0) return components[0];
return null;
}
public static Mesh[] GetMeshes(this GameObject aGo) {
return GetMeshes(aGo, true);
}
public static Mesh[] GetMeshes(this GameObject aGo, bool includeDisabled) {
MeshFilter[] mfs = aGo.GetComponentsInChildren<MeshFilter>(includeDisabled);
SkinnedMeshRenderer[] smr = aGo.GetComponentsInChildren<SkinnedMeshRenderer>(includeDisabled);
int newLen = 0;
if(mfs != null) newLen += mfs.Length;
if(smr != null) newLen += smr.Length;
if(newLen == 0) return null;
Mesh[] meshes = new Mesh[newLen];
int i = 0;
for(;mfs != null && i<mfs.Length;i++) {
meshes[i] = mfs[i].sharedMesh;
}
int offset = i;
for(i=0;smr != null && i<smr.Length;i++) {
meshes[i+offset] = smr[i].sharedMesh;
}
return meshes;
}
public static int GetTotalVertexCount(this GameObject aGo) {
MeshFilter[] mfs = aGo.GetComponentsInChildren<MeshFilter>(false);
SkinnedMeshRenderer[] smr = aGo.GetComponentsInChildren<SkinnedMeshRenderer>(false);
int totalVertexCount = 0;
for(int i=0;mfs != null && i<mfs.Length;i++) {
Mesh mesh = mfs[i].sharedMesh;
if(mesh != null) totalVertexCount += mesh.vertexCount;
}
for(int i=0;smr != null && i<smr.Length;i++) {
Mesh mesh = smr[i].sharedMesh;
if(mesh != null) totalVertexCount += mesh.vertexCount;
}
return totalVertexCount;
}
public static Mesh Get1stSharedMesh(this GameObject aGo) {
MeshFilter[] mfs = aGo.GetComponentsInChildren<MeshFilter>(false);
for(int i=0;mfs != null && i<mfs.Length;i++) {
if(mfs[i].sharedMesh != null) return mfs[i].sharedMesh;
}
SkinnedMeshRenderer[] smrs = aGo.GetComponentsInChildren<SkinnedMeshRenderer>(false);
for(int i=0;smrs != null && i<smrs.Length;i++) {
if(smrs[i].sharedMesh != null) return smrs[i].sharedMesh;
}
return null;
}
public static void SetMeshes(this GameObject aGo, Mesh[] meshes) {
SetMeshes(aGo, meshes, true, 0);
}
public static void SetMeshes(this GameObject aGo, Mesh[] meshes, int lodLevel) {
SetMeshes(aGo, meshes, true, lodLevel);
}
public static void SetMeshes(this GameObject aGo, Mesh[] meshes, bool includeDisabled, int lodLevel) {
MeshFilter[] mfs = aGo.GetComponentsInChildren<MeshFilter>(includeDisabled);
SkinnedMeshRenderer[] smr = aGo.GetComponentsInChildren<SkinnedMeshRenderer>(includeDisabled);
int meshLen = 0;
if(mfs != null) meshLen += mfs.Length;
if(smr != null) meshLen += smr.Length;
if(meshLen == 0) return;
int i = 0;
for(;mfs != null && i<mfs.Length;i++) {
LODSwitcher lodSwitcher = mfs[i].gameObject.GetComponent<LODSwitcher>();
if(meshes != null && meshes.Length>i) {
if(lodLevel == 0) mfs[i].sharedMesh = meshes[i];
if(lodSwitcher == null && lodLevel > 0) {
lodSwitcher = mfs[i].gameObject.AddComponent<LODSwitcher>();
lodSwitcher.SetMesh(mfs[i].sharedMesh, 0);
}
if(lodSwitcher != null) lodSwitcher.SetMesh(meshes[i], lodLevel);
} else {
if(lodSwitcher != null) lodSwitcher.SetMesh(null, lodLevel);
if(lodLevel == 0) mfs[i].sharedMesh = null;
}
}
int offset = i;
for(i=0;smr != null && i<smr.Length;i++) {
LODSwitcher lodSwitcher = smr[i].gameObject.GetComponent<LODSwitcher>();
if(meshes != null && meshes.Length>i+offset) {
if(lodLevel == 0) smr[i].sharedMesh = meshes[i + offset];
if(lodSwitcher == null && lodLevel > 0) {
lodSwitcher = smr[i].gameObject.AddComponent<LODSwitcher>();
lodSwitcher.SetMesh(smr[i].sharedMesh, 0);
}
if(lodSwitcher != null) lodSwitcher.SetMesh(meshes[i + offset], lodLevel);
} else {
if(lodSwitcher != null) lodSwitcher.SetMesh(null, lodLevel);
if(lodLevel == 0) smr[i].sharedMesh = null;
}
}
}
public static Material[] GetMaterials(this GameObject aGo, bool includeDisabled) {
List<Material> materials = new List<Material>();
MeshRenderer[] mrs = aGo.GetComponentsInChildren<MeshRenderer>(includeDisabled);
for(int i=0;i<mrs.Length;i++) {
materials.AddRange(mrs[i].sharedMaterials);
}
SkinnedMeshRenderer[] smr = aGo.GetComponentsInChildren<SkinnedMeshRenderer>(includeDisabled);
for(int i=0;i<smr.Length;i++) {
materials.AddRange(smr[i].sharedMaterials);
}
return materials.ToArray();
}
// Unity Mesh.CombineMeshes function sucks. It makes 1 submesh for each mesh
// but if the meshes already have submeshes, this info gets lost
// My function combines all submeshes of all child gameObjects and merges them per unique material
public static Mesh[] CombineMeshes(this GameObject aGO) {
return CombineMeshes(aGO, new string[0] {});
}
public static Mesh[] CombineMeshes(this GameObject aGO, string[] skipSubmeshNames, bool makeNewGameObjectWhenRendererPresent = true) {
List<Mesh> meshes = new List<Mesh>();
MeshRenderer[] meshRenderers = aGO.GetComponentsInChildren<MeshRenderer>(false);
SkinnedMeshRenderer[] skinnedMeshRenderers = aGO.GetComponentsInChildren<SkinnedMeshRenderer>(false);
int totalVertexCount = 0;
int totalMeshCount = 0;
int totalMeshCountAnyLightmapIdx = 0;
int lightmapIndex = -999;
bool makeNewGameObject = false;
if(aGO.GetComponent<SkinnedMeshRenderer>() != null || aGO.GetComponent<MeshRenderer>() != null) makeNewGameObject = makeNewGameObjectWhenRendererPresent;
if(skinnedMeshRenderers != null && skinnedMeshRenderers.Length > 0) {
foreach(SkinnedMeshRenderer skinnedMeshRenderer in skinnedMeshRenderers) {
if(skinnedMeshRenderer.sharedMesh != null) {
totalVertexCount += skinnedMeshRenderer.sharedMesh.vertexCount;
totalMeshCount++;
totalMeshCountAnyLightmapIdx++;
}
}
}
if(meshRenderers != null && meshRenderers.Length > 0) {
foreach(MeshRenderer meshRenderer in meshRenderers) {
MeshFilter filter = meshRenderer.gameObject.GetComponent<MeshFilter>();
if(filter != null && filter.sharedMesh != null) {
if(lightmapIndex == -999 && meshRenderer.lightmapIndex >= 0 && meshRenderer.lightmapIndex <= 253) {
lightmapIndex = meshRenderer.lightmapIndex;
}
if(lightmapIndex<0 || meshRenderer.lightmapIndex<0 || meshRenderer.lightmapIndex>253 || lightmapIndex == meshRenderer.lightmapIndex) {
totalVertexCount += filter.sharedMesh.vertexCount;
totalMeshCount++;
}
}
totalMeshCountAnyLightmapIdx++;
}
}
if(totalMeshCount == 0) {
throw new ApplicationException("No meshes found in children. There's nothing to combine.");
// Debug.LogError("No meshes found in children. There's nothing to combine.");
// return null;
}
if(makeNewGameObject) {
GameObject newGO = new GameObject();
string newName = aGO.name + "_Merged";
string newUniqueName = newName;
int uniqueCounter = 0;
while(GameObject.Find(newUniqueName) != null) {
newUniqueName = newName + "_" + uniqueCounter;
uniqueCounter++;
}
newGO.name = newUniqueName;
#if UNITY_4_3
newGO.transform.parent = aGO.transform.parent;
#elif UNITY_4_4
newGO.transform.parent = aGO.transform.parent;
#elif UNITY_4_5
newGO.transform.parent = aGO.transform.parent;
#else
newGO.transform.SetParent(aGO.transform.parent);
#endif
newGO.transform.localPosition = aGO.transform.localPosition;
newGO.transform.localRotation = aGO.transform.localRotation;
newGO.transform.localScale = aGO.transform.localScale;
aGO = newGO;
}
int partNr = 1;
int prevMeshOffset = -1;
int meshOffset = 0;
while(meshOffset < totalMeshCount) {
if(prevMeshOffset == meshOffset) break;
prevMeshOffset = meshOffset;
GameObject topGO = aGO;
if(totalVertexCount > 65534) {
topGO = new GameObject();
topGO.name = "Merged part "+(partNr++);
#if UNITY_4_3
topGO.transform.parent = aGO.transform;
#elif UNITY_4_4
topGO.transform.parent = aGO.transform;
#elif UNITY_4_5
topGO.transform.parent = aGO.transform;
#else
topGO.transform.SetParent(aGO.transform);
#endif
topGO.transform.localPosition = Vector3.zero;
topGO.transform.localRotation = Quaternion.identity;
topGO.transform.localScale = Vector3.one;
}
Mesh mesh = null;
List<Vector3> vertices = new List<Vector3>();
List<Vector3> normals = new List<Vector3>();
List<Vector2> uv1s = new List<Vector2>();
List<Vector2> uv2s = new List<Vector2>();
List<Vector2> uv3s = new List<Vector2>();
List<Vector2> uv4s = new List<Vector2>();
List<Color32> colors32 = new List<Color32>();
List<Transform> bones = new List<Transform>();
List<Matrix4x4> bindPoses = new List<Matrix4x4>();
List<BoneWeight> boneWeights = new List<BoneWeight>();
Dictionary<Material, List<int>> subMeshes = new Dictionary<Material, List<int>>();
if(skinnedMeshRenderers != null && skinnedMeshRenderers.Length > 0) {
bool meshLimitReached = false;
bool hasSkinnedMesh = false;
int counter = 0;
int nrOfMeshWithBlendShapes = -1;
// First we process the first mesh that has blendshapes
int meshNr = 0;
foreach(SkinnedMeshRenderer skinnedMeshRenderer in skinnedMeshRenderers) {
if(skinnedMeshRenderer.sharedMesh != null) {
if(vertices.Count + skinnedMeshRenderer.sharedMesh.vertexCount > 65534) meshLimitReached = true;
if(mesh == null && skinnedMeshRenderer.sharedMesh.blendShapeCount > 0) {
if(meshOffset <= counter && (!meshLimitReached)) {
// make copy to preserve blendshapes
mesh = (Mesh)Mesh.Instantiate(skinnedMeshRenderer.sharedMesh);
#if UNITY_4_3
#elif UNITY_4_4
#elif UNITY_4_5
#elif UNITY_4_6
#else
mesh.uv4 = null;
mesh.uv3 = null;
#endif
mesh.uv2 = null;
mesh.uv2 = null;
mesh.boneWeights = null;
mesh.colors32 = null;
mesh.triangles = null;
mesh.tangents = null;
mesh.normals = null;
mesh.vertices = null;
bool allSubmeshesMerged = MergeMeshInto(skinnedMeshRenderer.sharedMesh, skinnedMeshRenderer.bones, skinnedMeshRenderer.sharedMaterials, vertices, normals, uv1s, uv2s, uv3s, uv4s, colors32, boneWeights, bones, bindPoses, subMeshes, ((skinnedMeshRenderer.transform.localScale.x * skinnedMeshRenderer.transform.localScale.y * skinnedMeshRenderer.transform.localScale.z) < 0f), new Vector4(1,1,0,0), skinnedMeshRenderer.transform, topGO.transform, skinnedMeshRenderer.gameObject.name + "_" + skinnedMeshRenderer.sharedMesh.name, skipSubmeshNames);
if(allSubmeshesMerged && skinnedMeshRenderer.gameObject != topGO) {
skinnedMeshRenderer.gameObject.SetActive(false);
}
nrOfMeshWithBlendShapes = meshNr;
}
}
meshNr++;
}
}
// then we process all the other skinned meshes except the blendshaped one
foreach(SkinnedMeshRenderer skinnedMeshRenderer in skinnedMeshRenderers) {
if(skinnedMeshRenderer.sharedMesh != null) {
if(vertices.Count + skinnedMeshRenderer.sharedMesh.vertexCount > 65534) meshLimitReached = true;
if(meshOffset <= counter && (!meshLimitReached)) {
if(counter != nrOfMeshWithBlendShapes) {
bool allSubmeshesMerged = MergeMeshInto(skinnedMeshRenderer.sharedMesh, skinnedMeshRenderer.bones, skinnedMeshRenderer.sharedMaterials, vertices, normals, uv1s, uv2s, uv3s, uv4s, colors32, boneWeights, bones, bindPoses, subMeshes, ((skinnedMeshRenderer.transform.localScale.x * skinnedMeshRenderer.transform.localScale.y * skinnedMeshRenderer.transform.localScale.z) < 0f), new Vector4(1,1,0,0), skinnedMeshRenderer.transform, topGO.transform, skinnedMeshRenderer.gameObject.name + "_" + skinnedMeshRenderer.sharedMesh.name, skipSubmeshNames);
if(allSubmeshesMerged && skinnedMeshRenderer.gameObject != topGO) {
skinnedMeshRenderer.gameObject.SetActive(false);
}
}
hasSkinnedMesh = true;
meshOffset++;
}
counter++;
}
}
// and then the non-skinned meshes that were attached to some bone
if(meshRenderers != null && meshRenderers.Length > 0 && hasSkinnedMesh) {
foreach(MeshRenderer meshRenderer in meshRenderers) {
MeshFilter filter = meshRenderer.gameObject.GetComponent<MeshFilter>();
if(filter != null && filter.sharedMesh != null && filter.gameObject != topGO) {
if(vertices.Count + filter.sharedMesh.vertexCount > 65534) meshLimitReached = true;
if(meshOffset <= counter && (!meshLimitReached)) {
bool allSubmeshesMerged = MergeMeshInto(filter.sharedMesh, null, meshRenderer.sharedMaterials, vertices, normals, uv1s, uv2s, uv3s, uv4s, colors32, boneWeights, bones, bindPoses, subMeshes, ((filter.transform.localScale.x * filter.transform.localScale.y * filter.transform.localScale.z) < 0f), meshRenderer.lightmapScaleOffset, filter.transform, topGO.transform, filter.gameObject.name + "_" + filter.sharedMesh.name, skipSubmeshNames);
if(allSubmeshesMerged) {
// the gameObject has now become a bone, so I wll not disable it, but disable the renderer instead;
meshRenderer.enabled = false;
}
meshOffset++;
}
counter++;
}
}
}
} else if(meshRenderers != null && meshRenderers.Length > 0) {
int counter = 0;
foreach(MeshRenderer meshRenderer in meshRenderers) {
if(lightmapIndex<0 || meshRenderer.lightmapIndex<0 || meshRenderer.lightmapIndex>253 || lightmapIndex == meshRenderer.lightmapIndex) { // only merge objects with the same lightmapIndex
MeshFilter filter = meshRenderer.gameObject.GetComponent<MeshFilter>();
if(filter != null && filter.sharedMesh != null) {
if(meshOffset <= counter && vertices.Count + filter.sharedMesh.vertexCount <= 65534) {
bool allSubmeshesMerged = MergeMeshInto(filter.sharedMesh, null, meshRenderer.sharedMaterials, vertices, normals, uv1s, uv2s, uv3s, uv4s, colors32, boneWeights, bones, bindPoses, subMeshes, ((filter.transform.localScale.x * filter.transform.localScale.y * filter.transform.localScale.z) < 0f), meshRenderer.lightmapScaleOffset, filter.transform, topGO.transform, filter.gameObject.name + "_" + filter.sharedMesh.name, skipSubmeshNames);
if(allSubmeshesMerged && filter.gameObject != topGO) {
filter.gameObject.SetActive(false);
// Also disable parent (go's with meshes often have empty parent transforms that we dont need anymore)
Transform parentTransform = filter.gameObject.transform.parent;
if(parentTransform != null && parentTransform.gameObject != topGO) {
parentTransform.gameObject.SetActive(false);
}
}
meshOffset++;
}
counter++;
}
}
}
}
// remove unused vertices
LODMaker.RemoveUnusedVertices(vertices, normals, uv1s, uv2s, uv3s, uv4s, colors32, boneWeights, subMeshes);
if(mesh == null) mesh = new Mesh();
mesh.vertices = vertices.ToArray();
bool setValues = false;
if(normals.Count>0) mesh.normals = normals.ToArray();
setValues = false;
for(int i=0;i<uv1s.Count;i++) {
if(uv1s[i].x != 0f || uv1s[i].y != 0f) {
setValues = true;
break;
}
}
if(setValues) mesh.uv = uv1s.ToArray();
setValues = false;
for(int i=0;i<uv2s.Count;i++) {
if(uv2s[i].x != 0f || uv2s[i].y != 0f) {
setValues = true;
break;
}
}
if(setValues) mesh.uv2 = uv2s.ToArray();
#if UNITY_4_3
#elif UNITY_4_4
#elif UNITY_4_5
#elif UNITY_4_6
#else
setValues = false;
for(int i=0;i<uv3s.Count;i++) {
if(uv3s[i].x != 0f || uv3s[i].y != 0f) {
setValues = true;
break;
}
}
if(setValues) mesh.uv3 = uv3s.ToArray();
setValues = false;
for(int i=0;i<uv4s.Count;i++) {
if(uv4s[i].x != 0f || uv4s[i].y != 0f) {
setValues = true;
break;
}
}
if(setValues) mesh.uv4 = uv4s.ToArray();
#endif
setValues = false;
for(int i=0;i<colors32.Count;i++) {
if(colors32[i].r > 0 || colors32[i].g > 0 || colors32[i].b > 0) {
setValues = true;
break;
}
}
if(setValues) mesh.colors32 = colors32.ToArray();
if(bindPoses.Count>0) mesh.bindposes = bindPoses.ToArray();
if(boneWeights.Count>0) {
if(boneWeights.Count == vertices.Count) mesh.boneWeights = boneWeights.ToArray();
else Debug.LogWarning("Nr of bone weights not equal to nr of vertices.");
}
mesh.subMeshCount = subMeshes.Keys.Count;
Material[] materials = new Material[subMeshes.Keys.Count];
int mIdx = 0;
foreach(Material m in subMeshes.Keys) {
materials[mIdx] = m;
mesh.SetTriangles(subMeshes[m].ToArray(), mIdx++);
}
if(normals == null || normals.Count <= 0) mesh.RecalculateNormals();
mesh.RecalculateTangents();
mesh.RecalculateBounds();
if(skinnedMeshRenderers != null && skinnedMeshRenderers.Length > 0) {
SkinnedMeshRenderer meshRend = topGO.GetComponent<SkinnedMeshRenderer>();
if(meshRend == null) {
meshRend = topGO.AddComponent<SkinnedMeshRenderer>();
}
meshRend.quality = skinnedMeshRenderers[0].quality;
meshRend.sharedMesh = mesh;
meshRend.sharedMaterials = materials;
meshRend.bones = bones.ToArray();
} else if(meshRenderers != null && meshRenderers.Length > 0) {
MeshRenderer meshRend = topGO.GetComponent<MeshRenderer>();
if(meshRend == null) {
meshRend = topGO.AddComponent<MeshRenderer>();
}
if(lightmapIndex >=0 && lightmapIndex <= 253) meshRend.lightmapIndex = lightmapIndex;
meshRend.sharedMaterials = materials;
MeshFilter meshFilter = topGO.GetComponent<MeshFilter>();
if(meshFilter == null) {
meshFilter = topGO.AddComponent<MeshFilter>();
}
meshFilter.sharedMesh = mesh;
}
meshes.Add(mesh);
}
return meshes.ToArray();
}
private static int GiveUniqueNameIfNeeded(GameObject aGo, GameObject topGO, int uniqueId) {
if(IsChildWithNameUnique(topGO, aGo.name)) return uniqueId;
aGo.name = aGo.name + "_simpleLod" + (++uniqueId);
return uniqueId;
}
public static void SetUpLODLevels(this GameObject go) {
SetUpLODLevels(go, 1f);
}
public static void SetUpLODLevels(this GameObject go, float maxWeight) {
SetUpLODLevels(go, new float[3] {0.6f, 0.3f, 0.15f}, new float[3] {maxWeight * 0.65f, maxWeight, maxWeight * 1.5f});
}
public static void SetUpLODLevels(this GameObject go, float[] lodScreenSizes, float[] maxWeights) {
MeshFilter[] mfs = go.GetComponentsInChildren<MeshFilter>(false);
if(mfs == null || mfs.Length == 0) return;
for(int i=0;i<mfs.Length;i++) {
SetUpLODLevelsWithLODSwitcher(mfs[i].gameObject, lodScreenSizes, maxWeights, true);
}
}
public static Mesh[] SetUpLODLevelsWithLODSwitcher(this GameObject go, float[] lodScreenSizes, float[] maxWeights, bool recalcNormals, float removeSmallParts = 1f, float protectNormals = 1f, float protectUvs = 1f, float protectSubMeshesAndSharpEdges = 1f, float smallTrianglesFirst = 1f, int nrOfSteps = 1) {
Mesh mesh = null;
LODSwitcher lodSwitcher = go.GetComponent<LODSwitcher>();
if(lodSwitcher != null) {
lodSwitcher.ReleaseFixedLODLevel();
lodSwitcher.SetLODLevel(0);
}
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
if(smr != null) mesh = smr.sharedMesh;
else {
MeshFilter meshFilter = go.GetComponent<MeshFilter>();
if(meshFilter != null) mesh = meshFilter.sharedMesh;
}
if(mesh == null) {
throw new ApplicationException("No mesh found in " + go.name+". Maybe you need to select a child object?");
}
for(int i=0;i<maxWeights.Length;i++) {
if(maxWeights[i] <= 0f) throw new ApplicationException("MaxWeight should be more that 0 or else this operation will have no effect");
}
Mesh[] lodMeshes = mesh.MakeLODMeshes(maxWeights, recalcNormals, removeSmallParts, protectNormals, protectUvs, protectSubMeshesAndSharpEdges, smallTrianglesFirst, nrOfSteps);
if(lodMeshes == null) return null;
if(lodSwitcher == null) {
// add LODSwitcher component if needed
lodSwitcher = go.AddComponent<LODSwitcher>();
}
Array.Resize(ref lodMeshes, maxWeights.Length+1);
for(int i=maxWeights.Length;i>0;i--) lodMeshes[i] = lodMeshes[i-1];
lodMeshes[0] = mesh;
lodSwitcher.lodMeshes = lodMeshes;
lodSwitcher.lodScreenSizes = lodScreenSizes;
lodSwitcher.ComputeDimensions();
lodSwitcher.enabled = true;
return lodMeshes;
}
#if UNITY_WP8
#elif UNITY_WP_8_1
#elif UNITY_WSA
#elif UNITY_WSA_8_0
#elif UNITY_WSA_8_1
#elif UNITY_WINRT
#elif UNITY_WINRT_8_0
#elif UNITY_WINRT_8_1
#elif NETFX_CORE
#elif UNITY_WEBGL
#else
public static IEnumerator SetUpLODLevelsWithLODSwitcherInBackground(this GameObject go, float[] lodScreenSizes, float[] maxWeights, bool recalcNormals, float removeSmallParts = 1f, float protectNormals = 1f, float protectUvs = 1f, float protectSubMeshesAndSharpEdges = 1f, float smallTrianglesFirst = 1f) {
yield return null;
Mesh mesh = null;
LODSwitcher lodSwitcher = go.GetComponent<LODSwitcher>();
if(lodSwitcher != null) {
lodSwitcher.ReleaseFixedLODLevel();
lodSwitcher.SetLODLevel(0);
}
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
if(smr != null) mesh = smr.sharedMesh;
else {
MeshFilter meshFilter = go.GetComponent<MeshFilter>();
if(meshFilter != null) mesh = meshFilter.sharedMesh;
}
if(mesh == null) {
throw new ApplicationException("No mesh found in " + go.name+". Maybe you need to select a child object?");
}
for(int i=0;i<maxWeights.Length;i++) {
if(maxWeights[i] <= 0f) throw new ApplicationException("MaxWeight should be more that 0 or else this operation will have no effect");
}
Mesh mesh0 = mesh;
Mesh[] lodMeshes = new Mesh[maxWeights.Length];
for(int i=0;i<maxWeights.Length;i++) {
yield return null;
Hashtable lodInfo = new Hashtable();
lodInfo["maxWeight"] = maxWeights[i];
lodInfo["removeSmallParts"] = removeSmallParts;
Vector3[] vs = mesh.vertices;
if(vs.Length <= 0) throw new ApplicationException("Mesh was empty");
Vector3[] ns = mesh.normals;
if(ns.Length == 0) { // mesh has no normals
mesh.RecalculateNormals();
ns = mesh.normals;
}
Vector2[] uv1s = mesh.uv;
Vector2[] uv2s = mesh.uv2;
#if UNITY_4_3
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#elif UNITY_4_4
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#elif UNITY_4_5
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#elif UNITY_4_6
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#else
Vector2[] uv3s = mesh.uv3;
Vector2[] uv4s = mesh.uv4;
#endif
Color32[] colors32 = mesh.colors32;
int[] ts = mesh.triangles;
Matrix4x4[] bindposes = mesh.bindposes;
BoneWeight[] bws = mesh.boneWeights;
int[] subMeshOffsets = new int[mesh.subMeshCount];
if(mesh.subMeshCount > 1) { // read triangles of submeshes 1 by 1 because I dont know the internal order of the mesh
for(int s=0;s<mesh.subMeshCount;s++) {
int[] subTs = mesh.GetTriangles(s);
int t=0;
for(;t<subTs.Length;t++) ts[subMeshOffsets[s] + t] = subTs[t];
if(s+1 < mesh.subMeshCount) subMeshOffsets[s+1] = subMeshOffsets[s] + t;
}
}
Bounds meshBounds = mesh.bounds;
lodInfo["vertices"] = vs;
lodInfo["normals"] = ns;
lodInfo["uv1s"] = uv1s;
lodInfo["uv2s"] = uv2s;
lodInfo["uv3s"] = uv3s;
lodInfo["uv4s"] = uv4s;
lodInfo["colors32"] = colors32;
lodInfo["triangles"] = ts;
lodInfo["bindposes"] = bindposes;
lodInfo["boneWeights"] = bws;
lodInfo["subMeshOffsets"] = subMeshOffsets;
lodInfo["meshBounds"] = meshBounds;
Thread thread = new Thread(LODMaker.MakeLODMeshInBackground);
thread.Start(lodInfo);
while(!lodInfo.ContainsKey("ready")) {
yield return new WaitForSeconds(0.2f);
}
lodMeshes[i] = LODMaker.CreateNewMesh((Vector3[])lodInfo["vertices"], (Vector3[])lodInfo["normals"], (Vector2[])lodInfo["uv1s"], (Vector2[])lodInfo["uv2s"], (Vector2[])lodInfo["uv3s"], (Vector2[])lodInfo["uv4s"], (Color32[])lodInfo["colors32"], (int[])lodInfo["triangles"], (BoneWeight[])lodInfo["boneWeights"], (Matrix4x4[])lodInfo["bindposes"], (int[])lodInfo["subMeshOffsets"], recalcNormals);
mesh = lodMeshes[i];
go.transform.parent.gameObject.BroadcastMessage("LOD"+(i+1)+"IsReady", go, SendMessageOptions.DontRequireReceiver);
}
yield return null;
if(lodMeshes != null) {
if(lodSwitcher == null) {
// add LODSwitcher component if needed
lodSwitcher = go.AddComponent<LODSwitcher>();
}
Array.Resize(ref lodMeshes, maxWeights.Length+1);
for(int i=maxWeights.Length;i>0;i--) lodMeshes[i] = lodMeshes[i-1];
lodMeshes[0] = mesh0;
lodSwitcher.lodMeshes = lodMeshes;
lodSwitcher.lodScreenSizes = lodScreenSizes;
lodSwitcher.ComputeDimensions();
lodSwitcher.enabled = true;
}
go.transform.parent.gameObject.BroadcastMessage("LODsAreReady", go, SendMessageOptions.DontRequireReceiver);
}
#endif
public static Mesh[] SetUpLODLevelsAndChildrenWithLODSwitcher(this GameObject go, float[] lodScreenSizes, float[] maxWeights, bool recalcNormals, float removeSmallParts, float protectNormals = 1f, float protectUvs = 1f, float protectSubMeshesAndSharpEdges = 1f, float smallTrianglesFirst = 1f, int nrOfSteps = 1) {
Mesh mesh = null;
Material[] mats = null;
LODSwitcher lodSwitcher = go.GetComponent<LODSwitcher>();
if(lodSwitcher != null) {
lodSwitcher.ReleaseFixedLODLevel();
lodSwitcher.SetLODLevel(0);
}
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
if(smr != null) {
mesh = smr.sharedMesh;
mats = smr.sharedMaterials;
smr.enabled = false;
} else {
MeshFilter meshFilter = go.GetComponent<MeshFilter>();
if(meshFilter != null) mesh = meshFilter.sharedMesh;
MeshRenderer rend = go.GetComponent<MeshRenderer>();
if(rend == null) {
throw new ApplicationException("No MeshRenderer found");
}
mats = rend.sharedMaterials;
rend.enabled = false;
}
if(mesh == null) {
throw new ApplicationException("No mesh found in " + go.name+". Maybe you need to select a child object?");
}
for(int i=0;i<maxWeights.Length;i++) {
if(maxWeights[i] <= 0f) throw new ApplicationException("MaxWeight should be more that 0 or else this operation will have no effect");
}
Mesh[] lod1Meshes = mesh.MakeLODMeshes(maxWeights, recalcNormals, removeSmallParts, protectNormals, protectUvs, protectSubMeshesAndSharpEdges, smallTrianglesFirst, nrOfSteps);
if(lod1Meshes == null) return null;
Mesh[] lodMeshes = new Mesh[lod1Meshes.Length+1];
lodMeshes[0] = mesh;
for(int i=0;i<lod1Meshes.Length;i++) lodMeshes[i+1] = lod1Meshes[i];
if(lodSwitcher == null) lodSwitcher = go.AddComponent<LODSwitcher>();
lodSwitcher.lodScreenSizes = lodScreenSizes;
GameObject[] children = new GameObject[lodMeshes.Length];
for(int i=0;i<lodMeshes.Length;i++) {
Transform subTrans = go.transform.FindFirstChildWithName(go.name+"_LOD"+i);
if(subTrans != null) {
children[i] = subTrans.gameObject;
children[i].SetActive(true);
}
if(children[i] == null) {
children[i] = new GameObject(go.name+"_LOD"+i);
#if UNITY_4_3
children[i].transform.parent = go.transform;
#elif UNITY_4_4
children[i].transform.parent = go.transform;
#elif UNITY_4_5
children[i].transform.parent = go.transform;
#else
children[i].transform.SetParent(go.transform);
#endif
children[i].transform.localPosition = Vector3.zero;
children[i].transform.localRotation = Quaternion.identity;
children[i].transform.localScale = Vector3.one;
}
if(smr != null) {
SkinnedMeshRenderer sRend = children[i].GetComponent<SkinnedMeshRenderer>();
if(sRend == null) sRend = children[i].AddComponent<SkinnedMeshRenderer>();
sRend.sharedMesh = lodMeshes[i];
sRend.sharedMaterials = mats;
} else {
MeshFilter filter = children[i].GetComponent<MeshFilter>();
if(filter == null) filter = children[i].AddComponent<MeshFilter>();
filter.sharedMesh = lodMeshes[i];
MeshRenderer rend = children[i].GetComponent<MeshRenderer>();
if(rend == null) rend = children[i].AddComponent<MeshRenderer>();
rend.sharedMaterials = mats;
}
children[i].SetActive(i==0);
}
lodSwitcher.lodGameObjects = children;
lodSwitcher.ComputeDimensions();
lodSwitcher.enabled = true;
return lodMeshes;
}
public static Mesh[] SetUpLODLevelsAndChildrenWithLODGroup(this GameObject go, float[] relativeTransitionHeights, float[] maxWeights, bool recalcNormals, float removeSmallParts, float protectNormals = 1f, float protectUvs = 1f, float protectSubMeshesAndSharpEdges = 1f, float smallTrianglesFirst = 1f, int nrOfSteps = 1) {
GameObject parentGO = null;
LODGroup lodGroup = null;
if(relativeTransitionHeights.Length < 0 || relativeTransitionHeights.Length != maxWeights.Length) {
throw new ApplicationException("relativeTransitionHeights and maxWeights arrays need to have equal length and be longer than 0. Example: SetUpLODLevelsWithLODGroup(go, new float[2] {0.6f, 0.4f}, new float[2] {1f, 1.75f})");
}
for(int i=0;i<maxWeights.Length;i++) {
if(maxWeights[i] <= 0f) throw new ApplicationException("MaxWeight should be more that 0 or else this operation will have no effect");
}
parentGO = new GameObject(go.name+"_$LodGrp");
parentGO.transform.position = go.transform.position;
parentGO.transform.rotation = go.transform.rotation;
parentGO.transform.localScale = go.transform.localScale;
#if UNITY_4_3
go.transform.parent = parentGO.transform;
#elif UNITY_4_4
go.transform.parent = parentGO.transform;
#elif UNITY_4_5
go.transform.parent = parentGO.transform;
#else
go.transform.SetParent(parentGO.transform);
#endif
if(lodGroup == null) {
// add LODGroup component if needed
lodGroup = parentGO.AddComponent<LODGroup>() ;
} else {
// remove existing LOD gameObjects, because we will make new ones
// let's hope no one will call their sub gameobjects <parent name>_$Lod:*
Transform subObject = parentGO.transform.FindFirstChildWhereNameContains(go.name+"_$Lod:");
int safetyCounter = 0;
while(subObject != null && safetyCounter++ < 10) {
#if UNITY_4_3
subObject.parent = null;
#elif UNITY_4_4
subObject.parent = null;
#elif UNITY_4_5
subObject.parent = null;
#else
subObject.SetParent(null);
#endif
UnityEngine.Object.Destroy(subObject.gameObject);
subObject = parentGO.transform.FindFirstChildWhereNameContains(go.name+"_$Lod:");
}
}
// Create LODs
LOD[] lods = new LOD[maxWeights.Length + 1];
lods[0] = new LOD(relativeTransitionHeights[0], go.GetComponentsInChildren<MeshRenderer>(false));
List<Mesh> lodMeshes = new List<Mesh>();
Mesh[] meshesOrig = GetMeshes(go, false);
float prevWeight = 0f;
for(int l=1;l<lods.Length;l++) {
Mesh[] meshesLOD = new Mesh[meshesOrig.Length];
for(int i=0;i<meshesOrig.Length;i++) {
Mesh lodMesh = meshesOrig[i];
if(nrOfSteps < 1) nrOfSteps = 1;
for(int s=0;s<nrOfSteps;s++) {
float weight = (maxWeights[l-1] - prevWeight);
lodMesh = lodMesh.MakeLODMesh(((float)(s+1) * (weight / nrOfSteps)) + prevWeight, recalcNormals, removeSmallParts, protectNormals, protectUvs, protectSubMeshesAndSharpEdges, smallTrianglesFirst);
}
prevWeight = maxWeights[l-1];
meshesLOD[i] = lodMesh;
lodMesh.name = go.name+"_"+i+"_LOD"+l;
lodMeshes.Add(lodMesh);
}
GameObject goLOD = (GameObject)GameObject.Instantiate(go);
goLOD.name = go.name+"_$Lod:"+l;
#if UNITY_4_3
goLOD.transform.parent = parentGO.transform;
#elif UNITY_4_4
goLOD.transform.parent = parentGO.transform;
#elif UNITY_4_5
goLOD.transform.parent = parentGO.transform;
#else
goLOD.transform.SetParent(parentGO.transform);
#endif
goLOD.transform.localPosition = go.transform.localPosition;
goLOD.transform.localRotation = go.transform.localRotation;
goLOD.transform.localScale = go.transform.localScale;
SetMeshes(goLOD, meshesLOD);
float transitionHeight = l < relativeTransitionHeights.Length ? relativeTransitionHeights[l] : 0f;
lods[l] = new LOD(transitionHeight, goLOD.GetComponentsInChildren<MeshRenderer>(false));
meshesOrig = meshesLOD;
}
lodGroup.SetLODs(lods);
lodGroup.RecalculateBounds();
lodGroup.ForceLOD(-1);
return lodMeshes.ToArray();
}
public static Mesh GetSimplifiedMesh(this GameObject go, float maxWeight, bool recalcNormals, float removeSmallParts, float protectNormals = 1f, float protectUvs = 1f, float protectSubMeshesAndSharpEdges = 1f, float smallTrianglesFirst = 1f, int nrOfSteps = 1) {
Mesh mesh = null;
LODSwitcher lodSwitcher = go.GetComponent<LODSwitcher>();
if(lodSwitcher != null) {
lodSwitcher.ReleaseFixedLODLevel();
lodSwitcher.SetLODLevel(0);
}
MeshFilter mf = null;
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
if(maxWeight <= 0f) throw new ApplicationException("MaxWeight should be more that 0 or else this operation will have no effect");
if(smr != null) mesh = smr.sharedMesh;
else {
mf = go.GetComponent<MeshFilter>();
if(mf != null) mesh = mf.sharedMesh;
}
if(mesh == null) {
throw new ApplicationException("No mesh found. Maybe you need to select a child object?");
}
Mesh decimatedMesh = mesh;
if(nrOfSteps < 1) nrOfSteps = 1;
for(int i=0;i<nrOfSteps;i++) {
decimatedMesh = decimatedMesh.MakeLODMesh((float)(i+1) * (maxWeight / nrOfSteps), recalcNormals, removeSmallParts, protectNormals, protectUvs, protectSubMeshesAndSharpEdges, smallTrianglesFirst);
}
if(smr != null) {
smr.sharedMesh = decimatedMesh;
} else if(mf != null) {
mf.sharedMesh = decimatedMesh;
}
return decimatedMesh;
}
#if UNITY_WP8
#elif UNITY_WP_8_1
#elif UNITY_WSA
#elif UNITY_WSA_8_0
#elif UNITY_WSA_8_1
#elif UNITY_WINRT
#elif UNITY_WINRT_8_0
#elif UNITY_WINRT_8_1
#elif NETFX_CORE
#elif UNITY_WEBGL
#else
public static IEnumerator GetSimplifiedMeshInBackground(this GameObject go, float maxWeight, bool recalcNormals, float removeSmallParts, System.Action<Mesh> result) {
Mesh mesh = null;
LODSwitcher lodSwitcher = go.GetComponent<LODSwitcher>();
if(lodSwitcher != null) {
lodSwitcher.ReleaseFixedLODLevel();
lodSwitcher.SetLODLevel(0);
}
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
if(maxWeight <= 0f) throw new ApplicationException("MaxWeight should be more that 0 or else this operation will have no effect");
if(smr != null) mesh = smr.sharedMesh;
else {
MeshFilter meshFilter = go.GetComponent<MeshFilter>();
if(meshFilter != null) mesh = meshFilter.sharedMesh;
}
if(mesh == null) {
throw new ApplicationException("No mesh found. Maybe you need to select a child object?");
}
Hashtable lodInfo = new Hashtable();
lodInfo["maxWeight"] = maxWeight;
lodInfo["removeSmallParts"] = removeSmallParts;
Vector3[] vs = mesh.vertices;
if(vs.Length <= 0) throw new ApplicationException("Mesh was empty");
Vector3[] ns = mesh.normals;
if(ns.Length == 0) { // mesh has no normals
mesh.RecalculateNormals();
ns = mesh.normals;
}
Vector2[] uv1s = mesh.uv;
Vector2[] uv2s = mesh.uv2;
#if UNITY_4_3
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#elif UNITY_4_4
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#elif UNITY_4_5
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#elif UNITY_4_6
Vector2[] uv3s = new Vector2[0];
Vector2[] uv4s = new Vector2[0];
#else
Vector2[] uv3s = mesh.uv3;
Vector2[] uv4s = mesh.uv4;
#endif
Color32[] colors32 = mesh.colors32;
int[] ts = mesh.triangles;
Matrix4x4[] bindposes = mesh.bindposes;
BoneWeight[] bws = mesh.boneWeights;
int[] subMeshOffsets = new int[mesh.subMeshCount];
if(mesh.subMeshCount > 1) { // read triangles of submeshes 1 by 1 because I dont know the internal order of the mesh
for(int s=0;s<mesh.subMeshCount;s++) {
int[] subTs = mesh.GetTriangles(s);
int t=0;
for(;t<subTs.Length;t++) ts[subMeshOffsets[s] + t] = subTs[t];
if(s+1 < mesh.subMeshCount) subMeshOffsets[s+1] = subMeshOffsets[s] + t;
}
}
Bounds meshBounds = mesh.bounds;
lodInfo["vertices"] = vs;
lodInfo["normals"] = ns;
lodInfo["uv1s"] = uv1s;
lodInfo["uv2s"] = uv2s;
lodInfo["uv3s"] = uv3s;
lodInfo["uv4s"] = uv4s;
lodInfo["colors32"] = colors32;
lodInfo["triangles"] = ts;
lodInfo["bindposes"] = bindposes;
lodInfo["boneWeights"] = bws;
lodInfo["subMeshOffsets"] = subMeshOffsets;
lodInfo["meshBounds"] = meshBounds;
Thread thread = new Thread(LODMaker.MakeLODMeshInBackground);
thread.Start(lodInfo);
while(!lodInfo.ContainsKey("ready")) {
yield return new WaitForSeconds(0.2f);
}
result(LODMaker.CreateNewMesh((Vector3[])lodInfo["vertices"], (Vector3[])lodInfo["normals"], (Vector2[])lodInfo["uv1s"], (Vector2[])lodInfo["uv2s"], (Vector2[])lodInfo["uv3s"], (Vector2[])lodInfo["uv4s"], (Color32[])lodInfo["colors32"], (int[])lodInfo["triangles"], (BoneWeight[])lodInfo["boneWeights"], (Matrix4x4[])lodInfo["bindposes"], (int[])lodInfo["subMeshOffsets"], recalcNormals));
}
#endif
private static bool MergeMeshInto(
Mesh fromMesh,
Transform[] fromBones,
Material[] fromMaterials,
List<Vector3> vertices,
List<Vector3> normals,
List<Vector2> uv1s,
List<Vector2> uv2s,
List<Vector2> uv3s,
List<Vector2> uv4s,
List<Color32> colors32,
List<BoneWeight> boneWeights,
List<Transform> bones,
List<Matrix4x4> bindposes,
Dictionary<Material, List<int>> subMeshes,
bool usesNegativeScale,
Vector4 lightmapScaleOffset,
Transform fromTransform,
Transform topTransform,
string submeshName,
string[] skipSubmeshNames) {
if(fromMesh == null) return false;
bool allSubmeshesMerged = true;
int vertexOffset = vertices.Count;
Vector3[] fromVertices = fromMesh.vertices;
Vector3[] fromNormals = fromMesh.normals;
Vector2[] fromUv1s = fromMesh.uv;
Vector2[] fromUv2s = fromMesh.uv2;
#if UNITY_4_3
#elif UNITY_4_4
#elif UNITY_4_5
#elif UNITY_4_6
#else
Vector2[] fromUv3s = fromMesh.uv3;
Vector2[] fromUv4s = fromMesh.uv4;
#endif
Color32[] fromColors32 = fromMesh.colors32;
BoneWeight[] fromBoneWeights = fromMesh.boneWeights;
Matrix4x4[] fromBindposes = fromMesh.bindposes;
List<int> old2NewBoneIndex = new List<int>();
Vector3 oldPos = fromTransform.localPosition;
Quaternion oldRot = fromTransform.localRotation;
Vector3 oldScale = fromTransform.localScale;
bool remapVertices = false;
if(fromBones != null) {
fromTransform.localPosition = Vector3.zero;
fromTransform.localRotation = Quaternion.identity;
fromTransform.localScale = Vector3.one;
}
if(fromBones == null || fromBones.Length == 0) remapVertices = true;
if((fromBones == null || fromBones.Length == 0) && bones != null && bones.Count > 0) { // merging a non-skinned mesh into a skinned mesh
fromBones = new Transform[] {fromTransform};
Matrix4x4 newBindPose = fromTransform.worldToLocalMatrix * topTransform.localToWorldMatrix;
fromBindposes = new Matrix4x4[] {newBindPose};
fromBoneWeights = new BoneWeight[fromVertices.Length];
for(int i=0;i<fromVertices.Length;i++) {
fromBoneWeights[i].boneIndex0 = 0;
fromBoneWeights[i].weight0 = 1;
}
}
if(fromBones != null) {
bool hasDifferentBindposes = false;
for(int from=0;from<fromBones.Length;from++) {
int to = 0;
old2NewBoneIndex.Add(from);
for(;to<bones.Count;to++) {
if(fromBones[from] == bones[to]) {
old2NewBoneIndex[from] = to;
if(fromBindposes[from] != bindposes[to]) {
hasDifferentBindposes = true;
if(fromBones[from] != null) {
Debug.Log(fromTransform.gameObject.name + ": The bindpose of " + fromBones[from].gameObject.name + " is different, vertices will be moved to match the bindpose of the merged mesh");
} else {
Debug.LogError(fromTransform.gameObject.name + ": There is an error in the bonestructure. A bone could not be found.");
}
}
}
}
if(to >= bones.Count) { // bone not found
old2NewBoneIndex[from] = bones.Count;
bones.Add(fromBones[from]);
bindposes.Add(fromBindposes[from]);
}
}
if(hasDifferentBindposes) {
for(int i=0;i<fromVertices.Length;i++) {
Vector3 v = fromVertices[i];
BoneWeight bw = fromBoneWeights[i];
if(fromBones[bw.boneIndex0] != null) {
// apply the old bindpose and inverse apply the new bindpose to put the vertex in the right position
v = ApplyBindPose(fromVertices[i], fromBones[bw.boneIndex0], fromBindposes[bw.boneIndex0], bw.weight0);
if(bw.weight1 > 0f) v = v + ApplyBindPose(fromVertices[i], fromBones[bw.boneIndex1], fromBindposes[bw.boneIndex1], bw.weight1);
if(bw.weight2 > 0f) v = v + ApplyBindPose(fromVertices[i], fromBones[bw.boneIndex2], fromBindposes[bw.boneIndex2], bw.weight2);
if(bw.weight3 > 0f) v = v + ApplyBindPose(fromVertices[i], fromBones[bw.boneIndex3], fromBindposes[bw.boneIndex3], bw.weight3);
Vector3 v0 = v;
v = UnApplyBindPose(v0, bones[old2NewBoneIndex[bw.boneIndex0]], bindposes[old2NewBoneIndex[bw.boneIndex0]], bw.weight0);
if(bw.weight1 > 0f) v = v + UnApplyBindPose(v0, bones[old2NewBoneIndex[bw.boneIndex1]], bindposes[old2NewBoneIndex[bw.boneIndex1]], bw.weight1);
if(bw.weight2 > 0f) v = v + UnApplyBindPose(v0, bones[old2NewBoneIndex[bw.boneIndex2]], bindposes[old2NewBoneIndex[bw.boneIndex2]], bw.weight2);
if(bw.weight3 > 0f) v = v + UnApplyBindPose(v0, bones[old2NewBoneIndex[bw.boneIndex3]], bindposes[old2NewBoneIndex[bw.boneIndex3]], bw.weight3);
fromVertices[i] = v;
}
}
}
}
if(boneWeights != null && fromBoneWeights!=null && fromBoneWeights.Length>0) {
for(int i=0;i<fromBoneWeights.Length;i++) {
BoneWeight bw = new BoneWeight();
bw.boneIndex0 = old2NewBoneIndex[fromBoneWeights[i].boneIndex0];
bw.boneIndex1 = old2NewBoneIndex[fromBoneWeights[i].boneIndex1];
bw.boneIndex2 = old2NewBoneIndex[fromBoneWeights[i].boneIndex2];
bw.boneIndex3 = old2NewBoneIndex[fromBoneWeights[i].boneIndex3];
bw.weight0 = fromBoneWeights[i].weight0;
bw.weight1 = fromBoneWeights[i].weight1;
bw.weight2 = fromBoneWeights[i].weight2;
bw.weight3 = fromBoneWeights[i].weight3;
boneWeights.Add(bw);
}
}
Matrix4x4 transformMatrix = topTransform.worldToLocalMatrix * fromTransform.localToWorldMatrix;
if(remapVertices) {
for(int i=0;i<fromVertices.Length;i++) {
Vector3 v = fromVertices[i];
fromVertices[i] = transformMatrix.MultiplyPoint3x4(v);
}
}
vertices.AddRange(fromVertices);
Quaternion rotation = Quaternion.LookRotation(transformMatrix.GetColumn(2), transformMatrix.GetColumn(1));
if(fromNormals!=null && fromNormals.Length>0) {
for(int i=0;i<fromNormals.Length;i++) fromNormals[i] = rotation * fromNormals[i];
normals.AddRange(fromNormals);
}
if(fromUv1s==null || fromUv1s.Length != fromVertices.Length) fromUv1s = new Vector2[fromVertices.Length];
if(fromUv1s!=null && fromUv1s.Length>0) uv1s.AddRange(fromUv1s);
if(fromUv2s==null || fromUv2s.Length != fromVertices.Length) fromUv2s = new Vector2[fromVertices.Length];
for(int i=0;fromUv2s!=null && i<fromUv2s.Length;i++) {
uv2s.Add(new Vector2(lightmapScaleOffset.z + (fromUv2s[i].x * lightmapScaleOffset.x), lightmapScaleOffset.w + (fromUv2s[i].y * lightmapScaleOffset.y)));
}
#if UNITY_4_3
#elif UNITY_4_4
#elif UNITY_4_5
#elif UNITY_4_6
#else
if(fromUv3s==null || fromUv3s.Length != fromVertices.Length) fromUv3s = new Vector2[fromVertices.Length];
if(fromUv3s!=null && fromUv3s.Length>0) uv3s.AddRange(fromUv3s);
if(fromUv4s==null || fromUv4s.Length != fromVertices.Length) fromUv4s = new Vector2[fromVertices.Length];
if(fromUv4s!=null && fromUv4s.Length>0) uv4s.AddRange(fromUv3s);
#endif
if(fromColors32==null || fromColors32.Length != fromVertices.Length) fromColors32 = new Color32[fromVertices.Length];
if(fromColors32!=null && fromColors32.Length>0) colors32.AddRange(fromColors32);
int d=0;
for(int i=0;i<fromMaterials.Length;i++) {
if(i<fromMesh.subMeshCount) {
string thisSubmeshName = submeshName + "_" + i;
int j = 0;
for(;j<skipSubmeshNames.Length;j++) {
if(thisSubmeshName == skipSubmeshNames[j]) break;
}
if(j>=skipSubmeshNames.Length) {
int[] ts = fromMesh.GetTriangles(i);
if(ts.Length>0) {
if(fromMaterials[i]!=null && !subMeshes.ContainsKey(fromMaterials[i])) {
subMeshes.Add(fromMaterials[i], new List<int>());
}
List<int> subMesh = subMeshes[fromMaterials[i]];
for(int t=0;t<ts.Length;t+=3) {
if(usesNegativeScale) { // flip triangle
int t1 = ts[t+1];
int t2 = ts[t+2];
ts[t+1] = t2;
ts[t+2] = t1;
d++;
}
ts[t] += vertexOffset;
ts[t+1] += vertexOffset;
ts[t+2] += vertexOffset;
}
subMesh.AddRange(ts);
}
} else {
allSubmeshesMerged = false;
}
}
}
if(fromBones != null) {
fromTransform.localPosition = oldPos;
fromTransform.localRotation = oldRot;
fromTransform.localScale = oldScale;
}
return allSubmeshesMerged;
}
private static Vector3 ApplyBindPose(Vector3 vertex, Transform bone, Matrix4x4 bindpose, float boneWeight) {
Matrix4x4 newMatrix = (bone.localToWorldMatrix * bindpose);
for(int r=0;r<4;r++) {
for(int c=0;c<4;c++) {
newMatrix[r,c] *= boneWeight;
}
}
return newMatrix.MultiplyPoint3x4(vertex);
}
private static Vector3 UnApplyBindPose(Vector3 vertex, Transform bone, Matrix4x4 bindpose, float boneWeight) {
Matrix4x4 newMatrix = (bone.localToWorldMatrix * bindpose).inverse;
for(int r=0;r<4;r++) {
for(int c=0;c<4;c++) {
newMatrix[r,c] *= boneWeight;
}
}
return newMatrix.MultiplyPoint3x4(vertex);
}
}
}