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(); }