using UnityEngine; using UnityEditor; using System; using System.Collections; using System.Collections.Generic; using System.IO; using DigitalOpus.MB.Core; using System.Text.RegularExpressions; namespace DigitalOpus.MB.MBEditor { public static class MB_BatchPrefabBakerEditorFunctions { public static int EvalVersionPrefabLimit { get { return 5; } } public static void CreateEmptyOutputPrefabs(string outputFolder, MB3_BatchPrefabBaker target) { string errorMessage; bool isValidPath; MB3_MeshBakerEditorFunctionsCore.SanitizeAndMakeFullPathRelativeToAssetsFolderAndValidate(outputFolder, out errorMessage, out isValidPath); if (!isValidPath) { Debug.LogError(errorMessage); return; } HashSet srcPrefabs = new HashSet(); if (outputFolder.StartsWith("Assets")) outputFolder = MB_BatchPrefabBakerEditorFunctionsCore.ConvertProjectRelativePathToFullPath(outputFolder); int numCreated = 0; int numSkippedSrcNull = 0; int numSkippedSrcDuplicate = 0; int numSkippedAlreadyExisted = 0; MB3_BatchPrefabBaker prefabBaker = (MB3_BatchPrefabBaker)target; for (int i = 0; i < prefabBaker.prefabRows.Length; i++) { if (prefabBaker.prefabRows[i].sourcePrefab != null) { if (srcPrefabs.Contains(prefabBaker.prefabRows[i].sourcePrefab)) { Debug.LogError($"Skipping row {i} because the source prefab was a duplicate of a previous row."); numSkippedSrcDuplicate++; } else { srcPrefabs.Add(prefabBaker.prefabRows[i].sourcePrefab); if (prefabBaker.prefabRows[i].resultPrefab == null) { string outName = outputFolder + "/" + prefabBaker.prefabRows[i].sourcePrefab.name + ".prefab"; outName = outName.Replace(Application.dataPath, ""); outName = "Assets" + outName; outName = AssetDatabase.GenerateUniqueAssetPath(outName); GameObject go = new GameObject(prefabBaker.prefabRows[i].sourcePrefab.name); prefabBaker.prefabRows[i].resultPrefab = MBVersionEditor.PrefabUtility_CreatePrefab(outName, go); GameObject.DestroyImmediate(go); numCreated++; } else { numSkippedAlreadyExisted++; } } } else { numSkippedSrcNull++; } } string msg = String.Format("Created {0} prefabs. Skipped {1} because source prefab was null. Skipped {2} because source prefab was a duplicate. Skipped {3} because the result prefab was already assigned", numCreated, numSkippedSrcNull, numSkippedSrcDuplicate, numSkippedAlreadyExisted); if (numSkippedSrcNull > 0 || numSkippedAlreadyExisted > 0) { Debug.LogError(msg); } else { Debug.Log(msg); } } public static void PopulatePrefabRowsFromTextureBaker(MB3_BatchPrefabBaker prefabBaker) { MB3_TextureBaker texBaker = prefabBaker.GetComponent(); List newPrefabs = new List(); List gos = texBaker.GetObjectsToCombine(); for (int i = 0; i < gos.Count; i++) { GameObject go = (GameObject)MBVersionEditor.PrefabUtility_FindPrefabRoot(gos[i]); UnityEngine.Object obj = MBVersionEditor.PrefabUtility_GetCorrespondingObjectFromSource(go); if (obj != null && obj is GameObject) { if (!newPrefabs.Contains((GameObject)obj)) newPrefabs.Add((GameObject)obj); } else { Debug.LogWarning(String.Format("Object {0} did not have a prefab", gos[i])); } } // Remove prefabs that are already in the list of batch prefab baker's prefabs. { List tmpNewPrefabs = new List(); for (int i = 0; i < newPrefabs.Count; i++) { bool found = false; for (int j = 0; j < prefabBaker.prefabRows.Length; j++) { if (prefabBaker.prefabRows[j].sourcePrefab == newPrefabs[i]) { found = true; break; } } if (!found) { tmpNewPrefabs.Add(newPrefabs[i]); } } newPrefabs = tmpNewPrefabs; } List newRows = new List(); if (prefabBaker.prefabRows == null) prefabBaker.prefabRows = new MB3_BatchPrefabBaker.MB3_PrefabBakerRow[0]; newRows.AddRange(prefabBaker.prefabRows); for (int i = 0; i < newPrefabs.Count; i++) { MB3_BatchPrefabBaker.MB3_PrefabBakerRow row = new MB3_BatchPrefabBaker.MB3_PrefabBakerRow(); row.sourcePrefab = newPrefabs[i]; newRows.Add(row); } Undo.RecordObject(prefabBaker, "Populate prefab rows"); prefabBaker.prefabRows = newRows.ToArray(); } public static void BakePrefabs(MB3_BatchPrefabBaker pb, bool doReplaceTargetPrefab) { if (pb.LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("Batch baking prefabs"); if (MB3_MeshCombiner.EVAL_VERSION) { int numPrefabsLimit = EvalVersionPrefabLimit; if (pb.prefabRows.Length > numPrefabsLimit) { Debug.LogError("The free version of mesh baker is limited to batch baking " + numPrefabsLimit + " prefabs. The full version has no limit on the number of prefabs that can be baked. Delete the extra prefab rows before baking."); return; } } if (Application.isPlaying) { Debug.LogError("The BatchPrefabBaker cannot be run in play mode."); return; } MB3_MeshBaker mb = pb.GetComponent(); mb.UpgradeToCurrentVersionIfNecessary(); 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; } int numResultMats = mb.textureBakeResults.NumResultMaterials(); for (int i = 0; i < numResultMats; i++) { if (mb.textureBakeResults.GetCombinedMaterialForSubmesh(i) == null) { Debug.LogError("The texture bake result had a null result material on submesh " + i + ". Try re-baking textures"); return; } } if (mb.meshCombiner.outputOption != MB2_OutputOptions.bakeMeshAssetsInPlace) { mb.meshCombiner.outputOption = MB2_OutputOptions.bakeMeshAssetsInPlace; } MB2_TextureBakeResults tbr = mb.textureBakeResults; HashSet sourceMeshes = new HashSet(); HashSet allResultMeshes = new HashSet(); //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 (MBVersionEditor.PrefabUtility_GetPrefabType(pb.prefabRows[i].sourcePrefab) != MB_PrefabType.modelPrefabAsset && MBVersionEditor.PrefabUtility_GetPrefabType(pb.prefabRows[i].sourcePrefab) != MB_PrefabType.prefabAsset) { Debug.LogError("Row " + i + " source prefab is not a prefab asset "); return; } if (MBVersionEditor.PrefabUtility_GetPrefabType(pb.prefabRows[i].resultPrefab) != MB_PrefabType.modelPrefabAsset && MBVersionEditor.PrefabUtility_GetPrefabType(pb.prefabRows[i].resultPrefab) != MB_PrefabType.prefabAsset) { Debug.LogError("Row " + i + " result prefab is not a prefab asset"); return; } GameObject so = (GameObject)GameObject.Instantiate(pb.prefabRows[i].sourcePrefab); GameObject ro = (GameObject)GameObject.Instantiate(pb.prefabRows[i].resultPrefab); Renderer[] rs = (Renderer[])so.GetComponentsInChildren(true); for (int j = 0; j < rs.Length; j++) { if (MB_BatchPrefabBakerEditorFunctionsCore.IsGoodToBake(rs[j], tbr)) { sourceMeshes.Add(MB_Utility.GetMesh(rs[j].gameObject)); } } rs = ro.GetComponentsInChildren(true); 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); } } } GameObject.DestroyImmediate(so); //todo should cache these and have a proper cleanup at end GameObject.DestroyImmediate(ro); } sourceMeshes.IntersectWith(allResultMeshes); HashSet 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; } List unityTransforms = new List(); // Bake the meshes using the meshBaker component one prefab at a time // Version check for (int prefabIdx = 0; prefabIdx < pb.prefabRows.Length; prefabIdx++) { if (doReplaceTargetPrefab) { MB_BatchPrefabBakerEditorFunctionsCore._ProcessPrefabRowReplaceTargetPrefab(pb.LOG_LEVEL, pb.prefabRows[prefabIdx].sourcePrefab, pb.prefabRows[prefabIdx].resultPrefab, tbr, unityTransforms, (MB3_MeshCombinerSingle) mb.meshCombiner); } else { MB_BatchPrefabBakerEditorFunctionsCore._ProcessPrefabRowOnlyMeshesAndMaterials(pb.LOG_LEVEL, pb.prefabRows[prefabIdx].sourcePrefab, pb.prefabRows[prefabIdx].resultPrefab, tbr, unityTransforms, (MB3_MeshCombinerSingle) mb.meshCombiner); } } AssetDatabase.Refresh(); mb.ClearMesh(); } } }