private static bool IsGoodToBake(Renderer r, MB2_TextureBakeResults tbr)
 {
     if (r == null)
     {
         return(false);
     }
     if (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer))
     {
         return(false);
     }
     Material[] mats = r.sharedMaterials;
     for (int i = 0; i < mats.Length; i++)
     {
         if (!tbr.ContainsMaterial(mats[i]))
         {
             Debug.LogWarning("Mesh on " + r + " uses a material " + mats[i] + " that is not in the list of materials. This mesh will not be baked. The original mesh and material will be used in the result prefab.");
             return(false);
         }
     }
     if (MB_Utility.GetMesh(r.gameObject) == null)
     {
         return(false);
     }
     return(true);
 }
    public void _bakePrefabs()
    {
        Debug.Log("Batch baking prefabs");
        if (Application.isPlaying)
        {
            Debug.LogError("The BatchPrefabBaker cannot be run in play mode.");
            return;
        }
        MB3_BatchPrefabBaker pb = (MB3_BatchPrefabBaker)target;
        MB3_MeshBaker        mb = pb.GetComponent <MB3_MeshBaker>();

        if (mb == null)
        {
            Debug.LogError("Prefab baker needs to be attached to a Game Object with a MB3_MeshBaker component.");
            return;
        }

        if (mb.textureBakeResults == null)
        {
            Debug.LogError("Texture Bake Results is not set");
            return;
        }

        if (mb.meshCombiner.outputOption != MB2_OutputOptions.bakeMeshAssetsInPlace)
        {
            mb.meshCombiner.outputOption = MB2_OutputOptions.bakeMeshAssetsInPlace;
        }

        MB2_TextureBakeResults tbr = mb.textureBakeResults;

        HashSet <Mesh> sourceMeshes    = new HashSet <Mesh>();
        HashSet <Mesh> allResultMeshes = new HashSet <Mesh>();

        //validate prefabs
        for (int i = 0; i < pb.prefabRows.Length; i++)
        {
            if (pb.prefabRows[i] == null || pb.prefabRows[i].sourcePrefab == null)
            {
                Debug.LogError("Source Prefab on row " + i + " is not set.");
                return;
            }
            if (pb.prefabRows[i].resultPrefab == null)
            {
                Debug.LogError("Result Prefab on row " + i + " is not set.");
                return;
            }
            for (int j = i + 1; j < pb.prefabRows.Length; j++)
            {
                if (pb.prefabRows[i].sourcePrefab == pb.prefabRows[j].sourcePrefab)
                {
                    Debug.LogError("Rows " + i + " and " + j + " contain the same source prefab");
                    return;
                }
            }
            for (int j = 0; j < pb.prefabRows.Length; j++)
            {
                if (pb.prefabRows[i].sourcePrefab == pb.prefabRows[j].resultPrefab)
                {
                    Debug.LogError("Row " + i + " source prefab is the same as row " + j + " result prefab");
                    return;
                }
            }
            if (PrefabUtility.GetPrefabType(pb.prefabRows[i].sourcePrefab) != PrefabType.ModelPrefab &&
                PrefabUtility.GetPrefabType(pb.prefabRows[i].sourcePrefab) != PrefabType.Prefab)
            {
                Debug.LogError("Row " + i + " source prefab is not a prefab asset");
                return;
            }
            if (PrefabUtility.GetPrefabType(pb.prefabRows[i].resultPrefab) != PrefabType.ModelPrefab &&
                PrefabUtility.GetPrefabType(pb.prefabRows[i].resultPrefab) != PrefabType.Prefab)
            {
                Debug.LogError("Row " + i + " result prefab is not a prefab asset");
                return;
            }

            GameObject so = (GameObject)Instantiate(pb.prefabRows[i].sourcePrefab);
            GameObject ro = (GameObject)Instantiate(pb.prefabRows[i].resultPrefab);
            Renderer[] rs = (Renderer[])so.GetComponentsInChildren <Renderer>();

            for (int j = 0; j < rs.Length; j++)
            {
                if (IsGoodToBake(rs[j], tbr))
                {
                    sourceMeshes.Add(MB_Utility.GetMesh(rs[j].gameObject));
                }
            }
            rs = (Renderer[])ro.GetComponentsInChildren <Renderer>();

            for (int j = 0; j < rs.Length; j++)
            {
                Renderer r = rs[j];
                if (r is MeshRenderer || r is SkinnedMeshRenderer)
                {
                    Mesh m = MB_Utility.GetMesh(r.gameObject);
                    if (m != null)
                    {
                        allResultMeshes.Add(m);
                    }
                }
            }
            DestroyImmediate(so);             //todo should cache these and have a proper cleanup at end
            DestroyImmediate(ro);
        }

        sourceMeshes.IntersectWith(allResultMeshes);
        HashSet <Mesh> sourceMeshesThatAreUsedByResult = sourceMeshes;

        if (sourceMeshesThatAreUsedByResult.Count > 0)
        {
            foreach (Mesh m in sourceMeshesThatAreUsedByResult)
            {
                Debug.LogWarning("Mesh " + m + " is used by both the source and result prefabs. New meshes will be created.");
            }
            //return;
        }

        Dictionary <string, string> createdMeshPaths = new Dictionary <string, string>();

        // Bake the meshes using the meshBaker component one prefab at a time
        for (int prefabIdx = 0; prefabIdx < pb.prefabRows.Length; prefabIdx++)
        {
            Debug.Log("==== Processing Source Prefab " + pb.prefabRows[prefabIdx].sourcePrefab);
            GameObject sceneObj     = (GameObject)Instantiate(pb.prefabRows[prefabIdx].sourcePrefab);
            GameObject resultPrefab = (GameObject)Instantiate(pb.prefabRows[prefabIdx].resultPrefab);

            Renderer[] rs = sceneObj.GetComponentsInChildren <Renderer>();
            if (rs.Length < 1)
            {
                Debug.LogWarning("Prefab " + prefabIdx + " does not have a renderer");
                DestroyImmediate(sceneObj);
                DestroyImmediate(resultPrefab);
                continue;
            }

            List <Mesh>           usedMeshes      = new List <Mesh>();
            List <UnityTransform> unityTransforms = new List <UnityTransform>();
            for (int j = 0; j < rs.Length; j++)
            {
                unityTransforms.Clear();
                Renderer r = rs[j];

                if (!IsGoodToBake(r, tbr))
                {
                    continue;
                }

                //find the corresponding mesh in the result prefab
                string resultFolderPath = AssetDatabase.GetAssetPath(pb.prefabRows[prefabIdx].resultPrefab);
                resultFolderPath = Path.GetDirectoryName(resultFolderPath);

                Mesh      m    = null;
                Transform tRes = FindCorrespondingTransform(sceneObj.transform, r.transform, resultPrefab.transform);
                if (tRes != null)
                {
                    m = MB_Utility.GetMesh(tRes.gameObject);
                }

                //if the meshes on source and result are the same we want to remove mesh so will create a new one
                if (sourceMeshesThatAreUsedByResult.Contains(m))
                {
                    Debug.LogWarning("Source and result prefabs share a mesh. Creating a new mesh for " + m);
                    MB_Utility.SetMesh(tRes.gameObject, null);
                    m = null;
                }


                string meshPath;
                //check that the mesh is an asset and that we have not used it already
                if (m != null && AssetDatabase.IsMainAsset(m.GetInstanceID()) && !usedMeshes.Contains(m))
                {
                    meshPath = AssetDatabase.GetAssetPath(m);
                    if (createdMeshPaths.ContainsKey(meshPath))
                    {
                        Debug.LogWarning("Different result prefabs share a mesh." + meshPath);
                    }
                }
                else                     //create a new mesh asset with a unique name
                {
                    string resultPrefabFilename = AssetDatabase.GetAssetPath(pb.prefabRows[prefabIdx].resultPrefab);
                    resultPrefabFilename = resultPrefabFilename.Substring(0, resultPrefabFilename.Length - ".prefab".Length) + ".asset";
                    meshPath             = AssetDatabase.GenerateUniqueAssetPath(resultPrefabFilename);
                    m = new Mesh();
                    AssetDatabase.CreateAsset(m, meshPath);
                    m = (Mesh)AssetDatabase.LoadAssetAtPath(meshPath, typeof(Mesh));
                }
                Debug.Log("  creating new mesh asset at path " + meshPath);
                if (!createdMeshPaths.ContainsKey(meshPath))
                {
                    createdMeshPaths.Add(meshPath, meshPath);
                }

                // position rotation and scale are baked into combined mesh.
                // Remember all the transforms settings then
                // record transform values to root of hierarchy
                Transform t = r.transform;
                if (t != t.root)
                {
                    do
                    {
                        unityTransforms.Add(new UnityTransform(t));
                        t = t.parent;
                    } while (t != null && t != t.root);
                }
                //add the root
                unityTransforms.Add(new UnityTransform(t.root));

                //position at identity
                for (int k = 0; k < unityTransforms.Count; k++)
                {
                    unityTransforms[k].t.localPosition = Vector3.zero;
                    unityTransforms[k].t.localRotation = Quaternion.identity;
                    unityTransforms[k].t.localScale    = Vector3.one;
                }

                //throw new Exception("");
                //bake the mesh
                mb.ClearMesh();
                MB3_MeshCombiner mc = mb.meshCombiner;
                m = MB3_BakeInPlace.BakeOneMesh((MB3_MeshCombinerSingle)mc, meshPath, r.gameObject);

                //replace the mesh
                if (r is MeshRenderer)
                {
                    MeshFilter mf = r.gameObject.GetComponent <MeshFilter>();
                    mf.sharedMesh = m;
                }
                else                     //skinned mesh
                {
                    SkinnedMeshRenderer smr = r.gameObject.GetComponent <SkinnedMeshRenderer>();
                    smr.sharedMesh = m;
                    smr.bones      = ((SkinnedMeshRenderer)mc.targetRenderer).bones;
                }

                //replace the result material(s)
                if (mb.textureBakeResults.doMultiMaterial)
                {
                    Material[] rss = new Material[mb.textureBakeResults.resultMaterials.Length];
                    for (int k = 0; k < rss.Length; k++)
                    {
                        rss[k] = mb.textureBakeResults.resultMaterials[k].combinedMaterial;
                    }
                    r.sharedMaterials = rss;
                }
                else
                {
                    Material[] originalMats = r.sharedMaterials;
                    Material[] rss          = new Material[originalMats.Length];
                    for (int k = 0; k < originalMats.Length; k++)
                    {
                        if (tbr.ContainsMaterial(originalMats[k]))
                        {
                            rss[k] = mb.textureBakeResults.resultMaterial;
                        }
                        else
                        {
                            rss[k] = originalMats[k];
                        }
                    }

                    r.sharedMaterials = rss;
                }

                //restore the transforms
                for (int k = 0; k < unityTransforms.Count; k++)
                {
                    unityTransforms[k].t.localPosition = unityTransforms[k].p;
                    unityTransforms[k].t.localRotation = unityTransforms[k].q;
                    unityTransforms[k].t.localScale    = unityTransforms[k].s;
                }
            }

            //replace the result prefab with the source object
            //duplicate the sceneObj so we can replace the clone into the prefab, not the source
            GameObject clone = (GameObject)Instantiate(sceneObj);
            PrefabUtility.ReplacePrefab(clone, pb.prefabRows[prefabIdx].resultPrefab, ReplacePrefabOptions.ReplaceNameBased);
            DestroyImmediate(clone);
            DestroyImmediate(sceneObj);
            DestroyImmediate(resultPrefab);
        }
        AssetDatabase.Refresh();
        mb.ClearMesh();
    }