using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using LunarCatsStudio.SuperCombiner;
#if UNITY_EDITOR
using UnityEditor;
#endif
///
/// This class manage the combine process of meshes and skinned meshes
///
namespace LunarCatsStudio.SuperCombiner
{
public class MeshCombiner
{
///
/// The session name
///
private string _sessionName = "";
///
/// List of Blendshape frames
///
private Dictionary blendShapes = new Dictionary();
private int _vertexOffset = 0;
///
/// Flag to specify if UV2 have to be generated or not
///
private bool _generateUv2 = true;
///
/// The reference to the _combinedResult
///
private CombinedResult _combinedResult;
public CombinedResult CombinedResult
{
set
{
_combinedResult = value;
}
}
///
/// Set the different parameters of MeshCombiner
///
///
///
public void SetParameters(string sessionName_p, bool generateUv2_p)
{
_sessionName = sessionName_p;
_generateUv2 = generateUv2_p;
}
public void Clear()
{
blendShapes.Clear();
_vertexOffset = 0;
}
public List CombineToMeshes(List meshRenderers, List skinnedMeshRenderers, Transform parent, int combinedIndex)
{
// The list of Meshes created
List combinedMeshes = new List();
// The list of _combineInstances
CombineInstanceID combineInstances = new CombineInstanceID();
int verticesCount = 0;
int combinedGameObjectCount = 0;
// Process meshes for combine process
for (int i = 0; i < meshRenderers.Count; i++)
{
// Loop over all submeshes
for (int j = 0; j < meshRenderers[i].GetComponent().sharedMesh.subMeshCount; j++)
{
// Get a copy of the submesh at index j
Mesh newMesh = SubmeshSplitter.ExtractSubmesh(meshRenderers[i].GetComponent().sharedMesh, j);
verticesCount += newMesh.vertexCount;
// Create the list of CombineInstance for this mesh
Matrix4x4 matrix = parent.transform.worldToLocalMatrix * meshRenderers[i].transform.localToWorldMatrix;
combineInstances.AddRange(CreateCombinedInstances(newMesh, new Material[] { meshRenderers[i].sharedMaterials[j] }, meshRenderers[i].gameObject.GetInstanceID(), meshRenderers[i].gameObject.name, matrix, combinedIndex));
}
}
for (int i = 0; i < skinnedMeshRenderers.Count; i++)
{
// Loop over all submeshes
for (int j = 0; j < meshRenderers[i].GetComponent().sharedMesh.subMeshCount; j++)
{
// Get a snapshot of the sub skinnedMesh renderer at index j
Mesh newMesh = SubmeshSplitter.ExtractSubmesh(skinnedMeshRenderers[i].sharedMesh, j);
_vertexOffset += newMesh.vertexCount;
verticesCount += newMesh.vertexCount;
// Create the list of CombineInstance for this skinnedMesh
Matrix4x4 matrix = parent.transform.worldToLocalMatrix * skinnedMeshRenderers[i].transform.localToWorldMatrix;
combineInstances.AddRange(CreateCombinedInstances(newMesh, new Material[] { skinnedMeshRenderers[i].sharedMaterials[j] }, skinnedMeshRenderers[i].GetInstanceID(), skinnedMeshRenderers[i].gameObject.name, matrix, combinedIndex));
}
}
if (combineInstances.Count() > 0)
{
// Create the combined GameObject which contains the combined meshes
combinedMeshes.Add(CreateCombinedMeshGameObject(combineInstances, parent, combinedGameObjectCount, combinedIndex));
}
return combinedMeshes;
}
public List CombineToSkinnedMeshes(List meshRenderers, List skinnedMeshRenderers, Transform parent, int combinedIndex)
{
// The list of Meshes created
List combinedMeshes = new List();
// The list of _combineInstances
CombineInstanceID combineInstances = new CombineInstanceID();
int verticesCount = 0;
int combinedGameObjectCount = 0;
/*
/ Skinned mesh parameters
*/
// List of bone weight
List boneWeights = new List();
// List of bones
List bones = new List();
// List of bindposes
List bindposes = new List();
// List of original bones mapped to their instanceId
Dictionary originalBones = new Dictionary();
// Link original bone instanceId to the new created bones
Dictionary originToNewBoneMap = new Dictionary();
// The vertices count
int boneOffset = 0;
// Get bones hierarchies from all skinned mesh
for (int i = 0; i < skinnedMeshRenderers.Count; i++)
{
foreach (Transform t in skinnedMeshRenderers[i].bones)
{
if (!originalBones.ContainsKey(t.GetInstanceID()))
{
originalBones.Add(t.GetInstanceID(), t);
}
}
}
// Find the root bones
Transform[] rootBones = FindRootBone(originalBones);
for (int i = 0; i < rootBones.Length; i++)
{
// Instantiate the GameObject parent for this rootBone
GameObject rootBoneParent = new GameObject("rootBone" + i);
rootBoneParent.transform.position = rootBones[i].position;
rootBoneParent.transform.parent = parent;
rootBoneParent.transform.localPosition -= rootBones[i].localPosition;
rootBoneParent.transform.localRotation = Quaternion.identity;
// Instanciate a copy of the root bone
GameObject newRootBone = InstantiateCopy(rootBones[i].gameObject);
newRootBone.transform.position = rootBones[i].position;
newRootBone.transform.rotation = rootBones[i].rotation;
newRootBone.transform.parent = rootBoneParent.transform;
newRootBone.AddComponent();
// Get the correspondancy map between original bones and new bones
GetOrignialToNewBonesCorrespondancy(rootBones[i], newRootBone.transform, originToNewBoneMap);
}
// Copy Animator Controllers to new Combined GameObject
foreach (Animator anim in parent.parent.GetComponentsInChildren())
{
Transform[] children = anim.GetComponentsInChildren();
// Find the transform into which a copy of the Animator component will be added
Transform t = FindTransformForAnimator(children, rootBones, anim);
if (t != null)
{
CopyAnimator(anim, originToNewBoneMap[t.GetInstanceID()].parent.gameObject);
}
}
for (int i = 0; i < skinnedMeshRenderers.Count; i++)
{
// Get a snapshot of the skinnedMesh renderer
Mesh mesh = copyMesh(skinnedMeshRenderers[i].sharedMesh, skinnedMeshRenderers[i].GetInstanceID().ToString());
_vertexOffset += mesh.vertexCount;
verticesCount += skinnedMeshRenderers[i].sharedMesh.vertexCount;
// Copy bone weights
BoneWeight[] meshBoneweight = skinnedMeshRenderers[i].sharedMesh.boneWeights;
foreach (BoneWeight bw in meshBoneweight)
{
BoneWeight bWeight = bw;
bWeight.boneIndex0 += boneOffset;
bWeight.boneIndex1 += boneOffset;
bWeight.boneIndex2 += boneOffset;
bWeight.boneIndex3 += boneOffset;
boneWeights.Add(bWeight);
}
boneOffset += skinnedMeshRenderers[i].bones.Length;
// Copy bones and bindposes
Transform[] meshBones = skinnedMeshRenderers[i].bones;
foreach (Transform bone in meshBones)
{
bones.Add(originToNewBoneMap[bone.GetInstanceID()]);
bindposes.Add(bone.worldToLocalMatrix * parent.transform.localToWorldMatrix);
}
// Create the list of CombineInstance for this skinnedMesh
Matrix4x4 matrix = parent.transform.worldToLocalMatrix * skinnedMeshRenderers[i].transform.localToWorldMatrix;
combineInstances.AddRange(CreateCombinedInstances(mesh, skinnedMeshRenderers[i].sharedMaterials, skinnedMeshRenderers[i].GetInstanceID(), skinnedMeshRenderers[i].gameObject.name, matrix, combinedIndex));
}
if (combineInstances.Count() > 0)
{
// Create the combined GameObject which contains the combined meshes
// Create the new GameObject
GameObject combinedGameObject = CreateCombinedSkinnedMeshGameObject(combineInstances, parent, combinedGameObjectCount, combinedIndex);
// Assign skinnedMesh parameters values
SkinnedMeshRenderer sk = combinedGameObject.GetComponent();
AssignParametersToSkinnedMesh(sk, bones, boneWeights, bindposes);
combinedMeshes.Add(combinedGameObject);
}
return combinedMeshes;
}
// Assign the parameters of the new skinnedMesh
private void AssignParametersToSkinnedMesh(SkinnedMeshRenderer skin, List bones, List boneWeights, List bindposes)
{
// Complete bone weights list if some are missing. BoneWeight is either empty or has the same quantity of elements than vertexCount
if (boneWeights.Count > 0)
{
for (int i = boneWeights.Count; i < skin.sharedMesh.vertexCount; i++)
{
boneWeights.Add(boneWeights[0]);
}
}
skin.bones = bones.ToArray();
//sk.rootBone = newRootBone.transform;
skin.sharedMesh.boneWeights = boneWeights.ToArray();
skin.sharedMesh.bindposes = bindposes.ToArray();
skin.sharedMesh.RecalculateBounds();
skin.sharedMesh.RecalculateNormals();
bones.Clear();
boneWeights.Clear();
bindposes.Clear();
_vertexOffset = 0;
}
// Copy the animator component to the transform
private void CopyAnimator(Animator anim, GameObject target)
{
if (target.GetComponentsInChildren().Length == 0)
{
Animator newAnimator = target.AddComponent(typeof(Animator)) as Animator;
if (newAnimator != null)
{
newAnimator.applyRootMotion = anim.applyRootMotion;
newAnimator.avatar = anim.avatar;
newAnimator.updateMode = anim.updateMode;
newAnimator.cullingMode = anim.cullingMode;
newAnimator.runtimeAnimatorController = anim.runtimeAnimatorController;
}
}
}
// Find the transform in which to instanciate the animator component
private Transform FindTransformForAnimator(Transform[] children, Transform[] rootBones, Animator anim)
{
foreach (Transform t in children)
{
for (int i = 0; i < rootBones.Length; i++)
{
if (t.Equals(rootBones[i]))
{
return rootBones[i];
}
}
}
return null;
}
// Return a correspondancy map between original bones and the new bones
private void GetOrignialToNewBonesCorrespondancy(Transform rootBone, Transform newRootBone, Dictionary originToNewBoneMap)
{
Transform[] rootBoneTransforms = rootBone.GetComponentsInChildren();
Transform[] newRootBoneTransforms = newRootBone.GetComponentsInChildren();
// Get correspondancy between original bones and the new ones recently created
for (int i = 0; i < newRootBoneTransforms.Length; i++)
{
if (!originToNewBoneMap.ContainsKey(rootBoneTransforms[i].GetInstanceID()))
{
originToNewBoneMap.Add(rootBoneTransforms[i].GetInstanceID(), newRootBoneTransforms[i]);
}
else
{
Logger.Instance.AddLog("SuperCombiner", " Found duplicated root bone: " + rootBoneTransforms[i], Logger.LogLevel.LOG_WARNING);
}
}
}
// Find the list of root bone from a hierachy of bones
private Transform[] FindRootBone(Dictionary bones)
{
List rootBones = new List();
List bonesList = new List(bones.Values);
if (bonesList.Count == 0)
{
return rootBones.ToArray();
}
Transform rootBone = bonesList.ToArray()[0];
while (rootBone.parent != null)
{
if (bones.ContainsKey(rootBone.parent.GetInstanceID()))
{
rootBone = rootBone.parent;
}
else
{
rootBones.Add(rootBone.parent);
Transform[] children = rootBone.parent.GetComponentsInChildren();
foreach (Transform t in children)
{
bones.Remove(t.GetInstanceID());
if (t != rootBone.parent && rootBones.Contains(t))
{
rootBones.Remove(t);
}
}
Transform[] otherBones = (new List(bones.Values)).ToArray();
if (otherBones.Length > 0)
{
rootBone = otherBones[0];
}
else
{
break;
}
}
}
return rootBones.ToArray();
}
// Instantiate a copy of the GameObject, keeping it's transform values identical
private GameObject InstantiateCopy(GameObject original)
{
GameObject copy = GameObject.Instantiate(original) as GameObject;
copy.transform.parent = original.transform.parent;
copy.transform.localPosition = original.transform.localPosition;
copy.transform.localRotation = original.transform.localRotation;
copy.transform.localScale = original.transform.localScale;
copy.name = original.name;
// Remove all SkinnedMeshRenderes that may be inside root hierarchy
foreach (SkinnedMeshRenderer skin in copy.GetComponentsInChildren())
{
GameObject.DestroyImmediate(skin);
}
return copy;
}
///
/// Create a new combineInstance based on a new mesh
///
///
///
///
///
///
///
///
private CombineInstanceID CreateCombinedInstances(Mesh mesh, Material[] sharedMaterials, int instanceID, string name, Matrix4x4 matrix, int combinedIndex)
{
CombineInstanceID instances = new CombineInstanceID();
int[] textureIndexes = new int[mesh.subMeshCount];
for (int k = 0; k < mesh.subMeshCount; k++)
{
// Find corresponding _material for each submesh
if (k < sharedMaterials.Length)
{
Material mat = sharedMaterials[k];
textureIndexes[k] = _combinedResult.FindCorrespondingMaterialIndex(mat, combinedIndex);
}
else
{
Logger.Instance.AddLog("SuperCombiner", " Mesh '" + mesh.name + "' has " + mesh.subMeshCount + " submeshes but only " + sharedMaterials.Length + " _material(s) assigned", Logger.LogLevel.LOG_WARNING);
break;
}
}
// Update submesh count
_combinedResult._subMeshCount += (mesh.subMeshCount - 1);
// Generate new UVs only if there are more than 1 _material combined
if (_combinedResult._originalMaterialList[combinedIndex].Count > 1)
{
GenerateUV(mesh, textureIndexes, _combinedResult._combinedMaterials[combinedIndex].scaleFactors.ToArray(), name, combinedIndex);
}
for (int k = 0; k < mesh.subMeshCount; k++)
{
instances.AddCombineInstance(k, mesh, matrix, instanceID, name);
}
return instances;
}
///
/// Create a new GameObject based on the CombineInstance list
///
///
///
///
///
private GameObject CreateCombinedSkinnedMeshGameObject(CombineInstanceID instances, Transform parent, int number, int combinedIndex)
{
GameObject combined = new GameObject(_sessionName + number.ToString());
SkinnedMeshRenderer skinnedMeshRenderer = combined.AddComponent();
skinnedMeshRenderer.sharedMaterial = _combinedResult._combinedMaterials[combinedIndex].material;
skinnedMeshRenderer.sharedMesh = new Mesh();
skinnedMeshRenderer.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
skinnedMeshRenderer.sharedMesh.name = _sessionName + "_" + _combinedResult._combinedMaterials[combinedIndex].displayedIndex + "_mesh" + number;
skinnedMeshRenderer.sharedMesh.CombineMeshes(instances._combineInstances.ToArray(), true, true);
#if UNITY_5_3_OR_NEWER
// Add blendShapes to new skinnedMesh renderer if needed
foreach (BlendShapeFrame blendShape in blendShapes.Values)
{
Vector3[] detlaVertices = new Vector3[skinnedMeshRenderer.sharedMesh.vertexCount];
Vector3[] detlaNormals = new Vector3[skinnedMeshRenderer.sharedMesh.vertexCount];
Vector3[] detlaTangents = new Vector3[skinnedMeshRenderer.sharedMesh.vertexCount];
for (int p = 0; p < blendShape._deltaVertices.Length; p++)
{
detlaVertices.SetValue(blendShape._deltaVertices[p], p + blendShape._vertexOffset);
detlaNormals.SetValue(blendShape._deltaNormals[p], p + blendShape._vertexOffset);
detlaTangents.SetValue(blendShape._deltaTangents[p], p + blendShape._vertexOffset);
}
skinnedMeshRenderer.sharedMesh.AddBlendShapeFrame(blendShape._shapeName, blendShape._frameWeight, detlaVertices, detlaNormals, detlaTangents);
}
#endif
#if UNITY_EDITOR
MeshUtility.Optimize(skinnedMeshRenderer.sharedMesh);
#endif
combined.transform.SetParent(parent);
combined.transform.localPosition = Vector3.zero;
_combinedResult._totalVertexCount += skinnedMeshRenderer.sharedMesh.vertexCount;
_combinedResult.AddCombinedMesh(skinnedMeshRenderer.sharedMesh, instances, combinedIndex);
return combined;
}
///
/// Create a new GameObject based on the CombineInstance list.
/// Set its MeshFilter and MeshRenderer to the new combined Meshe/Material
///
///
///
///
///
public GameObject CreateCombinedMeshGameObject(CombineInstanceID instances, Transform parent, int number, int combinedIndex)
{
GameObject combined;
MeshFilter meshFilter;
MeshRenderer meshRenderer;
// If parent has components MeshFilters and MeshRenderers, replace meshes and materials
if (number == 0 && parent.GetComponent() != null && parent.GetComponent() != null)
{
combined = parent.gameObject;
meshFilter = parent.GetComponent();
meshRenderer = parent.GetComponent();
}
else
{
combined = new GameObject(_sessionName + "_" + _combinedResult._combinedMaterials[combinedIndex].displayedIndex + "_" + number.ToString());
meshFilter = combined.AddComponent();
meshRenderer = combined.AddComponent();
combined.transform.SetParent(parent);
combined.transform.localPosition = Vector3.zero;
}
meshRenderer.sharedMaterial = _combinedResult._combinedMaterials[combinedIndex].material;
meshFilter.mesh = new Mesh();
meshFilter.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
meshFilter.sharedMesh.name = _sessionName + "_" + _combinedResult._combinedMaterials[combinedIndex].displayedIndex + "_mesh" + number;
meshFilter.sharedMesh.CombineMeshes(instances._combineInstances.ToArray());
#if UNITY_EDITOR
MeshUtility.Optimize(meshFilter.sharedMesh);
if (_generateUv2)
{
Unwrapping.GenerateSecondaryUVSet(meshFilter.sharedMesh);
}
#endif
_combinedResult._totalVertexCount += meshFilter.sharedMesh.vertexCount;
_combinedResult.AddCombinedMesh(meshFilter.sharedMesh, instances, combinedIndex);
return combined;
}
// Generate the new transformed gameobjects and apply new materials to them
public bool GenerateUV(Mesh targetMesh, int[] textureIndex, float[] scaleFactors, string objectName, int combinedIndex)
{
int subMeshCount = targetMesh.subMeshCount;
if (subMeshCount > textureIndex.Length)
{
Logger.Instance.AddLog("SuperCombiner", "GameObject '" + objectName + "' has submeshes with no _material assigned", Logger.LogLevel.LOG_WARNING);
subMeshCount = textureIndex.Length;
}
Logger.Instance.AddLog("SuperCombiner", "Processing '" + objectName + "'...", Logger.LogLevel.LOG_DEBUG, false);
Vector2[] uv = (Vector2[])(targetMesh.uv);
if (uv.Length <= 0)
{
#if UNITY_EDITOR
// The mesh does not have UVs, so we try to unwrap the UV's
Logger.Instance.AddLog("SuperCombiner", "Object " + objectName + " doesn't have uv, SuperCombiner will try to unwrap it's uvs but the result may be incorrect. In order to avoid this potential issue, add uv map to this mesh with a 3d modeler tool.", Logger.LogLevel.LOG_WARNING);
Vector2[] uvTemp = Unwrapping.GeneratePerTriangleUV(targetMesh);
uv = new Vector2[targetMesh.vertexCount];
for (int i = 0; i < targetMesh.vertexCount; i++)
{
uv[i] = uvTemp[i];
}
targetMesh.uv = new Vector2[uv.Length];
#endif
}
Vector2[] uv2 = (Vector2[])(targetMesh.uv2);
Vector2[] new_uv = new Vector2[uv.Length];
Vector2[] new_uv2 = new Vector2[uv2.Length];
Rect[] uvsInAtlasTexture = new Rect[subMeshCount];
if (new_uv.Length > 0)
{
for (int i = 0; i < subMeshCount; i++)
{
// Get the list of triangles for the current submesh
int[] subMeshTriangles = targetMesh.GetTriangles(i);
if (textureIndex[i] < _combinedResult._combinedMaterials[combinedIndex].uvs.Length)
{
uvsInAtlasTexture[i] = _combinedResult._combinedMaterials[combinedIndex].uvs[textureIndex[i]];
// Target UV calculation, taking into account main map's scale and offset of the original _material
Rect targetUV = new Rect(uvsInAtlasTexture[i].position, uvsInAtlasTexture[i].size);
float factor = scaleFactors[textureIndex[i]];
if (factor > 1)
{
targetUV.size = Vector2.Scale(targetUV.size, Vector2.one / factor);
targetUV.position += new Vector2(uvsInAtlasTexture[i].width * (1 - 1 / factor) / 2f, uvsInAtlasTexture[i].height * (1 - 1 / factor) / 2f);
}
float xMin = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].xMin;
float yMin = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].yMin;
float width = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].width;
float height = _combinedResult._combinedMaterials[combinedIndex].meshUVBounds[textureIndex[i]].height;
for (int j = 0; j < subMeshTriangles.Length; j++)
{
int uvIndex = subMeshTriangles[j];
new_uv[uvIndex] = uv[uvIndex];
// Translate new mesh's uvs so that minimun is at coordinates (0, 0)
new_uv[uvIndex].x -= xMin;
new_uv[uvIndex].y -= yMin;
// Scale (if necessary) new mesh's uvs so that it fits in a (1, 1) square
if (width != 0 && width != 1)
{
new_uv[uvIndex].Scale(new Vector2(1 / width, 1));
}
if (height != 0 && height != 1)
{
new_uv[uvIndex].Scale(new Vector2(1, 1 / height));
}
// Scale and translate new uvs to fit the correct texture in the atlas
new_uv[uvIndex].Scale(targetUV.size);
new_uv[uvIndex] += targetUV.position;
}
}
else
{
Logger.Instance.AddLog("SuperCombiner", "Texture _index exceed packed texture size", Logger.LogLevel.LOG_ERROR);
}
}
}
else
{
Logger.Instance.AddLog("SuperCombiner", "Object " + objectName + " doesn't have uv, combine process may be incorrect. Add uv map with a 3d modeler tool.", Logger.LogLevel.LOG_WARNING);
}
// Assign new uv
targetMesh.uv = new_uv;
// Lightmap
if (_generateUv2)
{
if (uv2 != null && uv2.Length > 0 && _combinedResult._combinedMaterials[combinedIndex].uvs2 != null && _combinedResult._combinedMaterials[combinedIndex].uvs2.Length > 0)
{
for (int l = 0; l < uv2.Length; l++)
{
new_uv2[uv2.Length + l] = new Vector2((uv2[l].x * _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].width) + _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].x, (uv2[l].y * _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].height) + _combinedResult._combinedMaterials[combinedIndex].uvs2[textureIndex[0]].y);
}
targetMesh.uv2 = new_uv2;
}
else
{
// target mesh doesn't have uv2
}
}
return true;
}
// Copy a Mesh into a new instance
public Mesh copyMesh(Mesh mesh, string id = "")
{
Mesh copy = new Mesh();
copy.indexFormat = mesh.indexFormat; // Carefull to set indexFormat before setting subMeshCount!
copy.subMeshCount = mesh.subMeshCount;
copy.vertices = mesh.vertices;
copy.normals = mesh.normals;
copy.uv = mesh.uv;
copy.uv2 = mesh.uv2;
copy.uv3 = mesh.uv3;
copy.uv4 = mesh.uv4;
copy.tangents = mesh.tangents;
copy.bindposes = mesh.bindposes;
copy.boneWeights = mesh.boneWeights;
copy.bounds = mesh.bounds;
copy.colors32 = mesh.colors32;
copy.name = mesh.name;
copy.subMeshCount = mesh.subMeshCount;
for (int i = 0; i < mesh.subMeshCount; i++)
{
copy.SetIndices(mesh.GetIndices(i), mesh.GetTopology(i), i);
copy.SetTriangles(mesh.GetTriangles(i), i);
}
#if UNITY_5_3_OR_NEWER
// Blendshape management
if (mesh.blendShapeCount > 0)
{
Vector3[] deltaVertices = new Vector3[mesh.vertexCount];
Vector3[] deltaNormals = new Vector3[mesh.vertexCount];
Vector3[] deltaTangents = new Vector3[mesh.vertexCount];
for (int s = 0; s < mesh.blendShapeCount; s++)
{
for (int f = 0; f < mesh.GetBlendShapeFrameCount(s); f++)
{
if (!blendShapes.ContainsKey(mesh.GetBlendShapeName(s) + id))
{
// Copy blendShape to the new mesh
mesh.GetBlendShapeFrameVertices(s, f, deltaVertices, deltaNormals, deltaTangents);
copy.AddBlendShapeFrame(
mesh.GetBlendShapeName(s),
mesh.GetBlendShapeFrameWeight(s, f),
deltaVertices, deltaNormals, deltaTangents
);
// Add this blendShape to the list
blendShapes.Add(mesh.GetBlendShapeName(s) + id, new BlendShapeFrame(mesh.GetBlendShapeName(s) + id, mesh.GetBlendShapeFrameWeight(s, f), deltaVertices, deltaNormals, deltaTangents, _vertexOffset));
}
}
}
}
#endif
return copy;
}
// Copy a new mesh and assign it to destination
private void CopyNewMeshesByCombine(Mesh original, Mesh destination)
{
int subMeshCount = original.subMeshCount;
CombineInstance[] combineInstances = new CombineInstance[subMeshCount];
for (int j = 0; j < subMeshCount; j++)
{
combineInstances[j] = new CombineInstance();
combineInstances[j].subMeshIndex = j;
combineInstances[j].mesh = original;
combineInstances[j].transform = Matrix4x4.identity;
}
destination.CombineMeshes(combineInstances, false);
}
public GameObject CombineMeshToSubmeshes(List meshes, MeshOutput output)
{
int vertexOffset = 0;
List indices = new List();
List triangles = new List();
List vertices = new List();
List verticesArray = new List();
List normals = new List();
List tangents = new List();
List colors = new List();
List uv0 = new List();
List uv2 = new List();
for (int i = 0; i < meshes.Count; i++)
{
// Indices
indices.Add(meshes[i].GetIndices(0));
// Triangles
int[] trianglesTmp = meshes[i].GetTriangles(0);
for (int j = 0; j < trianglesTmp.Length; j++)
{
trianglesTmp[j] = trianglesTmp[j] + vertexOffset;
}
triangles.Add(trianglesTmp);
// Vertices
List vertexTmp = new List();
meshes[i].GetVertices(vertexTmp);
vertices.AddRange(vertexTmp);
vertexOffset += vertexTmp.Count;
// Normals
List normalsTmp = new List();
meshes[i].GetNormals(normalsTmp);
normals.AddRange(normalsTmp);
// Tangents
List tangentsTmp = new List();
meshes[i].GetTangents(tangentsTmp);
tangents.AddRange(tangentsTmp);
// Colors
List colorsTmp = new List();
meshes[i].GetColors(colorsTmp);
colors.AddRange(colorsTmp);
// UVs
List uvTmp = new List();
meshes[i].GetUVs(0, uvTmp);
uv0.AddRange(uvTmp);
meshes[i].GetUVs(1, uvTmp); // channel 1 is uv2
uv2.AddRange(uvTmp);
}
GameObject go = new GameObject(_sessionName);
Mesh mesh = new Mesh();
if (output == LunarCatsStudio.SuperCombiner.MeshOutput.Mesh)
{
MeshFilter meshFilter = go.AddComponent();
meshFilter.sharedMesh = mesh;
}
else
{
SkinnedMeshRenderer skinnedMeshRenderer = go.AddComponent();
skinnedMeshRenderer.sharedMesh = mesh;
}
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.name = _sessionName + "_mesh";
mesh.subMeshCount = _combinedResult.GetMaterialGroupCount();
mesh.SetVertices(vertices);
mesh.SetNormals(normals);
mesh.SetTangents(tangents);
mesh.SetColors(colors);
mesh.SetUVs(0, uv0);
mesh.SetUVs(1, uv2);
for (int i = 0; i < indices.Count; i++)
{
mesh.SetIndices(indices[i], mesh.GetTopology(0), i);
mesh.SetTriangles(triangles[i], i);
}
mesh.RecalculateBounds();
#if UNITY_EDITOR
MeshUtility.Optimize(mesh);
#endif
if (output == LunarCatsStudio.SuperCombiner.MeshOutput.Mesh)
{
MeshRenderer meshRenderer = go.AddComponent();
meshRenderer.sharedMaterials = _combinedResult.GetCombinedMaterials().ToArray();
}
else
{
SkinnedMeshRenderer skinnedmeshRenderer = go.GetComponent();
skinnedmeshRenderer.sharedMaterials = _combinedResult.GetCombinedMaterials().ToArray();
}
return go;
}
}
}