public static SMesh BakeMesh(Transform transform, Mesh mesh, bool rendererEnabled, SVector3Array customVertices = null)
 {
     SMesh surrogate = new SMesh(transform, mesh.name, mesh.GetInstanceID(), transform.gameObject.activeInHierarchy && rendererEnabled);
     surrogate.header.type = SMesh.Type.full;
     CopyMeshData(mesh, surrogate, customVertices);
     return surrogate;
 }
 public static bool CheckMeshKeyFrame(SMesh mesh)
 {
     return
         mesh != null &&
         mesh.vertices != null &&
         mesh.vertices.count > 0 &&
         mesh.triangles != null &&
         mesh.triangles.Length > 0;
 }
        private void OnDestroy(){
            if (this.mesh != null)
                UnityEngine.Object.DestroyImmediate(this.mesh);

            DisposeMaterials();
            materials = null;
            mesh = null;
            meshFilter = null;
            keyframeMesh = null;
            previousMesh = null;
        }
        public void GetResultEOF(ref int streamPriority, GameObject go, MeshCaptureMethod captureMethod, bool forceKeyframe, List<SMesh> storeTo)
        {
            MeshFilter[] filters = go.GetComponentsInChildren<MeshFilter>(true);
            //Debug.Log("found: " + go.name + ", count: " + filters.Length);

            foreach (MeshFilter filter in filters) {
                MeshRenderer renderer = filter.gameObject.GetComponent<MeshRenderer>();

                if (renderer == null)
                    continue;

                Mesh mesh = null;
                SMesh surrogate = null;
                switch (captureMethod) {
                    case MeshCaptureMethod.smartMesh:
                        mesh = SceneHierarchyCache.GetMesh(filter);
                        //mesh = filter.sharedMesh != null ? filter.sharedMesh : filter.mesh;
                        if (mesh != null) {
                            if (forceKeyframe) {
                                surrogate = MeshGrabberHelper.BakeMesh(filter.transform, mesh, renderer.enabled);
                                streamPriority ++;
                            } else {
                                surrogate = new SMesh(filter.transform, mesh.name, mesh.GetInstanceID(), filter.gameObject.activeInHierarchy && renderer.enabled);
                            }
                        } else {
                            if (vBug.settings.general.debugMode)
                                Debug.LogWarning("Mesh == Null");
                        }
                        break;

                    case MeshCaptureMethod.fullMesh:
                        mesh = filter.sharedMesh != null ? filter.sharedMesh : filter.mesh;
                        if (mesh != null) {
                            surrogate = MeshGrabberHelper.BakeMesh(filter.transform, mesh, renderer.enabled);
                            streamPriority++;
                        } else {
                            if (vBug.settings.general.debugMode)
                                Debug.LogWarning("Mesh == Null");
                        }
                        break;
                }

                if (surrogate != null) {
                    if (surrogate.subMeshes != null)
                        surrogate.subMeshes.materialIDs = MaterialDataGrabber.RegisterMaterials(renderer);

                    storeTo.Add(surrogate);
                } else {
                    if (vBug.settings.general.debugMode)
                        Debug.Log(go.name + "_" + filter.name + " has no SMesh!!! " + captureMethod + ", changed? " + MeshDataChangeDetector.DetectChange(filter.GetInstanceID(), mesh, filter.transform));
                }
            }
        }
        public static SMesh BakeBonesOnly(SkinnedMeshRenderer source)
        {
            if (source == null)
                return null;

            Mesh mesh = SceneHierarchyCache.GetMesh(source);
            if (mesh == null)
                return null;

            Renderer renderer = source.gameObject.GetComponent<Renderer>();
            SMesh surrogate = new SMesh(source.transform, mesh.name, mesh.GetInstanceID(), source.gameObject.activeInHierarchy && (renderer != null && renderer.enabled));
            surrogate.header.type = SMesh.Type.bonesOnly;
            CopyBoneTransform(source, surrogate);

            return surrogate;
        }
        public void SetCurrentFrame(int frameNumber){
            if (frameNumber == currentFrameNumber)
                return;

            SMesh currentMesh = GetClosestAvailableMesh(frameNumber, false, vBugEditorSettings.PlaybackMeshSearchRange);
            if (SceneVisualsHelper.CheckMeshKeyFrame(currentMesh)) //if we can use this as a keyframe-mesh
                keyframeMesh = currentMesh;

            if(keyframeMesh == null)//Check if not previously set
                keyframeMesh = GetClosestAvailableMesh(frameNumber, true, vBugEditorSettings.PlaybackMeshSearchRange); //if unusable and still null

            if (currentMesh == null || keyframeMesh == null)
                return;

            if (DetectChanges(currentMesh))
                ApplyMeshData(frameNumber, currentMesh);

            currentFrameNumber = frameNumber;
            previousMesh = currentMesh;
        }
        private static void CopyMeshData(Mesh mesh, SMesh destination, SVector3Array customVertices = null)
        {
            destination.triangles = mesh.triangles;
            destination.subMeshes = new SSubMeshArray(mesh);

            if (customVertices != null) {
                destination.vertices = customVertices;
            } else {
                destination.vertices = new SVector3Array(mesh.vertices);
            }

            if (mesh.uv != null)
                destination.uv = new SVector2Array(mesh.uv);

            #if UNITY_4_6
            if (mesh.uv1 != null)
                destination.uv1 = new SVector2Array(mesh.uv1);
            #endif
            if (mesh.uv2 != null)
                destination.uv2 = new SVector2Array(mesh.uv2);

            #if !UNITY_4_6
            if (mesh.uv3 != null)
                destination.uv3 = new SVector2Array(mesh.uv3);

            if (mesh.uv4 != null)
                destination.uv4 = new SVector2Array(mesh.uv4);
            #endif
        }
        private static void CopyBoneWeights(Mesh source, SMesh destination)
        {
            BoneWeight[] weights = SceneHierarchyCache.GetBoneWeights(source);
            if (weights == null)
                return;

            destination.boneWeights = new SBoneWeightArray(weights);
        }
        private static void CopyBoneTransform(SkinnedMeshRenderer source, SMesh destination)
        {
            Transform[] bones = source.bones;
            if (bones == null)
                return;

            int rootID = -1;
            if (source.rootBone != null)
                source.rootBone.GetInstanceID();

            destination.bones = new SBonesArray(bones, rootID);
        }
        private Vector3[] GetMeshVertices(SMesh mesh)
        {
            int vMax = mesh.vertices.count;
            Vector3[] result = new Vector3[vMax];
            int v = vMax;
            while (--v > -1)
                result[v] = keyframeMesh.vertices.GetVertex(v);

            return result;
        }
        public Vector3[] ApplyBoneWeights(SMesh keyframeMesh, SMesh currentMesh)
        {
            if (keyframeMesh == null || currentMesh == null) {
                if (vBugEditorSettings.DebugMode)
                    Debug.LogError("Error: keyframeMesh || currentMesh null: " + currentMesh + ", " + keyframeMesh);
                return null;
            }

            //--------------- Get Bones Matrix --------------------
            SBoneWeightArray boneWeights = (keyframeMesh.boneWeights != null && keyframeMesh.boneWeights.count > 0) ? keyframeMesh.boneWeights : currentMesh.boneWeights;
            if (boneWeights == null) {
                if (vBugEditorSettings.DebugMode)
                    Debug.LogError("Error @ " + currentMesh.header.parentName + ": boneWeight null");
                return null;
            }

            int bMax = boneWeights.count;
            BoneWeight[] weights = new BoneWeight[bMax];
            int b = bMax;
            while (--b > -1)
                weights[b] = boneWeights.GetBoneWeight(b);
            //--------------- Get Bones Matrix --------------------


            //--------------- Init --------------------
            if (currentMesh.bones == null || currentMesh.bones.count == 0 || weights.Length == 0) {
                if (vBugEditorSettings.DebugMode)
                    Debug.LogError("Error @ " + currentMesh.header.parentName + ": currentMesh.bones null || currentMesh.bones.count == 0 || weights.Length == 0"); 
                return null;
            }

            int vMax = keyframeMesh.vertices.count;
            Vector3[] result = new Vector3[vMax];
            Vector3[] vertices = GetMeshVertices(keyframeMesh);

            if (weights.Length != vertices.Length || keyframeMesh.bindPoses.count != currentMesh.bones.count) {// should not happen, but anyways
                if (vBugEditorSettings.DebugMode)
                    Debug.LogError("Error: " + weights.Length + ", " + currentMesh.bindPoses.count + ", " + currentMesh.bones.count);
                return ApplyMeshMatrix(keyframeMesh, currentMesh);
            }

            Matrix4x4[] boneMatrices = new Matrix4x4[currentMesh.bones.count];
            int i = currentMesh.bones.count;
            while (--i > -1)
                boneMatrices[i] = currentMesh.bones.matrices.GetMatrix(i) * keyframeMesh.bindPoses.GetMatrix(i);

            i = 0;
            int iMax = vertices.Length;
            //--------------- Init --------------------


            //--------------- Apply influences --------------------
            while (i < iMax)
            {
                BoneWeight boneWeight = weights[i];
                Vector3 oV = vertices[i];
                Vector3 v = oV;
                float wTotal = 0f;
                float weight0 = boneWeight.weight0;
                float weight1 = boneWeight.weight1;
                float weight2 = boneWeight.weight2;
                float weight3 = boneWeight.weight3;

                if (weight0 > 0f)
                {
                    v = boneMatrices[boneWeight.boneIndex0].MultiplyPoint3x4(oV) * weight0;
                    wTotal = weight0;
                }
                if (weight1 > 0f)
                {
                    v += boneMatrices[boneWeight.boneIndex1].MultiplyPoint3x4(oV) * weight1;
                    wTotal += weight1;
                }
                if (weight2 > 0f)
                {
                    v += boneMatrices[boneWeight.boneIndex2].MultiplyPoint3x4(oV) * weight2;
                    wTotal += weight2;
                }
                if (weight3 > 0f)
                {
                    v += boneMatrices[boneWeight.boneIndex3].MultiplyPoint3x4(oV) * weight3;
                    wTotal += weight3;
                }
                if (wTotal < 1f)
                    v /= wTotal;

                result[i++] = v;
            }
            //--------------- Apply influences --------------------


            return result;
        }
        private Vector3[] ApplyMeshMatrix(SMesh keyframeMesh, SMesh currentMesh)
        {
            int vMax = keyframeMesh.vertices.count;
            Vector3[] result = new Vector3[vMax];
            Matrix4x4 matrix = currentMesh.header.matrix;
            int v = vMax;
            while (--v > -1)
                result[v] = matrix.MultiplyPoint(keyframeMesh.vertices.GetVertex(v));

            return result;
        }
        //--------------------------------------- DETECT CHANGE PERFORMANCE OPTIMIZE --------------------------------------
        //--------------------------------------- DETECT CHANGE PERFORMANCE OPTIMIZE --------------------------------------
			










        //--------------------------------------- APPLY BONES TO VERTICES --------------------------------------
        //--------------------------------------- APPLY BONES TO VERTICES --------------------------------------
        #region APPLY BONES TO VERTICES



        private void ApplyMeshData(int frameNumber, SMesh currentMesh) {
            if (!currentMesh.header.activeInHierarchy) {
                isEnabled = false;
            } else {
                isEnabled = true;
                int vMax = keyframeMesh.vertices.count;
                
                //--------------- Vertices --------------------
                Vector3[] vertices = new Vector3[vMax];
                if (currentMesh.header.type == SMesh.Type.full || currentMesh.bones == null || currentMesh.bones.count == 0) {
                    vertices = ApplyMeshMatrix(keyframeMesh, currentMesh);
                } else {
                    vertices = ApplyBoneWeights(keyframeMesh, currentMesh);
                }
                //--------------- Vertices --------------------


                if (vertices != null && vertices.Length > 0) {
                    //--------------- Set mesh-data --------------------
                    if (this.mesh != null) {
                        this.mesh.Clear();
                        this.mesh.vertices = vertices;
                        this.mesh.triangles = keyframeMesh.triangles;

                        if (keyframeMesh.uv != null && keyframeMesh.uv.count > 0)
                            this.mesh.uv = keyframeMesh.uv.GetVertexArray();
#if UNITY_4_6
                        if (keyframeMesh.uv1 != null && keyframeMesh.uv1.count > 0)
                            this.mesh.uv1 = keyframeMesh.uv1.GetVertexArray();
#endif
                        if (keyframeMesh.uv2 != null && keyframeMesh.uv2.count > 0)
                            this.mesh.uv2 = keyframeMesh.uv2.GetVertexArray();
#if !UNITY_4_6
                        if (keyframeMesh.uv3 != null && keyframeMesh.uv3.count > 0)
                            this.mesh.uv3 = keyframeMesh.uv3.GetVertexArray();
                        if (keyframeMesh.uv4 != null && keyframeMesh.uv4.count > 0)
                            this.mesh.uv4 = keyframeMesh.uv4.GetVertexArray();
#endif

                        this.mesh.RecalculateNormals();
                        this.mesh.tangents = CalculateCheapTangents(keyframeMesh.triangles, vertices, mesh.normals);
                        //this.mesh.Optimize();
                    }
                    //--------------- Set mesh-data --------------------


                    //--------------- Set submesh-data --------------------
                    if (keyframeMesh.subMeshes != null) {
                        if (keyframeMesh.subMeshes.triangles != null) {
                            int i = keyframeMesh.subMeshes.triangles.Length;
                            this.mesh.subMeshCount = i;

                            while (--i > -1)
                                this.mesh.SetTriangles(keyframeMesh.subMeshes.triangles[i], i); 
                        }

                        if (keyframeMesh.subMeshes.materialIDs != null) {
                            DisposeMaterials();
                            Material[] materials = EditorMaterialHelper.GetClosestAvailableMaterialsByID(keyframeMesh.subMeshes.materialIDs, frameNumber);
                            if (materials != null && materials.Length > 0)
                                this.meshRenderer.sharedMaterials = materials;

                            EditorMaterialHelper.SetFallbackMaterial(this.meshRenderer);
                        }
                    } 
                    //--------------- Set submesh-data --------------------


                    //--------------- Set currentframe boundingbox for clipping --------------------
                    if (!boundsPerFrame.ContainsKey(frameNumber))
                        AddMeshBounds(frameNumber, vertices);
                    //--------------- Set currentframe boundingbox for clipping --------------------
			
                }
            }
            this.meshRenderer.enabled = isVisible && isEnabled;
        }
        //--------------------------------------- Get mesh data --------------------------------------
        //--------------------------------------- Get mesh data --------------------------------------










        //--------------------------------------- DETECT CHANGE PERFORMANCE OPTIMIZE --------------------------------------
        //--------------------------------------- DETECT CHANGE PERFORMANCE OPTIMIZE --------------------------------------
        #region DETECT CHANGE PERFORMANCE OPTIMIZE


        private bool DetectChanges(SMesh currentMesh) {
            if (currentMesh == null)
                return false;

            if (previousMesh == null || currentMesh.header.type == SMesh.Type.full || currentMesh == keyframeMesh) //if first frame, full meshcapture or equal to the keyframe... then update!
                return true;

            if (previousMesh.header.matrix != currentMesh.header.matrix) // if position changed
                return true;

            if (previousMesh.bones != null && currentMesh.bones != null && previousMesh.bones.count == currentMesh.bones.count) {
                int i = previousMesh.bones.count;
                while (--i > -1) {
                    if (previousMesh.bones.matrices.GetMatrix(i) != currentMesh.bones.matrices.GetMatrix(i)) //if bones differ, it changed!
                        return true;
                }

                return false; //At this point, all the bones are the same!

            } else { // no bone data to test, must be a meshfilter

                if (
                    (previousMesh.vertices.count != currentMesh.vertices.count) ||
                    (previousMesh.triangles.Length != currentMesh.triangles.Length)) {
                        return true;
                }
            }

            return false; //Nothing to see, so nothing changed
        }
        public static SMesh Deserialize(byte[] input)
        {
            SMesh result = new SMesh();
            ComposedByteStream stream = ComposedByteStream.FromByteArray(input);

            result.header = SMesh.HeaderData.Deserialize(stream.ReadNextStream<byte>());
            result.subMeshes = SSubMeshArray.Deserialize(stream.ReadNextStream<byte>());

            result.triangles = stream.ReadNextStream<int>();
            result.vertices = new SVector3Array(stream.ReadNextStream<float>());
            result.uv = new SVector2Array(stream.ReadNextStream<float>());
            result.uv1 = new SVector2Array(stream.ReadNextStream<float>());
            result.uv2 = new SVector2Array(stream.ReadNextStream<float>());
            result.uv3 = new SVector2Array(stream.ReadNextStream<float>());
            result.uv4 = new SVector2Array(stream.ReadNextStream<float>());

            result.bindPoses = new SMatrix4x4Array(stream.ReadNextStream<float>());
            result.bones = SBonesArray.Deserialize(stream.ReadNextStream<byte>());
            result.boneWeights = SBoneWeightArray.Deserialize(stream.ReadNextStream<byte>());

            stream.Dispose();
            return result;
        }
        //--------------- Sub classes / enums --------------------
        //--------------- Serialize / Deserialize --------------------
        public static byte[] Serialize(SMesh input)
        {
            ComposedByteStream stream = ComposedByteStream.FetchStream(10);
            stream.AddStream(SMesh.HeaderData.Serialize(input.header));
            stream.AddStream(SSubMeshArray.Serialize(input.subMeshes));

            stream.AddStream(input.triangles);
            stream.AddStream(input.vertices != null ? input.vertices.vectorData : null);
            stream.AddStream(input.uv != null ? input.uv.vectorData : null);
            stream.AddStream(input.uv1 != null ? input.uv1.vectorData : null);
            stream.AddStream(input.uv2 != null ? input.uv2.vectorData : null);
            stream.AddStream(input.uv3 != null ? input.uv3.vectorData : null);
            stream.AddStream(input.uv4 != null ? input.uv4.vectorData : null);

            stream.AddStream(input.bindPoses != null ? input.bindPoses.matrixData : null);
            stream.AddStream(SBonesArray.Serialize(input.bones));
            stream.AddStream(SBoneWeightArray.Serialize(input.boneWeights));
            return stream.Compose();
        }
        private static void CopyBoneBindPoses(Mesh source, SMesh destination)
        {
            Matrix4x4[] bindPoses = source.bindposes;
            if (bindPoses == null)
                return;

            destination.bindPoses = new SMatrix4x4Array(bindPoses);
        }