diff options
| author | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
|---|---|---|
| committer | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
| commit | eb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch) | |
| tree | efd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3Worlds/Assets/MeshBaker/Editor | |
| download | unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2 unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip | |
move to self host
Diffstat (limited to 'VRCSDK3Worlds/Assets/MeshBaker/Editor')
85 files changed, 10653 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BatchPrefabBakerEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BatchPrefabBakerEditor.cs new file mode 100644 index 00000000..b2625b79 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BatchPrefabBakerEditor.cs @@ -0,0 +1,229 @@ +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 +{ + [CustomEditor(typeof(MB3_BatchPrefabBaker))] + public class MB3_BatchPrefabBakerEditor : Editor + { + SerializedObject prefabBaker = null; + + GUIContent GUIContentLogLevelContent = new GUIContent("Log Level"); + GUIContent GUIContentBatchBakePrefabReplacePrefab = new GUIContent("Batch Bake Prefabs (Replace Prefab)", + "This will clone the source prefab, replace the meshes in the clone with baked versions and replace the target prefab with the clone.\n\n" + + "IF ANY CHANGES HAD BEEN MADE TO THE TARGET PREFAB, THOSE WILL BE LOST."); + GUIContent GUIContentBatchBakePrefabOnlyMeshesAndMats = new GUIContent("Batch Bake Prefabs (Only Replace Meshes & Materials)", + "This will attempt to match the meshes used by the target prefab to those used by the source prefab. For this to work" + + " well, the source and target prefabs should have the same hierarchy. The meshes and materials in the target prefab will be updated to baked versions. " + + " Modifications to the target prefab other than the meshes and materials will be preserved.\n\n" + + "Check the console for errors after baking the prefabs."); + + SerializedProperty prefabRows, outputFolder, logLevel; + + Color buttonColor = new Color(.8f, .8f, 1f, 1f); + + [MenuItem("GameObject/Create Other/Mesh Baker/Batch Prefab Baker", false, 1000)] + public static void CreateNewBatchPrefabBaker() + { + //if (MB3_MeshCombiner.EVAL_VERSION) + //{ + // Debug.LogError("The prefab baker is only available in the full version of MeshBaker."); + // return; + //} + + MB3_TextureBaker[] mbs = (MB3_TextureBaker[])Editor.FindObjectsOfType(typeof(MB3_TextureBaker)); + // Generate unique name + int largest = 0; + { + Regex regex = new Regex(@"\((\d+)\)$", RegexOptions.Compiled | RegexOptions.CultureInvariant); + try + { + for (int i = 0; i < mbs.Length; i++) + { + Match match = regex.Match(mbs[i].name); + if (match.Success) + { + int val = Convert.ToInt32(match.Groups[1].Value); + if (val >= largest) + largest = val + 1; + } + } + } + catch (Exception e) + { + if (e == null) e = null; //Do nothing supress compiler warning + } + } + + GameObject nmb = new GameObject("BatchPrefabBaker (" + largest + ")"); + nmb.transform.position = Vector3.zero; + + MB3_BatchPrefabBaker bpb = nmb.AddComponent<MB3_BatchPrefabBaker>(); + nmb.AddComponent<MB3_TextureBaker>(); + nmb.AddComponent<MB3_MeshBaker>(); + bpb.prefabRows = new MB3_BatchPrefabBaker.MB3_PrefabBakerRow[0]; + bpb.outputPrefabFolder = ""; + } + + void OnEnable() + { + prefabBaker = new SerializedObject(target); + prefabRows = prefabBaker.FindProperty("prefabRows"); + outputFolder = prefabBaker.FindProperty("outputPrefabFolder"); + logLevel = prefabBaker.FindProperty("LOG_LEVEL"); + } + + void OnDisable() + { + prefabBaker = null; + } + + public override void OnInspectorGUI() + { + prefabBaker.Update(); + + EditorGUILayout.HelpBox( + "This tool speeds up the process of preparing prefabs " + + " for static and dynamic batching. It creates duplicate prefab assets and meshes " + + "that share a combined material. Source assets are not touched.\n\n" + + "1) Create instances of source prefabs to this scene.\n" + + "2) Add these instances to the TextureBaker on this GameObject and bake the textures used by the prefabs.\n" + + "2) Using the BatchPrefabBaker component click 'Populate Prefab Rows From Texture Baker' or manually set up Prefab Rows by dragging to the Prefab Rows list.\n" + + "4) Choose a folder where the result prefabs will be stored and click 'Create Empty Result Prefabs'\n" + + "5) click 'Batch Bake Prefabs'\n" + + "6) Check the console for messages and errors\n" + + "7) (Optional) If you want to compare the source objects to the result objects use the BatchPrefabBaker '...' menu command 'Create Instances For Prefab Rows'. This will create aligned instances of the prefabs in the scene so that it is easy to see any differences.\n", MessageType.Info); + EditorGUILayout.PropertyField(logLevel, GUIContentLogLevelContent); + + EditorGUILayout.PropertyField(prefabRows, true); + + EditorGUILayout.LabelField("Output Folder", EditorStyles.boldLabel); + EditorGUILayout.LabelField(outputFolder.stringValue); + + if (GUILayout.Button("Browse For Output Folder")) + { + string path = EditorUtility.OpenFolderPanel("Browse For Output Folder", "", ""); + path = MB_BatchPrefabBakerEditorFunctions.ConvertFullPathToProjectRelativePath(path); + outputFolder.stringValue = path; + } + + if (GUILayout.Button("Create Empty Result Prefabs")) + { + MB_BatchPrefabBakerEditorFunctions.CreateEmptyOutputPrefabs(outputFolder.stringValue, (MB3_BatchPrefabBaker) target); + } + + Color oldColor = GUI.backgroundColor; + GUI.backgroundColor = buttonColor; + if (GUILayout.Button(GUIContentBatchBakePrefabReplacePrefab)) + { + MB3_BatchPrefabBaker pb = (MB3_BatchPrefabBaker)target; + MB_BatchPrefabBakerEditorFunctions.BakePrefabs(pb, true); + } + if (GUILayout.Button(GUIContentBatchBakePrefabOnlyMeshesAndMats)) + { + MB3_BatchPrefabBaker pb = (MB3_BatchPrefabBaker)target; + MB_BatchPrefabBakerEditorFunctions.BakePrefabs(pb, false); + } + GUI.backgroundColor = oldColor; + + if (GUILayout.Button("Poplate Prefab Rows From Texture Baker")) + { + PopulatePrefabRowsFromTextureBaker((MB3_BatchPrefabBaker)prefabBaker.targetObject); + } + + if (GUILayout.Button("Open Replace Prefabs In Scene Window")) + { + MB3_BatchPrefabBaker pb = (MB3_BatchPrefabBaker)target; + MB_ReplacePrefabsInSceneEditorWindow.ShowWindow(pb.prefabRows); + } + + + prefabBaker.ApplyModifiedProperties(); + prefabBaker.SetIsDifferentCacheDirty(); + } + + public void PopulatePrefabRowsFromTextureBaker(MB3_BatchPrefabBaker prefabBaker) + { + MB3_TextureBaker texBaker = prefabBaker.GetComponent<MB3_TextureBaker>(); + List<GameObject> newPrefabs = new List<GameObject>(); + List<GameObject> gos = texBaker.GetObjectsToCombine(); + for (int i = 0; i < gos.Count; i++) + { + GameObject go = (GameObject)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<GameObject> tmpNewPrefabs = new List<GameObject>(); + 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; + } + + if (MB3_MeshCombiner.EVAL_VERSION) + { + int numPrefabsLimit = MB_BatchPrefabBakerEditorFunctions.EvalVersionPrefabLimit; + int numNew = numPrefabsLimit - prefabBaker.prefabRows.Length; + if (newPrefabs.Count + prefabBaker.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. " + (newPrefabs.Count - numNew) + " prefabs were not added."); + + } + + + for (int i = newPrefabs.Count - 1; i >= numNew; i--) + { + newPrefabs.RemoveAt(i); + } + } + + List<MB3_BatchPrefabBaker.MB3_PrefabBakerRow> newRows = new List<MB3_BatchPrefabBaker.MB3_PrefabBakerRow>(); + 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(); + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BatchPrefabBakerEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BatchPrefabBakerEditor.cs.meta new file mode 100644 index 00000000..fd2728c0 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BatchPrefabBakerEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9eec7d996f61d7245a9c9d169979b8dd +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BoneWeightCopierEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BoneWeightCopierEditor.cs new file mode 100644 index 00000000..3a556b57 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BoneWeightCopierEditor.cs @@ -0,0 +1,255 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2015-2016 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using DigitalOpus.MB.Core; +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + + [CustomEditor(typeof(MB3_BoneWeightCopier))] + public class MB3_BoneWeightCopierEditor : Editor + { + SerializedProperty inputGameObjectProp; + SerializedProperty outputPrefabProp; + SerializedProperty radiusProp; + SerializedProperty seamMeshProp; + SerializedProperty targetMeshesProp; + SerializedProperty outputFolderProp; + + GameObject copyOfInput; + + GUIContent radiusGC = new GUIContent("Radius", "Vertices in each Target Mesh that are within Radius of a vertex in the Seam Mesh will be assigned the same bone weight as the Seam Mesh vertex."); + GUIContent seamMeshGC = new GUIContent("Seam Mesh", "The seam mesh should contain vertices for the seams with correct bone weights. Seams are the vertices that are shared by two skinned meshes that overlap. For example there should be a seam between an arm skinned mesh and a hand skinned mesh."); + + [MenuItem("GameObject/Create Other/Mesh Baker/Bone Weight Copier", false, 1000)] + public static GameObject CreateNewMeshBaker() + { + GameObject nmb = new GameObject("BoneWeightCopier"); + nmb.transform.position = Vector3.zero; + nmb.AddComponent<MB3_BoneWeightCopier>(); + return nmb.gameObject; + } + + + void OnEnable() + { + radiusProp = serializedObject.FindProperty("radius"); + seamMeshProp = serializedObject.FindProperty("seamMesh"); + //targetMeshesProp = serializedObject.FindProperty("targetMeshes"); + outputFolderProp = serializedObject.FindProperty("outputFolder"); + inputGameObjectProp = serializedObject.FindProperty("inputGameObject"); + outputPrefabProp = serializedObject.FindProperty("outputPrefab"); + } + + string ConvertAbsolutePathToUnityPath(string absolutePath) + { + if (absolutePath.StartsWith(Application.dataPath)) + { + return "Assets" + absolutePath.Substring(Application.dataPath.Length); + } + return null; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + MB3_BoneWeightCopier bwc = (MB3_BoneWeightCopier)target; + + + EditorGUILayout.HelpBox("== BETA (please report problems) ==\n\n" + + "This tool helps create skinned mesh parts that can be mix and matched to customize characters. " + + "It adjusts the boneWeights, positions, normals and tangents at the joins to have the same values so there are no tears." + + "\n\n" + + "1) Model the skinned meshes and attach them all to the same rig. The rig may have multiple arms, hands, hair etc...\n" + + "2) At every seam (eg. between an arm mesh and hand mesh) there must be a set of vertices duplicated in both meshes.\n" + + "3) Create one additional mesh that contains another copy of all the geometry. This will be called the Seam Mesh\n" + + "4) Attach it to the rig and adjust the bone weights. This will be the master set of bone weights that will be copied to all the other skinned meshes.\n" + + "5) Mark the seam vertices using the UV channel.\n" + + " verts with UV > (.5,.5) are seam verts\n" + + " verts with UV < (.5,.5) are ignored\n" + + "6) Import the model into Unity and create an instance of it in the scene\n" + + "7) Assign the Seam Mesh to the Seam Mesh field\n" + + "8) Assign the parent game object of all the skinned meshes to the Input Game Object field\n" + + "9) Adjust the radius\n" + + "10) Choose an output folder and save the meshes\n" + + "11) Click 'Copy Bone Weights From Seam Mesh'\n", MessageType.Info); + + EditorGUILayout.PropertyField(radiusProp, radiusGC); + EditorGUILayout.PropertyField(seamMeshProp, seamMeshGC); + EditorGUILayout.PropertyField(inputGameObjectProp); + EditorGUILayout.PropertyField(outputPrefabProp); + //if (GUILayout.Button("Get Skinned Meshes From Input Game Object")) { + // GetSkinnedMeshesFromGameObject(bwc); + //} + //EditorGUILayout.PropertyField(targetMeshesProp,true); + + if (GUILayout.Button("Copy Bone Weights From Seam Mesh")) + { + CopyBoneWeightsFromSeamMeshToOtherMeshes(bwc); + } + EditorGUILayout.Separator(); + EditorGUILayout.Separator(); + EditorGUILayout.LabelField("Save Meshes To Project Folder", EditorStyles.boldLabel); + EditorGUILayout.LabelField(outputFolderProp.stringValue); + if (GUILayout.Button("Browse For Output Folder")) + { + string path = EditorUtility.OpenFolderPanel("Browse For Output Folder", "", ""); + outputFolderProp.stringValue = path; + } + if (GUILayout.Button("Set Output Folder To Output Prefab Folder")) + { + string path = AssetDatabase.GetAssetPath(bwc.outputPrefab); + path = new System.IO.FileInfo(path).Directory.FullName.Replace('\\', '/'); + outputFolderProp.stringValue = path; + } + serializedObject.ApplyModifiedProperties(); + } + + public void CopyBoneWeightsFromSeamMeshToOtherMeshes(MB3_BoneWeightCopier bwc) + { + if (bwc.seamMesh == null) + { + Debug.LogError("The seamMesh cannot be null."); + return; + } + + UnityEngine.Object pr = (UnityEngine.Object)PrefabUtility.GetPrefabObject(bwc.seamMesh.gameObject); + string assetPath = null; + if (pr != null) + { + assetPath = AssetDatabase.GetAssetPath(pr); + } + if (assetPath != null) + { + ModelImporter mi = (ModelImporter)AssetImporter.GetAtPath(assetPath); + if (mi != null) + { + if (mi.optimizeMesh) + { + Debug.LogError(string.Format("The seam mesh has 'optimized' checked in the asset importer. This will result in no vertices. Uncheck 'optimized'.")); + return; + } + } + } + //todo check that output game object exists and is a prefab + if (bwc.outputPrefab == null) + { + Debug.LogError(string.Format("The output game object must be assigned and must be a prefab of a game object in the project folder.")); + return; + } + if (MBVersionEditor.GetPrefabType(bwc.outputPrefab) != MB_PrefabType.prefabAsset) + { + Debug.LogError("The output game object must be a prefab. Create a prefab in the project and drag an empty game object to it."); + return; + } + + //duplicate the source prefab and the meshes + if (copyOfInput != null) + { + DestroyImmediate(copyOfInput); + } + copyOfInput = (GameObject)GameObject.Instantiate(bwc.inputGameObject); + SkinnedMeshRenderer[] targSkinnedMeshes = copyOfInput.GetComponentsInChildren<SkinnedMeshRenderer>(); + Mesh[] targs = new Mesh[targSkinnedMeshes.Length]; + for (int i = 0; i < targSkinnedMeshes.Length; i++) + { + if (targSkinnedMeshes[i].sharedMesh == null) + { + Debug.LogError(string.Format("Skinned Mesh {0} does not have a mesh", targSkinnedMeshes[i])); + return; + } + MB_PrefabType pt = MBVersionEditor.GetPrefabType(targSkinnedMeshes[i].gameObject); + if (pt == MB_PrefabType.modelPrefabAsset) + { + Debug.LogError(string.Format("Target Mesh {0} is an imported model prefab. Can't modify these meshes because changes will be overwritten the next time the model is saved or reimported. Try instantiating the prefab and using skinned meshes from the scene instance.", i)); + return; + } + targs[i] = (Mesh)GameObject.Instantiate(targSkinnedMeshes[i].sharedMesh); + } + MB3_CopyBoneWeights.CopyBoneWeightsFromSeamMeshToOtherMeshes(bwc.radius, bwc.seamMesh.sharedMesh, targs); + SaveMeshesToOutputFolderAndAssignToSMRs(targs, targSkinnedMeshes); + + EditorUtility.SetDirty(copyOfInput); + + + // TODO tried using 2018 replace prefab but there were errors. + //MBVersionEditor.ReplacePrefab(copyOfInput, assetPath, MB_ReplacePrefabOption.connectToPrefab | MB_ReplacePrefabOption.nameBased); + PrefabUtility.ReplacePrefab(copyOfInput, bwc.outputPrefab, ReplacePrefabOptions.ConnectToPrefab | ReplacePrefabOptions.ReplaceNameBased); + + AssetDatabase.SaveAssets(); + DestroyImmediate(copyOfInput); + } + + public void SaveMeshesToOutputFolderAndAssignToSMRs(Mesh[] targetMeshes, SkinnedMeshRenderer[] targetSMRs) + { + //validate meshes + for (int i = 0; i < targetMeshes.Length; i++) + { + if (targetSMRs[i] == null) + { + Debug.LogError(string.Format("Target Mesh {0} is null", i)); + return; + } + + if (targetSMRs[i].sharedMesh == null) + { + Debug.LogError(string.Format("Target Mesh {0} does not have a mesh", i)); + return; + } + MB_PrefabType pt = MBVersionEditor.GetPrefabType(targetMeshes[i]); + if (pt == MB_PrefabType.modelPrefabAsset) + { + Debug.LogError(string.Format("Target Mesh {0} is an imported model prefab. Can't modify these meshes because changes will be overwritten the next time the model is saved or reimported. Try instantiating the prefab and using skinned meshes from the scene instance.", i)); + return; + } + + } + //validate output folder + if (outputFolderProp.stringValue == null) + { + Debug.LogError("Output folder must be set"); + return; + } + if (outputFolderProp.stringValue.StartsWith(Application.dataPath)) + { + string relativePath = "Assets" + outputFolderProp.stringValue.Substring(Application.dataPath.Length); + string gid = AssetDatabase.AssetPathToGUID(relativePath); + if (gid == null) + { + Debug.LogError("Output folder must be a folder in the Unity project Asset folder"); + return; + } + } + else + { + Debug.LogError("Output folder must be a folder in the Unity project Asset folder"); + return; + } + for (int i = 0; i < targetMeshes.Length; i++) + { + Mesh m = targetMeshes[i]; + string pth = ConvertAbsolutePathToUnityPath(outputFolderProp.stringValue + "/" + targetMeshes[i].name + ".Asset"); + if (pth == null) + { + Debug.LogError("The output folder must be a folder in the project Assets folder."); + return; + } + AssetDatabase.CreateAsset(m, pth); + targetSMRs[i].sharedMesh = m; + Debug.Log(string.Format("Created mesh at {0}. Updated Skinned Mesh {1} to use created mesh.", pth, targetSMRs[i].name)); + } + AssetDatabase.SaveAssets(); + } + + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BoneWeightCopierEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BoneWeightCopierEditor.cs.meta new file mode 100644 index 00000000..c8aab581 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_BoneWeightCopierEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0a5f54a19161b7e489e7c1815bb56b79 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_DisableHiddenAnimationsEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_DisableHiddenAnimationsEditor.cs new file mode 100644 index 00000000..0cce8473 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_DisableHiddenAnimationsEditor.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using System.Collections; +using UnityEditor; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + [CustomEditor(typeof(MB3_DisableHiddenAnimations))] + public class MB3_DisableHiddenAnimationsEditor : Editor + { + public override void OnInspectorGUI() + { + EditorGUILayout.HelpBox( + "HOW TO USE \n\nPlace this component on the same game object that has the combined SkinMeshRenderer " + + "\n\n Drag game objects with Animator and/or Animation components that were baked into the combined SkinnedMeshRenderer into the lists below", MessageType.Info); + DrawDefaultInspector(); + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_DisableHiddenAnimationsEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_DisableHiddenAnimationsEditor.cs.meta new file mode 100644 index 00000000..90972b57 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_DisableHiddenAnimationsEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e1435b73816486941b0ea827c7645cf6 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MBVersionConcreteEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MBVersionConcreteEditor.cs new file mode 100644 index 00000000..1e009bae --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MBVersionConcreteEditor.cs @@ -0,0 +1,475 @@ +/** + * \brief Hax! DLLs cannot interpret preprocessor directives, so this class acts as a "bridge" + */ +using System; +using UnityEngine; +using UnityEditor; +using System.Collections; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class MBVersionEditorConcrete : MBVersionEditorInterface + { + + /// <summary> + /// Used to map the activeBuildTarget to a string argument needed by TextureImporter.GetPlatformTextureSettings + /// The allowed values for GetPlatformTextureSettings are "Web", "Standalone", "iPhone", "Android" and "FlashPlayer". + /// </summary> + /// <returns></returns> + public string GetPlatformString() + { +#if (UNITY_4_6 || UNITY_4_7 || UNITY_4_5 || UNITY_4_3 || UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 || UNITY_3_5) + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iPhone){ + return "iPhone"; + } +#else + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS) + { + return "iPhone"; + } +#endif + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WSAPlayer) + { + return "Windows Store Apps"; + } + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.PSP2) + { + return "PSP2"; + } + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.PS4) + { + return "PS4"; + } + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.XboxOne) + { + return "XboxOne"; + } +#if (UNITY_2017_3_OR_NEWER) +#else + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.SamsungTV) + { + return "Samsung TV"; + } +#endif +#if (UNITY_5_5_OR_NEWER) + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.N3DS) + { + return "Nintendo 3DS"; + } +#endif +#if (UNITY_5_3 || UNITY_5_2 || UNITY_5_3_OR_NEWER) +#if (UNITY_2018_1_OR_NEWER) + // wiiu support was removed in 2018.1 +#else + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WiiU) + { + return "WiiU"; + } +#endif +#endif +#if (UNITY_5_3 || UNITY_5_3_OR_NEWER) + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS) + { + return "tvOS"; + } +#endif +#if (UNITY_2018_2_OR_NEWER) + +#else + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Tizen) + { + return "Tizen"; + } +#endif + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) + { + return "Android"; + } + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneLinux || + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneLinux64 || + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneLinuxUniversal || + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows || + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64 || +#if UNITY_2017_3_OR_NEWER + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneOSX +#else + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneOSXIntel || + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneOSXIntel64 || + EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneOSXUniversal +#endif + ) + { + return "Standalone"; + } +#if !UNITY_5_4_OR_NEWER + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WebPlayer || + EditorUserBuildSettings.activeBuildTarget == BuildTarget.WebPlayerStreamed + ) + { + return "Web"; + } +#endif + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WebGL) + { + return "WebGL"; + } + return null; + } + + public void RegisterUndo(UnityEngine.Object o, string s) + { +#if (UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 || UNITY_3_5) + Undo.RegisterUndo(o, s); +#else + Undo.RecordObject(o, s); +#endif + } + + public void SetInspectorLabelWidth(float width) + { +#if (UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 || UNITY_3_5) + EditorGUIUtility.LookLikeControls(width); +#else + EditorGUIUtility.labelWidth = width; +#endif + } + + public void UpdateIfDirtyOrScript(SerializedObject so) + { +#if UNITY_5_6_OR_NEWER + so.UpdateIfRequiredOrScript(); +#else + so.UpdateIfDirtyOrScript(); +#endif + } + + public UnityEngine.Object PrefabUtility_GetCorrespondingObjectFromSource(GameObject go) + { +#if UNITY_2018_2_OR_NEWER + return PrefabUtility.GetCorrespondingObjectFromSource(go); +#else + return PrefabUtility.GetPrefabParent(go); +#endif + } + + public bool IsAutoPVRTC(TextureImporterFormat platformFormat, TextureImporterFormat platformDefaultFormat) + { + if (( +#if UNITY_2017_1_OR_NEWER + platformFormat == TextureImporterFormat.Automatic +#elif UNITY_5_5_OR_NEWER + platformFormat == TextureImporterFormat.Automatic || + platformFormat == TextureImporterFormat.Automatic16bit || + platformFormat == TextureImporterFormat.AutomaticCompressed || + platformFormat == TextureImporterFormat.AutomaticCompressedHDR || + platformFormat == TextureImporterFormat.AutomaticCrunched || + platformFormat == TextureImporterFormat.AutomaticHDR +#else + platformFormat == TextureImporterFormat.Automatic16bit || + platformFormat == TextureImporterFormat.AutomaticCompressed || + platformFormat == TextureImporterFormat.AutomaticCrunched +#endif + ) && ( + platformDefaultFormat == TextureImporterFormat.PVRTC_RGB2 || + platformDefaultFormat == TextureImporterFormat.PVRTC_RGB4 || + platformDefaultFormat == TextureImporterFormat.PVRTC_RGBA2 || + platformDefaultFormat == TextureImporterFormat.PVRTC_RGBA4 + )) + { + return true; + } + return false; + } + + public MB_PrefabType GetPrefabType(UnityEngine.Object obj) + { +#if UNITY_2018_3_OR_NEWER + if (PrefabUtility.IsPartOfNonAssetPrefabInstance(obj)) + { + return MB_PrefabType.scenePefabInstance; + } + + if (!PrefabUtility.IsPartOfAnyPrefab(obj)) + { + return MB_PrefabType.isInstanceAndNotAPartOfAnyPrefab; + } + + PrefabAssetType assetType = PrefabUtility.GetPrefabAssetType(obj); + if (assetType == PrefabAssetType.NotAPrefab) + { + if (PrefabUtility.GetPrefabInstanceStatus(obj) != PrefabInstanceStatus.NotAPrefab) + { + return MB_PrefabType.isInstanceAndNotAPartOfAnyPrefab; + } + else + { + return MB_PrefabType.scenePefabInstance; + } + } + else if (assetType == PrefabAssetType.Model) + { + return MB_PrefabType.modelPrefabAsset; + } + else if (assetType == PrefabAssetType.Regular || + assetType == PrefabAssetType.Variant || + assetType == PrefabAssetType.MissingAsset) + { + return MB_PrefabType.prefabAsset; + } + else + { + Debug.Assert(false, "Should never get here. Unknown prefab asset type."); + return MB_PrefabType.isInstanceAndNotAPartOfAnyPrefab; + } +#else + PrefabType prefabType = PrefabUtility.GetPrefabType(obj); + if (prefabType == PrefabType.ModelPrefab) + { + return MB_PrefabType.modelPrefabAsset; + } else if (prefabType == PrefabType.Prefab) + { + return MB_PrefabType.prefabAsset; + } else if (prefabType == PrefabType.PrefabInstance || prefabType == PrefabType.ModelPrefabInstance) + { + return MB_PrefabType.scenePefabInstance; + } else + { + return MB_PrefabType.isInstanceAndNotAPartOfAnyPrefab; + } +#endif + } + + public void UnpackPrefabInstance(UnityEngine.GameObject go, ref SerializedObject so) + { +#if UNITY_2018_3_OR_NEWER + UnityEngine.Object targetObj = null; + if (so != null) targetObj = so.targetObject; + PrefabUtility.UnpackPrefabInstance(go, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction); + + // This is a workaround for a nasty Unity bug. The call to UnpackPrefabInstance + // corrupts the serialized object, Recreate a clean reference here. + if (so != null) so = new SerializedObject(targetObj); +#else + // Do nothing. +#endif + } + + public void ReplacePrefab(GameObject gameObject, string assetPath, MB_ReplacePrefabOption replacePrefabOptions) + { +#if UNITY_2018_3_OR_NEWER + PrefabUtility.SaveAsPrefabAssetAndConnect(gameObject, assetPath, InteractionMode.AutomatedAction); +#else + GameObject obj = (GameObject) AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)); + PrefabUtility.ReplacePrefab(gameObject, obj, (ReplacePrefabOptions) replacePrefabOptions); +#endif + } + + public GameObject GetPrefabInstanceRoot(GameObject sceneInstance) + { +#if UNITY_2018_3_OR_NEWER + return PrefabUtility.GetOutermostPrefabInstanceRoot(sceneInstance); +#else + return PrefabUtility.FindRootGameObjectWithSameParentPrefab(sceneInstance); +#endif + } + + public TextureImporterFormat Map_TextureFormat_2_TextureImporterFormat(TextureFormat texFormat, out bool success) + { + TextureImporterFormat texImporterFormat; + success = true; + switch (texFormat) + { + case TextureFormat.ARGB32: + texImporterFormat = TextureImporterFormat.ARGB32; + break; + case TextureFormat.RGBA32: + texImporterFormat = TextureImporterFormat.RGBA32; + break; + case TextureFormat.RGB24: + texImporterFormat = TextureImporterFormat.RGB24; + break; + case TextureFormat.Alpha8: + texImporterFormat = TextureImporterFormat.Alpha8; + break; + +#if UNITY_2020_1_OR_NEWER + case TextureFormat.ASTC_10x10: + texImporterFormat = TextureImporterFormat.ASTC_10x10; + break; + case TextureFormat.ASTC_12x12: + texImporterFormat = TextureImporterFormat.ASTC_12x12; + break; + case TextureFormat.ASTC_4x4: + texImporterFormat = TextureImporterFormat.ASTC_4x4; + break; + case TextureFormat.ASTC_5x5: + texImporterFormat = TextureImporterFormat.ASTC_5x5; + break; + case TextureFormat.ASTC_6x6: + texImporterFormat = TextureImporterFormat.ASTC_6x6; + break; + case TextureFormat.ASTC_8x8: + texImporterFormat = TextureImporterFormat.ASTC_8x8; + break; +#else + case TextureFormat.ASTC_RGBA_10x10: + texImporterFormat = TextureImporterFormat.ASTC_RGBA_10x10; + break; + case TextureFormat.ASTC_RGBA_12x12: + texImporterFormat = TextureImporterFormat.ASTC_RGBA_12x12; + break; + case TextureFormat.ASTC_RGBA_4x4: + texImporterFormat = TextureImporterFormat.ASTC_RGBA_4x4; + break; + case TextureFormat.ASTC_RGBA_5x5: + texImporterFormat = TextureImporterFormat.ASTC_RGBA_5x5; + break; + case TextureFormat.ASTC_RGBA_6x6: + texImporterFormat = TextureImporterFormat.ASTC_RGBA_6x6; + break; + case TextureFormat.ASTC_RGBA_8x8: + texImporterFormat = TextureImporterFormat.ASTC_RGBA_8x8; + break; + + case TextureFormat.ASTC_RGB_10x10: + texImporterFormat = TextureImporterFormat.ASTC_RGB_10x10; + break; + case TextureFormat.ASTC_RGB_12x12: + texImporterFormat = TextureImporterFormat.ASTC_RGB_12x12; + break; + case TextureFormat.ASTC_RGB_4x4: + texImporterFormat = TextureImporterFormat.ASTC_RGB_4x4; + break; + case TextureFormat.ASTC_RGB_5x5: + texImporterFormat = TextureImporterFormat.ASTC_RGB_5x5; + break; + case TextureFormat.ASTC_RGB_6x6: + texImporterFormat = TextureImporterFormat.ASTC_RGB_6x6; + break; + case TextureFormat.ASTC_RGB_8x8: + texImporterFormat = TextureImporterFormat.ASTC_RGB_8x8; + break; +#endif + case TextureFormat.BC4: + texImporterFormat = TextureImporterFormat.BC4; + break; + case TextureFormat.BC5: + texImporterFormat = TextureImporterFormat.BC5; + break; + case TextureFormat.BC6H: + texImporterFormat = TextureImporterFormat.BC6H; + break; + case TextureFormat.BC7: + texImporterFormat = TextureImporterFormat.BC7; + break; + + case TextureFormat.DXT1: + texImporterFormat = TextureImporterFormat.DXT1; + break; + case TextureFormat.DXT1Crunched: + texImporterFormat = TextureImporterFormat.DXT1Crunched; + break; + case TextureFormat.DXT5: + texImporterFormat = TextureImporterFormat.DXT5; + break; + case TextureFormat.DXT5Crunched: + texImporterFormat = TextureImporterFormat.DXT5Crunched; + break; + + case TextureFormat.EAC_R: + texImporterFormat = TextureImporterFormat.EAC_R; + break; + case TextureFormat.EAC_RG: + texImporterFormat = TextureImporterFormat.EAC_RG; + break; + case TextureFormat.EAC_RG_SIGNED: + texImporterFormat = TextureImporterFormat.EAC_RG_SIGNED; + break; + case TextureFormat.EAC_R_SIGNED: + texImporterFormat = TextureImporterFormat.EAC_R_SIGNED; + break; + + case TextureFormat.ETC_RGB4: + texImporterFormat = TextureImporterFormat.ETC_RGB4; + break; +#if UNITY_2017_3_OR_NEWER + case TextureFormat.ETC_RGB4Crunched: + texImporterFormat = TextureImporterFormat.ETC_RGB4Crunched; + break; +#endif + case TextureFormat.ETC2_RGB: + texImporterFormat = TextureImporterFormat.ETC2_RGB4; + break; + case TextureFormat.ETC2_RGBA8: + texImporterFormat = TextureImporterFormat.ETC2_RGBA8; + break; +#if UNITY_2017_3_OR_NEWER + case TextureFormat.ETC2_RGBA8Crunched: + texImporterFormat = TextureImporterFormat.ETC2_RGBA8Crunched; + break; +#endif + case TextureFormat.PVRTC_RGB2: + texImporterFormat = TextureImporterFormat.PVRTC_RGB2; + break; + case TextureFormat.PVRTC_RGB4: + texImporterFormat = TextureImporterFormat.PVRTC_RGB4; + break; + case TextureFormat.PVRTC_RGBA2: + texImporterFormat = TextureImporterFormat.PVRTC_RGBA2; + break; + case TextureFormat.PVRTC_RGBA4: + texImporterFormat = TextureImporterFormat.PVRTC_RGBA4; + break; +#if UNITY_2018_3_OR_NEWER + case TextureFormat.R16: + texImporterFormat = TextureImporterFormat.R16; + break; +#endif +#if UNITY_2018_2_OR_NEWER + case TextureFormat.R8: + texImporterFormat = TextureImporterFormat.R8; + break; +#endif +#if UNITY_2018_3_OR_NEWER + case TextureFormat.RFloat: + texImporterFormat = TextureImporterFormat.RFloat; + break; +#endif +#if UNITY_2018_3_OR_NEWER + case TextureFormat.RG16: + texImporterFormat = TextureImporterFormat.RG16; + break; +#endif +#if UNITY_2018_3_OR_NEWER + case TextureFormat.RGB9e5Float: + texImporterFormat = TextureImporterFormat.RGB9E5; + break; +#endif +#if UNITY_2018_3_OR_NEWER + case TextureFormat.RGHalf: + texImporterFormat = TextureImporterFormat.RGHalf; + break; +#endif +#if UNITY_2018_3_OR_NEWER + case TextureFormat.RGFloat: + texImporterFormat = TextureImporterFormat.RGFloat; + break; +#endif +#if UNITY_2018_3_OR_NEWER + case TextureFormat.RHalf: + texImporterFormat = TextureImporterFormat.RHalf; + break; +#endif + default: + texImporterFormat = TextureImporterFormat.ARGB32; + success = false; + Debug.LogError("No mapping for TextureFormat: " + texFormat + " to a TextureImporterFormat. "); + break; + } + + return texImporterFormat; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MBVersionConcreteEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MBVersionConcreteEditor.cs.meta new file mode 100644 index 00000000..ff77c98a --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MBVersionConcreteEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 86e7cea5d902c1647b14db77b02f967e +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditor.cs new file mode 100644 index 00000000..136afc56 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditor.cs @@ -0,0 +1,104 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using DigitalOpus.MB.Core; +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + [CustomEditor(typeof(MB3_MeshBaker))] + [CanEditMultipleObjects] + public class MB3_MeshBakerEditor : Editor + { + MB3_MeshBakerEditorInternal mbe = new MB3_MeshBakerEditorInternal(); + + [MenuItem("GameObject/Create Other/Mesh Baker/TextureBaker and MeshBaker", false, 100)] + public static GameObject CreateNewMeshBaker() + { + MB3_TextureBaker[] mbs = (MB3_TextureBaker[])GameObject.FindObjectsOfType(typeof(MB3_TextureBaker)); + Regex regex = new Regex(@"\((\d+)\)$", RegexOptions.Compiled | RegexOptions.CultureInvariant); + int largest = 0; + try + { + for (int i = 0; i < mbs.Length; i++) + { + Match match = regex.Match(mbs[i].name); + if (match.Success) + { + int val = Convert.ToInt32(match.Groups[1].Value); + if (val >= largest) + largest = val + 1; + } + } + } + catch (Exception e) + { + if (e == null) e = null; //Do nothing supress compiler warning + } + GameObject nmb = new GameObject("TextureBaker (" + largest + ")"); + nmb.transform.position = Vector3.zero; + MB3_TextureBaker tb = nmb.AddComponent<MB3_TextureBaker>(); + tb.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; + MB3_MeshBakerGrouper mbg = nmb.AddComponent<MB3_MeshBakerGrouper>(); + GameObject meshBaker = new GameObject("MeshBaker"); + MB3_MeshBaker mb = meshBaker.AddComponent<MB3_MeshBaker>(); + meshBaker.transform.parent = nmb.transform; + mb.meshCombiner.settingsHolder = mbg; + return nmb.gameObject; + } + + [MenuItem("GameObject/Create Other/Mesh Baker/MeshBaker", false, 100)] + public static GameObject CreateNewMeshBakerOnly() + { + MB3_MeshBaker[] mbs = (MB3_MeshBaker[])GameObject.FindObjectsOfType(typeof(MB3_MeshBaker)); + Regex regex = new Regex(@"\((\d+)\)$", RegexOptions.Compiled | RegexOptions.CultureInvariant); + int largest = 0; + try + { + for (int i = 0; i < mbs.Length; i++) + { + Match match = regex.Match(mbs[i].name); + if (match.Success) + { + int val = Convert.ToInt32(match.Groups[1].Value); + if (val >= largest) + largest = val + 1; + } + } + } + catch (Exception e) + { + if (e == null) e = null; //Do nothing supress compiler warning + } + GameObject meshBaker = new GameObject("MeshBaker (" + largest + ")"); + meshBaker.AddComponent<MB3_MeshBaker>(); + return meshBaker.gameObject; + } + + void OnEnable() + { + mbe.OnEnable(serializedObject); + } + + void OnDisable() + { + mbe.OnDisable(); + } + + public override void OnInspectorGUI() + { + mbe.OnInspectorGUI(serializedObject, (MB3_MeshBakerCommon)target, targets, typeof(MB3_MeshBakerEditorWindow)); + } + + + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditor.cs.meta new file mode 100644 index 00000000..2e339325 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d0de7b6551ddaeb4091cb0d096f8963b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorInternal.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorInternal.cs new file mode 100644 index 00000000..92630348 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorInternal.cs @@ -0,0 +1,626 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + + public interface MB3_MeshBakerEditorWindowInterface + { + MonoBehaviour target + { + get; + set; + } + } + + public class MB_EditorStyles + { + + public GUIStyle multipleMaterialBackgroundStyle = new GUIStyle(); + public GUIStyle multipleMaterialBackgroundStyleDarker = new GUIStyle(); + public GUIStyle editorBoxBackgroundStyle = new GUIStyle(); + + Texture2D multipleMaterialBackgroundColor; + Texture2D multipleMaterialBackgroundColorDarker; + Texture2D editorBoxBackgroundColor; + + public void Init() + { + bool isPro = EditorGUIUtility.isProSkin; + Color backgroundColor = isPro + ? new Color32(35, 35, 35, 255) + : new Color32(174, 174, 174, 255); + if (multipleMaterialBackgroundColor == null) + { + multipleMaterialBackgroundColor = MB3_MeshBakerEditorFunctions.MakeTex(8, 8, backgroundColor); + } + + backgroundColor = isPro + ? new Color32(50, 50, 50, 255) + : new Color32(160, 160, 160, 255); + if (multipleMaterialBackgroundColorDarker == null) + { + multipleMaterialBackgroundColorDarker = MB3_MeshBakerEditorFunctions.MakeTex(8, 8, backgroundColor); + } + + backgroundColor = isPro + ? new Color32(35, 35, 35, 255) + : new Color32(174, 174, 174, 255); + + multipleMaterialBackgroundStyle.normal.background = multipleMaterialBackgroundColor; + multipleMaterialBackgroundStyleDarker.normal.background = multipleMaterialBackgroundColorDarker; + + if (editorBoxBackgroundColor == null) + { + editorBoxBackgroundColor = MB3_MeshBakerEditorFunctions.MakeTex(8, 8, backgroundColor); + } + + editorBoxBackgroundStyle.normal.background = editorBoxBackgroundColor; + editorBoxBackgroundStyle.border = new RectOffset(0, 0, 0, 0); + editorBoxBackgroundStyle.margin = new RectOffset(5, 5, 5, 5); + editorBoxBackgroundStyle.padding = new RectOffset(10, 10, 10, 10); + } + + public void DestroyTextures() + { + if (multipleMaterialBackgroundColor != null) GameObject.DestroyImmediate(multipleMaterialBackgroundColor); + if (multipleMaterialBackgroundColorDarker != null) GameObject.DestroyImmediate(multipleMaterialBackgroundColorDarker); + if (editorBoxBackgroundColor != null) GameObject.DestroyImmediate(editorBoxBackgroundColor); + } + } + + public class MB3_MeshBakerEditorInternal + { + //add option to exclude skinned mesh renderer and mesh renderer in filter + //example scenes for multi material + private static GUIContent + gc_outputOptoinsGUIContent = new GUIContent("Output"), + gc_logLevelContent = new GUIContent("Log Level"), + gc_openToolsWindowLabelContent = new GUIContent("Open Tools For Adding Objects", "Use these tools to find out what can be combined, discover problems with meshes, and quickly add objects."), + gc_parentSceneObject = new GUIContent("Parent Scene Object (Optional)", "Must be a scene object. If set, then combined meshes will be added as children of this GameObject in the hierarchy"), + gc_resultPrefabLeaveInstanceInSceneAfterBake = new GUIContent("Leave Instance In Scene After Bake", "If checked then an instance will be left in the scene after baking. Otherwise scene instance will be deleted after prefab is baked."), + gc_objectsToCombineGUIContent = new GUIContent("Custom List Of Objects To Be Combined", "You can add objects here that were not on the list in the MB3_TextureBaker as long as they use a material that is in the Texture Bake Results"), + gc_textureBakeResultsGUIContent = new GUIContent("Texture Bake Result", "When materials are combined a MB2_TextureBakeResult Asset is generated. Drag that Asset to this field to use the combined material."), + gc_useTextureBakerObjsGUIContent = new GUIContent("Same As Texture Baker", "Build a combined mesh using using the same list of objects that generated the Combined Material"), + gc_combinedMeshPrefabGUIContent = new GUIContent("Combined Mesh Prefab", "Create a new prefab asset an drag an empty game object to it. Drag the prefab asset to here."), + gc_SortAlongAxis = new GUIContent("SortAlongAxis", "Transparent materials often require that triangles be rendered in a certain order. This will sort Game Objects along the specified axis. Triangles will be added to the combined mesh in this order."), + gc_combinedMesh = new GUIContent("Mesh", "This is the Mesh used by this baker and assigned to the combined Renderer.\n\n" + + "If it is null then a new Mesh will be created for the next bake.\n\n" + + "If it is a project folder asset then that asset will be overwitten (changes may be reverted if the scene is not saved).\n\n" + + "If your are re-using the same MeshBaker to bake different combined meshes, set this to null before each new bake. If you don't then each bake will overwrite the previous bake's mesh."), + gc_Settings = new GUIContent("Use Shared Settings", "Different bakers can share the same settings. If this field is None, then the settings below will be used. " + + "Assign one of the following:\n" + + " - Mesh Baker Settings project asset \n" + + " - Mesh Baker Grouper scene instance \n"); + + + + private SerializedObject meshBaker; + private SerializedProperty logLevel, combiner, outputOptions, textureBakeResults, useObjsToMeshFromTexBaker, objsToMesh, mesh, sortOrderAxis, parentSceneObject, resultPrefabLeaveInstanceInSceneAfterBake; + + private SerializedProperty settingsHolder; + + private MB_MeshBakerSettingsEditor meshBakerSettingsThis; + private MB_MeshBakerSettingsEditor meshBakerSettingsExternal; + + bool showInstructions = false; + bool showContainsReport = true; + + MB_EditorStyles editorStyles = new MB_EditorStyles(); + + Color buttonColor = new Color(.8f, .8f, 1f, 1f); + void _init(SerializedObject mb) + { + this.meshBaker = mb; + objsToMesh = meshBaker.FindProperty("objsToMesh"); + combiner = meshBaker.FindProperty("_meshCombiner"); + parentSceneObject = meshBaker.FindProperty("parentSceneObject"); + resultPrefabLeaveInstanceInSceneAfterBake = meshBaker.FindProperty("resultPrefabLeaveInstanceInSceneAfterBake"); + logLevel = combiner.FindPropertyRelative("_LOG_LEVEL"); + outputOptions = combiner.FindPropertyRelative("_outputOption"); + useObjsToMeshFromTexBaker = meshBaker.FindProperty("useObjsToMeshFromTexBaker"); + textureBakeResults = combiner.FindPropertyRelative("_textureBakeResults"); + mesh = combiner.FindPropertyRelative("_mesh"); + sortOrderAxis = meshBaker.FindProperty("sortAxis"); + settingsHolder = combiner.FindPropertyRelative("_settingsHolder"); + meshBakerSettingsThis = new MB_MeshBakerSettingsEditor(); + meshBakerSettingsThis.OnEnable(combiner, meshBaker); + editorStyles.Init(); + } + + public void OnEnable(SerializedObject meshBaker) + { + _init(meshBaker); + } + + public void OnDisable() + { + editorStyles.DestroyTextures(); + if (meshBakerSettingsThis != null) meshBakerSettingsThis.OnDisable(); + if (meshBakerSettingsExternal != null) meshBakerSettingsExternal.OnDisable(); + } + + public void OnInspectorGUI(SerializedObject meshBaker, MB3_MeshBakerCommon target, UnityEngine.Object[] targets, System.Type editorWindowType) + { + DrawGUI(meshBaker, target, targets, editorWindowType); + } + + public void DrawGUI(SerializedObject meshBaker, MB3_MeshBakerCommon target, UnityEngine.Object[] targets, System.Type editorWindowType) + { + if (meshBaker == null) + { + return; + } + + meshBaker.Update(); + + showInstructions = EditorGUILayout.Foldout(showInstructions, "Instructions:"); + if (showInstructions) + { + EditorGUILayout.HelpBox("1. Bake combined material(s).\n\n" + + "2. If necessary set the 'Texture Bake Results' field.\n\n" + + "3. Add scene objects or prefabs to combine or check 'Same As Texture Baker'. For best results these should use the same shader as result material.\n\n" + + "4. Select options and 'Bake'.\n\n" + + "6. Look at warnings/errors in console. Decide if action needs to be taken.\n\n" + + "7. (optional) Disable renderers in source objects.", UnityEditor.MessageType.None); + + EditorGUILayout.Separator(); + } + + MB3_MeshBakerCommon momm = (MB3_MeshBakerCommon)target; + EditorGUILayout.PropertyField(logLevel, gc_logLevelContent); + EditorGUILayout.PropertyField(textureBakeResults, gc_textureBakeResultsGUIContent); + bool doingTextureArray = false; + if (textureBakeResults.objectReferenceValue != null) + { + doingTextureArray = ((MB2_TextureBakeResults)textureBakeResults.objectReferenceValue).resultType == MB2_TextureBakeResults.ResultType.textureArray; + showContainsReport = EditorGUILayout.Foldout(showContainsReport, "Shaders & Materials Contained"); + if (showContainsReport) + { + EditorGUILayout.HelpBox(((MB2_TextureBakeResults)textureBakeResults.objectReferenceValue).GetDescription(), MessageType.Info); + } + } + + EditorGUILayout.BeginVertical(editorStyles.editorBoxBackgroundStyle); + EditorGUILayout.LabelField("Objects To Be Combined", EditorStyles.boldLabel); + if (momm.GetTextureBaker() != null) + { + EditorGUILayout.PropertyField(useObjsToMeshFromTexBaker, gc_useTextureBakerObjsGUIContent); + } + else + { + useObjsToMeshFromTexBaker.boolValue = false; + momm.useObjsToMeshFromTexBaker = false; + GUI.enabled = false; + EditorGUILayout.PropertyField(useObjsToMeshFromTexBaker, gc_useTextureBakerObjsGUIContent); + GUI.enabled = true; + } + + if (!momm.useObjsToMeshFromTexBaker) + { + if (GUILayout.Button(gc_openToolsWindowLabelContent)) + { + MB3_MeshBakerEditorWindow mmWin = (MB3_MeshBakerEditorWindow) EditorWindow.GetWindow(editorWindowType); + mmWin.SetTarget((MB3_MeshBakerRoot)momm); + } + + object[] objs = MB3_EditorMethods.DropZone("Drag & Drop Renderers Or Parents Here To Add Objects To Be Combined", 300, 50); + MB3_EditorMethods.AddDroppedObjects(objs, momm); + + EditorGUILayout.PropertyField(objsToMesh, gc_objectsToCombineGUIContent, true); + EditorGUILayout.Separator(); + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Select Objects In Scene")) + { + List<MB3_MeshBakerCommon> selectedBakers = _getBakersFromTargets(targets); + List<GameObject> obsToCombine = new List<GameObject>(); + + foreach(MB3_MeshBakerCommon baker in selectedBakers) obsToCombine.AddRange(baker.GetObjectsToCombine()); + Selection.objects = obsToCombine.ToArray(); + if (momm.GetObjectsToCombine().Count > 0) + { + SceneView.lastActiveSceneView.pivot = momm.GetObjectsToCombine()[0].transform.position; + } + } + if (GUILayout.Button(gc_SortAlongAxis)) + { + MB3_MeshBakerRoot.ZSortObjects sorter = new MB3_MeshBakerRoot.ZSortObjects(); + sorter.sortAxis = sortOrderAxis.vector3Value; + sorter.SortByDistanceAlongAxis(momm.GetObjectsToCombine()); + } + EditorGUILayout.PropertyField(sortOrderAxis, GUIContent.none); + EditorGUILayout.EndHorizontal(); + } + else + { + GUI.enabled = false; + EditorGUILayout.PropertyField(objsToMesh, gc_objectsToCombineGUIContent, true); + GUI.enabled = true; + } + EditorGUILayout.EndVertical(); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Output", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(outputOptions, gc_outputOptoinsGUIContent); + if (momm.meshCombiner.outputOption == MB2_OutputOptions.bakeIntoSceneObject) + { + Transform pgo = (Transform)EditorGUILayout.ObjectField(gc_parentSceneObject, parentSceneObject.objectReferenceValue, typeof(Transform), true); + if (pgo != null && MB_Utility.IsSceneInstance(pgo.gameObject)) + { + parentSceneObject.objectReferenceValue = pgo; + } + else + { + parentSceneObject.objectReferenceValue = null; + } + + //todo switch to renderer + momm.meshCombiner.resultSceneObject = (GameObject)EditorGUILayout.ObjectField("Combined Mesh Object", momm.meshCombiner.resultSceneObject, typeof(GameObject), true); + if (momm is MB3_MeshBaker) + { + string l = "Mesh"; + Mesh m = (Mesh)mesh.objectReferenceValue; + if (m != null) + { + l += " (" + m.GetInstanceID() + ")"; + } + Mesh nm = (Mesh)EditorGUILayout.ObjectField(gc_combinedMesh, m, typeof(Mesh), true); + if (nm != m) + { + Undo.RecordObject(momm, "Assign Mesh"); + ((MB3_MeshCombinerSingle)momm.meshCombiner).SetMesh(nm); + mesh.objectReferenceValue = nm; + } + } + } + else if (momm.meshCombiner.outputOption == MB2_OutputOptions.bakeIntoPrefab) + { + if (momm.meshCombiner.settings.renderType == MB_RenderType.skinnedMeshRenderer) + { + EditorGUILayout.HelpBox("The workflow for baking Skinned Meshes into prefabs has changed as of version 29.1. " + + "It is no longer necessary to manually copy bones to the target prefab after baking. This should happen automatically.", MessageType.Info); + } + + Transform pgo = (Transform)EditorGUILayout.ObjectField(gc_parentSceneObject, parentSceneObject.objectReferenceValue, typeof(Transform), true); + if (pgo != null && MB_Utility.IsSceneInstance(pgo.gameObject)) + { + parentSceneObject.objectReferenceValue = pgo; + } + else + { + parentSceneObject.objectReferenceValue = null; + } + + EditorGUILayout.BeginHorizontal(); + momm.resultPrefab = (GameObject)EditorGUILayout.ObjectField(gc_combinedMeshPrefabGUIContent, momm.resultPrefab, typeof(GameObject), true); + if (momm.resultPrefab != null) + { + string assetPath = AssetDatabase.GetAssetPath(momm.resultPrefab); + if (assetPath == null || assetPath.Length == 0) + { + Debug.LogError("The " + gc_combinedMeshPrefabGUIContent.text + " must be a prefab asset, not a scene GameObject"); + momm.resultPrefab = null; + } else + { + MB_PrefabType pt = MBVersionEditor.GetPrefabType(momm.resultPrefab); + if (pt != MB_PrefabType.prefabAsset) + { + Debug.LogError("The " + gc_combinedMeshPrefabGUIContent.text + " must be a prefab asset, the prefab type was '" + pt + "'"); + momm.resultPrefab = null; + } + } + } else + { + if (GUILayout.Button("Create Empty Prefab")) + { + if (!Application.isPlaying) + { + string path = EditorUtility.SaveFilePanelInProject("Create Empty Prefab", "MyPrefab", "prefab", "Create a prefab containing an empty GameObject"); + string pathNoFolder = Path.GetDirectoryName(path); + string fileNameNoExt = Path.GetFileNameWithoutExtension(path); + List<MB3_MeshBakerCommon> selectedBakers = _getBakersFromTargets(targets); + if (selectedBakers.Count > 1) Debug.Log("About to create prefabs for " + selectedBakers.Count); + int idx = 0; + foreach (MB3_MeshBakerCommon baker in selectedBakers) + { + createEmptyPrefab(baker, pathNoFolder, fileNameNoExt, idx); + idx++; + } + } + } + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.PropertyField(resultPrefabLeaveInstanceInSceneAfterBake, gc_resultPrefabLeaveInstanceInSceneAfterBake); + if (momm is MB3_MeshBaker) + { + string l = "Mesh"; + Mesh m = (Mesh)mesh.objectReferenceValue; + if (m != null) + { + l += " (" + m.GetInstanceID() + ")"; + } + Mesh nm = (Mesh)EditorGUILayout.ObjectField(gc_combinedMesh, m, typeof(Mesh), true); + if (nm != m) + { + Undo.RecordObject(momm, "Assign Mesh"); + ((MB3_MeshCombinerSingle)momm.meshCombiner).SetMesh(nm); + mesh.objectReferenceValue = nm; + } + } + } + else if (momm.meshCombiner.outputOption == MB2_OutputOptions.bakeMeshAssetsInPlace) + { + EditorGUILayout.HelpBox("NEW! Try the BatchPrefabBaker component. It makes preparing a batch of prefabs for static/ dynamic batching much easier.", MessageType.Info); + if (GUILayout.Button("Choose Folder For Bake In Place Meshes")) + { + string newFolder = EditorUtility.SaveFolderPanel("Folder For Bake In Place Meshes", Application.dataPath, ""); + if (!newFolder.Contains(Application.dataPath)) Debug.LogWarning("The chosen folder must be in your assets folder."); + string folder = "Assets" + newFolder.Replace(Application.dataPath, ""); + List<MB3_MeshBakerCommon> selectedBakers = _getBakersFromTargets(targets); + Undo.RecordObjects(targets, "Undo Set Folder"); + foreach (MB3_MeshBakerCommon baker in selectedBakers) + { + baker.bakeAssetsInPlaceFolderPath = folder; + EditorUtility.SetDirty(baker); + } + } + + EditorGUILayout.LabelField("Folder For Meshes: " + momm.bakeAssetsInPlaceFolderPath); + } + + if (momm is MB3_MultiMeshBaker) + { + MB3_MultiMeshCombiner mmc = (MB3_MultiMeshCombiner)momm.meshCombiner; + mmc.maxVertsInMesh = EditorGUILayout.IntField("Max Verts In Mesh", mmc.maxVertsInMesh); + } + + //----------------------------------- + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel); + bool settingsEnabled = true; + + //------------- Mesh Baker Settings is a bit tricky because it is an interface. + + + + EditorGUILayout.Space(); + UnityEngine.Object obj = settingsHolder.objectReferenceValue; + + // Don't use a PropertyField because we may not be able to use the assigned object. It may not implement requried interface. + obj = EditorGUILayout.ObjectField(gc_Settings, obj, typeof(UnityEngine.Object), true); + + if (obj == null) + { + settingsEnabled = true; + settingsHolder.objectReferenceValue = null; + if (meshBakerSettingsExternal != null) + { + meshBakerSettingsExternal.OnDisable(); + meshBakerSettingsExternal = null; + } + } + + else if (obj is GameObject) + { + // Check to see if there is a component on this game object that implements MB_IMeshBakerSettingsHolder + MB_IMeshBakerSettingsHolder itf = (MB_IMeshBakerSettingsHolder)((GameObject)obj).GetComponent(typeof(MB_IMeshBakerSettingsHolder)); + if (itf != null) + { + settingsEnabled = false; + Component settingsHolderComponent = (Component)itf; + if (settingsHolder.objectReferenceValue != settingsHolderComponent) + { + settingsHolder.objectReferenceValue = settingsHolderComponent; + meshBakerSettingsExternal = new MB_MeshBakerSettingsEditor(); + UnityEngine.Object targetObj; + string propertyName; + itf.GetMeshBakerSettingsAsSerializedProperty(out propertyName, out targetObj); + SerializedProperty meshBakerSettings = new SerializedObject(targetObj).FindProperty(propertyName); + meshBakerSettingsExternal.OnEnable(meshBakerSettings); + } + } + else + { + settingsEnabled = true; + settingsHolder = null; + if (meshBakerSettingsExternal != null) + { + meshBakerSettingsExternal.OnDisable(); + meshBakerSettingsExternal = null; + } + } + } + else if (obj is MB_IMeshBakerSettingsHolder) + { + settingsEnabled = false; + if (settingsHolder.objectReferenceValue != obj) + { + settingsHolder.objectReferenceValue = obj; + meshBakerSettingsExternal = new MB_MeshBakerSettingsEditor(); + UnityEngine.Object targetObj; + string propertyName; + ((MB_IMeshBakerSettingsHolder)obj).GetMeshBakerSettingsAsSerializedProperty(out propertyName, out targetObj); + SerializedProperty meshBakerSettings = new SerializedObject(targetObj).FindProperty(propertyName); + meshBakerSettingsExternal.OnEnable(meshBakerSettings); + } + } + else + { + Debug.LogError("Object was not a Mesh Baker Settings object."); + } + EditorGUILayout.Space(); + + if (settingsHolder.objectReferenceValue == null) + { + // Use the meshCombiner settings + meshBakerSettingsThis.DrawGUI(momm.meshCombiner, settingsEnabled, doingTextureArray); + } + else + { + if (meshBakerSettingsExternal == null) + { + meshBakerSettingsExternal = new MB_MeshBakerSettingsEditor(); + UnityEngine.Object targetObj; + string propertyName; + ((MB_IMeshBakerSettingsHolder)obj).GetMeshBakerSettingsAsSerializedProperty(out propertyName, out targetObj); + SerializedProperty meshBakerSettings = new SerializedObject(targetObj).FindProperty(propertyName); + meshBakerSettingsExternal.OnEnable(meshBakerSettings); + } + meshBakerSettingsExternal.DrawGUI(((MB_IMeshBakerSettingsHolder)settingsHolder.objectReferenceValue).GetMeshBakerSettings(), settingsEnabled, doingTextureArray); + } + + Color oldColor = GUI.backgroundColor; + GUI.backgroundColor = buttonColor; + if (GUILayout.Button("Bake")) + { + List<MB3_MeshBakerCommon> selectedBakers = _getBakersFromTargets(targets); + if (selectedBakers.Count > 1) Debug.Log("About to bake " + selectedBakers.Count); + foreach(MB3_MeshBakerCommon baker in selectedBakers) + { + // Why are we caching and recreating the SerializedObject? Because "bakeIntoPrefab" corrupts the serialized object + // and the meshBaker SerializedObject throws an NRE the next time it gets used. + MB3_MeshBakerCommon mbr = (MB3_MeshBakerCommon) meshBaker.targetObject; + bake(baker); + meshBaker = new SerializedObject(mbr); + } + } + GUI.backgroundColor = oldColor; + + string enableRenderersLabel; + bool disableRendererInSource = false; + if (momm.GetObjectsToCombine().Count > 0) + { + Renderer r = MB_Utility.GetRenderer(momm.GetObjectsToCombine()[0]); + if (r != null && r.enabled) disableRendererInSource = true; + } + if (disableRendererInSource) + { + enableRenderersLabel = "Disable Renderers On Source Objects"; + } + else + { + enableRenderersLabel = "Enable Renderers On Source Objects"; + } + if (GUILayout.Button(enableRenderersLabel)) + { + List<MB3_MeshBakerCommon> selectedBakers = _getBakersFromTargets(targets); + foreach (MB3_MeshBakerCommon baker in selectedBakers) + { + baker.EnableDisableSourceObjectRenderers(!disableRendererInSource); + } + } + + meshBaker.ApplyModifiedProperties(); + meshBaker.SetIsDifferentCacheDirty(); + } + + public static void updateProgressBar(string msg, float progress) + { + EditorUtility.DisplayProgressBar("Combining Meshes", msg, progress); + } + + public static bool bake(MB3_MeshBakerCommon mom) + { + SerializedObject so = null; + return bake(mom, ref so); + } + + private List<MB3_MeshBakerCommon> _getBakersFromTargets(UnityEngine.Object[] targs) + { + List<MB3_MeshBakerCommon> outList = new List<MB3_MeshBakerCommon>(targs.Length); + for (int i = 0; i < targs.Length; i++) + { + outList.Add((MB3_MeshBakerCommon) targs[i]); + } + + return outList; + } + + private static void createEmptyPrefab(MB3_MeshBakerCommon mom, string folder, string prefabNameNoExtension, int idx) + { + if (prefabNameNoExtension != null && prefabNameNoExtension.Length > 0) + { + string prefabName = prefabNameNoExtension + idx; + GameObject go = new GameObject(prefabName); + string fullName = folder + "/" + prefabName + ".prefab"; + fullName = AssetDatabase.GenerateUniqueAssetPath(fullName); + Debug.Log(fullName); + PrefabUtility.CreatePrefab(fullName, go); + GameObject.DestroyImmediate(go); + SerializedObject so = new SerializedObject(mom); + so.FindProperty("resultPrefab").objectReferenceValue = (GameObject)AssetDatabase.LoadAssetAtPath(fullName, typeof(GameObject)); + so.ApplyModifiedProperties(); + } + } + + /// <summary> + /// Bakes a combined mesh. + /// </summary> + /// <param name="mom"></param> + /// <param name="so">This is needed to work around a Unity bug where UnpackPrefabInstance corrupts + /// a SerializedObject. Only needed for bake into prefab.</param> + public static bool bake(MB3_MeshBakerCommon mom, ref SerializedObject so) + { + bool createdDummyTextureBakeResults = false; + bool success = false; + try + { + if (mom.meshCombiner.outputOption == MB2_OutputOptions.bakeIntoSceneObject || + mom.meshCombiner.outputOption == MB2_OutputOptions.bakeIntoPrefab) + { + success = MB3_MeshBakerEditorFunctions.BakeIntoCombined(mom, out createdDummyTextureBakeResults, ref so); + } + else + { + //bake meshes in place + if (mom is MB3_MeshBaker) + { + if (MB3_MeshCombiner.EVAL_VERSION) + { + Debug.LogError("Bake Meshes In Place is disabled in the evaluation version."); + } + else + { + MB2_ValidationLevel vl = Application.isPlaying ? MB2_ValidationLevel.quick : MB2_ValidationLevel.robust; + if (!MB3_MeshBakerRoot.DoCombinedValidate(mom, MB_ObjsToCombineTypes.prefabOnly, new MB3_EditorMethods(), vl)) return false; + + List<GameObject> objsToMesh = mom.GetObjectsToCombine(); + success = MB3_BakeInPlace.BakeMeshesInPlace((MB3_MeshCombinerSingle)((MB3_MeshBaker)mom).meshCombiner, objsToMesh, mom.bakeAssetsInPlaceFolderPath, mom.clearBuffersAfterBake, updateProgressBar); + } + } + else + { + Debug.LogError("Multi-mesh Baker components cannot be used for Bake In Place. Use an ordinary Mesh Baker object instead."); + } + } + mom.meshCombiner.CheckIntegrity(); + } + catch (Exception ex) + { + Debug.LogError(ex.Message + "\n" + ex.StackTrace.ToString()); + } + finally + { + if (createdDummyTextureBakeResults && mom.textureBakeResults != null) + { + MB_Utility.Destroy(mom.textureBakeResults); + mom.textureBakeResults = null; + } + EditorUtility.ClearProgressBar(); + } + return success; + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorInternal.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorInternal.cs.meta new file mode 100644 index 00000000..352855e4 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorInternal.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6187499e1f9c67c4b8f295a0d54cb7ba +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindow.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindow.cs new file mode 100644 index 00000000..faad34dc --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindow.cs @@ -0,0 +1,64 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEditor; +using UnityEngine; +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + public class MB3_MeshBakerEditorWindow : EditorWindow + { + MB3_MeshBakerEditorWindowAddObjectsTab addObjectsTab; + MB3_MeshBakerEditorWindowAnalyseSceneTab analyseSceneTab; + Vector2 scrollPos = Vector2.zero; + int selectedTab = 0; + GUIContent[] tabs = new GUIContent[] { new GUIContent("Analyse Scene & Generate Bakers"), new GUIContent("Search For Meshes To Add") }; + + [MenuItem("Window/Mesh Baker/Mesh Baker")] + static void Init() + { + MB3_MeshBakerEditorWindow me = (MB3_MeshBakerEditorWindow) EditorWindow.GetWindow(typeof(MB3_MeshBakerEditorWindow)); + } + + public void SetTarget(MB3_MeshBakerRoot targ) + { + if (addObjectsTab == null) addObjectsTab = new MB3_MeshBakerEditorWindowAddObjectsTab(); + addObjectsTab.target = targ; + } + + void OnGUI() + { + selectedTab = GUILayout.Toolbar(selectedTab, tabs); + scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Width(position.width), GUILayout.Height(position.height)); + + if (selectedTab == 0) + { + analyseSceneTab.drawTabAnalyseScene(position); + } + else + { + addObjectsTab.drawTabAddObjectsToBakers(); + } + + EditorGUILayout.EndScrollView(); + } + + void OnEnable() + { + if (addObjectsTab == null) addObjectsTab = new MB3_MeshBakerEditorWindowAddObjectsTab(); + if (analyseSceneTab == null) analyseSceneTab = new MB3_MeshBakerEditorWindowAnalyseSceneTab(); + addObjectsTab.OnEnable(); + } + + void OnDisable() + { + addObjectsTab.OnDisable(); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindow.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindow.cs.meta new file mode 100644 index 00000000..83991109 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindow.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 08905a0f08b77054d9ee3ee91b6a3804 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAddObjectsTab.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAddObjectsTab.cs new file mode 100644 index 00000000..a897a3f6 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAddObjectsTab.cs @@ -0,0 +1,589 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEditor; +using UnityEngine; +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + public class MB3_MeshBakerEditorWindowAddObjectsTab : MB3_MeshBakerEditorWindowInterface + { + static string[] LODLevelLabels = new string[] + { + "All LOD Levels", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" + }; + + static int[] LODLevelValues = new int[] + { + -1,0,1,2,3,4,5,6,7,8,9 + }; + + public MB3_MeshBakerRoot _target = null; + public MonoBehaviour target + { + get { return _target; } + set { _target = (MB3_MeshBakerRoot)value; } + } + + GameObject targetGO = null; + GameObject oldTargetGO = null; + MB3_TextureBaker textureBaker; + MB3_MeshBaker meshBaker; + UnityEngine.Object[] targs = new UnityEngine.Object[1]; + SerializedObject serializedObject; + + GUIContent GUIContentRegExpression = new GUIContent("Matches Regular Expression", @"A valid # regular express. Examples:" + "\n\n" + + @" ([A-Za-z0-9\-]+)(LOD1) matches one or more chars,numbers and hyphen ending with LOD1." + "\n\n" + + @" (Grass)([A-Za-z0-9\-\(\) ]+) matches the string 'Grass' followed by characters, numbers, hyphen, brackets or space." + "\n\n"); + + string helpBoxString = ""; + string regExParseError = ""; + bool onlyStaticObjects = false; + bool onlyEnabledObjects = false; + bool excludeMeshesWithOBuvs = true; + bool excludeMeshesAlreadyAddedToBakers = true; + int lodLevelToInclude = -1; + int lightmapIndex = -2; + string searchRegEx = ""; + Material shaderMat = null; + Material mat = null; + + bool tbFoldout = false; + bool mbFoldout = false; + + MB3_MeshBakerEditorInternal mbe = new MB3_MeshBakerEditorInternal(); + MB3_TextureBakerEditorInternal tbe = new MB3_TextureBakerEditorInternal(); + + public void OnEnable() + { + if (textureBaker != null) + { + serializedObject = new SerializedObject(textureBaker); + tbe.OnEnable(serializedObject); + } + else if (meshBaker != null) + { + serializedObject = new SerializedObject(meshBaker); + mbe.OnEnable(serializedObject); + } + } + + public void OnDisable() + { + tbe.OnDisable(); + mbe.OnDisable(); + } + + public void drawTabAddObjectsToBakers() + { + if (helpBoxString == null) helpBoxString = ""; + EditorGUILayout.HelpBox("To add, select one or more objects in the hierarchy view. Child Game Objects with MeshRender or SkinnedMeshRenderer will be added. Use the fields below to filter what is added." + + "To remove, use the fields below to filter what is removed.\n" + helpBoxString, UnityEditor.MessageType.None); + target = (MB3_MeshBakerRoot)EditorGUILayout.ObjectField("Target to add objects to", target, typeof(MB3_MeshBakerRoot), true); + + if (target != null) + { + targetGO = target.gameObject; + } + else + { + targetGO = null; + } + + if (targetGO != oldTargetGO && targetGO != null) + { + textureBaker = targetGO.GetComponent<MB3_TextureBaker>(); + meshBaker = targetGO.GetComponent<MB3_MeshBaker>(); + tbe = new MB3_TextureBakerEditorInternal(); + mbe = new MB3_MeshBakerEditorInternal(); + oldTargetGO = targetGO; + if (textureBaker != null) + { + serializedObject = new SerializedObject(textureBaker); + tbe.OnEnable(serializedObject); + } + else if (meshBaker != null) + { + serializedObject = new SerializedObject(meshBaker); + mbe.OnEnable(serializedObject); + } + } + + + EditorGUIUtility.labelWidth = 300; + onlyStaticObjects = EditorGUILayout.Toggle("Only Static Objects", onlyStaticObjects); + + onlyEnabledObjects = EditorGUILayout.Toggle("Only Enabled Objects", onlyEnabledObjects); + + excludeMeshesWithOBuvs = EditorGUILayout.Toggle("Exclude meshes with out-of-bounds UVs", excludeMeshesWithOBuvs); + + excludeMeshesAlreadyAddedToBakers = EditorGUILayout.Toggle("Exclude GameObjects already added to bakers", excludeMeshesAlreadyAddedToBakers); + + lodLevelToInclude = EditorGUILayout.IntPopup("Only include objects on LOD Level", lodLevelToInclude, LODLevelLabels, LODLevelValues); + + mat = (Material)EditorGUILayout.ObjectField("Using Material", mat, typeof(Material), true); + shaderMat = (Material)EditorGUILayout.ObjectField("Using Shader", shaderMat, typeof(Material), true); + + string[] lightmapDisplayValues = new string[257]; + int[] lightmapValues = new int[257]; + lightmapValues[0] = -2; + lightmapValues[1] = -1; + lightmapDisplayValues[0] = "don't filter on lightmapping"; + lightmapDisplayValues[1] = "not lightmapped"; + for (int i = 2; i < lightmapDisplayValues.Length; i++) + { + lightmapDisplayValues[i] = "" + i; + lightmapValues[i] = i; + } + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Using Lightmap Index "); + lightmapIndex = EditorGUILayout.IntPopup(lightmapIndex, + lightmapDisplayValues, + lightmapValues); + EditorGUILayout.EndHorizontal(); + if (regExParseError != null && regExParseError.Length > 0) + { + EditorGUILayout.HelpBox("Error In Regular Expression:\n" + regExParseError, MessageType.Error); + } + searchRegEx = EditorGUILayout.TextField(GUIContentRegExpression, searchRegEx); + + + EditorGUILayout.Separator(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Add Selected Meshes To Target")) + { + addSelectedObjects(); + } + if (GUILayout.Button("Remove Matching Meshes From Target")) + { + removeSelectedObjects(); + } + EditorGUILayout.EndHorizontal(); + + if (textureBaker != null) + { + MB_EditorUtil.DrawSeparator(); + tbFoldout = EditorGUILayout.Foldout(tbFoldout, "Texture Baker"); + if (tbFoldout) + { + if (targs == null) targs = new UnityEngine.Object[1]; + targs[0] = textureBaker; + tbe.DrawGUI(serializedObject, (MB3_TextureBaker)textureBaker, targs, typeof(MB3_MeshBakerEditorWindow)); + } + + } + if (meshBaker != null) + { + MB_EditorUtil.DrawSeparator(); + mbFoldout = EditorGUILayout.Foldout(mbFoldout, "Mesh Baker"); + if (mbFoldout) + { + if (targs == null) targs = new UnityEngine.Object[1]; + targs[0] = meshBaker; + mbe.DrawGUI(serializedObject, (MB3_MeshBaker)meshBaker, targs, typeof(MB3_MeshBakerEditorWindow)); + } + } + } + + List<GameObject> GetFilteredList(bool addingObjects) + { + List<GameObject> newMomObjs = new List<GameObject>(); + MB3_MeshBakerRoot mom = (MB3_MeshBakerRoot)target; + if (mom == null) + { + Debug.LogError("Must select a target MeshBaker"); + return newMomObjs; + } + + GameObject dontAddMe = null; + Renderer r = MB_Utility.GetRenderer(mom.gameObject); + if (r != null) + { //make sure that this MeshBaker object is not in list + dontAddMe = r.gameObject; + } + + MB3_MeshBakerRoot[] allBakers = GameObject.FindObjectsOfType<MB3_MeshBakerRoot>(); + HashSet<GameObject> objectsAlreadyIncludedInBakers = new HashSet<GameObject>(); + + if (addingObjects) + { + for (int i = 0; i < allBakers.Length; i++) + { + List<GameObject> objsToCombine = allBakers[i].GetObjectsToCombine(); + for (int j = 0; j < objsToCombine.Count; j++) + { + if (objsToCombine[j] != null) objectsAlreadyIncludedInBakers.Add(objsToCombine[j]); + } + } + } + + GameObject[] gos = Selection.gameObjects; + if (gos.Length == 0) + { + Debug.LogWarning("No objects selected in hierarchy view. Nothing added. Try selecting some objects."); + return newMomObjs; + } + + List<GameObject> mrs = new List<GameObject>(); + for (int i = 0; i < gos.Length; i++) + { + GameObject go = gos[i]; + Renderer[] rs = go.GetComponentsInChildren<Renderer>(true); + for (int j = 0; j < rs.Length; j++) + { + if (rs[j] is MeshRenderer || rs[j] is SkinnedMeshRenderer) + { + mrs.Add(rs[j].gameObject); + } + } + } + + newMomObjs = FilterList(mrs, objectsAlreadyIncludedInBakers, dontAddMe, addingObjects); + return newMomObjs; + } + + int GetLODLevelForRenderer(Renderer r) + { + if (r != null) + { + LODGroup lodGroup = r.GetComponentInParent<LODGroup>(); + if (lodGroup != null) + { + LOD[] lods = lodGroup.GetLODs(); + for (int lodIdx = 0; lodIdx < lods.Length; lodIdx++) + { + Renderer[] rs = lods[lodIdx].renderers; + for (int j = 0; j < rs.Length; j++) + { + if (rs[j] == r) + { + return lodIdx; + } + } + } + } + } + return 0; + } + + List<GameObject> FilterList(List<GameObject> mrss, + HashSet<GameObject> objectsAlreadyIncludedInBakers, + GameObject dontAddMe, + bool addingObjects) + { + int numInSelection = 0; + int numStaticExcluded = 0; + int numEnabledExcluded = 0; + int numLightmapExcluded = 0; + int numLodLevelExcluded = 0; + int numOBuvExcluded = 0; + int numMatExcluded = 0; + int numShaderExcluded = 0; + int numRegExExcluded = 0; + int numAlreadyIncludedExcluded = 0; + System.Text.RegularExpressions.Regex regex = null; + if (searchRegEx != null && searchRegEx.Length > 0) + { + + try + { + regex = new System.Text.RegularExpressions.Regex(searchRegEx); + regExParseError = ""; + } + catch (Exception ex) + { + regExParseError = ex.Message; + } + } + + Dictionary<int, MB_Utility.MeshAnalysisResult> meshAnalysisResultsCache = new Dictionary<int, MB_Utility.MeshAnalysisResult>(); //cache results + List<GameObject> newMomObjs = new List<GameObject>(); + for (int j = 0; j < mrss.Count; j++) + { + if (mrss[j] == null) + { + continue; + } + Renderer mrs = mrss[j].GetComponent<Renderer>(); + if (mrs is MeshRenderer || mrs is SkinnedMeshRenderer) + { + if (mrs.GetComponent<TextMesh>() != null) + { + continue; //don't add TextMeshes + } + + numInSelection++; + if (!newMomObjs.Contains(mrs.gameObject)) + { + bool addMe = true; + if (!mrs.gameObject.isStatic && onlyStaticObjects) + { + numStaticExcluded++; + addMe = false; + continue; + } + + if ((!mrs.enabled || !mrs.gameObject.activeInHierarchy) && onlyEnabledObjects) + { + numEnabledExcluded++; + addMe = false; + continue; + } + + if (lightmapIndex != -2) + { + if (mrs.lightmapIndex != lightmapIndex) + { + numLightmapExcluded++; + addMe = false; + continue; + } + } + + if (lodLevelToInclude == -1) + { + // not filtering on LODLevel + } + else + { + if (GetLODLevelForRenderer(mrs) != lodLevelToInclude) + { + numLodLevelExcluded++; + addMe = false; + continue; + } + } + + // only do this check when adding objects. If removing objects shouldn't do it + if (addingObjects && + excludeMeshesAlreadyAddedToBakers && + objectsAlreadyIncludedInBakers.Contains(mrs.gameObject)) + { + numAlreadyIncludedExcluded++; + addMe = false; + continue; + } + + Mesh mm = MB_Utility.GetMesh(mrs.gameObject); + if (mm != null) + { + MB_Utility.MeshAnalysisResult mar; + if (!meshAnalysisResultsCache.TryGetValue(mm.GetInstanceID(), out mar)) + { + MB_Utility.hasOutOfBoundsUVs(mm, ref mar); + meshAnalysisResultsCache.Add(mm.GetInstanceID(), mar); + } + if (mar.hasOutOfBoundsUVs && excludeMeshesWithOBuvs) + { + numOBuvExcluded++; + addMe = false; + continue; + } + } + + if (shaderMat != null) + { + Material[] nMats = mrs.sharedMaterials; + bool usesShader = false; + foreach (Material nMat in nMats) + { + if (nMat != null && nMat.shader == shaderMat.shader) + { + usesShader = true; + } + } + if (!usesShader) + { + numShaderExcluded++; + addMe = false; + continue; + } + } + + if (mat != null) + { + Material[] nMats = mrs.sharedMaterials; + bool usesMat = false; + foreach (Material nMat in nMats) + { + if (nMat == mat) + { + usesMat = true; + } + } + if (!usesMat) + { + numMatExcluded++; + addMe = false; + continue; + } + } + + if (regex != null) + { + if (!regex.IsMatch(mrs.gameObject.name)) + { + numRegExExcluded++; + addMe = false; + continue; + } + } + + if (addMe && mrs.gameObject != dontAddMe) + { + if (!newMomObjs.Contains(mrs.gameObject)) + { + newMomObjs.Add(mrs.gameObject); + } + } + } + } + } + + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + //sb.AppendFormat("Total objects in selection {0}\n", numInSelection); + //Debug.Log( "Total objects in selection " + numInSelection); + if (numStaticExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they were not static\n", numStaticExcluded); + Debug.Log(numStaticExcluded + " objects were excluded because they were not static\n"); + } + if (numEnabledExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they were disabled\n", numEnabledExcluded); + Debug.Log(numEnabledExcluded + " objects were excluded because they were disabled\n"); + } + if (numOBuvExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they were had out of bounds uvs\n", numOBuvExcluded); + Debug.Log(numOBuvExcluded + " objects were excluded because they had out of bounds uvs\n"); + } + if (numLightmapExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they did not match lightmap filter.\n", numLightmapExcluded); + Debug.Log(numLightmapExcluded + " objects did not match lightmap filter.\n"); + } + if (numLodLevelExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they did not match the selected LOD level filter.\n", numLodLevelExcluded); + Debug.Log(numLodLevelExcluded + " objects did not match LOD level filter.\n"); + } + if (numShaderExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they did not use the selected shader.\n", numShaderExcluded); + Debug.Log(numShaderExcluded + " objects were excluded because they did not use the selected shader.\n"); + } + if (numMatExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they did not use the selected material.\n", numMatExcluded); + Debug.Log(numMatExcluded + " objects were excluded because they did not use the selected material.\n"); + } + if (numRegExExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they did not match the regular expression.\n", numRegExExcluded); + Debug.Log(numRegExExcluded + " objects were excluded because they did not match the regular expression.\n"); + } + if (numAlreadyIncludedExcluded > 0) + { + sb.AppendFormat(" {0} objects were excluded because they did were already included in other bakers.\n", numAlreadyIncludedExcluded); + Debug.Log(numAlreadyIncludedExcluded + " objects were excluded because they did were already included in other bakers.\n"); + } + + helpBoxString = sb.ToString(); + return newMomObjs; + } + + void removeSelectedObjects() + { + MB3_MeshBakerRoot mom = (MB3_MeshBakerRoot)target; + if (mom == null) + { + Debug.LogError("Must select a target MeshBaker"); + return; + } + List<GameObject> objsToCombine = mom.GetObjectsToCombine(); + HashSet<GameObject> objectsAlreadyIncludedInBakers = new HashSet<GameObject>(); + GameObject dontAddMe = null; + Renderer r = MB_Utility.GetRenderer(mom.gameObject); + if (r != null) + { //make sure that this MeshBaker object is not in list + dontAddMe = r.gameObject; + } + + List<GameObject> objsSelectedMatchingFilter = GetFilteredList(addingObjects:false); + Debug.Log("Matching filter " + objsSelectedMatchingFilter.Count); + List<GameObject> objsToRemove = new List<GameObject>(); + for (int i = 0; i < objsSelectedMatchingFilter.Count; i++) + { + if (objsToCombine.Contains(objsSelectedMatchingFilter[i])) + { + objsToRemove.Add(objsSelectedMatchingFilter[i]); + } + } + + MBVersionEditor.RegisterUndo(mom, "Remove Objects"); + for (int i = 0; i < objsToRemove.Count; i++) + { + objsToCombine.Remove(objsToRemove[i]); + } + + SerializedObject so = new SerializedObject(mom); + so.SetIsDifferentCacheDirty(); + Debug.Log("Removed " + objsToRemove.Count + " objects from " + mom.name); + helpBoxString += String.Format("\nRemoved {0} objects from {1}", objsToRemove.Count, mom.name); + } + + void addSelectedObjects() + { + MB3_MeshBakerRoot mom = (MB3_MeshBakerRoot)target; + if (mom == null) + { + Debug.LogError("Must select a target MeshBaker to add objects to"); + return; + } + + List<GameObject> newMomObjs = GetFilteredList(addingObjects:true); + MBVersionEditor.RegisterUndo(mom, "Add Objects"); + List<GameObject> momObjs = mom.GetObjectsToCombine(); + int numAdded = 0; + int numAlreadyInList = 0; + for (int i = 0; i < newMomObjs.Count; i++) + { + if (!momObjs.Contains(newMomObjs[i])) + { + momObjs.Add(newMomObjs[i]); + numAdded++; + } + else + { + numAlreadyInList++; + } + } + + SerializedObject so = new SerializedObject(mom); + so.SetIsDifferentCacheDirty(); + if (numAlreadyInList > 0) + { + Debug.Log(String.Format("Skipped adding {0} objects to Target because these objects had already been added to this Target. ", numAlreadyInList)); + } + + if (numAdded == 0) + { + Debug.LogWarning("Added 0 objects. Make sure some or all objects are selected in the hierarchy view. Also check ths 'Only Static Objects', 'Using Material' and 'Using Shader' settings"); + } + else + { + Debug.Log(string.Format("Added {0} objects to {1}. ", numAdded, mom.name)); + } + + helpBoxString += String.Format("\nAdded {0} objects to {1}", numAdded, mom.name); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAddObjectsTab.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAddObjectsTab.cs.meta new file mode 100644 index 00000000..2c9120f1 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAddObjectsTab.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4caf9fbf2fe59bf479dc70cb752462a6 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAnalyseSceneTab.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAnalyseSceneTab.cs new file mode 100644 index 00000000..efc6d6df --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAnalyseSceneTab.cs @@ -0,0 +1,751 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEditor; +using UnityEngine; +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + public class MB3_MeshBakerEditorWindowAnalyseSceneTab + { + const int NUM_FILTERS = 5; + bool writeReportFile = false; + bool splitAtlasesSoMeshesFit = false; + int atlasSize = 4096; + string generate_AssetsFolder = ""; + List<List<GameObjectFilterInfo>> sceneAnalysisResults = new List<List<GameObjectFilterInfo>>(); + bool[] sceneAnalysisResultsFoldouts = new bool[0]; + int[] groupByFilterIdxs = new int[NUM_FILTERS]; + string[] groupByOptionNames; + IGroupByFilter[] groupByOptionFilters; + IGroupByFilter[] filters; + Vector2 scrollPos2 = Vector2.zero; + + GUIContent gc_atlasSize = new GUIContent("Max Atlas Size", ""); + GUIContent gc_splitAtlasesSoMeshesFit = new GUIContent("Split Groups If Textures Would Exceed Atlas Size (beta)", "If combining the textures into a single atlas would exceed the maximum atlas size then create multiple atlases. Othersize texture sizes are reduced."); + + public static bool InterfaceFilter(Type typeObj, System.Object criteriaObj) + { + return typeObj.ToString() == criteriaObj.ToString(); + } + + void populateGroupByFilters() + { + string qualifiedInterfaceName = "DigitalOpus.MB.Core.IGroupByFilter"; + var interfaceFilter = new TypeFilter(InterfaceFilter); + List<Type> types = new List<Type>(); + foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies()) + { + System.Collections.IEnumerable typesIterator = null; + try + { + typesIterator = ass.GetTypes(); + } + catch (Exception e) + { + //Debug.Log("The assembly that I could not read types for was: " + ass.GetName()); + //suppress error + e.Equals(null); + } + if (typesIterator != null) + { + foreach (Type ty in ass.GetTypes()) + { + var myInterfaces = ty.FindInterfaces(interfaceFilter, qualifiedInterfaceName); + if (myInterfaces.Length > 0) + { + types.Add(ty); + } + } + } + } + + List<string> filterNames = new List<string>(); + List<IGroupByFilter> filters = new List<IGroupByFilter>(); + filterNames.Add("None"); + filters.Add(null); + foreach (Type tt in types) + { + if (!tt.IsAbstract && !tt.IsInterface) + { + IGroupByFilter instance = (IGroupByFilter)System.Activator.CreateInstance(tt); + filterNames.Add(instance.GetName()); + filters.Add(instance); + } + } + groupByOptionNames = filterNames.ToArray(); + groupByOptionFilters = filters.ToArray(); + } + + public void drawTabAnalyseScene(Rect position) + { + + //first time we are displaying collect the filters + if (groupByOptionNames == null || groupByOptionNames.Length == 0) + { + //var types = AppDomain.CurrentDomain.GetAssemblies() + // .SelectMany(s => s.GetTypes()) + // .Where(p => type.IsAssignableFrom(p)); + populateGroupByFilters(); + + //set filter initial values + for (int i = 0; i < groupByOptionFilters.Length; i++) + { + if (groupByOptionFilters[i] is GroupByShader) + { + groupByFilterIdxs[0] = i; + break; + } + } + for (int i = 0; i < groupByOptionFilters.Length; i++) + { + if (groupByOptionFilters[i] is GroupByStatic) + { + groupByFilterIdxs[1] = i; + break; + } + } + for (int i = 0; i < groupByOptionFilters.Length; i++) + { + if (groupByOptionFilters[i] is GroupByRenderType) + { + groupByFilterIdxs[2] = i; + break; + } + } + for (int i = 0; i < groupByOptionFilters.Length; i++) + { + if (groupByOptionFilters[i] is GroupByOutOfBoundsUVs) + { + groupByFilterIdxs[3] = i; + break; + } + } + groupByFilterIdxs[4] = 0; //none + } + if (groupByFilterIdxs == null || groupByFilterIdxs.Length < NUM_FILTERS) + { + groupByFilterIdxs = new int[]{ + 0,0,0,0,0 + }; + } + EditorGUILayout.HelpBox("List shaders in scene prints a report to the console of shaders and which objects use them. This is useful for planning which objects to combine.", UnityEditor.MessageType.None); + + groupByFilterIdxs[0] = EditorGUILayout.Popup("Group By:", groupByFilterIdxs[0], groupByOptionNames); + for (int i = 1; i < NUM_FILTERS; i++) + { + groupByFilterIdxs[i] = EditorGUILayout.Popup("Then Group By:", groupByFilterIdxs[i], groupByOptionNames); + } + + EditorGUILayout.BeginHorizontal(); + float oldLabelWidth = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = 300; + splitAtlasesSoMeshesFit = EditorGUILayout.Toggle(gc_splitAtlasesSoMeshesFit, splitAtlasesSoMeshesFit); + EditorGUIUtility.labelWidth = oldLabelWidth; + bool enableAtlasField = true; + if (splitAtlasesSoMeshesFit) + { + enableAtlasField = false; + } + EditorGUI.BeginDisabledGroup(enableAtlasField); + atlasSize = EditorGUILayout.IntField(gc_atlasSize, atlasSize); + EditorGUI.EndDisabledGroup(); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Select Folder For Combined Material Assets")) + { + generate_AssetsFolder = EditorUtility.SaveFolderPanel("Create Combined Material Assets In Folder", "", ""); + generate_AssetsFolder = "Assets" + generate_AssetsFolder.Replace(Application.dataPath, "") + "/"; + } + EditorGUILayout.LabelField("Folder: " + generate_AssetsFolder); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("List Shaders In Scene")) + { + EditorUtility.DisplayProgressBar("Analysing Scene", "", .05f); + try + { + listMaterialsInScene(); + } + catch (Exception ex) + { + Debug.LogError(ex.Message + "\n" + ex.StackTrace.ToString()); + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + + if (GUILayout.Button("Bake Every MeshBaker In Scene")) + { + try + { + MB3_TextureBaker[] texBakers = (MB3_TextureBaker[]) GameObject.FindObjectsOfType(typeof(MB3_TextureBaker)); + for (int i = 0; i < texBakers.Length; i++) + { + texBakers[i].CreateAtlases(updateProgressBar, true, new MB3_EditorMethods()); + } + MB3_MeshBakerCommon[] mBakers = (MB3_MeshBakerCommon[]) GameObject.FindObjectsOfType(typeof(MB3_MeshBakerCommon)); + bool createTempMaterialBakeResult; + for (int i = 0; i < mBakers.Length; i++) + { + if (mBakers[i].textureBakeResults != null) + { + MB3_MeshBakerEditorFunctions.BakeIntoCombined(mBakers[i], out createTempMaterialBakeResult); + } + } + } + catch (Exception ex) + { + Debug.LogError(ex.Message + "\n" + ex.StackTrace.ToString()); + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + EditorGUILayout.EndHorizontal(); + + if (sceneAnalysisResults.Count > 0) + { + float height = position.height - 150f; + if (height < 500f) height = 500f; + MB_EditorUtil.DrawSeparator(); + scrollPos2 = EditorGUILayout.BeginScrollView(scrollPos2, false, true); //(scrollPos2,, GUILayout.Width(position.width - 20f), GUILayout.Height(height)); + EditorGUILayout.LabelField("Shaders In Scene", EditorStyles.boldLabel); + for (int i = 0; i < sceneAnalysisResults.Count; i++) + { + List<GameObjectFilterInfo> gows = sceneAnalysisResults[i]; + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Generate Baker", GUILayout.Width(200))) + { + createAndSetupBaker(gows, generate_AssetsFolder); + } + if (GUILayout.Button("Select", GUILayout.Width(200))) + { + UnityEngine.Object[] selected = new UnityEngine.Object[gows.Count]; + for (int j = 0; j < gows.Count; j++) + { + selected[j] = gows[j].go; + } + Selection.objects = selected; + SceneView.lastActiveSceneView.FrameSelected(); + } + + string descr = String.Format("Objs={0} AtlasIndex={1} {2}", gows.Count, gows[0].atlasIndex, gows[0].GetDescription(filters, gows[0])); + + EditorGUILayout.LabelField(descr, EditorStyles.wordWrappedLabel); + EditorGUILayout.EndHorizontal(); + sceneAnalysisResultsFoldouts[i] = EditorGUILayout.Foldout(sceneAnalysisResultsFoldouts[i], ""); + if (sceneAnalysisResultsFoldouts[i]) + { + EditorGUI.indentLevel += 1; + for (int j = 0; j < gows.Count; j++) + { + if (gows[j].go != null) + { + EditorGUILayout.LabelField(gows[j].go.name + " " + gows[j].GetDescription(filters, gows[j])); + } + } + EditorGUI.indentLevel -= 1; + } + + } + EditorGUILayout.EndScrollView(); + MB_EditorUtil.DrawSeparator(); + } + } + + int GetLODLevelForRenderer(Renderer r) + { + if (r != null) + { + LODGroup lodGroup = r.GetComponentInParent<LODGroup>(); + if (lodGroup != null) + { + LOD[] lods = lodGroup.GetLODs(); + for (int lodIdx = 0; lodIdx < lods.Length; lodIdx++) + { + Renderer[] rs = lods[lodIdx].renderers; + for (int j = 0; j < rs.Length; j++) + { + if (rs[j] == r) + { + return lodIdx; + } + } + } + } + } + return 0; + } + + void listMaterialsInScene() + { + if (!ValidateGroupByFields()) return; + if (groupByOptionFilters == null) + { + populateGroupByFilters(); + } + + List<IGroupByFilter> gbfs = new List<IGroupByFilter>(); + for (int i = 0; i < groupByFilterIdxs.Length; i++) + { + if (groupByFilterIdxs[i] != 0) + { + gbfs.Add(groupByOptionFilters[groupByFilterIdxs[i]]); + } + } + filters = gbfs.ToArray(); + + //Get All Objects Already In a list of objects to be combined + MB3_MeshBakerRoot[] allBakers = GameObject.FindObjectsOfType<MB3_MeshBakerRoot>(); + HashSet<GameObject> objectsAlreadyIncludedInBakers = new HashSet<GameObject>(); + for (int i = 0; i < allBakers.Length; i++) + { + List<GameObject> objsToCombine = allBakers[i].GetObjectsToCombine(); + for (int j = 0; j < objsToCombine.Count; j++) + { + if (objsToCombine[j] != null) objectsAlreadyIncludedInBakers.Add(objsToCombine[j]); + } + } + + //collect all renderers in scene + List<GameObjectFilterInfo> gameObjects = new List<GameObjectFilterInfo>(); + Renderer[] rs = (Renderer[]) GameObject.FindObjectsOfType(typeof(Renderer)); + // Profile.StartProfile("listMaterialsInScene1"); + EditorUtility.DisplayProgressBar("Analysing Scene", "Collecting Renderers", .25f); + for (int i = 0; i < rs.Length; i++) + { + Renderer r = rs[i]; + if (r is MeshRenderer || r is SkinnedMeshRenderer) + { + if (r.GetComponent<TextMesh>() != null) + { + continue; //don't add TextMeshes + } + GameObjectFilterInfo goaw = new GameObjectFilterInfo(r.gameObject, objectsAlreadyIncludedInBakers, filters); + if (goaw.materials.Length > 0) //don't consider renderers with no materials + { + gameObjects.Add(goaw); + EditorUtility.DisplayProgressBar("Analysing Scene", "Collecting Renderer For " + r.name, .1f); + } + } + } + + //analyse meshes + Dictionary<int, MB_Utility.MeshAnalysisResult> meshAnalysisResultCache = new Dictionary<int, MB_Utility.MeshAnalysisResult>(); + int totalVerts = 0; + for (int i = 0; i < gameObjects.Count; i++) + { + string rpt = String.Format("Processing {0} [{1} of {2}]", gameObjects[i].go.name, i, gameObjects.Count); + EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " A", .6f); + Mesh mm = MB_Utility.GetMesh(gameObjects[i].go); + int nVerts = 0; + if (mm != null) + { + nVerts += mm.vertexCount; + MB_Utility.MeshAnalysisResult mar; + if (!meshAnalysisResultCache.TryGetValue(mm.GetInstanceID(), out mar)) + { + + EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Check Out Of Bounds UVs", .6f); + MB_Utility.hasOutOfBoundsUVs(mm, ref mar); + //Rect dummy = mar.uvRect; + MB_Utility.doSubmeshesShareVertsOrTris(mm, ref mar); + meshAnalysisResultCache.Add(mm.GetInstanceID(), mar); + } + if (mar.hasOutOfBoundsUVs) + { + int w = (int)mar.uvRect.width; + int h = (int)mar.uvRect.height; + gameObjects[i].outOfBoundsUVs = true; + gameObjects[i].warning += " [WARNING: has uvs outside the range (0,1) tex is tiled " + w + "x" + h + " times]"; + } + if (mar.hasOverlappingSubmeshVerts) + { + gameObjects[i].submeshesOverlap = true; + gameObjects[i].warning += " [WARNING: Submeshes share verts or triangles. 'Multiple Combined Materials' feature may not work.]"; + } + } + totalVerts += nVerts; + EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Validate OBuvs Multi Material", .6f); + Renderer mr = gameObjects[i].go.GetComponent<Renderer>(); + if (!MB_Utility.AreAllSharedMaterialsDistinct(mr.sharedMaterials)) + { + gameObjects[i].warning += " [WARNING: Object uses same material on multiple submeshes. This may produce poor results when used with multiple materials or fix out of bounds uvs.]"; + } + } + + List<GameObjectFilterInfo> objsNotAddedToBaker = new List<GameObjectFilterInfo>(); + + + Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap = sortIntoBakeGroups3(gameObjects, objsNotAddedToBaker, filters, splitAtlasesSoMeshesFit, atlasSize); + + sceneAnalysisResults = new List<List<GameObjectFilterInfo>>(); + foreach (GameObjectFilterInfo gow in gs2bakeGroupMap.Keys) + { + List<List<GameObjectFilterInfo>> gows = gs2bakeGroupMap[gow]; + for (int i = 0; i < gows.Count; i++) //if split atlases by what fits in atlas + { + sceneAnalysisResults.Add(gows[i]); + } + } + sceneAnalysisResultsFoldouts = new bool[sceneAnalysisResults.Count]; + for (int i = 0; i < sceneAnalysisResults.Count; i++) { sceneAnalysisResultsFoldouts[i] = true; } + + if (writeReportFile) + { + string fileName = Application.dataPath + "/MeshBakerSceneAnalysisReport.txt"; + try + { + System.IO.File.WriteAllText(fileName, generateSceneAnalysisReport(gs2bakeGroupMap, objsNotAddedToBaker)); + Debug.Log(String.Format("Wrote scene analysis file to '{0}'. This file contains a list of all renderers and the materials/shaders that they use. It is designed to be opened with a spreadsheet.", fileName)); + } + catch (Exception e) + { + e.GetHashCode(); //supress compiler warning + Debug.Log("Failed to write file: " + fileName); + } + } + } + + string generateSceneAnalysisReport(Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap, List<GameObjectFilterInfo> objsNotAddedToBaker) + { + string outStr = "(Click me, if I am too big copy and paste me into a spreadsheet or text editor)\n";// Materials in scene " + shader2GameObjects.Keys.Count + " and the objects that use them:\n"; + outStr += "\t\tOBJECT NAME\tLIGHTMAP INDEX\tSTATIC\tOVERLAPPING SUBMESHES\tOUT-OF-BOUNDS UVs\tNUM MATS\tMATERIAL\tWARNINGS\n"; + + int totalVerts = 0; + string outStr2 = ""; + foreach (List<List<GameObjectFilterInfo>> goss in gs2bakeGroupMap.Values) + { + for (int atlasIdx = 0; atlasIdx < goss.Count; atlasIdx++) + { + List<GameObjectFilterInfo> gos = goss[atlasIdx]; + outStr2 = ""; + totalVerts = 0; + gos.Sort(); + for (int i = 0; i < gos.Count; i++) + { + totalVerts += gos[i].numVerts; + string matStr = ""; + Renderer mr = gos[i].go.GetComponent<Renderer>(); + foreach (Material mmm in mr.sharedMaterials) + { + matStr += "[" + mmm + "] "; + } + outStr2 += "\t\t" + gos[i].go.name + " (" + gos[i].numVerts + " verts)\t" + gos[i].lightmapIndex + "\t" + gos[i].isStatic + "\t" + gos[i].submeshesOverlap + "\t" + gos[i].outOfBoundsUVs + "\t" + gos[i].numMaterials + "\t" + matStr + "\t" + gos[i].warning + "\n"; + } + outStr2 = "\t" + gos[0].shaderName + " (" + totalVerts + " verts): \n" + outStr2; + outStr += outStr2; + } + } + if (objsNotAddedToBaker.Count > 0) + { + outStr += "Other objects\n"; + string shaderName = ""; + totalVerts = 0; + List<GameObjectFilterInfo> gos1 = objsNotAddedToBaker; + gos1.Sort(); + outStr2 = ""; + for (int i = 0; i < gos1.Count; i++) + { + if (!shaderName.Equals(objsNotAddedToBaker[i].shaderName)) + { + outStr2 += "\t" + gos1[0].shaderName + "\n"; + shaderName = objsNotAddedToBaker[i].shaderName; + } + totalVerts += gos1[i].numVerts; + string matStr = ""; + Renderer mr = gos1[i].go.GetComponent<Renderer>(); + foreach (Material mmm in mr.sharedMaterials) + { + matStr += "[" + mmm + "] "; + } + outStr2 += "\t\t" + gos1[i].go.name + " (" + gos1[i].numVerts + " verts)\t" + gos1[i].lightmapIndex + "\t" + gos1[i].isStatic + "\t" + gos1[i].submeshesOverlap + "\t" + gos1[i].outOfBoundsUVs + "\t" + gos1[i].numMaterials + "\t" + matStr + "\t" + gos1[i].warning + "\n"; + } + outStr += outStr2; + } + + return outStr; + } + + bool MaterialsAreTheSame(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + HashSet<Material> aMats = new HashSet<Material>(); + for (int i = 0; i < a.materials.Length; i++) aMats.Add(a.materials[i]); + HashSet<Material> bMats = new HashSet<Material>(); + for (int i = 0; i < b.materials.Length; i++) bMats.Add(b.materials[i]); + return aMats.SetEquals(bMats); + } + + bool ShadersAreTheSame(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + HashSet<Shader> aMats = new HashSet<Shader>(); + for (int i = 0; i < a.shaders.Length; i++) aMats.Add(a.shaders[i]); + HashSet<Shader> bMats = new HashSet<Shader>(); + for (int i = 0; i < b.shaders.Length; i++) bMats.Add(b.shaders[i]); + return aMats.SetEquals(bMats); + } + + public static Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> sortIntoBakeGroups3(List<GameObjectFilterInfo> gameObjects, List<GameObjectFilterInfo> objsNotAddedToBaker, IGroupByFilter[] filters, bool splitAtlasesSoMeshesFit, int atlasSize) + { + + Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap = new Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>>(); + + List<GameObjectFilterInfo> gos = gameObjects; + if (gos.Count < 1) return gs2bakeGroupMap; + + gos.Sort(); + List<List<GameObjectFilterInfo>> l = null; + GameObjectFilterInfo key = gos[0]; + for (int i = 0; i < gos.Count; i++) + { + GameObjectFilterInfo goaw = gos[i]; + //compare with key and decide if we need a new list + for (int j = 0; j < filters.Length; j++) + { + if (filters[j] != null && filters[j].Compare(key, goaw) != 0) l = null; + } + if (l == null) + { + l = new List<List<GameObjectFilterInfo>>(); + l.Add(new List<GameObjectFilterInfo>()); + gs2bakeGroupMap.Add(gos[i], l); + key = gos[i]; + } + l[0].Add(gos[i]); + } + + //now that objects have been grouped by the sort criteria we can see how many atlases are needed + Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap2 = new Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>>(); + if (splitAtlasesSoMeshesFit) + { + foreach (GameObjectFilterInfo k in gs2bakeGroupMap.Keys) + { + List<GameObjectFilterInfo> vs = gs2bakeGroupMap[k][0]; + List<GameObject> objsInGroup = new List<GameObject>(); + for (int i = 0; i < vs.Count; i++) + { + objsInGroup.Add(vs[i].go); + } + MB3_TextureCombiner tc = new MB3_TextureCombiner(); + tc.maxAtlasSize = atlasSize; + tc.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; + tc.LOG_LEVEL = MB2_LogLevel.warn; + List<AtlasPackingResult> packingResults = new List<AtlasPackingResult>(); + Material tempResMat = k.materials[0]; //we don't write to the materials so can use this as the result material + MB_AtlasesAndRects tempAtlasesAndRects = new MB_AtlasesAndRects(); + if (tc.CombineTexturesIntoAtlases(null, tempAtlasesAndRects, tempResMat, objsInGroup, null, null, packingResults, + onlyPackRects:true, splitAtlasWhenPackingIfTooBig:false)) + { + List<List<GameObjectFilterInfo>> atlasGroups = new List<List<GameObjectFilterInfo>>(); + for (int i = 0; i < packingResults.Count; i++) + { + List<GameObjectFilterInfo> ngos = new List<GameObjectFilterInfo>(); + List<MB_MaterialAndUVRect> matsData = (List<MB_MaterialAndUVRect>)packingResults[i].data; + for (int j = 0; j < matsData.Count; j++) + { + for (int kk = 0; kk < matsData[j].objectsThatUse.Count; kk++) + { + GameObjectFilterInfo gofi = vs.Find(x => x.go == matsData[j].objectsThatUse[kk]); + //Debug.Assert(gofi != null); + ngos.Add(gofi); + } + } + ngos[0].atlasIndex = (short)i; + atlasGroups.Add(ngos); + } + gs2bakeGroupMap2.Add(k, atlasGroups); + } + else + { + gs2bakeGroupMap2.Add(k, gs2bakeGroupMap[k]); + } + } + } + else + { + gs2bakeGroupMap2 = gs2bakeGroupMap; + } + return gs2bakeGroupMap2; + } + + void createBakers(Dictionary<GameObjectFilterInfo, List<GameObjectFilterInfo>> gs2bakeGroupMap, List<GameObjectFilterInfo> objsNotAddedToBaker) + { + string s = ""; + int numBakers = 0; + int numObjsAdded = 0; + + if (generate_AssetsFolder == null || generate_AssetsFolder == "") + { + Debug.LogError("Need to choose a folder for saving the combined material assets."); + return; + } + + List<GameObjectFilterInfo> singletonObjsNotAddedToBaker = new List<GameObjectFilterInfo>(); + foreach (List<GameObjectFilterInfo> gaw in gs2bakeGroupMap.Values) + { + if (gaw.Count > 1) + { + numBakers++; + numObjsAdded += gaw.Count; + createAndSetupBaker(gaw, generate_AssetsFolder); + s += " Created meshbaker for shader=" + gaw[0].shaderName + " lightmap=" + gaw[0].lightmapIndex + " OBuvs=" + gaw[0].outOfBoundsUVs + "\n"; + } + else + { + singletonObjsNotAddedToBaker.Add(gaw[0]); + } + } + s = "Created " + numBakers + " bakers. Added " + numObjsAdded + " objects\n" + s; + Debug.Log(s); + s = "Objects not added=" + objsNotAddedToBaker.Count + " objects that have unique material=" + singletonObjsNotAddedToBaker.Count + "\n"; + for (int i = 0; i < objsNotAddedToBaker.Count; i++) + { + s += " " + objsNotAddedToBaker[i].go.name + + " isStatic=" + objsNotAddedToBaker[i].isStatic + + " submeshesOverlap" + objsNotAddedToBaker[i].submeshesOverlap + + " numMats=" + objsNotAddedToBaker[i].numMaterials + "\n"; + } + for (int i = 0; i < singletonObjsNotAddedToBaker.Count; i++) + { + s += " " + singletonObjsNotAddedToBaker[i].go.name + " single\n"; + } + Debug.Log(s); + } + + void createAndSetupBaker(List<GameObjectFilterInfo> gaws, string pthRoot) + { + for (int i = gaws.Count - 1; i >= 0; i--) + { + if (gaws[i].go == null) gaws.RemoveAt(i); + } + if (gaws.Count < 1) + { + Debug.LogError("No game objects."); + return; + } + + if (pthRoot == null || pthRoot == "") + { + Debug.LogError("Folder for saving created assets was not set."); + return; + } + + int numVerts = 0; + for (int i = 0; i < gaws.Count; i++) + { + if (gaws[i].go != null) + { + numVerts = gaws[i].numVerts; + } + } + + GameObject newMeshBaker = null; + if (numVerts >= 65535) + { + newMeshBaker = MB3_MultiMeshBakerEditor.CreateNewMeshBaker(); + } + else + { + newMeshBaker = MB3_MeshBakerEditor.CreateNewMeshBaker(); + } + + newMeshBaker.name = ("MeshBaker-" + gaws[0].shaderName + "-LM" + gaws[0].lightmapIndex).ToString().Replace("/", "-"); + + MB3_TextureBaker tb = newMeshBaker.GetComponent<MB3_TextureBaker>(); + MB3_MeshBakerCommon mb = tb.GetComponentInChildren<MB3_MeshBakerCommon>(); + + tb.GetObjectsToCombine().Clear(); + for (int i = 0; i < gaws.Count; i++) + { + if (gaws[i].go != null && !tb.GetObjectsToCombine().Contains(gaws[i].go)) + { + tb.GetObjectsToCombine().Add(gaws[i].go); + } + } + + if (splitAtlasesSoMeshesFit) + { + tb.maxAtlasSize = atlasSize; + } + if (gaws[0].numMaterials > 1) + { + string pthMat = AssetDatabase.GenerateUniqueAssetPath(pthRoot + newMeshBaker.name + ".asset"); + MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, pthMat); + tb.doMultiMaterial = true; + SerializedObject tbr = new SerializedObject(tb); + SerializedProperty resultMaterials = tbr.FindProperty("resultMaterials"); + MB_TextureBakerEditorConfigureMultiMaterials.ConfigureMutiMaterialsFromObjsToCombine2(tb, resultMaterials, tbr); + } + else + { + string pthMat = AssetDatabase.GenerateUniqueAssetPath(pthRoot + newMeshBaker.name + ".asset"); + MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, pthMat); + } + if (gaws[0].isMeshRenderer) + { + mb.meshCombiner.settings.renderType = MB_RenderType.meshRenderer; + } + else + { + mb.meshCombiner.settings.renderType = MB_RenderType.skinnedMeshRenderer; + } + } + + void bakeAllBakersInScene() + { + MB3_MeshBakerRoot[] bakers = (MB3_MeshBakerRoot[]) GameObject.FindObjectsOfType(typeof(MB3_MeshBakerRoot)); + for (int i = 0; i < bakers.Length; i++) + { + if (bakers[i] is MB3_TextureBaker) + { + MB3_TextureBaker tb = (MB3_TextureBaker)bakers[i]; + tb.CreateAtlases(updateProgressBar, true, new MB3_EditorMethods()); + } + } + EditorUtility.ClearProgressBar(); + } + + public void updateProgressBar(string msg, float progress) + { + EditorUtility.DisplayProgressBar("Combining Meshes", msg, progress); + } + + bool ValidateGroupByFields() + { + bool foundNone = false; + for (int i = 0; i < groupByFilterIdxs.Length; i++) + { + if (groupByFilterIdxs[i] == 0) foundNone = true; //zero is the none selection + if (foundNone && groupByFilterIdxs[i] != 0) + { + Debug.LogError("All non-none values must be at the top of the group by list"); + return false; + } + } + for (int i = 0; i < groupByFilterIdxs.Length; i++) + { + for (int j = i + 1; j < groupByFilterIdxs.Length; j++) + { + if (groupByFilterIdxs[i] == groupByFilterIdxs[j] && groupByFilterIdxs[i] != 0) + { + Debug.LogError("Two of the group by options are the same."); + return false; + } + } + } + return true; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAnalyseSceneTab.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAnalyseSceneTab.cs.meta new file mode 100644 index 00000000..3451d446 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerEditorWindowAnalyseSceneTab.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4dede5205ae9b44dbe7a898ae2d1e53 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerGrouperEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerGrouperEditor.cs new file mode 100644 index 00000000..bb93badc --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerGrouperEditor.cs @@ -0,0 +1,473 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using DigitalOpus.MB.Core; +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + + [CustomEditor(typeof(MB3_MeshBakerGrouper))] + [CanEditMultipleObjects] + public class MB3_MeshBakerGrouperEditor : Editor + { + + long lastBoundsCheckRefreshTime = 0; + + static GUIContent gc_ClusterType = new GUIContent("Cluster Type", "The scene will be divided cells. Meshes in each cell will be grouped into a single mesh baker"); + static GUIContent gc_GridOrigin = new GUIContent("Origin", "The scene will be divided into of cells. Meshes in each cell will be grouped into a single baker. This sets the origin for the clustering."); + static GUIContent gc_CellSize = new GUIContent("Cell Size", "The scene will be divided into a grid of cells. Meshes in each cell will be grouped into a single baker. This sets the size of the cells."); + static GUIContent gc_ClusterOnLMIndex = new GUIContent("Group By Lightmap Index", "Meshes sharing a lightmap index will be grouped together."); + static GUIContent gc_NumSegements = new GUIContent("Num Pie Segments", "Number of segments/slices in the pie."); + static GUIContent gc_PieAxis = new GUIContent("Pie Axis", "Scene will be divided into segments about this axis."); + static GUIContent gc_ClusterByLODLevel = new GUIContent("Cluster By LOD Level", "A baker will be created for each LOD level."); + static GUIContent gc_ClusterDistance = new GUIContent("Max Distance", "Source meshes closer than this value will be grouped into clusters."); + static GUIContent gc_IncludeCellsWithOnlyOneRenderer = new GUIContent("Include Cells With Only One Renderer", "There is no benefit in combining meshes with only one mesh except to adjust UVs to share an atlas."); + static GUIContent gc_Settings = new GUIContent("Use Shared Settings Asset", "Different bakers can share the same settings. If this field is None, then the settings below will be used."); + static GUIContent gc_PieRingSpacing = new GUIContent("Ring Spacing", "Pie segments will be divided into rings."); + static GUIContent gc_PieCombineAllInCenterRing = new GUIContent("Combine Center Ring Segments Together", "All segments in the centermost ring will be merged into a single segment."); + static GUIContent gc_ParentSceneObject = new GUIContent("Parent Scene Object","Must be a scene GameObject. Generated combined meshes will be children of this GameObject."); + static GUIContent gc_prefabOptions_outputFolder = new GUIContent("Prefab Output Folder", "Prefabs will be saved to this output folder."); + static GUIContent gc_prefabOptions_autoGeneratePrefabs = new GUIContent("Auto Generate Prefabs", "Configure each generated baker to use 'Bake Into Prefab' and generate a prefab in the output folder for the baker."); + + private SerializedObject grouper; + private SerializedProperty clusterType, gridOrigin, cellSize, clusterOnLMIndex, numSegments, pieAxis, clusterByLODLevel, + clusterDistance, includeCellsWithOnlyOneRenderer, mbSettings, mbSettingsAsset, pieRingSpacing, pieCombineAllInCenterRing, + prefabOptions_outputFolder, prefabOptions_autoGeneratePrefabs, parentSceneObject; + + private MB_MeshBakerSettingsEditor meshBakerSettingsMe; + private MB_MeshBakerSettingsEditor meshBakerSettingsExternal; + + public void OnEnable() + { + lastBoundsCheckRefreshTime = 0; + grouper = new SerializedObject(target); + + SerializedProperty d = grouper.FindProperty("data"); + + clusterType = grouper.FindProperty("clusterType"); + includeCellsWithOnlyOneRenderer = d.FindPropertyRelative("includeCellsWithOnlyOneRenderer"); + gridOrigin = d.FindPropertyRelative("origin"); + cellSize = d.FindPropertyRelative("cellSize"); + clusterOnLMIndex = d.FindPropertyRelative("clusterOnLMIndex"); + clusterByLODLevel = d.FindPropertyRelative("clusterByLODLevel"); + numSegments = d.FindPropertyRelative("pieNumSegments"); + pieAxis = d.FindPropertyRelative("pieAxis"); + clusterDistance = d.FindPropertyRelative("maxDistBetweenClusters"); + mbSettings = grouper.FindProperty("meshBakerSettings"); + mbSettingsAsset = grouper.FindProperty("meshBakerSettingsAsset"); + pieRingSpacing = d.FindPropertyRelative("ringSpacing"); + pieCombineAllInCenterRing = d.FindPropertyRelative("combineSegmentsInInnermostRing"); + + parentSceneObject = grouper.FindProperty("parentSceneObject"); + prefabOptions_outputFolder = grouper.FindProperty("prefabOptions_outputFolder"); + prefabOptions_autoGeneratePrefabs = grouper.FindProperty("prefabOptions_autoGeneratePrefabs"); + + meshBakerSettingsMe = new MB_MeshBakerSettingsEditor(); + meshBakerSettingsMe.OnEnable(mbSettings); + if (mbSettingsAsset.objectReferenceValue != null) + { + meshBakerSettingsExternal = new MB_MeshBakerSettingsEditor(); + UnityEngine.Object targetObj; + string propertyName; + ((MB3_MeshCombinerSettings)mbSettingsAsset.objectReferenceValue).GetMeshBakerSettingsAsSerializedProperty(out propertyName, out targetObj); + SerializedProperty meshBakerSettings = new SerializedObject(targetObj).FindProperty(propertyName); + meshBakerSettingsExternal.OnEnable(meshBakerSettings); + } + } + + public void OnDisable() + { + if (meshBakerSettingsMe != null) meshBakerSettingsMe.OnDisable(); + if (meshBakerSettingsExternal != null) meshBakerSettingsExternal.OnDisable(); + } + + public override void OnInspectorGUI() + { + grouper.Update(); + DrawGrouperInspector(); + if (GUILayout.Button("Generate Mesh Bakers")) + { + for(int tIdx = 0; tIdx < targets.Length; tIdx++) + { + _generateMeshBakers(targets[tIdx]); + } + } + + if (GUILayout.Button("Bake All Child MeshBakers")) + { + for (int tIdx = 0; tIdx < targets.Length; tIdx++) + { + _bakeAllChildMeshBakers(targets[tIdx], ref grouper); + } + } + + string buttonTextEnableRenderers = "Disable Renderers On All Child MeshBaker Source Objs"; + bool enableRenderers = false; + { + MB3_MeshBakerGrouper tbg = (MB3_MeshBakerGrouper)target; + MB3_MeshBakerCommon bc = tbg.GetComponentInChildren<MB3_MeshBakerCommon>(); + if (bc != null && bc.GetObjectsToCombine().Count > 0) + { + GameObject go = bc.GetObjectsToCombine()[0]; + if (go != null && go.GetComponent<Renderer>() != null && go.GetComponent<Renderer>().enabled == false) + { + buttonTextEnableRenderers = "Enable Renderers On All Child MeshBaker Source Objs"; + enableRenderers = true; + } + } + } + + if (GUILayout.Button(buttonTextEnableRenderers)) + { + for (int tIdx = 0; tIdx < targets.Length; tIdx++) + { + _enableDisableRenderers(targets[tIdx], enableRenderers); + } + } + + if (GUILayout.Button("Delete All Child Mesh Bakers & Combined Meshes")) + { + if (EditorUtility.DisplayDialog("Delete Mesh Bakers", "Delete all child mesh bakers", "OK", "Cancel")) + { + for (int i = 0; i < targets.Length; i++) + { + MB3_MeshBakerGrouper tbg = (MB3_MeshBakerGrouper)targets[i]; + tbg.DeleteAllChildMeshBakers(); + } + } + } + + + if (DateTime.UtcNow.Ticks - lastBoundsCheckRefreshTime > 10000000) + { + MB3_TextureBaker tb = ((MB3_MeshBakerGrouper)target).GetComponent<MB3_TextureBaker>(); + if (tb != null) + { + MB3_MeshBakerGrouper tbg = (MB3_MeshBakerGrouper)target; + List<GameObject> gos = tb.GetObjectsToCombine(); + Bounds b = new Bounds(Vector3.zero, Vector3.one); + if (gos.Count > 0 && gos[0] != null && gos[0].GetComponent<Renderer>() != null) + { + b = gos[0].GetComponent<Renderer>().bounds; + } + for (int i = 0; i < gos.Count; i++) + { + if (gos[i] != null && gos[i].GetComponent<Renderer>() != null) + { + b.Encapsulate(gos[i].GetComponent<Renderer>().bounds); + } + } + + tbg.sourceObjectBounds = b; + lastBoundsCheckRefreshTime = DateTime.UtcNow.Ticks; + } + } + + grouper.ApplyModifiedProperties(); + } + + public void DrawGrouperInspector() + { + EditorGUILayout.HelpBox("This component helps you group meshes that are close together so they can be combined together." + + " It generates multiple MB3_MeshBaker objects from the List Of Objects to be combined in the MB3_TextureBaker component." + + " Objects that are close together will be grouped together and added to a new child MB3_MeshBaker object.\n\n" + + " TIP: Try the new agglomerative cluster type. It's awsome!", MessageType.Info); + MB3_MeshBakerGrouper tbg = (MB3_MeshBakerGrouper)target; + + MB3_TextureBaker tb = tbg.GetComponent<MB3_TextureBaker>(); + + Transform pgo = (Transform)EditorGUILayout.ObjectField(gc_ParentSceneObject, parentSceneObject.objectReferenceValue, typeof(Transform), true); + if (pgo != null && MB_Utility.IsSceneInstance(pgo.gameObject)) + { + parentSceneObject.objectReferenceValue = pgo; + } + else + { + parentSceneObject.objectReferenceValue = null; + } + + EditorGUILayout.PropertyField(clusterType, gc_ClusterType); + MB3_MeshBakerGrouper.ClusterType gg = (MB3_MeshBakerGrouper.ClusterType)clusterType.enumValueIndex; + if ((gg == MB3_MeshBakerGrouper.ClusterType.none && !(tbg.grouper is MB3_MeshBakerGrouperNone)) || + (gg == MB3_MeshBakerGrouper.ClusterType.grid && !(tbg.grouper is MB3_MeshBakerGrouperGrid)) || + (gg == MB3_MeshBakerGrouper.ClusterType.pie && !(tbg.grouper is MB3_MeshBakerGrouperPie)) || + (gg == MB3_MeshBakerGrouper.ClusterType.agglomerative && !(tbg.grouper is MB3_MeshBakerGrouperCluster)) + ) + { + tbg.CreateGrouper(gg, tbg.data); + tbg.clusterType = gg; + } + + if (clusterType.enumValueIndex == (int)MB3_MeshBakerGrouper.ClusterType.grid) + { + EditorGUILayout.PropertyField(gridOrigin, gc_GridOrigin); + EditorGUILayout.PropertyField(cellSize, gc_CellSize); + } + else if (clusterType.enumValueIndex == (int)MB3_MeshBakerGrouper.ClusterType.pie) + { + EditorGUILayout.PropertyField(gridOrigin, gc_GridOrigin); + EditorGUILayout.PropertyField(numSegments, gc_NumSegements); + EditorGUILayout.PropertyField(pieAxis, gc_PieAxis); + EditorGUILayout.PropertyField(pieRingSpacing, gc_PieRingSpacing); + EditorGUILayout.PropertyField(pieCombineAllInCenterRing, gc_PieCombineAllInCenterRing); + } + else if (clusterType.enumValueIndex == (int)MB3_MeshBakerGrouper.ClusterType.agglomerative) + { + float dist = clusterDistance.floatValue; + float maxDist = 100f; + float minDist = .000001f; + MB3_MeshBakerGrouperCluster cl = null; + if (tbg.grouper is MB3_MeshBakerGrouperCluster) + { + cl = (MB3_MeshBakerGrouperCluster)tbg.grouper; + maxDist = cl._ObjsExtents; + minDist = cl._minDistBetweenClusters; + if (dist < minDist) + { + dist = Mathf.Lerp(minDist, maxDist, .11f); + } + } + + dist = EditorGUILayout.Slider(gc_ClusterDistance, dist, minDist, maxDist); + clusterDistance.floatValue = dist; + + string btnName = "Refresh Clusters"; + if (cl.cluster == null || cl.cluster.clusters == null || cl.cluster.clusters.Length == 0) + { + btnName = "Click To Build Clusters"; + } + if (GUILayout.Button(btnName)) + { + if (tbg.grouper is MB3_MeshBakerGrouperCluster) + { + MB3_MeshBakerGrouperCluster cg = (MB3_MeshBakerGrouperCluster)tbg.grouper; + if (tb != null) + { + cg.BuildClusters(tb.GetObjectsToCombine(), updateProgressBar); + EditorUtility.ClearProgressBar(); + Repaint(); + } + } + } + } + + EditorGUILayout.PropertyField(clusterOnLMIndex, gc_ClusterOnLMIndex); + EditorGUILayout.PropertyField(clusterByLODLevel, gc_ClusterByLODLevel); + EditorGUILayout.PropertyField(includeCellsWithOnlyOneRenderer, gc_IncludeCellsWithOnlyOneRenderer); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Prefab Output Settings", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(prefabOptions_autoGeneratePrefabs, gc_prefabOptions_autoGeneratePrefabs); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(prefabOptions_outputFolder, gc_prefabOptions_outputFolder); + if (GUILayout.Button("Browse")) + { + string path = EditorUtility.OpenFolderPanel("Browse For Output Folder", "", ""); + path = MB_BatchPrefabBakerEditorFunctions.ConvertFullPathToProjectRelativePath(path); + prefabOptions_outputFolder.stringValue = path; + } + EditorGUILayout.EndHorizontal(); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Mesh Baker Settings", EditorStyles.boldLabel); + EditorGUILayout.HelpBox("These settings will be shared by all created Mesh Bakers.", MessageType.Info); + + UnityEngine.Object oldObjVal = mbSettingsAsset.objectReferenceValue; + EditorGUILayout.PropertyField(mbSettingsAsset, gc_Settings); + + bool doingTextureArrays = false; + if (tb != null && tb.textureBakeResults != null) doingTextureArrays = tb.textureBakeResults.resultType == MB2_TextureBakeResults.ResultType.textureArray; + if (mbSettingsAsset.objectReferenceValue == null) + { + meshBakerSettingsMe.DrawGUI(tbg.meshBakerSettings, true, doingTextureArrays); + } + else + { + if (meshBakerSettingsExternal == null || oldObjVal != mbSettingsAsset.objectReferenceValue) + { + UnityEngine.Object targetObj; + string propertyName; + ((MB3_MeshCombinerSettings)mbSettingsAsset.objectReferenceValue).GetMeshBakerSettingsAsSerializedProperty(out propertyName, out targetObj); + SerializedProperty meshBakerSettings = new SerializedObject(targetObj).FindProperty(propertyName); + } + + meshBakerSettingsExternal.DrawGUI(((MB3_MeshCombinerSettings)mbSettingsAsset.objectReferenceValue).data, false, doingTextureArrays); + } + } + + private static void _generateMeshBakers(UnityEngine.Object target) + { + MB3_MeshBakerGrouper tbg = (MB3_MeshBakerGrouper)target; + MB3_TextureBaker tb = tbg.GetComponent<MB3_TextureBaker>(); + if (tb == null) + { + Debug.LogError("There must be an MB3_TextureBaker attached to this game object."); + return; + } + + if (tb.GetObjectsToCombine().Count == 0) + { + Debug.LogError("The MB3_MeshBakerGrouper creates clusters based on the objects to combine in the MB3_TextureBaker component. There were no objects in this list."); + return; + } + + if (tbg.parentSceneObject == null || + !MB_Utility.IsSceneInstance(tbg.parentSceneObject.gameObject)) + { + GameObject g = new GameObject("CombinedMeshes-" + tbg.name); + tbg.parentSceneObject = g.transform; + } + + //check if any of the objes that will be added to bakers already exist in child bakers + List<GameObject> objsWeAreGrouping = tb.GetObjectsToCombine(); + MB3_MeshBakerCommon[] alreadyExistBakers = tbg.GetComponentsInChildren<MB3_MeshBakerCommon>(); + bool foundChildBakersWithObjsToCombine = false; + for (int i = 0; i < alreadyExistBakers.Length; i++) + { + List<GameObject> childOjs2Combine = alreadyExistBakers[i].GetObjectsToCombine(); + for (int j = 0; j < childOjs2Combine.Count; j++) + { + if (childOjs2Combine[j] != null && objsWeAreGrouping.Contains(childOjs2Combine[j])) + { + foundChildBakersWithObjsToCombine = true; + break; + } + } + } + + bool proceed = true; + if (foundChildBakersWithObjsToCombine) + { + proceed = EditorUtility.DisplayDialog("Replace Previous Generated Bakers", "Delete child bakers?\n\n" + + "This grouper has child Mesh Baker objects from a previous clustering. Do you want to delete these and create new ones?", "OK", "Cancel"); + } + + if (tbg.prefabOptions_autoGeneratePrefabs) + { + if (!MB_BatchPrefabBakerEditorFunctions.ValidateFolderIsInProject("Output Folder", tbg.prefabOptions_outputFolder)) + { + Debug.LogError("If " + gc_prefabOptions_autoGeneratePrefabs.text + " is enabled, you must provide an output folder. Prefabs will be saved in this folder."); + proceed = false; + } + } + + if (proceed) + { + if (foundChildBakersWithObjsToCombine) tbg.DeleteAllChildMeshBakers(); + List<MB3_MeshBakerCommon> newBakers = tbg.grouper.DoClustering(tb, tbg); + if (newBakers.Count > 0) DoGeneratePrefabsIfNecessary(tbg, newBakers); + } + } + + private static void DoGeneratePrefabsIfNecessary(MB3_MeshBakerGrouper grouper, List<MB3_MeshBakerCommon> newBakers) + { + if (!grouper.prefabOptions_autoGeneratePrefabs && + !grouper.prefabOptions_mergeOutputIntoSinglePrefab) return; + if (!MB_BatchPrefabBakerEditorFunctions.ValidateFolderIsInProject("Output Folder", grouper.prefabOptions_outputFolder)) return; + + if (grouper.prefabOptions_autoGeneratePrefabs) + { + for (int i = 0; i < newBakers.Count; i++) + { + MB3_MeshBakerCommon baker = newBakers[i]; + + string path = grouper.prefabOptions_outputFolder; + // Generate a new prefab name + string prefabName = baker.name.Replace("MeshBaker", "CombinedMesh"); + prefabName = prefabName.Replace(" ", "_"); + prefabName = prefabName.Replace(",", "_"); + prefabName = prefabName.Trim(Path.GetInvalidFileNameChars()); + prefabName = prefabName.Trim(Path.GetInvalidPathChars()); + + string pathName = AssetDatabase.GenerateUniqueAssetPath(path + "/" + prefabName + ".prefab"); + if (pathName == null || pathName.Length == 0) + { + Debug.LogError("Could not generate prefab " + prefabName + " in folder " + path + ". There is something wrong with the path or prefab name."); + continue; + } + + // Generate a new prefab + GameObject go = new GameObject(baker.name); + GameObject pf = PrefabUtility.CreatePrefab(pathName, go); + + // Configure the baker to bake into the prefab + baker.resultPrefab = pf; + baker.resultPrefabLeaveInstanceInSceneAfterBake = true; + baker.meshCombiner.outputOption = MB2_OutputOptions.bakeIntoPrefab; + if (grouper.parentSceneObject != null) + { + baker.parentSceneObject = grouper.parentSceneObject; + } + + MB_Utility.Destroy(go); + } + } + } + + private static void _bakeAllChildMeshBakers(UnityEngine.Object target, ref SerializedObject grouper) + { + MB3_MeshBakerGrouper tbg = (MB3_MeshBakerGrouper)target; + MB3_TextureBaker tb = tbg.GetComponent<MB3_TextureBaker>(); + try + { + MB3_MeshBakerCommon[] mBakers = tbg.GetComponentsInChildren<MB3_MeshBakerCommon>(); + for (int i = 0; i < mBakers.Length; i++) + { + bool createdDummyMaterialBakeResult; + if (grouper.targetObject == tbg) + { + MB3_MeshBakerEditorFunctions.BakeIntoCombined(mBakers[i], out createdDummyMaterialBakeResult, ref grouper); + } + else + { + MB3_MeshBakerEditorFunctions.BakeIntoCombined(mBakers[i], out createdDummyMaterialBakeResult); + } + } + } + catch (Exception ex) + { + Debug.LogError(ex.Message + "\n" + ex.StackTrace.ToString()); + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + + private static void _enableDisableRenderers(UnityEngine.Object target, bool enableRenderers) + { + MB3_MeshBakerGrouper tbg = (MB3_MeshBakerGrouper)target; + MB3_TextureBaker tb = tbg.GetComponent<MB3_TextureBaker>(); + try + { + MB3_MeshBakerCommon[] mBakers = tbg.GetComponentsInChildren<MB3_MeshBakerCommon>(); + for (int i = 0; i < mBakers.Length; i++) + { + mBakers[i].EnableDisableSourceObjectRenderers(enableRenderers); + } + } + catch (Exception ex) + { + Debug.LogError(ex.Message + "\n" + ex.StackTrace.ToString()); + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + + public bool updateProgressBar(string msg, float progress) + { + //EditorUtility.DisplayProgressBar("Creating Clusters", msg, progress); + return EditorUtility.DisplayCancelableProgressBar("Creating Clusters", msg, progress); + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerGrouperEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerGrouperEditor.cs.meta new file mode 100644 index 00000000..625c235b --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerGrouperEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8627c7a515355354798d5990a965619e +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsAssetEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsAssetEditor.cs new file mode 100644 index 00000000..1cdd01ea --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsAssetEditor.cs @@ -0,0 +1,44 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using DigitalOpus.MB.Core; +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + [CustomEditor(typeof(MB3_MeshCombinerSettings))] + public class MB3_MeshBakerSettingsAssetEditor : Editor + { + + private SerializedObject settingsSerializedObj; + private SerializedProperty mbSettings; + private MB_MeshBakerSettingsEditor meshBakerSettingsEditor; + + public void OnEnable() + { + settingsSerializedObj = new SerializedObject(target); + mbSettings = settingsSerializedObj.FindProperty("data"); + meshBakerSettingsEditor = new MB_MeshBakerSettingsEditor(); + meshBakerSettingsEditor.OnEnable(mbSettings); + } + + public override void OnInspectorGUI() + { + MB3_MeshCombinerSettings tbg = (MB3_MeshCombinerSettings)target; + settingsSerializedObj.Update(); + EditorGUILayout.HelpBox("This asset can be shared by many Mesh Bakers and MultiMeshBakers. Drag this " + + " asset to the 'Use Shared Settings' field of any Mesh Baker", MessageType.Info); + meshBakerSettingsEditor.DrawGUI(tbg.data, true, false); + settingsSerializedObj.ApplyModifiedProperties(); + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsAssetEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsAssetEditor.cs.meta new file mode 100644 index 00000000..e47dc0b8 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsAssetEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b79b3cba502aa134ca22cc156985b07e +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsEditor.cs new file mode 100644 index 00000000..4ed19f12 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsEditor.cs @@ -0,0 +1,216 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + + public class MB_MeshBakerSettingsEditor + { + private static GUIContent + gc_renderTypeGUIContent = new GUIContent("Renderer", "The type of renderer to add to the combined mesh."), + gc_lightmappingOptionGUIContent = new GUIContent("Lightmapping UVs", "preserve current lightmapping: Use this if all source objects are lightmapped and you want to preserve it. All source objects must use the same lightmap. DOES NOT WORK IN UNITY 5.\n\n" + + "generate new UV Layout: Use this if you want to bake a lightmap after the combined mesh has been generated\n\n" + + "copy UV2 unchanged: Use this if UV2 is being used for something other than lightmaping.\n\n" + + "ignore UV2: A UV2 channel will not be generated for the combined mesh\n\n" + + "copy UV2 unchanged to separate rects: Use this if your meshes include a custom lightmap that you want to use with the combined mesh.\n\n"), + gc_clearBuffersAfterBakeGUIContent = new GUIContent("Clear Buffers After Bake", "Frees memory used by the MeshCombiner. Set to false if you want to update the combined mesh at runtime."), + gc_doNormGUIContent = new GUIContent("Include Normals"), + gc_doTanGUIContent = new GUIContent("Include Tangents"), + gc_doColGUIContent = new GUIContent("Include Colors"), + gc_doBlendShapeGUIContent = new GUIContent("Include Blend Shapes"), + gc_doUVGUIContent = new GUIContent("Include UV"), + gc_doUV3GUIContent = new GUIContent("Include UV3"), + gc_doUV4GUIContent = new GUIContent("Include UV4"), + + gc_doUV5GUIContent = new GUIContent("Include UV5"), + gc_doUV6GUIContent = new GUIContent("Include UV6"), + gc_doUV7GUIContent = new GUIContent("Include UV7"), + gc_doUV8GUIContent = new GUIContent("Include UV8"), + + gc_uv2HardAngleGUIContent = new GUIContent(" UV2 Hard Angle", "Angles greater than 'hard angle' in degrees will be split."), + gc_uv2PackingMarginUV3GUIContent = new GUIContent(" UV2 Packing Margin", "The margin between islands in the UV layout measured in UV coordinates (0..1) not pixels"), + gc_PivotLocationType = new GUIContent("Pivot Location Type", "Centers the verticies of the mesh about the render bounds center and translates the game object. This makes the combined meshes easier to work with. There is a performance and memory allocation cost to this so if you are frequently baking meshes at runtime disable it."), + gc_PivotLocation = new GUIContent("Pivot Location"), + gc_OptimizeAfterBake = new GUIContent("Optimize After Bake", "This does the same thing that 'Optimize' does on the ModelImporter."), + gc_AssignMeshCusomizer = new GUIContent("Assign To Mesh Customizer", "This is a custom script that can be used to alter data just before channels are assigned to the mesh." + + " It could be used for example to inject a Texture Array slice index into mesh coordinate (uv.z or colors.a)."), + gc_smrNoExtraBonesWhenCombiningMeshRenderers = new GUIContent("No Extra Bones For Mesh Renderers", "If a MeshRenderer is a child of another bone in the hierarchy (e.g. a sword is a child of a hand bone), then the MeshRenderer's vertices " + + " will be merged with the parent bone's vertices eliminating the extra bone (the sword becomes part of the hand). This reduces the bone count, but the MeshRenderer can never be moved independently of its parent.\n\n" + + "If you want some bones merged and some bones Independent, then move the 'Independent' bones in the hierarchy so that they are not descendants of other bones before baking. They can be re-parented after baking."), + gc_smrMergeBlendShapesWithSameNames = new GUIContent("Merge Blend Shapes With Same Names", "Enable this if you are combining multiple skinned meshes on a single rig (mixing and matching different body part variations and clothes) and the body parts have the " + + " same blend shape names. The combined mesh will preserve the original blend shape names. All blend shapes with the same name will activate in lockstep\n\n" + + "Disable this if you are combining multiple characters into a single combined skinned mesh and want to be able to activate the blend shapes on the different characters independently. " + + " the blend shapes will be re-named. Use the MB_BlendShape2CombinedMap component to control which blend shape activate."); + + + private SerializedProperty doNorm, doTan, doUV, doUV3, doUV4, doUV5, doUV6, doUV7, doUV8, doCol, doBlendShapes, lightmappingOption, renderType, clearBuffersAfterBake, uv2OutputParamsPackingMargin, uv2OutputParamsHardAngle, pivotLocationType, pivotLocation, optimizeAfterBake, assignToMeshCustomizer; + private SerializedProperty smrNoExtraBonesWhenCombiningMeshRenderers, smrMergeBlendShapesWithSameNames; + private MB_EditorStyles editorStyles = new MB_EditorStyles(); + + /// <summary> + /// This is the regular init method that should usually be used + /// </summary> + /// <param name="meshBakerSettingsData">Should be the MB_IMeshBakerSettingsData implementor</param> + public void OnEnable(SerializedProperty meshBakerSettingsData) + { + _InitCommon(meshBakerSettingsData); + clearBuffersAfterBake = meshBakerSettingsData.FindPropertyRelative("_clearBuffersAfterBake"); + } + + /// <summary> + /// This is necessary for backward compatibility. MeshCombinerCommon does not contain the + /// clearBuffersAfterBake field. It is stored in the MeshBaker and is serialized in many, many + /// scenes. This Init method is only used for MeshCombinerCommon. + /// </summary> + /// <param name="combiner"></param> + /// <param name="meshBaker"></param> + public void OnEnable(SerializedProperty combiner, SerializedObject meshBaker) + { + _InitCommon(combiner); + clearBuffersAfterBake = meshBaker.FindProperty("clearBuffersAfterBake"); + } + + public void OnDisable() + { + editorStyles.DestroyTextures(); + } + + private void _InitCommon(SerializedProperty combiner) + { + renderType = combiner.FindPropertyRelative("_renderType"); + lightmappingOption = combiner.FindPropertyRelative("_lightmapOption"); + doNorm = combiner.FindPropertyRelative("_doNorm"); + doTan = combiner.FindPropertyRelative("_doTan"); + doUV = combiner.FindPropertyRelative("_doUV"); + doUV3 = combiner.FindPropertyRelative("_doUV3"); + doUV4 = combiner.FindPropertyRelative("_doUV4"); + doUV5 = combiner.FindPropertyRelative("_doUV5"); + doUV6 = combiner.FindPropertyRelative("_doUV6"); + doUV7 = combiner.FindPropertyRelative("_doUV7"); + doUV8 = combiner.FindPropertyRelative("_doUV8"); + doCol = combiner.FindPropertyRelative("_doCol"); + doBlendShapes = combiner.FindPropertyRelative("_doBlendShapes"); + uv2OutputParamsPackingMargin = combiner.FindPropertyRelative("_uv2UnwrappingParamsPackMargin"); + uv2OutputParamsHardAngle = combiner.FindPropertyRelative("_uv2UnwrappingParamsHardAngle"); + pivotLocationType = combiner.FindPropertyRelative("_pivotLocationType"); + pivotLocation = combiner.FindPropertyRelative("_pivotLocation"); + optimizeAfterBake = combiner.FindPropertyRelative("_optimizeAfterBake"); + assignToMeshCustomizer = combiner.FindPropertyRelative("_assignToMeshCustomizer"); + smrNoExtraBonesWhenCombiningMeshRenderers = combiner.FindPropertyRelative("_smrNoExtraBonesWhenCombiningMeshRenderers"); + smrMergeBlendShapesWithSameNames = combiner.FindPropertyRelative("_smrMergeBlendShapesWithSameNames"); + editorStyles.Init(); + } + + public void DrawGUI(MB_IMeshBakerSettings momm, bool settingsEnabled, bool doingTextureArrays) + { + EditorGUILayout.BeginVertical(editorStyles.editorBoxBackgroundStyle); + GUI.enabled = settingsEnabled; + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(doNorm, gc_doNormGUIContent); + EditorGUILayout.PropertyField(doTan, gc_doTanGUIContent); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(doUV, gc_doUVGUIContent); + EditorGUILayout.PropertyField(doUV3, gc_doUV3GUIContent); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(doUV4, gc_doUV4GUIContent); + EditorGUILayout.PropertyField(doUV5, gc_doUV5GUIContent); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(doUV6, gc_doUV6GUIContent); + EditorGUILayout.PropertyField(doUV7, gc_doUV7GUIContent); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(doUV8, gc_doUV8GUIContent); + EditorGUILayout.PropertyField(doCol, gc_doColGUIContent); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.PropertyField(doBlendShapes, gc_doBlendShapeGUIContent); + + if (momm.lightmapOption == MB2_LightmapOptions.preserve_current_lightmapping) + { + if (MBVersion.GetMajorVersion() == 5) + { + EditorGUILayout.HelpBox("The best choice for Unity 5 is to Ignore_UV2 or Generate_New_UV2 layout. Unity's baked GI will create the UV2 layout it wants. See manual for more information.", MessageType.Warning); + } + } + + if (momm.lightmapOption == MB2_LightmapOptions.generate_new_UV2_layout) + { + EditorGUILayout.HelpBox("Generating new lightmap UVs can split vertices which can push the number of vertices over the 64k limit.", MessageType.Warning); + } + + EditorGUILayout.PropertyField(lightmappingOption, gc_lightmappingOptionGUIContent); + if (momm.lightmapOption == MB2_LightmapOptions.generate_new_UV2_layout) + { + EditorGUILayout.PropertyField(uv2OutputParamsHardAngle, gc_uv2HardAngleGUIContent); + EditorGUILayout.PropertyField(uv2OutputParamsPackingMargin, gc_uv2PackingMarginUV3GUIContent); + EditorGUILayout.Separator(); + } + + UnityEngine.Object obj = null; + obj = assignToMeshCustomizer.objectReferenceValue; + + + EditorGUILayout.PropertyField(renderType, gc_renderTypeGUIContent); + + + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(clearBuffersAfterBake, gc_clearBuffersAfterBakeGUIContent); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.PropertyField(pivotLocationType, gc_PivotLocationType); + if (pivotLocationType.enumValueIndex == (int) MB_MeshPivotLocation.customLocation) + { + EditorGUILayout.PropertyField(pivotLocation, gc_PivotLocation); + } + EditorGUILayout.PropertyField(optimizeAfterBake, gc_OptimizeAfterBake); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Skinned Mesh Renderer Settings", EditorStyles.boldLabel); + EditorGUI.BeginDisabledGroup((MB_RenderType)renderType.intValue != MB_RenderType.skinnedMeshRenderer); + EditorGUILayout.PropertyField(smrNoExtraBonesWhenCombiningMeshRenderers, gc_smrNoExtraBonesWhenCombiningMeshRenderers); + EditorGUILayout.PropertyField(smrMergeBlendShapesWithSameNames, gc_smrMergeBlendShapesWithSameNames); + EditorGUI.EndDisabledGroup(); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Advanced Settings", EditorStyles.boldLabel); + + // Don't use a PropertyField because we may not be able to use the assigned object. It may not implement requried interface. + + if (doingTextureArrays && assignToMeshCustomizer.objectReferenceValue == null) + { + EditorGUILayout.HelpBox("The Textures were baked into Texture Arrays. You probaly need to a customizer here" + + " to embed the slice index in the mesh. You can use one of the included customizers or write your own. " + + "See the example customizers in MeshBaker/Scripts/AssignToMeshCustomizers.", MessageType.Error); + } + obj = EditorGUILayout.ObjectField(gc_AssignMeshCusomizer, obj, typeof(UnityEngine.Object), true); + if (obj == null || !(obj is IAssignToMeshCustomizer)) + { + assignToMeshCustomizer.objectReferenceValue = null; + } + else + { + assignToMeshCustomizer.objectReferenceValue = obj; + } + + GUI.enabled = true; + EditorGUILayout.EndVertical(); + + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsEditor.cs.meta new file mode 100644 index 00000000..17b532bc --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MeshBakerSettingsEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 82ca664c08ee65a42938adb59475f380 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MultiMeshBakerEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MultiMeshBakerEditor.cs new file mode 100644 index 00000000..a60533ec --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MultiMeshBakerEditor.cs @@ -0,0 +1,78 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using UnityEditor; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + [CustomEditor(typeof(MB3_MultiMeshBaker))] + [CanEditMultipleObjects] + public class MB3_MultiMeshBakerEditor : Editor + { + MB3_MeshBakerEditorInternal mbe = new MB3_MeshBakerEditorInternal(); + [MenuItem(@"GameObject/Create Other/Mesh Baker/TextureBaker and MultiMeshBaker", false, 100)] + public static GameObject CreateNewMeshBaker() + { + MB3_TextureBaker[] mbs = (MB3_TextureBaker[])GameObject.FindObjectsOfType(typeof(MB3_TextureBaker)); + Regex regex = new Regex(@"\((\d+)\)$", RegexOptions.Compiled | RegexOptions.CultureInvariant); + int largest = 0; + try + { + for (int i = 0; i < mbs.Length; i++) + { + Match match = regex.Match(mbs[i].name); + if (match.Success) + { + int val = Convert.ToInt32(match.Groups[1].Value); + if (val >= largest) + largest = val + 1; + } + } + } + catch (Exception ex) + { + if (ex == null) ex = null; //Do nothing supress compiler warning + } + GameObject nmb = new GameObject("TextureBaker (" + largest + ")"); + nmb.transform.position = Vector3.zero; + MB3_TextureBaker tb = nmb.AddComponent<MB3_TextureBaker>(); + tb.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; + MB3_MeshBakerGrouper mbg = nmb.AddComponent<MB3_MeshBakerGrouper>(); + GameObject meshBaker = new GameObject("MultiMeshBaker"); + MB3_MultiMeshBaker mmb = meshBaker.AddComponent<MB3_MultiMeshBaker>(); + mmb.meshCombiner.settingsHolder = mbg; + meshBaker.transform.parent = nmb.transform; + + return nmb; + } + + void OnEnable() + { + mbe.OnEnable(serializedObject); + } + + void OnDisable() + { + mbe.OnDisable(); + } + + public override void OnInspectorGUI() + { + mbe.OnInspectorGUI(serializedObject, (MB3_MeshBakerCommon)target, targets, typeof(MB3_MeshBakerEditorWindow)); + } + + + } +} + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MultiMeshBakerEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MultiMeshBakerEditor.cs.meta new file mode 100644 index 00000000..0e4e1235 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_MultiMeshBakerEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e5238fa3cc273c740a7860c0aadcacf2 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_SearchFilters.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_SearchFilters.cs new file mode 100644 index 00000000..0c76d2c1 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_SearchFilters.cs @@ -0,0 +1,235 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public interface IGroupByFilter + { + + /// <summary> + /// this name appears in the dropdown list. + /// </summary> + /// <returns>The name.</returns> + string GetName(); + + /// <summary> + /// returns a description of the game object for this filter + /// eg. renderType=MeshFilter + /// </summary> + /// <returns>The description.</returns> + /// <param name="fi">Fi.</param> + string GetDescription(GameObjectFilterInfo fi); + + /// <summary> + /// For sorting Similar to IComparer.Compare + /// </summary> + int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b); + } + + [Serializable] + public class GameObjectFilterInfo : IComparable + { + + public enum StandardShaderBlendMode + { + NotApplicable = -1, + Opaque = 0, + Cutout = 1, + Fade = 2, + Transparent = 3, + } + + public enum URPBlendMode // called SurfaceType in URP + { + Opaque = 0, + Transparent = 1 + } + + public enum HDRPBlendMode // called SurfaceType in HDRP + { + Opaque = 0, + Transparent = 1 + } + + public int[] URPBlendMode_2_StandardBlendMode = new int[] + { + (int) StandardShaderBlendMode.Opaque, + (int) StandardShaderBlendMode.Transparent + }; + + public int[] HDRPBlendMode_2_StandardBlendMode = new int[] + { + (int) StandardShaderBlendMode.Opaque, + (int) StandardShaderBlendMode.Transparent + }; + + + public GameObject go; + public string shaderName = ""; + public Shader[] shaders = null; //distinct set of shaders used + + + public string materialName = ""; + public Material[] materials = null; //disinct set of materials used + public string standardShaderBlendModesName = ""; + public StandardShaderBlendMode[] standardShaderBlendModes = null; + public bool outOfBoundsUVs = false; + public bool submeshesOverlap = false; + public bool alreadyInBakerList = false; + public int numMaterials = 1; + public int lightmapIndex = -1; + public int numVerts = 0; + public bool isStatic = false; + public bool isMeshRenderer = true; + public string warning; + public short atlasIndex = 0; + + IGroupByFilter[] filters; + + + public int CompareTo(System.Object obj) + { + if (obj is GameObjectFilterInfo) + { + GameObjectFilterInfo gobj = (GameObjectFilterInfo)obj; + int gb; + + for (int i = 0; i < filters.Length; i++) + { + if (filters[i] != null) + { + gb = filters[i].Compare(this, gobj); + if (gb != 0) return gb; + } + } + } + return 0; + } + + public GameObjectFilterInfo(GameObject g, HashSet<GameObject> objsAlreadyInBakers, IGroupByFilter[] filts) + { + go = g; + Renderer r = MB_Utility.GetRenderer(g); + //material = r.sharedMaterial; + //if (material != null) shader = material.shader; + HashSet<Material> matsSet = new HashSet<Material>(); + HashSet<Shader> shaderSet = new HashSet<Shader>(); + if (r is SkinnedMeshRenderer) isMeshRenderer = false; + else isMeshRenderer = true; + materials = r.sharedMaterials; + //shaders = new Shader[materials.Length]; + for (int i = 0; i < materials.Length; i++) + { + if (materials[i] != null) + { + matsSet.Add(materials[i]); + shaderSet.Add(materials[i].shader); + } + } + materials = new Material[matsSet.Count]; + matsSet.CopyTo(materials); + shaders = new Shader[shaderSet.Count]; + standardShaderBlendModes = new StandardShaderBlendMode[matsSet.Count]; + + shaderSet.CopyTo(shaders); + + Array.Sort(materials, new NameComparer()); + Array.Sort(shaders, new NameComparer()); + + List<string> standardShaderBlendModesNameSet = new List<string>(); + for (int i = 0; i < materials.Length; i++) + { + if (materials[i].shader.name.StartsWith("Standard") && materials[i].HasProperty("_Mode")) + { + standardShaderBlendModes[i] = (StandardShaderBlendMode)materials[i].GetFloat("_Mode"); + } else if (materials[i].shader.name.StartsWith("Universal Render Pipeline") && materials[i].HasProperty("_Surface")) + { + int surfaceMode = (int) materials[i].GetFloat("_Surface"); + if (surfaceMode < 0 || surfaceMode > (int) URPBlendMode.Transparent) + { + Debug.LogError("Unsupported surface mode, were more surface modes added to the URP?"); + surfaceMode = Mathf.Clamp(surfaceMode, (int)URPBlendMode.Opaque, (int)URPBlendMode.Transparent); + } + + standardShaderBlendModes[i] = (StandardShaderBlendMode) URPBlendMode_2_StandardBlendMode[surfaceMode]; + } else if (materials[i].shader.name.StartsWith("HDRP") && materials[i].HasProperty("_SurfaceType")) + { + int surfaceMode = (int)materials[i].GetFloat("_SurfaceType"); + if (surfaceMode < 0 || surfaceMode > (int)HDRPBlendMode.Transparent) + { + Debug.LogError("Unsupported surface mode, were more surface modes added to the HDRP?"); + surfaceMode = Mathf.Clamp(surfaceMode, (int)HDRPBlendMode.Opaque, (int)HDRPBlendMode.Transparent); + } + + standardShaderBlendModes[i] = (StandardShaderBlendMode)URPBlendMode_2_StandardBlendMode[surfaceMode]; + } + else + { + standardShaderBlendModes[i] = StandardShaderBlendMode.NotApplicable; + } + if (!standardShaderBlendModesNameSet.Contains(standardShaderBlendModes[i].ToString())) standardShaderBlendModesNameSet.Add(standardShaderBlendModes[i].ToString()); + materialName += (materials[i] == null ? "null" : materials[i].name); + if (i < materials.Length - 1) materialName += ","; + } + + standardShaderBlendModesName = ""; + standardShaderBlendModesNameSet.Sort(); + foreach (string modeName in standardShaderBlendModesNameSet) + { + standardShaderBlendModesName += modeName + ","; + } + for (int i = 0; i < shaders.Length; i++) + { + shaderName += (shaders[i] == null ? "null" : shaders[i].name); + if (i < shaders.Length - 1) shaderName += ","; + } + + lightmapIndex = r.lightmapIndex; + Mesh mesh = MB_Utility.GetMesh(g); + numVerts = 0; + if (mesh != null) + { + numVerts = mesh.vertexCount; + } + isStatic = go.isStatic; + numMaterials = materials.Length; + warning = ""; + alreadyInBakerList = objsAlreadyInBakers.Contains(g); + outOfBoundsUVs = false; + submeshesOverlap = false; + filters = filts; + } + + public string GetDescription(IGroupByFilter[] groupBy, GameObjectFilterInfo fi) + { + string desc = ""; + if (groupBy != null) + { + for (int i = 0; i < groupBy.Length; i++) + { + desc += groupBy[i].GetDescription(fi) + " "; + } + return desc; + } + else + { + return "todo"; + } + } + + private class NameComparer : Comparer<UnityEngine.Object> + { + public override int Compare(UnityEngine.Object a, UnityEngine.Object b) + { + return a.name.CompareTo(b.name); + } + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_SearchFilters.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_SearchFilters.cs.meta new file mode 100644 index 00000000..1dcecac9 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_SearchFilters.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 435b539c6ab6a4346bc6c56e18046e1b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditor.cs new file mode 100644 index 00000000..7e5632ef --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditor.cs @@ -0,0 +1,41 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using UnityEditor; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + [CustomEditor(typeof(MB3_TextureBaker))] + [CanEditMultipleObjects] + public class MB3_TextureBakerEditor : Editor + { + + MB3_TextureBakerEditorInternal tbe = new MB3_TextureBakerEditorInternal(); + + void OnEnable() + { + tbe.OnEnable(serializedObject); + } + + void OnDisable() + { + tbe.OnDisable(); + } + + public override void OnInspectorGUI() + { + tbe.DrawGUI(serializedObject, (MB3_TextureBaker)target, targets, typeof(MB3_MeshBakerEditorWindow)); + } + + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditor.cs.meta new file mode 100644 index 00000000..f96a1be4 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 26f57cf1f0be79d4f995b90509950e0f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditorInternal.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditorInternal.cs new file mode 100644 index 00000000..fecb4622 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditorInternal.cs @@ -0,0 +1,589 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using DigitalOpus.MB.Core; + +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + public class MB3_TextureBakerEditorInternal + { + + public class MultiMatSubmeshInfo + { + public Shader shader; + public GameObjectFilterInfo.StandardShaderBlendMode stdBlendMode = GameObjectFilterInfo.StandardShaderBlendMode.NotApplicable; + + public MultiMatSubmeshInfo(Shader s, Material m) + { + shader = s; + if (m.shader.name.StartsWith("Standard") && m.HasProperty("_Mode")) + { + stdBlendMode = (GameObjectFilterInfo.StandardShaderBlendMode)m.GetFloat("_Mode"); + } + else + { + stdBlendMode = GameObjectFilterInfo.StandardShaderBlendMode.NotApplicable; + } + } + + public override bool Equals(object obj) + { + MultiMatSubmeshInfo b = (MultiMatSubmeshInfo)obj; + return (stdBlendMode == b.stdBlendMode && shader == b.shader); + } + + public override int GetHashCode() + { + return shader.GetHashCode() ^ (int)stdBlendMode; + } + } + + public class MeshSubmeshMaterial + { + public Mesh mesh; + public Material srcMat; + public int submesh; + public bool obUVs; + public GameObject srcGameObj; + + public MeshSubmeshMaterial(Mesh mesh, Material srcMat, int submesh, bool obUVs, GameObject srcGo) + { + this.mesh = mesh; + this.srcMat = srcMat; + this.submesh = submesh; + this.obUVs = obUVs; + this.srcGameObj = srcGo; + } + + public override bool Equals(object obj) + { + var material = obj as MeshSubmeshMaterial; + return material != null && + EqualityComparer<Mesh>.Default.Equals(mesh, material.mesh) && + EqualityComparer<Material>.Default.Equals(srcMat, material.srcMat) && + submesh == material.submesh && + obUVs == material.obUVs; + } + + public override int GetHashCode() + { + var hashCode = -1560254091; + hashCode = hashCode * -1521134295 + EqualityComparer<Mesh>.Default.GetHashCode(mesh); + hashCode = hashCode * -1521134295 + EqualityComparer<Material>.Default.GetHashCode(srcMat); + hashCode = hashCode * -1521134295 + submesh.GetHashCode(); + hashCode = hashCode * -1521134295 + obUVs.GetHashCode(); + return hashCode; + } + } + + //add option to exclude skinned mesh renderer and mesh renderer in filter + //example scenes for multi material + + internal static GUIContent insertContent = new GUIContent("+", "add a material"); + internal static GUIContent deleteContent = new GUIContent("-", "delete a material"); + internal static GUILayoutOption buttonWidth = GUILayout.MaxWidth(20f); + public static GUIContent noneContent = new GUIContent(""); + + //private SerializedObject textureBaker; + internal SerializedProperty logLevel, textureBakeResults, maxTilingBakeSize, maxAtlasSize, + doMultiMaterial, doMultiMaterialSplitAtlasesIfTooBig, doMultiMaterialIfOBUVs, considerMeshUVs, resultMaterial, resultMaterials, atlasPadding, + resizePowerOfTwoTextures, customShaderProperties, objsToMesh, texturePackingAlgorithm, layerTexturePackerFastMesh, maxAtlasWidthOverride, maxAtlasHeightOverride, useMaxAtlasWidthOverride, useMaxAtlasHeightOverride, + forcePowerOfTwoAtlas, considerNonTextureProperties, sortOrderAxis, resultType, + resultMaterialsTexArray, textureArrayOutputFormats; + + internal bool resultMaterialsFoldout = true; + internal bool showInstructions = false; + internal bool showContainsReport = true; + + internal MB_EditorStyles editorStyles = new MB_EditorStyles(); + + Color buttonColor = new Color(.8f, .8f, 1f, 1f); + + internal static GUIContent + createPrefabAndMaterialLabelContent = new GUIContent("Create Empty Assets For Combined Material", "Creates a material asset and a 'MB2_TextureBakeResult' asset. You should set the shader on the material. Mesh Baker uses the Texture properties on the material to decide what atlases need to be created. The MB2_TextureBakeResult asset should be used in the 'Texture Bake Result' field."), + logLevelContent = new GUIContent("Log Level"), + openToolsWindowLabelContent = new GUIContent("Open Tools For Adding Objects", "Use these tools to find out what can be combined, discover possible problems with meshes, and quickly add objects."), + fixOutOfBoundsGUIContent = new GUIContent("Consider Mesh UVs", "(Was called 'fix out of bounds UVs') The textures will be sampled based on mesh uv rectangle as well as material tiling. This can have two effects:\n\n" + + "1) If the mesh only uses a small rectangle of it's source material (atlas). Only that small rectangle will be baked into the atlas.\n\n" + + "2) If the mesh has uvs outside the 0,1 range (tiling) then this tiling will be baked into the atlas."), + resizePowerOfTwoGUIContent = new GUIContent("Resize Power-Of-Two Textures", "Shrinks textures so they have a clear border of width 'Atlas Padding' around them. Improves texture packing efficiency."), + customShaderPropertyNamesGUIContent = new GUIContent("Custom Shader Propert Names", "Mesh Baker has a list of common texture properties that it looks for in shaders to generate atlases. Custom shaders may have texture properties not on this list. Add them here and Meshbaker will generate atlases for them."), + combinedMaterialsGUIContent = new GUIContent("Combined Materials", "Use the +/- buttons to add multiple combined materials. You will also need to specify which materials on the source objects map to each combined material."), + textureArrayCombinedMaterialFoldoutGUIContent = new GUIContent("Texture Array Combined Materials", "Use the +/- buttons to add texture array materials. You will also need to specify which materials on the source objects map to each texture array slice."), + maxTilingBakeSizeGUIContent = new GUIContent("Max Tiling Bake Size", "This is the maximum size tiling textures will be baked to."), + maxAtlasSizeGUIContent = new GUIContent("Max Atlas Size", "This is the maximum size of the atlas. If the atlas is larger than this textures being added will be shrunk."), + + objectsToCombineGUIContent = new GUIContent("Objects To Be Combined", "These can be prefabs or scene objects. They must be game objects with Renderer components, not the parent objects. Materials on these objects will baked into the combined material(s)"), + textureBakeResultsGUIContent = new GUIContent("Texture Bake Result", "This asset contains a mapping of materials to UV rectangles in the atlases. It is needed to create combined meshes or adjust meshes so they can use the combined material(s). Create it using 'Create Empty Assets For Combined Material'. Drag it to the 'Texture Bake Result' field to use it."), + texturePackingAgorithmGUIContent = new GUIContent("Texture Packer", "Unity's PackTextures: Atlases are always a power of two. Can crash when trying to generate large atlases. \n\n " + + "Mesh Baker Texture Packer: Atlases will be most efficient size and shape (not limited to a power of two). More robust for large atlases. \n\n" + + "Mesh Baker Texture Packer Fast: Same as Mesh Baker Texture Packer but creates atlases on the graphics card using RenderTextures instead of the CPU. Source textures can be compressed. May not be pixel perfect. \n\n" + + "Mesh Baker Texture Packer Horizontal (Experimental): Packs all images vertically to allow horizontal-only UV-tiling.\n\n" + + "Mesh Baker Texture Packer Vertical (Experimental): Packs all images horizontally other to allow vertical-only UV-tiling.\n\n" + + "Mesh Baker Texture Packer Fast V2 (Experimental): A rewrite of 'Mesh Baker Texture Packer Fast' that is compatible with URP and HDRP and even faster.\n\n"), + + layerTexturePackerFastMeshGUIContent = new GUIContent("Atlas Render Layer", "Bad 'Atlas Render Layer' value. The atlas is rendered using a MeshRenderer in the scene. This MeshRenderer needs to be on a layer that is not used by any other renderers. If there are other renderers on this layer, those renderers could render in front of the atlas which would ruin it."), + configAtlasMultiMatsFromObjsContent = new GUIContent("Build Source To Combined Mapping From \n Objects To Be Combined", "This will group the materials on your source objects by shader and create one source to combined mapping for each shader found. For example if combining trees then all the materials with the same bark shader will be grouped togther and all the materials with the same leaf material will be grouped together. You can adjust the results afterwards. \n\nIf fix out-of-bounds UVs is NOT checked then submeshes with UVs outside 0,0..1,1 will be mapped to their own submesh regardless of shader."), + configAtlasTextureSlicesFromObjsContent = new GUIContent("Build Texture Array Slices From \n Objects To Be Combined", "This will group the materials on your source objects by shader and create slices. Objects with out-of-bounds UVs will be put on their own slice. You can adjust the results afterwards."), + forcePowerOfTwoAtlasContent = new GUIContent("Force Power-Of-Two Atlas", "Forces atlas x and y dimensions to be powers of two with aspect ratio 1:1,1:2 or 2:1. Unity recommends textures be a power of two for everything but GUI textures."), + considerNonTexturePropertiesContent = new GUIContent("Blend Non-Texture Properties", "Will blend non-texture properties such as _Color, _Glossiness with the textures. Objects with different non-texture property values will be copied into different parts of the atlas even if they use the same textures. This feature requires that TextureBlenders " + + "exist for the result material shader. It is easy to extend Mesh Baker by writing custom TextureBlenders. Default TextureBlenders exist for: \n" + + " - Standard \n" + + " - Diffuse \n" + + " - Bump Diffuse\n"), + gc_SortAlongAxis = new GUIContent("Sort Along Axis", "Transparent materials often require that triangles be rendered in a certain order. This will sort Game Objects along the specified axis. Triangles will be added to the combined mesh in this order."), + gc_DoMultiMaterialSplitAtlasesIfTooBig = new GUIContent("Split Atlases If Textures Don't Fit", ""), + gc_DoMultiMaterialSplitAtlasesIfOBUVs = new GUIContent("Put Meshes With Out Of Bounds UV On Submesh", ""), + gc_overrideMaxAtlasWidth = new GUIContent("Override Max Atlas Width", "Set the maximum width of the atlas to this."), + gc_overrideMaxAtlasHeight = new GUIContent("Override Max Atlas Height", "Set the maximum height of the atlas to this."), + gc_useMaxAtlasWidthOverride = new GUIContent("Use Max Width Override", "Force the atlas width to not exceed the override value"), + gc_useMaxAtlasHeightOverride = new GUIContent("Use Max Height Override", "Force the atlas width to not exceed the override value"), + gc_atlasPadding = new GUIContent("Atlas Padding", "Number of pixels to pad around the edge of the atlas."); + + protected string layerTexturePackerFastMeshMessage; + + + [MenuItem("GameObject/Create Other/Mesh Baker/TextureBaker", false, 100)] + public static void CreateNewTextureBaker() + { + MB3_TextureBaker[] mbs = (MB3_TextureBaker[])Editor.FindObjectsOfType(typeof(MB3_TextureBaker)); + Regex regex = new Regex(@"\((\d+)\)$", RegexOptions.Compiled | RegexOptions.CultureInvariant); + int largest = 0; + try + { + for (int i = 0; i < mbs.Length; i++) + { + Match match = regex.Match(mbs[i].name); + if (match.Success) + { + int val = Convert.ToInt32(match.Groups[1].Value); + if (val >= largest) + largest = val + 1; + } + } + } + catch (Exception ex) + { + if (ex == null) ex = null; //Do nothing supress compiler warning + } + GameObject nmb = new GameObject("TextureBaker (" + largest + ")"); + nmb.transform.position = Vector3.zero; + nmb.AddComponent<MB3_MeshBakerGrouper>(); + MB3_TextureBaker tb = nmb.AddComponent<MB3_TextureBaker>(); + tb.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; + } + + void _init(SerializedObject textureBaker) + { + //textureBaker = new SerializedObject(target); + logLevel = textureBaker.FindProperty("LOG_LEVEL"); + doMultiMaterial = textureBaker.FindProperty("_doMultiMaterial"); + doMultiMaterialSplitAtlasesIfTooBig = textureBaker.FindProperty("_doMultiMaterialSplitAtlasesIfTooBig"); + doMultiMaterialIfOBUVs = textureBaker.FindProperty("_doMultiMaterialSplitAtlasesIfOBUVs"); + considerMeshUVs = textureBaker.FindProperty("_fixOutOfBoundsUVs"); + resultMaterial = textureBaker.FindProperty("_resultMaterial"); + resultMaterials = textureBaker.FindProperty("resultMaterials"); + atlasPadding = textureBaker.FindProperty("_atlasPadding"); + resizePowerOfTwoTextures = textureBaker.FindProperty("_resizePowerOfTwoTextures"); + customShaderProperties = textureBaker.FindProperty("_customShaderProperties"); + objsToMesh = textureBaker.FindProperty("objsToMesh"); + maxTilingBakeSize = textureBaker.FindProperty("_maxTilingBakeSize"); + maxAtlasSize = textureBaker.FindProperty("_maxAtlasSize"); + maxAtlasWidthOverride = textureBaker.FindProperty("_maxAtlasWidthOverride"); + maxAtlasHeightOverride = textureBaker.FindProperty("_maxAtlasHeightOverride"); + useMaxAtlasWidthOverride = textureBaker.FindProperty("_useMaxAtlasWidthOverride"); + useMaxAtlasHeightOverride = textureBaker.FindProperty("_useMaxAtlasHeightOverride"); + textureBakeResults = textureBaker.FindProperty("_textureBakeResults"); + texturePackingAlgorithm = textureBaker.FindProperty("_packingAlgorithm"); + layerTexturePackerFastMesh = textureBaker.FindProperty("_layerTexturePackerFastMesh"); + forcePowerOfTwoAtlas = textureBaker.FindProperty("_meshBakerTexturePackerForcePowerOfTwo"); + considerNonTextureProperties = textureBaker.FindProperty("_considerNonTextureProperties"); + sortOrderAxis = textureBaker.FindProperty("sortAxis"); + resultType = textureBaker.FindProperty("_resultType"); + resultMaterialsTexArray = textureBaker.FindProperty("resultMaterialsTexArray"); + textureArrayOutputFormats = textureBaker.FindProperty("textureArrayOutputFormats"); + } + + public void OnEnable(SerializedObject textureBaker) + { + _init(textureBaker); + if (editorStyles == null) editorStyles = new MB_EditorStyles(); + editorStyles.Init(); + + } + + public void OnDisable() + { + editorStyles.DestroyTextures(); + } + + public void DrawGUI(SerializedObject textureBaker, MB3_TextureBaker momm, UnityEngine.Object[] targets, System.Type editorWindow) + { + if (textureBaker == null) + { + return; + } + textureBaker.Update(); + + + showInstructions = EditorGUILayout.Foldout(showInstructions, "Instructions:"); + if (showInstructions) + { + EditorGUILayout.HelpBox("1. Add scene objects or prefabs to combine. For best results these should use the same shader as result material.\n\n" + + "2. Create Empty Assets For Combined Material(s)\n\n" + + "3. Check that shader on result material(s) are correct.\n\n" + + "4. Bake materials into combined material(s).\n\n" + + "5. Look at warnings/errors in console. Decide if action needs to be taken.\n\n" + + "6. You are now ready to build combined meshs or adjust meshes to use the combined material(s).", UnityEditor.MessageType.None); + + } + + EditorGUILayout.PropertyField(logLevel, logLevelContent); + EditorGUILayout.Separator(); + + // Selected objects + EditorGUILayout.BeginVertical(editorStyles.editorBoxBackgroundStyle); + EditorGUILayout.LabelField("Objects To Be Combined", EditorStyles.boldLabel); + if (GUILayout.Button(openToolsWindowLabelContent)) + { + MB3_MeshBakerEditorWindow mmWin = (MB3_MeshBakerEditorWindow) EditorWindow.GetWindow(editorWindow); + mmWin.SetTarget((MB3_MeshBakerRoot) momm); + } + + object[] objs = MB3_EditorMethods.DropZone("Drag & Drop Renderers Or Parents Here To Add Objects To Be Combined", 300, 50); + MB3_EditorMethods.AddDroppedObjects(objs, momm); + + EditorGUILayout.PropertyField(objsToMesh, objectsToCombineGUIContent, true); + EditorGUILayout.Separator(); + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("Select Objects In Scene")) + { + List<MB3_TextureBaker> selectedBakers = _getBakersFromTargets(targets); + List<GameObject> obsToCombine = new List<GameObject>(); + + foreach (MB3_TextureBaker baker in selectedBakers) obsToCombine.AddRange(baker.GetObjectsToCombine()); + Selection.objects = obsToCombine.ToArray(); + if (momm.GetObjectsToCombine().Count > 0) + { + SceneView.lastActiveSceneView.pivot = momm.GetObjectsToCombine()[0].transform.position; + } + + } + if (GUILayout.Button(gc_SortAlongAxis)) + { + MB3_MeshBakerRoot.ZSortObjects sorter = new MB3_MeshBakerRoot.ZSortObjects(); + sorter.sortAxis = sortOrderAxis.vector3Value; + sorter.SortByDistanceAlongAxis(momm.GetObjectsToCombine()); + } + EditorGUILayout.PropertyField(sortOrderAxis, GUIContent.none); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.EndVertical(); + + + // output + EditorGUILayout.Separator(); + EditorGUILayout.LabelField("Output", EditorStyles.boldLabel); + + EditorGUILayout.PropertyField(resultType); + + if (GUILayout.Button(createPrefabAndMaterialLabelContent)) + { + List<MB3_TextureBaker> selectedBakers = _getBakersFromTargets(targets); + string newPrefabPath = EditorUtility.SaveFilePanelInProject("Asset name", "", "asset", "Enter a name for the baked texture results"); + if (newPrefabPath != null) + { + for (int i = 0; i < selectedBakers.Count; i++) + { + CreateCombinedMaterialAssets(selectedBakers[i], newPrefabPath, i == 0 ? true : false); + } + } + } + + EditorGUILayout.PropertyField(textureBakeResults, textureBakeResultsGUIContent); + if (textureBakeResults.objectReferenceValue != null) + { + showContainsReport = EditorGUILayout.Foldout(showContainsReport, "Shaders & Materials Contained"); + if (showContainsReport) + { + EditorGUILayout.HelpBox(((MB2_TextureBakeResults)textureBakeResults.objectReferenceValue).GetDescription(), MessageType.Info); + } + } + + if (resultType.enumValueIndex == (int) MB2_TextureBakeResults.ResultType.textureArray) + { + MB_TextureBakerConfigureTextureArrays.DrawTextureArrayConfiguration(momm, textureBaker, this); + } + else + { + EditorGUILayout.PropertyField(doMultiMaterial, new GUIContent("Multiple Combined Materials")); + + if (momm.doMultiMaterial) + { + MB_TextureBakerEditorConfigureMultiMaterials.DrawMultipleMaterialsMappings(momm, textureBaker, this); + } + else + { + EditorGUILayout.PropertyField(resultMaterial, new GUIContent("Combined Mesh Material")); + } + } + + // settings + int labelWidth = 200; + EditorGUILayout.Separator(); + EditorGUILayout.BeginVertical(editorStyles.editorBoxBackgroundStyle); + EditorGUILayout.LabelField("Material Bake Options", EditorStyles.boldLabel); + + DrawPropertyFieldWithLabelWidth(atlasPadding, gc_atlasPadding, labelWidth); + DrawPropertyFieldWithLabelWidth(maxAtlasSize, maxAtlasSizeGUIContent, labelWidth); + DrawPropertyFieldWithLabelWidth(resizePowerOfTwoTextures, resizePowerOfTwoGUIContent, labelWidth); + DrawPropertyFieldWithLabelWidth(maxTilingBakeSize, maxTilingBakeSizeGUIContent, labelWidth); + EditorGUI.BeginDisabledGroup(momm.doMultiMaterial); + DrawPropertyFieldWithLabelWidth(considerMeshUVs, fixOutOfBoundsGUIContent, labelWidth); + EditorGUI.EndDisabledGroup(); + if (texturePackingAlgorithm.intValue == (int)MB2_PackingAlgorithmEnum.MeshBakerTexturePacker || + texturePackingAlgorithm.intValue == (int)MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Fast || + texturePackingAlgorithm.intValue == (int)MB2_PackingAlgorithmEnum.MeshBakerTexturePaker_Fast_V2_Beta) + { + DrawPropertyFieldWithLabelWidth(forcePowerOfTwoAtlas, forcePowerOfTwoAtlasContent, labelWidth); + } + DrawPropertyFieldWithLabelWidth(considerNonTextureProperties, considerNonTexturePropertiesContent, labelWidth); + if (texturePackingAlgorithm.intValue == (int)MB2_PackingAlgorithmEnum.UnitysPackTextures) + { + EditorGUILayout.HelpBox("Unity's texture packer has memory problems and frequently crashes the editor.", MessageType.Warning); + } + EditorGUILayout.PropertyField(texturePackingAlgorithm, texturePackingAgorithmGUIContent); + if (MB3_TextureCombinerPipeline.USE_EXPERIMENTAL_HOIZONTALVERTICAL) + { + if (texturePackingAlgorithm.enumValueIndex == (int)MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal) + { + + EditorGUILayout.PropertyField(useMaxAtlasWidthOverride, gc_useMaxAtlasWidthOverride); + if (!useMaxAtlasWidthOverride.boolValue) EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.PropertyField(maxAtlasWidthOverride, gc_overrideMaxAtlasWidth); + if (!useMaxAtlasWidthOverride.boolValue) EditorGUI.EndDisabledGroup(); + } + else if (texturePackingAlgorithm.enumValueIndex == (int)MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical) + { + EditorGUILayout.PropertyField(useMaxAtlasHeightOverride, gc_useMaxAtlasHeightOverride); + if (!useMaxAtlasHeightOverride.boolValue) EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.PropertyField(maxAtlasHeightOverride, gc_overrideMaxAtlasHeight); + if (!useMaxAtlasHeightOverride.boolValue) EditorGUI.EndDisabledGroup(); + } + } + + if (texturePackingAlgorithm.intValue == (int) MB2_PackingAlgorithmEnum.MeshBakerTexturePaker_Fast_V2_Beta) + { + // layer field + int newValueLayerTexturePackerFastMesh = EditorGUILayout.LayerField(layerTexturePackerFastMeshGUIContent, layerTexturePackerFastMesh.intValue); + bool isNewValue = newValueLayerTexturePackerFastMesh == layerTexturePackerFastMesh.intValue; + layerTexturePackerFastMesh.intValue = newValueLayerTexturePackerFastMesh; + if (isNewValue) + { + Renderer[] rs = GameObject.FindObjectsOfType<Renderer>(); + int numRenderersOnLayer = 0; + for (int i = 0; i < rs.Length; i++) + { + if (rs[i].gameObject.layer == newValueLayerTexturePackerFastMesh) numRenderersOnLayer++; + } + + string layerName = LayerMask.LayerToName(layerTexturePackerFastMesh.intValue); + if (layerName != null && layerName.Length > 0 && numRenderersOnLayer == 0) + { + layerTexturePackerFastMeshMessage = null; + } else + { + layerTexturePackerFastMeshMessage = layerTexturePackerFastMeshGUIContent.tooltip; + } + } + + string scriptDefinesErrMessage = ValidatePlayerSettingsDefineSymbols(); + + if (layerTexturePackerFastMesh.intValue == -1 || + (layerTexturePackerFastMeshMessage != null && layerTexturePackerFastMeshMessage.Length > 0) || + (scriptDefinesErrMessage != null)) + { + EditorGUILayout.HelpBox(layerTexturePackerFastMeshMessage + "\n\n" + scriptDefinesErrMessage, MessageType.Error); + } + } + + EditorGUILayout.PropertyField(customShaderProperties, customShaderPropertyNamesGUIContent, true); + EditorGUILayout.EndVertical(); + EditorGUILayout.Separator(); + Color oldColor = GUI.backgroundColor; + GUI.color = buttonColor; + if (GUILayout.Button("Bake Materials Into Combined Material")) + { + List<MB3_TextureBaker> selectedBakers = _getBakersFromTargets(targets); + foreach (MB3_TextureBaker tb in selectedBakers) + { + tb.CreateAtlases(updateProgressBar, true, new MB3_EditorMethods()); + EditorUtility.ClearProgressBar(); + if (tb.textureBakeResults != null) EditorUtility.SetDirty(momm.textureBakeResults); + } + } + GUI.backgroundColor = oldColor; + textureBaker.ApplyModifiedProperties(); + if (GUI.changed) + { + textureBaker.SetIsDifferentCacheDirty(); + } + } + + public void DrawPropertyFieldWithLabelWidth(SerializedProperty prop, GUIContent content, int labelWidth) + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(content, GUILayout.Width(labelWidth)); + EditorGUILayout.PropertyField(prop, noneContent); + EditorGUILayout.EndHorizontal(); + } + + public void updateProgressBar(string msg, float progress) + { + EditorUtility.DisplayProgressBar("Combining Meshes", msg, progress); + } + + public static void CreateCombinedMaterialAssets(MB3_TextureBaker target, string pth, bool allowOverwrite=true) + { + MB3_TextureBaker mom = (MB3_TextureBaker)target; + string baseName = Path.GetFileNameWithoutExtension(pth); + if (baseName == null || baseName.Length == 0) return; + string folderPath = Path.GetDirectoryName(pth) + "/"; + + List<string> matNames = new List<string>(); + if (mom.resultType == MB2_TextureBakeResults.ResultType.textureArray) + { + for (int i = 0; i < mom.resultMaterialsTexArray.Length; i++) + { + string nm = folderPath + baseName + "-mat" + i + ".mat"; + if (!allowOverwrite) nm = AssetDatabase.GenerateUniqueAssetPath(nm); + matNames.Add(nm); + AssetDatabase.CreateAsset(new Material(Shader.Find("Diffuse")), matNames[i]); + mom.resultMaterialsTexArray[i].combinedMaterial = (Material) AssetDatabase.LoadAssetAtPath(matNames[i], typeof(Material)); + } + } + else + { + if (mom.doMultiMaterial) + { + for (int i = 0; i < mom.resultMaterials.Length; i++) + { + string nm = folderPath + baseName + "-mat" + i + ".mat"; + if (!allowOverwrite) nm = AssetDatabase.GenerateUniqueAssetPath(nm); + matNames.Add(nm); + AssetDatabase.CreateAsset(new Material(Shader.Find("Diffuse")), matNames[i]); + mom.resultMaterials[i].combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matNames[i], typeof(Material)); + } + } + else + { + string nm = folderPath + baseName + "-mat.mat"; + if (!allowOverwrite) nm = AssetDatabase.GenerateUniqueAssetPath(nm); + matNames.Add(nm); + Material newMat = null; + if (mom.GetObjectsToCombine().Count > 0 && mom.GetObjectsToCombine()[0] != null) + { + Renderer r = mom.GetObjectsToCombine()[0].GetComponent<Renderer>(); + if (r == null) + { + Debug.LogWarning("Object " + mom.GetObjectsToCombine()[0] + " does not have a Renderer on it."); + } + else + { + if (r.sharedMaterial != null) + { + newMat = new Material(r.sharedMaterial); + //newMat.shader = r.sharedMaterial.shader; + MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, r.sharedMaterial); + } + } + } + else + { + Debug.Log("If you add objects to be combined before creating the Combined Material Assets. Then Mesh Baker will create a result material that is a duplicate of the material on the first object to be combined. This saves time configuring the shader."); + } + if (newMat == null) + { + newMat = new Material(Shader.Find("Diffuse")); + } + AssetDatabase.CreateAsset(newMat, matNames[0]); + mom.resultMaterial = (Material)AssetDatabase.LoadAssetAtPath(matNames[0], typeof(Material)); + } + } + + //create the MB2_TextureBakeResults + string nmm = pth; + if (!allowOverwrite) nmm = AssetDatabase.GenerateUniqueAssetPath(nmm); + AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<MB2_TextureBakeResults>(), nmm); + mom.textureBakeResults = (MB2_TextureBakeResults)AssetDatabase.LoadAssetAtPath(nmm, typeof(MB2_TextureBakeResults)); + AssetDatabase.Refresh(); + } + + List<MB3_TextureBaker> _getBakersFromTargets(UnityEngine.Object[] targs) + { + List<MB3_TextureBaker> outList = new List<MB3_TextureBaker>(targs.Length); + for (int i = 0; i < targs.Length; i++) + { + outList.Add((MB3_TextureBaker) targs[i]); + } + + return outList; + } + + public static string ValidatePlayerSettingsDefineSymbols() + { + // Check that the needed defines exist or are present when they should not be. + MBVersion.PipelineType pipelineType = MBVersion.DetectPipeline(); + BuildTargetGroup targetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; + string scriptDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup); + + string s = ""; + if (pipelineType == MBVersion.PipelineType.HDRP) + { + if (!scriptDefines.Contains(MBVersion.MB_USING_HDRP)) + { + s += "The GraphicsSettings -> Render Pipeline Asset is configured to use HDRP. Please add 'MB_USING_HDRP' to PlayerSettings -> Scripting Define Symbols for all the build platforms " + + " that you are targeting. If there are compile errors check that the MeshBakerCore.asmdef file has references for:\n\n" + + " Unity.RenderPipelines.HighDefinition.Runtime\n" + + " Unity.RenderPipelines.HighDefinition.Config.Runtime (Unity 2019.3+)\n"; + } + + /* + Type tp = Type.GetType("UnityEngine.Rendering.HighDefinition.HDAdditionalCameraData"); + if (tp == null) + { + s += "The class 'HDAdditionalCameraData' cannot be found by the MeshBaker assembly. Ensure that the following assemblies are referenced by the MeshBaker.asmdef file: \n" + + " Unity.RenderPipelines.HighDefinition.Runtime\n" + + " Unity.RenderPipelines.HighDefinition.Config.Runtime (Unity 2019.3+)\n\n"+ + "Or download the HDRP version of the package from the asset store."; + } + */ + } + else + { + if (scriptDefines.Contains(MBVersion.MB_USING_HDRP)) + { + s += "Please remove 'MB_USING_HDRP' from PlayerSettings -> Scripting Define Symbols for the current build platform. If this define is present there may be compile errors because Mesh Baker tries to access classes which only exist in the HDRP API."; + } + } + + if (s.Length > 0) + { + return s; + } + else + { + return null; + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditorInternal.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditorInternal.cs.meta new file mode 100644 index 00000000..de4a3768 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB3_TextureBakerEditorInternal.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3e6251fd17be4a142ab0827940bdba27 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_BuildPreprocessChecker.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_BuildPreprocessChecker.cs new file mode 100644 index 00000000..3e553eee --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_BuildPreprocessChecker.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using UnityEngine; +using DigitalOpus.MB.Core; + +#if UNITY_2018_1_OR_NEWER +using UnityEditor.Build; +using UnityEditor.Build.Reporting; + +namespace DigitalOpus.MB.MBEditor +{ + class MB_BuildPreprocessChecker : IPreprocessBuildWithReport + { + public int callbackOrder { get { return 0; } } + public void OnPreprocessBuild(BuildReport report) + { + string msg = MB3_TextureBakerEditorInternal.ValidatePlayerSettingsDefineSymbols(); + if (msg != null) Debug.LogError(msg); + } + } +} +#endif + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_BuildPreprocessChecker.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_BuildPreprocessChecker.cs.meta new file mode 100644 index 00000000..5bbbee5b --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_BuildPreprocessChecker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 44eb70764e9827e4588ea4a1c5888b59 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsInSceneEditorWindow.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsInSceneEditorWindow.cs new file mode 100644 index 00000000..66e71610 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsInSceneEditorWindow.cs @@ -0,0 +1,189 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + public class MB_ReplacePrefabsInSceneEditorWindow : EditorWindow + { + private MB_ReplacePrefabsSettings model; + private SerializedObject soModel; + private SerializedProperty prefabPairs; + private SerializedProperty reverseSrcAndTarg; + private SerializedProperty enforceSrcAndTargHaveSameStructure; + Vector2 scrollViewPos; + private string errorMessages; + private string infoMessages; + + private GUIContent GUI_replacePefabsInScene = new GUIContent("Replace Prefabs In Scene", "This will find all instances of the source prefabs in the scene and replace them with instances of the target prefabs." + + "Will attempt to copy component settings from the source to the target (ignoring references to project assets, those are left alone)." + + "Prefabs should have identical hierarchy and components for the copy component settings to work."); + private GUIContent GUI_switchSrcAndTarget = new GUIContent("Switch Source and Target Prefabs", "If this is checked then search the scene for target prefabs and replace them with source prefabs."); + private GUIContent GUI_enforceSrcAndTargetHaveSameStructure = new GUIContent("Enforce Source And Target Have Same Structure", "If this is checked then prefab instances will only be replaced if they have the same hierarchy and components. \n\n" + + "If it is not checked then prefabs can be very different and ONLY THE TRANSFORM, LAYERS, TAGS & STATIC FLAGS ARE COPIED.\n\n" + + "WARNING THIS IS A DESTRUCTIVE OPERARTION! BACK UP YOUR SCENE FIRST."); + + [MenuItem("Window/Mesh Baker/Replace Prefabs In Scene")] + public static void ShowWindow() + { + GetWindow<MB_ReplacePrefabsInSceneEditorWindow>(true, "Replace Prefabs In Scene", true).Show(); + } + + public static void ShowWindow(MB3_BatchPrefabBaker.MB3_PrefabBakerRow[] prefabPairs) + { + MB_ReplacePrefabsInSceneEditorWindow window = GetWindow<MB_ReplacePrefabsInSceneEditorWindow>(true, "Replace Prefabs In Scene", true); + window.Show(); + MB_ReplacePrefabsSettings.PrefabPair[] pps = new MB_ReplacePrefabsSettings.PrefabPair[prefabPairs.Length]; + for (int i = 0; i < pps.Length; i++) + { + pps[i] = new MB_ReplacePrefabsSettings.PrefabPair() + { + enabled = true, + srcPrefab = prefabPairs[i].sourcePrefab, + targPrefab = prefabPairs[i].resultPrefab, + }; + } + + window.model.prefabsToSwitch = pps; + window.soModel.Update(); + } + + private void InitModel() + { + soModel = new SerializedObject(model); + prefabPairs = soModel.FindProperty("prefabsToSwitch"); + reverseSrcAndTarg = soModel.FindProperty("reverseSrcAndTarg"); + enforceSrcAndTargHaveSameStructure = soModel.FindProperty("enforceSrcAndTargHaveSameStructure"); + infoMessages = ""; + errorMessages = ""; + } + + private void OnEnable() + { + minSize = new Vector2(800f, 290f); + if (model == null) + { + model = ScriptableObject.CreateInstance<MB_ReplacePrefabsSettings>(); + + // Set the Hide flags so that this windows data not destroyed when entering playmode or a new scene. + model.hideFlags = HideFlags.DontSave; + } + + InitModel(); + } + + private void OnGUI() + { + EditorGUILayout.BeginHorizontal(); + MB_ReplacePrefabsSettings newModel = (MB_ReplacePrefabsSettings)EditorGUILayout.ObjectField("Settings", model, typeof(MB_ReplacePrefabsSettings), false); + if (newModel != model) + { + if (newModel != null) + { + model = newModel; + } + else + { + if (model == null) model = ScriptableObject.CreateInstance<MB_ReplacePrefabsSettings>(); + + // Set the Hide flags so that this windows data not destroyed when entering playmode or a new scene. + model.hideFlags = HideFlags.DontSave; + } + + InitModel(); + } + + if (model != null) + { + if (GUILayout.Button("Save Settings", GUILayout.Width(200))) + { + string path = EditorUtility.SaveFilePanel("Save Settings", Application.dataPath, "ReplacePrefabSettings", "asset"); + if (path != null) + { + model.hideFlags = HideFlags.None; + string relativepath = "Assets" + path.Substring(Application.dataPath.Length); + Debug.Log("Saved: " + relativepath); + AssetDatabase.CreateAsset(model, relativepath); + } + } + } + + EditorGUILayout.EndHorizontal(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + soModel.Update(); + if (GUILayout.Button(GUI_replacePefabsInScene)) + { + if (EditorUtility.DisplayDialog("Replace Prefabs In Scene", + "Are you sure you want to replace all source prefab instances with the target prefab instances in this scene? \n\n" + + "It is highly recommended that you back up your scene before doing this.", "OK", "Cancel")) + { + ReplacePrefabsInScene(); + } + } + + float labelWidth = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = 300f; + EditorGUILayout.PropertyField(reverseSrcAndTarg, GUI_switchSrcAndTarget); + EditorGUILayout.PropertyField(enforceSrcAndTargHaveSameStructure, GUI_enforceSrcAndTargetHaveSameStructure); + EditorGUIUtility.labelWidth = labelWidth; + if (infoMessages != "" || errorMessages != "") + { + EditorGUILayout.HelpBox(errorMessages + "\n" + infoMessages, errorMessages == "" ? MessageType.Info : MessageType.Error); + } + + scrollViewPos = EditorGUILayout.BeginScrollView(scrollViewPos); + EditorGUILayout.PropertyField(prefabPairs, true); + EditorGUILayout.EndScrollView(); + soModel.ApplyModifiedProperties(); + } + + private void ReplacePrefabsInScene() + { + MB_ReplacePrefabsInScene rp = new MB_ReplacePrefabsInScene(); + rp.replaceEnforceStructure = model.enforceSrcAndTargHaveSameStructure; + int numReplaced = 0; + int numErrors = 0; + errorMessages = ""; + infoMessages = ""; + EditorUtility.DisplayProgressBar("Replace Prefabs In Scene", "Replace Prefabs In Scene", 0); + for (int i = 0; i < model.prefabsToSwitch.Length; i++) + { + MB_ReplacePrefabsSettings.PrefabPair pp = model.prefabsToSwitch[i]; + pp.objsWithErrors.Clear(); + if (pp.enabled) + { + GameObject src, targ; + if (model.reverseSrcAndTarg) + { + src = pp.targPrefab; + targ = pp.srcPrefab; + } + else + { + src = pp.srcPrefab; + targ = pp.targPrefab; + } + + numReplaced += rp.ReplacePrefabInstancesInScene(src, targ, pp.objsWithErrors); + numErrors += pp.objsWithErrors.Count; + } + + EditorUtility.DisplayProgressBar("Replace Prefabs In Scene", "Replace In Scene: " + pp.srcPrefab, (float)i / (float)model.prefabsToSwitch.Length); + } + + EditorUtility.ClearProgressBar(); + + Debug.Log("Total prefab instances replaced: " + numReplaced); + infoMessages = "Total prefab instances replaced: " + numReplaced; + if (numErrors > 0) + { + errorMessages = "There were errors replacing some of the prefabs in the scene. See console for details."; + } + + soModel.Update(); + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsInSceneEditorWindow.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsInSceneEditorWindow.cs.meta new file mode 100644 index 00000000..e6bb46df --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsInSceneEditorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 568dea53a514f9a4fa3a4483bad6a0b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsSettings.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsSettings.cs new file mode 100644 index 00000000..721a17ef --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsSettings.cs @@ -0,0 +1,27 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + [System.Serializable] + public class MB_ReplacePrefabsSettings : ScriptableObject + { + [System.Serializable] + public class PrefabPair + { + public bool enabled = true; + public GameObject srcPrefab; + public GameObject targPrefab; + public List<MB_ReplacePrefabsInScene.Error> objsWithErrors = new List<MB_ReplacePrefabsInScene.Error>(); + } + + public PrefabPair[] prefabsToSwitch = new PrefabPair[0]; + + public bool reverseSrcAndTarg; + + public bool enforceSrcAndTargHaveSameStructure = true; + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsSettings.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsSettings.cs.meta new file mode 100644 index 00000000..3d853138 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_ReplacePrefabsSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2e44f04becd89f469c0431787b827bb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureMultiMaterials.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureMultiMaterials.cs new file mode 100644 index 00000000..0cbd1783 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureMultiMaterials.cs @@ -0,0 +1,413 @@ +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using DigitalOpus.MB.Core; + +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + + public class MB_TextureBakerEditorConfigureMultiMaterials + { + + + public static void DrawMultipleMaterialsMappings(MB3_TextureBaker momm, SerializedObject textureBaker, MB3_TextureBakerEditorInternal tbEditor) + { + EditorGUILayout.BeginVertical(tbEditor.editorStyles.multipleMaterialBackgroundStyle); + EditorGUILayout.LabelField("Source Material To Combined Mapping", EditorStyles.boldLabel); + + float oldLabelWidth = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = 300; + EditorGUILayout.PropertyField(tbEditor.doMultiMaterialIfOBUVs, MB3_TextureBakerEditorInternal.gc_DoMultiMaterialSplitAtlasesIfOBUVs); + EditorGUILayout.PropertyField(tbEditor.doMultiMaterialSplitAtlasesIfTooBig, MB3_TextureBakerEditorInternal.gc_DoMultiMaterialSplitAtlasesIfTooBig); + EditorGUIUtility.labelWidth = oldLabelWidth; + + + if (GUILayout.Button(MB3_TextureBakerEditorInternal.configAtlasMultiMatsFromObjsContent)) + { + MB_TextureBakerEditorConfigureMultiMaterials.ConfigureMutiMaterialsFromObjsToCombine(momm, tbEditor.resultMaterials, textureBaker); + } + + EditorGUILayout.BeginHorizontal(); + tbEditor.resultMaterialsFoldout = EditorGUILayout.Foldout(tbEditor.resultMaterialsFoldout, MB3_TextureBakerEditorInternal.combinedMaterialsGUIContent); + + if (GUILayout.Button(MB3_TextureBakerEditorInternal.insertContent, EditorStyles.miniButtonLeft, MB3_TextureBakerEditorInternal.buttonWidth)) + { + if (tbEditor.resultMaterials.arraySize == 0) + { + momm.resultMaterials = new MB_MultiMaterial[1]; + momm.resultMaterials[0] = new MB_MultiMaterial(); + momm.resultMaterials[0].considerMeshUVs = momm.fixOutOfBoundsUVs; + } + else + { + int idx = tbEditor.resultMaterials.arraySize - 1; + tbEditor.resultMaterials.InsertArrayElementAtIndex(idx); + tbEditor.resultMaterials.GetArrayElementAtIndex(idx + 1).FindPropertyRelative("considerMeshUVs").boolValue = momm.fixOutOfBoundsUVs; + } + } + if (GUILayout.Button(MB3_TextureBakerEditorInternal.deleteContent, EditorStyles.miniButtonRight, MB3_TextureBakerEditorInternal.buttonWidth)) + { + tbEditor.resultMaterials.DeleteArrayElementAtIndex(tbEditor.resultMaterials.arraySize - 1); + } + EditorGUILayout.EndHorizontal(); + if (tbEditor.resultMaterialsFoldout) + { + for (int i = 0; i < tbEditor.resultMaterials.arraySize; i++) + { + EditorGUILayout.Separator(); + if (i % 2 == 1) + { + EditorGUILayout.BeginVertical(tbEditor.editorStyles.multipleMaterialBackgroundStyle); + } + else + { + EditorGUILayout.BeginVertical(tbEditor.editorStyles.multipleMaterialBackgroundStyleDarker); + } + string s = ""; + if (i < momm.resultMaterials.Length && momm.resultMaterials[i] != null && momm.resultMaterials[i].combinedMaterial != null) s = momm.resultMaterials[i].combinedMaterial.shader.ToString(); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("---------- submesh:" + i + " " + s, EditorStyles.boldLabel); + if (GUILayout.Button(MB3_TextureBakerEditorInternal.deleteContent, EditorStyles.miniButtonRight, MB3_TextureBakerEditorInternal.buttonWidth)) + { + tbEditor.resultMaterials.DeleteArrayElementAtIndex(i); + } + EditorGUILayout.EndHorizontal(); + if (i < tbEditor.resultMaterials.arraySize) + { + EditorGUILayout.Separator(); + SerializedProperty resMat = tbEditor.resultMaterials.GetArrayElementAtIndex(i); + EditorGUILayout.PropertyField(resMat.FindPropertyRelative("combinedMaterial")); + EditorGUILayout.PropertyField(resMat.FindPropertyRelative("considerMeshUVs")); + SerializedProperty sourceMats = resMat.FindPropertyRelative("sourceMaterials"); + EditorGUILayout.PropertyField(sourceMats, true); + } + EditorGUILayout.EndVertical(); + } + } + EditorGUILayout.EndVertical(); + } + + + /* tried to see if the MultiMaterialConfig could be done using the GroupBy filters. Saddly it didn't work */ + public static void ConfigureMutiMaterialsFromObjsToCombine2(MB3_TextureBaker mom, SerializedProperty resultMaterials, SerializedObject textureBaker) + { + if (mom.GetObjectsToCombine().Count == 0) + { + Debug.LogError("You need to add some objects to combine before building the multi material list."); + return; + } + if (resultMaterials.arraySize > 0) + { + Debug.LogError("You already have some source to combined material mappings configured. You must remove these before doing this operation."); + return; + } + if (mom.textureBakeResults == null) + { + Debug.LogError("Texture Bake Result asset must be set before using this operation."); + return; + } + + //validate that the objects to be combined are valid + for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) + { + GameObject go = mom.GetObjectsToCombine()[i]; + if (go == null) + { + Debug.LogError("Null object in list of objects to combine at position " + i); + return; + } + Renderer r = go.GetComponent<Renderer>(); + if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer))) + { + Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer"); + return; + } + if (r.sharedMaterial == null) + { + Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material"); + return; + } + } + + IGroupByFilter[] filters = new IGroupByFilter[3]; + filters[0] = new GroupByOutOfBoundsUVs(); + filters[1] = new GroupByShader(); + filters[2] = new MB3_GroupByStandardShaderType(); + + List<GameObjectFilterInfo> gameObjects = new List<GameObjectFilterInfo>(); + HashSet<GameObject> objectsAlreadyIncludedInBakers = new HashSet<GameObject>(); + for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) + { + GameObjectFilterInfo goaw = new GameObjectFilterInfo(mom.GetObjectsToCombine()[i], objectsAlreadyIncludedInBakers, filters); + if (goaw.materials.Length > 0) //don't consider renderers with no materials + { + gameObjects.Add(goaw); + } + } + + //analyse meshes + Dictionary<int, MB_Utility.MeshAnalysisResult> meshAnalysisResultCache = new Dictionary<int, MB_Utility.MeshAnalysisResult>(); + int totalVerts = 0; + for (int i = 0; i < gameObjects.Count; i++) + { + //string rpt = String.Format("Processing {0} [{1} of {2}]", gameObjects[i].go.name, i, gameObjects.Count); + //EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " A", .6f); + Mesh mm = MB_Utility.GetMesh(gameObjects[i].go); + int nVerts = 0; + if (mm != null) + { + nVerts += mm.vertexCount; + MB_Utility.MeshAnalysisResult mar; + if (!meshAnalysisResultCache.TryGetValue(mm.GetInstanceID(), out mar)) + { + + //EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Check Out Of Bounds UVs", .6f); + MB_Utility.hasOutOfBoundsUVs(mm, ref mar); + //Rect dummy = mar.uvRect; + MB_Utility.doSubmeshesShareVertsOrTris(mm, ref mar); + meshAnalysisResultCache.Add(mm.GetInstanceID(), mar); + } + if (mar.hasOutOfBoundsUVs) + { + int w = (int)mar.uvRect.width; + int h = (int)mar.uvRect.height; + gameObjects[i].outOfBoundsUVs = true; + gameObjects[i].warning += " [WARNING: has uvs outside the range (0,1) tex is tiled " + w + "x" + h + " times]"; + } + if (mar.hasOverlappingSubmeshVerts) + { + gameObjects[i].submeshesOverlap = true; + gameObjects[i].warning += " [WARNING: Submeshes share verts or triangles. 'Multiple Combined Materials' feature may not work.]"; + } + } + totalVerts += nVerts; + //EditorUtility.DisplayProgressBar("Analysing Scene", rpt + " Validate OBuvs Multi Material", .6f); + Renderer mr = gameObjects[i].go.GetComponent<Renderer>(); + if (!MB_Utility.AreAllSharedMaterialsDistinct(mr.sharedMaterials)) + { + gameObjects[i].warning += " [WARNING: Object uses same material on multiple submeshes. This may produce poor results when used with multiple materials or fix out of bounds uvs.]"; + } + } + + List<GameObjectFilterInfo> objsNotAddedToBaker = new List<GameObjectFilterInfo>(); + + Dictionary<GameObjectFilterInfo, List<List<GameObjectFilterInfo>>> gs2bakeGroupMap = MB3_MeshBakerEditorWindowAnalyseSceneTab.sortIntoBakeGroups3(gameObjects, objsNotAddedToBaker, filters, false, mom.maxAtlasSize); + + mom.resultMaterials = new MB_MultiMaterial[gs2bakeGroupMap.Keys.Count]; + string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults); + string baseName = Path.GetFileNameWithoutExtension(pth); + string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6); + int k = 0; + foreach (GameObjectFilterInfo m in gs2bakeGroupMap.Keys) + { + MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial(); + mm.sourceMaterials = new List<Material>(); + mm.sourceMaterials.Add(m.materials[0]); + string matName = folderPath + baseName + "-mat" + k + ".mat"; + Material newMat = new Material(Shader.Find("Diffuse")); + MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, m.materials[0]); + AssetDatabase.CreateAsset(newMat, matName); + mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); + k++; + } + MBVersionEditor.UpdateIfDirtyOrScript(textureBaker); + } + + + //posibilities + // using fixOutOfBoundsUVs or not + // + public static void ConfigureMutiMaterialsFromObjsToCombine(MB3_TextureBaker mom, SerializedProperty resultMaterials, SerializedObject textureBaker) + { + if (mom.GetObjectsToCombine().Count == 0) + { + Debug.LogError("You need to add some objects to combine before building the multi material list."); + return; + } + if (resultMaterials.arraySize > 0) + { + Debug.LogError("You already have some source to combined material mappings configured. You must remove these before doing this operation."); + return; + } + if (mom.textureBakeResults == null) + { + Debug.LogError("Texture Bake Result asset must be set before using this operation."); + return; + } + + Dictionary<MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List<List<Material>>> shader2Material_map = new Dictionary<MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List<List<Material>>>(); + Dictionary<Material, Mesh> obUVobject2mesh_map = new Dictionary<Material, Mesh>(); + + //validate that the objects to be combined are valid + for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) + { + GameObject go = mom.GetObjectsToCombine()[i]; + if (go == null) + { + Debug.LogError("Null object in list of objects to combine at position " + i); + return; + } + Renderer r = go.GetComponent<Renderer>(); + if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer))) + { + Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer"); + return; + } + if (r.sharedMaterial == null) + { + Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material"); + return; + } + } + + //first pass put any meshes with obUVs on their own submesh if not fixing OB uvs + if (mom.doMultiMaterialSplitAtlasesIfOBUVs) + { + for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) + { + GameObject go = mom.GetObjectsToCombine()[i]; + Mesh m = MB_Utility.GetMesh(go); + MB_Utility.MeshAnalysisResult dummyMar = new MB_Utility.MeshAnalysisResult(); + Renderer r = go.GetComponent<Renderer>(); + for (int j = 0; j < r.sharedMaterials.Length; j++) + { + if (MB_Utility.hasOutOfBoundsUVs(m, ref dummyMar, j)) + { + if (!obUVobject2mesh_map.ContainsKey(r.sharedMaterials[j])) + { + Debug.LogWarning("Object " + go + " submesh " + j + " uses UVs outside the range 0,0..1,1 to generate tiling. This object has been mapped to its own submesh in the combined mesh. It can share a submesh with other objects that use different materials if you use the fix out of bounds UVs feature which will bake the tiling"); + obUVobject2mesh_map.Add(r.sharedMaterials[j], m); + } + } + } + } + } + + //second pass put other materials without OB uvs in a shader to material map + for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) + { + Renderer r = mom.GetObjectsToCombine()[i].GetComponent<Renderer>(); + for (int j = 0; j < r.sharedMaterials.Length; j++) + { + if (!obUVobject2mesh_map.ContainsKey(r.sharedMaterials[j])) + { //if not already added + if (r.sharedMaterials[j] == null) continue; + List<List<Material>> binsOfMatsThatUseShader = null; + MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo newKey = new MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo(r.sharedMaterials[j].shader, r.sharedMaterials[j]); + if (!shader2Material_map.TryGetValue(newKey, out binsOfMatsThatUseShader)) + { + binsOfMatsThatUseShader = new List<List<Material>>(); + binsOfMatsThatUseShader.Add(new List<Material>()); + shader2Material_map.Add(newKey, binsOfMatsThatUseShader); + } + if (!binsOfMatsThatUseShader[0].Contains(r.sharedMaterials[j])) binsOfMatsThatUseShader[0].Add(r.sharedMaterials[j]); + } + } + } + + int numResMats = shader2Material_map.Count; + //third pass for each shader grouping check how big the atlas would be and group into bins that would fit in an atlas + if (mom.doMultiMaterialSplitAtlasesIfTooBig) + { + if (mom.packingAlgorithm == MB2_PackingAlgorithmEnum.UnitysPackTextures) + { + Debug.LogWarning("Unity texture packer does not support splitting atlases if too big. Atlases will not be split."); + } + else + { + numResMats = 0; + foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo sh in shader2Material_map.Keys) + { + List<List<Material>> binsOfMatsThatUseShader = shader2Material_map[sh]; + List<Material> allMatsThatUserShader = binsOfMatsThatUseShader[0];//at this point everything is in the same list + binsOfMatsThatUseShader.RemoveAt(0); + MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); + combiner.saveAtlasesAsAssets = false; + if (allMatsThatUserShader.Count > 1) combiner.fixOutOfBoundsUVs = mom.fixOutOfBoundsUVs; + else combiner.fixOutOfBoundsUVs = false; + + // Do the texture pack + List<AtlasPackingResult> packingResults = new List<AtlasPackingResult>(); + Material tempMat = new Material(sh.shader); + MB_AtlasesAndRects atlasesAndRects = new MB_AtlasesAndRects(); + combiner.CombineTexturesIntoAtlases(null, atlasesAndRects, tempMat, mom.GetObjectsToCombine(), allMatsThatUserShader, null, packingResults, + onlyPackRects:true, splitAtlasWhenPackingIfTooBig:true); + for (int i = 0; i < packingResults.Count; i++) + { + + List<MB_MaterialAndUVRect> matsData = (List<MB_MaterialAndUVRect>)packingResults[i].data; + List<Material> mats = new List<Material>(); + for (int j = 0; j < matsData.Count; j++) + { + Material mat = matsData[j].material; + if (!mats.Contains(mat)) + { + mats.Add(mat); + } + } + binsOfMatsThatUseShader.Add(mats); + } + numResMats += binsOfMatsThatUseShader.Count; + } + } + } + + //build the result materials + if (shader2Material_map.Count == 0 && obUVobject2mesh_map.Count == 0) Debug.LogError("Found no materials in list of objects to combine"); + mom.resultMaterials = new MB_MultiMaterial[numResMats + obUVobject2mesh_map.Count]; + string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults); + string baseName = Path.GetFileNameWithoutExtension(pth); + string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6); + int k = 0; + foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo sh in shader2Material_map.Keys) + { + foreach (List<Material> matsThatUse in shader2Material_map[sh]) + { + MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial(); + mm.sourceMaterials = matsThatUse; + if (mm.sourceMaterials.Count == 1) + { + mm.considerMeshUVs = false; + } + else + { + mm.considerMeshUVs = mom.fixOutOfBoundsUVs; + } + string matName = folderPath + baseName + "-mat" + k + ".mat"; + Material newMat = new Material(Shader.Find("Diffuse")); + if (matsThatUse.Count > 0 && matsThatUse[0] != null) + { + MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, matsThatUse[0]); + } + AssetDatabase.CreateAsset(newMat, matName); + mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); + k++; + } + } + foreach (Material m in obUVobject2mesh_map.Keys) + { + MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial(); + mm.sourceMaterials = new List<Material>(); + mm.sourceMaterials.Add(m); + mm.considerMeshUVs = false; + string matName = folderPath + baseName + "-mat" + k + ".mat"; + Material newMat = new Material(Shader.Find("Diffuse")); + MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, m); + AssetDatabase.CreateAsset(newMat, matName); + mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); + k++; + } + MBVersionEditor.UpdateIfDirtyOrScript(textureBaker); + } + + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureMultiMaterials.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureMultiMaterials.cs.meta new file mode 100644 index 00000000..c38bef37 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureMultiMaterials.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a61c02ab9beccee4aa8d443c9e2625fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureTextureArrays.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureTextureArrays.cs new file mode 100644 index 00000000..833024de --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureTextureArrays.cs @@ -0,0 +1,551 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using DigitalOpus.MB.Core; + +using UnityEditor; + +namespace DigitalOpus.MB.MBEditor +{ + + public class MB_TextureBakerConfigureTextureArrays + { + private class Slice + { + public List<MB_MaterialAndUVRect> atlasRects; + public AtlasPackingResult packingResult; + public int numAtlasRects; + } + + private static GUIContent gc_TextureArrayOutputFormats = new GUIContent( + "Texture Array Output Formats", + "Texture Arrays do not have a 'TextureImporter' that lets you change the TextureArray format." + + "You can provide a list of formats to be generated here. You will probably have one set of formats per platform."); + public static void DrawTextureArrayConfiguration(MB3_TextureBaker momm, SerializedObject textureBaker, MB3_TextureBakerEditorInternal editorInternal) + { + EditorGUILayout.BeginVertical(editorInternal.editorStyles.multipleMaterialBackgroundStyle); + EditorGUILayout.LabelField("Texture Array Slice Configuration", EditorStyles.boldLabel); + + float oldLabelWidth = EditorGUIUtility.labelWidth; + + if (GUILayout.Button(MB3_TextureBakerEditorInternal.configAtlasTextureSlicesFromObjsContent)) + { + ConfigureTextureArraysFromObjsToCombine(momm, editorInternal.resultMaterialsTexArray, textureBaker); + } + + if (GUILayout.Button("Report texture sizes")) + { + Debug.Log(ReportTextureSizesAndFormats(momm)); + } + + if (editorInternal.textureArrayOutputFormats.arraySize == 0) + { + EditorGUILayout.HelpBox("You need at least one output format.", MessageType.Error); + } + + EditorGUILayout.PropertyField(editorInternal.textureArrayOutputFormats, gc_TextureArrayOutputFormats, true); + EditorGUILayout.BeginHorizontal(); + editorInternal.resultMaterialsFoldout = EditorGUILayout.Foldout(editorInternal.resultMaterialsFoldout, MB3_TextureBakerEditorInternal.textureArrayCombinedMaterialFoldoutGUIContent); + if (GUILayout.Button(MB3_TextureBakerEditorInternal.insertContent, EditorStyles.miniButtonLeft, MB3_TextureBakerEditorInternal.buttonWidth)) + { + if (editorInternal.resultMaterialsTexArray.arraySize == 0) + { + momm.resultMaterialsTexArray = new MB_MultiMaterialTexArray[1]; + } + else + { + int idx = editorInternal.resultMaterialsTexArray.arraySize - 1; + editorInternal.resultMaterialsTexArray.InsertArrayElementAtIndex(idx); + } + } + + if (GUILayout.Button(MB3_TextureBakerEditorInternal.deleteContent, EditorStyles.miniButtonRight, MB3_TextureBakerEditorInternal.buttonWidth)) + { + editorInternal.resultMaterialsTexArray.DeleteArrayElementAtIndex(editorInternal.resultMaterialsTexArray.arraySize - 1); + } + + EditorGUILayout.EndHorizontal(); + + + if (editorInternal.resultMaterialsFoldout) + { + for (int i = 0; i < editorInternal.resultMaterialsTexArray.arraySize; i++) + { + + EditorGUILayout.Separator(); + if (i % 2 == 1) + { + EditorGUILayout.BeginVertical(editorInternal.editorStyles.multipleMaterialBackgroundStyle); + } + else + { + EditorGUILayout.BeginVertical(editorInternal.editorStyles.multipleMaterialBackgroundStyleDarker); + } + + string s = ""; + if (i < momm.resultMaterialsTexArray.Length && momm.resultMaterialsTexArray[i] != null && momm.resultMaterialsTexArray[i].combinedMaterial != null) s = momm.resultMaterialsTexArray[i].combinedMaterial.shader.ToString(); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("---------- submesh:" + i + " " + s, EditorStyles.boldLabel); + if (GUILayout.Button(MB3_TextureBakerEditorInternal.deleteContent, EditorStyles.miniButtonRight, MB3_TextureBakerEditorInternal.buttonWidth)) + { + editorInternal.resultMaterialsTexArray.DeleteArrayElementAtIndex(i); + } + + EditorGUILayout.EndHorizontal(); + if (i < editorInternal.resultMaterialsTexArray.arraySize) + { + EditorGUILayout.Separator(); + SerializedProperty resMat = editorInternal.resultMaterialsTexArray.GetArrayElementAtIndex(i); + EditorGUILayout.PropertyField(resMat.FindPropertyRelative("combinedMaterial")); + SerializedProperty slices = resMat.FindPropertyRelative("slices"); + EditorGUILayout.PropertyField(slices, true); + } + + EditorGUILayout.EndVertical(); + } + } + + EditorGUILayout.EndVertical(); + + } + + public static string ReportTextureSizesAndFormats(MB3_TextureBaker mom) + { + if (mom.resultType != MB2_TextureBakeResults.ResultType.textureArray) + { + Debug.LogError("Result Type must be Texture Array."); + return ""; + } + + for (int resMatIdx = 0; resMatIdx < mom.resultMaterialsTexArray.Length; resMatIdx++) + { + MB_MultiMaterialTexArray resMatTexArray = mom.resultMaterialsTexArray[resMatIdx]; + if (resMatTexArray.combinedMaterial == null) + { + Debug.LogError("Result Material " + resMatIdx + " is null"); + return ""; + } + } + + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + // Visit each result material + for (int resMatIdx = 0; resMatIdx < mom.resultMaterialsTexArray.Length; resMatIdx++) + { + MB_MultiMaterialTexArray resMatTexArray = mom.resultMaterialsTexArray[resMatIdx]; + + // Do an atlas pack in order to collect all the textures needed by the result material + // And group these by material texture property. + + MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); + combiner.saveAtlasesAsAssets = false; + combiner.fixOutOfBoundsUVs = false; + List<AtlasPackingResult> packingResults = new List<AtlasPackingResult>(); + Material tempMat = new Material(resMatTexArray.combinedMaterial.shader); + + List<Material> allSourceMaterials = new List<Material>(); + for (int sliceIdx = 0; sliceIdx < resMatTexArray.slices.Count; sliceIdx++) + { + List<Material> srcMats = new List<Material>(); + resMatTexArray.slices[sliceIdx].GetAllUsedMaterials(srcMats); + for (int srcMatIdx = 0; srcMatIdx < srcMats.Count; srcMatIdx++) + { + if (srcMats[srcMatIdx] != null && !allSourceMaterials.Contains(srcMats[srcMatIdx])) + { + allSourceMaterials.Add(srcMats[srcMatIdx]); + } + } + } + + MB_AtlasesAndRects atlasesAndRects = new MB_AtlasesAndRects(); + combiner.CombineTexturesIntoAtlases(null, atlasesAndRects, tempMat, mom.GetObjectsToCombine(), allSourceMaterials, null, packingResults, + onlyPackRects:true, splitAtlasWhenPackingIfTooBig:false); + + // Now vist the packing results and collect all the textures + Debug.Assert(packingResults.Count == 1); + for (int texPropIdx = 0; texPropIdx < atlasesAndRects.texPropertyNames.Length; texPropIdx++) + { + string propertyName = atlasesAndRects.texPropertyNames[texPropIdx]; + sb.AppendLine(String.Format("Prop: {0}", propertyName)); + + List<MB_MaterialAndUVRect> matsData = (List<MB_MaterialAndUVRect>)packingResults[0].data; + HashSet<Material> visitedMats = new HashSet<Material>(); + for (int matAndGoIdx = 0; matAndGoIdx < matsData.Count; matAndGoIdx++) + { + Material mat = matsData[matAndGoIdx].material; + if (visitedMats.Contains(mat)) continue; + visitedMats.Add(mat); + if (mat.HasProperty(propertyName)) + { + Texture tex = mat.GetTexture(propertyName); + if (tex != null) + { + string texFormatString = "UnknownFormat"; + string texWrapMode = "UnknownClampMode"; + if (tex is Texture2D) + { + texFormatString = ((Texture2D)tex).format.ToString(); + texWrapMode = ((Texture2D)tex).wrapMode.ToString(); + } + + sb.AppendLine(String.Format(" {0} x {1} format:{2} wrapMode:{3} {4}", tex.width.ToString().PadLeft(6,' '), tex.height.ToString().PadRight(6,' '), texFormatString.PadRight(20,' '), texWrapMode.PadRight(12,' '), tex.name)); + } + } + } + } + } + + return sb.ToString(); + } + + public static void ConfigureTextureArraysFromObjsToCombine(MB3_TextureBaker mom, SerializedProperty resultMaterialsTexArrays, SerializedObject textureBaker) + { + if (mom.GetObjectsToCombine().Count == 0) + { + Debug.LogError("You need to add some objects to combine before building the texture array result materials."); + return; + } + if (resultMaterialsTexArrays.arraySize > 0) + { + Debug.LogError("You already have some texture array result materials configured. You must remove these before doing this operation."); + return; + } + if (mom.textureBakeResults == null) + { + Debug.LogError("Texture Bake Result asset must be set before using this operation."); + return; + } + + //validate that the objects to be combined are valid + for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) + { + GameObject go = mom.GetObjectsToCombine()[i]; + if (go == null) + { + Debug.LogError("Null object in list of objects to combine at position " + i); + return; + } + + if (MB_Utility.GetMesh(go) == null) + { + Debug.LogError("Could not get mesh for object in list of objects to combine at position " + i); + return; + } + + Renderer r = go.GetComponent<Renderer>(); + if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer))) + { + Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer"); + return; + } + if (r.sharedMaterial == null) + { + Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material"); + return; + } + } + + //Will sort into "result material" + // slices + Dictionary<MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List<Slice>> shader2ResultMat_map = new Dictionary<MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List<Slice>>(); + + // first pass split by shader and analyse meshes. + List<GameObject> objsToCombine = mom.GetObjectsToCombine(); + for (int meshIdx = 0; meshIdx < objsToCombine.Count; meshIdx++) + { + GameObject srcGo = objsToCombine[meshIdx]; + Mesh mesh = MB_Utility.GetMesh(srcGo); + Renderer r = MB_Utility.GetRenderer(srcGo); + + if (mom.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log("1st Pass 'Split By Shader' Processing Mesh: "+ mesh +" Num submeshes: " + r.sharedMaterials.Length); + for (int submeshIdx = 0; submeshIdx < r.sharedMaterials.Length; submeshIdx++) + { + if (r.sharedMaterials[submeshIdx] == null) continue; + + MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo newKey = new MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo(r.sharedMaterials[submeshIdx].shader, r.sharedMaterials[submeshIdx]); + // Initially we fill the list of srcMaterials with garbage MB_MaterialAndUVRects. Will get proper ones when we atlas pack. + MB_MaterialAndUVRect submeshMaterial = new MB_MaterialAndUVRect( + r.sharedMaterials[submeshIdx], + new Rect(0, 0, 0, 0), // garbage value + false, //garbage value + new Rect(0, 0, 0, 0), // garbage value + new Rect(0, 0, 0, 0), // garbage value + new Rect(0, 0, 1, 1), // garbage value + MB_TextureTilingTreatment.unknown, // garbage value + r.name); + submeshMaterial.objectsThatUse = new List<GameObject>(); + submeshMaterial.objectsThatUse.Add(r.gameObject); + if (!shader2ResultMat_map.ContainsKey(newKey)) + { + // this is a new shader create a new result material + Slice srcMaterials = new Slice + { + atlasRects = new List<MB_MaterialAndUVRect>(), + numAtlasRects = 1, + + }; + srcMaterials.atlasRects.Add(submeshMaterial); + List<Slice> binsOfMatsThatUseShader = new List<Slice>(); + binsOfMatsThatUseShader.Add(srcMaterials); + if (mom.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" Adding Source Material: " + submeshMaterial.material); + shader2ResultMat_map.Add(newKey, binsOfMatsThatUseShader); + } + else + { + // there is a result material that uses this shader. Add this source material + Slice srcMaterials = shader2ResultMat_map[newKey][0]; // There should only be one list of source materials + if (srcMaterials.atlasRects.Find(x => x.material == submeshMaterial.material) == null) + { + if (mom.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" Adding Source Material: " + submeshMaterial.material); + srcMaterials.atlasRects.Add(submeshMaterial); + } + } + } + } + + int resMatCount = 0; + foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo resultMat in shader2ResultMat_map.Keys) + { + // at this point there there should be only one slice with all the source materials + resMatCount++; + + // For each result material, all source materials are in the first slice. + // We will now split these using a texture packer. Each "atlas" generated by the packer will be a slice. + { + // All source materials should be in the first slice at this point. + List<Slice> slices = shader2ResultMat_map[resultMat]; + List<Slice> newSlices = new List<Slice>(); + Slice firstSlice = slices[0]; + List<Material> allMatsThatUserShader = new List<Material>(); + List<GameObject> objsThatUseFirstSlice = new List<GameObject>(); + for (int i = 0; i < firstSlice.atlasRects.Count; i++) + { + allMatsThatUserShader.Add(firstSlice.atlasRects[i].material); + if (!objsThatUseFirstSlice.Contains(firstSlice.atlasRects[i].objectsThatUse[0])) + { + objsThatUseFirstSlice.Add(firstSlice.atlasRects[i].objectsThatUse[0]); + } + } + + MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); + combiner.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; + combiner.saveAtlasesAsAssets = false; + combiner.fixOutOfBoundsUVs = true; + combiner.doMergeDistinctMaterialTexturesThatWouldExceedAtlasSize = true; + List<AtlasPackingResult> packingResults = new List<AtlasPackingResult>(); + Material tempMat = new Material(resultMat.shader); + + if (mom.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("======== 2nd pass. Use atlas packer to split the first slice into multiple if it exceeds atlas size. "); + combiner.CombineTexturesIntoAtlases(null, null, tempMat, mom.GetObjectsToCombine(), allMatsThatUserShader, null, packingResults, + onlyPackRects:true, splitAtlasWhenPackingIfTooBig:true); + if (mom.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("======== Completed packing with texture packer. numPackingResults: " + packingResults.Count); + newSlices.Clear(); + + // The texture packing just split the atlas into multiple atlases. Each atlas will become a "slice". + for (int newSliceIdx = 0; newSliceIdx < packingResults.Count; newSliceIdx++) + { + List<MB_MaterialAndUVRect> sourceMats = new List<MB_MaterialAndUVRect>(); + List<MB_MaterialAndUVRect> packedMatRects = (List<MB_MaterialAndUVRect>) packingResults[newSliceIdx].data; + HashSet<Rect> distinctAtlasRects = new HashSet<Rect>(); + for (int packedMatRectIdx = 0; packedMatRectIdx < packedMatRects.Count; packedMatRectIdx++) + { + MB_MaterialAndUVRect muvr = packedMatRects[packedMatRectIdx]; + distinctAtlasRects.Add(muvr.atlasRect); + { + Rect encapsulatingRect = muvr.GetEncapsulatingRect(); + Vector2 sizeInAtlas_px = new Vector2( + packingResults[newSliceIdx].atlasX * encapsulatingRect.width, + packingResults[newSliceIdx].atlasY * encapsulatingRect.height); + } + sourceMats.Add(muvr); + } + + Slice slice = new Slice() + { + atlasRects = sourceMats, + packingResult = packingResults[newSliceIdx], + numAtlasRects = distinctAtlasRects.Count, + }; + + newSlices.Add(slice); + } + + // Replace first slice with split version. + if (mom.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("First slice exceeded atlas size splitting it into " + newSlices.Count + " slices"); + slices.RemoveAt(0); + for (int i = 0; i < newSlices.Count; i++) + { + slices.Insert(i, newSlices[i]); + } + } + } + + // build the texture array result materials + if (shader2ResultMat_map.Count == 0) Debug.LogError("Found no materials in list of objects to combine"); + mom.resultMaterialsTexArray = new MB_MultiMaterialTexArray[shader2ResultMat_map.Count]; + int k = 0; + foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo resMatKey in shader2ResultMat_map.Keys) + { + List<Slice> srcSlices = shader2ResultMat_map[resMatKey]; + MB_MultiMaterialTexArray mm = mom.resultMaterialsTexArray[k] = new MB_MultiMaterialTexArray(); + for (int sliceIdx = 0; sliceIdx < srcSlices.Count; sliceIdx++) + { + Slice slice = srcSlices[sliceIdx]; + MB_TexArraySlice resSlice = new MB_TexArraySlice(); + List<Material> usedMats = new List<Material>(); + + for (int srcMatIdx = 0; srcMatIdx < slice.atlasRects.Count; srcMatIdx++) + { + MB_MaterialAndUVRect matAndUVRect = slice.atlasRects[srcMatIdx]; + List<GameObject> objsThatUse = matAndUVRect.objectsThatUse; + for (int objsThatUseIdx = 0; objsThatUseIdx < objsThatUse.Count; objsThatUseIdx++) + { + GameObject obj = objsThatUse[objsThatUseIdx]; + if (!resSlice.ContainsMaterialAndMesh(slice.atlasRects[srcMatIdx].material, MB_Utility.GetMesh(obj))) + { + resSlice.sourceMaterials.Add( + new MB_TexArraySliceRendererMatPair() + { + renderer = obj, + sourceMaterial = slice.atlasRects[srcMatIdx].material + } + ); + } + } + } + + { + // Should we use considerUVs + bool doConsiderUVs = false; + // If there is more than one atlas rectangle in a slice then use considerUVs + if (slice.numAtlasRects > 1) + { + doConsiderUVs = true; + } + else + { + // There is only one source material, could be: + // - lots of tiling (don't want consider UVs) + // - We are extracting a small part of a large atlas (want considerUVs) + if (slice.packingResult.atlasX >= mom.maxAtlasSize || + slice.packingResult.atlasY >= mom.maxAtlasSize) + { + doConsiderUVs = false; // lots of tiling + } + else + { + doConsiderUVs = true; // extracting a small part of an atlas + } + } + + resSlice.considerMeshUVs = doConsiderUVs; + } + + mm.slices.Add(resSlice); + } + + // Enforce integrity. If a material appears in more than one slice then all those slices must be considerUVs=true + { + // collect all distinct materials + HashSet<Material> distinctMats = new HashSet<Material>(); + Dictionary<Material, int> mat2sliceCount = new Dictionary<Material, int>(); + for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) + { + for (int sliceMatIdx = 0; sliceMatIdx < mm.slices[sliceIdx].sourceMaterials.Count; sliceMatIdx++) + { + Material mat = mm.slices[sliceIdx].sourceMaterials[sliceMatIdx].sourceMaterial; + distinctMats.Add(mat); + mat2sliceCount[mat] = 0; + } + } + + // Count the number of slices that use each material. + foreach (Material mat in distinctMats) + { + for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) + { + if (mm.slices[sliceIdx].ContainsMaterial(mat)) + { + mat2sliceCount[mat] = mat2sliceCount[mat] + 1; + } + } + } + + // Check that considerUVs is true for any materials that appear more than once + foreach (Material mat in distinctMats) + { + if (mat2sliceCount[mat] > 1) + { + for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) + { + if (mm.slices[sliceIdx].ContainsMaterial(mat)) + { + if (mom.LOG_LEVEL >= MB2_LogLevel.debug && + mm.slices[sliceIdx].considerMeshUVs) Debug.Log("There was a material " + mat + " that was used by more than one slice and considerUVs was false. sliceIdx:" + sliceIdx); + mm.slices[sliceIdx].considerMeshUVs = true; + } + } + } + } + } + + // Cleanup. remove "Renderer"s from source materials that do not use considerUVs and delete extra + { + // put any slices with consider UVs first + List<MB_TexArraySlice> newSlices = new List<MB_TexArraySlice>(); + for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) + { + if (mm.slices[sliceIdx].considerMeshUVs == true) + { + newSlices.Add(mm.slices[sliceIdx]); + } + } + + // for any slices without considerUVs, remove "renderer" and truncate + for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) + { + MB_TexArraySlice slice = mm.slices[sliceIdx]; + if (slice.considerMeshUVs == false) + { + newSlices.Add(slice); + HashSet<Material> distinctMats = slice.GetDistinctMaterials(); + slice.sourceMaterials.Clear(); + foreach (Material mat in distinctMats) + { + slice.sourceMaterials.Add(new MB_TexArraySliceRendererMatPair() {sourceMaterial = mat }); + } + } + } + + mm.slices = newSlices; + } + + string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults); + string baseName = Path.GetFileNameWithoutExtension(pth); + string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6); + string matName = folderPath + baseName + "-mat" + k + ".mat"; + Material existingAsset = AssetDatabase.LoadAssetAtPath<Material>(matName); + if (!existingAsset) + { + Material newMat = new Material(Shader.Find("Standard")); + // Don't try to configure the material we need the user to pick a shader that has TextureArrays + AssetDatabase.CreateAsset(newMat, matName); + } + + mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); + k++; + } + + + MBVersionEditor.UpdateIfDirtyOrScript(textureBaker); + textureBaker.Update(); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureTextureArrays.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureTextureArrays.cs.meta new file mode 100644 index 00000000..2f55f7f2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MB_TextureBakerEditorConfigureTextureArrays.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8860927141262124c92c04030afa23d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MeshBakerEditor.asmdef b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MeshBakerEditor.asmdef new file mode 100644 index 00000000..7f33fdde --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MeshBakerEditor.asmdef @@ -0,0 +1,16 @@ +{ + "name": "MeshBakerEditor", + "references": [ + "MeshBakerCore" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/MeshBakerEditor.asmdef.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MeshBakerEditor.asmdef.meta new file mode 100644 index 00000000..5f6a81f6 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/MeshBakerEditor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: dd9493fbefb52fd48926bb1291844728 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core.meta new file mode 100644 index 00000000..e1778b84 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 86da0ab80204ad045a06973e6f83bcab +folderAsset: yes +DefaultImporter: + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_BakeInPlace.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_BakeInPlace.cs new file mode 100644 index 00000000..7d52d85b --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_BakeInPlace.cs @@ -0,0 +1,123 @@ +using UnityEngine; +using UnityEditor; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class MB3_BakeInPlace + { + + public static bool BakeMeshesInPlace(MB3_MeshCombinerSingle mom, List<GameObject> objsToMesh, string saveFolder, bool clearBuffersAfterBake, ProgressUpdateDelegate updateProgressBar) + { + if (MB3_MeshCombiner.EVAL_VERSION) return false; + if (saveFolder.Length < 6) + { + Debug.LogError("Please select a folder for meshes."); + return false; + } + if (!Directory.Exists(Application.dataPath + saveFolder.Substring(6))) + { + Debug.Log((Application.dataPath + saveFolder.Substring(6))); + Debug.Log(Path.GetFullPath(Application.dataPath + saveFolder.Substring(6))); + Debug.LogError("The selected Folder For Meshes does not exist or is not inside the projects Assets folder. Please 'Choose Folder For Bake In Place Meshes' that is inside the project's assets folder."); + return false; + } + + MB3_EditorMethods editorMethods = new MB3_EditorMethods(); + mom.DestroyMeshEditor(editorMethods); + + MB_RenderType originalRenderType = mom.settings.renderType; + bool success = false; + string[] objNames = GenerateNames(objsToMesh); + for (int i = 0; i < objsToMesh.Count; i++) + { + if (objsToMesh[i] == null) + { + Debug.LogError("The " + i + "th object on the list of objects to combine is 'None'. Use Command-Delete on Mac OS X; Delete or Shift-Delete on Windows to remove this one element."); + return false; + } + + Mesh m = new Mesh(); + success = BakeOneMesh(mom, m, objsToMesh[i]); + if (success) + { + string newMeshFilePath = saveFolder + "/" + objNames[i]; + Debug.Log("Creating mesh asset at " + newMeshFilePath + " for mesh " + m + " numVerts " + m.vertexCount); + AssetDatabase.CreateAsset(mom.GetMesh(), newMeshFilePath); + } + if (updateProgressBar != null) updateProgressBar("Created mesh saving mesh on " + objsToMesh[i].name + " to asset " + objNames[i], .6f); + } + mom.settings.renderType = originalRenderType; + MB_Utility.Destroy(mom.resultSceneObject); + if (clearBuffersAfterBake) { mom.ClearBuffers(); } + return success; + } + + static public bool BakeOneMesh(MB3_MeshCombinerSingle mom, Mesh targMesh, GameObject objToBake) + { + if (objToBake == null) + { + Debug.LogError("An object on the list of objects to combine is 'None'. Use Command-Delete on Mac OS X; Delete or Shift-Delete on Windows to remove this one element."); + return false; + } + if (targMesh == null) + { + + Debug.LogError("No mesh was provided."); + return false; + } + + mom.SetMesh(targMesh); + mom.ClearMesh(); + GameObject[] objs = new GameObject[] { objToBake }; + Renderer r = MB_Utility.GetRenderer(objToBake); + if (r is SkinnedMeshRenderer) + { + mom.settings.renderType = MB_RenderType.skinnedMeshRenderer; + } + else if (r is MeshRenderer) + { + mom.settings.renderType = MB_RenderType.meshRenderer; + } + else + { + Debug.LogError("Unsupported Renderer type on object. Must be SkinnedMesh or MeshFilter."); + return false; + } + if (mom.AddDeleteGameObjects(objs, null, false)) + { + mom.Apply(MB3_MeshBakerEditorFunctions.UnwrapUV2); + Mesh mf = MB_Utility.GetMesh(objToBake); + if (mf == null) + { + Debug.LogError("Failed to create mesh for " + objToBake.name); + return false; + } + } + + return true; + } + + public static string[] GenerateNames(List<GameObject> objsToMesh) + { + string[] ns = new string[objsToMesh.Count]; + for (int i = 0; i < objsToMesh.Count; i++) + { + string newNameBase = objsToMesh[i].name; + string newName = newNameBase + ".asset"; + int j = 1; + while (ArrayUtility.Contains<string>(ns, objsToMesh[i].name)) + { + newName = newNameBase + "-" + j + ".asset"; + j++; + } + ns[i] = newName; + } + return ns; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_BakeInPlace.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_BakeInPlace.cs.meta new file mode 100644 index 00000000..40934574 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_BakeInPlace.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f952330a1fffff945bddaa6d99f086bf +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MBVersionEditor.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MBVersionEditor.cs new file mode 100644 index 00000000..80ae31a5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MBVersionEditor.cs @@ -0,0 +1,120 @@ +/** + * DLLs cannot interpret preprocessor directives, so this class acts as a "bridge" + */ +using System; +using UnityEngine; +using UnityEditor; +using System.Collections; + +namespace DigitalOpus.MB.Core +{ + + public enum MB_ReplacePrefabOption + { + mbDefault = 0, + connectToPrefab = 1, + nameBased = 2, + } + + public enum MB_PrefabType + { + modelPrefabAsset, + prefabAsset, + scenePefabInstance, + isInstanceAndNotAPartOfAnyPrefab, + } + + public interface MBVersionEditorInterface + { + string GetPlatformString(); + void RegisterUndo(UnityEngine.Object o, string s); + void SetInspectorLabelWidth(float width); + void UpdateIfDirtyOrScript(SerializedObject so); + UnityEngine.Object PrefabUtility_GetCorrespondingObjectFromSource(GameObject go); + bool IsAutoPVRTC(TextureImporterFormat platformFormat, TextureImporterFormat platformDefaultFormat); + MB_PrefabType GetPrefabType(UnityEngine.Object go); + void UnpackPrefabInstance(UnityEngine.GameObject go, ref SerializedObject so); + void ReplacePrefab(GameObject gameObject, string assetPath, MB_ReplacePrefabOption replacePrefabOptions); + + GameObject GetPrefabInstanceRoot(GameObject sceneInstance); + + TextureImporterFormat Map_TextureFormat_2_TextureImporterFormat(TextureFormat texFormat, out bool success); + } + + public class MBVersionEditor + { + private static MBVersionEditorInterface _MBVersion; + + private static MBVersionEditorInterface GetInstance() + { + if (_MBVersion == null) _MBVersion = _CreateMBVersionConcrete(); + return _MBVersion; + } + + private static MBVersionEditorInterface _CreateMBVersionConcrete() + { + Type vit = null; +#if EVAL_VERSION + vit = Type.GetType("DigitalOpus.MB.Core.MBVersionEditorConcrete,Assembly-CSharp-Editor"); +#else + vit = typeof(MBVersionEditorConcrete); +#endif + return (MBVersionEditorInterface)Activator.CreateInstance(vit); + } + + public static string GetPlatformString() + { + return GetInstance().GetPlatformString(); + } + + public static void RegisterUndo(UnityEngine.Object o, string s) + { + GetInstance().RegisterUndo(o, s); + } + + public static void SetInspectorLabelWidth(float width) + { + GetInstance().SetInspectorLabelWidth(width); + } + + public static void UpdateIfDirtyOrScript(SerializedObject so) + { + GetInstance().UpdateIfDirtyOrScript(so); + } + + public static UnityEngine.Object PrefabUtility_GetCorrespondingObjectFromSource(GameObject go) + { + return GetInstance().PrefabUtility_GetCorrespondingObjectFromSource(go); + } + + public static bool IsAutoPVRTC(TextureImporterFormat platformFormat, TextureImporterFormat platformDefaultFormat) + { + return GetInstance().IsAutoPVRTC(platformFormat, platformDefaultFormat); + } + + public static MB_PrefabType GetPrefabType(UnityEngine.Object go) + { + return GetInstance().GetPrefabType(go); + } + + public static void UnpackPrefabInstance(UnityEngine.GameObject go, ref SerializedObject so) + { + GetInstance().UnpackPrefabInstance(go, ref so); + } + + public static void ReplacePrefab(GameObject gameObject, string assetPath, MB_ReplacePrefabOption replacePrefabOptions) + { + GetInstance().ReplacePrefab(gameObject, assetPath, replacePrefabOptions); + } + + public static GameObject GetPrefabInstanceRoot(GameObject sceneInstance) + { + return GetInstance().GetPrefabInstanceRoot(sceneInstance); + } + + public static TextureImporterFormat Map_TextureFormat_2_TextureImporterFormat(TextureFormat texFormat, out bool success) + { + return GetInstance().Map_TextureFormat_2_TextureImporterFormat(texFormat, out success); + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MBVersionEditor.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MBVersionEditor.cs.meta new file mode 100644 index 00000000..e38eac7b --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MBVersionEditor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9938a92be6deed7438f1b5efdcc48e75 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MeshBakerEditorFunctions.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MeshBakerEditorFunctions.cs new file mode 100644 index 00000000..1f141086 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MeshBakerEditorFunctions.cs @@ -0,0 +1,619 @@ +using UnityEngine; +using System.Collections; +using System.IO; +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using DigitalOpus.MB.Core; +using UnityEditor; +using DigitalOpus.MB.MBEditor; + +public class MB3_MeshBakerEditorFunctions +{ + + /// <summary> + /// Used by UnityEditorInspectors for background colors + /// </summary> + public static Texture2D MakeTex(int width, int height, Color col) + { + Color[] pix = new Color[width * height]; + + for (int i = 0; i < pix.Length; i++) + pix[i] = col; + + Texture2D result = new Texture2D(width, height); + result.SetPixels(pix); + result.Apply(); + + return result; + } + + public static bool BakeIntoCombined(MB3_MeshBakerCommon mom, out bool createdDummyTextureBakeResults) + { + SerializedObject so = null; + return BakeIntoCombined(mom, out createdDummyTextureBakeResults, ref so); + } + + /// <summary> + /// Bakes a combined mesh. + /// </summary> + /// <param name="so">If this is being called from Inspector code then pass in the SerializedObject for the component. + /// This is necessary for "bake into prefab" which can corrupt the SerializedObject.</param> + public static bool BakeIntoCombined(MB3_MeshBakerCommon mom, out bool createdDummyTextureBakeResults, ref SerializedObject so) + { + MB2_OutputOptions prefabOrSceneObject = mom.meshCombiner.outputOption; + createdDummyTextureBakeResults = false; + + // Initial Validate + { + if (mom.meshCombiner.resultSceneObject != null && + (MBVersionEditor.GetPrefabType(mom.meshCombiner.resultSceneObject) == MB_PrefabType.modelPrefabAsset || + MBVersionEditor.GetPrefabType(mom.meshCombiner.resultSceneObject) == MB_PrefabType.prefabAsset)) + { + Debug.LogWarning("Result Game Object was a project asset not a scene object instance. Clearing this field."); + mom.meshCombiner.resultSceneObject = null; + } + + if (prefabOrSceneObject != MB2_OutputOptions.bakeIntoPrefab && prefabOrSceneObject != MB2_OutputOptions.bakeIntoSceneObject) + { + Debug.LogError("Paramater prefabOrSceneObject must be bakeIntoPrefab or bakeIntoSceneObject"); + return false; + } + + if (prefabOrSceneObject == MB2_OutputOptions.bakeIntoPrefab) + { + if (MB3_MeshCombiner.EVAL_VERSION) + { + Debug.LogError("Cannot BakeIntoPrefab with evaluation version."); + return false; + } + + if (mom.resultPrefab == null) + { + Debug.LogError("Need to set the Combined Mesh Prefab field. Create a prefab asset, drag an empty game object into it, and drag it to the 'Combined Mesh Prefab' field."); + return false; + } + + string prefabPth = AssetDatabase.GetAssetPath(mom.resultPrefab); + if (prefabPth == null || prefabPth.Length == 0) + { + Debug.LogError("Could not save result to prefab. Result Prefab value is not a project asset. Is it an instance in the scene?"); + return false; + } + } + } + + { + // Find or create texture bake results + MB3_TextureBaker tb = mom.GetComponentInParent<MB3_TextureBaker>(); + if (mom.textureBakeResults == null && tb != null) + { + mom.textureBakeResults = tb.textureBakeResults; + } + + if (mom.textureBakeResults == null) + { + if (_OkToCreateDummyTextureBakeResult(mom)) + { + createdDummyTextureBakeResults = true; + List<GameObject> gos = mom.GetObjectsToCombine(); + if (mom.GetNumObjectsInCombined() > 0) + { + if (mom.clearBuffersAfterBake) { mom.ClearMesh(); } + else + { + Debug.LogError("'Texture Bake Result' must be set to add more objects to a combined mesh that already contains objects. Try enabling 'clear buffers after bake'"); + return false; + } + } + mom.textureBakeResults = MB2_TextureBakeResults.CreateForMaterialsOnRenderer(gos.ToArray(), mom.meshCombiner.GetMaterialsOnTargetRenderer()); + if (mom.meshCombiner.LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("'Texture Bake Result' was not set. Creating a temporary one. Each material will be mapped to a separate submesh."); } + } + } + } + + // Second level of validation now that TextureBakeResults exists. + MB2_ValidationLevel vl = Application.isPlaying ? MB2_ValidationLevel.quick : MB2_ValidationLevel.robust; + if (!MB3_MeshBakerRoot.DoCombinedValidate(mom, MB_ObjsToCombineTypes.sceneObjOnly, new MB3_EditorMethods(), vl)) + { + return false; + } + + // Add Delete Game Objects + bool success; + if (prefabOrSceneObject == MB2_OutputOptions.bakeIntoSceneObject) + { + success = _BakeIntoCombinedSceneObject(mom, createdDummyTextureBakeResults, ref so); + } + else if (prefabOrSceneObject == MB2_OutputOptions.bakeIntoPrefab) + { + success = _BakeIntoCombinedPrefab(mom, createdDummyTextureBakeResults, ref so); + } else + { + Debug.LogError("Should be impossible."); + success = false; + } + + if (mom.clearBuffersAfterBake) { mom.meshCombiner.ClearBuffers(); } + if (createdDummyTextureBakeResults) MB_Utility.Destroy(mom.textureBakeResults); + return success; + } + + private static bool _BakeIntoCombinedSceneObject(MB3_MeshBakerCommon mom, bool createdDummyTextureBakeResults, ref SerializedObject so) + { + bool success; + mom.ClearMesh(); + if (mom.AddDeleteGameObjects(mom.GetObjectsToCombine().ToArray(), null, false)) + { + success = true; + mom.Apply(UnwrapUV2); + if (mom.parentSceneObject != null) + { + mom.meshCombiner.resultSceneObject.transform.parent = mom.parentSceneObject; + } + + if (createdDummyTextureBakeResults) + { + Debug.Log(String.Format("Successfully baked {0} meshes each material is mapped to its own submesh.", mom.GetObjectsToCombine().Count)); + } + else + { + Debug.Log(String.Format("Successfully baked {0} meshes", mom.GetObjectsToCombine().Count)); + } + } + else + { + success = false; + } + + return success; + } + + private static bool _BakeIntoCombinedPrefab(MB3_MeshBakerCommon mom, bool createdDummyTextureBakeResults, ref SerializedObject so) + { + bool success = false; + + List<Transform> tempPrefabInstanceRoots = null; + GameObject[] objsToCombine = mom.GetObjectsToCombine().ToArray(); + if (mom.meshCombiner.settings.renderType == MB_RenderType.skinnedMeshRenderer) + { + tempPrefabInstanceRoots = new List<Transform>(); + // We are going to move bones of source objs and transforms into our combined mesh prefab so make some duplicates + // so that we don't destroy a setup. + _DuplicateSrcObjectInstancesAndUnpack(mom.meshCombiner.settings.renderType, objsToCombine, tempPrefabInstanceRoots); + } + try + { + MB3_EditorMethods editorMethods = new MB3_EditorMethods(); + mom.ClearMesh(editorMethods); + if (mom.AddDeleteGameObjects(objsToCombine, null, false)) + { + success = true; + mom.Apply(UnwrapUV2); + if (mom.parentSceneObject != null) + { + mom.meshCombiner.resultSceneObject.transform.parent = mom.parentSceneObject; + } + + if (createdDummyTextureBakeResults) + { + Debug.Log(String.Format("Successfully baked {0} meshes each material is mapped to its own submesh.", mom.GetObjectsToCombine().Count)); + } + else + { + Debug.Log(String.Format("Successfully baked {0} meshes", mom.GetObjectsToCombine().Count)); + } + + string prefabPth = AssetDatabase.GetAssetPath(mom.resultPrefab); + if (prefabPth == null || prefabPth.Length == 0) + { + Debug.LogError("Could not save result to prefab. Result Prefab value is not an Asset."); + success = false; + } + else + { + string baseName = Path.GetFileNameWithoutExtension(prefabPth); + string folderPath = prefabPth.Substring(0, prefabPth.Length - baseName.Length - 7); + string newFilename = folderPath + baseName + "-mesh"; + SaveMeshsToAssetDatabase(mom, folderPath, newFilename); + GameObject rootGO = RebuildPrefab(mom, ref so, mom.resultPrefabLeaveInstanceInSceneAfterBake, tempPrefabInstanceRoots, objsToCombine); + } + } + else + { + success = false; + } + + } +#pragma warning disable 0169 + catch (Exception ex) +#pragma warning restore 0169 + { + throw; + } finally + { + // Clean up temporary created instances. If success was true then they should have been added to a prefab + // and cleaned up for us. + if (success == false) + { + if (tempPrefabInstanceRoots != null) + { + for (int i = 0; i < tempPrefabInstanceRoots.Count; i++) + { + MB_Utility.Destroy(tempPrefabInstanceRoots[i]); + } + } + } + } + + return success; + } + + /// <summary> + /// We will modify the source objects (unpack prefabs and re-organize prefabs) so duplicate them. + /// </summary> + /// <param name="tempGameObjectInstances"></param> + private static void _MoveBonesToCombinedMeshPrefabAndDeleteRenderers(Transform newPrefabInstanceRoot, List<Transform> tempGameObjectInstances, GameObject[] srcRenderers) + { + for (int i = 0; i < srcRenderers.Length; i++) + { + MeshRenderer mr = srcRenderers[i].GetComponent<MeshRenderer>(); + if (mr != null) MB_Utility.Destroy(mr); + MeshFilter mf = srcRenderers[i].GetComponent<MeshFilter>(); + if (mf != null) MB_Utility.Destroy(mf); + SkinnedMeshRenderer smr = srcRenderers[i].GetComponent<SkinnedMeshRenderer>(); + if (smr != null) MB_Utility.Destroy(smr); + } + + for (int i = 0; i < tempGameObjectInstances.Count; i++) + { + Transform tt = tempGameObjectInstances[i]; + tempGameObjectInstances[i].parent = newPrefabInstanceRoot; + } + } + + /// <summary> + /// We will modify the source object so duplicate them + /// </summary> + /// <param name="tempGameObjectInstances"></param> + public static void _DuplicateSrcObjectInstancesAndUnpack(MB_RenderType renderType, GameObject[] objsToCombine, List<Transform> tempGameObjectInstances) + { + Debug.Assert(renderType == MB_RenderType.skinnedMeshRenderer, "RenderType must be Skinned Mesh Renderer"); + // first pass, collect the prefab-instance roots for each of the src objects. + Transform[] sceneInstanceParents = new Transform[objsToCombine.Length]; + for (int i = 0; i < objsToCombine.Length; i++) + { + // Get the prefab root + GameObject pr = null; + { + MB_PrefabType pt = MBVersionEditor.GetPrefabType(objsToCombine[i]); + if (pt == MB_PrefabType.scenePefabInstance || pt == MB_PrefabType.isInstanceAndNotAPartOfAnyPrefab) + { + pr = MBVersionEditor.GetPrefabInstanceRoot(objsToCombine[i]); + } + + if (pr == null) + { + pr = _FindCommonAncestorForBonesAnimatorAndSmr(objsToCombine[i]); + + } + } + + sceneInstanceParents[i] = pr.transform; + } + + // second pass, some of the parents could be children of other parents. ensure that we are + // using the uppermost ancestor for all. + for (int i = 0; i < objsToCombine.Length; i++) + { + sceneInstanceParents[i] = _FindUppermostParent(objsToCombine[i], sceneInstanceParents); + } + + // Now build a map of sceneInstanceParents to the renderers contained beneath. + Dictionary<Transform, List<Transform>> srcPrefabInstances2Renderers = new Dictionary<Transform, List<Transform>>(); + for (int i = 0; i < objsToCombine.Length; i++) + { + List<Transform> renderersUsed; + if (!srcPrefabInstances2Renderers.TryGetValue(sceneInstanceParents[i], out renderersUsed)) + { + renderersUsed = new List<Transform>(); + srcPrefabInstances2Renderers.Add(sceneInstanceParents[i], renderersUsed); + } + + renderersUsed.Add(objsToCombine[i].transform); + } + + // Duplicate the prefab-instance-root scene objects + List<Transform> srcRoots = new List<Transform>(srcPrefabInstances2Renderers.Keys); + List<Transform> targRoots = new List<Transform>(); + for (int i = 0; i < srcRoots.Count; i++) + { + Transform src = srcRoots[i]; + GameObject n = GameObject.Instantiate<GameObject>(src.gameObject); + n.transform.rotation = src.rotation; + n.transform.position = src.position; + n.transform.localScale = src.localScale; + targRoots.Add(n.transform); + tempGameObjectInstances.Add(targRoots[i]); + _CheckSrcRootScale(renderType, src); + } + + // Find the correct duplicated objsToCombine in the new instances that maps to objs in "objsToCombine". + List<GameObject> newObjsToCombine = new List<GameObject>(); + for (int i = 0; i < srcRoots.Count; i++) + { + List<Transform> renderers = srcPrefabInstances2Renderers[srcRoots[i]]; + for (int j = 0; j < renderers.Count; j++) + { + Transform t = MB_BatchPrefabBakerEditorFunctions.FindCorrespondingTransform(srcRoots[i], renderers[j], targRoots[i]); + Debug.Assert(!newObjsToCombine.Contains(t.gameObject)); + newObjsToCombine.Add(t.gameObject); + } + + } + Debug.Assert(newObjsToCombine.Count == objsToCombine.Length); + + for (int i = 0; i < newObjsToCombine.Count; i++) + { + //GameObject go = newObjsToCombine[i]; + //SerializedObject so = null; + //MB_PrefabType pt = MBVersionEditor.GetPrefabType(go); + //if (pt == MB_PrefabType.sceneInstance) + //{ + // MBVersionEditor.UnpackPrefabInstance(go, ref so); + //} + + objsToCombine[i] = newObjsToCombine[i]; + } + } + + private static GameObject _FindCommonAncestorForBonesAnimatorAndSmr(GameObject sceneInstance) + { + Renderer mr = MB_Utility.GetRenderer(sceneInstance); + Debug.Assert(mr != null, "Should only be called on a GameObject with a Renderer"); + + Transform lca = sceneInstance.transform; + + if (mr is SkinnedMeshRenderer) + { + // find lowest common ancestor of bones and SMR + Transform[] bones = ((SkinnedMeshRenderer)mr).bones; + HashSet<Transform> ancestorsOfLCA = new HashSet<Transform>(); + + _CollectAllAncestors(ancestorsOfLCA, lca); + + // visit each other bone, find LCA of LCA and bone + for (int i = 0; i < bones.Length; i++) + { + if (bones[i] != null) + { + lca = _FindLowestCommonAncestor(ancestorsOfLCA, lca, bones[i], sceneInstance); + } + } + } + + // Search ancestors for an Animator/Animation + { + Transform t = lca; + while (t != null) + { + if (t.GetComponent<Animator>() != null || + t.GetComponent<Animation>() != null) + { + //Debug.Log("Found ancestor with Animation/Animator: " + t); + lca = t; + break; + } + + t = t.parent; + } + + return lca.gameObject; + } + } + + private static Transform _FindLowestCommonAncestor(HashSet<Transform> ancestorsOfLCA, Transform lca, Transform b, GameObject db_Renderer) + { + // visit all ancestors of b + Transform newLca = lca; + Transform t = b; + bool found = false; + while (t != null) + { + if (ancestorsOfLCA.Contains(t)) + { + found = true; + newLca = t; + break; + } + + t = t.parent; + } + + if (found) + { + //Debug.Log("Finding all ancestors for: " + lca + ", " + b + " found: " + newLca); + if (newLca != lca) + { + _CollectAllAncestors(ancestorsOfLCA, newLca); + } + + return newLca; + } + else + { + Debug.LogError("Renderer '" + db_Renderer + "' does not share a common ancestor in the hierarcy with its bones. If you are baking a prefab, then the prefab will not contain the bones. Try creating a GameObject parent for '" + db_Renderer + "' and its bones and re-baking."); + return null; + } + } + + private static void _CollectAllAncestors(HashSet<Transform> ancestorsOfA, Transform targ) + { + Transform t = targ; + ancestorsOfA.Clear(); + ancestorsOfA.Add(t); + while (t != null) + { + t = t.parent; + ancestorsOfA.Add(t); + } + + //Debug.Log("_CollectAllAncestors of: " + targ + " found: " + ancestorsOfA.Count); + } + + private static void _CheckSrcRootScale(MB_RenderType renderType, Transform trans) + { + Debug.Assert(renderType == MB_RenderType.skinnedMeshRenderer, "Render Type must be skinned mesh Renderer"); + Transform t = trans.parent; + while (t != null) + { + if (Vector3.Distance(t.localScale, Vector3.one) > 10e-5f) + { + Debug.LogError("Src object " + trans.gameObject + " is a game object instance in the scene that is a child of a hierarchy with scale that is not (1,1,1). " + + "This object will become the bones of a skinned mesh renderer and these bones will be copied to the Combined Mesh Prefab. When this happens, it may not " + + "be possible to re create the bone position and scale in the Combined Mesh Prefab that matches the position and scale of the source object. /n/n" + + "When baking into a prefab it is recommended that all source objects be part of prefab instances in the scene. For best " + + " results create temporary prefabs if necessary and include all scaling in each prefab's hierarchy."); + } + t = t.parent; + } + } + + private static Transform _FindUppermostParent(GameObject go, Transform[] objsToCombinePrefabInstanceParent) + { + // traverse up to parent checking if any of the gameObjs are in the list of objs to combine. + Transform commonParent = go.transform; + Transform t = go.transform; + while (t != null) + { + for (int i = 0; i < objsToCombinePrefabInstanceParent.Length; i++) + { + if (objsToCombinePrefabInstanceParent[i] == t) commonParent = objsToCombinePrefabInstanceParent[i]; + } + t = t.parent; + } + + return commonParent; + } + + public static void SaveMeshsToAssetDatabase(MB3_MeshBakerCommon mom, string folderPath, string newFileNameBase) + { + if (MB3_MeshCombiner.EVAL_VERSION) return; + if (mom is MB3_MeshBaker) + { + MB3_MeshBaker mb = (MB3_MeshBaker)mom; + string newFilename = newFileNameBase + ".asset"; + string ap = AssetDatabase.GetAssetPath(((MB3_MeshCombinerSingle)mb.meshCombiner).GetMesh()); + if (ap == null || ap.Equals("")) + { + Debug.Log("Saving mesh asset to " + newFilename); + AssetDatabase.CreateAsset(((MB3_MeshCombinerSingle)mb.meshCombiner).GetMesh(), newFilename); + } + else + { + Debug.Log("Mesh is an existing asset at " + ap); + } + } + else if (mom is MB3_MultiMeshBaker) + { + MB3_MultiMeshBaker mmb = (MB3_MultiMeshBaker)mom; + List<MB3_MultiMeshCombiner.CombinedMesh> combiners = ((MB3_MultiMeshCombiner)mmb.meshCombiner).meshCombiners; + for (int i = 0; i < combiners.Count; i++) + { + string newFilename = newFileNameBase + i + ".asset"; + Mesh mesh = combiners[i].combinedMesh.GetMesh(); + string ap = AssetDatabase.GetAssetPath(mesh); + if (ap == null || ap.Equals("")) + { + Debug.Log("Saving mesh asset to " + newFilename); + AssetDatabase.CreateAsset(mesh, newFilename); + } + else + { + Debug.Log("Mesh is an asset at " + ap); + } + } + } + else + { + Debug.LogError("Argument was not a MB3_MeshBaker or an MB3_MultiMeshBaker."); + } + } + + // The serialized object reference is necessary to work around a nasty unity bug. + public static GameObject RebuildPrefab(MB3_MeshBakerCommon mom, ref SerializedObject so, bool leaveInstanceInSceneAfterBake, List<Transform> tempPrefabInstanceRoots, GameObject[] objsToCombine) + { + if (MB3_MeshCombiner.EVAL_VERSION) return null; + + if (mom.meshCombiner.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Rebuilding Prefab: " + mom.resultPrefab); + GameObject prefabRoot = mom.resultPrefab; + GameObject instanceRootGO = mom.meshCombiner.resultSceneObject; + /* + GameObject instanceRootGO = (GameObject)PrefabUtility.InstantiatePrefab(prefabRoot); + instanceRootGO.transform.position = Vector3.zero; + instanceRootGO.transform.rotation = Quaternion.identity; + instanceRootGO.transform.localScale = Vector3.one; + + //remove everything in the prefab. + + MBVersionEditor.UnpackPrefabInstance(instanceRootGO, ref so); + int numChildren = instanceRootGO.transform.childCount; + for (int i = numChildren - 1; i >= 0; i--) + { + MB_Utility.Destroy(instanceRootGO.transform.GetChild(i).gameObject); + } + + if (mom is MB3_MeshBaker) + { + MB3_MeshBaker mb = (MB3_MeshBaker)mom; + MB3_MeshCombinerSingle mbs = (MB3_MeshCombinerSingle)mb.meshCombiner; + MB3_MeshCombinerSingle.BuildPrefabHierarchy(mbs, instanceRootGO, mbs.GetMesh()); + } + else if (mom is MB3_MultiMeshBaker) + { + MB3_MultiMeshBaker mmb = (MB3_MultiMeshBaker)mom; + MB3_MultiMeshCombiner mbs = (MB3_MultiMeshCombiner)mmb.meshCombiner; + for (int i = 0; i < mbs.meshCombiners.Count; i++) + { + MB3_MeshCombinerSingle.BuildPrefabHierarchy(mbs.meshCombiners[i].combinedMesh, instanceRootGO, mbs.meshCombiners[i].combinedMesh.GetMesh(), true); + } + } + else + { + Debug.LogError("Argument was not a MB3_MeshBaker or an MB3_MultiMeshBaker."); + } + */ + + if (mom.meshCombiner.settings.renderType == MB_RenderType.skinnedMeshRenderer) + { + _MoveBonesToCombinedMeshPrefabAndDeleteRenderers(instanceRootGO.transform, tempPrefabInstanceRoots, objsToCombine); + } + + string prefabPth = AssetDatabase.GetAssetPath(prefabRoot); + MBVersionEditor.ReplacePrefab(instanceRootGO, prefabPth, MB_ReplacePrefabOption.connectToPrefab); + mom.resultPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPth); + if (!leaveInstanceInSceneAfterBake) + { + Editor.DestroyImmediate(instanceRootGO); + } + + return instanceRootGO; + } + + public static void UnwrapUV2(Mesh mesh, float hardAngle, float packingMargin) + { + UnwrapParam up = new UnwrapParam(); + UnwrapParam.SetDefaults(out up); + up.hardAngle = hardAngle; + up.packMargin = packingMargin; + Unwrapping.GenerateSecondaryUVSet(mesh, up); + } + + public static bool _OkToCreateDummyTextureBakeResult(MB3_MeshBakerCommon mom) + { + List<GameObject> objsToMesh = mom.GetObjectsToCombine(); + if (objsToMesh.Count == 0) + return false; + return true; + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MeshBakerEditorFunctions.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MeshBakerEditorFunctions.cs.meta new file mode 100644 index 00000000..0b9509c6 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_MeshBakerEditorFunctions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2864fc95e81c81b428d7fe94f8e705b6 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_TextureCombinerEditorFunctions.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_TextureCombinerEditorFunctions.cs new file mode 100644 index 00000000..b023efcd --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_TextureCombinerEditorFunctions.cs @@ -0,0 +1,1208 @@ +using UnityEngine; +using UnityEditor; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace DigitalOpus.MB.Core +{ + + public class MB3_EditorMethods : MB2_EditorMethodsInterface + { + + enum saveTextureFormat + { + png, + tga, + } + + private saveTextureFormat SAVE_FORMAT = saveTextureFormat.png; + + private List<Texture2D> _texturesWithReadWriteFlagSet = new List<Texture2D>(); + + private Dictionary<Texture2D, TextureFormatInfo_AbstractDefaultPlatform> _textureFormatMap_DefaultAbstract = new Dictionary<Texture2D, TextureFormatInfo_AbstractDefaultPlatform>(); + private Dictionary<Texture2D, TextureFormatInfo_PlatformOverride> _textureFormatMap_PlatformOverride = new Dictionary<Texture2D, TextureFormatInfo_PlatformOverride>(); + + public MobileTextureSubtarget AndroidBuildTexCompressionSubtarget; +#if UNITY_TIZEN + //public MobileTextureSubtarget TizenBuildTexCompressionSubtarget; +#endif + public void Clear() + { + _texturesWithReadWriteFlagSet.Clear(); + _textureFormatMap_DefaultAbstract.Clear(); + _textureFormatMap_PlatformOverride.Clear(); + } + + public void OnPreTextureBake() + { + AndroidBuildTexCompressionSubtarget = MobileTextureSubtarget.Generic; + + // the texture override in build settings for some platforms causes poor quality + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android && + EditorUserBuildSettings.androidBuildSubtarget != MobileTextureSubtarget.Generic) + { + AndroidBuildTexCompressionSubtarget = EditorUserBuildSettings.androidBuildSubtarget; //remember so we can restore later + EditorUserBuildSettings.androidBuildSubtarget = MobileTextureSubtarget.Generic; + } +#if UNITY_TIZEN + TizenBuildTexCompressionSubtarget = MobileTextureSubtarget.Generic; + if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Tizen && + EditorUserBuildSettings.tizenBuildSubtarget != MobileTextureSubtarget.Generic) + { + TizenBuildTexCompressionSubtarget = EditorUserBuildSettings.tizenBuildSubtarget; //remember so we can restore later + EditorUserBuildSettings.tizenBuildSubtarget = MobileTextureSubtarget.Generic; + } +#endif + } + + public void OnPostTextureBake() + { + if (AndroidBuildTexCompressionSubtarget != MobileTextureSubtarget.Generic) + { + EditorUserBuildSettings.androidBuildSubtarget = AndroidBuildTexCompressionSubtarget; + AndroidBuildTexCompressionSubtarget = MobileTextureSubtarget.Generic; + } +#if UNITY_TIZEN + if (TizenBuildTexCompressionSubtarget != MobileTextureSubtarget.Generic) + { + EditorUserBuildSettings.tizenBuildSubtarget = TizenBuildTexCompressionSubtarget; + TizenBuildTexCompressionSubtarget = MobileTextureSubtarget.Generic; + } +#endif + } + +#if UNITY_5_5_OR_NEWER + class TextureFormatInfo_AbstractDefaultPlatform + { + public TextureImporterCompression abstractDefaultTextureCompression; + public bool abstractDefaultDoCrunchCompression; + public TextureImporterFormat abstractDefaultPlatformFormat; + public int platformCompressionQuality; + public String platform; + public bool isNormalMap; + public bool doPlatformOverride; + + public TextureFormatInfo_AbstractDefaultPlatform(TextureImporterCompression textureCompression, bool doCrunch, string platformString, TextureImporterFormat abstractDefaultPlatformFormat, int platformCompressionQuality, bool isNormMap, bool overridden) + { + this.abstractDefaultTextureCompression = textureCompression; + abstractDefaultDoCrunchCompression = doCrunch; + platform = platformString; + this.abstractDefaultPlatformFormat = abstractDefaultPlatformFormat; + this.platformCompressionQuality = platformCompressionQuality; + this.isNormalMap = isNormMap; + doPlatformOverride = overridden; +#if UNITY_2018_3_OR_NEWER + Debug.Assert(abstractDefaultPlatformFormat == TextureImporterFormat.Automatic || + abstractDefaultPlatformFormat == TextureImporterFormat.Alpha8 || + abstractDefaultPlatformFormat == TextureImporterFormat.RGBA32 || + abstractDefaultPlatformFormat == TextureImporterFormat.RGB24 || + abstractDefaultPlatformFormat == TextureImporterFormat.RGB16 || + abstractDefaultPlatformFormat == TextureImporterFormat.R16 || + abstractDefaultPlatformFormat == TextureImporterFormat.R8, "Platform format should be an abstract format, Auto, Alpha8, RGBA32, RGB24, RGB16, R16, R8"); +#endif + } + } + + class TextureFormatInfo_PlatformOverride + { + public TextureImporterFormat format; + public String platform; + public bool isNormalMap; + public bool doPlatformOverride; + + public TextureFormatInfo_PlatformOverride(string platformString, TextureImporterFormat platformFormat, bool isNormMap, bool overridden) + { + platform = platformString; + format = platformFormat; + this.isNormalMap = isNormMap; + doPlatformOverride = overridden; + } + } + + + + public bool IsNormalMap(Texture2D tx) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is TextureImporter) + { + if (((TextureImporter)ai).textureType == TextureImporterType.NormalMap) return true; + } + return false; + } + + public void ConvertTextureFormat_DefaultPlatform(Texture2D tx, TextureFormat targetFormat, bool isNormalMap) + { + //pixel values don't copy correctly from one texture to another when isNormal is set so unset it. + bool isFormatMapping; + TextureImporterFormat importerFormat = Map_TextureFormat_2_TextureImporterFormat(targetFormat, out isFormatMapping); + if (!isFormatMapping) + { + importerFormat = TextureImporterFormat.RGBA32; + } + + TextureFormatInfo_AbstractDefaultPlatform toFormat = new TextureFormatInfo_AbstractDefaultPlatform(TextureImporterCompression.Uncompressed, false, MBVersionEditor.GetPlatformString(), importerFormat, 0, isNormalMap, false); + _SetTextureFormat_DefaultPlatform(tx, toFormat, true, false); + } + + public void ConvertTextureFormat_PlatformOverride(Texture2D tx, TextureFormat targetFormat, bool isNormalMap) + { + //pixel values don't copy correctly from one texture to another when isNormal is set so unset it. + bool isFormatMapping; + TextureImporterFormat importerFormat = Map_TextureFormat_2_TextureImporterFormat(targetFormat, out isFormatMapping); + if (!isFormatMapping) + { + importerFormat = TextureImporterFormat.RGBA32; + } + + TextureFormatInfo_PlatformOverride toFormat = new TextureFormatInfo_PlatformOverride(MBVersionEditor.GetPlatformString(), importerFormat, isNormalMap, true); + _SetTextureFormat_PlatformOverride(tx, toFormat, true, false); + } + + private void _SetTextureFormat_DefaultPlatform(Texture2D tx, TextureFormatInfo_AbstractDefaultPlatform toThisFormat, bool addToList, bool setNormalMap) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is UnityEditor.TextureImporter) + { + TextureImporter textureImporter = (TextureImporter)ai; + bool doImport = false; + + bool is2017 = Application.unityVersion.StartsWith("20"); + if (is2017) + { + doImport = _Set_DefaultPlatform_TextureFormatAndEnableDisablePlatformOverride_2017(tx, toThisFormat, addToList, setNormalMap, textureImporter); + } + else + { + doImport = _SetTextureFormatUnity5(tx, toThisFormat, addToList, setNormalMap, textureImporter); + } + if (doImport) AssetDatabase.ImportAsset(AssetDatabase.GetAssetOrScenePath(tx), ImportAssetOptions.ForceUpdate); + } + } + + private void _SetTextureFormat_PlatformOverride(Texture2D tx, TextureFormatInfo_PlatformOverride toThisFormat, bool addToList, bool setNormalMap) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is UnityEditor.TextureImporter) + { + TextureImporter textureImporter = (TextureImporter)ai; + bool doImport = _Set_PlatformOverride_2017(tx, toThisFormat, addToList, setNormalMap, textureImporter); + if (doImport) AssetDatabase.ImportAsset(AssetDatabase.GetAssetOrScenePath(tx), ImportAssetOptions.ForceUpdate); + } + } + + private bool _ChangeNormalMapTypeIfNecessary(TextureImporter textureImporter, bool setNormalMap) + { + bool doImport = false; + if (textureImporter.textureType == TextureImporterType.NormalMap && !setNormalMap) + { + textureImporter.textureType = TextureImporterType.Default; + doImport = true; + } + + if (textureImporter.textureType != TextureImporterType.NormalMap && setNormalMap) + { + textureImporter.textureType = TextureImporterType.NormalMap; + doImport = true; + } + return doImport; + } + + private void RememberTextureFormatChange(Texture2D tx, TextureFormatInfo_AbstractDefaultPlatform tfi) + { + Debug.Assert(!_textureFormatMap_DefaultAbstract.ContainsKey(tx),"We have already converted the format for this texture " + tx + " we should only do this once."); + Debug.Assert(!_textureFormatMap_PlatformOverride.ContainsKey(tx), "We have added a TextureImporter platform override for this texture " + tx + " we should not also be changing the default format."); + _textureFormatMap_DefaultAbstract.Add(tx, tfi); + } + + private void RememberTextureFormatChange(Texture2D tx, TextureFormatInfo_PlatformOverride tfi) + { + Debug.Assert(!_textureFormatMap_PlatformOverride.ContainsKey(tx), "We have already added a platform override for texture " + tx + " we should only do this once."); + Debug.Assert(!_textureFormatMap_DefaultAbstract.ContainsKey(tx), "We have added a converted the format for default platform for texture " + tx + " we should not also be changing the platform override."); + _textureFormatMap_PlatformOverride.Add(tx, tfi); + } + + + private bool _Set_PlatformOverride_2017(Texture2D tx, TextureFormatInfo_PlatformOverride toThisFormat, bool rememberRestoreSettings, bool setNormalMap, TextureImporter textureImporter) + { + bool is2017 = Application.unityVersion.StartsWith("20"); + if (!is2017) + { + Debug.LogError("Wrong texture format converter. 2017 Should not be called for Unity Version " + Application.unityVersion); + return false; + } + + // Reimport takes a long time so we only want to reimport if necessary. + bool doImport = false; + + // Record the old format so we can restore after changing format. + string restoreBuildPlatform = GetPlatformString(); + + // Get the restore settings + // First check if there is an override for this platform. + TextureImporterPlatformSettings platformOverriddenTips = textureImporter.GetPlatformTextureSettings(restoreBuildPlatform); + TextureFormatInfo_PlatformOverride restoreTfi; + { + restoreTfi = new TextureFormatInfo_PlatformOverride(restoreBuildPlatform, platformOverriddenTips.format, textureImporter.textureType == TextureImporterType.NormalMap, platformOverriddenTips.overridden); + } + + string targetBuildPlatform = toThisFormat.platform; + + // Check if anything needs changing and if so remember that we need to reimport; + { + if (targetBuildPlatform != null) + { + if (platformOverriddenTips.overridden != toThisFormat.doPlatformOverride) + { + // Disable/enable the platform override + platformOverriddenTips.overridden = toThisFormat.doPlatformOverride; + textureImporter.SetPlatformTextureSettings(platformOverriddenTips); + doImport = true; + } + } + + if (_ChangeNormalMapTypeIfNecessary(textureImporter, setNormalMap)) + { + doImport = true; + } + + if (platformOverriddenTips.format != toThisFormat.format) + { + platformOverriddenTips.format = toThisFormat.format; + textureImporter.SetPlatformTextureSettings(platformOverriddenTips); + doImport = true; + } + } + + if (doImport) + { + string s; + if (rememberRestoreSettings) + { + s = "Setting texture platform override for "; + } + else + { + s = "Restoring texture platform override for "; + } + s += String.Format("{0} FROM: isNormal{1} format={2} hadOverride={3} TO: isNormal={4} format={5} hadOverride={6}", + tx, restoreTfi.isNormalMap, restoreTfi.format, restoreTfi.doPlatformOverride, + setNormalMap, toThisFormat.format, toThisFormat.doPlatformOverride); + + Debug.Log(s); + if (doImport && rememberRestoreSettings && !_textureFormatMap_PlatformOverride.ContainsKey(tx)) + { + RememberTextureFormatChange(tx, restoreTfi); + } + } + + return doImport; + } + + /// <summary> + /// Useful for from <--> to truecolor <--> compressed. + /// Not useful for setting a texture to a specific compression format (eg DXT5) + /// + /// Importer has "Default" PlatformImportSettings which can be overridden by "platform overrides". + /// This enables/disables the override and sets the "Defalut" to/from something compressed. + /// </summary> + private bool _Set_DefaultPlatform_TextureFormatAndEnableDisablePlatformOverride_2017(Texture2D tx, TextureFormatInfo_AbstractDefaultPlatform toThisFormat, bool rememberRestoreSettings, bool setNormalMap, TextureImporter textureImporter) + { + /* + * HOW THE TEXTURE IMPORTER WORKS. + * Importer has "Default" PlatformImportSettings which can be overridden on a platform by platform basis. + * Default: + * Format can be + * Automatic => uses TextureImporter.GetAutomaticFormat + * RGB, ARGB, etc... These are abstract formats (list of channels, bitdepth), not specific algorithms like PVRT, ETC. + * Compression is Normal *DEFAULT, None, High, Low. These are abstract. Will get translated to specific algorithm depending on platform and Format. + * Overrides per platform (iOS, Android ...) + * Format is a concrete algorithm like ETC, ASTC, BVRT + * Compressor Quality (Fastest, Normal, Best) These are abstract, not sure how this affects format + * + * Crunch compression: See blog post: https://blogs.unity3d.com/2017/11/15/updated-crunch-texture-compression-library/. Is lossy, Is for distribution + * (textures are decompressed before being loaded into GPU). Only for DXT, RGB Crunched ETC, RGBA Crunched ETC2 format. If you enable the + * “Use Crunch Compression” option in the Default tab, all the textures on Android platform will be compressed with ETC Crunch by default. + * + * WHAT MESH BAKER NEEDS. + * Needs to be able to read the pixels. Also textures should be in "true-color" RGB or ARGB format for best fidelity. + * Turn off platform override if it is enabled + * Set default to "uncompressed", "RGB or ARGB" + * + */ + bool is2017 = Application.unityVersion.StartsWith("20"); + if (!is2017) + { + Debug.LogError("Wrong texture format converter. 2017 Should not be called for Unity Version " + Application.unityVersion); + return false; + } + + // Reimport takes a long time so we only want to reimport if necessary. + bool doImport = false; + + // Record the old format so we can restore after changing format. + string restoreBuildPlatform = GetPlatformString(); + + // Get the restore settings + // First check if there is an override for this platform. + + bool currentHasOverride; + { + TextureImporterPlatformSettings platformOverriddenTips = textureImporter.GetPlatformTextureSettings(restoreBuildPlatform); + currentHasOverride = platformOverriddenTips.overridden; + } + + // Get the default settings. + TextureImporterFormat abstractDefaultCurrentFormat; + TextureFormatInfo_AbstractDefaultPlatform restoreTfi; + { + TextureImporterPlatformSettings defaultTips = textureImporter.GetDefaultPlatformTextureSettings(); + abstractDefaultCurrentFormat = defaultTips.format; + restoreTfi = new TextureFormatInfo_AbstractDefaultPlatform(defaultTips.textureCompression, + defaultTips.crunchedCompression, + restoreBuildPlatform, + defaultTips.format, + defaultTips.compressionQuality, + textureImporter.textureType == TextureImporterType.NormalMap, + currentHasOverride); + } + string targetBuildPlatform = toThisFormat.platform; + + // Check if anything needs changing and if so remember that we need to reimport; + { + if (targetBuildPlatform != null) + { + if (currentHasOverride != toThisFormat.doPlatformOverride) + { + // Disable/enable the platform override + TextureImporterPlatformSettings platformOverriddenTips = textureImporter.GetPlatformTextureSettings(restoreBuildPlatform); + platformOverriddenTips.overridden = toThisFormat.doPlatformOverride; + textureImporter.SetPlatformTextureSettings(platformOverriddenTips); + doImport = true; + } + } + + if (textureImporter.textureCompression != toThisFormat.abstractDefaultTextureCompression) + { + textureImporter.textureCompression = toThisFormat.abstractDefaultTextureCompression; + doImport = true; + } + + if (textureImporter.crunchedCompression != toThisFormat.abstractDefaultDoCrunchCompression) + { + textureImporter.crunchedCompression = toThisFormat.abstractDefaultDoCrunchCompression; + doImport = true; + } + + if (_ChangeNormalMapTypeIfNecessary(textureImporter, setNormalMap)) + { + doImport = true; + } + + if (abstractDefaultCurrentFormat != toThisFormat.abstractDefaultPlatformFormat) + { + TextureImporterPlatformSettings defTips = textureImporter.GetDefaultPlatformTextureSettings(); + defTips.format = toThisFormat.abstractDefaultPlatformFormat; + textureImporter.SetPlatformTextureSettings(defTips); + doImport = true; + } + } + + if (doImport) + { + string s; + if (rememberRestoreSettings) + { + s = "Setting DefaultPlatform texture compression for "; + } + else + { + s = "Restoring DefaultPlatform texture compression for "; + } + s += String.Format("{0} FROM: compression={1} isNormal{2} format={3} hadOverride={4} TO: compression={5} isNormal={6} format={7} hadOverride={8}", + tx, restoreTfi.abstractDefaultTextureCompression, restoreTfi.isNormalMap, restoreTfi.abstractDefaultPlatformFormat, restoreTfi.doPlatformOverride, + toThisFormat.abstractDefaultTextureCompression, setNormalMap, toThisFormat.abstractDefaultPlatformFormat, toThisFormat.doPlatformOverride); + + Debug.Log(s); + if (doImport && rememberRestoreSettings && !_textureFormatMap_DefaultAbstract.ContainsKey(tx)) + { + RememberTextureFormatChange(tx, restoreTfi); + } + } + + return doImport; + } + + private bool _SetTextureFormatUnity5(Texture2D tx, TextureFormatInfo_AbstractDefaultPlatform toThisFormat, bool addToList, bool setNormalMap, TextureImporter textureImporter) + { + bool doImport = false; + + TextureFormatInfo_AbstractDefaultPlatform restoreTfi = new TextureFormatInfo_AbstractDefaultPlatform(textureImporter.textureCompression, + false, + toThisFormat.platform, + TextureImporterFormat.RGBA32, + toThisFormat.platformCompressionQuality, + textureImporter.textureType == TextureImporterType.NormalMap, + false); + + string platform = toThisFormat.platform; + if (platform != null) + { + TextureImporterPlatformSettings tips = textureImporter.GetPlatformTextureSettings(platform); + if (tips.overridden) + { + restoreTfi.abstractDefaultPlatformFormat = tips.format; + restoreTfi.platformCompressionQuality = tips.compressionQuality; + TextureImporterPlatformSettings tipsOverridden = new TextureImporterPlatformSettings(); + tips.CopyTo(tipsOverridden); + tipsOverridden.compressionQuality = toThisFormat.platformCompressionQuality; + tipsOverridden.format = toThisFormat.abstractDefaultPlatformFormat; + textureImporter.SetPlatformTextureSettings(tipsOverridden); + doImport = true; + } + } + + if (textureImporter.textureCompression != toThisFormat.abstractDefaultTextureCompression) + { + textureImporter.textureCompression = toThisFormat.abstractDefaultTextureCompression; + doImport = true; + } + + if (doImport) + { + string s; + if (addToList) + { + s = "Setting texture compression for "; + } + else + { + s = "Restoring texture compression for "; + } + s += String.Format("{0} FROM: compression={1} isNormal{2} TO: compression={3} isNormal={4} ", tx, restoreTfi.abstractDefaultTextureCompression, restoreTfi.isNormalMap, toThisFormat.abstractDefaultTextureCompression, setNormalMap); + if (toThisFormat.platform != null) + { + s += String.Format(" setting platform override format for platform {0} to {1} compressionQuality {2}", toThisFormat.platform, toThisFormat.abstractDefaultPlatformFormat, toThisFormat.platformCompressionQuality); + } + Debug.Log(s); + } + if (doImport && addToList && !_textureFormatMap_DefaultAbstract.ContainsKey(tx)) + { + RememberTextureFormatChange(tx, restoreTfi); + } + return doImport; + } + + public void SetNormalMap(Texture2D tx) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is TextureImporter) + { + TextureImporter textureImporter = (TextureImporter)ai; + if (textureImporter.textureType != TextureImporterType.NormalMap) + { + textureImporter.textureType = TextureImporterType.NormalMap; + AssetDatabase.ImportAsset(AssetDatabase.GetAssetOrScenePath(tx)); + } + } + } + + public bool IsCompressed(Texture2D tx) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is TextureImporter) + { + TextureImporter textureImporter = (TextureImporter)ai; + if (textureImporter.textureCompression == TextureImporterCompression.Uncompressed) + { + return true; + } + } + return false; + } +#else + // 5_4 and earlier + class TextureFormatInfo + { + public TextureImporterFormat format; + public bool isNormalMap; + public String platform; + public TextureImporterFormat platformOverrideFormat; + + public TextureFormatInfo(TextureImporterFormat f, string p, TextureImporterFormat pf, bool isNormMap) + { + format = f; + platform = p; + platformOverrideFormat = pf; + isNormalMap = isNormMap; + } + } + + public bool IsNormalMap(Texture2D tx) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is TextureImporter) + { + if (((TextureImporter)ai).normalmap) return true; + } + return false; + } + + public void AddTextureFormat(Texture2D tx, bool isNormalMap) + { + //pixel values don't copy correctly from one texture to another when isNormal is set so unset it. + _SetTextureFormat(tx, + new TextureFormatInfo(TextureImporterFormat.ARGB32, MBVersionEditor.GetPlatformString(), TextureImporterFormat.AutomaticTruecolor, isNormalMap), + true, false); + } + + void _SetTextureFormat(Texture2D tx, TextureFormatInfo tfi, bool addToList, bool setNormalMap) + { + + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is UnityEditor.TextureImporter) + { + string s; + if (addToList) + { + s = "Setting texture format for "; + } + else { + s = "Restoring texture format for "; + } + s += tx + " to " + tfi.format; + if (tfi.platform != null) + { + s += " setting platform override format for " + tfi.platform + " to " + tfi.platformOverrideFormat; + } + Debug.Log(s); + TextureImporter textureImporter = (TextureImporter)ai; + TextureFormatInfo restoreTfi = new TextureFormatInfo(textureImporter.textureFormat, + tfi.platform, + TextureImporterFormat.AutomaticTruecolor, + textureImporter.normalmap); + string platform = tfi.platform; + bool doImport = false; + if (platform != null) + { + int maxSize; + TextureImporterFormat f; + textureImporter.GetPlatformTextureSettings(platform, out maxSize, out f); + restoreTfi.platformOverrideFormat = f; + if (f != 0) + { //f == 0 means no override or platform doesn't exist + textureImporter.SetPlatformTextureSettings(platform, maxSize, tfi.platformOverrideFormat); + doImport = true; + } + } + + if (textureImporter.textureFormat != tfi.format) + { + textureImporter.textureFormat = tfi.format; + doImport = true; + } + if (textureImporter.normalmap && !setNormalMap) + { + textureImporter.normalmap = false; + doImport = true; + } + if (!textureImporter.normalmap && setNormalMap) + { + textureImporter.normalmap = true; + doImport = true; + } + if (addToList && !_textureFormatMap.ContainsKey(tx)) _textureFormatMap.Add(tx, restoreTfi); + if (doImport) AssetDatabase.ImportAsset(AssetDatabase.GetAssetOrScenePath(tx), ImportAssetOptions.ForceUpdate); + } + } + + public void SetNormalMap(Texture2D tx) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is TextureImporter) + { + TextureImporter textureImporter = (TextureImporter)ai; + if (!textureImporter.normalmap) + { + textureImporter.normalmap = true; + AssetDatabase.ImportAsset(AssetDatabase.GetAssetOrScenePath(tx)); + } + } + } + + public bool IsCompressed(Texture2D tx) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is TextureImporter) + { + TextureImporter textureImporter = (TextureImporter)ai; + TextureImporterFormat tf = textureImporter.textureFormat; + if (tf != TextureImporterFormat.ARGB32) + { + return true; + } + } + return false; + } +#endif + + + public void RestoreReadFlagsAndFormats(ProgressUpdateDelegate progressInfo) + { + { + for (int i = 0; i < _texturesWithReadWriteFlagSet.Count; i++) + { + if (progressInfo != null) progressInfo("Restoring read flag for " + _texturesWithReadWriteFlagSet[i], .9f); + SetReadWriteFlag(_texturesWithReadWriteFlagSet[i], false, false); + } + _texturesWithReadWriteFlagSet.Clear(); + } + + { + foreach (Texture2D tex in _textureFormatMap_DefaultAbstract.Keys) + { + if (progressInfo != null) progressInfo("Restoring format for " + tex, .9f); + _SetTextureFormat_DefaultPlatform(tex, _textureFormatMap_DefaultAbstract[tex], false, _textureFormatMap_DefaultAbstract[tex].isNormalMap); + } + _textureFormatMap_DefaultAbstract.Clear(); + } + + { + foreach (Texture2D tex in _textureFormatMap_PlatformOverride.Keys) + { + if (progressInfo != null) progressInfo("Restoring format for " + tex, .9f); + _SetTextureFormat_PlatformOverride(tex, _textureFormatMap_PlatformOverride[tex], false, _textureFormatMap_PlatformOverride[tex].isNormalMap); + } + _textureFormatMap_PlatformOverride.Clear(); + } + + } + + + public void SetReadWriteFlag(Texture2D tx, bool isReadable, bool addToList) + { + AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)); + if (ai != null && ai is TextureImporter) + { + TextureImporter textureImporter = (TextureImporter)ai; + if (textureImporter.isReadable != isReadable) + { + if (addToList) _texturesWithReadWriteFlagSet.Add(tx); + textureImporter.isReadable = isReadable; + // Debug.LogWarning("Setting read flag for Texture asset " + AssetDatabase.GetAssetPath(tx) + " to " + isReadable); + AssetDatabase.ImportAsset(AssetDatabase.GetAssetOrScenePath(tx)); + } + } + } + + private bool ConstructFilename(Material resMat, string texPropertyName, string atlasType, string formatString, int atlasNum, + out string pth, + out string relativePath) + { + string prefabPth = AssetDatabase.GetAssetPath(resMat); + if (prefabPth == null || prefabPth.Length == 0) + { + Debug.LogError("Could save atlas. Could not find result material in AssetDatabase."); + pth = ""; + relativePath = ""; + return false; + } + string baseName = Path.GetFileNameWithoutExtension(prefabPth); + string folderPath = prefabPth.Substring(0, prefabPth.Length - baseName.Length - 4); + string fullFolderPath = Application.dataPath + folderPath.Substring("Assets".Length, folderPath.Length - "Assets".Length); + pth = fullFolderPath + baseName + "-" + texPropertyName + "-" + atlasType + "-" + atlasNum; + relativePath = folderPath + baseName + "-" + texPropertyName + "-" + atlasType + "-" + formatString + atlasNum; + return true; + } + + public void SaveTextureArrayToAssetDatabase(Texture2DArray atlas, TextureFormat format, string texPropertyName, int atlasNum, Material resMat) + { + if (atlas == null) + { + if (resMat.HasProperty(texPropertyName)) + { + resMat.SetTexture(texPropertyName, null); + } + } + else + { + string pth, relativePath; + if (ConstructFilename(resMat, texPropertyName, "texarray-", format.ToString(), atlasNum, out pth, out relativePath)) + { + string assetFilename = relativePath + ".asset"; + Texture2DArray existingAsset = AssetDatabase.LoadAssetAtPath<Texture2DArray>(assetFilename); + if (!existingAsset) + { + AssetDatabase.CreateAsset(atlas, assetFilename); + } + else + { + EditorUtility.CopySerialized(atlas, existingAsset); + } + + Debug.Log(String.Format("Wrote Texture2DArray for {0} to file:{1}", texPropertyName, pth)); + if (resMat.HasProperty(texPropertyName)) + { + Texture2DArray txx = (Texture2DArray)(AssetDatabase.LoadAssetAtPath(assetFilename, typeof(Texture2DArray))); + resMat.SetTexture(texPropertyName, txx); + } + } + } + } + + /** + pass in System.IO.File.WriteAllBytes for parameter fileSaveFunction. This is necessary because on Web Player file saving + functions only exist for Editor classes + */ + public void SaveAtlasToAssetDatabase(Texture2D atlas, ShaderTextureProperty texPropertyName, int atlasNum, bool doAnySrcMatsHaveProperty, Material resMat) + { + if (atlas == null) + { + if (doAnySrcMatsHaveProperty) + { + SetMaterialTextureProperty(resMat, texPropertyName, null); + } + } + else + { + string pth, relativePath; + if (ConstructFilename(resMat, texPropertyName.name, "atlas", "", atlasNum, out pth, out relativePath)) + { + //need to create a copy because sometimes the packed atlases are not in ARGB32 format + Texture2D newTex = MB_Utility.createTextureCopy(atlas); + int size = Mathf.Max(newTex.height, newTex.width); + if (SAVE_FORMAT == saveTextureFormat.png) + { + pth += ".png"; + relativePath += ".png"; + byte[] bytes = newTex.EncodeToPNG(); + System.IO.File.WriteAllBytes(pth, bytes); + } + else + { + pth += ".tga"; + relativePath += ".tga"; + if (File.Exists(pth)) + { + File.Delete(pth); + } + + //Create the file. + FileStream fs = File.Create(pth); + MB_TGAWriter.Write(newTex.GetPixels(), newTex.width, newTex.height, fs); + } + Editor.DestroyImmediate(newTex); + AssetDatabase.Refresh(); + Debug.Log(String.Format("Wrote atlas for {0} to file:{1}", texPropertyName.name, pth)); + Texture2D txx = (Texture2D)(AssetDatabase.LoadAssetAtPath(relativePath, typeof(Texture2D))); + SetTextureSize(txx, size); + if (doAnySrcMatsHaveProperty) + { + SetMaterialTextureProperty(resMat, texPropertyName, relativePath); + } + } + } + } + + public void SetMaterialTextureProperty(Material target, ShaderTextureProperty texPropName, string texturePath) + { + // if (LOG_LEVEL >= MB2_LogLevel.debug) MB2_Log.Log(MB2_LogLevel.debug,"Assigning atlas " + texturePath + " to result material " + target + " for property " + texPropName,LOG_LEVEL); + if (texPropName.isNormalMap) + { + SetNormalMap((Texture2D)(AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)))); + } + if (target.HasProperty(texPropName.name)) + { + target.SetTexture(texPropName.name, (Texture2D)(AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)))); + } + } + + public void SetTextureSize(Texture2D tx, int size) + { + TextureImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetOrScenePath(tx)) as TextureImporter; + if (ai == null) return; + + int maxSize = 32; + if (size > 32) maxSize = 64; + if (size > 64) maxSize = 128; + if (size > 128) maxSize = 256; + if (size > 256) maxSize = 512; + if (size > 512) maxSize = 1024; + if (size > 1024) maxSize = 2048; + if (size > 2048) maxSize = 4096; + + bool isSettingsChanged = false; + if (ai.maxTextureSize != maxSize) + { + ai.maxTextureSize = maxSize; + isSettingsChanged = true; + } + +#if UNITY_5_5_OR_NEWER + string[] platforms = { "Standalone", "Web", "iPhone", "Android", "WebGL", "Windows Store Apps", "PSP2", "PS4", "XboxOne", "Nintendo 3DS", "tvOS" }; + foreach (string platform in platforms) + { + TextureImporterPlatformSettings settings = ai.GetPlatformTextureSettings(platform); + if (settings != null && settings.overridden && settings.maxTextureSize != maxSize) + { + settings.maxTextureSize = maxSize; + ai.SetPlatformTextureSettings(settings); + isSettingsChanged = true; + } + } + + // reimport if anything changed + if (isSettingsChanged) + { + ai.SaveAndReimport(); + } +#else + if (ai.maxTextureSize != maxSize) + { + ai.maxTextureSize = maxSize; + AssetDatabase.ImportAsset(AssetDatabase.GetAssetOrScenePath(tx), ImportAssetOptions.ForceUpdate); + } +#endif + } + + public void CommitChangesToAssets() + { + AssetDatabase.Refresh(); + } + + public string GetPlatformString() + { + return MBVersionEditor.GetPlatformString(); + } + + public void CheckBuildSettings(long estimatedArea) + { + if (Math.Sqrt(estimatedArea) > 1000f) + { + if (EditorUserBuildSettings.selectedBuildTargetGroup != BuildTargetGroup.Standalone) + { + Debug.LogWarning("If the current selected build target is not standalone then the generated atlases may be capped at size 1024. If build target is Standalone then atlases of 4096 can be built"); + } + } + } + + public bool CheckPrefabTypes(MB_ObjsToCombineTypes objToCombineType, List<GameObject> objsToMesh) + { + for (int i = 0; i < objsToMesh.Count; i++) + { + MB_PrefabType pt = MBVersionEditor.GetPrefabType(objsToMesh[i]); + if (pt == MB_PrefabType.scenePefabInstance || pt == MB_PrefabType.isInstanceAndNotAPartOfAnyPrefab) + { + // these are scene objects + if (objToCombineType == MB_ObjsToCombineTypes.prefabOnly) + { + Debug.LogWarning("The list of objects to combine contains scene objects. You probably want prefabs. If using scene objects ensure position is zero, rotation is zero and scale is one. Translation, Rotation and Scale will be baked into the generated mesh." + objsToMesh[i] + " is a scene object"); + return false; + } + } + else if (objToCombineType == MB_ObjsToCombineTypes.sceneObjOnly) + { + //these are prefabs + if (pt == MB_PrefabType.modelPrefabAsset || pt == MB_PrefabType.prefabAsset) + { + Debug.LogError("The list of objects to combine contains prefab assets. You need scene instances." + objsToMesh[i] + "(position " + i + ") is a project prefab object. Create a scene instance of this prefab and use that in the list of objects to combine."); + return false; + } + } + } + return true; + } + + public bool ValidateSkinnedMeshes(List<GameObject> objs) + { + for (int i = 0; i < objs.Count; i++) + { + Renderer r = MB_Utility.GetRenderer(objs[i]); + if (r is SkinnedMeshRenderer) + { + Mesh m = MB_Utility.GetMesh(objs[i]); + if (m != null) + { + Matrix4x4[] bindposes = m.bindposes; + if (bindposes.Length > 0) + { + // There should be a 1-to-1 match between bindposes and bones; + Transform[] bones = ((SkinnedMeshRenderer)r).bones; + if (bones.Length == 0) + { + Debug.LogWarning("SkinnedMesh " + i + " (" + objs[i] + ") in the list of objects to combine has no bones. Check that 'optimize game object' is not checked in the 'Rig' tab of the asset importer. Mesh Baker cannot combine optimized skinned meshes because the bones are not available."); + } + // UnityEngine.Object parentObject = EditorUtility.GetPrefabParent(r.gameObject); + // string path = AssetDatabase.GetAssetPath(parentObject); + // Debug.Log (path); + // AssetImporter ai = AssetImporter.GetAtPath( path ); + // Debug.Log ("bbb " + ai); + // if (ai != null && ai is ModelImporter){ + // Debug.Log ("valing 2"); + // ModelImporter modelImporter = (ModelImporter) ai; + // if(modelImporter.optimizeMesh){ + // Debug.LogError("SkinnedMesh " + i + " (" + objs[i] + ") in the list of objects to combine is optimized. Mesh Baker cannot combine optimized skinned meshes because the bones are not available."); + // } + // } + } else + { + // This was a skinned mesh with no bindposes (bones). This is allowed if there are blendshapes + if (m.blendShapeCount == 0) + { + Debug.LogWarning("SkinnedMesh " + i + " (" + objs[i] + ") in the list of objects to combine has no bindposes or blendshapes. Should this renderer be a MeshRenderer?"); + } + } + } + } + } + return true; + } + + public void Destroy(UnityEngine.Object o) + { + if (Application.isPlaying) + { + if (!IsAnAsset(o))// This is an asset. Don't destroy it. + { + MonoBehaviour.Destroy(o); + } + } + else + { + if (!IsAnAsset(o)) // don't try to destroy assets + { + MonoBehaviour.DestroyImmediate(o, false); + } + } + } + + public void DestroyAsset(UnityEngine.Object o) + { + if (o == null) return; + string path = AssetDatabase.GetAssetPath(o); + if (path != null && path != "") + { + AssetDatabase.DeleteAsset(path); + } else + { + Debug.LogError("DestroyAsset was called on an object that was not an asset: " + o); + } + } + + public static object[] DropZone(string title, int w, int h) + { + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Box(title, GUILayout.Width(w), GUILayout.Height(h)); + Rect dropRect = GUILayoutUtility.GetLastRect(); + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + + EventType eventType = Event.current.type; + bool isAccepted = false; + + if (eventType == EventType.DragUpdated || eventType == EventType.DragPerform) + { + if (dropRect.Contains(Event.current.mousePosition)) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + if (eventType == EventType.DragPerform) + { + DragAndDrop.AcceptDrag(); + isAccepted = true; + //Debug.Log("Consuming drop event in inspector. " + Event.current.mousePosition + " rect" + dropRect); + Event.current.Use(); + } + } + } + + return isAccepted ? DragAndDrop.objectReferences : null; + } + + public static void AddDroppedObjects(object[] objs, MB3_MeshBakerRoot momm) + { + if (objs != null) + { + HashSet<Renderer> renderersToAdd = new HashSet<Renderer>(); + for (int i = 0; i < objs.Length; i++) + { + object obj = objs[i]; + if (obj is GameObject) + { + Renderer[] rs = ((GameObject)obj).GetComponentsInChildren<Renderer>(true); + for (int j = 0; j < rs.Length; j++) + { + if (rs[j] is MeshRenderer || rs[j] is SkinnedMeshRenderer) + { + renderersToAdd.Add(rs[j]); + } + } + } + } + + int numAdded = 0; + List<GameObject> objsToCombine = momm.GetObjectsToCombine(); + bool failedToAddAssets = false; + foreach (Renderer r in renderersToAdd) + { + if (!objsToCombine.Contains(r.gameObject)) + { + MB_PrefabType prefabType = MBVersionEditor.GetPrefabType(r.gameObject); + if (prefabType == MB_PrefabType.modelPrefabAsset || prefabType == MB_PrefabType.prefabAsset) + { + failedToAddAssets = true; + } + else + { + objsToCombine.Add(r.gameObject); + numAdded++; + } + } + } + + if (failedToAddAssets) + { + Debug.LogError("Did not add some object(s) because they are not scene objects"); + } + Debug.Log("Added " + numAdded + " renderers"); + } + } + + public bool IsAnAsset(UnityEngine.Object o) + { + string path = AssetDatabase.GetAssetPath(o); + bool isAsset = !(path == null || path.Length == 0); + return isAsset; + } + + public Texture2D CreateTemporaryAssetCopy(ShaderTextureProperty shaderProp, Texture2D sliceTex, int w, int h, TextureFormat format, MB2_LogLevel logLevel) + { + bool foundMatch; + UnityEditor.TextureImporterFormat targetImporterFormat = Map_TextureFormat_2_TextureImporterFormat(format, out foundMatch); + if (!foundMatch) + { + Debug.LogError("Could not find target importer format matching " + format); + return null; + } + + // Can't do a pixel copy with normal maps because unity has swizzled the color channels. Turn of the TextureType:Normal first. + if (shaderProp.isNormalMap) + { + if (!_textureFormatMap_DefaultAbstract.ContainsKey(sliceTex) + && !_textureFormatMap_PlatformOverride.ContainsKey(sliceTex)) + { + ConvertTextureFormat_DefaultPlatform(sliceTex, TextureFormat.RGBA32, isNormalMap:false); + } + } + + string workingFolder = MB_EditorUtil.GetShortPathToWorkingDirectoryAndEnsureItExists(); + string tryPth = workingFolder + "/" + sliceTex.name + "_TEMP.png"; + string shortPath = AssetDatabase.GenerateUniqueAssetPath(tryPth); + string fullPath = MB_Utility.ConvertAssetsRelativePathToFullSystemPath(shortPath); + // Duplicate the source texture and save it as a truecolor temporary asset + { + Texture2D newTex1 = new Texture2D(sliceTex.width, sliceTex.height, TextureFormat.ARGB32, true); + newTex1.SetPixels(sliceTex.GetPixels()); + newTex1.Apply(); + + // Resize it. + Texture2D newTex = MB_Utility.resampleTexture(newTex1, w, h); + System.IO.File.WriteAllBytes(fullPath, newTex.EncodeToPNG()); + GameObject.DestroyImmediate(newTex); + GameObject.DestroyImmediate(newTex1); + AssetDatabase.Refresh(); + } + + Texture2D temporaryTex = AssetDatabase.LoadAssetAtPath<Texture2D>(shortPath); + TextureImporter ai = (TextureImporter)AssetImporter.GetAtPath(shortPath); + ai.isReadable = true; + string platformString = GetPlatformString(); + TextureImporterPlatformSettings settings = ai.GetPlatformTextureSettings(platformString); + + // Note that it is not enough to set the default platform settings. Default only uses abstract formats, true formats are auto generated settings. + // Need to use the plaftorm override to set the true setting. + if (settings.format != targetImporterFormat) + { + settings.overridden = true; + settings.format = targetImporterFormat; + ai.SetPlatformTextureSettings(settings); + } + + if (logLevel >= MB2_LogLevel.debug) Debug.LogFormat("Creating temporary texuture asset to resize texture: {0} w:{1} h:{2} format:{3} TO w:{4} h:{5} format:{6}", + sliceTex, sliceTex.width, sliceTex.height, sliceTex.format, w, h, format); + ai.SaveAndReimport(); + settings = ai.GetPlatformTextureSettings(platformString); + Debug.Assert(settings.format == targetImporterFormat, "Format of temporary texture after import was " + settings.format + " not targetFormat: " + targetImporterFormat); + return temporaryTex; + } + + public static TextureImporterFormat Map_TextureFormat_2_TextureImporterFormat(TextureFormat texFormat, out bool success) + { + return MBVersionEditor.Map_TextureFormat_2_TextureImporterFormat(texFormat, out success); + } + + public bool TextureImporterFormatExistsForTextureFormat(TextureFormat texFormat) + { + bool success; + Map_TextureFormat_2_TextureImporterFormat(texFormat, out success); + return success; + } + + public bool ConvertTexture2DArray(Texture2DArray inArray, Texture2DArray outArray, TextureFormat outFormat) + { + bool foundFormat; + TextureImporterFormat outImporterFormat = Map_TextureFormat_2_TextureImporterFormat(outFormat, out foundFormat); + if (!foundFormat) + { + Debug.LogError("Could not find a TextureImporterFormat matching format: " + outFormat); + return false; + } + + Texture2D tempTex = new Texture2D(inArray.width, inArray.height, inArray.format, true); + + // Create a temporary texture asset of the correct size. We need this for the TextureImporter + string shortPath, fullPath; + { + shortPath = MB_EditorUtil.GetShortPathToWorkingDirectoryAndEnsureItExists(); + shortPath += inArray.name + "_" + outFormat.ToString() + "_TEMP.png"; + shortPath = AssetDatabase.GenerateUniqueAssetPath(shortPath); + fullPath = MB_Utility.ConvertAssetsRelativePathToFullSystemPath(shortPath); + Debug.Log("Saving temp tex: " + fullPath + " shortPth " + shortPath); + byte[] bytes = tempTex.EncodeToPNG(); + System.IO.File.WriteAllBytes(fullPath, bytes); + AssetDatabase.Refresh(); + } + + // This is horrible, but the only way to convert textures to compressed formats it through the asset importer. We need an asset! + TextureImporter ai = (TextureImporter)AssetImporter.GetAtPath(shortPath); + { + ai.isReadable = true; + TextureImporterPlatformSettings tips = new TextureImporterPlatformSettings(); + tips.format = outImporterFormat; + //tips.textureCompression = TextureImporterCompression.Uncompressed; + ai.SetPlatformTextureSettings(tips); + ai.SaveAndReimport(); + } + + for (int sliceIdx = 0; sliceIdx < inArray.depth; sliceIdx++) + { + Graphics.CopyTexture(inArray, sliceIdx, 0, tempTex, sliceIdx, 0); + byte[] bytes = tempTex.EncodeToPNG(); + System.IO.File.WriteAllBytes(fullPath, bytes); + AssetDatabase.Refresh(); + Texture2D srcTexConverted = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>(shortPath); + int numMips = srcTexConverted.mipmapCount; + for (int mipIdx = 0; mipIdx < numMips; mipIdx++) + { + Graphics.CopyTexture(srcTexConverted, 0, mipIdx, outArray, sliceIdx, mipIdx); + } + } + + MonoBehaviour.DestroyImmediate(tempTex); + AssetDatabase.DeleteAsset(shortPath); + return true; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_TextureCombinerEditorFunctions.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_TextureCombinerEditorFunctions.cs.meta new file mode 100644 index 00000000..93d695c0 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB3_TextureCombinerEditorFunctions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0a4a0d3ada80a12409388f37408aced8 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_BatchPrefabBakerEditorFunctions.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_BatchPrefabBakerEditorFunctions.cs new file mode 100644 index 00000000..1aba216d --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_BatchPrefabBakerEditorFunctions.cs @@ -0,0 +1,1025 @@ +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 class MB_BatchPrefabBakerEditorFunctions + { + + public static int EvalVersionPrefabLimit + { + get { return 5; } + } + + public class UnityTransform + { + public Vector3 p; + public Quaternion q; + public Vector3 s; + public Transform t; + + public UnityTransform(Transform t) + { + this.t = t; + p = t.localPosition; + q = t.localRotation; + s = t.localScale; + } + } + + private enum TargetMeshTreatment + { + createNewMesh, + replaceMesh, + reuseMesh + } + + private class ProcessedMeshInfo + { + public Material[] srcMaterials; + public Material[] targMaterials; + public Mesh targetMesh; + } + + public static void PopulatePrefabRowsFromTextureBaker(MB3_BatchPrefabBaker prefabBaker) + { + MB3_TextureBaker texBaker = prefabBaker.GetComponent<MB3_TextureBaker>(); + List<GameObject> newPrefabs = new List<GameObject>(); + List<GameObject> gos = texBaker.GetObjectsToCombine(); + for (int i = 0; i < gos.Count; i++) + { + GameObject go = (GameObject)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<GameObject> tmpNewPrefabs = new List<GameObject>(); + 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<MB3_BatchPrefabBaker.MB3_PrefabBakerRow> newRows = new List<MB3_BatchPrefabBaker.MB3_PrefabBakerRow>(); + 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<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; + } + + 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<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 (MBVersionEditor.GetPrefabType(pb.prefabRows[i].sourcePrefab) != MB_PrefabType.modelPrefabAsset && + MBVersionEditor.GetPrefabType(pb.prefabRows[i].sourcePrefab) != MB_PrefabType.prefabAsset) + { + Debug.LogError("Row " + i + " source prefab is not a prefab asset "); + return; + } + if (MBVersionEditor.GetPrefabType(pb.prefabRows[i].resultPrefab) != MB_PrefabType.modelPrefabAsset && + MBVersionEditor.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<Renderer>(true); + + for (int j = 0; j < rs.Length; j++) + { + if (IsGoodToBake(rs[j], tbr)) + { + sourceMeshes.Add(MB_Utility.GetMesh(rs[j].gameObject)); + } + } + rs = ro.GetComponentsInChildren<Renderer>(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<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; + } + + List<UnityTransform> unityTransforms = new List<UnityTransform>(); + // Bake the meshes using the meshBaker component one prefab at a time + for (int prefabIdx = 0; prefabIdx < pb.prefabRows.Length; prefabIdx++) + { + if (doReplaceTargetPrefab) + { + ProcessPrefabRowReplaceTargetPrefab(pb, pb.prefabRows[prefabIdx], tbr, unityTransforms, mb); + } + else + { + ProcessPrefabRowOnlyMeshesAndMaterials(pb, pb.prefabRows[prefabIdx], tbr, unityTransforms, mb); + } + } + AssetDatabase.Refresh(); + mb.ClearMesh(); + } + + private static void ProcessPrefabRowOnlyMeshesAndMaterials(MB3_BatchPrefabBaker pb, MB3_BatchPrefabBaker.MB3_PrefabBakerRow pr, MB2_TextureBakeResults tbr, List<UnityTransform> unityTransforms, MB3_MeshBaker mb) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("==== Processing Source Prefab " + pr.sourcePrefab); + + GameObject srcPrefab = pr.sourcePrefab; + GameObject targetPrefab = pr.resultPrefab; + string targetPrefabName = AssetDatabase.GetAssetPath(targetPrefab); + GameObject srcPrefabInstance = GameObject.Instantiate(srcPrefab); + GameObject targPrefabInstance = GameObject.Instantiate(targetPrefab); + + Renderer[] rs = srcPrefabInstance.GetComponentsInChildren<Renderer>(true); + if (rs.Length < 1) + { + Debug.LogWarning("Prefab " + pr.sourcePrefab + " does not have a renderer"); + GameObject.DestroyImmediate(srcPrefabInstance); + GameObject.DestroyImmediate(targPrefabInstance); + return; + } + + Renderer[] sourceRenderers = srcPrefabInstance.GetComponentsInChildren<Renderer>(true); + Dictionary<Mesh, List<ProcessedMeshInfo>> processedMeshesSrcToTargetMap = new Dictionary<Mesh, List<ProcessedMeshInfo>>(); + for (int i = 0; i < sourceRenderers.Length; i++) + { + if (!IsGoodToBake(sourceRenderers[i], tbr)) + { + continue; + } + + Mesh sourceMesh = MB_Utility.GetMesh(sourceRenderers[i].gameObject); + + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("== Visiting renderer: " + sourceRenderers[i]); + // Try to find an existing mesh in the target that we can re-use + Mesh targetMeshAsset = null; + Transform tr = FindCorrespondingTransform(srcPrefabInstance.transform, sourceRenderers[i].transform, targPrefabInstance.transform); + Renderer targetRenderer = null; + if (tr != null) + { + Mesh targMesh = MB_Utility.GetMesh(tr.gameObject); + + // Only replace target meshes if they are part of the target prefab. + if (AssetDatabase.GetAssetPath(targMesh) == AssetDatabase.GetAssetPath(targetPrefab)) + { + targetRenderer = tr.GetComponent<Renderer>(); + if (sourceRenderers[i].GetType() == targetRenderer.GetType()) + { + targetMeshAsset = MB_Utility.GetMesh(tr.gameObject); + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log("Found correspoinding transform in target prefab: " + tr + " mesh: " + targetMeshAsset); + } + else + { + Debug.LogError("The renderer on the target prefab matching source prefab " + pr.sourcePrefab + " was not the same kind of renderer " + sourceRenderers[i].name); + continue; + } + + } + } + else + { + Debug.LogError("There was a renderer in source prefab " + pr.sourcePrefab + " that could not be a matched to a target renderer in the target prefab: " + sourceRenderers[i].name + + " This can happen if the hierarchy in the source prefab is different from the hierarchy in the target prefab."); + continue; + } + + // Check that we haven't processed this mesh already. + List<ProcessedMeshInfo> lpmi; + if (processedMeshesSrcToTargetMap.TryGetValue(sourceMesh, out lpmi)) + { + Material[] srcMats = MB_Utility.GetGOMaterials(sourceRenderers[i].gameObject); + for (int j = 0; j < lpmi.Count; j++) + { + if (ComapreMaterials(srcMats, lpmi[j].srcMaterials)) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log("Found already processed mesh that uses the same mats"); + targetMeshAsset = lpmi[j].targetMesh; + break; + } + } + } + + Material[] sourceMaterials = MB_Utility.GetGOMaterials(sourceRenderers[i].gameObject); + TargetMeshTreatment targetMeshTreatment = TargetMeshTreatment.createNewMesh; + string newMeshName = sourceMesh.name; + if (targetMeshAsset != null) + { + // check if this mesh has already been processed + processedMeshesSrcToTargetMap.TryGetValue(sourceMesh, out lpmi); + if (lpmi != null) + { + // check if this mesh uses the same materials as one of the processed meshs + bool foundMatch = false; + bool targetMeshHasBeenUsed = false; + Material[] foundMatchMaterials = null; + for (int j = 0; j < lpmi.Count; j++) + { + if (lpmi[j].targetMesh == targetMeshAsset) + { + targetMeshHasBeenUsed = true; + } + if (ComapreMaterials(sourceMaterials, lpmi[j].srcMaterials)) + { + foundMatchMaterials = lpmi[j].targMaterials; + foundMatch = true; + break; + } + } + + if (foundMatch) + { + // If materials match then we can re-use this processed mesh don't process. + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" we can re-use this processed mesh don't process. " + targetMeshAsset); + targetMeshTreatment = TargetMeshTreatment.reuseMesh; + MB_Utility.SetMesh(tr.gameObject, targetMeshAsset); + SetMaterials(foundMatchMaterials, targetRenderer); + continue; + } + else + { + if (targetMeshHasBeenUsed) + { + // we need a new target mesh with a safe different name + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" we can't re-use this processed mesh create new with different name. " + targetMeshAsset); + newMeshName = GetNameForNewMesh(AssetDatabase.GetAssetPath(targetPrefab), newMeshName); + targetMeshTreatment = TargetMeshTreatment.createNewMesh; + targetMeshAsset = null; + } + else + { + // is it safe to reuse the target mesh + // we need a new target mesh with a safe different name + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" we can replace this processed mesh. " + targetMeshAsset); + targetMeshTreatment = TargetMeshTreatment.replaceMesh; + } + } + } + else + { + // source mesh has not been processed can reuse the target mesh + targetMeshTreatment = TargetMeshTreatment.replaceMesh; + } + } + + if (targetMeshTreatment == TargetMeshTreatment.replaceMesh) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Replace mesh " + targetMeshAsset); + EditorUtility.CopySerialized(sourceMesh, targetMeshAsset); + AssetDatabase.SaveAssets(); + AssetDatabase.ImportAsset(targetPrefabName); + } + else if (targetMeshTreatment == TargetMeshTreatment.createNewMesh) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Create new mesh " + newMeshName); + targetMeshAsset = GameObject.Instantiate<Mesh>(sourceMesh); + targetMeshAsset.name = newMeshName; + AssetDatabase.AddObjectToAsset(targetMeshAsset, targetPrefab); +#if UNITY_2018_3_OR_NEWER + PrefabUtility.SavePrefabAsset(targetPrefab); +#endif + Debug.Assert(targetMeshAsset != null); + // need a new mesh + } + + if (targetMeshTreatment == TargetMeshTreatment.createNewMesh || targetMeshTreatment == TargetMeshTreatment.replaceMesh) + { + if (ProcessMesh(sourceRenderers[i], targetMeshAsset, unityTransforms, mb)) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Done processing mesh " + targetMeshAsset + " verts " + targetMeshAsset.vertexCount); + ProcessedMeshInfo pmi = new ProcessedMeshInfo(); + pmi.targetMesh = targetMeshAsset; + pmi.srcMaterials = sourceMaterials; + pmi.targMaterials = sourceRenderers[i].sharedMaterials; + AddToDictionary(sourceMesh, pmi, processedMeshesSrcToTargetMap); + } + else + { + Debug.LogError("Error processing mesh " + targetMeshAsset); + } + } + + MB_Utility.SetMesh(tr.gameObject, targetMeshAsset); + } + + + // TODO replace this with MBVersionEditor.ReplacePrefab I tried to do this, but when I did, + // ProcessedMeshInfo.targetMesh becomes null, not sure what is going on there. + GameObject obj = (GameObject)AssetDatabase.LoadAssetAtPath(targetPrefabName, typeof(GameObject)); + PrefabUtility.ReplacePrefab(targPrefabInstance, obj, ReplacePrefabOptions.ReplaceNameBased); + + GameObject.DestroyImmediate(srcPrefabInstance); + GameObject.DestroyImmediate(targPrefabInstance); + + // Destroy obsolete meshes + UnityEngine.Object[] allAssets = AssetDatabase.LoadAllAssetsAtPath(targetPrefabName); + HashSet<Mesh> usedByTarget = new HashSet<Mesh>(); + foreach (List<ProcessedMeshInfo> ll in processedMeshesSrcToTargetMap.Values) + { + for (int i = 0; i < ll.Count; i++) + { + usedByTarget.Add(ll[i].targetMesh); + } + } + int numDestroyed = 0; + for (int i = 0; i < allAssets.Length; i++) + { + if (allAssets[i] is Mesh) + { + if (!usedByTarget.Contains((Mesh)allAssets[i]) && AssetDatabase.GetAssetPath(allAssets[i]) == AssetDatabase.GetAssetPath(targetPrefab)) + { + numDestroyed++; + GameObject.DestroyImmediate(allAssets[i], true); + } + } + } + + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Destroyed " + numDestroyed + " meshes"); + AssetDatabase.SaveAssets(); + //-------------------------- + } + + private static void ProcessPrefabRowReplaceTargetPrefab(MB3_BatchPrefabBaker pb, MB3_BatchPrefabBaker.MB3_PrefabBakerRow pr, MB2_TextureBakeResults tbr, List<UnityTransform> unityTransforms, MB3_MeshBaker mb) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("==== Processing Source Prefab " + pr.sourcePrefab); + + GameObject srcPrefab = pr.sourcePrefab; + GameObject targetPrefab = pr.resultPrefab; + string targetPrefabName = AssetDatabase.GetAssetPath(targetPrefab); + GameObject prefabInstance = GameObject.Instantiate(srcPrefab); + + Renderer[] rs = prefabInstance.GetComponentsInChildren<Renderer>(true); + if (rs.Length < 1) + { + Debug.Log("Prefab " + pr.sourcePrefab + " does not have a renderer. Not replacing prefab."); + GameObject.DestroyImmediate(prefabInstance); + return; + } + + Renderer[] sourceRenderers = prefabInstance.GetComponentsInChildren<Renderer>(true); + + Dictionary<Mesh, List<ProcessedMeshInfo>> processedMeshesSrcToTargetMap = new Dictionary<Mesh, List<ProcessedMeshInfo>>(); + for (int i = 0; i < sourceRenderers.Length; i++) + { + if (!IsGoodToBake(sourceRenderers[i], tbr)) + { + continue; + } + + Mesh sourceMesh = MB_Utility.GetMesh(sourceRenderers[i].gameObject); + + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("== Visiting source renderer: " + sourceRenderers[i]); + // Try to find an existing mesh in the target that we can re-use + Mesh targetMeshAsset = null; + Transform tr = FindCorrespondingTransform(prefabInstance.transform, sourceRenderers[i].transform, targetPrefab.transform); + if (tr != null) + { + Mesh targMesh = MB_Utility.GetMesh(tr.gameObject); + + // Only replace target meshes if they are part of the target prefab. + if (AssetDatabase.GetAssetPath(targMesh) == AssetDatabase.GetAssetPath(targetPrefab)) + { + targetMeshAsset = MB_Utility.GetMesh(tr.gameObject); + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log("Found corresponding transform in target prefab: " + tr + " mesh: " + targetMeshAsset); + } + } + + // Check that we haven't processed this mesh already. + List<ProcessedMeshInfo> lpmi; + if (processedMeshesSrcToTargetMap.TryGetValue(sourceMesh, out lpmi)) + { + Material[] srcMats = MB_Utility.GetGOMaterials(sourceRenderers[i].gameObject); + for (int j = 0; j < lpmi.Count; j++) + { + if (ComapreMaterials(srcMats, lpmi[j].srcMaterials)) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log("Found already processed mesh that uses the same mats"); + targetMeshAsset = lpmi[j].targetMesh; + break; + } + } + } + + Material[] sourceMaterials = MB_Utility.GetGOMaterials(sourceRenderers[i].gameObject); + TargetMeshTreatment targetMeshTreatment = TargetMeshTreatment.createNewMesh; + string newMeshName = sourceMesh.name; + if (targetMeshAsset != null) + { + // check if this mesh has already been processed + processedMeshesSrcToTargetMap.TryGetValue(sourceMesh, out lpmi); + if (lpmi != null) + { + // check if this mesh uses the same materials as one of the processed meshs + bool foundMatch = false; + bool targetMeshHasBeenUsed = false; + Material[] foundMatchMaterials = null; + for (int j = 0; j < lpmi.Count; j++) + { + if (lpmi[j].targetMesh == targetMeshAsset) + { + targetMeshHasBeenUsed = true; + } + if (ComapreMaterials(sourceMaterials, lpmi[j].srcMaterials)) + { + foundMatchMaterials = lpmi[j].targMaterials; + foundMatch = true; + break; + } + } + + if (foundMatch) + { + // If materials match then we can re-use this processed mesh don't process. + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" we can re-use this processed mesh don't process. " + targetMeshAsset); + targetMeshTreatment = TargetMeshTreatment.reuseMesh; + MB_Utility.SetMesh(sourceRenderers[i].gameObject, targetMeshAsset); + SetMaterials(foundMatchMaterials, sourceRenderers[i]); + continue; + } + else + { + if (targetMeshHasBeenUsed) + { + // we need a new target mesh with a safe different name + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" we can't re-use this processed mesh create new with different name. " + targetMeshAsset); + newMeshName = GetNameForNewMesh(AssetDatabase.GetAssetPath(targetPrefab), newMeshName); + targetMeshTreatment = TargetMeshTreatment.createNewMesh; + targetMeshAsset = null; + } + else + { + // is it safe to reuse the target mesh + // we need a new target mesh with a safe different name + if (pb.LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(" we can replace this processed mesh. " + targetMeshAsset); + targetMeshTreatment = TargetMeshTreatment.replaceMesh; + } + } + } + else + { + // source mesh has not been processed can reuse the target mesh + targetMeshTreatment = TargetMeshTreatment.replaceMesh; + } + } + + if (targetMeshTreatment == TargetMeshTreatment.replaceMesh) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Replace mesh " + targetMeshAsset); + EditorUtility.CopySerialized(sourceMesh, targetMeshAsset); + AssetDatabase.SaveAssets(); + AssetDatabase.ImportAsset(targetPrefabName); + } + else if (targetMeshTreatment == TargetMeshTreatment.createNewMesh) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Create new mesh " + newMeshName); + targetMeshAsset = GameObject.Instantiate<Mesh>(sourceMesh); + targetMeshAsset.name = newMeshName; + AssetDatabase.AddObjectToAsset(targetMeshAsset, targetPrefab); +#if UNITY_2018_3_OR_NEWER + PrefabUtility.SavePrefabAsset(targetPrefab); +#endif + Debug.Assert(targetMeshAsset != null); + // need a new mesh + } + + if (targetMeshTreatment == TargetMeshTreatment.createNewMesh || targetMeshTreatment == TargetMeshTreatment.replaceMesh) + { + if (ProcessMesh(sourceRenderers[i], targetMeshAsset, unityTransforms, mb)) + { + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Done processing mesh " + targetMeshAsset + " verts " + targetMeshAsset.vertexCount); + ProcessedMeshInfo pmi = new ProcessedMeshInfo(); + pmi.targetMesh = targetMeshAsset; + pmi.srcMaterials = sourceMaterials; + pmi.targMaterials = sourceRenderers[i].sharedMaterials; + AddToDictionary(sourceMesh, pmi, processedMeshesSrcToTargetMap); + } + else + { + Debug.LogError("Error processing mesh " + targetMeshAsset); + } + } + + MB_Utility.SetMesh(sourceRenderers[i].gameObject, targetMeshAsset); + } + + // TODO replace this with MBVersionEditor.ReplacePrefab I tried to do this, but when I did, + // ProcessedMeshInfo.targetMesh becomes null, not sure what is going on there. + GameObject obj = (GameObject)AssetDatabase.LoadAssetAtPath(targetPrefabName, typeof(GameObject)); + PrefabUtility.ReplacePrefab(prefabInstance, obj, ReplacePrefabOptions.ReplaceNameBased); + GameObject.DestroyImmediate(prefabInstance); + + // Destroy obsolete meshes + UnityEngine.Object[] allAssets = AssetDatabase.LoadAllAssetsAtPath(targetPrefabName); + HashSet<Mesh> usedByTarget = new HashSet<Mesh>(); + foreach (List<ProcessedMeshInfo> ll in processedMeshesSrcToTargetMap.Values) + { + for (int i = 0; i < ll.Count; i++) + { + usedByTarget.Add(ll[i].targetMesh); + } + } + int numDestroyed = 0; + for (int i = 0; i < allAssets.Length; i++) + { + if (allAssets[i] is Mesh) + { + if (!usedByTarget.Contains((Mesh)allAssets[i]) && AssetDatabase.GetAssetPath(allAssets[i]) == AssetDatabase.GetAssetPath(targetPrefab)) + { + numDestroyed++; + GameObject.DestroyImmediate(allAssets[i], true); + } + } + } + + if (pb.LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Destroyed " + numDestroyed + " meshes"); + AssetDatabase.SaveAssets(); + //-------------------------- + } + + private static string GetNameForNewMesh(string prefabPath, string baseName) + { + // get all Mesh assets in prefab + UnityEngine.Object[] objs = AssetDatabase.LoadAllAssetsAtPath(prefabPath); + string[] oldNames = new string[objs.Length]; // TODO get these + for (int i = 0; i < oldNames.Length; i++) + { + oldNames[i] = objs[i].name; + } + + bool isUnique = false; + int idx = 0; + string name = baseName; + while (!isUnique) + { + bool wasAMatch = false; + for (int i = 0; i < oldNames.Length; i++) + { + if (oldNames[i].Equals(name)) + { + wasAMatch = true; + break; + } + } + if (wasAMatch) + { + idx++; + name = baseName + idx; + } + else + { + isUnique = true; + } + } + return name; + } + + private static bool ComapreMaterials(Material[] a, Material[] b) + { + if (a.Length != b.Length) return false; + for (int i = 0; i < a.Length; i++) + { + if (a[i] != b[i]) return false; + } + return true; + } + + private static void AddToDictionary(Mesh sourceMesh, ProcessedMeshInfo pmi, Dictionary<Mesh, List<ProcessedMeshInfo>> dict) + { + List<ProcessedMeshInfo> lpmi; + if (!dict.ContainsKey(sourceMesh)) + { + lpmi = new List<ProcessedMeshInfo>(); + dict[sourceMesh] = lpmi; + } + else + { + lpmi = dict[sourceMesh]; + } + lpmi.Add(pmi); + } + + private static bool ProcessMesh(Renderer r, Mesh m, List<UnityTransform> unityTransforms, MB3_MeshBaker mb) + { + unityTransforms.Clear(); + // 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; + } + + //bake the mesh + MB3_MeshCombinerSingle mc = (MB3_MeshCombinerSingle)mb.meshCombiner; + if (!MB3_BakeInPlace.BakeOneMesh(mc, m, r.gameObject)) + { + return false; + } + + //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; + } + + if (mc.targetRenderer != null) + { + SetMaterials(mc.targetRenderer.sharedMaterials, r); + } + + //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; + } + + mc.SetMesh(null); + return true; + } + + private static void SetMaterials(Material[] sharedMaterials, Renderer r) + { + //First try to get the materials from the target renderer. This is because the mesh may have fewer submeshes than number of result materials if some of the submeshes had zero length tris. + //If we have just baked then materials on the target renderer will be correct wheras materials on the textureBakeResult may not be correct. + + Material[] sharedMats = new Material[sharedMaterials.Length]; + for (int i = 0; i < sharedMats.Length; i++) + { + sharedMats[i] = sharedMaterials[i]; + } + if (r is SkinnedMeshRenderer) + { + r.sharedMaterial = null; + r.sharedMaterials = sharedMats; + } + else + { + r.sharedMaterial = null; + r.sharedMaterials = sharedMats; + } + } + + 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; + } + + internal static Transform FindCorrespondingTransform(Transform srcRoot, Transform srcChild, + Transform targRoot) + { + if (srcRoot == srcChild) return targRoot; + + // Debug.Log ("start ============"); + //build the path to the root in the source prefab + List<Transform> path_root2child = new List<Transform>(); + Transform t = srcChild; + do + { + path_root2child.Insert(0, t); + t = t.parent; + } while (t != null && t != t.root && t != srcRoot); + if (t == null) + { + Debug.LogError("scrChild was not child of srcRoot " + srcRoot + " " + srcChild); + return null; + } + path_root2child.Insert(0, srcRoot); + // Debug.Log ("path to root for " + srcChild + " " + path_root2child.Count); + + //try to find a matching path in the target prefab + t = targRoot; + for (int i = 1; i < path_root2child.Count; i++) + { + Transform tSrc = path_root2child[i - 1]; + //try to find child in same position with same name + int srcIdx = TIndexOf(tSrc, path_root2child[i]); + if (srcIdx < t.childCount && path_root2child[i].name.Equals(t.GetChild(srcIdx).name)) + { + t = t.GetChild(srcIdx); + // Debug.Log ("found child in same position with same name " + t); + continue; + } + //try to find child with same name + for (int j = 0; j < t.childCount; j++) + { + if (t.GetChild(j).name.Equals(path_root2child[i].name)) + { + t = t.GetChild(j); + // Debug.Log ("found child with same name " + t); + continue; + } + } + t = null; + break; + } + // Debug.Log ("end =============== " + t); + return t; + } + + private static int TIndexOf(Transform p, Transform c) + { + for (int i = 0; i < p.childCount; i++) + { + if (c == p.GetChild(i)) + { + return i; + } + } + return -1; + } + + public static string ConvertProjectRelativePathToFullPath(string projectRelativePath) + { + if (projectRelativePath.StartsWith("Assets")) + { + string fullPath = Application.dataPath + projectRelativePath.Substring(6); + return fullPath; + } else + { + return projectRelativePath; + } + } + + public static string ConvertFullPathToProjectRelativePath(string path) + { + if (path != null && path.Length > 0) + { + Uri projectFolder = new Uri(Application.dataPath); + Uri outputFolder = new Uri(path); + Uri relativeUri = projectFolder.MakeRelativeUri(outputFolder); + string relativePath = MBVersion.UnescapeURL(relativeUri.ToString()); + if (relativePath.Length == 0) + { + relativePath = "Assets/"; + } + + if (!relativePath.StartsWith("Assets/")) + { + Debug.LogError("Bad folder path. The folder must be in the project Assets folder."); + return ""; + } else + { + return relativePath; + } + } else + { + return path; + } + } + + public static bool ValidateFolderIsInProject(string fieldName, string folder) + { + if (folder == null) + { + Debug.LogError(fieldName + " must be set"); + return false; + } + + if (folder.StartsWith("Assets")) + { + folder = MB_BatchPrefabBakerEditorFunctions.ConvertProjectRelativePathToFullPath(folder); + } + + if (folder.StartsWith(Application.dataPath)) + { + string relativePath = "Assets" + folder.Substring(Application.dataPath.Length); + string gid = AssetDatabase.AssetPathToGUID(relativePath); + if (gid == null || gid.Length == 0) + { + Debug.LogError(fieldName + " must be an existing folder in the Unity project Assets folder"); + return false; + } + } + else + { + Debug.LogError(fieldName + " must be an existing folder in the Unity project Assets folder"); + return false; + } + + return true; + } + + public static void CreateEmptyOutputPrefabs(string outputFolder, MB3_BatchPrefabBaker target) + { + if (!ValidateFolderIsInProject("Output Folder", outputFolder)) + { + return; + } + + if (outputFolder.StartsWith("Assets")) outputFolder = ConvertProjectRelativePathToFullPath(outputFolder); + int numCreated = 0; + int numSkippedSrcNull = 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 (prefabBaker.prefabRows[i].resultPrefab == null) + { + string outName = outputFolder + "/" + prefabBaker.prefabRows[i].sourcePrefab.name + ".prefab"; + outName = outName.Replace(Application.dataPath, ""); + outName = "Assets" + outName; + GameObject go = new GameObject(prefabBaker.prefabRows[i].sourcePrefab.name); + prefabBaker.prefabRows[i].resultPrefab = PrefabUtility.CreatePrefab(outName, go); + GameObject.DestroyImmediate(go); + numCreated++; + } + else + { + numSkippedAlreadyExisted++; + } + } + else + { + numSkippedSrcNull++; + } + } + Debug.Log(String.Format("Created {0} prefabs. Skipped {1} because source prefab was null. Skipped {2} because the result prefab was already assigned", numCreated, numSkippedSrcNull, numSkippedAlreadyExisted)); + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_BatchPrefabBakerEditorFunctions.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_BatchPrefabBakerEditorFunctions.cs.meta new file mode 100644 index 00000000..a0f9faa2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_BatchPrefabBakerEditorFunctions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac48c91c4533eca4aae60b5556e62017 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_EditorUtil.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_EditorUtil.cs new file mode 100644 index 00000000..867bd619 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_EditorUtil.cs @@ -0,0 +1,179 @@ +//---------------------------------------------- +// MeshBaker +// Copyright © 2011-2012 Ian Deane +//---------------------------------------------- + +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; + +public static class MB_EditorUtil +{ + public const string MESH_BAKER_WORKING_FOLDER_NAME = "MeshBakerWorkingFolder"; + + public static string GetShortPathToWorkingDirectoryAndEnsureItExists() + { + string workingFolder = "Assets/MeshBaker/" + MESH_BAKER_WORKING_FOLDER_NAME; + if (!AssetDatabase.IsValidFolder("Assets/MeshBaker")) + { + AssetDatabase.CreateFolder("Assets", "MeshBaker"); + } + + if (!AssetDatabase.IsValidFolder(workingFolder)) + { + AssetDatabase.CreateFolder("Assets/MeshBaker", MESH_BAKER_WORKING_FOLDER_NAME); + } + + return workingFolder; + } + + static public void DrawSeparator() + { + GUILayout.Space(12f); + + if (Event.current.type == EventType.Repaint) + { + Texture2D tex = EditorGUIUtility.whiteTexture; + Rect rect = GUILayoutUtility.GetLastRect(); + GUI.color = new Color(0f, 0f, 0f, 0.25f); + GUI.DrawTexture(new Rect(0f, rect.yMin + 6f, Screen.width, 4f), tex); + GUI.DrawTexture(new Rect(0f, rect.yMin + 6f, Screen.width, 1f), tex); + GUI.DrawTexture(new Rect(0f, rect.yMin + 9f, Screen.width, 1f), tex); + GUI.color = Color.white; + } + } + + static public Rect DrawHeader(string text) + { + GUILayout.Space(28f); + Rect rect = GUILayoutUtility.GetLastRect(); + rect.yMin += 5f; + rect.yMax -= 4f; + rect.width = Screen.width; + + if (Event.current.type == EventType.Repaint) + { + GUI.color = Color.black; + GUI.color = new Color(0f, 0f, 0f, 0.25f); + GUI.DrawTexture(new Rect(0f, rect.yMin, Screen.width, 1f), EditorGUIUtility.whiteTexture); + GUI.DrawTexture(new Rect(0f, rect.yMax - 1, Screen.width, 1f), EditorGUIUtility.whiteTexture); + GUI.color = Color.white; + GUI.Label(new Rect(rect.x + 4f, rect.y, rect.width - 4, rect.height), text, EditorStyles.boldLabel); + } + return rect; + } + + [Flags] + public enum EditorListOption + { + None = 0, + ListSize = 1, + ListLabel = 2, + ElementLabels = 4, + Buttons = 8, + Default = ListSize | ListLabel | ElementLabels, + NoElementLabels = ListSize | ListLabel, + All = Default | Buttons + } + + private static GUILayoutOption miniButtonWidth = GUILayout.Width(20f); + + private static GUIContent + moveButtonContent = new GUIContent("\u21b4", "move down"), + duplicateButtonContent = new GUIContent("+", "duplicate"), + deleteButtonContent = new GUIContent("-", "delete"), + addButtonContent = new GUIContent("+", "add element"); + + public static void Show(SerializedProperty list, EditorListOption options = EditorListOption.Default) + { + if (!list.isArray) + { + EditorGUILayout.HelpBox(list.name + " is neither an array nor a list!", MessageType.Error); + return; + } + + bool + showListLabel = (options & EditorListOption.ListLabel) != 0, + showListSize = (options & EditorListOption.ListSize) != 0; + + if (showListLabel) + { + EditorGUILayout.PropertyField(list); + EditorGUI.indentLevel += 1; + } + if (!showListLabel || list.isExpanded) + { + SerializedProperty size = list.FindPropertyRelative("Array.size"); + if (showListSize) + { + EditorGUILayout.PropertyField(size); + } + if (size.hasMultipleDifferentValues) + { + EditorGUILayout.HelpBox("Not showing lists with different sizes.", MessageType.Info); + } + else + { + ShowElements(list, options); + } + } + if (showListLabel) + { + EditorGUI.indentLevel -= 1; + } + } + + private static void ShowElements(SerializedProperty list, EditorListOption options) + { + bool + showElementLabels = (options & EditorListOption.ElementLabels) != 0, + showButtons = (options & EditorListOption.Buttons) != 0; + + for (int i = 0; i < list.arraySize; i++) + { + if (showButtons) + { + EditorGUILayout.BeginHorizontal(); + } + if (showElementLabels) + { + EditorGUILayout.PropertyField(list.GetArrayElementAtIndex(i)); + } + else + { + EditorGUILayout.PropertyField(list.GetArrayElementAtIndex(i), GUIContent.none); + } + if (showButtons) + { + ShowButtons(list, i); + EditorGUILayout.EndHorizontal(); + } + } + if (showButtons && list.arraySize == 0 && GUILayout.Button(addButtonContent, EditorStyles.miniButton)) + { + list.arraySize += 1; + } + } + + private static void ShowButtons(SerializedProperty list, int index) + { + if (GUILayout.Button(moveButtonContent, EditorStyles.miniButtonLeft, miniButtonWidth)) + { + list.MoveArrayElement(index, index + 1); + } + if (GUILayout.Button(duplicateButtonContent, EditorStyles.miniButtonMid, miniButtonWidth)) + { + list.InsertArrayElementAtIndex(index); + } + if (GUILayout.Button(deleteButtonContent, EditorStyles.miniButtonRight, miniButtonWidth)) + { + int oldSize = list.arraySize; + list.DeleteArrayElementAtIndex(index); + if (list.arraySize == oldSize) + { + list.DeleteArrayElementAtIndex(index); + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_EditorUtil.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_EditorUtil.cs.meta new file mode 100644 index 00000000..23c95b3c --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_EditorUtil.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d303b9ab4cbd3e74a84f2a83c9c9da6c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_ReplacePrefabsInScene.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_ReplacePrefabsInScene.cs new file mode 100644 index 00000000..8784a1e1 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_ReplacePrefabsInScene.cs @@ -0,0 +1,518 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using System; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + public class MB_ReplacePrefabsInScene : MonoBehaviour + { + [System.Serializable] + public class Error + { + public GameObject errorObj; + public String error; + } + + private class Prop2Reference + { + public string propertyName; + public bool overriddenInSrcInstance; + public UnityEngine.Object obj; + public string parentArrayName; + public int parentArraySize; + } + + private class Component2MeshAndMaterials + { + public Component component; + public List<Prop2Reference> props = new List<Prop2Reference>(); + } + + private List<Error> errors; + + public bool replaceEnforceStructure = true; + + void AddError(GameObject obj, string message) + { + Error err = new Error() + { + errorObj = obj, + error = message, + }; + + errors.Add(err); + Debug.LogError(message); + } + + public int ReplacePrefabInstancesInScene(GameObject mySrcPrefab, GameObject myTargPrefab, List<Error> objsWithErrors) + { + + errors = objsWithErrors; + errors.Clear(); + + if (mySrcPrefab == null) + { + AddError(mySrcPrefab, "Source Prefab was null"); + return 0; + } + + if (myTargPrefab == null) + { + AddError(myTargPrefab, "Target Prefab was null"); + return 0; + } + + if (MB_Utility.IsSceneInstance(mySrcPrefab)) + { + AddError(mySrcPrefab, "The source prefab was a scene instance. It must be a project asset."); + return 0; + } + + if (MB_Utility.IsSceneInstance(myTargPrefab)) + { + AddError(mySrcPrefab, "The target prefab was a scene instance. It must be a project asset."); + return 0; + } + + // First validate all the prefabs, check that they are "up-to-date" + if (replaceEnforceStructure) + { + bool structureIsSame = ValidateStructureAndCollectInternalReferences(mySrcPrefab, myTargPrefab, null, null); + if (!structureIsSame) + { + AddError(mySrcPrefab, "Prefab Structure is not the same for prefabs:" + mySrcPrefab + " and " + myTargPrefab); + return 0; + } + } + + // Second pass collect all prefab instances in the scene, validate if they are replacable + List<GameObject> instancesInScene = FindAllPrefabInstances(mySrcPrefab); + if (replaceEnforceStructure) + { + for (int i = 0; i < instancesInScene.Count; i++) + { + if (!ValidateStructureAndCollectInternalReferences(instancesInScene[i], myTargPrefab, null, null)) + { + AddError(instancesInScene[i], "A scene instance for prefab " + mySrcPrefab + " has a modified structure that is too different from targetPrefab:" + instancesInScene[i]); + } + } + } + + if (errors.Count > 0) return 0; + + // Third pass replace all the prefabs in the scene. + int numReplaced = 0; + for (int i = 0; i < instancesInScene.Count; i++) + { + GameObject srcInstance = instancesInScene[i]; + GameObject targInstance = (GameObject)PrefabUtility.InstantiatePrefab(myTargPrefab); + Undo.RegisterCreatedObjectUndo(targInstance, "Replace Prefabs"); + targInstance.transform.parent = srcInstance.transform.parent; + targInstance.name = srcInstance.name; + if (ReplaceSinglePrefabInstance(srcInstance, targInstance)) + { + Undo.DestroyObjectImmediate(srcInstance); + MB_Utility.Destroy(srcInstance); + numReplaced++; + } + else + { + Undo.DestroyObjectImmediate(targInstance); + MB_Utility.Destroy(targInstance); + } + } + + Debug.Log("Replaced " + numReplaced + " instances in the scene for prefab:" + mySrcPrefab); + return numReplaced; + } + + private static List<GameObject> FindAllPrefabInstances(UnityEngine.Object myPrefab) + { + List<GameObject> result = new List<GameObject>(); + GameObject[] allObjects = (GameObject[])FindObjectsOfType(typeof(GameObject)); + foreach (GameObject go in allObjects) + { + PrefabType objPrefabType = EditorUtility.GetPrefabType(go); + if (objPrefabType == PrefabType.PrefabInstance || + objPrefabType == PrefabType.ModelPrefabInstance) + { + UnityEngine.Object GO_prefab = EditorUtility.GetPrefabParent(go); + if (myPrefab == GO_prefab) + { + result.Add(go); + } + } + } + + return result; + } + + private bool ReplaceSinglePrefabInstance(GameObject src, GameObject targ) + { + Debug.Assert(MB_Utility.IsSceneInstance(src)); + Debug.Assert(MB_Utility.IsSceneInstance(targ)); + + // Build a source 2 target map of all internal references + if (replaceEnforceStructure) + { + Dictionary<UnityEngine.Object, UnityEngine.Object> src2targetObjMap = new Dictionary<UnityEngine.Object, UnityEngine.Object>(); + + // Collect all references to project assets. + Dictionary<Component, Component2MeshAndMaterials> component2MeshAndMats = new Dictionary<Component, Component2MeshAndMaterials>(); + bool identicalStructure = ValidateStructureAndCollectInternalReferences(src, targ, src2targetObjMap, component2MeshAndMats); + if (!identicalStructure) + { + AddError(src, "Prefabs did not have identical structure " + targ); + return false; + } + + return VisitObj(src, src, targ, src2targetObjMap, component2MeshAndMats); + } else + { + targ.layer = src.layer; + targ.tag = src.tag; + GameObjectUtility.SetStaticEditorFlags(targ, GameObjectUtility.GetStaticEditorFlags(src)); + targ.transform.localPosition = src.transform.localPosition; + targ.transform.localRotation = src.transform.localRotation; + targ.transform.localScale = src.transform.localScale; + return true; + } + } + + /// <summary> + /// + /// </summary> + /// <param name="srcObj"></param> + /// <param name="targObj"></param> + /// <param name="src2targetObjMap">Can be null.</param> + /// <param name="component2MeshAndMats">Can be null</param> + private bool ValidateStructureAndCollectInternalReferences(GameObject srcObj, GameObject targObj, + Dictionary<UnityEngine.Object, UnityEngine.Object> src2targetObjMap, + Dictionary<Component, Component2MeshAndMaterials> component2MeshAndMats) + { + if (src2targetObjMap != null) src2targetObjMap.Add(srcObj, targObj); + Component[] srcComponents = srcObj.GetComponents<Component>(); + Component[] targComponents = targObj.GetComponents<Component>(); + if (srcComponents.Length != targComponents.Length) return false; + for (int i = 0; i < srcComponents.Length; i++) + { + if (srcComponents[i].GetType() != targComponents[i].GetType()) + { + Debug.Log("Components are different " + srcObj + " " + srcComponents[i] + " " + targObj + " " + targComponents[i]); + return false; + } + + if (src2targetObjMap != null && component2MeshAndMats != null) + { + src2targetObjMap.Add(srcComponents[i], targComponents[i]); + CollectAssetReferncesForComponent(srcComponents[i], component2MeshAndMats); + CollectAssetReferncesForComponent(targComponents[i], component2MeshAndMats, true); + } + } + + if (srcObj.transform.childCount != targObj.transform.childCount) return false; + for (int i = 0; i < srcObj.transform.childCount; i++) + { + Transform srcChild = srcObj.transform.GetChild(i); + Transform targChild = targObj.transform.GetChild(i); + bool childIsValid = ValidateStructureAndCollectInternalReferences(srcChild.gameObject, targChild.gameObject, + src2targetObjMap, component2MeshAndMats); + if (!childIsValid) return false; + } + + return true; + } + + private static void CollectAssetReferncesForComponent(Component comp, Dictionary<Component, Component2MeshAndMaterials> propRefereces, bool log = false) + { + SerializedObject srcSO = new SerializedObject(comp); + SerializedProperty prop = srcSO.GetIterator(); + List<Prop2Reference> propRefs = new List<Prop2Reference>(); + SerializedProperty arrayPropParent = null; + if (prop.NextVisible(true)) + { + do + { + if (log && prop.isArray) Debug.Log("Found Array prop: " + prop.propertyPath + " size: " + prop.arraySize); + if (prop.isArray) + { + arrayPropParent = srcSO.FindProperty(prop.propertyPath); + } + + // If is an internal reference + // Or is a reference to a mesh or material that is different. + if (prop.propertyType == SerializedPropertyType.ObjectReference) + { + if (!IsSceneInstanceAsset(prop.objectReferenceValue)) + { + if (log) Debug.Log("Visiting coponent " + comp + " propName: " + prop.name + " propPath: " + prop.propertyPath + " isArray: " + prop.isArray); + // Get some info about the array if this is an element of an array. + string parentArrayPath; + int parentArraySize; + if (arrayPropParent != null && + prop.propertyPath.StartsWith(arrayPropParent.propertyPath + ".Array.data[")) + { + parentArrayPath = arrayPropParent.propertyPath; + parentArraySize = arrayPropParent.arraySize; + } else + { + parentArrayPath = ""; + parentArraySize = -1; + } + + // mesh to materials + Prop2Reference srcComp2Mats = new Prop2Reference() + { + propertyName = prop.propertyPath, + overriddenInSrcInstance = prop.prefabOverride, + obj = prop.objectReferenceValue, + parentArrayName = parentArrayPath, + parentArraySize = parentArraySize, + }; + + propRefs.Add(srcComp2Mats); + } + } + } + + while (prop.NextVisible(true)); + } + + if (propRefs.Count > 0) + { + Component2MeshAndMaterials srcComp2Mats = new Component2MeshAndMaterials() + { + component = comp, + props = propRefs, + }; + + propRefereces.Add(srcComp2Mats.component, srcComp2Mats); + } + } + + public static bool IsSceneInstanceAsset(UnityEngine.Object obj) + { + if (obj == null) return true; + string pth = AssetDatabase.GetAssetPath(obj); + if (pth == null || pth.Equals("")) return true; + return false; + } + + private bool CopyGameObjectDifferences(GameObject srcGO, GameObject targGO, + Dictionary<UnityEngine.Object, UnityEngine.Object> src2targetObjMap, + Dictionary<Component, Component2MeshAndMaterials> component2MeshAndMats) + { + Component[] srcComps = srcGO.GetComponents<Component>(); + Component[] targComps = targGO.GetComponents<Component>(); + if (srcComps.Length != targComps.Length) + { + AddError(srcGO, "Source GameObject had a different number of components than target."); + return false; + } + + for (int i = 0; i < srcComps.Length; i++) + { + if (srcComps[i].GetType() == targComps[i].GetType()) + { + List<Prop2Reference> targetInernalRefs = new List<Prop2Reference>(); + SerializedObject serializedObject = new SerializedObject(targComps[i]); + + // Go through target and find all references, check if these are refs to internal gameObjects/components of the prefab + // snapshot all internal refs. + { + SerializedProperty arrayPropParent = null; + SerializedProperty prop = serializedObject.GetIterator(); + if (prop.NextVisible(true)) + { + do + { + // Get some info about the array if this is an element of an array. + string parentArrayPath; + int parentArraySize; + if (arrayPropParent != null && + prop.propertyPath.StartsWith(arrayPropParent.propertyPath + ".Array.data[")) + { + parentArrayPath = arrayPropParent.propertyPath; + parentArraySize = arrayPropParent.arraySize; + } + else + { + parentArrayPath = ""; + parentArraySize = -1; + } + + // If is an internal reference + // Or is a reference to a mesh or material that is different. + if (prop.propertyType == SerializedPropertyType.ObjectReference && + src2targetObjMap.ContainsValue(prop.objectReferenceValue)) + { + Prop2Reference p2r = new Prop2Reference() + { + propertyName = prop.propertyPath, + obj = prop.objectReferenceValue, + parentArrayName = parentArrayPath, + parentArraySize = parentArraySize, + }; + + targetInernalRefs.Add(p2r); + } + } + + while (prop.NextVisible(true)); + } + } + + EditorUtility.CopySerializedIfDifferent(srcComps[i], targComps[i]); + serializedObject.Update(); + + // Restore internal references. + for (int refIdx = 0; refIdx < targetInernalRefs.Count; refIdx++) + { + Prop2Reference p2r = targetInernalRefs[refIdx]; + SerializedProperty sp = serializedObject.FindProperty(p2r.propertyName); + sp.objectReferenceValue = targetInernalRefs[refIdx].obj; + // The copy may have resized arrays. Restore the array size from the prefab. + if (p2r.parentArraySize != -1) + { + sp = serializedObject.FindProperty(p2r.parentArrayName); + sp.arraySize = p2r.parentArraySize; + } + } + + // Restore references to project assets + // Don't restore renderer assets because these are materials and go with the meshes. + { + if (component2MeshAndMats.ContainsKey(targComps[i])) + { + Component2MeshAndMaterials meshAndMats = component2MeshAndMats[targComps[i]]; + SerializedObject targSO = new SerializedObject(meshAndMats.component); + for (int prpIdx = 0; prpIdx < meshAndMats.props.Count; prpIdx++) + { + Prop2Reference p2r = meshAndMats.props[prpIdx]; + SerializedProperty prop = targSO.FindProperty(p2r.propertyName); + Debug.Log("Restoring asset refs for component " + targComps[i] + " nm " + prop.name + " parentArray " + p2r.parentArrayName + " arraySize" + p2r.parentArraySize); + prop.objectReferenceValue = meshAndMats.props[prpIdx].obj; + // The copy may have resized arrays. Restore the array size from the prefab. + if (p2r.parentArraySize != -1) + { + prop = targSO.FindProperty(p2r.parentArrayName); + prop.arraySize = p2r.parentArraySize; + } + } + + targSO.ApplyModifiedPropertiesWithoutUndo(); + } + } + + serializedObject.ApplyModifiedProperties(); + } + else + { + AddError(srcGO, "Components did not match"); + return false; + } + } + + return true; + } + + private bool VisitObj(GameObject srcRoot, GameObject srcChildObj, GameObject targRoot, + Dictionary<UnityEngine.Object, UnityEngine.Object> src2targetObjMap, + Dictionary<Component, Component2MeshAndMaterials> component2MeshAndMats) + { + Debug.Assert(replaceEnforceStructure, "Should only be called if enforcing structure."); + // Ensure that it has the same hierarchy and every game object hase the same components + Transform targChildTr = FindCorrespondingTransform(srcRoot.transform, srcChildObj.transform, targRoot.transform); + + if (targChildTr != null) + { + targChildTr.gameObject.layer = srcChildObj.gameObject.layer; + targChildTr.gameObject.tag = srcChildObj.gameObject.tag; + GameObjectUtility.SetStaticEditorFlags(targChildTr.gameObject, GameObjectUtility.GetStaticEditorFlags(srcChildObj.gameObject)); + if (!CopyGameObjectDifferences(srcChildObj, targChildTr.gameObject, src2targetObjMap, component2MeshAndMats)) + { + return false; + } + } + else + { + AddError(srcRoot, "PrefabInstance " + srcRoot + " had an child " + srcChildObj + " that was not found in the target prefab."); + return false; + } + + foreach (Transform tr in srcChildObj.transform) + { + if (!VisitObj(srcRoot, tr.gameObject, targRoot, src2targetObjMap, component2MeshAndMats)) + { + return false; + } + } + + return true; + } + + private static Transform FindCorrespondingTransform(Transform srcRoot, Transform srcChild, + Transform targRoot) + { + if (srcRoot == srcChild) return targRoot; + + //build the path to the root in the source prefab + List<Transform> path_root2child = new List<Transform>(); + Transform t = srcChild; + do + { + path_root2child.Insert(0, t); + t = t.parent; + } while (t != null && t != t.root && t != srcRoot); + if (t == null) + { + Debug.LogError("scrChild was not child of srcRoot " + srcRoot + " " + srcChild); + return null; + } + path_root2child.Insert(0, srcRoot); + + //try to find a matching path in the target prefab + t = targRoot; + for (int i = 1; i < path_root2child.Count; i++) + { + Transform tSrc = path_root2child[i - 1]; + //try to find child in same position with same name + int srcIdx = TIndexOf(tSrc, path_root2child[i]); + if (srcIdx < t.childCount && path_root2child[i].name.Equals(t.GetChild(srcIdx).name)) + { + t = t.GetChild(srcIdx); + continue; + } + //try to find child with same name + for (int j = 0; j < t.childCount; j++) + { + if (t.GetChild(j).name.Equals(path_root2child[i].name)) + { + t = t.GetChild(j); + continue; + } + } + t = null; + break; + } + + + return t; + } + + private static int TIndexOf(Transform p, Transform c) + { + for (int i = 0; i < p.childCount; i++) + { + if (c == p.GetChild(i)) + { + return i; + } + } + return -1; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_ReplacePrefabsInScene.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_ReplacePrefabsInScene.cs.meta new file mode 100644 index 00000000..6b67d038 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/core/MB_ReplacePrefabsInScene.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5a70ad841248acf4ab4bdadc52c88841 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers.meta new file mode 100644 index 00000000..523ec449 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c307e366f91a4284d810409a68c5b7bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB3_ShaderTexturePropertyDrawer.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB3_ShaderTexturePropertyDrawer.cs new file mode 100644 index 00000000..72ddab56 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB3_ShaderTexturePropertyDrawer.cs @@ -0,0 +1,31 @@ +using UnityEditor; +using UnityEngine; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + [CustomPropertyDrawer(typeof(ShaderTextureProperty))] + public class MB3_ShaderTexturePropertyDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + label = EditorGUI.BeginProperty(position, label, property); + Rect contentPosition = EditorGUI.PrefixLabel(position, label); + if (position.height > 16f) + { + position.height = 16f; + EditorGUI.indentLevel += 1; + contentPosition = EditorGUI.IndentedRect(position); + contentPosition.y += 18f; + } + contentPosition.width *= 0.75f; + EditorGUI.indentLevel = 0; + EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("name"), GUIContent.none); + contentPosition.x += contentPosition.width; + contentPosition.width /= 3f; + EditorGUIUtility.labelWidth = 50f; + EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("isNormalMap"), new GUIContent("isBump")); + EditorGUI.EndProperty(); + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB3_ShaderTexturePropertyDrawer.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB3_ShaderTexturePropertyDrawer.cs.meta new file mode 100644 index 00000000..54705e92 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB3_ShaderTexturePropertyDrawer.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 24ced5fa006456249ac31e87abe5aa5b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_ConvertTextureArrayFormatWizard.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_ConvertTextureArrayFormatWizard.cs new file mode 100644 index 00000000..b20869d9 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_ConvertTextureArrayFormatWizard.cs @@ -0,0 +1,65 @@ +using UnityEditor; +using UnityEngine; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.MBEditor +{ + public class MB_ConvertTextureArrayFormatWizard : ScriptableWizard + { + public Texture2DArray textureArray; + public TextureFormat format = TextureFormat.ARGB32; + + [MenuItem("Window/Mesh Baker/TextureArray Format Converter")] + static void CreateWizard() + { + ScriptableWizard.DisplayWizard<MB_ConvertTextureArrayFormatWizard>("Convert Texture Array Format", "Close", "Convert"); + } + + void OnWizardCreate() + { + + } + + void OnWizardUpdate() + { + helpString = "Please assign a texture array"; + } + + void OnWizardOtherButton() + { + helpString = ""; + if (textureArray == null) + { + helpString = "Please assign a texture array"; + return; + } + + MB3_EditorMethods editorMethods = new MB3_EditorMethods(); + if (!editorMethods.TextureImporterFormatExistsForTextureFormat(format)) + { + helpString = "No ImporterFormat exists for the selected format. Please select a different format."; + return; + } + + if (textureArray.format != TextureFormat.ARGB32 && + textureArray.format != TextureFormat.RGB24) + { + helpString = "Source TextureArray must be in format ARGB32 or RGB24. This will probably be changed in" + + "a future version of Mesh Baker."; + return; + } + + Texture2DArray outArray = new Texture2DArray(textureArray.width, textureArray.height, textureArray.depth, format, true); + if (editorMethods.ConvertTexture2DArray(textureArray, outArray, format)) + { + string pth = UnityEditor.AssetDatabase.GetAssetPath(textureArray); + if (pth == null) pth = "Assets/TextureArray.asset"; + pth = pth.Replace(".asset", ""); + pth += format.ToString() + ".asset"; + UnityEditor.AssetDatabase.CreateAsset(outArray, pth); + Debug.Log("Convert success saved asset: " + pth); + } + } + } +} + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_ConvertTextureArrayFormatWizard.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_ConvertTextureArrayFormatWizard.cs.meta new file mode 100644 index 00000000..5603530c --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_ConvertTextureArrayFormatWizard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d355da520c795f543a934da176eb59ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_PrefabPairPropertyDrawer.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_PrefabPairPropertyDrawer.cs new file mode 100644 index 00000000..3c1d9978 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_PrefabPairPropertyDrawer.cs @@ -0,0 +1,82 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using DigitalOpus.MB.MBEditor; + +namespace DigitalOpus.MB.MBEditor +{ + /// <summary> + /// Draws rows for the PrafabPairs in the Switch Prefabs In Scene Window. + /// </summary> + [CustomPropertyDrawer(typeof(MB_ReplacePrefabsSettings.PrefabPair))] + public class MB_PrefabPairPropertyDrawer : PropertyDrawer + { + private GUIStyle redFont = new GUIStyle(); + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); + int indent = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + + float xx = position.x; + float yy = position.y; + float ww = 15f; + float hh = position.height; + + Rect enabledRect = new Rect(xx, yy, ww, EditorGUIUtility.singleLineHeight); + xx += ww; + ww = 50; + Rect srcLabel = new Rect(xx, yy, ww, EditorGUIUtility.singleLineHeight); + xx += ww; + ww = 200; + Rect sourceRect = new Rect(xx, yy, ww, EditorGUIUtility.singleLineHeight); + xx += ww; + ww = 50; + Rect targetLabel = new Rect(xx, yy, ww, EditorGUIUtility.singleLineHeight); + xx += ww; + ww = 200; + Rect targetRect = new Rect(xx, yy, ww, EditorGUIUtility.singleLineHeight); + + EditorGUI.PropertyField(enabledRect, property.FindPropertyRelative("enabled"), GUIContent.none); + EditorGUI.LabelField(srcLabel, "Source:"); + EditorGUI.PropertyField(sourceRect, property.FindPropertyRelative("srcPrefab"), GUIContent.none); + EditorGUI.LabelField(targetLabel, "Target:"); + EditorGUI.PropertyField(targetRect, property.FindPropertyRelative("targPrefab"), GUIContent.none); + + SerializedProperty errorsProp = property.serializedObject.FindProperty(property.propertyPath + ".objsWithErrors"); + if (errorsProp.arraySize > 0) + { + redFont.normal.textColor = Color.red; + for (int i = 0; i < errorsProp.arraySize; i++) + { + xx = position.x; + yy = position.y + (1 + i) * EditorGUIUtility.singleLineHeight; + ww = 100; + SerializedProperty errProp = errorsProp.GetArrayElementAtIndex(i); + GameObject obj = (GameObject)errProp.FindPropertyRelative("errorObj").objectReferenceValue; + string errStr = errProp.FindPropertyRelative("error").stringValue; + Rect buttonRect = new Rect(xx, yy, ww, EditorGUIUtility.singleLineHeight); + if (GUI.Button(buttonRect, "Select")) + { + if (obj != null) Selection.activeGameObject = obj; + } + xx += ww; + ww = 500; + EditorGUI.LabelField(new Rect(xx, yy, ww, EditorGUIUtility.singleLineHeight), errStr, redFont); + } + } + + EditorGUI.indentLevel = indent; + EditorGUI.EndProperty(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + SerializedProperty errorsProp = property.serializedObject.FindProperty(property.propertyPath + ".objsWithErrors"); + return base.GetPropertyHeight(property, label) + errorsProp.arraySize * EditorGUIUtility.singleLineHeight; + } + } +} diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_PrefabPairPropertyDrawer.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_PrefabPairPropertyDrawer.cs.meta new file mode 100644 index 00000000..741dc8a6 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/propertyDrawers/MB_PrefabPairPropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f8fdae7bfb5aa3a41ab365cd5a8238c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters.meta new file mode 100644 index 00000000..aa71d3b5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 31b5ad3e3da6e4244bdaa2a76ab994b6 +folderAsset: yes +DefaultImporter: + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByAlreadyAdded.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByAlreadyAdded.cs new file mode 100644 index 00000000..0e226e39 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByAlreadyAdded.cs @@ -0,0 +1,39 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class GroupByAlreadyAdded : IGroupByFilter + { + public string GetName() + { + return "AlreadyAdded"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return "alreadyAdded=" + fi.alreadyInBakerList; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + int alreadyAddedCompare = 0; + if (b.alreadyInBakerList == true && a.alreadyInBakerList == false) + { + alreadyAddedCompare = -1; + } + if (b.alreadyInBakerList == false && a.alreadyInBakerList == true) + { + alreadyAddedCompare = 1; + } + return alreadyAddedCompare; + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByAlreadyAdded.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByAlreadyAdded.cs.meta new file mode 100644 index 00000000..1eacfccd --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByAlreadyAdded.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac8e94c94918f5245823661062e06239 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByEnabledDisabled.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByEnabledDisabled.cs new file mode 100644 index 00000000..3a0f17b2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByEnabledDisabled.cs @@ -0,0 +1,39 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class GroupByEnabledDisabled : IGroupByFilter + { + public string GetName() + { + return "Is Enabled"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return fi.go.activeInHierarchy ? "Enabled" : "Disabled"; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + int compareVal = 0; + if (b.go.activeInHierarchy == true && a.go.activeInHierarchy == false) + { + compareVal = -1; + } + if (b.go.activeInHierarchy == false && a.go.activeInHierarchy == true) + { + compareVal = 1; + } + return compareVal; + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByEnabledDisabled.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByEnabledDisabled.cs.meta new file mode 100644 index 00000000..04bac058 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByEnabledDisabled.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 000b6aa8344585d4fb87fb96b8149202 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByLightmapIndex.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByLightmapIndex.cs new file mode 100644 index 00000000..7c310264 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByLightmapIndex.cs @@ -0,0 +1,30 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class GroupByLightmapIndex : IGroupByFilter + { + public string GetName() + { + return "Lightmap Index"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return "lightmapIndex=" + fi.lightmapIndex; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + return b.lightmapIndex - a.lightmapIndex; + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByLightmapIndex.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByLightmapIndex.cs.meta new file mode 100644 index 00000000..048289e3 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByLightmapIndex.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8add88348b69079419318c17a4aec61a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByMaterial.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByMaterial.cs new file mode 100644 index 00000000..7745a04f --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByMaterial.cs @@ -0,0 +1,29 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + public class GroupByMaterial : IGroupByFilter + { + public string GetName() + { + return "Material"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return "material=" + fi.materialName; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + return a.materialName.CompareTo(b.materialName); + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByMaterial.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByMaterial.cs.meta new file mode 100644 index 00000000..8aeb4f5c --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByMaterial.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2ff3bf9d7e4fcb64eba007e5d54d5f6a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByOutOfBoundsUVs.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByOutOfBoundsUVs.cs new file mode 100644 index 00000000..63f10b68 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByOutOfBoundsUVs.cs @@ -0,0 +1,30 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class GroupByOutOfBoundsUVs : IGroupByFilter + { + public string GetName() + { + return "OutOfBoundsUVs"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return "OutOfBoundsUVs=" + fi.outOfBoundsUVs; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + return Convert.ToInt32(b.outOfBoundsUVs) - Convert.ToInt32(a.outOfBoundsUVs); + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByOutOfBoundsUVs.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByOutOfBoundsUVs.cs.meta new file mode 100644 index 00000000..dc0c9b71 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByOutOfBoundsUVs.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 29fb03f40f7582c41a09dec611a68e8c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByRenderType.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByRenderType.cs new file mode 100644 index 00000000..e065c743 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByRenderType.cs @@ -0,0 +1,39 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class GroupByRenderType : IGroupByFilter + { + public string GetName() + { + return "RenderType"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return fi.isMeshRenderer ? "MeshRenderer" : "SkinnedMeshRenderer"; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + int renderTypeCompare = 0; + if (b.isMeshRenderer == true && a.isMeshRenderer == false) + { + renderTypeCompare = -1; + } + if (b.isMeshRenderer == false && a.isMeshRenderer == true) + { + renderTypeCompare = 1; + } + return renderTypeCompare; + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByRenderType.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByRenderType.cs.meta new file mode 100644 index 00000000..c0dac2ef --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByRenderType.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ccb4d2ba98c9989449e34934c09ec5ca +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByShader.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByShader.cs new file mode 100644 index 00000000..6420e898 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByShader.cs @@ -0,0 +1,31 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class GroupByShader : IGroupByFilter + { + public string GetName() + { + return "Shader"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return "shader=" + fi.shaderName; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + return a.shaderName.CompareTo(b.shaderName); + } + } + +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByShader.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByShader.cs.meta new file mode 100644 index 00000000..57675a34 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByShader.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f0f3a86ced986a4aa686551a1c3a4ad +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStandardShaderType.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStandardShaderType.cs new file mode 100644 index 00000000..d42fe4cc --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStandardShaderType.cs @@ -0,0 +1,31 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class MB3_GroupByStandardShaderType : IGroupByFilter + { + public string GetName() + { + return "Standard Rendering Mode"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return "renderingMode=" + fi.standardShaderBlendModesName; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + return a.standardShaderBlendModesName.CompareTo(b.standardShaderBlendModesName); + } + } + +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStandardShaderType.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStandardShaderType.cs.meta new file mode 100644 index 00000000..b3cf3d1d --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStandardShaderType.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b9be5ed7795b65d448a3cd700e472f8a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStatic.cs b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStatic.cs new file mode 100644 index 00000000..e0f6cba0 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStatic.cs @@ -0,0 +1,39 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using DigitalOpus.MB.Core; + +namespace DigitalOpus.MB.Core +{ + + public class GroupByStatic : IGroupByFilter + { + public string GetName() + { + return "Static"; + } + + public string GetDescription(GameObjectFilterInfo fi) + { + return "isStatic=" + fi.isStatic; + } + + public int Compare(GameObjectFilterInfo a, GameObjectFilterInfo b) + { + int staticCompare = 0; + if (b.isStatic == true && a.isStatic == false) + { + staticCompare = -1; + } + if (b.isStatic == false && a.isStatic == true) + { + staticCompare = 1; + } + return staticCompare; + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStatic.cs.meta b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStatic.cs.meta new file mode 100644 index 00000000..23569a68 --- /dev/null +++ b/VRCSDK3Worlds/Assets/MeshBaker/Editor/searchFilters/MB3_GroupByStatic.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e91e5646c425c8428d175ac3fe62800 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: |