/// <summary>
        /// Add a new CombinedMesh
        /// </summary>
        /// <param name="combinedMesh"></param>
        /// <param name="combineInstanceID"></param>
        /// <param name="combinedIndex"></param>
        public void AddCombinedMesh(Mesh combinedMesh, CombineInstanceID combineInstanceID, int combinedIndex)
        {
            MeshCombined meshResult = new MeshCombined();

            int vertexIndex   = 0;
            int triangleIndex = 0;

            for (int i = 0; i < combineInstanceID._combineInstances.Count; i++)
            {
                if (!meshResult.instanceIds.Contains(combineInstanceID._instancesID[i]))
                {
                    vertexIndex   += combineInstanceID._combineInstances[i].mesh.vertexCount;
                    triangleIndex += combineInstanceID._combineInstances[i].mesh.triangles.Length;
                    meshResult.names.Add(combineInstanceID._names[i]);
                    meshResult.instanceIds.Add(combineInstanceID._instancesID[i]);
                    meshResult.indexes.Add(new CombineInstanceIndexes(combineInstanceID._combineInstances[i].mesh, vertexIndex, triangleIndex));
                }
            }

            _meshResults.Add(meshResult);
        }
        /// <summary>
        /// Create a new GameObject based on the CombineInstance list
        /// </summary>
        /// <param name="instances"></param>
        /// <param name="parent"></param>
        /// <param name="number"></param>
        /// <returns></returns>
        private GameObject CreateCombinedSkinnedMeshGameObject(CombineInstanceID instances, Transform parent, int number, int combinedIndex)
        {
            GameObject          combined            = new GameObject(_sessionName + number.ToString());
            SkinnedMeshRenderer skinnedMeshRenderer = combined.AddComponent <SkinnedMeshRenderer>();

            skinnedMeshRenderer.sharedMaterial  = _combinedResult._combinedMaterials[combinedIndex].material;
            skinnedMeshRenderer.sharedMesh      = new Mesh();
            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);
        }
        /// <summary>
        /// Create a new GameObject based on the CombineInstance list.
        /// Set its MeshFilter and MeshRenderer to the new combined Meshe/Material
        /// </summary>
        /// <param name="instances"></param>
        /// <param name="parent"></param>
        /// <param name="number"></param>
        /// <returns></returns>
        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 <MeshFilter>() != null && parent.GetComponent <MeshRenderer>() != null)
            {
                combined     = parent.gameObject;
                meshFilter   = parent.GetComponent <MeshFilter>();
                meshRenderer = parent.GetComponent <MeshRenderer>();
            }
            else
            {
                combined     = new GameObject(_sessionName + "_" + _combinedResult._combinedMaterials[combinedIndex].displayedIndex + "_" + number.ToString());
                meshFilter   = combined.AddComponent <MeshFilter>();
                meshRenderer = combined.AddComponent <MeshRenderer>();
                combined.transform.SetParent(parent);
                combined.transform.localPosition = Vector3.zero;
            }

            meshRenderer.sharedMaterial = _combinedResult._combinedMaterials[combinedIndex].material;
            meshFilter.mesh             = new Mesh();
            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);
        }
        /// <summary>
        /// Create a new combineInstance based on a new mesh
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="sharedMaterials"></param>
        /// <param name="instanceID"></param>
        /// <param name="name"></param>
        /// <param name="matrix"></param>
        /// <param name="combinedIndex"></param>
        /// <returns></returns>
        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);
        }
 public void AddRange(CombineInstanceID instances)
 {
     _combineInstances.AddRange(instances._combineInstances);
     _instancesID.AddRange(instances._instancesID);
     _names.AddRange(instances._names);
 }
        public List <GameObject> CombineToMeshes(List <MeshRenderer> meshRenderers, List <SkinnedMeshRenderer> skinnedMeshRenderers, Transform parent, int combinedIndex)
        {
            // The list of Meshes created
            List <GameObject> combinedMeshes = new List <GameObject>();
            // 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++)
            {
                for (int j = 0; j < meshRenderers[i].GetComponent <MeshFilter>().sharedMesh.subMeshCount; j++)
                {
                    // Get a copy of the submesh at index j
                    Mesh newMesh = SubmeshSplitter.ExtractSubmesh(meshRenderers[i].GetComponent <MeshFilter>().sharedMesh, j);

                    verticesCount += newMesh.vertexCount;
                    if (verticesCount > _maxVerticesCount)
                    {
                        combinedMeshes.Add(CreateCombinedMeshGameObject(combineInstances, parent, combinedGameObjectCount, combinedIndex));
                        combineInstances.Clear();
                        combinedGameObjectCount++;
                        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++)
            {
                for (int j = 0; j < meshRenderers[i].GetComponent <MeshFilter>().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;
                    if (verticesCount > _maxVerticesCount && combineInstances.Count() > 0)
                    {
                        combinedMeshes.Add(CreateCombinedMeshGameObject(combineInstances, parent, combinedGameObjectCount, combinedIndex));
                        combineInstances.Clear();
                        combinedGameObjectCount++;
                        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 <GameObject> CombineToSkinnedMeshes(List <MeshRenderer> meshRenderers, List <SkinnedMeshRenderer> skinnedMeshRenderers, Transform parent, int combinedIndex)
        {
            // The list of Meshes created
            List <GameObject> combinedMeshes = new List <GameObject>();
            // The list of _combineInstances
            CombineInstanceID combineInstances = new CombineInstanceID();

            int verticesCount           = 0;
            int combinedGameObjectCount = 0;

            /*
             *          / Skinned mesh parameters
             */
            // List of bone weight
            List <BoneWeight> boneWeights = new List <BoneWeight>();
            // List of bones
            List <Transform> bones = new List <Transform>();
            // List of bindposes
            List <Matrix4x4> bindposes = new List <Matrix4x4>();
            // List of original bones mapped to their instanceId
            Dictionary <int, Transform> originalBones = new Dictionary <int, Transform>();
            // Link original bone instanceId to the new created bones
            Dictionary <int, Transform> originToNewBoneMap = new Dictionary <int, Transform>();
            // 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 <MeshRenderer>();

                // 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 <Animator>())
            {
                Transform[] children = anim.GetComponentsInChildren <Transform>();
                // 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;
                if (verticesCount > _maxVerticesCount && combineInstances.Count() > 0)
                {
                    // Create the new GameObject
                    GameObject combinedGameObject = CreateCombinedSkinnedMeshGameObject(combineInstances, parent, combinedGameObjectCount, combinedIndex);
                    // Assign skinnedMesh parameters values
                    SkinnedMeshRenderer sk = combinedGameObject.GetComponent <SkinnedMeshRenderer>();
                    AssignParametersToSkinnedMesh(sk, bones, boneWeights, bindposes);
                    combinedMeshes.Add(combinedGameObject);
                    boneOffset = 0;

                    combineInstances.Clear();
                    combinedGameObjectCount++;
                    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 <SkinnedMeshRenderer>();
                AssignParametersToSkinnedMesh(sk, bones, boneWeights, bindposes);
                combinedMeshes.Add(combinedGameObject);
            }

            return(combinedMeshes);
        }