rabidus-test/Assets/PolyFew/Scripts/Editor/InspectorDrawer.cs

7188 lines
319 KiB
C#
Raw Normal View History

2023-10-20 13:29:44 +03:00
//////////////////////////////////////////////////////
// Copyright (c) BrainFailProductions
//////////////////////////////////////////////////////
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.Linq;
using UnityEditor.Callbacks;
using static BrainFailProductions.PolyFew.UtilityServices;
using UnityEditor.SceneManagement;
using System.IO;
using Resolution = BrainFailProductions.PolyFew.CombiningInformation.Resolution;
using CompressionType = BrainFailProductions.PolyFew.CombiningInformation.CompressionType;
using static BrainFailProductions.PolyFew.CombiningInformation;
using System.Threading.Tasks;
using static BrainFailProductions.PolyFew.DataContainer;
using UnityEngine.SceneManagement;
using UnityEditor.Presets;
using System.Threading;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
namespace BrainFailProductions.PolyFew
{
[CanEditMultipleObjects]
[CustomEditor(typeof(PolyFew))]
public class InspectorDrawer: Editor
{
private static bool PreserveBorders { get { return dataContainer.preserveBorders; } set { dataContainer.preserveBorders = value; } }
private static bool PreserveUVSeams { get { return dataContainer.preserveUVSeams; } set { dataContainer.preserveUVSeams = value; } }
private static bool PreserveUVFoldover { get { return dataContainer.preserveUVFoldover; } set { dataContainer.preserveUVFoldover = value; } }
private static bool UseEdgeSort { get { return dataContainer.useEdgeSort; } set { dataContainer.useEdgeSort = value; } }
private static bool RecalculateNormals { get { return dataContainer.recalculateNormals; } set { dataContainer.recalculateNormals = value; } }
private static int MaxIterations { get { return dataContainer.maxIterations; } set { dataContainer.maxIterations = value; } }
private static float Aggressiveness { get { return dataContainer.aggressiveness; } set { dataContainer.aggressiveness = value; } }
private static bool ConsiderChildren { get { return dataContainer.considerChildren; } set { dataContainer.considerChildren = value; } }
private static bool RegardCurvature { get { return dataContainer.regardCurvature; } set { dataContainer.regardCurvature = value; } }
private static int TriangleCount { get { return dataContainer.triangleCount; } set { dataContainer.triangleCount = value; } }
private static float ReductionStrength { get { return dataContainer.reductionStrength; } set { dataContainer.reductionStrength = value; } }
private static bool FoldoutAutoLOD { get { return dataContainer.foldoutAutoLOD; } set { dataContainer.foldoutAutoLOD = value; } }
private static bool FoldoutBatchFew { get { return dataContainer.foldoutBatchFew; } set { dataContainer.foldoutBatchFew = value; } }
private static bool IsPreservationActive { get { return dataContainer.isPreservationActive; } set { dataContainer.isPreservationActive = value; } }
private static float SphereDefaultDiameter { get { return dataContainer.sphereDiameter; } set { dataContainer.sphereDiameter = value; } }
private static bool isFeasibleTargetForPolyFew;
private static string sphereColHex = "#FBFF00C8";
private static Color sphereDefaultColor = UtilityServices.HexToColor(sphereColHex);
private static Texture icon;
private static bool toolMainFoldout = true;
private const string ICONS_PATH = "Assets/PolyFew/icons/";
private const string SPHERE_PRESETS_PATH = "Assets/PolyFew/SpherePresets/";
#pragma warning disable
private bool isVersionOk = true;
private GameObject thisGameObject;
private static UnityEngine.Object LastDrawer { get { if (dataContainer == null) { return null; }; return dataContainer.lastDrawer; } set { dataContainer.lastDrawer = value; } }
private bool areAllMeshesSaved;
private bool applyForOptionsChange;
private bool ReductionPending { get { return dataContainer.reductionPending; } set { dataContainer.reductionPending = value; } }
private GameObject PrevFeasibleTarget { get { return dataContainer.prevFeasibleTarget; } set { dataContainer.prevFeasibleTarget = value; } }
private static bool RunOnThreads { get { return dataContainer.runOnThreads; } set { dataContainer.runOnThreads = value; } }
private static Vector3 ObjPositionPrevFrame { get { return dataContainer.objPositionPrevFrame; } set { dataContainer.objPositionPrevFrame = value; } }
private static Vector3 ObjScalePrevFrame { get { return dataContainer.objScalePrevFrame; } set { dataContainer.objScalePrevFrame = value; } }
private static bool ConsiderChildrenBatchFew { get { return dataContainer.considerChildrenBatchFew; } set { dataContainer.considerChildrenBatchFew = value; } }
private static bool FoldoutAdditionalOpts { get { return dataContainer.foldoutAdditionalOpts; } set { dataContainer.foldoutAdditionalOpts = value; } }
private static bool GenerateUV2LODs { get { return dataContainer.generateUV2LODs; } set { dataContainer.generateUV2LODs = value; } }
private static bool CopyParentStaticFlags { get { return dataContainer.copyParentStaticFlags; } set { dataContainer.copyParentStaticFlags = value; } }
private static bool CopyParentTag { get { return dataContainer.copyParentTag; } set { dataContainer.copyParentTag = value; } }
private static bool CopyParentLayer { get { return dataContainer.copyParentLayer; } set { dataContainer.copyParentLayer = value; } }
private static bool CreateAsChildren { get { return dataContainer.createAsChildren; } set { dataContainer.createAsChildren = value; } }
private static bool RemoveLODBackupComponent { get { return dataContainer.removeLODBackupComponent; } set { dataContainer.removeLODBackupComponent = value; } }
private static bool GenerateUV2batchfew { get { return dataContainer.generateUV2batchfew; } set { dataContainer.generateUV2batchfew = value; } }
private static bool RemoveMaterialLinkComponent { get { return dataContainer.removeMaterialLinksComponent; } set { dataContainer.removeMaterialLinksComponent = value; } }
private static bool ClearBlendshapesComplete { get { return dataContainer.clearBlendshapesComplete; } set { dataContainer.clearBlendshapesComplete = value; } }
private static bool ClearBlendshapesNormals { get { return dataContainer.clearBlendshapesNormals; } set { dataContainer.clearBlendshapesNormals = value; } }
private static bool ClearBlendshapesTangents { get { return dataContainer.clearBlendshapesTangents; } set { dataContainer.clearBlendshapesTangents = value; } }
private static List<Texture2DArray> existingTextureArrays { get { return dataContainer.existingTextureArrays; } set { dataContainer.existingTextureArrays = value; } }
private static bool existingTextureArraysFoldout { get { return dataContainer.existingTextureArraysFoldout; } set { dataContainer.existingTextureArraysFoldout = value; } }
private static int existingTextureArraysSize { get { return dataContainer.existingTextureArraysSize; } set { dataContainer.existingTextureArraysSize = value; } }
private static bool textureArraysPropsFoldout { get { return dataContainer.textureArraysPropsFoldout; } set { dataContainer.textureArraysPropsFoldout = value; } }
private static TextureArrayUserSettings existingArraysProps { get { return dataContainer.existingArraysProps; } set { dataContainer.existingArraysProps = value; } }
#pragma warning disable
private readonly System.Object threadLock1 = new System.Object();
#pragma warning disable
private readonly System.Object threadLock2 = new System.Object();
private Func<bool> CheckOnThreads = new Func<bool>(() => { return (TriangleCount >= 1500 && dataContainer.objectMeshPairs.Count >= 2); });
private static bool areMultiObjectsSelected;
private static bool FoldAutoLODMultiple { get { return dataContainer.foldoutAutoLODMultiple; } set { dataContainer.foldoutAutoLODMultiple = value; } }
private static string UnityVersion { get { return Application.unityVersion.Trim(); } }
private static bool isPlainSkin { get { return dataContainer.isPlainSkin; } set { dataContainer.isPlainSkin = value; } }
private static bool didPressButton;
private static Color originalColor;
private static List<GameObject> lastMultiSelectedObjects;
private int width;
private GUIStyle style;
private GUIContent content;
private RectOffset prevPadding;
private static Rect lastRect;
private static UndoRedoOps objectsHistory;
private static ObjectMeshPair objectMeshPairs;
private static LODBackup lodBackup;
private static List<MaterialProperties> materialsToRestore;
private static ObjectMaterialLinks lastObjMaterialLinks;
private static DataContainer dataContainer { get { return UtilityServices.dataContainer; } set { UtilityServices.dataContainer = value; } }
void OnEnable()
{
//Debug.Log("OnEnable called on InspectorDrawer for POLYFEW");
if (!Application.isEditor || Application.isPlaying)
{
isFeasibleTargetForPolyFew = false;
}
// For multiple selections
if (Selection.gameObjects != null && Selection.gameObjects.Length > 1)
{
isFeasibleTargetForPolyFew = false;
foreach (GameObject selected in Selection.gameObjects)
{
if (selected.activeSelf && selected.GetComponent<PolyFew>() != null)
{
dataContainer = selected.GetComponent<PolyFew>().dataContainer;
if (dataContainer.currentLodLevelSettings == null || dataContainer.currentLodLevelSettings.Count == 0)
{
dataContainer.currentLodLevelSettings = new List<DataContainer.LODLevelSettings>();
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(0, 0.6f, false, false, false, true, false, 7, 100, false, false, false, new List<float>()));
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(30, 0.4f, false, false, false, true, false, 7, 100, false, false, false, new List<float>()));
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(60, 0.15f, false, false, false, true, false, 7, 100, false, false, false, new List<float>()));
}
if (dataContainer.objectsHistory == null)
{
dataContainer.objectsHistory = new DataContainer.UndoRedoOps(selected, new List<DataContainer.ObjectHistory>(), new List<DataContainer.ObjectHistory>());
}
if (dataContainer.toleranceSpheres == null)
{
dataContainer.toleranceSpheres = new List<ToleranceSphere>();
}
}
}
return;
}
// For single selection
else if(Selection.activeGameObject != null && Selection.activeGameObject.GetComponent<PolyFew>() != null)
{
isFeasibleTargetForPolyFew = UtilityServices.CheckIfFeasible(Selection.activeTransform);
UtilityServices.maxConcurrentThreads = SystemInfo.processorCount * 2;
isVersionOk = UnityVersion.Contains("2017.1") || UnityVersion.Contains("2017.2") ? false : true;
if (UnityVersion.Contains("2015")) { isVersionOk = false; }
Selection.selectionChanged -= SelectionChanged;
Selection.selectionChanged += SelectionChanged;
thisGameObject = Selection.activeGameObject;
dataContainer = thisGameObject.GetComponent<PolyFew>().dataContainer;
if (dataContainer.objectsHistory == null)
{
dataContainer.objectsHistory = new DataContainer.UndoRedoOps(thisGameObject, new List<DataContainer.ObjectHistory>(), new List<DataContainer.ObjectHistory>());
}
if (dataContainer.toleranceSpheres == null)
{
dataContainer.toleranceSpheres = new List<ToleranceSphere>();
}
if (dataContainer.textureArraysSettings == null)
{
ResetTextureArrays();
}
#region Restoring persistent data
UtilityServices.AutoLODSavePath = EditorPrefs.HasKey("autoLODSavePath") ? EditorPrefs.GetString("autoLODSavePath") : SetAndReturnStringPref("autoLODSavePath", "");
UtilityServices.BatchFewSavePath = EditorPrefs.HasKey("batchFewSavePath") ? EditorPrefs.GetString("batchFewSavePath") : SetAndReturnStringPref("batchFewSavePath", "");
string hex = EditorPrefs.HasKey("sphereColHex") ? EditorPrefs.GetString("sphereColHex") : SetAndReturnStringPref("sphereColHex", sphereColHex);
sphereDefaultColor = UtilityServices.HexToColor(hex);
isPlainSkin = EditorPrefs.HasKey("isPlainSkin") ? EditorPrefs.GetBool("isPlainSkin") : SetAndReturnBoolPref("isPlainSkin", false);
#endregion Restoring persistent data
LastDrawer = this;
SelectionChanged();
if (isFeasibleTargetForPolyFew)
{
ObjPositionPrevFrame = Selection.activeTransform.position;
ObjScalePrevFrame = Selection.activeTransform.lossyScale;
}
if (Selection.gameObjects == null || Selection.gameObjects.Length == 1)
{
isFeasibleTargetForPolyFew = UtilityServices.CheckIfFeasible(Selection.activeTransform);
}
}
}
void OnDisable()
{
if (!Application.isEditor || Application.isPlaying)
{
isFeasibleTargetForPolyFew = false;
isFeasibleTargetForPolyFew = false;
return;
}
//Debug.Log("OnDisable called on InspectorDrawer for POLYFEW");
Selection.selectionChanged -= SelectionChanged;
if (target == null)
{
//Debug.Log("PolyFewHost Component removed from inspector by user");
}
}
public void OnDestroy()
{
if (!Application.isEditor || Application.isPlaying)
{
isFeasibleTargetForPolyFew = false;
return;
}
if (ReductionPending)
{
UtilityServices.RestorePolyFewGameObjects(new GameObject[] { thisGameObject });
}
}
void OnSceneGUI()
{
if (isFeasibleTargetForPolyFew && Selection.activeTransform != null)
{
PrevFeasibleTarget = Selection.activeTransform.gameObject;
#region Draw custom handles for preservation sphere
if (dataContainer.toleranceSpheres != null && IsPreservationActive)
{
foreach (var toleranceSphere in dataContainer.toleranceSpheres)
{
//Handles.color = toleranceSphere.color;
//Handles.DrawSphere(-1, toleranceSphere.worldPosition, Quaternion.identity, toleranceSphere.diameter);
//Gizmos.color = toleranceSphere.color;
//Gizmos.DrawSphere(toleranceSphere.worldPosition, toleranceSphere.diameter / 2f);
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
Handles.color = toleranceSphere.color;
if (!toleranceSphere.isHidden)
{
Handles.SphereHandleCap(-1, toleranceSphere.worldPosition, Quaternion.identity, toleranceSphere.diameter, EventType.Repaint);
if (Tools.current == Tool.Move)
{
// the reference to the scriptable object might be null
if (toleranceSphere != null)
{
Undo.RecordObject(toleranceSphere, "Tolerance Sphere Change Move");
}
Vector3 objCurrentPos = Selection.activeTransform.position;
Vector3 change = objCurrentPos - ObjPositionPrevFrame;
toleranceSphere.worldPosition += change;
Vector3 prevPos = toleranceSphere.worldPosition;
toleranceSphere.worldPosition = Handles.DoPositionHandle(toleranceSphere.worldPosition, Quaternion.identity);
}
else if (Tools.current == Tool.Scale)
{
// the reference to the scriptable object might be null
if (toleranceSphere != null)
{
Undo.RecordObject(toleranceSphere, "Tolerance Sphere Change Scale");
}
Vector3 originalPos = toleranceSphere.worldPosition;
Vector3 objCurrentScale = Selection.activeTransform.lossyScale;
Vector3 change = objCurrentScale - ObjScalePrevFrame;
float oldDiameter = toleranceSphere.diameter;
Vector3 newScale = new Vector3(oldDiameter, oldDiameter, oldDiameter) + change;
toleranceSphere.diameter = UtilityServices.Average(newScale.x, newScale.y, newScale.z);
newScale = new Vector3(toleranceSphere.diameter, toleranceSphere.diameter, toleranceSphere.diameter);
newScale = Handles.DoScaleHandle(newScale, toleranceSphere.worldPosition, Quaternion.identity, HandleUtility.GetHandleSize(toleranceSphere.worldPosition));
toleranceSphere.diameter = UtilityServices.Average(newScale.x, newScale.y, newScale.z);
toleranceSphere.worldPosition = originalPos;
}
}
}
}
#endregion Draw custom handles for preservation sphere
ObjPositionPrevFrame = Selection.activeTransform.position;
ObjScalePrevFrame = Selection.activeTransform.lossyScale;
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (Selection.activeGameObject == null) { return; }
if(!Selection.activeGameObject.activeSelf) { return; }
if (Event.current.type == EventType.DragUpdated)
{
objectsHistory = dataContainer.objectsHistory;
objectMeshPairs = dataContainer.objectMeshPairs;
lodBackup = dataContainer.lodBackup;
materialsToRestore = dataContainer.materialsToRestore;
lastObjMaterialLinks = dataContainer.lastObjMaterialLinks;
}
// When a preset is applied it sets the object mesh pairs to null
// So unless you reselect the object the object mesh pairs are null
// Reduction slider does nothing. So always check
// Also other variables that are specific to an object are null inside the preset
// This causes all of those to get null on the applied object as well. So we need to restore them
if (Event.current.type == EventType.DragExited)
{
DelayAssignVariablesAfterPreset();
}
if (isFeasibleTargetForPolyFew)
{
toolMainFoldout = EditorGUILayout.Foldout(toolMainFoldout, "");
EditorGUILayout.BeginVertical("GroupBox");
GUIStyle oldStyle;
#region Title Header
EditorGUILayout.BeginHorizontal();
content = new GUIContent();
string saveMeshPath = ICONS_PATH + "icon.png";
icon = EditorGUIUtility.Load(saveMeshPath) as Texture;
if (icon) GUILayout.Label(icon, GUILayout.Width(30), GUILayout.MaxHeight(30));
GUILayout.Space(6);
EditorGUILayout.BeginVertical();
GUILayout.Space(8);
var style = GUI.skin.label;
style.richText = true; // #FF6347ff4
if(isPlainSkin)
{
if (GUILayout.Button("<size=14><b>POLY FEW</b></size> <size=7><b>v7.65</b></size>", style)) { toolMainFoldout = !toolMainFoldout; }
}
else
{
if (GUILayout.Button("<size=14><color=#A52A2AFF><b>POLY FEW</b></color></size> <size=7><b><color=#A52A2AFF>v7.65</color></b></size>", style)) { toolMainFoldout = !toolMainFoldout; }
}
EditorGUILayout.EndVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginVertical();
GUILayout.Space(5);
string iconPath = "";
if (isPlainSkin)
{
iconPath = ICONS_PATH + "theme-white.png";
content.tooltip = "You are currently using the plain skin. Click this icon to change to default/colorful skin.";
}
else
{
iconPath = ICONS_PATH + "theme-default.png";
content.tooltip = "You are currently using the default/colorful skin. Click this icon to change to plain skin with better visibility in dark mode.";
}
content.image = EditorGUIUtility.Load(iconPath) as Texture;
if (GUILayout.Button(content, GUILayout.Width(24), GUILayout.Height(24)))
{
isPlainSkin = !isPlainSkin;
EditorPrefs.SetBool("isPlainSkin", isPlainSkin);
}
EditorGUILayout.EndVertical();
//GUILayout.Space(1);
EditorGUILayout.BeginVertical();
GUILayout.Space(5);
if (isPlainSkin) { iconPath = ICONS_PATH + "faq-white.png"; }
else { iconPath = ICONS_PATH + "faq-default.png"; }
content.image = EditorGUIUtility.Load(iconPath) as Texture;
content.tooltip = "Open FAQ page";
if (GUILayout.Button(content, GUILayout.Width(24), GUILayout.Height(24)))
{
Application.OpenURL("https://brainfailproductions.000webhostapp.com/polyfew_site");
}
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical();
GUILayout.Space(5);
if (isPlainSkin) { iconPath = ICONS_PATH + "help-white.png"; }
else { iconPath = ICONS_PATH + "help-default.png"; }
content.image = EditorGUIUtility.Load(iconPath) as Texture;
content.tooltip = "Open reference for the runtime API";
if (GUILayout.Button(content, GUILayout.Width(24), GUILayout.Height(24)))
{
Application.OpenURL("https://brainfailproductions.000webhostapp.com/polyfew_runtime_api_docs/html/class_brain_fail_productions_1_1_poly_few_runtime_1_1_polyfew_runtime.html");
}
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical();
GUILayout.Space(5);
#region Additional options
if (isPlainSkin) { iconPath = ICONS_PATH + "settings-white.png"; }
else { iconPath = ICONS_PATH + "settings-default.png"; }
content.tooltip = "Additional tool preferences and operations.";
content.image = EditorGUIUtility.Load(iconPath) as Texture;
if (GUILayout.Button(content, GUILayout.Width(24), GUILayout.Height(24)))
{
if (lastRect != null)
{
lastRect = new Rect(Event.current.mousePosition, lastRect.size);
var definitions = new List<PopupToggleTemplate.ToggleDefinition>();
#if UNITY_2019_1_OR_NEWER
definitions.Add(new PopupToggleTemplate.ToggleDefinition(content, 190, -4, null, null, true, () =>
{
oldStyle = style;
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
content.text = "<size=11><b>Cleanup Missing Scripts</b></size>";
content.tooltip = "Clean missing script references from all GameObjects in all currently open scenes. Any changes to prefabs must be manually applied.";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(140), GUILayout.Height(20), GUILayout.ExpandWidth(true));
if (didPressButton)
{
PolyfewMenu.CleanMissingScripts();
}
style = oldStyle;
return didPressButton;
}));
#endif
definitions.Add(new PopupToggleTemplate.ToggleDefinition(content, 190, -4, null, null, true, () =>
{
oldStyle = style;
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
content = new GUIContent();
content.text = "<size=11><b>Remove Hidden Scripts</b></size>";
content.tooltip = "Remove all hidden Poly Few components/scripts from all GameObjects in all currently open scenes. Any changes to prefabs must be manually applied.";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(140), GUILayout.Height(20), GUILayout.ExpandWidth(true));
if (didPressButton)
{
PolyfewMenu.RemovePolyFewScripts();
}
style = oldStyle;
return didPressButton;
}));
int height = definitions.Count * 24 + 4;
PopupWindow.Show(lastRect, new PopupToggleTemplate(definitions.ToArray(), new Vector2(230, height), null, null));
}
}
if (Event.current.type == EventType.Repaint) lastRect = GUILayoutUtility.GetLastRect();
oldStyle = style;
#endregion Additional options
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
#endregion Title Header
if (toolMainFoldout)
{
UtilityServices.DrawHorizontalLine(Color.black, 1, 8);
#region Section Header
GUILayout.Space(10);
EditorGUILayout.BeginHorizontal();
#region Go Deep
content = new GUIContent();
style = GUI.skin.textField;
style.richText = true;
RectOffset oldPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
style.padding = new RectOffset(6, style.padding.right, 3, style.padding.bottom);
content.text = "<b>Consider Children</b>";
content.tooltip = "Check this option to consider the deep nested child hierarchy during reduction and other operations. If this option is unchecked then an operation only considers the currently selected object. This might be slow for complex object hierarchies containing lots of meshes.";
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
GUI.backgroundColor = UtilityServices.HexToColor("#EFEEEF"); //E1E1E1
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(2);
EditorGUILayout.LabelField(content, style, GUILayout.Width(124), GUILayout.Height(20));
#else
EditorGUILayout.LabelField(content, style, GUILayout.Width(126), GUILayout.Height(20));
#endif
GUI.backgroundColor = originalColor;
GUILayout.Space(4);
bool prevValue = ConsiderChildren;
ConsiderChildren = EditorGUILayout.Toggle(ConsiderChildren, GUILayout.Width(28), GUILayout.ExpandWidth(false));
style.padding = oldPadding;
if (prevValue != ConsiderChildren)
{
UtilityServices.RestoreMeshesFromPairs(dataContainer.objectMeshPairs);
dataContainer.objectMeshPairs = UtilityServices.GetObjectMeshPairs(Selection.activeGameObject, true, true);
TriangleCount = UtilityServices.CountTriangles(ConsiderChildren, dataContainer.objectMeshPairs, Selection.activeGameObject);
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
#endregion Go Deep
GUILayout.Space(14);
#region Undo / Redo buttons
content = new GUIContent();
//GUILayout.FlexibleSpace();
content.tooltip = "Undo the last reduction operation. Please note that you will have to save the scene to keep these changes persistent";
GameObject kee = Selection.activeGameObject;
bool flag1 = true;
flag1 = dataContainer.objectsHistory == null
|| dataContainer.objectsHistory.undoOperations == null
|| dataContainer.objectsHistory.undoOperations.Count == 0;
EditorGUI.BeginDisabledGroup(flag1);
bool hasLods = false;
if (!flag1)
{
hasLods = UtilityServices.HasLODs(Selection.activeGameObject);
}
content.text = "";
iconPath = isPlainSkin ? ICONS_PATH + "undo-white.png" : ICONS_PATH + "undo.png";
content.image = EditorGUIUtility.Load(iconPath) as Texture;
style = GUI.skin.button;
if (GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.MaxHeight(24), GUILayout.ExpandWidth(true)))
{
if (hasLods)
{
EditorUtility.DisplayDialog("LODs found under this object", "This object appears to have an LOD group or LOD assets generated. Please remove them first before trying to undo the last reduction operation", "Ok");
}
else
{
// undo
dataContainer.objectsHistory.ApplyUndoRedoOperation(true);
TriangleCount = UtilityServices.CountTriangles(ConsiderChildren, dataContainer.objectMeshPairs, Selection.activeGameObject);
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
//EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
GUIUtility.ExitGUI();
}
}
EditorGUI.EndDisabledGroup();
GUILayout.Space(1);
content.tooltip = "Redo the last undo operation. Please note that you will have to save the scene to keep these changes persistent";
iconPath = isPlainSkin ? ICONS_PATH + "redo-white.png" : ICONS_PATH + "redo.png";
content.image = EditorGUIUtility.Load(iconPath) as Texture;
flag1 = dataContainer.objectsHistory == null
|| dataContainer.objectsHistory.redoOperations == null
|| dataContainer.objectsHistory.redoOperations.Count == 0;
EditorGUI.BeginDisabledGroup(flag1);
if (!flag1)
{
hasLods = UtilityServices.HasLODs(Selection.activeGameObject);
}
if (GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.MaxHeight(24), GUILayout.ExpandWidth(true)))
{
if (hasLods)
{
EditorUtility.DisplayDialog("LODs found under this object", "This object appears to have an LOD group or LOD assets generated. Please remove them first before trying to redo the last undo operation", "Ok");
}
else
{
//redo
dataContainer.objectsHistory.ApplyUndoRedoOperation(false);
TriangleCount = UtilityServices.CountTriangles(ConsiderChildren, dataContainer.objectMeshPairs, Selection.activeGameObject);
//EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene); //baw did
//EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
GUIUtility.ExitGUI();
}
}
EditorGUI.EndDisabledGroup();
#endregion Undo / Redo buttons
GUILayout.Space(10);
#region Apply Changes here
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//# ffc14d 60%
//# F0FFFF 73%
//# F5F5DC 75%
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
if(isPlainSkin) { content.text = "<size=11><b>Reduce</b></size>"; }
else { content.text = "<size=11> <b><color=#000000>Reduce</color></b> </size>"; }
content.tooltip = "Apply reduction to this object with the current settings. If you don't reduce the object the changes will be lost when this object gets out of focus. Please note that you must save this scene after reducing the object otherwise the reduce operation will be reset on Editor restart.";
//EditorGUI.BeginDisabledGroup((!ReductionPending || Mathf.Approximately(ReductionStrength, 0)));
EditorGUI.BeginDisabledGroup(!ReductionPending && (!ClearBlendshapesComplete && !ClearBlendshapesNormals && !ClearBlendshapesTangents));
didPressButton = GUILayout.Button(content, style, GUILayout.Width(92), GUILayout.Height(24), GUILayout.ExpandWidth(true));
EditorGUI.EndDisabledGroup();
GUI.backgroundColor = originalColor;
if (!hasLods && didPressButton)
{
bool prevRedPendingVal = ReductionPending;
bool reducedAnyShape = false;
bool shouldClearBlendshapes = (ClearBlendshapesComplete || ClearBlendshapesNormals || ClearBlendshapesTangents);
bool meshless = false;
bool isJustForBlendshapes = !ReductionPending && shouldClearBlendshapes;
HashSet<Mesh> blendShapesClearedMeshes = new HashSet<Mesh>();
//if reduction pending is false then you're just
//simplifying blendshapes. So create and assign new meshes
//and clear their shape
//otherwise clear shapes for what is attached
if (shouldClearBlendshapes)
{
if (ConsiderChildren)
{
foreach (var kvp in dataContainer.objectMeshPairs)
{
GameObject go = kvp.Key;
DataContainer.MeshRendererPair pair = kvp.Value;
Mesh originalMesh = null;
Mesh blendSimplified = null;
int? rendererId = null;
if (go == null || pair == null) { continue; }
if (pair.attachedToMeshFilter)
{
var renderer = go.GetComponent<MeshRenderer>();
var filter = go.GetComponent<MeshFilter>();
if(renderer != null) { rendererId = renderer.GetHashCode(); }
if(filter != null) { originalMesh = filter.sharedMesh; }
}
else
{
var renderer = go.GetComponent<SkinnedMeshRenderer>();
if(renderer != null)
{
originalMesh = renderer.sharedMesh;
rendererId = renderer.GetHashCode();
}
}
if (rendererId == null || originalMesh == null) { continue; }
if (originalMesh.blendShapeCount == 0) { continue; }
bool didNothing = true;
blendSimplified = ReductionPending ? originalMesh : Instantiate(originalMesh);
blendSimplified.name = originalMesh.name;
if (ClearBlendshapesComplete)
{
reducedAnyShape = UtilityServices.SimplifyBlendShapes(ref blendSimplified, rendererId, BlendShapeClearType.CLEAR_ALL_DATA);
didNothing = !reducedAnyShape;
}
else
{
Dictionary<String, UnityMeshSimplifier.BlendShapeFrame> blendShapes = new Dictionary<string, UnityMeshSimplifier.BlendShapeFrame>();
if (ClearBlendshapesNormals)
{
reducedAnyShape = UtilityServices.SimplifyBlendShapes(ref blendSimplified, rendererId, BlendShapeClearType.CLEAR_NORMALS);
didNothing = !reducedAnyShape;
}
else if (ClearBlendshapesTangents)
{
reducedAnyShape = UtilityServices.SimplifyBlendShapes(ref blendSimplified, rendererId, BlendShapeClearType.CLEAR_TANGENTS);
didNothing = !reducedAnyShape;
}
}
if(!didNothing)
{
blendSimplified.name = blendSimplified.name.Replace("-BLENDSHAPES_SIMPLIFIED", "");
blendSimplified.name += "-BLENDSHAPES_SIMPLIFIED";
if(!ReductionPending)
{
if(!blendShapesClearedMeshes.Contains(blendSimplified))
{
blendShapesClearedMeshes.Add(blendSimplified);
}
if (pair.attachedToMeshFilter)
{
go.GetComponent<MeshFilter>().sharedMesh = blendSimplified;
}
else
{
go.GetComponent<SkinnedMeshRenderer>().sharedMesh = blendSimplified;
}
}
}
else if (!ReductionPending && blendSimplified != null)
{
DestroyImmediate(blendSimplified);
DestroyImmediate(blendSimplified);
}
}
}
else
{
GameObject go = Selection.activeGameObject;
SkinnedMeshRenderer smr = go.GetComponent<SkinnedMeshRenderer>();
MeshFilter mf = go.GetComponent<MeshFilter>();
Mesh originalMesh = null;
int? rendererId = null;
bool attachedToMeshFilter = true;
Mesh blendSimplified = null;
if (smr != null)
{
rendererId = smr.GetHashCode();
originalMesh = smr.sharedMesh;
attachedToMeshFilter = false;
}
if (originalMesh == null && mf != null)
{
rendererId = mf.GetHashCode();
originalMesh = mf.sharedMesh;
}
if (originalMesh == null) { meshless = true; }
if (smr != null && originalMesh != null)
{
blendSimplified = ReductionPending ? originalMesh : Instantiate(originalMesh);
blendSimplified.name = originalMesh.name;
if (ClearBlendshapesComplete)
{
reducedAnyShape = UtilityServices.SimplifyBlendShapes(ref blendSimplified, rendererId, BlendShapeClearType.CLEAR_ALL_DATA);
}
else if(ClearBlendshapesNormals)
{
reducedAnyShape = UtilityServices.SimplifyBlendShapes(ref blendSimplified, rendererId, BlendShapeClearType.CLEAR_NORMALS);
}
else if(ClearBlendshapesTangents)
{
reducedAnyShape = UtilityServices.SimplifyBlendShapes(ref blendSimplified, rendererId, BlendShapeClearType.CLEAR_TANGENTS);
}
if (!ReductionPending)
{
blendSimplified.name = blendSimplified.name.Replace("-BLENDSHAPES_SIMPLIFIED", "");
blendSimplified.name += "-BLENDSHAPES_SIMPLIFIED";
if (attachedToMeshFilter)
{
go.GetComponent<MeshFilter>().sharedMesh = blendSimplified;
}
else
{
go.GetComponent<SkinnedMeshRenderer>().sharedMesh = blendSimplified;
}
}
if(!ReductionPending && !reducedAnyShape && blendSimplified != null)
{
DestroyImmediate(blendSimplified);
}
}
}
}
if(!ReductionPending && shouldClearBlendshapes && !reducedAnyShape)
{
// Get out of this if
EditorUtility.DisplayDialog("Operation Failed", "Unable to simplify blendshapes. Not enough feasible meshes found. Are there any blendshapes on this model? You might want to mark the \"Consider Children\" checkbox", "ok");
goto ENDIF;
}
// Must save the meshes as assets before applying reduction operations
if (ConsiderChildren)
{
List<Mesh> originalMeshes = UtilityServices.GetMeshesFromPairs(dataContainer.objectMeshPairs);
// The unsaved reduced meshes are the those which have their original meshes in (dataContainer.objectMeshPairs) unsaved as .mesh file
//HashSet<Mesh> unsavedReducedMeshes = UtilityServices.GetUnsavedReducedMeshes(dataContainer.objectMeshPairs);
Tuple<HashSet<Mesh>, DataContainer.ObjectMeshPair> unsavedReducedMeshesPairs = UtilityServices.GetUnsavedReducedMeshes(dataContainer.objectMeshPairs);
HashSet<Mesh> unsavedReducedMeshes = unsavedReducedMeshesPairs?.Item1;
// Contains copies of the original meshes that will be saved in the undo operations for this object
DataContainer.ObjectMeshPair originalMeshesClones = new DataContainer.ObjectMeshPair();
bool areMeshesSaved = UtilityServices.AreMeshesSavedAsAssets(originalMeshes);
// Indicates if the reduction operation is successfully applied to all the target meshes.
// If this value is true we can then add this reduce operation to the list of undo operations for this object.
bool fullySucceeded = true;
try
{
bool savedJustNow = false;
if (!areMeshesSaved)
{
//Debug.Log("Saving meshes as the meshes weren't saved");
int option = EditorUtility.DisplayDialogComplex("Unsaved Meshes",
"The reduce operation won't be applied unless you save the modified meshes under this object. This is also required for keeping the changes persistent and for making prefabs workable for the modified objects. You only have to save the meshes once for an object. Please note that any changes to prefabs must be manually applied. You must save this scene after saving the object.",
"Save",
"Cancel",
"Don't Save");
List<Mesh> tempUnsavedMeshes = unsavedReducedMeshes.ToList();
switch (option)
{
case 0:
if (!IsPathInAssetsDir(AutoLODSavePath))
{
UtilityServices.AutoLODSavePath = UtilityServices.SetAndReturnStringPref("autoLODSavePath", "Assets/");
}
bool passed = UtilityServices.SaveAllMeshes(tempUnsavedMeshes, AutoLODSavePath, true, (error) =>
{
EditorUtility.DisplayDialog("Cannot Save Meshes", error, "Ok");
areMeshesSaved = false;
});
if (passed)
{
//AssetDatabase.Refresh();
areMeshesSaved = true;
if (!(UnityVersion.Contains("2017") || UnityVersion.Contains("2018")))
{
UtilityServices.RestoreMeshesFromPairs(unsavedReducedMeshesPairs.Item2);
}
}
break;
case 1:
case 2:
areMeshesSaved = false;
savedJustNow = false;
break;
}
if (UtilityServices.AreMeshesSavedAsAssets(tempUnsavedMeshes))
{
areMeshesSaved = true;
savedJustNow = true;
ReductionPending = false;
ReductionStrength = 0;
}
else
{
areMeshesSaved = false;
savedJustNow = false;
}
}
fullySucceeded = areMeshesSaved;
// After successfully saving the original meshes copy the modified properties from the modded meshes to the original meshes in the dataContainer list and add the meshes to the objects
if (areMeshesSaved)
{
int toOverwrite = 0;
int done = 0;
foreach (var kvp in dataContainer.objectMeshPairs) { toOverwrite++; }
//Debug.Log("Original meshes are saved so copying properties");
foreach (var kvp in dataContainer.objectMeshPairs)
{
GameObject gameObject = kvp.Key;
if (gameObject == null) { continue; }
DataContainer.MeshRendererPair mRendererPair = kvp.Value;
if (mRendererPair.mesh == null) { continue; }
if (mRendererPair.attachedToMeshFilter)
{
MeshFilter filter = gameObject.GetComponent<MeshFilter>();
if (filter != null)
{
Mesh moddedMesh = filter.sharedMesh;
// Do this for those meshes that are just saved and exclude those that aren't saved now and had their original meshes saved before
if (savedJustNow && unsavedReducedMeshes.Contains(moddedMesh))
{
//Debug.Log("Mesh was SavedJustNow " + moddedMesh.name);
Mesh prevMeshCopy = Instantiate(mRendererPair.mesh);
prevMeshCopy.name = mRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(true, prevMeshCopy);
originalMeshesClones.Add(gameObject, mRenderPair);
//Debug.Log("Created mesh copy for undo Triangles count " + mRenderPair.mesh.triangles.Length / 3 + " modded tris length " + moddedMesh.triangles.Length / 3 + " moddedMesh.HashCode " + moddedMesh.GetHashCode() + " created undo mesh hashcode " + mRenderPair.mesh.GetHashCode());
mRendererPair.mesh = moddedMesh;
}
else
{
EditorUtility.DisplayProgressBar("Saving Changes", $"Writing mesh changes to existing files {++done}/{toOverwrite}", ((float)done / toOverwrite));
Mesh prevMeshCopy = Instantiate(mRendererPair.mesh);
prevMeshCopy.name = mRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(true, prevMeshCopy);
originalMeshesClones.Add(gameObject, mRenderPair);
//mRendererPair.mesh.Clear();
//EditorUtility.CopySerialized(moddedMesh, mRendererPair.mesh);
//Overwrites the mesh assets and keeps references intact
if (UtilityServices.OverwriteAssetWith(mRendererPair.mesh, moddedMesh, true)) { }
else
{
mRendererPair.mesh.Clear();
EditorUtility.CopySerialized(moddedMesh, mRendererPair.mesh);
DestroyImmediate(moddedMesh);
}
//mRendererPair.mesh.MakeSimilarToOtherMesh(moddedMesh);
}
filter.sharedMesh = mRendererPair.mesh;
}
}
else
{
SkinnedMeshRenderer sRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
if (sRenderer != null)
{
Mesh moddedMesh = sRenderer.sharedMesh;
// Do this for those meshes that are just saved and exclude those that aren't saved now and had their original meshes saved before
if (savedJustNow && unsavedReducedMeshes.Contains(moddedMesh))
{
Mesh prevMeshCopy = Instantiate(mRendererPair.mesh);
prevMeshCopy.name = mRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(false, prevMeshCopy);
originalMeshesClones.Add(gameObject, mRenderPair);
mRendererPair.mesh = moddedMesh;
}
else
{
bool breakOut = false;
if (isJustForBlendshapes && !blendShapesClearedMeshes.Contains(moddedMesh))
{
breakOut = true;
}
if (!breakOut)
{
EditorUtility.DisplayProgressBar("Saving Changes", $"Writing mesh changes to existing files {++done}/{toOverwrite}", ((float)done / toOverwrite));
Mesh prevMeshCopy = Instantiate(mRendererPair.mesh);
prevMeshCopy.name = mRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(false, prevMeshCopy);
originalMeshesClones.Add(gameObject, mRenderPair);
//Overwrites the mesh assets and keeps references intact
if (UtilityServices.OverwriteAssetWith(mRendererPair.mesh, moddedMesh, true)) { }
else
{
mRendererPair.mesh.Clear();
EditorUtility.CopySerialized(moddedMesh, mRendererPair.mesh);
DestroyImmediate(moddedMesh);
}
}
}
sRenderer.sharedMesh = mRendererPair.mesh;
}
}
}
EditorUtility.ClearProgressBar();
// Required inorder to force unity to record changes on the modified object
var tempO = Selection.activeGameObject.AddComponent<RefreshEnforcer>();
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
dataContainer.objectMeshPairs = UtilityServices.GetObjectMeshPairs(Selection.activeGameObject, true, true);
bool didSave = EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
ReductionPending = false;
ReductionStrength = 0;
}
}
#pragma warning disable
catch (Exception ex)
{
ReductionPending = prevRedPendingVal;
fullySucceeded = false;
}
// Add this operation to the undo ops for this object if reduction operation succeeded
if (fullySucceeded)
{
// Add here the undo record
dataContainer.objectsHistory.SaveRecord(true, true, originalMeshesClones);
}
// Destroy the mesh copies if failed to reduce.
else if (originalMeshesClones.Count > 0)
{
foreach (var item in originalMeshesClones)
{
item.Value.Destruct();
}
originalMeshesClones = null;
}
}
else if(!meshless)
{
GameObject gameObject = Selection.activeGameObject;
// Contains the copy of the original mesh that will be saved in the undo operations for this object
DataContainer.ObjectMeshPair originalMeshClone = new DataContainer.ObjectMeshPair();
DataContainer.MeshRendererPair originalRendererPair = dataContainer.objectMeshPairs[gameObject];
Mesh moddedMesh = UtilityServices.GetReducedMesh(gameObject, originalRendererPair);
bool isMeshPresent = originalRendererPair.mesh == null ? false : true;
bool isMeshSaved = UtilityServices.IsMeshSavedAsAsset(originalRendererPair.mesh);
bool savedJustNow = false;
// Indicates if the reduction operation is successfully applied to the target mesh.
// If this value is true we can then add this reduce operation to the list of undo operations for this object.
bool fullySucceeded = true;
if (isMeshPresent)
{
try
{
if (!isMeshSaved)
{
//Debug.Log("Saving mesh as the mesh wasn't saved");
int option = EditorUtility.DisplayDialogComplex("Unsaved Mesh",
"The reduce operation won't be applied unless you save the modified mesh of this object. This is also required for keeping the changes persistent and for making prefabs workable for the modified object. You only have to save the mesh once for an object. You must save this scene after saving the object.",
"Save",
"Cancel",
"Don't Save");
switch (option)
{
case 0:
if (!IsPathInAssetsDir(AutoLODSavePath))
{
UtilityServices.AutoLODSavePath = UtilityServices.SetAndReturnStringPref("autoLODSavePath", "Assets/");
}
bool isSuccess = SaveMesh(moddedMesh, AutoLODSavePath, true, (error) =>
{
EditorUtility.DisplayDialog("Cannot Save Mesh", error, "Ok");
isMeshSaved = false;
});
if (isSuccess)
{
isMeshSaved = true;
if (!(UnityVersion.Contains("2017") || UnityVersion.Contains("2018")))
{
DataContainer.ObjectMeshPair reducedMeshPair = new DataContainer.ObjectMeshPair();
DataContainer.MeshRendererPair mPair = new DataContainer.MeshRendererPair(originalRendererPair.attachedToMeshFilter, moddedMesh);
reducedMeshPair.Add(gameObject, mPair);
UtilityServices.RestoreMeshesFromPairs(reducedMeshPair);
}
}
break;
case 1:
case 2:
isMeshSaved = false;
break;
}
if (UtilityServices.IsMeshSavedAsAsset(moddedMesh))
{
isMeshSaved = true;
savedJustNow = true;
ReductionPending = false;
ReductionStrength = 0;
}
else
{
isMeshSaved = false;
savedJustNow = false;
}
}
fullySucceeded = isMeshSaved;
// After successfully saving the modded mesh copy the modified properties from the modded mesh to the original mesh in the dataContainer list and add the mesh to the object
if (isMeshSaved)
{
//Debug.Log("Object saved so now applying to original mesh");
if (originalRendererPair.attachedToMeshFilter)
{
MeshFilter filter = gameObject.GetComponent<MeshFilter>();
if (filter != null)
{
if (savedJustNow)
{
//Debug.Log("SavedJustNow");
Mesh prevMeshCopy = Instantiate(originalRendererPair.mesh);
prevMeshCopy.name = originalRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(true, prevMeshCopy);
originalMeshClone.Add(gameObject, mRenderPair);
//Debug.Log("Created mesh copy for undo Triangles count " + mRenderPair.mesh.triangles.Length / 3 + " modded tris length " + moddedMesh.triangles.Length / 3 + " moddedMesh.HashCode " + moddedMesh.GetHashCode() + " created undo mesh hashcode " + mRenderPair.mesh.GetHashCode());
originalRendererPair.mesh = moddedMesh;
}
else
{
EditorUtility.DisplayProgressBar("Saving Changes", $"Writing mesh changes to existing file {1}/{1}", ((float)1 / 1));
Mesh prevMeshCopy = Instantiate(originalRendererPair.mesh);
prevMeshCopy.name = originalRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(true, prevMeshCopy);
originalMeshClone.Add(gameObject, mRenderPair);
//mRendererPair.mesh.Clear();
//EditorUtility.CopySerialized(moddedMesh, mRendererPair.mesh);
//mRendererPair.mesh.MakeSimilarToOtherMesh(moddedMesh);
//Overwrites the mesh assets and keeps references intact
if (UtilityServices.OverwriteAssetWith(originalRendererPair.mesh, moddedMesh, true)) { }
else
{
originalRendererPair.mesh.Clear();
EditorUtility.CopySerialized(moddedMesh, originalRendererPair.mesh);
DestroyImmediate(moddedMesh);
}
}
filter.sharedMesh = originalRendererPair.mesh;
}
}
else
{
SkinnedMeshRenderer sRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
if (sRenderer != null)
{
if (savedJustNow)
{
Mesh prevMeshCopy = Instantiate(originalRendererPair.mesh);
prevMeshCopy.name = originalRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(false, prevMeshCopy);
originalMeshClone.Add(gameObject, mRenderPair);
originalRendererPair.mesh = moddedMesh;
}
else
{
EditorUtility.DisplayProgressBar("Saving Changes", $"Writing mesh changes to existing file {1}/{1}", ((float)1 / 1));
Mesh prevMeshCopy = Instantiate(originalRendererPair.mesh);
prevMeshCopy.name = originalRendererPair.mesh.name;
DataContainer.MeshRendererPair mRenderPair = new DataContainer.MeshRendererPair(false, prevMeshCopy);
originalMeshClone.Add(gameObject, mRenderPair);
//mRendererPair.mesh.Clear();
//EditorUtility.CopySerialized(moddedMesh, mRendererPair.mesh);
//mRendererPair.mesh.MakeSimilarToOtherMesh(moddedMesh);
//Overwrites the mesh assets and keeps references intact
if (UtilityServices.OverwriteAssetWith(originalRendererPair.mesh, moddedMesh, true)) { }
else
{
originalRendererPair.mesh.Clear();
EditorUtility.CopySerialized(moddedMesh, originalRendererPair.mesh);
DestroyImmediate(moddedMesh);
}
}
sRenderer.sharedMesh = originalRendererPair.mesh;
}
}
EditorUtility.ClearProgressBar();
// Required inorder to force unity to record changes on the modified object
var tempO = gameObject.AddComponent<RefreshEnforcer>();
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
dataContainer.objectMeshPairs = UtilityServices.GetObjectMeshPairs(Selection.activeGameObject, true, true);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
ReductionPending = false;
ReductionStrength = 0;
}
}
#pragma warning disable
catch (Exception ex)
{
ReductionPending = prevRedPendingVal;
fullySucceeded = false;
}
// Add this operation to the undo ops for this object if reduction operation succeeded
if (fullySucceeded)
{
// Add here the undo record
dataContainer.objectsHistory.SaveRecord(false, true, originalMeshClone);
}
// Destroy the mesh copies if failed to reduce. This might fail as DestroyImmediate() might not be allowed from a secondary thread.
else if (originalMeshClone.Count > 0)
{
// There will be just one value in this case because it's not a reduceDeep operation
originalMeshClone[gameObject].Destruct();
originalMeshClone = null;
}
}
}
}
ENDIF:;
#endregion Apply Changes here
EditorGUILayout.EndHorizontal();
GUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
GUILayout.Space(180);
EditorGUILayout.EndHorizontal();
#endregion Section Header
#region Section body
GUILayout.Space(6);
UtilityServices.DrawHorizontalLine(new Color(105 / 255f, 105 / 255f, 105 / 255f), 1, 5);
#region Reduction options
GUILayout.Space(8);
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
EditorGUILayout.BeginHorizontal();
bool previousValue;
content.text = "Preserve UV Foldover";
content.tooltip = "Check this option to preserve UV foldover areas. Usually these are the areas where sharp edges, corners or dents are formed in the mesh or simply the areas where the mesh folds over.";
width = 130;
EditorGUILayout.LabelField(content, style, GUILayout.Width(130));
previousValue = PreserveUVFoldover;
PreserveUVFoldover = EditorGUILayout.Toggle(PreserveUVFoldover, GUILayout.Width(28), GUILayout.ExpandWidth(false));
if (previousValue != PreserveUVFoldover && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
GUILayout.Space(12);
content.text = "Preserve UV Seams";
content.tooltip = "Preserve the mesh areas where the UV seams are made.These are the areas where different UV islands are formed (usually the shallow polygon conjested areas).";
#if UNITY_2019_1_OR_NEWER
width = 124;
#else
width = 120;
#endif
EditorGUILayout.LabelField(content, style, GUILayout.Width(width));
previousValue = PreserveUVSeams;
PreserveUVSeams = EditorGUILayout.Toggle(PreserveUVSeams, GUILayout.Width(20), GUILayout.ExpandWidth(false));
if (previousValue != PreserveUVSeams && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
GUILayout.Space(12);
#region Additional options
oldStyle = style;
style = EditorStyles.toolbarDropDown;
style.richText = true;
content = new GUIContent();
content.text = "<size=11><b>Set Extra Options</b></size>";
content.tooltip = "Set extra options for the mesh simpification process";
if (GUILayout.Button(content, style, GUILayout.Width(134), GUILayout.Height(17), GUILayout.ExpandWidth(false)))
{
if (lastRect != null)
{
lastRect = new Rect(Event.current.mousePosition, lastRect.size);
var definitions = new PopupToggleTemplate.ToggleDefinition[3];
content = new GUIContent();
content.text = "Clear Blendshapes Completely";
content.tooltip = "Clear all blendshapes data in the simplified meshes";
definitions[0] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
ClearBlendshapesComplete = value;
if (ClearBlendshapesComplete) { ClearBlendshapesTangents = ClearBlendshapesNormals = true; }
},
() =>
{
return ClearBlendshapesComplete;
});
content = new GUIContent();
content.text = "Clear Blendshapes Normals";
content.tooltip = "Clear all blendshapes normals data in the simplified meshes. DISCLAIMER: At the moment this option doesn't work because unity doesn't allow you to do this. It's still there in the hope if unity improves in the future.";
definitions[1] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
ClearBlendshapesNormals = value;
if (ClearBlendshapesTangents && ClearBlendshapesNormals) { ClearBlendshapesComplete = true; }
else { ClearBlendshapesComplete = false; }
},
() =>
{
return ClearBlendshapesNormals;
});
content = new GUIContent();
content.text = "Clear Blendshapes Tangents";
content.tooltip = "Clear all blendshapes tangents data in the simplified meshes. DISCLAIMER: At the moment this option doesn't work because unity doesn't allow you to do this. It's still there in the hope if unity improves in the future.";
definitions[2] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
ClearBlendshapesTangents = value;
if (ClearBlendshapesTangents && ClearBlendshapesNormals) { ClearBlendshapesComplete = true; }
else { ClearBlendshapesComplete = false; }
},
() =>
{
return ClearBlendshapesTangents;
});
PopupWindow.Show(lastRect, new PopupToggleTemplate(definitions, new Vector2(230, 78), null, null));
}
}
if (Event.current.type == EventType.Repaint) lastRect = GUILayoutUtility.GetLastRect();
style = oldStyle;
#endregion Additional options
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
content.text = "Preserve Borders";
content.tooltip = "Check this option to preserve border edges of the mesh. Border edges are the edges that are unconnected and open. Preserving border edges might lead to lesser polygon reduction but can be helpful where you see serious mesh and texture distortions.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(130));
previousValue = PreserveBorders;
PreserveBorders = EditorGUILayout.Toggle(PreserveBorders, GUILayout.Width(28), GUILayout.ExpandWidth(false));
if (previousValue != PreserveBorders && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
GUILayout.Space(12);
//content.text = "Smart Linking";
//content.tooltip = "Smart linking links vertices that are very close to each other. This helps in the mesh simplification process where holes or other serious issues could arise. Disabling this (where not needed) can cause a minor performance gain.";
content.text = "Use Edge Sort";
content.tooltip = "Using edge sort can result in very good quality mesh simplification in some cases but can be a little slow to run.";
#if UNITY_2019_1_OR_NEWER
width = 124;
#else
width = 120;
#endif
EditorGUILayout.LabelField(content, style, GUILayout.Width(width));
previousValue = UseEdgeSort;
UseEdgeSort = EditorGUILayout.Toggle(UseEdgeSort, GUILayout.Width(20), GUILayout.ExpandWidth(false));
if (previousValue != UseEdgeSort && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
content.text = "Regard Curvature";
content.tooltip = "Check this option to take into account the discrete curvature of mesh surface during simplification. Taking surface curvature into account can result in very good quality mesh simplification, but it can slow the simplification process significantly.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(130));
previousValue = RegardCurvature;
RegardCurvature = EditorGUILayout.Toggle(RegardCurvature, GUILayout.Width(28), GUILayout.ExpandWidth(false));
if (previousValue != RegardCurvature && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
GUILayout.Space(12);
content.text = "Recalculate Normals";
content.tooltip = "Recalculate mesh normals after simplification. Use this option if you see incorrect lighting or dark regions on the simplified mesh(es). This also recalculates the tangents afterwards.";
#if UNITY_2019_1_OR_NEWER
width = 124;
#else
width = 120;
#endif
EditorGUILayout.LabelField(content, style, GUILayout.Width(width));
previousValue = RecalculateNormals;
RecalculateNormals = EditorGUILayout.Toggle(RecalculateNormals, GUILayout.Width(20), GUILayout.ExpandWidth(false));
if (previousValue != RecalculateNormals && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
content.text = "Aggressiveness";
content.tooltip = "The aggressiveness of the reduction algorithm. Higher number equals higher quality, but more expensive to run. Lowest value is 7. Only valid if \"Use Edge Sort\" is unchecked.";
EditorGUI.BeginDisabledGroup(UseEdgeSort);
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
EditorGUILayout.LabelField(content, GUILayout.Width(129));
#else
EditorGUILayout.LabelField(content, GUILayout.Width(131));
#endif
content.text = "";
//aggressiveness = Mathf.Abs(EditorGUILayout.FloatField(content, aggressiveness, GUILayout.Width(168), GUILayout.ExpandWidth(false)));
float previous = Aggressiveness;
Aggressiveness = Mathf.Abs(EditorGUILayout.DelayedFloatField(content, Aggressiveness, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
if (Aggressiveness < 7) { Aggressiveness = 7; }
if (!Mathf.Approximately(previous, Aggressiveness) && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
EditorGUI.EndDisabledGroup();
GUILayout.EndHorizontal();
GUILayout.Space(2);
GUILayout.BeginHorizontal();
content.text = "Max Iterations";
content.tooltip = "The maximum passes the reduction algorithm does. Higher number is more expensive but can bring you closer to your target quality. 100 is the lowest allowed value. Only valid if \"Use Edge Sort\" is unchecked.";
EditorGUI.BeginDisabledGroup(UseEdgeSort);
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
EditorGUILayout.LabelField(content, GUILayout.Width(129));
#else
EditorGUILayout.LabelField(content, GUILayout.Width(131));
#endif
content.text = "";
//maxIterations = Mathf.Abs(EditorGUILayout.IntField(content, maxIterations, GUILayout.Width(168), GUILayout.ExpandWidth(false)));
int temp = MaxIterations;
MaxIterations = Mathf.Abs(EditorGUILayout.DelayedIntField(content, MaxIterations, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
if (MaxIterations < 100) { MaxIterations = 100; }
if (!Mathf.Approximately(temp, MaxIterations) && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
EditorGUI.EndDisabledGroup();
GUILayout.EndHorizontal();
GUILayout.Space(10);
#region Preservation Sphere
EditorGUILayout.BeginHorizontal();
content.text = "Tolerance Spheres";
content.tooltip = "Check this option to enable the tolerance spheres. Adding a tolerance sphere allows you to encompass specific areas of the Mesh that you want to preserve polygons of during the reduction process. This can leave such areas of the mesh with the original quality by ignoring them during the reduction process. Please note that reduction with preservation spheres might get slow.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(130));
previousValue = IsPreservationActive;
IsPreservationActive = EditorGUILayout.Toggle(IsPreservationActive, GUILayout.Width(28), GUILayout.ExpandWidth(false));
if (previousValue != IsPreservationActive && !applyForOptionsChange)
{
RunOnThreads = CheckOnThreads();
applyForOptionsChange = true;
}
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(15);
#else
GUILayout.Space(15);
#endif
EditorGUI.BeginDisabledGroup(!IsPreservationActive);
#region LOAD PRESET
originalColor = GUI.backgroundColor;
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
//GUILayout.FlexibleSpace();
style = GUI.skin.button;
content.tooltip = "Load an already saved tolerance sphere preset";
content.text = "<size=11><b>Load Preset</b></size>";
if (GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.MaxHeight(24), GUILayout.ExpandWidth(true)))
{
string path = EditorUtility.OpenFilePanel("Open Tolerance Spheres Preset", "Assets/", "spp");
// User pressed the cancel button
if (string.IsNullOrWhiteSpace(path)) { }
else
{
string presetJson = File.ReadAllText(path);
ToleranceSphereJson[] spheresJsonable = null;
bool failed = false;
try
{
spheresJsonable = JsonUtilityArrays.FromJson<ToleranceSphereJson>(presetJson);
}
catch(Exception ex)
{
failed = true;
}
if(spheresJsonable == null || failed)
{
EditorUtility.DisplayDialog("Failed", "Failed to load preset. Please check that the specfied file is a valid tolerance spheres preset file", "Ok");
failed = true;
}
if (!failed)
{
dataContainer.toleranceSpheres = new List<ToleranceSphere>();
foreach(var sphere in spheresJsonable)
{
var toleranceSphere = ScriptableObject.CreateInstance(typeof(ToleranceSphere)) as ToleranceSphere;
toleranceSphere.SetProperties(sphere);
dataContainer.toleranceSpheres.Add(toleranceSphere);
}
if (dataContainer.currentLodLevelSettings != null && dataContainer.currentLodLevelSettings.Count > 0)
{
foreach (var lodLevel in dataContainer.currentLodLevelSettings)
{
lodLevel.sphereIntensities = new List<float>();
foreach(var sphere in spheresJsonable)
{
lodLevel.sphereIntensities.Add(sphere.preservationStrength);
}
if (lodLevel.sphereIntensities.Count == 0) { lodLevel.intensityFoldout = false; }
}
}
}
}
}
GUI.backgroundColor = originalColor;
#endregion LOAD PRESET
GUILayout.Space(4);
#region SAVE PRESET
EditorGUI.BeginDisabledGroup(dataContainer.toleranceSpheres == null || dataContainer.toleranceSpheres.Count == 0);
originalColor = GUI.backgroundColor;
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
//GUILayout.FlexibleSpace();
style = GUI.skin.button;
content.tooltip = "Save these tolerance sphere settings as a new preset which can be loaded later on";
content.text = "<size=11><b>Save Preset</b></size>";
if (GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.MaxHeight(24), GUILayout.ExpandWidth(true)))
{
string path = EditorUtility.SaveFilePanel("Save Tolerance Spheres Preset", "Assets/", "PRESET_NAME", "spp");
// User pressed the cancel button
if (string.IsNullOrWhiteSpace(path)) { }
else
{
ToleranceSphereJson[] spheresJsonable = new ToleranceSphereJson[dataContainer.toleranceSpheres.Count];
for (int a = 0; a < dataContainer.toleranceSpheres.Count; a++)
{
var toleranceSphere = dataContainer.toleranceSpheres[a];
spheresJsonable[a] = new ToleranceSphereJson(toleranceSphere);
}
string preset = JsonUtilityArrays.ToJson<ToleranceSphereJson>(spheresJsonable, true);
System.IO.File.WriteAllText(path, preset);
}
}
GUI.backgroundColor = originalColor;
EditorGUI.EndDisabledGroup();
#endregion SAVE PRESET
GUILayout.Space(30);
#region ADD TOLERANCE SPHERE
content = new GUIContent();
//GUILayout.FlexibleSpace();
style = GUI.skin.button;
content.tooltip = "Add a new tolerance sphere";
content.text = "<b>Add Sphere</b>";
if (GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.MaxHeight(24), GUILayout.ExpandWidth(true)))
{
Transform selected = Selection.activeTransform;
Vector3 lossyScale = selected.lossyScale;
float avg = UtilityServices.Average(lossyScale.x, lossyScale.y, lossyScale.z);
float sphereDiameter = SphereDefaultDiameter * avg;
Vector3 worldPosition = new Vector3(selected.position.x + (lossyScale.x / 2f + sphereDiameter / 2f), selected.position.y, selected.position.z);
//ToleranceSphere sphere = new ToleranceSphere(worldPosition, sphereDiameter, sphereDefaultColor, selected.gameObject, 100f);
ToleranceSphere sphere = ScriptableObject.CreateInstance(typeof(ToleranceSphere)) as ToleranceSphere;
sphere.SetProperties(worldPosition, sphereDiameter, sphereDefaultColor, 100f);
dataContainer.toleranceSpheres.Add(sphere);
foreach (var lodLevel in dataContainer.currentLodLevelSettings)
{
lodLevel.sphereIntensities.Add(100f);
}
}
#endregion ADD TOLERANCE SPHERE
EditorGUILayout.EndHorizontal();
GUILayout.Space(2);
#region Draw Tolerance Spheres Settings
for (int a = 0; a < dataContainer.toleranceSpheres.Count; a++)
{
var toleranceSphere = dataContainer.toleranceSpheres[a];
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
content = new GUIContent(); //FF6347ff //006699
if(isPlainSkin) { content.text = String.Format("<b>Sphere {0}</b>", a + 1); }
else { content.text = String.Format("<b><color=#3e2723>Sphere {0}</color></b>", a + 1); }
style = GUI.skin.label;
style.richText = true;
GUILayout.Label(content, style);
GUILayout.Space(190);
#region DUPLICATE TOLERANCE SPHERE
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Duplicate</b></size>"; }
else { content.text = "<size=11><color=#006699><b>Duplicate</b></color></size>"; }
content.tooltip = $"Creates a duplicate of this tolerance sphere";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.Height(17), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
if (didPressButton)
{
Transform selected = Selection.activeTransform;
ToleranceSphere sphere = ScriptableObject.CreateInstance(typeof(ToleranceSphere)) as ToleranceSphere;
sphere.SetProperties(toleranceSphere.worldPosition, toleranceSphere.diameter, toleranceSphere.color, toleranceSphere.preservationStrength, toleranceSphere.isHidden);
dataContainer.toleranceSpheres.Add(sphere);
foreach (var lodLevel in dataContainer.currentLodLevelSettings)
{
lodLevel.sphereIntensities.Add(toleranceSphere.preservationStrength);
}
}
#endregion DUPLICATE TOLERANCE SPHERE
GUILayout.Space(10);
#region HIDE/UNHIDE SPHERE
originalColor = GUI.backgroundColor;
//GUI.backgroundColor = UtilityServices.HexToColor("#EEFAFF");
string text = toleranceSphere.isHidden ? "Unhide this tolerance sphere" : "Hide this tolerance sphere";
GUIContent HideTogglecontent = new GUIContent();
HideTogglecontent.tooltip = text;
if (toleranceSphere.isHidden)
{
iconPath = isPlainSkin ? ICONS_PATH + "unhide-white.png" : ICONS_PATH + "unhide.png";
}
else
{
iconPath = isPlainSkin ? ICONS_PATH + "hide-white.png" : ICONS_PATH + "hide.png";
}
HideTogglecontent.image = toleranceSphere.isHidden ? EditorGUIUtility.Load(iconPath) as Texture : EditorGUIUtility.Load(iconPath) as Texture;
if (GUILayout.Button(HideTogglecontent, GUILayout.Width(38), GUILayout.Height(17)))
{
toleranceSphere.isHidden = !toleranceSphere.isHidden;
}
GUI.backgroundColor = originalColor;
#endregion HIDE/UNHIDE SPHERE
GUILayout.Space(2);
var previousBackgroundColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(Color.gray.r, Color.gray.g, Color.gray.b, 0.8f);
GUIContent deleteSphereButtonContent = new GUIContent("<b><color=#FFFFFFD2>X</color></b>", "Remove this tolerance sphere.");
style = GUI.skin.button;
style.richText = true;
if (GUILayout.Button(deleteSphereButtonContent, GUILayout.Width(20)))
{
dataContainer.toleranceSpheres.RemoveAt(a);
foreach (var lodLevel in dataContainer.currentLodLevelSettings)
{
lodLevel.sphereIntensities.RemoveAt(a);
if(lodLevel.sphereIntensities.Count == 0) { lodLevel.intensityFoldout = false; }
}
a--;
continue;
}
GUI.backgroundColor = previousBackgroundColor;
EditorGUILayout.EndHorizontal();
GUILayout.Space(6);
GUILayout.BeginHorizontal();
style = GUI.skin.label;
style.richText = true;
content.text = "Position";
content.tooltip = "The current position values of this tolerance sphere in world space.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(124));
// the reference to the scriptable object might be null
if(toleranceSphere != null)
{
Undo.RecordObject(toleranceSphere, "Tolerance Sphere inspector change");
}
toleranceSphere.worldPosition = EditorGUILayout.Vector3Field("", toleranceSphere.worldPosition, GUILayout.Width(140), GUILayout.ExpandWidth(true));
GUILayout.Space(21);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
content.text = "Sphere Size";
content.tooltip = "The diameter of this tolerance sphere.";
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
EditorGUILayout.LabelField(content, GUILayout.Width(124));
#else
EditorGUILayout.LabelField(content, GUILayout.Width(126));
#endif
content.text = "";
float newDiameter = 0;
#if UNITY_2019_1_OR_NEWER
newDiameter = Mathf.Abs(EditorGUILayout.FloatField(content, toleranceSphere.diameter, GUILayout.Width(42), GUILayout.ExpandWidth(true)));
#else
newDiameter = Mathf.Abs(EditorGUILayout.FloatField(content, toleranceSphere.diameter, GUILayout.Width(45), GUILayout.ExpandWidth(true)));
#endif
if (!Mathf.Approximately(newDiameter, 0))
{
toleranceSphere.diameter = newDiameter;
}
GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
GUILayout.EndHorizontal();
style = GUI.skin.label;
style.richText = true;
content.text = "Colour";
content.tooltip = "Change the color of the tolerance sphere.";
#if UNITY_2019_1_OR_NEWER
EditorGUILayout.LabelField(content, style, GUILayout.Width(51));
#else
EditorGUILayout.LabelField(content, style, GUILayout.Width(51));
#endif
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(3);
toleranceSphere.color = EditorGUILayout.ColorField(toleranceSphere.color, GUILayout.Width(48), GUILayout.ExpandWidth(true));
#else
toleranceSphere.color = EditorGUILayout.ColorField(toleranceSphere.color, GUILayout.Width(52), GUILayout.ExpandWidth(true));
#endif
GUILayout.Space(3);
GUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.EndHorizontal();
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(2);
#else
GUILayout.Space(1);
#endif
#region Preservation Strength Slider
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
content.text = "Sphere Intensity";
content.tooltip = "The percentage of triangles to preserve in the region enclosed by this preservation sphere.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(125));
width = 137;
float oldValue = toleranceSphere.preservationStrength;
toleranceSphere.preservationStrength = Mathf.Abs(GUILayout.HorizontalSlider(toleranceSphere.preservationStrength, 0, 100, GUILayout.Width(width), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
//if (!Mathf.Approximately(oldValue, toleranceSphere.preservationStrength) && !applyForOptionsChange)
//{
// RunOnThreads = CheckOnThreads();
// applyForOptionsChange = true;
//}
GUILayout.Space(5);
content.text = "";
oldValue = toleranceSphere.preservationStrength;
toleranceSphere.preservationStrength = Mathf.Abs(EditorGUILayout.DelayedFloatField(content, toleranceSphere.preservationStrength, style, GUILayout.Width(10), GUILayout.ExpandWidth(true)));
if ((int)toleranceSphere.preservationStrength > 100)
{
toleranceSphere.preservationStrength = GetFirstNDigits((int)toleranceSphere.preservationStrength, 2);
}
//if (!Mathf.Approximately(oldValue, toleranceSphere.preservationStrength) && !applyForOptionsChange)
//{
// RunOnThreads = CheckOnThreads();
// applyForOptionsChange = true;
//}
#if UNITY_2019_1_OR_NEWER
width = 15;
#else
width = 20;
#endif
style = GUI.skin.label;
content.text = "<b><size=13>%</size></b>";
EditorGUILayout.LabelField(content, style, GUILayout.Width(width));
GUILayout.EndHorizontal();
#endregion Preservation Strength Slider
EditorGUILayout.EndVertical();
}
#endregion Draw Tolerance Spheres Settings
#endregion Reduction options
EditorGUI.EndDisabledGroup();
#region Reduction slider section
GUILayout.Space(8);
UtilityServices.DrawHorizontalLine(new Color(105 / 255f, 105 / 255f, 105 / 255f), 1, 5);
GUILayout.Space(8);
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
style.padding.left = -2;
content.text = "Reduction Strength";
content.tooltip = "The intensity of the reduction process. This is the amount in percentage to reduce the model by.";
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(2);
#else
GUILayout.Space(4);
#endif
EditorGUILayout.LabelField(content, style, GUILayout.Width(127));
style.padding = prevPadding;
if (Mathf.Approximately(ReductionStrength, 0)) { applyForOptionsChange = false; }
float oldStrength = ReductionStrength;
bool isMeshless = ConsiderChildren ? false : UtilityServices.IsMeshless(Selection.activeTransform);
hasLods = UtilityServices.HasLODs(Selection.activeGameObject);
ReductionStrength = Mathf.Abs(GUILayout.HorizontalSlider(ReductionStrength, 0, 100, GUILayout.Width(138), GUILayout.ExpandWidth(true)));
if (ReductionPending && Mathf.Approximately(ReductionStrength, 0))
{
UtilityServices.RestoreMeshesFromPairs(dataContainer.objectMeshPairs);
TriangleCount = UtilityServices.CountTriangles(ConsiderChildren, dataContainer.objectMeshPairs, Selection.activeGameObject);
ReductionPending = false;
}
float quality = 1f - (ReductionStrength / 100f);
bool isFeasible1 = !Mathf.Approximately(oldStrength, ReductionStrength) && (!isMeshless && !hasLods);
bool isFeasible2 = applyForOptionsChange && (!isMeshless && !hasLods);
//Debug.Log("IsFeasible1? " +isFeasible1 + " !Mathf.Approximately(oldStrength, reductionStrength)? " + !Mathf.Approximately(oldStrength, reductionStrength) + " !Mathf.Approximately(reductionStrength, 0)? " + !Mathf.Approximately(reductionStrength, 0) + " Flag? " + flag + " ReductionStrenght is " +reductionStrength);
//Debug.Log("IsFeasibl2? " +isFeasible2 + " applyForReduceDeep? " + applyForReduceDeep);
if (!Mathf.Approximately(oldStrength, ReductionStrength))
{
if (isMeshless)
{
EditorUtility.DisplayDialog("Meshless Object", "This object appears to have no feasible mesh for reduction. You might want to enable \"Consider Children\" to consider the nested children for reduction.", "Ok");
ReductionStrength = oldStrength;
}
else if (hasLods)
{
EditorUtility.DisplayDialog("LODs found under this object", "This object appears to have an LOD group or LOD assets generated. Please remove them first before trying to simplify the mesh for this object", "Ok");
ReductionStrength = oldStrength;
}
}
if (isFeasible1 || isFeasible2)
{
ReductionPending = true;
try
{
if (ConsiderChildren)
{
int prevTriangleCount = TriangleCount;
//System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch();
//w.Start();
bool isToleranceActive = false;
if (dataContainer.toleranceSpheres == null || dataContainer.toleranceSpheres.Count == 0)
{
isToleranceActive = false;
}
else if (IsPreservationActive)
{
isToleranceActive = true;
}
try
{
//var w = new System.Diagnostics.Stopwatch();
//w.Start();
TriangleCount = UtilityServices.SimplifyObjectDeep(dataContainer.objectMeshPairs, dataContainer.toleranceSpheres, RunOnThreads, isToleranceActive, quality, (string err) =>
{
applyForOptionsChange = false;
Debug.LogError(err);
TriangleCount = prevTriangleCount;
});
//w.Stop();
//Debug.Log("OVERALL Time ellapsed = " + w.ElapsedMilliseconds);
}
catch (Exception ex)
{
Debug.LogError(ex.ToString());
}
}
else
{
if (applyForOptionsChange)
{
//Debug.Log("Consider Children was unchecked so restoring other meshes quality is: " +quality + " ISFeasible1? " + isFeasible1 + "IsFeasible2 " + isFeasible2 + " !Mathf.Approximately(quality, 0)? " + !Mathf.Approximately(quality, 0));
UtilityServices.RestoreMeshesFromPairs(dataContainer.objectMeshPairs);
}
DataContainer.MeshRendererPair meshRendererPair;
GameObject selectedObject = Selection.activeGameObject;
//EditorUtility.DisplayProgressBar("Reducing Mesh", "Simplifying selected object's mesh. Depending on the mesh complexity this might take some time.", 0);
if (dataContainer.objectMeshPairs.TryGetValue(selectedObject, out meshRendererPair))
{
bool isToleranceActive = false;
if (dataContainer.toleranceSpheres == null || dataContainer.toleranceSpheres.Count == 0)
{
isToleranceActive = false;
}
else if (IsPreservationActive)
{
isToleranceActive = true;
}
TriangleCount = SimplifyObjectShallow(meshRendererPair, dataContainer.toleranceSpheres, selectedObject, isToleranceActive, quality);
}
}
}
catch (Exception ex)
{
//EditorUtility.ClearProgressBar();
applyForOptionsChange = false;
}
//areAllMeshesSaved = AreAllMeshesSaved(Selection.activeGameObject, true); Might not need this
applyForOptionsChange = false;
//EditorUtility.ClearProgressBar();
}
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
oldStrength = ReductionStrength;
ReductionStrength = Mathf.Abs(EditorGUILayout.DelayedFloatField(content, ReductionStrength, style, GUILayout.Width(10), GUILayout.ExpandWidth(true)));
if ((int)ReductionStrength > 100)
{
ReductionStrength = GetFirstNDigits((int)ReductionStrength, 2);
}
if (!Mathf.Approximately(oldStrength, ReductionStrength))
{
if (isMeshless)
{
EditorUtility.DisplayDialog("Meshless Object", "This object appears to have no feasible mesh for reduction. You might want to enable \"Consider Children\" to consider the nested children for reduction.", "Ok");
ReductionStrength = oldStrength;
}
else if (hasLods)
{
EditorUtility.DisplayDialog("LODs found under this object", "This object appears to have an LOD group or LOD assets generated. Please remove them first before trying to simplify the mesh for this object", "Ok");
ReductionStrength = oldStrength;
}
else
{
applyForOptionsChange = true;
}
if (ReductionPending && Mathf.Approximately(ReductionStrength, 0))
{
UtilityServices.RestoreMeshesFromPairs(dataContainer.objectMeshPairs);
TriangleCount = UtilityServices.CountTriangles(ConsiderChildren, dataContainer.objectMeshPairs, Selection.activeGameObject);
ReductionPending = false;
}
}
//GUILayout.Space(2);
style = GUI.skin.label;
content.text = "<b><size=13>%</size></b>";
EditorGUILayout.LabelField(content, style, GUILayout.Width(20));
GUILayout.EndHorizontal();
GUILayout.Space(2);
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
style.padding.left = -2;
content.text = "Triangles Count";
content.tooltip = "The current number of triangles in the selected mesh.";
if (ConsiderChildren)
{
content.tooltip = "The total number of triangles in the selected object. Includes the triangles of this mesh as well as all of its children meshes.";
}
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(2);
#else
GUILayout.Space(4);
#endif
EditorGUILayout.LabelField(content, style, GUILayout.Width(127));
style.padding = prevPadding;
style = GUI.skin.textField;
content.text = TriangleCount.ToString();
//trianglesCount = Mathf.Abs(EditorGUILayout.IntField(content, trianglesCount, style, GUILayout.Width(50), GUILayout.ExpandWidth(true)));
EditorGUILayout.LabelField(content, style, GUILayout.Width(50), GUILayout.ExpandWidth(true));
GUILayout.EndHorizontal();
#endregion Reduction slider section
#endregion Section body
#endregion Section body
#region AUTO LOD
GUILayout.Space(12);
UtilityServices.DrawHorizontalLine(Color.black, 1, 8);
#region TITLE HEADER
GUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
content = new GUIContent();
if(isPlainSkin) { content.text = "<size=13><b>AUTOMATIC LOD</b></size>"; }
else { content.text = "<size=13><b><color=#A52A2AFF>AUTOMATIC LOD</color></b></size>"; }
content.tooltip = "Expand this section to see options for automatic LOD generation.";
style = EditorStyles.foldout;
style.richText = true; // #FF6347ff //A52A2AFF
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
style.padding = new RectOffset(20, 0, -1, 0);
GUILayout.Space(19);
FoldoutAutoLOD = EditorGUILayout.Foldout(FoldoutAutoLOD, content, true, style);
style.padding = prevPadding;
style = new GUIStyle();
style.richText = true;
EditorGUILayout.EndHorizontal();
#endregion TITLE HEADER
if (FoldoutAutoLOD)
{
UtilityServices.DrawHorizontalLine(Color.black, 1, 8);
GUILayout.Space(6);
#region Section Header
GUILayout.Space(6);
EditorGUILayout.BeginHorizontal();
#region Change Save Path
style = GUI.skin.button;
style.richText = true;
content = new GUIContent();
if (isPlainSkin) { content.text = "<b><size=11>Change Save Path</size></b>"; }
else { content.text = "<b><size=11><color=#006699>Change Save Path</color></size></b>"; }
content.tooltip = "Change the path where the generated LODs mesh assets will be saved. If you don't select a path the default path will be used. Please note that the chosen path will be used for saving LOD mesh assets in the future, unless changed.";
if (GUILayout.Button(content, style, GUILayout.Width(134), GUILayout.Height(20), GUILayout.ExpandWidth(false)))
{
//sphereDefaultColor
string toOpen = String.IsNullOrWhiteSpace(AutoLODSavePath) ? "Assets/" : AutoLODSavePath;
if(!String.IsNullOrWhiteSpace(toOpen))
{
if (toOpen.EndsWith("/")) { toOpen.Remove(toOpen.Length - 1, 1); }
if (!AssetDatabase.IsValidFolder(toOpen))
{
toOpen = "Assets/";
}
}
string path = EditorUtility.OpenFolderPanel("Choose LOD Assets Save path", toOpen, "");
//Validate the save path. It might be outside the assets folder
// User pressed the cancel button
if (string.IsNullOrWhiteSpace(path)) { }
else if (!UtilityServices.IsPathInAssetsDir(path))
{
EditorUtility.DisplayDialog("Invalid Path", "The path you chose is not valid.Please choose a path that points to a directory that exists in the project's Assets folder.", "Ok");
}
else
{
path = UtilityServices.GetValidFolderPath(path);
if (!string.IsNullOrWhiteSpace(path))
{
UtilityServices.AutoLODSavePath = UtilityServices.SetAndReturnStringPref("autoLODSavePath", path);
}
}
}
EditorGUI.EndDisabledGroup();
#endregion Change Save Path
GUILayout.Space(40);
#region Add LOD Level
content = new GUIContent();
//GUILayout.FlexibleSpace();
content.tooltip = "Add an LOD level.";
content.text = "<b>Add</b>";
#if UNITY_2019_1_OR_NEWER
width = 55;
#else
width = 40;
#endif
if (GUILayout.Button(content, style, GUILayout.Width(width), GUILayout.MaxHeight(24), GUILayout.ExpandWidth(true)))
{
//if (dataContainer.currentLodLevelSettings.Count < UtilityServices.MAX_LOD_COUNT)
//{
List<float> strengths = new List<float>();
foreach(var sphere in dataContainer.toleranceSpheres) { strengths.Add(100f); }
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(0, 0, false, false, false, true, false, 7, 100, false, false, false, strengths));
//}
}
#endregion Add LOD Level
GUILayout.Space(2);
#region Generate LODs
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//# ffc14d 60%
//# F0FFFF 73%
//# F5F5DC 75%
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Generate LODS</b> </size>"; }
else { content.text = "<size=11> <b><color=#000000>Generate LODS</color></b> </size>"; }
content.tooltip = "Generate LODs for this GameObject with the settings specified. Please note that you must save the scene after successful generation of LODs and apply changes to any prefabs manually.";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(120), GUILayout.Height(24), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
if (didPressButton)
{
UtilityServices.RestoreMeshesFromPairs(dataContainer.objectMeshPairs);
dataContainer.objectMeshPairs = UtilityServices.GetObjectMeshPairs(Selection.activeGameObject, true, true);
ReductionPending = false;
ReductionStrength = 0;
try
{
// Delete LOD levels that have 0 screen relative height and 0 reduction strength(Excluding the 1st one)
List<DataContainer.LODLevelSettings> levelsToDelete = new List<DataContainer.LODLevelSettings>();
if (dataContainer.currentLodLevelSettings.Count > 1)
{
for (int a = 1; a < dataContainer.currentLodLevelSettings.Count; a++)
{
var lodLevel = dataContainer.currentLodLevelSettings[a];
if (Mathf.Approximately(lodLevel.transitionHeight, 0))
{
levelsToDelete.Add(lodLevel);
}
if (Mathf.Approximately(lodLevel.reductionStrength, 0))
{
levelsToDelete.Add(lodLevel);
}
}
}
foreach (var toDelete in levelsToDelete)
{
dataContainer.currentLodLevelSettings.Remove(toDelete);
}
if(dataContainer != null && dataContainer.currentLodLevelSettings != null && dataContainer.currentLodLevelSettings.Count > 1)
{
bool isSuccess = UtilityServices.GenerateLODS(Selection.activeGameObject, dataContainer.toleranceSpheres, dataContainer.currentLodLevelSettings, UtilityServices.AutoLODSavePath, null, true, GenerateUV2LODs);
EditorUtility.ClearProgressBar();
if (isSuccess)
{
ApplyExtraOptionsForLOD(Selection.activeGameObject);
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
}
else
{
EditorUtility.DisplayDialog("Failed", "Failed to generate LODs", "Ok");
}
}
else
{
EditorUtility.DisplayDialog("Failed", "Failed to generate LODs. You must have at least one non-base lod level with reduction strength and transition height > 0. Press the \"Add\" button to add more lod levels.", "Ok");
}
}
catch (Exception error)
{
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog("Failed to generate LODs. The LODs might be partially generated", error.ToString(), "Ok");
}
GUIUtility.ExitGUI();
}
#endregion Generate LODs
EditorGUILayout.EndHorizontal();
GUILayout.Space(2);
EditorGUILayout.BeginHorizontal();
#if UNITY_2019_1_OR_NEWER
//GUILayout.Space(177);
#else
//GUILayout.Space(174);
#endif
#region Additional options
style = EditorStyles.toolbarDropDown;
style.richText = true;
content = new GUIContent();
content.text = "<size=11><b>Set Extra Options</b></size>";
content.tooltip = "Set extra options for the LOD generation process";
if (GUILayout.Button(content, style, GUILayout.Width(134), GUILayout.Height(17), GUILayout.ExpandWidth(false)))
{
if (lastRect != null)
{
lastRect = new Rect(Event.current.mousePosition, lastRect.size);
var definitions = new PopupToggleTemplate.ToggleDefinition[5];
content = new GUIContent();
content.text = "Copy static flags to new objects";
content.tooltip = "Copy the static flags from this object to the newly created LOD objects";
definitions[0] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
CopyParentStaticFlags = value;
},
() =>
{
return CopyParentStaticFlags;
});
content = new GUIContent();
content.text = "Copy layer to new objects";
content.tooltip = "Copy layer from this object to the newly created LOD objects";
definitions[1] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
CopyParentLayer = value;
},
() =>
{
return CopyParentLayer;
});
content = new GUIContent();
content.text = "Copy tag to new objects";
content.tooltip = "Copy tag from this object to the newly created LOD objects";
definitions[2] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
CopyParentTag = value;
},
() =>
{
return CopyParentTag;
});
content = new GUIContent();
content.text = "Generate UV2";
content.tooltip = "Should we generate uv2 with default settings for each mesh, and fill them in?. Note that generating uv2 can cause the LOD generation process to get slow";
definitions[3] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
GenerateUV2LODs = value;
},
() =>
{
return GenerateUV2LODs;
});
content = new GUIContent();
content.text = "Remove LODBackup Component";
content.tooltip = "Generate LODs but do not add the \"LODBackup\" component. Please note that without this component the \"DestroyLODs\" button won't function correctly and the LOD meshes in the folders will have to be deleted manually";
definitions[4] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
RemoveLODBackupComponent = value;
},
() =>
{
return RemoveLODBackupComponent;
});
PopupWindow.Show(lastRect, new PopupToggleTemplate(definitions, new Vector2(230, 128), null, null));
}
}
if (Event.current.type == EventType.Repaint) lastRect = GUILayoutUtility.GetLastRect();
#endregion Additional options
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(40);
#else
GUILayout.Space(40);
#endif
#region Copy Preview Settings
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//# ffc14d 60%
//# F0FFFF 73%
//# F5F5DC 75%
//GUI.backgroundColor = UtilityServices.HexToColor("#f9f5f5");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Copy Preview</b></size>"; }
else { content.text = "<size=11><color=#006699><b>Copy Preview</b></color></size>"; }
content.tooltip = $"Copies all the settings from the preview above into each LOD level";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.Height(17), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
if (didPressButton)
{
for (int a = 0; a < dataContainer.currentLodLevelSettings.Count; a++)
{
var lodLevel = dataContainer.currentLodLevelSettings[a];
// in Base level don't copy over any settings if reduction is 0
if(a == 0)
{
if(!Mathf.Approximately(lodLevel.reductionStrength, 0))
{
CopyOverPreviewSettings(lodLevel);
}
}
else
{
CopyOverPreviewSettings(lodLevel);
}
}
}
#endregion Copy Preview Settings
GUILayout.Space(2);
#region Destroy LODs
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//# ffc14d 60%
//# F0FFFF 73%
//# F5F5DC 75%
//GUI.backgroundColor = UtilityServices.HexToColor("#f9f5f5");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Destroy LODs</b></size>"; }
else { content.text = "<size=11><color=#006699><b>Destroy LODs</b></color></size>"; }
content.tooltip = $"Destroy the generated LODs for this mesh. This will also delete the \".mesh\" files in the folder \"{UtilityServices.LOD_ASSETS_DEFAULT_SAVE_PATH}\" that were created for this object during the LOD generation process. Please note that you will have to delete the empty folders manually.";
bool hasLODs = UtilityServices.HasLODs(Selection.activeGameObject);
EditorGUI.BeginDisabledGroup(!hasLODs);
didPressButton = GUILayout.Button(content, style, GUILayout.Height(17), GUILayout.ExpandWidth(true));
EditorGUI.EndDisabledGroup();
GUI.backgroundColor = originalColor;
if (didPressButton)
{
bool didSucceed = UtilityServices.DestroyLODs(Selection.activeGameObject);
if (didSucceed)
{
EditorUtility.DisplayDialog("Success", $"Successfully destroyed the LODS and deleted the associated mesh assets. Please note that you must delete the empty folders in the path {LOD_ASSETS_DEFAULT_SAVE_PATH} manually.", "Ok");
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
}
GUIUtility.ExitGUI();
}
#endregion Destroy LODs
EditorGUILayout.EndHorizontal();
#endregion Section Header
GUILayout.Space(14);
#region Draw LOD Level
for (int a = 0; a < dataContainer.currentLodLevelSettings.Count; a++)
{
var lodLevel = dataContainer.currentLodLevelSettings[a];
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
content = new GUIContent(); //FF6347ff //006699
if (isPlainSkin) { content.text = String.Format("<b>Level {0}</b>", a + 1); }
else { content.text = String.Format("<b><color=#3e2723>Level {0}</color></b>", a + 1); }
if (a == 0)
{
content.tooltip = $"This is the base lod level or LOD 0 as unity calls it. This would be the actual renderers of the current object. So, in this level the object renders at the highest quality";
if (isPlainSkin) { content.text = String.Format("<b>Level {0} (Base)</b>", a + 1); }
else { content.text = String.Format("<b><color=#3e2723>Level {0} (Base)</color></b>", a + 1); }
}
style = GUI.skin.label;
style.richText = true;
GUILayout.Label(content, style);
var previousBackgroundColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(Color.gray.r, Color.gray.g, Color.gray.b, 0.8f);
GUIContent deleteLevelButtonContent = new GUIContent("<b><color=#FFFFFFD2>X</color></b>", "Delete this LOD level.");
style = GUI.skin.button;
style.richText = true;
if (a != 0 && GUILayout.Button(deleteLevelButtonContent, GUILayout.Width(20)))
{
if (dataContainer.currentLodLevelSettings.Count > 1)
{
dataContainer.currentLodLevelSettings.RemoveAt(a);
a--;
}
}
GUI.backgroundColor = previousBackgroundColor;
EditorGUILayout.EndHorizontal();
GUILayout.Space(6);
if (a != 0)
{
#region Reduction Strength Slider
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Reduction Strength";
content.tooltip = "The intensity of the reduction process. This is the amount in percentage to reduce the model by in this LOD level. The lower this value the higher will be the quality of this LOD level. For the base level or level 1 you should keep this to 0.";
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(115));
lodLevel.reductionStrength = Mathf.Abs(GUILayout.HorizontalSlider(lodLevel.reductionStrength, 0, 100, GUILayout.Width(130), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
lodLevel.reductionStrength = Mathf.Abs(EditorGUILayout.FloatField(content, lodLevel.reductionStrength, style, GUILayout.Width(10), GUILayout.ExpandWidth(true)));
if ((int)lodLevel.reductionStrength > 100)
{
lodLevel.reductionStrength = GetFirstNDigits((int)lodLevel.reductionStrength, 2);
}
style = GUI.skin.label;
content.text = "<b><size=13>%</size></b>";
EditorGUILayout.LabelField(content, style, GUILayout.Width(20));
GUILayout.EndHorizontal();
#endregion Reduction Strength Slider
}
#region Screen relative transition height
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Transition Height";
content.tooltip = "The screen relative height controls how far the viewing camera must be from the object before a transition to the next LOD level is made.";
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(115));
float oldHeight = lodLevel.transitionHeight;
lodLevel.transitionHeight = Mathf.Abs(GUILayout.HorizontalSlider(lodLevel.transitionHeight, 0, 1, GUILayout.Width(130), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
lodLevel.transitionHeight = Mathf.Abs(EditorGUILayout.FloatField(content, lodLevel.transitionHeight, style, GUILayout.Width(10), GUILayout.ExpandWidth(true)));
lodLevel.transitionHeight = Mathf.Clamp01(lodLevel.transitionHeight);
if (!Mathf.Approximately(oldHeight, lodLevel.transitionHeight) && a > 0)
{
float lastLevelHeight = dataContainer.currentLodLevelSettings[a - 1].transitionHeight;
float currentLevelHeight = lodLevel.transitionHeight;
if ((lastLevelHeight - currentLevelHeight) <= 0.05f)
{
//Debug.Log($"Last level height {lastLevelHeight} currentLevelHeight = {currentLevelHeight} Mathf.Abs(lastLevelHeight - currentLevelHeight) " +(Mathf.Abs(lastLevelHeight - currentLevelHeight) + " a is " + a));
lodLevel.transitionHeight = lastLevelHeight - 0.05f;
lodLevel.transitionHeight = Mathf.Clamp01(lodLevel.transitionHeight);
}
}
if (!Mathf.Approximately(oldHeight, lodLevel.transitionHeight) && a != (dataContainer.currentLodLevelSettings.Count - 1))
{
float nextLevelHeight = dataContainer.currentLodLevelSettings[a + 1].transitionHeight;
float currentLevelHeight = lodLevel.transitionHeight;
if ((currentLevelHeight - nextLevelHeight) <= 0.05f)
{
//Debug.Log($"Next level height {nextLevelHeight} currentLevelHeight = {currentLevelHeight} Mathf.Abs(lastLevelHeight - currentLevelHeight) " +(Mathf.Abs(lastLevelHeight - currentLevelHeight) + " a is " + a));
lodLevel.transitionHeight = nextLevelHeight + 0.05f;
lodLevel.transitionHeight = Mathf.Clamp01(lodLevel.transitionHeight);
}
}
GUILayout.Space(24);
GUILayout.EndHorizontal();
#endregion Screen relative transition height
if (a != 0)
{
#region Reduction extra options
GUILayout.Space(2);
EditorGUILayout.BeginHorizontal();
GUILayout.Space(16);
content = new GUIContent();
content.text = "Reduction Options";
content.tooltip = "Expand this section to see options for mesh simplification for this LOD level.";
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
#endif
lodLevel.simplificationOptionsFoldout = EditorGUILayout.Foldout(lodLevel.simplificationOptionsFoldout, content, true);
EditorGUILayout.EndHorizontal();
if (lodLevel.simplificationOptionsFoldout)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(6);
UtilityServices.DrawHorizontalLine(Color.black, 1, 8, 14);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
GUILayout.Space(16);
if (lodLevel.simplificationOptionsFoldout)
{
style = GUI.skin.label;
content.text = "Preserve UV Foldover";
content.tooltip = "Check this option to preserve UV foldover for this LOD level.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(135));
lodLevel.preserveUVFoldover = EditorGUILayout.Toggle(lodLevel.preserveUVFoldover, GUILayout.Width(18), GUILayout.ExpandWidth(false));
GUILayout.Space(8);
content.text = "Preserve UV Seams";
content.tooltip = "Preserve the mesh areas where the UV seams are made.These are the areas where different UV islands are formed (usually the shallow polygon conjested areas).";
EditorGUILayout.LabelField(content, style, GUILayout.Width(135));
lodLevel.preserveUVSeams = EditorGUILayout.Toggle(lodLevel.preserveUVSeams, GUILayout.Width(20), GUILayout.ExpandWidth(false));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.Space(16);
content.text = "Preserve Borders";
content.tooltip = "Check this option to preserve border edges for this LOD level. Border edges are the edges that are unconnected and open. Preserving border edges might lead to lesser polygon reduction but can be helpful where you see serious mesh and texture distortions.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(114));
GUILayout.Space(21);
lodLevel.preserveBorders = EditorGUILayout.Toggle(lodLevel.preserveBorders, GUILayout.Width(15), GUILayout.ExpandWidth(false));
GUILayout.Space(11);
//content.text = "Smart Linking";
//content.tooltip = "Smart linking links vertices that are very close to each other. This helps in the mesh simplification process where holes or other serious issues could arise. Disabling this (where not needed) can cause a minor performance gain.";
content.text = "Use Edge Sort";
content.tooltip = "Using edge sort can result in very good quality mesh simplification in some cases but can be a little slow to run.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(114));
GUILayout.Space(21);
lodLevel.useEdgeSort = EditorGUILayout.Toggle(lodLevel.useEdgeSort, GUILayout.Width(10), GUILayout.ExpandWidth(false));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
content.text = "Regard Curvature";
content.tooltip = "Check this option to take into account the discrete curvature of mesh surface during simplification. Taking surface curvature into account can result in very good quality mesh simplification, but it can slow the simplification process significantly.";
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(135));
lodLevel.regardCurvature = EditorGUILayout.Toggle(lodLevel.regardCurvature, GUILayout.Width(15));
GUILayout.Space(11);
content.text = "Recalculate Normals";
content.tooltip = "Recalculate mesh normals after simplification in this LOD level. Use this option if you see incorrect lighting or dark regions on the simplified mesh(es). This also recalculates the tangents afterwards.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(135));
lodLevel.recalculateNormals = EditorGUILayout.Toggle(lodLevel.recalculateNormals, GUILayout.Width(15), GUILayout.ExpandWidth(false));
EditorGUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(17);
#else
GUILayout.Space(16);
#endif
content.text = "Aggressiveness";
content.tooltip = "The agressiveness of the reduction algorithm to use for this LOD level. Higher number equals higher quality, but more expensive to run. Lowest value is 7.";
#if UNITY_2019_1_OR_NEWER
EditorGUILayout.LabelField(content, GUILayout.Width(134));
#else
EditorGUILayout.LabelField(content, GUILayout.Width(134));
GUILayout.Space(2);
#endif
content.text = "";
lodLevel.aggressiveness = Mathf.Abs(EditorGUILayout.FloatField(content, lodLevel.aggressiveness, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
if (lodLevel.aggressiveness < 7) { lodLevel.aggressiveness = 7; }
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Space(16);
content.text = "Max Iterations";
content.tooltip = "The maximum passes the reduction algorithm does for this LOD level. Higher number is more expensive but can bring you closer to your target quality. 100 is the lowest allowed value.";
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
EditorGUILayout.LabelField(content, GUILayout.Width(134));
#else
EditorGUILayout.LabelField(content, GUILayout.Width(136));
#endif
content.text = "";
#if UNITY_2019_1_OR_NEWER
lodLevel.maxIterations = Mathf.Abs(EditorGUILayout.IntField(content, lodLevel.maxIterations, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
#else
lodLevel.maxIterations = Mathf.Abs(EditorGUILayout.IntField(content, lodLevel.maxIterations, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
#endif
if (lodLevel.maxIterations < 100) { lodLevel.maxIterations = 100; }
}
GUILayout.EndHorizontal();
#endregion Reduction extra options
#region Regard Tolerance Sphere And Combine Meshes
if (lodLevel.simplificationOptionsFoldout)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(6);
UtilityServices.DrawHorizontalLine(Color.black, 1, 8, 14);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
content.text = "Regard Tolerance";
content.tooltip = "Check this option if you want this LOD level to regard the tolerance sphere and retain the original quality of the mesh area enclosed within the tolerance sphere. Please note that the LOD generation for this level with preservation sphere might get slow.";
style = GUI.skin.label;
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(114));
lodLevel.regardTolerance = EditorGUILayout.Toggle(lodLevel.regardTolerance, GUILayout.Width(28), GUILayout.ExpandWidth(false));
#if UNITY_2019_1_OR_NEWER
width = 108;
#else
GUILayout.Space(2);
width = 109;
#endif
/* [DEPRECATED. USE BATCH FEW TO COMBINE MESHES]
content.text = "Combine Meshes";
content.tooltip = "[Deprecated] Combine all renderers and meshes under this level into one, where possible. Please note that this option is present just in case if someone has any special use case where they need to generate LODs with some levels having combined meshes and some having uncombined meshes. You can now use BatchFew to combine meshes exclusively.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(width));
lodLevel.combineMeshes = EditorGUILayout.Toggle(lodLevel.combineMeshes, GUILayout.Width(28), GUILayout.ExpandWidth(false));
*/
EditorGUILayout.EndHorizontal();
#endregion Regard Tolerance Sphere And Combine Meshes
if(lodLevel.regardTolerance)
{
#region Tolerance Spheres Intensities
GUILayout.Space(2);
EditorGUILayout.BeginHorizontal();
GUILayout.Space(16);
content = new GUIContent();
content.text = "Spheres Intensities";
content.tooltip = "Expand this section to adjust the intensities of tolerance spheres for this LOD level.";
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
lodLevel.intensityFoldout = EditorGUILayout.Foldout(lodLevel.intensityFoldout, content, true);
#else
lodLevel.intensityFoldout = EditorGUILayout.Foldout(lodLevel.intensityFoldout, content, true);
#endif
EditorGUILayout.EndHorizontal();
if (lodLevel.intensityFoldout && lodLevel.regardTolerance)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(6);
UtilityServices.DrawHorizontalLine(Color.black, 1, 8, 14);
EditorGUILayout.EndHorizontal();
for (int b = 0; b < lodLevel.sphereIntensities.Count; b++)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(14);
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
content = new GUIContent(); //FF6347ff //006699 // 3e2723 //006699
if (isPlainSkin) { content.text = String.Format("Sphere {0} Intensity", b + 1); }
else { content.text = String.Format("<color=#006699>Sphere {0} Intensity</color>", b + 1); }
content.tooltip = "The percentage of triangles to preserve in the region enclosed by this preservation sphere, for this LOD level.";
style = GUI.skin.label;
style.richText = true;
EditorGUILayout.LabelField(content, style, GUILayout.Width(114));
width = 122;
float oldValue = lodLevel.sphereIntensities[b];
lodLevel.sphereIntensities[b] = Mathf.Abs(GUILayout.HorizontalSlider(lodLevel.sphereIntensities[b], 0, 100, GUILayout.Width(width), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(6);
content.text = "";
oldValue = lodLevel.sphereIntensities[b];
lodLevel.sphereIntensities[b] = Mathf.Abs(EditorGUILayout.FloatField(content, lodLevel.sphereIntensities[b], style, GUILayout.Width(3), GUILayout.ExpandWidth(true)));
if ((int)lodLevel.sphereIntensities[b] > 100)
{
lodLevel.sphereIntensities[b] = GetFirstNDigits((int)lodLevel.sphereIntensities[b], 2);
}
#if UNITY_2019_1_OR_NEWER
width = 16;
#else
width = 20;
#endif
style = GUI.skin.label;
content.text = "<b><size=13>%</size></b>";
EditorGUILayout.LabelField(content, style, GUILayout.Width(width));
EditorGUILayout.EndHorizontal();
GUILayout.EndHorizontal();
}
}
#endregion Tolerance Spheres Intensities
}
}
EditorGUILayout.EndVertical();
}
#endregion Draw LOD Level
}
#endregion AUTO LOD
#region BATCH FEW
DrawBatchFewUI();
#endregion BATCH FEW
}
EditorGUILayout.EndVertical();
}
else if (Selection.gameObjects != null && Selection.gameObjects.Length > 1)
{
#region AUTO LOD
EditorGUILayout.BeginVertical("GroupBox");
#region TITLE HEADER
GUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
content = new GUIContent();
if(isPlainSkin) { content.text = "<size=13><b>AUTOMATIC LOD</b></size>"; }
else { content.text = "<size=13><b><color=#A52A2AFF>AUTOMATIC LOD</color></b></size>"; }
content.tooltip = "Expand this section to see options for automatic LOD generation.";
style = EditorStyles.foldout;
style.richText = true; // #FF6347ff //A52A2AFF
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
style.padding = new RectOffset(20, 0, -1, 0);
GUILayout.Space(19);
FoldAutoLODMultiple = !EditorGUILayout.Foldout(!FoldAutoLODMultiple, content, true, style);
style.padding = prevPadding;
style = new GUIStyle();
style.richText = true;
EditorGUILayout.EndHorizontal();
#endregion TITLE HEADER
if (!FoldAutoLODMultiple)
{
UtilityServices.DrawHorizontalLine(Color.black, 1, 8);
GUILayout.Space(6);
#region Section Header
GUILayout.Space(6);
EditorGUILayout.BeginHorizontal();
#region Change Save Path
style = GUI.skin.button;
style.richText = true;
content = new GUIContent();
if(isPlainSkin) { content.text = "<b><size=11>Change Save Path</size></b>"; }
else { content.text = "<b><size=11><color=#006699>Change Save Path</color></size></b>"; }
content.tooltip = "Change the path where the generated LODs mesh assets will be saved. If you don't select a path the default path will be used. Please note that the chosen path will be used for saving LOD mesh assets in the future, unless changed.";
if (GUILayout.Button(content, style, GUILayout.Width(134), GUILayout.Height(20), GUILayout.ExpandWidth(false)))
{
string toOpen = String.IsNullOrWhiteSpace(AutoLODSavePath) ? "Assets/" : AutoLODSavePath;
if (!String.IsNullOrWhiteSpace(toOpen))
{
if (toOpen.EndsWith("/")) { toOpen.Remove(toOpen.Length - 1, 1); }
if (!AssetDatabase.IsValidFolder(toOpen))
{
toOpen = "Assets/";
}
}
string path = EditorUtility.OpenFolderPanel("Choose LOD Assets Save path", toOpen, "");
//Validate the save path. It might be outside the assets folder
// User pressed the cancel button
if (string.IsNullOrWhiteSpace(path)) { }
else if (!UtilityServices.IsPathInAssetsDir(path))
{
EditorUtility.DisplayDialog("Invalid Path", "The path you chose is not valid.Please choose a path that points to a directory that exists in the project's Assets folder.", "Ok");
}
else
{
path = UtilityServices.GetValidFolderPath(path);
if (!string.IsNullOrWhiteSpace(path))
{
UtilityServices.AutoLODSavePath = UtilityServices.SetAndReturnStringPref("autoLODSavePath", path);
}
}
}
EditorGUI.EndDisabledGroup();
#endregion Change Save Path
GUILayout.Space(40);
#region Add LOD Level
content = new GUIContent();
//GUILayout.FlexibleSpace();
content.tooltip = "Add an LOD level.";
content.text = "<b>Add</b>";
#if UNITY_2019_1_OR_NEWER
width = 55;
#else
width = 40;
#endif
if (GUILayout.Button(content, style, GUILayout.Width(width), GUILayout.MaxHeight(24), GUILayout.ExpandWidth(true)))
{
if (dataContainer.currentLodLevelSettings == null)
{
dataContainer.currentLodLevelSettings = new List<DataContainer.LODLevelSettings>();
}
List<float> strengths = new List<float>();
foreach (var sphere in dataContainer.toleranceSpheres) { strengths.Add(100f); }
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(0, 0, false, false, false, true, false, 7, 100, false, false, false, strengths));
}
#endregion Add LOD Level
GUILayout.Space(2);
#region Generate LODs
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//# ffc14d 60%
//# F0FFFF 73%
//# F5F5DC 75%
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Generate LODS</b> </size>"; }
else { content.text = "<size=11> <b><color=#000000>Generate LODS</color></b> </size>"; }
content.tooltip = "Generate LODs for the selected GameObjects with the common settings specified. Please note that you must save the scene after successful generation of LODs and apply changes to any prefabs manually. Any errors will be silently ignored.";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(120), GUILayout.Height(24), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
if (didPressButton)
{
bool anySuccess = false;
// Delete LOD levels that have 0 screen relative height and 0 reduction strength(Excluding the 1st one)
List<DataContainer.LODLevelSettings> levelsToDelete = new List<DataContainer.LODLevelSettings>();
if (dataContainer.currentLodLevelSettings.Count > 1)
{
for (int a = 1; a < dataContainer.currentLodLevelSettings.Count; a++)
{
var lodLevel = dataContainer.currentLodLevelSettings[a];
if (Mathf.Approximately(lodLevel.transitionHeight, 0))
{
levelsToDelete.Add(lodLevel);
}
if (Mathf.Approximately(lodLevel.reductionStrength, 0))
{
levelsToDelete.Add(lodLevel);
}
}
}
foreach (var toDelete in levelsToDelete)
{
dataContainer.currentLodLevelSettings.Remove(toDelete);
}
if (dataContainer != null && dataContainer.currentLodLevelSettings != null && dataContainer.currentLodLevelSettings.Count > 1)
{
foreach (GameObject selected in Selection.gameObjects)
{
dataContainer.objectMeshPairs = UtilityServices.GetObjectMeshPairs(selected, true, true);
ReductionPending = false;
ReductionStrength = 0;
try
{
string error = "";
bool isSuccess = UtilityServices.GenerateLODS(selected, dataContainer.toleranceSpheres, dataContainer.currentLodLevelSettings, UtilityServices.AutoLODSavePath, (string err) =>
{
error = err;
}, false, GenerateUV2LODs);
EditorUtility.ClearProgressBar();
if (isSuccess)
{
anySuccess = true;
ApplyExtraOptionsForLOD(selected);
}
else
{
Debug.LogWarning($"Failed to generate LODs for GameObject \"{selected.name}\". {error}");
}
}
catch (Exception error)
{
EditorUtility.ClearProgressBar();
Debug.LogWarning($"Failed to generate LODs for GameObject \"{selected.name}\". The LODs for this object might be partially generated. {error.ToString()}");
}
}
if (anySuccess)
{
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
}
}
else
{
EditorUtility.DisplayDialog("Failed", "Failed to generate LODs. You must have at least one non-base lod level with reduction strength and transition height > 0. Press the \"Add\" button to add more lod levels.", "Ok");
}
GUIUtility.ExitGUI();
}
#endregion Generate LODs
EditorGUILayout.EndHorizontal();
GUILayout.Space(2);
EditorGUILayout.BeginHorizontal();
#if UNITY_2019_1_OR_NEWER
//GUILayout.Space(177);
#else
//GUILayout.Space(178);
#endif
#region Additional options
style = EditorStyles.toolbarDropDown;
style.richText = true;
content = new GUIContent();
content.text = "<size=11><b>Set Extra Options</b></size>";
content.tooltip = "Set extra options for the LOD generation process";
if (GUILayout.Button(content, style, GUILayout.Width(134), GUILayout.Height(17), GUILayout.ExpandWidth(false)))
{
if (lastRect != null)
{
lastRect = new Rect(Event.current.mousePosition, lastRect.size);
var definitions = new PopupToggleTemplate.ToggleDefinition[5];
content = new GUIContent();
content.text = "Copy static flags to new objects";
content.tooltip = "Copy the static flags from this object to the newly created LOD objects";
definitions[0] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
CopyParentStaticFlags = value;
},
() =>
{
return CopyParentStaticFlags;
});
content = new GUIContent();
content.text = "Copy layer to new objects";
content.tooltip = "Copy layer from this object to the newly created LOD objects";
definitions[1] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
CopyParentLayer = value;
},
() =>
{
return CopyParentLayer;
});
content = new GUIContent();
content.text = "Copy tag to new objects";
content.tooltip = "Copy tag from this object to the newly created LOD objects";
definitions[2] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
CopyParentTag = value;
},
() =>
{
return CopyParentTag;
});
content = new GUIContent();
content.text = "Generate UV2";
content.tooltip = "Should we generate uv2 with default settings for each mesh, and fill them in?. Note that generating uv2 can cause the LOD generation process to get slow";
definitions[3] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
GenerateUV2LODs = value;
},
() =>
{
return GenerateUV2LODs;
});
content = new GUIContent();
content.text = "Remove LODBackup Component";
content.tooltip = "Generate LODs but do not add the \"LODBackup\" component. Please note that without this component the \"DestroyLODs\" button won't function correctly and the LOD meshes in the folders will have to be deleted manually";
definitions[4] = new PopupToggleTemplate.ToggleDefinition(content, 190, -4, (bool value) =>
{
RemoveLODBackupComponent = value;
},
() =>
{
return RemoveLODBackupComponent;
});
PopupWindow.Show(lastRect, new PopupToggleTemplate(definitions, new Vector2(230, 128), null, null));
}
}
if (Event.current.type == EventType.Repaint) lastRect = GUILayoutUtility.GetLastRect();
#endregion Additional options
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(40);
#else
GUILayout.Space(40);
#endif
#region Copy Preview Settings
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//# ffc14d 60%
//# F0FFFF 73%
//# F5F5DC 75%
//GUI.backgroundColor = UtilityServices.HexToColor("#f9f5f5");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Copy Preview</b></size>"; }
else { content.text = "<size=11><color=#006699><b>Copy Preview</b></color></size>"; }
content.tooltip = $"Copies all the settings from the preview above into each LOD level";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(20), GUILayout.Height(17), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
if (didPressButton)
{
for (int a = 0; a < dataContainer.currentLodLevelSettings.Count; a++)
{
var lodLevel = dataContainer.currentLodLevelSettings[a];
// in Base level don't copy over any settings if reduction is 0
if(a == 0)
{
if(!Mathf.Approximately(lodLevel.reductionStrength, 0))
{
CopyOverPreviewSettings(lodLevel);
}
}
else
{
CopyOverPreviewSettings(lodLevel);
}
}
}
#endregion Copy Preview Settings
GUILayout.Space(2);
#region Destroy LODs
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Destroy LODs</b></size>"; }
else { content.text = "<size=11><color=#006699><b>Destroy LODs</b></color></size>"; }
content.tooltip = $"Destroy the generated LODs for all the selected objects. This will also delete the \".mesh\" files in the folder \"{UtilityServices.LOD_ASSETS_DEFAULT_SAVE_PATH}\" that were created for these objects during the LOD generation process. Please note that you will have to delete the empty folders manually.";
didPressButton = GUILayout.Button(content, style, GUILayout.Height(17), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
bool didAnySucceed = false;
if (didPressButton)
{
foreach (GameObject selected in Selection.gameObjects)
{
if (UtilityServices.HasLODs(selected))
{
try
{
didAnySucceed = UtilityServices.DestroyLODs(selected);
if (!didAnySucceed)
{
Debug.LogWarning($"Failed to delete LODs on GameObject \"{selected.name}\"");
}
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to delete LODs on GameObject \"{selected.name}\". {ex.ToString()}");
}
}
}
if (didAnySucceed)
{
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
}
GUIUtility.ExitGUI();
}
#endregion Destroy LODs
EditorGUILayout.EndHorizontal();
#endregion Section Header
GUILayout.Space(14);
#region Draw LOD Level
for (int a = 0; a < dataContainer.currentLodLevelSettings.Count; a++)
{
var lodLevel = dataContainer.currentLodLevelSettings[a];
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
content = new GUIContent(); //FF6347ff //006699
if(isPlainSkin) { content.text = String.Format("<b>Level {0}</b>", a + 1); }
else { content.text = String.Format("<b><color=#3e2723>Level {0}</color></b>", a + 1); }
if (a == 0)
{
content.tooltip = $"This is the base lod level or LOD 0 as unity calls it. This would be the actual renderers of the current object. So, in this level the object renders at the highest quality";
if (isPlainSkin) { content.text = String.Format("<b>Level {0} (Base)</b>", a + 1); }
else { content.text = String.Format("<b><color=#3e2723>Level {0} (Base)</color></b>", a + 1); }
}
style = GUI.skin.label;
style.richText = true;
GUILayout.Label(content, style);
var previousBackgroundColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(Color.gray.r, Color.gray.g, Color.gray.b, 0.8f);
GUIContent deleteLevelButtonContent = new GUIContent("<b><color=#FFFFFFD2>X</color></b>", "Delete this LOD level.");
style = GUI.skin.button;
style.richText = true;
if (a != 0 && GUILayout.Button(deleteLevelButtonContent, GUILayout.Width(20)))
{
if (dataContainer.currentLodLevelSettings.Count > 1)
{
dataContainer.currentLodLevelSettings.RemoveAt(a);
a--;
}
}
GUI.backgroundColor = previousBackgroundColor;
EditorGUILayout.EndHorizontal();
GUILayout.Space(6);
if (a != 0)
{
#region Reduction Strength Slider
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Reduction Strength";
content.tooltip = "The intensity of the reduction process. This is the amount in percentage to simplify the selected GameObjects by in this LOD level. The lower this value the higher will be the quality of this LOD level. For the base level or level 1 you should keep this to 0.";
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(115));
lodLevel.reductionStrength = Mathf.Abs(GUILayout.HorizontalSlider(lodLevel.reductionStrength, 0, 100, GUILayout.Width(130), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
lodLevel.reductionStrength = Mathf.Abs(EditorGUILayout.FloatField(content, lodLevel.reductionStrength, style, GUILayout.Width(10), GUILayout.ExpandWidth(true)));
if ((int)lodLevel.reductionStrength > 100)
{
lodLevel.reductionStrength = GetFirstNDigits((int)lodLevel.reductionStrength, 2);
}
style = GUI.skin.label;
content.text = "<b><size=13>%</size></b>";
EditorGUILayout.LabelField(content, style, GUILayout.Width(20));
GUILayout.EndHorizontal();
#endregion Reduction Strength Slider
}
#region Screen relative transition height
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Transition Height";
content.tooltip = "The screen relative height controls how far the viewing camera must be from an object before a transition to the next LOD level is made.";
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(115));
float oldHeight = lodLevel.transitionHeight;
lodLevel.transitionHeight = Mathf.Abs(GUILayout.HorizontalSlider(lodLevel.transitionHeight, 0, 1, GUILayout.Width(130), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
lodLevel.transitionHeight = Mathf.Abs(EditorGUILayout.FloatField(content, lodLevel.transitionHeight, style, GUILayout.Width(10), GUILayout.ExpandWidth(true)));
lodLevel.transitionHeight = Mathf.Clamp01(lodLevel.transitionHeight);
if (!Mathf.Approximately(oldHeight, lodLevel.transitionHeight) && a > 0)
{
float lastLevelHeight = dataContainer.currentLodLevelSettings[a - 1].transitionHeight;
float currentLevelHeight = lodLevel.transitionHeight;
if ((lastLevelHeight - currentLevelHeight) <= 0.05f)
{
//Debug.Log($"Last level height {lastLevelHeight} currentLevelHeight = {currentLevelHeight} Mathf.Abs(lastLevelHeight - currentLevelHeight) " +(Mathf.Abs(lastLevelHeight - currentLevelHeight) + " a is " + a));
lodLevel.transitionHeight = lastLevelHeight - 0.05f;
lodLevel.transitionHeight = Mathf.Clamp01(lodLevel.transitionHeight);
}
}
if (!Mathf.Approximately(oldHeight, lodLevel.transitionHeight) && a != (dataContainer.currentLodLevelSettings.Count - 1))
{
float nextLevelHeight = dataContainer.currentLodLevelSettings[a + 1].transitionHeight;
float currentLevelHeight = lodLevel.transitionHeight;
if ((currentLevelHeight - nextLevelHeight) <= 0.05f)
{
//Debug.Log($"Next level height {nextLevelHeight} currentLevelHeight = {currentLevelHeight} Mathf.Abs(lastLevelHeight - currentLevelHeight) " +(Mathf.Abs(lastLevelHeight - currentLevelHeight) + " a is " + a));
lodLevel.transitionHeight = nextLevelHeight + 0.05f;
lodLevel.transitionHeight = Mathf.Clamp01(lodLevel.transitionHeight);
}
}
GUILayout.Space(24);
GUILayout.EndHorizontal();
#endregion Screen relative transition height
if (a != 0)
{
GUILayout.Space(2);
EditorGUILayout.BeginHorizontal();
GUILayout.Space(16);
content = new GUIContent();
content.text = "Reduction Options";
content.tooltip = "Expand this section to see options for mesh simplification for this LOD level.";
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
lodLevel.simplificationOptionsFoldout = EditorGUILayout.Foldout(lodLevel.simplificationOptionsFoldout, content, true);
#else
lodLevel.simplificationOptionsFoldout = EditorGUILayout.Foldout(lodLevel.simplificationOptionsFoldout, content, true);
#endif
#region Combine Meshes
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(66);
#else
GUILayout.Space(40);
#endif
EditorGUILayout.BeginHorizontal();
/* [DEPRECATED. USE BATCH FEW TO COMBINE MESHES]
style = GUI.skin.label;
content.text = "Combine Meshes";
content.tooltip = "[Deprecated] Combine all renderers and meshes under this level into one, where possible. Please note that this option is present just in case if someone has any special use case where they need to generate LODs with some levels having combined meshes and some having uncombined meshes. You can now use BatchFew to combine meshes exclusively.";
// Added it to Batch few
EditorGUILayout.LabelField(content, style, GUILayout.Width(109));
lodLevel.combineMeshes = EditorGUILayout.Toggle(lodLevel.combineMeshes, GUILayout.Width(28), GUILayout.ExpandWidth(false));
*/
EditorGUILayout.EndHorizontal();
#endregion Combine Meshes
EditorGUILayout.EndHorizontal();
if (lodLevel.simplificationOptionsFoldout)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(6);
UtilityServices.DrawHorizontalLine(Color.black, 1, 8, 14);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
#region Reduction extra options
GUILayout.Space(16);
if (lodLevel.simplificationOptionsFoldout)
{
style = GUI.skin.label;
content.text = "Preserve UV Foldover";
content.tooltip = "Check this option to preserve UV foldover for this LOD level.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(135));
lodLevel.preserveUVFoldover = EditorGUILayout.Toggle(lodLevel.preserveUVFoldover, GUILayout.Width(18), GUILayout.ExpandWidth(false));
GUILayout.Space(8);
content.text = "Preserve UV Seams";
content.tooltip = "Preserve the mesh areas where the UV seams are made.These are the areas where different UV islands are formed (usually the shallow polygon conjested areas).";
EditorGUILayout.LabelField(content, style, GUILayout.Width(135));
lodLevel.preserveUVSeams = EditorGUILayout.Toggle(lodLevel.preserveUVSeams, GUILayout.Width(20), GUILayout.ExpandWidth(false));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.Space(16);
content.text = "Preserve Borders";
content.tooltip = "Check this option to preserve border edges for this LOD level. Border edges are the edges that are unconnected and open. Preserving border edges might lead to lesser polygon reduction but can be helpful where you see serious mesh and texture distortions.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(114));
GUILayout.Space(21);
lodLevel.preserveBorders = EditorGUILayout.Toggle(lodLevel.preserveBorders, GUILayout.Width(15), GUILayout.ExpandWidth(false));
GUILayout.Space(11);
//content.text = "Smart Linking";
//content.tooltip = "Smart linking links vertices that are very close to each other. This helps in the mesh simplification process where holes or other serious issues could arise. Disabling this (where not needed) can cause a minor performance gain.";
content.text = "Use Edge Sort";
content.tooltip = "Using edge sort can result in very good quality mesh simplification in some cases but can be a little slow to run.";
EditorGUILayout.LabelField(content, style, GUILayout.Width(114));
GUILayout.Space(21);
lodLevel.useEdgeSort = EditorGUILayout.Toggle(lodLevel.useEdgeSort, GUILayout.Width(10), GUILayout.ExpandWidth(false));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
content.text = "Regard Curvature";
content.tooltip = "Check this option to take into account the discrete curvature of mesh surface during simplification. Taking surface curvature into account can result in very good quality mesh simplification, but it can slow the simplification process significantly.";
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(135));
lodLevel.regardCurvature = EditorGUILayout.Toggle(lodLevel.regardCurvature, GUILayout.Width(50));
EditorGUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(17);
#else
GUILayout.Space(16);
#endif
content.text = "Aggressiveness";
content.tooltip = "The agressiveness of the reduction algorithm to use for this LOD level. Higher number equals higher quality, but more expensive to run. Lowest value is 7.";
#if UNITY_2019_1_OR_NEWER
EditorGUILayout.LabelField(content, GUILayout.Width(134));
#else
EditorGUILayout.LabelField(content, GUILayout.Width(134));
GUILayout.Space(2);
#endif
content.text = "";
lodLevel.aggressiveness = Mathf.Abs(EditorGUILayout.FloatField(content, lodLevel.aggressiveness, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
if (lodLevel.aggressiveness < 7) { lodLevel.aggressiveness = 7; }
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Space(16);
content.text = "Max Iterations";
content.tooltip = "The maximum passes the reduction algorithm does for this LOD level. Higher number is more expensive but can bring you closer to your target quality. 100 is the lowest allowed value.";
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(1);
EditorGUILayout.LabelField(content, GUILayout.Width(134));
#else
EditorGUILayout.LabelField(content, GUILayout.Width(136));
#endif
content.text = "";
#if UNITY_2019_1_OR_NEWER
lodLevel.maxIterations = Mathf.Abs(EditorGUILayout.IntField(content, lodLevel.maxIterations, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
#else
lodLevel.maxIterations = Mathf.Abs(EditorGUILayout.IntField(content, lodLevel.maxIterations, GUILayout.Width(168), GUILayout.ExpandWidth(true)));
#endif
if (lodLevel.maxIterations < 100) { lodLevel.maxIterations = 100; }
}
#endregion Reduction extra options
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
#endregion Draw LOD Level
}
#endregion AUTO LOD
#region BATCH FEW
DrawBatchFewUI();
#endregion BATCH FEW
EditorGUILayout.EndVertical();
}
else if(!isFeasibleTargetForPolyFew && !areMultiObjectsSelected)
{
EditorGUILayout.BeginVertical("GroupBox");
#region BATCH FEW
DrawBatchFewUI(true);
#endregion BATCH FEW
EditorGUILayout.EndVertical();
}
}
public void DrawBatchFewUI(bool boxDrawnAlready = false)
{
if (FoldoutAutoLOD)
{
GUILayout.Space(12);
}
#region TITLE HEADER
if (!boxDrawnAlready)
{
UtilityServices.DrawHorizontalLine(Color.black, 1, 8);
}
GUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=13><b>BATCH FEW</b></size> <size=7><b>v2.50</b></size>"; }
else { content.text = "<size=13><b><color=#A52A2AFF>BATCH FEW</color></b></size> <size=7><b><color=#A52A2AFF>v2.50</color></b></size>"; }
content.tooltip = "Expand this section to see options for combining materials and meshes and for generating texture atlasses (Texture Arrays).";
style = EditorStyles.foldout;
style.richText = true; // #FF6347ff //A52A2AFF
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
style.padding = new RectOffset(20, 0, -1, 0);
GUILayout.Space(19);
FoldoutBatchFew = EditorGUILayout.Foldout(FoldoutBatchFew, content, true, style);
style.padding = prevPadding;
style = new GUIStyle();
style.richText = true;
EditorGUILayout.EndHorizontal();
#endregion TITLE HEADER
if (FoldoutBatchFew)
{
UtilityServices.DrawHorizontalLine(Color.black, 1, 8);
GUILayout.Space(6);
#region Section Header
GUILayout.Space(6);
EditorGUILayout.BeginHorizontal();
#region Change Save Path
style = GUI.skin.button;
style.richText = true;
content = new GUIContent();
if (isPlainSkin) { content.text = "<b><size=11>Change Save Path</size></b>"; }
else { content.text = "<b><size=11><color=#006699>Change Save Path</color></size></b>"; }
content.tooltip = "Change the path where the new combined meshes, texture atlases and materials will be saved. If you don't select a path the default path will be used. Please note that the chosen path will be used for saving such assets in the future, unless changed.";
if (GUILayout.Button(content, style, GUILayout.Width(134), GUILayout.Height(20), GUILayout.ExpandWidth(false)))
{
string toOpen = String.IsNullOrWhiteSpace(BatchFewSavePath) ? "Assets/" : BatchFewSavePath;
if (!String.IsNullOrWhiteSpace(toOpen))
{
if (toOpen.EndsWith("/")) { toOpen.Remove(toOpen.Length - 1, 1); }
if (!AssetDatabase.IsValidFolder(toOpen))
{
toOpen = "Assets/";
}
}
string path = EditorUtility.OpenFolderPanel("Choose Mesh and Material Combiner assets save path", toOpen, "");
//Validate the save path. It might be outside the assets folder
// User pressed the cancel button
if (string.IsNullOrWhiteSpace(path)) { }
else if (!UtilityServices.IsPathInAssetsDir(path))
{
EditorUtility.DisplayDialog("Invalid Path", "The path you chose is not valid.Please choose a path that points to a directory that exists in the project's Assets folder.", "Ok");
}
else
{
path = UtilityServices.GetValidFolderPath(path);
if (!string.IsNullOrWhiteSpace(path))
{
UtilityServices.BatchFewSavePath = UtilityServices.SetAndReturnStringPref("batchFewSavePath", path);
}
}
}
EditorGUI.EndDisabledGroup();
#endregion Change Save Path
GUILayout.Space(40);
GUILayout.Space(2);
#region Combine Materials
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
//# ffc14d 60%
//# F0FFFF 73%
//# F5F5DC 75%
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11> <b>Combine Materials</b> </size>"; }
else { content.text = "<size=11> <b><color=#000000>Combine Materials</color></b> </size>"; }
content.tooltip = "Combine all the materials in the selected objects and generate Texture Atlases with the settings specified. Materials that don't use the Standard Shader or it's variants (Standard Specular etc) will be ignored. Please note that you must save the scene after successful operations and apply changes to any prefabs manually.";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(120), GUILayout.Height(24), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
if (didPressButton)
{
var cS = dataContainer.colorSpaceChoices[dataContainer.choiceDiffuseColorSpace];
var colorSpace = (CombiningInformation.DiffuseColorSpace)Enum.Parse(typeof(CombiningInformation.DiffuseColorSpace), cS.ToUpper());
try
{
PolyFewResetter.ResetToInitialState();
GameObject forObject = Selection.activeGameObject;
bool success = MaterialCombiner.CombineMaterials(Selection.gameObjects, BatchFewSavePath, dataContainer.textureArraysSettings, colorSpace, ConsiderChildrenBatchFew, RemoveMaterialLinkComponent, true, (string error) =>
{
// Do something on error
});
if (success)
{
EditorUtility.DisplayDialog("Operation Successfull", "Successfully combined materials in the selected objects. Please note that changes to any prefabs must be applied manually", "Ok");
PolyFewResetter.RefreshObjectMeshPairs(forObject);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
}
}
catch (Exception ex)
{
string error;
if (ex is TextureFormatNotSupportedException)
{
error = ex.Message;
}
else
{
error = $"Failed to combine materials due to unknown reasons. Please check console for any clues.";
}
EditorUtility.DisplayDialog("Operation Failed", error, "Ok");
Debug.LogError(ex.StackTrace);
}
}
#endregion Combine Materials
EditorGUILayout.EndHorizontal();
GUILayout.Space(2);
EditorGUILayout.BeginHorizontal();
#region Additional options batch few
style = EditorStyles.toolbarDropDown;
style.richText = true;
content = new GUIContent();
content.text = "<size=11><b>Set Extra Options</b></size>";
content.tooltip = "Set extra options for the mesh combiner and material merger";
if (GUILayout.Button(content, style, GUILayout.Width(134), GUILayout.Height(17), GUILayout.ExpandWidth(false)))
{
if (lastRect != null)
{
lastRect = new Rect(Event.current.mousePosition, lastRect.size);
var definitions = new PopupToggleTemplate.ToggleDefinition[4];
content = new GUIContent();
content.text = "Remove MaterialLinks Component";
content.tooltip = "Combine materials but do not add the \"ObjectMaterialLinks\" component. Please note that without this component you won't be able to adjust the individual material properties after combining the materials";
definitions[0] = new PopupToggleTemplate.ToggleDefinition(content, 200, -4, (bool value) =>
{
RemoveMaterialLinkComponent = value;
},
() =>
{
return RemoveMaterialLinkComponent;
});
content = new GUIContent();
content.text = "Create As Children";
content.tooltip = "Make newly created GameObjects children of the object whose meshes are converted or combined?. Selecting yes will also disable all renderers in the target GameObject. Chosing no will simply disable the target object and create a new object in the scene hierarchy with the new mesh";
definitions[1] = new PopupToggleTemplate.ToggleDefinition(content, 200, -4, (bool value) =>
{
CreateAsChildren = value;
},
() =>
{
return CreateAsChildren;
});
content = new GUIContent();
content.text = "Generate UV2";
content.tooltip = "Should we generate uv2 with default settings for each mesh and fill them in?. This options is only valid when combining meshes or converting skinned meshes to non skinned meshes. Generating uv2 can also cause the respective processes to get slow";
definitions[2] = new PopupToggleTemplate.ToggleDefinition(content, 200, -4, (bool value) =>
{
GenerateUV2batchfew = value;
},
() =>
{
return GenerateUV2batchfew;
});
content = new GUIContent();
content.text = "Consider Children";
content.tooltip = "Checking this option will automatically take into account all of the deep nested children of the selected object(s) while combining materials without the need for explicitly selecting each child object. If this option is unchecked then only the selected objects(Without their children) are considered while combining the materials.";
definitions[3] = new PopupToggleTemplate.ToggleDefinition(content, 200, -4, (bool value) =>
{
ConsiderChildrenBatchFew = value;
},
() =>
{
return ConsiderChildrenBatchFew;
});
PopupWindow.Show(lastRect, new PopupToggleTemplate(definitions, new Vector2(240, 104), null, null));
}
}
if (Event.current.type == EventType.Repaint) lastRect = GUILayoutUtility.GetLastRect();
#endregion Additional options batch few
GUILayout.Space(42);
#region CONVERT SKINNED MESHES
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Convert Skinned Meshes</b></size>"; }
else { content.text = "<size=11><color=#000000><b>Convert Skinned Meshes</b></color></size>"; }
content.tooltip = "Convert skinned meshes to non skinned/static meshes in the selected object. Please note that all animation data and bones hierarchy from the skinned mesh(es) will be lost. If \"Consider Children\" option in \"Set Extra Options\" is checked then all the deep nested skinned meshes under this object will also be converted, otherwise only the skinned mesh renderer if any attached to this particular object is considered.";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(170), GUILayout.Height(20), GUILayout.ExpandWidth(true));
if(didPressButton && ReductionPending)
{
didPressButton = EditorUtility.DisplayDialog("Warning", "You have a reduction applied in preview mode. If you continue with this the converted skinned meshes will be simplified as seen in the preview mode. You can move the reduction strength slider to 0 if you don't want this.", "Continue", "No");
}
GUI.backgroundColor = originalColor;
if (didPressButton)
{
if (Selection.gameObjects.Length == 1)
{
bool didSucceed = true;
GameObject toDuplicate = Selection.activeGameObject;
var duplicated = GameObject.Instantiate(toDuplicate);
duplicated.name = toDuplicate.name;
var origPos = duplicated.transform.position;
var origRot = duplicated.transform.rotation;
var origScale = duplicated.transform.localScale;
duplicated.transform.position = Vector3.one;
duplicated.transform.rotation = Quaternion.identity;
duplicated.transform.localScale = Vector3.one;
try
{
UtilityServices.ConvertSkinnedMeshes(duplicated, duplicated.name, BatchFewSavePath, (errorTitle, error) =>
{
didSucceed = false;
EditorUtility.DisplayDialog(errorTitle, error, "Ok");
}, ConsiderChildrenBatchFew, GenerateUV2batchfew);
}
catch (Exception ex)
{
didSucceed = false;
EditorUtility.DisplayDialog("Operation Failed", $"Failed to convert skinned meshes. Please check the console for details.", "Ok");
Debug.LogError(ex.ToString());
}
if (didSucceed)
{
string message = "";
if (CreateAsChildren)
{
message = $"All renderers in the target GameObject have been disabled. A copy of the original object \"{duplicated.name}\" with the converted mesh(es) has been created as a child to the target object. Any changes to prefabs must be manually applied.";
}
else
{
message = $"The original GameObject has been disabled. A copy of the original object \"{duplicated.name}\" with the converted mesh(es) has been created. Any changes to prefabs must be manually applied.";
}
EditorUtility.DisplayDialog("Successfully Converted Skinned Meshes", message, "Ok");
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
duplicated.name = toDuplicate.name + "_SkinnedConverted_Meshes";
duplicated.transform.position = origPos;
duplicated.transform.rotation = origRot;
duplicated.transform.localScale = origScale;
if (CreateAsChildren)
{
foreach (var renderer in toDuplicate.GetComponentsInChildren<Renderer>())
{
renderer.enabled = false;
}
duplicated.transform.parent = toDuplicate.transform;
}
else
{
toDuplicate.SetActive(false);
}
}
else
{
DestroyImmediate(duplicated);
}
// Remove Polyfew on the duplicated object to avoid inconsistency with datastructures
PolyFew polyfew = duplicated.GetComponent<PolyFew>();
if (polyfew)
{
DestroyImmediate(polyfew);
}
EditorUtility.ClearProgressBar();
}
else if (Selection.gameObjects.Length > 1)
{
bool anySuccess = false;
foreach (var selection in Selection.gameObjects)
{
bool didSucceed = true;
GameObject duplicated = GameObject.Instantiate(selection);
duplicated.name = selection.name;
var origPos = duplicated.transform.position;
var origRot = duplicated.transform.rotation;
var origScale = duplicated.transform.localScale;
duplicated.transform.position = Vector3.one;
duplicated.transform.rotation = Quaternion.identity;
duplicated.transform.localScale = Vector3.one;
try
{
UtilityServices.ConvertSkinnedMeshes(duplicated, duplicated.name, BatchFewSavePath, (errorTitle, error) =>
{
didSucceed = false;
Debug.LogWarning(error);
}, ConsiderChildrenBatchFew, GenerateUV2batchfew);
}
catch (Exception ex)
{
didSucceed = false;
Debug.LogError(ex);
EditorUtility.ClearProgressBar();
}
if (didSucceed)
{
anySuccess = true;
duplicated.name = selection.name + "_SkinnedConverted_Meshes";
duplicated.transform.position = origPos;
duplicated.transform.rotation = origRot;
duplicated.transform.localScale = origScale;
if (CreateAsChildren)
{
foreach (var renderer in selection.GetComponentsInChildren<Renderer>())
{
renderer.enabled = false;
}
duplicated.transform.parent = selection.transform;
}
else
{
selection.SetActive(false);
}
}
else
{
DestroyImmediate(duplicated);
}
// Remove Polyfew on the duplicated object to avoid inconsistency with datastructures
PolyFew polyfew = duplicated.GetComponent<PolyFew>();
if (polyfew)
{
DestroyImmediate(polyfew);
}
}
if (anySuccess)
{
string message = "";
if (CreateAsChildren)
{
message = $"All renderers in the target GameObjects have been disabled. A copy of each original object with the converted mesh(es) has been created as a child to the corresponding target object. Any changes to prefabs must be manually applied.";
}
else
{
message = $"The original GameObjects have been disabled. Copies of the original objects with converted mesh(es) have been created. Any changes to prefabs must be manually applied.";
}
EditorUtility.DisplayDialog("Successfully Converted Skinned Meshes", message, "Ok");
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
}
else
{
EditorUtility.DisplayDialog("Operation Failed", "No skinned meshes converted, see the console for more information.", "Ok");
}
}
}
#endregion CONVERT SKINNED MESHES
GUILayout.Space(2);
#region COMBINE MESHES
bool canCombineMeshes = false;
canCombineMeshes = Selection.gameObjects != null && Selection.gameObjects.Length == 1;
EditorGUI.BeginDisabledGroup(!canCombineMeshes);
style = GUI.skin.button;
style.richText = true;
originalColor = new Color(GUI.backgroundColor.r, GUI.backgroundColor.g, GUI.backgroundColor.b);
GUI.backgroundColor = UtilityServices.HexToColor("#F5F5DC");
content = new GUIContent();
if (isPlainSkin) { content.text = "<size=11><b>Combine Meshes</b></size>"; }
else { content.text = "<size=11><color=#000000><b>Combine Meshes</b></color></size>"; }
content.tooltip = "Combine all renderers and meshes nested under the selected object(s). Select a top level parent root object to begin with.";
didPressButton = GUILayout.Button(content, style, GUILayout.Width(120), GUILayout.Height(20), GUILayout.ExpandWidth(true));
GUI.backgroundColor = originalColor;
if (didPressButton)
{
int option = EditorUtility.DisplayDialogComplex("Choose Combine Target",
"What kind of meshes/renderers do you want to combine?. Both skinned and static?, Static meshes only? or Skinned meshes only?. Closing this window will only combine static meshes.",
"Skinned And Static",
"Static Only",
"Skinned Only");
UtilityServices.MeshCombineTarget combineTarget = MeshCombineTarget.SkinnedAndStatic;
switch (option)
{
case 0:
combineTarget = MeshCombineTarget.SkinnedAndStatic;
break;
case 1:
combineTarget = MeshCombineTarget.StaticOnly;
break;
case 2:
combineTarget = MeshCombineTarget.SkinnedOnly;
break;
default:
combineTarget = MeshCombineTarget.SkinnedAndStatic;
break;
}
if (Selection.gameObjects.Length == 1)
{
bool didSucceed = true;
GameObject toDuplicate = Selection.activeGameObject;
var duplicated = GameObject.Instantiate(toDuplicate);
duplicated.name = toDuplicate.name;
var origPos = duplicated.transform.position;
var origRot = duplicated.transform.rotation;
var origScale = duplicated.transform.localScale;
duplicated.transform.position = Vector3.one;
duplicated.transform.rotation = Quaternion.identity;
duplicated.transform.localScale = Vector3.one;
try
{
UtilityServices.CombineMeshes(duplicated, duplicated.name, BatchFewSavePath, (errorTitle, error) =>
{
didSucceed = false;
EditorUtility.DisplayDialog(errorTitle, error, "Ok");
}, combineTarget, GenerateUV2batchfew);
}
catch (Exception ex)
{
didSucceed = false;
EditorUtility.DisplayDialog("Operation Failed", $"Failed to combine meshes. Please check the console for details.", "Ok");
Debug.LogError(ex.ToString());
}
if (didSucceed)
{
string message = "";
if(CreateAsChildren)
{
message = $"All renderers in the target GameObject have been disabled. A copy of the original object \"{duplicated.name}\" with combined mesh(es) has been created as a children to the target object. Please note that the root bones for the combined skinned mesh renderers should not be deleted in the new object. Any changes to prefabs must be manually applied.";
}
else
{
message = $"The original GameObject has been disabled. A copy of the original object \"{duplicated.name}\" with combined mesh(es) has been created. Please note that the root bones for the combined skinned mesh renderers should not be deleted in the new object. Any changes to prefabs must be manually applied.";
}
EditorUtility.DisplayDialog("Successfully Combined Meshes", message, "Ok");
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
duplicated.name = toDuplicate.name + "Combined_Meshes";
duplicated.transform.position = origPos;
duplicated.transform.rotation = origRot;
duplicated.transform.localScale = origScale;
if (CreateAsChildren)
{
foreach (var renderer in toDuplicate.GetComponentsInChildren<Renderer>())
{
renderer.enabled = false;
}
duplicated.transform.parent = toDuplicate.transform;
}
else
{
toDuplicate.SetActive(false);
}
}
else
{
DestroyImmediate(duplicated);
}
// Remove Polyfew on the duplicated object to avoid inconsistency with datastructures
PolyFew polyfew = duplicated.GetComponent<PolyFew>();
if (polyfew)
{
DestroyImmediate(polyfew);
}
// Destroy the dummy gameobject created when collapsing submeshes
// in the case of a single object
if (UtilityServices.dummyStatic != null)
{
DestroyImmediate(UtilityServices.dummyStatic);
}
if(UtilityServices.dummySkinned != null)
{
DestroyImmediate(UtilityServices.dummySkinned);
}
EditorUtility.ClearProgressBar();
}
}
EditorGUI.EndDisabledGroup();
#endregion COMBINE MESHES
EditorGUILayout.EndHorizontal();
#endregion Section Header
GUILayout.Space(10);
#region DRAW TEXTURE ARRAY SETTINGS
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
UtilityServices.DrawHorizontalLine(Color.black, 1, 2, 7, 4);
content = new GUIContent();//TEXTURE ARRAYS SETTINGS //2F4F4F //008080 //191970 //006699
if (isPlainSkin) { content.text = "<b>Texture Arrays Settings</b>"; }
else { content.text = "<b><color=#006699>Texture Arrays Settings</color></b>"; }
content.tooltip = "Settings for the generated Texture Arrays.";
style = GUI.skin.label;
style.richText = true;
TextAnchor OldAlignment = style.alignment;
style.alignment = TextAnchor.MiddleCenter;
EditorGUILayout.LabelField(content, style);
style.alignment = OldAlignment;
UtilityServices.DrawHorizontalLine(Color.black, 1, 1, 7, 4);
GUILayout.Space(4);
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
content = new GUIContent(); //FF6347ff //006699 //008080
if (isPlainSkin) { content.text = "<b>Texture Map Array :</b>"; }
else { content.text = "<b><color=#2F4F4F>Texture Map Array :</color></b>"; }
content.tooltip = "Choose a Texture map to adjust its generated Texture Array's settings.";
style = GUI.skin.label;
style.richText = true;
GUILayout.Label(content, style, GUILayout.Width(137), GUILayout.ExpandWidth(false));
dataContainer.choiceTextureMap = EditorGUILayout.Popup("", dataContainer.choiceTextureMap, dataContainer.textureMapsChoices, GUILayout.MinWidth(100), GUILayout.ExpandWidth(true));
style = GUI.skin.button;
style.richText = true;
content = new GUIContent();
if (isPlainSkin) { content.text = "<b><size=11>Reset</size></b>"; }
else { content.text = "<b><size=11><color=#D2691E>Reset</color></size></b>"; }
content.tooltip = "Reset all settings to default.";
if (GUILayout.Button(content, style, GUILayout.Width(80), GUILayout.Height(20)))
{
ResetTextureArrays();
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(6);
var selectedTextureArraySettings = GetTexArrSettingsFromName(dataContainer.textureMapsChoices[dataContainer.choiceTextureMap]);
#region TEXTURES RESOLUTION
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Textures Resolution";
content.tooltip = "The resolution of each texture in the selected Texture Map Array. Every texture in the selected Texture Map Array will be resized to this resolution. Texture Arrays have this inherent limitation that they must have same resolution textures in them.";
GUILayout.Space(12);
EditorGUILayout.LabelField(content, style, GUILayout.Width(130));
selectedTextureArraySettings.choiceResolutionW = EditorGUILayout.Popup("", selectedTextureArraySettings.choiceResolutionW, dataContainer.resolutionsChoices, GUILayout.Width(60), GUILayout.ExpandWidth(true));
content.text = "<b>x</b>";
GUILayout.Space(2);
EditorGUILayout.LabelField(content, style, GUILayout.Width(11));
GUILayout.Space(2);
selectedTextureArraySettings.choiceResolutionH = EditorGUILayout.Popup("", selectedTextureArraySettings.choiceResolutionH, dataContainer.resolutionsChoices, GUILayout.Width(60), GUILayout.ExpandWidth(true));
var res = new CombiningInformation.Resolution();
res.width = Int32.Parse(dataContainer.resolutionsChoices[selectedTextureArraySettings.choiceResolutionW]);
res.height = Int32.Parse(dataContainer.resolutionsChoices[selectedTextureArraySettings.choiceResolutionH]);
selectedTextureArraySettings.resolution = res;
EditorGUILayout.LabelField("", style, GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion TEXTURES RESOLUTION
#region FILTERING MODE
GUILayout.BeginHorizontal();
content.text = "Filtering Mode";
content.tooltip = "The filtering mode for the textures in the selected Texture Map Array.";
GUILayout.Space(13);
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
selectedTextureArraySettings.choiceFilteringMode = EditorGUILayout.Popup("", selectedTextureArraySettings.choiceFilteringMode, dataContainer.filteringModesChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
var fMode = dataContainer.filteringModesChoices[selectedTextureArraySettings.choiceFilteringMode];
FilterMode filteringMode;
if (fMode.ToLower().Contains("point")) { filteringMode = FilterMode.Point; }
else if (fMode.ToLower().Contains("Bilinear")) { filteringMode = FilterMode.Bilinear; }
else { filteringMode = FilterMode.Trilinear; }
selectedTextureArraySettings.filteringMode = filteringMode;
EditorGUILayout.LabelField("", style, GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion FILTERING MODE
#region ANISOTROPIC FILTERING
GUILayout.Space(-2);
GUILayout.BeginHorizontal();
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Anisotropic Level";
content.tooltip = "The level of the anisotropic filtering for the textures in the selected Texture Map Array.";
GUILayout.Space(13);
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
float floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(selectedTextureArraySettings.anisotropicFilteringLevel, 0, 16, GUILayout.Width(142), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
selectedTextureArraySettings.anisotropicFilteringLevel = Mathf.RoundToInt(floatLevel);
GUILayout.Space(5);
content.text = "";
selectedTextureArraySettings.anisotropicFilteringLevel = Mathf.Abs(EditorGUILayout.IntField(content, selectedTextureArraySettings.anisotropicFilteringLevel, style, GUILayout.Width(40)));
selectedTextureArraySettings.anisotropicFilteringLevel = Mathf.Clamp(selectedTextureArraySettings.anisotropicFilteringLevel, (int)0, (int)16);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion ANISOTROPIC FILTERING
#region COMPRESSION QUALITY
GUILayout.Space(1);
GUILayout.BeginHorizontal();
content.text = "Compression Quality";
content.tooltip = "The compression quality for the textures in the selected Texture Map Array. This option is only valid if the compression type selected is \"ASTC RGB\" ";
style = GUI.skin.label;
GUILayout.Space(12);
EditorGUI.BeginDisabledGroup(dataContainer.compressionTypesChoices[selectedTextureArraySettings.choiceCompressionType] != "ASTC_RGB");
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
selectedTextureArraySettings.choiceCompressionQuality = EditorGUILayout.Popup("", selectedTextureArraySettings.choiceCompressionQuality, dataContainer.compressionQualitiesChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
EditorGUI.EndDisabledGroup();
var cQ = dataContainer.compressionQualitiesChoices[selectedTextureArraySettings.choiceCompressionQuality];
var compQuality = (CombiningInformation.CompressionQuality)Enum.Parse(typeof(CombiningInformation.CompressionQuality), cQ.ToUpper());
selectedTextureArraySettings.compressionQuality = compQuality;
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion COMPRESSION QUALITY
#region COMPRESSION TYPE
GUILayout.BeginHorizontal();
content.text = "Compression Type";
content.tooltip = "The compression type for the textures in the selected Texture Map Array.";
style = GUI.skin.label;
GUILayout.Space(12);
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
selectedTextureArraySettings.choiceCompressionType = EditorGUILayout.Popup("", selectedTextureArraySettings.choiceCompressionType, dataContainer.compressionTypesChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
var cT = dataContainer.compressionTypesChoices[selectedTextureArraySettings.choiceCompressionType];
var compType = (CombiningInformation.CompressionType)Enum.Parse(typeof(CombiningInformation.CompressionType), cT.ToUpper());
selectedTextureArraySettings.compressionType = compType;
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion COMPRESSION TYPE
#region DIFFUSE COLOR SPACE
GUILayout.BeginHorizontal();
content.text = "Diffuse Color Space";
content.tooltip = "The color space diffuse maps are in. This should only be changed to \"Linear\" if you're generating Texture arrays on a platform where linear rendering mode can cause diffuse maps to be too dark, Occulus Quest is an example of such a platform.";
style = GUI.skin.label;
GUILayout.Space(12);
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
dataContainer.choiceDiffuseColorSpace = EditorGUILayout.Popup("", dataContainer.choiceDiffuseColorSpace, dataContainer.colorSpaceChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion DIFFUSE COLOR SPACE
GUILayout.Space(6);
EditorGUILayout.EndVertical();
#endregion RAW TEXTURE ARRAY SETTINGS
#region DRAW MATERIALS PROPERTIES
if (Selection.gameObjects != null && Selection.gameObjects.Length == 1)
{
ObjectMaterialLinks objMaterialLinks = Selection.gameObjects[0].GetComponent<ObjectMaterialLinks>();
GUILayout.Space(4);
if (objMaterialLinks != null && objMaterialLinks.linkedMaterialEntities != null && !dataContainer.relocateMaterialLinks)
{
int a = -1;
bool wasFeasible = false;
foreach (var materialEntity in objMaterialLinks.linkedMaterialEntities)
{
a++;
#region INITIALIZATION AND PRECHECKS
bool isFeasible = false;
Texture2D attrImg = null;
List<CombineMetaData> combinedMats = null;
var comb = materialEntity.combinedMats;
if (comb != null && comb.Count != 0)
{
isFeasible = true;
attrImg = objMaterialLinks.linkedAttrImg;
combinedMats = comb;
}
if (attrImg == null) { isFeasible = false; }
if (attrImg != null && isFeasible && dataContainer.reInitializeTempMatProps && !dataContainer.relocateMaterialLinks)
{
foreach (var combMat in combinedMats)
{
MaterialProperties origMatProps = combMat.materialProperties;
combMat.tempMaterialProperties = new MaterialProperties();
MaterialProperties tempProps = combMat.tempMaterialProperties;
tempProps.albedoTint = origMatProps.albedoTint;
tempProps.uvTileOffset = origMatProps.uvTileOffset;
tempProps.normalIntensity = origMatProps.normalIntensity;
tempProps.occlusionIntensity = origMatProps.occlusionIntensity;
tempProps.smoothnessIntensity = origMatProps.smoothnessIntensity;
tempProps.glossMapScale = origMatProps.glossMapScale;
tempProps.metalIntensity = origMatProps.metalIntensity;
tempProps.emissionColor = origMatProps.emissionColor;
tempProps.detailUVTileOffset = origMatProps.detailUVTileOffset;
tempProps.alphaCutoff = origMatProps.alphaCutoff;
tempProps.specularColor = origMatProps.specularColor;
tempProps.detailNormalScale = origMatProps.detailNormalScale;
tempProps.heightIntensity = origMatProps.heightIntensity;
tempProps.uvSec = origMatProps.uvSec;
}
}
#endregion INITIALIZATION AND PRECHECKS
// Only the first time
if (isFeasible && a == 0)
{
wasFeasible = true;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
#region SECTION HEADER
UtilityServices.DrawHorizontalLine(Color.black, 1, 2, 7, 4);
content = new GUIContent(); //2F4F4F //008080 //191970 //006699
if (isPlainSkin) { content.text = "<b>Materials Properties</b>"; }
else { content.text = "<b><color=#006699>Materials Properties</color></b>"; }
content.tooltip = "Adjust the materials properties for this GameObject. This won't change the old materials properties but it will allow you to adjust properties of the combined material exclusively for each object.";
style = GUI.skin.label;
style.richText = true;
OldAlignment = style.alignment;
style.alignment = TextAnchor.MiddleCenter;
EditorGUILayout.LabelField(content, style);
style.alignment = OldAlignment;
UtilityServices.DrawHorizontalLine(Color.black, 1, 1, 7, 4);
#endregion SECTION HEADER
}
int matIndex = -1;
int texArrIndex = -1;
if (isFeasible)
{
matIndex = combinedMats[0].materialProperties.matIndex;
texArrIndex = combinedMats[0].materialProperties.texArrIndex;
}
for (int b = 0; (isFeasible && b < combinedMats.Count); b++, matIndex++)
{
GUILayout.Space(6);
MaterialProperties originalMatProps = combinedMats[b].materialProperties;
MaterialProperties tempMatProps = combinedMats[b].tempMaterialProperties;
originalMatProps.matIndex = tempMatProps.matIndex = matIndex;
#region MATERIAL NAME AND APPLY CHANGES
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
GUILayout.Space(12);
content = new GUIContent(); //FF6347ff //006699 //008080
if (isPlainSkin) { content.text = $"<b>Material : {originalMatProps.materialName}</b>"; }
else { content.text = $"<b><color=#2F4F4F>Material : {originalMatProps.materialName}</color></b>"; }
content.tooltip = "Click to toggle displaying properties for this material.";
style = GUI.skin.label;
style.richText = true;
originalMatProps.foldOut = EditorGUILayout.Foldout(originalMatProps.foldOut, content, true);
style = GUI.skin.button;
style.richText = true;
content = new GUIContent();
if (isPlainSkin) { content.text = "<b><size=11>Reset</size></b>"; }
else { content.text = "<b><size=11><color=#D2691E>Reset</color></size></b>"; }
content.tooltip = "Reset this material's properties to the ones after the last apply operation";
if (GUILayout.Button(content, style, GUILayout.Width(60), GUILayout.Height(20)))
{
tempMatProps = new MaterialProperties();
combinedMats[b].tempMaterialProperties = tempMatProps;
tempMatProps.albedoTint = originalMatProps.albedoTint;
tempMatProps.uvTileOffset = originalMatProps.uvTileOffset;
tempMatProps.normalIntensity = originalMatProps.normalIntensity;
tempMatProps.occlusionIntensity = originalMatProps.occlusionIntensity;
tempMatProps.smoothnessIntensity = originalMatProps.smoothnessIntensity;
tempMatProps.glossMapScale = originalMatProps.glossMapScale;
tempMatProps.metalIntensity = originalMatProps.metalIntensity;
tempMatProps.emissionColor = originalMatProps.emissionColor;
tempMatProps.detailUVTileOffset = originalMatProps.detailUVTileOffset;
tempMatProps.alphaCutoff = originalMatProps.alphaCutoff;
tempMatProps.specularColor = originalMatProps.specularColor;
tempMatProps.detailNormalScale = originalMatProps.detailNormalScale;
tempMatProps.heightIntensity = originalMatProps.heightIntensity;
tempMatProps.uvSec = originalMatProps.uvSec;
try
{
tempMatProps.BurnAttrToImg(ref attrImg, matIndex, texArrIndex);
int index = dataContainer.materialsToRestore.IndexOf(originalMatProps);
if (index == -1)
{
dataContainer.materialsToRestore.Add(originalMatProps);
}
}
catch (Exception ex) { }
}
GUILayout.Space(2);
content = new GUIContent();
if (isPlainSkin) { content.text = "<b><size=11>Apply</size></b>"; }
else { content.text = "<b><size=11><color=#D2691E>Apply</color></size></b>"; }
content.tooltip = "Apply all property changes on this material. This is important if you want to keep the changes persistent. Please do save the scene after applying the changes and before existing the editor, otherwise you will get data inconsistencies";
if (GUILayout.Button(content, style, GUILayout.Width(80), GUILayout.Height(20)))
{
bool failed = false;
try
{
tempMatProps.BurnAttrToImg(ref attrImg, matIndex, texArrIndex);
string path = AssetDatabase.GetAssetPath(attrImg);
AttributesImage.BurnToAttributesImg(attrImg, path);
int index = dataContainer.materialsToRestore.IndexOf(originalMatProps);
if (index != -1) { dataContainer.materialsToRestore.RemoveAt(index); }
}
catch (Exception ex)
{
failed = true;
}
if (!failed)
{
EditorSceneManager.MarkSceneDirty(Selection.activeGameObject.scene);
originalMatProps.albedoTint = tempMatProps.albedoTint;
originalMatProps.uvTileOffset = tempMatProps.uvTileOffset;
originalMatProps.normalIntensity = tempMatProps.normalIntensity;
originalMatProps.occlusionIntensity = tempMatProps.occlusionIntensity;
originalMatProps.smoothnessIntensity = tempMatProps.smoothnessIntensity;
originalMatProps.glossMapScale = tempMatProps.glossMapScale;
originalMatProps.metalIntensity = tempMatProps.metalIntensity;
originalMatProps.emissionColor = tempMatProps.emissionColor;
originalMatProps.detailUVTileOffset = tempMatProps.detailUVTileOffset;
originalMatProps.alphaCutoff = tempMatProps.alphaCutoff;
originalMatProps.specularColor = tempMatProps.specularColor;
originalMatProps.detailNormalScale = tempMatProps.detailNormalScale;
originalMatProps.heightIntensity = tempMatProps.heightIntensity;
}
}
EditorGUILayout.EndHorizontal();
#endregion MATERIAL NAME AND APPLY CHANGES
GUILayout.Space(6);
if (originalMatProps.foldOut)
{
EditorGUI.BeginChangeCheck();
#region ALBEDO TINT COLOR
GUILayout.BeginHorizontal();
content = new GUIContent();
style = GUI.skin.label;
style.richText = true;
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Albedo Tint Color";
content.tooltip = "Adjust the Albedo tint color";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
tempMatProps.albedoTint = EditorGUILayout.ColorField(tempMatProps.albedoTint, GUILayout.Width(110), GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion ALBEDO TINT COLOR
#region METALLIC INTENSITY
GUILayout.BeginHorizontal();
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Metallic Intensity";
content.tooltip = "Adjust the metal intensity/strength";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.metalIntensity, 0, 10, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.metalIntensity = Mathf.Clamp(floatLevel, 0f, 10f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion METALLIC INTENSITY
#region SMOOTHNESS INTENSITY
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Smoothness Intensity";
content.tooltip = "Adjust the smoothness intensity/strength";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.smoothnessIntensity, 0, 10, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.smoothnessIntensity = Mathf.Clamp(floatLevel, 0f, 10f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion SMOOTHNESS INTENSITY
#region NORMAL INTENSITY
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Normal Intensity";
content.tooltip = "Adjust the normal intensity/strength";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.normalIntensity, 0, 10, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.normalIntensity = Mathf.Clamp(floatLevel, 0f, 10f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion NORMAL INTENSITY
#region HEIGHT INTENSITY
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Height Intensity";
content.tooltip = "Adjust the parallax height intensity/strength";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.heightIntensity, 0, 0.2f, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.heightIntensity = Mathf.Clamp(floatLevel, 0f, 0.2f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion HEIGHT INTENSITY
#region OCCLUSION INTENSITY
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Occlusion Intensity";
content.tooltip = "Adjust the parallax occlusion intensity/strength";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.occlusionIntensity, 0, 10, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.occlusionIntensity = Mathf.Clamp(floatLevel, 0f, 10f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion OCCLUSION INTENSITY
#region DETAIL NORMAL INTENSITY
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Detail Normal Intensity";
content.tooltip = "Adjust the detail normal intensity/strength";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.detailNormalScale, 0, 10, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.detailNormalScale = Mathf.Clamp(floatLevel, 0f, 10f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion DETAIL NORMAL INTENSITY
#region GLOSS INTENSITY
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Gloss Intensity";
content.tooltip = "Adjust the gloss intensity/strength";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.glossMapScale, 0, 1, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.glossMapScale = Mathf.Clamp(floatLevel, 0f, 1f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion GLOSS INTENSITY
#region ALPHA CUTOFF
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Alpha Cutoff";
content.tooltip = "Adjust the Alpha Cutoff value";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(tempMatProps.alphaCutoff, 0, 1, GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
GUILayout.Space(5);
content.text = "";
floatLevel = Mathf.Abs(EditorGUILayout.FloatField(content, floatLevel, style, GUILayout.Width(40)));
tempMatProps.alphaCutoff = Mathf.Clamp(floatLevel, 0f, 1f);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion ALPHA CUTOFF
#region SPECULAR COLOR
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content.text = "Specular Color";
content.tooltip = "Adjust the specular color";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
tempMatProps.specularColor = EditorGUILayout.ColorField(tempMatProps.specularColor, GUILayout.Width(110), GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion SPECULAR COLOR
#region EMISSION COLOR
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content.text = "Emission Color";
content.tooltip = "Adjust the emission color";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
tempMatProps.emissionColor = EditorGUILayout.ColorField(tempMatProps.emissionColor, GUILayout.Width(110), GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion EMISSION COLOR
#region UV TILE OFFSET
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "UV Tile Offset";
content.tooltip = "Adjust the UV tiling and offset values";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
tempMatProps.uvTileOffset = EditorGUILayout.Vector4Field("", tempMatProps.uvTileOffset, GUILayout.MinWidth(100), GUILayout.ExpandWidth(true));
style = GUI.skin.textField;
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion UV TILE OFFSET
#region DETAIL UV TILE OFFSET
GUILayout.BeginHorizontal();
style = GUI.skin.label;
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Detail UV Tile Offset";
content.tooltip = "Adjust the UV tiling and offset values for detail maps";
GUILayout.Space(24);
EditorGUILayout.LabelField(content, style, GUILayout.Width(141));
tempMatProps.detailUVTileOffset = EditorGUILayout.Vector4Field("", tempMatProps.detailUVTileOffset, GUILayout.MinWidth(100), GUILayout.ExpandWidth(true));
style = GUI.skin.textField;
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion DETAIL UV TILE OFFSET
if (EditorGUI.EndChangeCheck())
{
try
{
tempMatProps.BurnAttrToImg(ref attrImg, matIndex, texArrIndex);
int index = dataContainer.materialsToRestore.IndexOf(originalMatProps);
if (index == -1)
{
dataContainer.materialsToRestore.Add(originalMatProps);
}
}
catch (Exception ex)
{
}
}
matIndex++;
}
}
}
if (wasFeasible) { EditorGUILayout.EndVertical(); }
dataContainer.reInitializeTempMatProps = false;
}
}
#endregion DRAW MATERIALS PROPERTIES
#region REGENERATE TEXTURE ARRAYS
Rect drop_area = EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(50), GUILayout.ExpandWidth(true));
drop_area.height = 70;
UtilityServices.DrawHorizontalLine(Color.black, 1, 2, 7, 4);
content = new GUIContent();//TEXTURE ARRAYS SETTINGS //2F4F4F //008080 //191970 //006699
if (isPlainSkin) { content.text = "<b>Alter Texture Arrays</b>"; }
else { content.text = "<b><color=#006699>Alter Texture Arrays</color></b>"; }
content.tooltip = "Change properties of existing texture arrays.";
style = GUI.skin.label;
style.richText = true;
OldAlignment = style.alignment;
style.alignment = TextAnchor.MiddleCenter;
EditorGUILayout.LabelField(content, style);
style.alignment = OldAlignment;
UtilityServices.DrawHorizontalLine(Color.black, 1, 1, 7, 4);
EditorGUILayout.HelpBox("Drop here existing texture arrays to change their properties", MessageType.Info);
GUILayout.Space(4);
GUILayout.BeginHorizontal();
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(17);
#else
GUILayout.Space(16);
#endif
content = new GUIContent();
content.text = "Texture Arrays";
content.tooltip = "Expand this to see which texture arrays have been added.";
style = new GUIStyle(EditorStyles.foldout);
float width = style.fixedWidth;
style.fixedWidth = 60;
existingTextureArraysFoldout = EditorGUILayout.Foldout(existingTextureArraysFoldout, content, true, style);
style.fixedWidth = width;
GUILayout.Space(60);
content.text = "Size";
EditorGUILayout.LabelField(content, GUILayout.Width(30));
content.text = "";
int oldSize = existingTextureArrays.Count;
existingTextureArraysSize = Mathf.Abs(EditorGUILayout.DelayedIntField(content, existingTextureArrays.Count, GUILayout.Width(40)));
int diff = existingTextureArraysSize - oldSize;
if (existingTextureArraysSize == 0)
{
existingTextureArraysFoldout = false;
existingTextureArrays = new List<Texture2DArray>();
}
else if (diff < 0)
{
diff *= -1;
for (int a = 0; a < diff; a++)
{
existingTextureArrays.RemoveAt(existingTextureArrays.Count - 1);
}
}
else if (diff != 0)
{
for (int a = 0; a < diff; a++)
{
existingTextureArrays.Add(null);
}
}
if (oldSize != existingTextureArraysSize)
{
existingTextureArraysFoldout = true;
}
GUILayout.FlexibleSpace();
style = GUI.skin.button;
style.richText = true;
RectOffset oldPadding = style.margin;
style.margin = new RectOffset(0, 0, -1, 0);
content = new GUIContent();
if (isPlainSkin) { content.text = "<b><size=11>Regenerate</size></b>"; }
else { content.text = "<b><size=11><color=#D2691E>Regenerate</color></size></b>"; }
content.tooltip = "Regenerate the added texture arrays with the new properties.";
if (GUILayout.Button(content, style, GUILayout.Width(100), GUILayout.Height(20)))
{
try
{
if (existingTextureArrays != null && existingTextureArrays.Count > 0)
{
int index = 0;
DiffuseColorSpace colorSpace = DiffuseColorSpace.NON_LINEAR;
if (dataContainer.colorSpaceChoices[dataContainer.choiceColorSpace].ToLower().Contains("non"))
{
colorSpace = DiffuseColorSpace.NON_LINEAR;
}
else
{
colorSpace = DiffuseColorSpace.LINEAR;
}
int regeneratedCount = 0;
foreach (var existingTexArr in existingTextureArrays)
{
index++;
EditorUtility.DisplayProgressBar("Changing Texture Arrays", $"Regenerating texture arrays with new settings {index}/{existingTextureArrays.Count}", (float)(index) / existingTextureArrays.Count);
if (existingTexArr == null) { continue; }
string existingTexArrPath = AssetDatabase.GetAssetPath(existingTexArr);
Texture2DArray textArr = AssetDatabase.LoadAssetAtPath<Texture2DArray>(existingTexArrPath);
string parentDirPath = Directory.GetParent(existingTexArrPath).ToString().Replace('\\', '/');
string attrImgPath = parentDirPath + "/_MatAttributes.atim";
var attrImg = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(attrImgPath);
if (attrImg == null)
{
string msg = $"The TextureArray \"{existingTexArr.name}\" at path \"{existingTexArrPath}\" has no AttributesImage present at the same path so it was skipped.";
Debug.LogWarning(msg);
continue;
}
try
{
existingTexArr.GetPixels(0);
}
catch (Exception ex)
{
string msg = $"The TextureArray \"{existingTexArr.name}\" at path \"{existingTexArrPath}\" is not readible so it was skipped. Please mark it readible.";
Debug.LogWarning(msg);
continue;
}
Resolution resolution = new Resolution();
resolution.width = existingTexArr.width;
resolution.height = existingTexArr.height;
existingArraysProps.resolution = resolution;
Texture2DArray relocatedTexArr = null;
bool hasAlphaChannel = MaterialCombiner.IsAlphaCompressed(existingTexArr);
var textureFormat = MaterialCombiner.GetTextureFormat(existingArraysProps.compressionType, existingArraysProps.compressionQuality, hasAlphaChannel);
if (!SystemInfo.SupportsTextureFormat(textureFormat))
{
string msg = $"The texture compression format \"{existingArraysProps.compressionType}\" chosen for the TextureArray \"{existingTexArr.name}\" at path \"{existingTexArrPath}\" is not supported on this platform. The texture array was skipped.";
Debug.LogWarning(msg);
continue;
}
else
{
relocatedTexArr = MaterialCombiner.AllocateArray(existingArraysProps, existingTexArr.depth, colorSpace, MaterialCombiner.IsAlphaCompressed(existingTexArr));
}
for (int a = 0; a < existingTexArr.depth; a++)
{
Texture2D tempTex = new Texture2D(1, 1);
Resolution reso = new Resolution();
reso.width = existingTexArr.width;
reso.height = existingTexArr.height;
Texture2D texture = MaterialCombiner.GetResizedTexture(tempTex, reso, colorSpace == DiffuseColorSpace.NON_LINEAR ? false : true);
texture.SetPixels(existingTexArr.GetPixels(a));
texture.Apply();
DestroyImmediate(tempTex);
MaterialCombiner.CompressTexture(texture, existingArraysProps, hasAlphaChannel);
MaterialCombiner.WriteTextureToTextureArray(texture, relocatedTexArr, a, MaterialCombiner.SizeToMipCount(existingArraysProps));
DestroyImmediate(texture);
}
EditorUtility.CopySerialized(relocatedTexArr, existingTexArr);
AssetDatabase.SaveAssets();
DestroyImmediate(relocatedTexArr);
regeneratedCount++;
}
EditorUtility.ClearProgressBar();
if (regeneratedCount == existingTextureArrays.Count)
{
string msg = $"Successfully regenerated all TextureArrays with the specified settings";
EditorUtility.DisplayDialog("Operation Successful", msg, "Ok");
}
else if (regeneratedCount != 0 && regeneratedCount < existingTextureArrays.Count)
{
string msg = $"Some of the TextureArrays were regenerated with the specified settings while the others couldn't be regenerated. Please see the console for more details.";
EditorUtility.DisplayDialog("Operation Successful", msg, "Ok");
}
else
{
string error = $"None of the TextureArrays were regenerated with the specified settings. Please see the console for more details.";
EditorUtility.DisplayDialog("Operation Failed", error, "Ok");
}
}
}
catch (Exception ex)
{
EditorUtility.ClearProgressBar();
string error = $"Failed to regenerate texture arrays with the specified settings due to unknown reasons. Please check console for any clues.";
EditorUtility.DisplayDialog("Operation Failed", error, "Ok");
Debug.LogError(ex);
}
}
style.margin = oldPadding;
GUILayout.EndHorizontal();
Event evt = Event.current;
switch (evt.type)
{
case EventType.DragUpdated:
case EventType.DragPerform:
if (drop_area.Contains(evt.mousePosition))
{
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (evt.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
foreach (UnityEngine.Object dragged_object in DragAndDrop.objectReferences)
{
// Do On Drag Stuff here
string path = AssetDatabase.GetAssetPath(dragged_object);
if (!String.IsNullOrWhiteSpace(path))
{
Texture2DArray textArr = AssetDatabase.LoadAssetAtPath<Texture2DArray>(path);
if (textArr != null)
{
string attrImgPath = Directory.GetParent(path).ToString().Replace('\\', '/') + "/_MatAttributes.atim";
var attrImg = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(attrImgPath);
//if(attrImg == null) { Debug.Log("Not loaded attr image check your path " + attrImgPath); }
if (!existingTextureArrays.Contains(textArr) && attrImg != null)
{
//Debug.Log("Added");
if (existingTextureArrays.Count > 0)
{
bool alreadyAdded = false;
for (int a = 0; a < existingTextureArrays.Count; a++)
{
var item = existingTextureArrays[a];
if (item == null)
{
existingTextureArrays[a] = textArr;
alreadyAdded = true;
break;
}
}
if (!alreadyAdded)
{
int oldCount = existingTextureArrays.Count;
existingTextureArrays.Add(textArr);
if (oldCount == 0) { existingTextureArraysFoldout = true; }
}
}
else
{
int oldCount = existingTextureArrays.Count;
existingTextureArrays.Add(textArr);
if (oldCount == 0) { existingTextureArraysFoldout = true; }
}
}
}
}
}
}
}
break;
}
if (existingTextureArrays != null && existingTextureArrays.Count > 0)
{
if (existingTextureArraysFoldout)
{
UtilityServices.DrawHorizontalLine(new Color(105 / 255f, 105 / 255f, 105 / 255f), 1, 5, 8, 4);
GUILayout.Space(8);
for (int a = 0; a < existingTextureArrays.Count; a++)
{
var existingItem = existingTextureArrays[a];
var rect = GUILayoutUtility.GetRect(0.0f, 16.0f, GUILayout.ExpandWidth(true));
GUILayout.Space(2);
var obj = EditorGUI.ObjectField(rect, existingItem, typeof(UnityEngine.Object), false);
if (obj != null && existingItem == null)
{
string path = AssetDatabase.GetAssetPath(obj);
if (!String.IsNullOrWhiteSpace(path))
{
Texture2DArray textArr = AssetDatabase.LoadAssetAtPath<Texture2DArray>(path);
string attrImgPath = Directory.GetParent(path).ToString().Replace('\\', '/') + "/_MatAttributes.atim";
var attrImg = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(attrImgPath);
if (textArr != null && !existingTextureArrays.Contains(textArr) && attrImg != null)
{
existingTextureArrays[a] = textArr;
}
}
}
else if (obj == null)
{
existingTextureArrays[a] = null;
}
}
UtilityServices.DrawHorizontalLine(new Color(105 / 255f, 105 / 255f, 105 / 255f), 1, 5, 8, 4);
}
GUILayout.Space(6);
content = new GUIContent();
content.text = "Properties";
content.tooltip = "Expand this to change properties for the added texture arrays.";
GUILayout.BeginHorizontal();
#if UNITY_2019_1_OR_NEWER
GUILayout.Space(17);
#else
GUILayout.Space(16);
#endif
textureArraysPropsFoldout = EditorGUILayout.Foldout(textureArraysPropsFoldout, content, true);
GUILayout.EndHorizontal();
if (existingArraysProps == null)
{
//dataContainer.textureArraysSettings = new MaterialCombiner.CombiningInformation.TextureArrayGroup();
var defaultRes = new CombiningInformation.Resolution();
defaultRes.width = Int32.Parse(dataContainer.resolutionsChoices[4]);
defaultRes.height = Int32.Parse(dataContainer.resolutionsChoices[4]);
fMode = dataContainer.filteringModesChoices[0];
if (fMode.ToLower().Contains("point")) { filteringMode = FilterMode.Point; }
else if (fMode.ToLower().Contains("Bilinear")) { filteringMode = FilterMode.Bilinear; }
else { filteringMode = FilterMode.Trilinear; }
cT = dataContainer.compressionTypesChoices[0];
compType = (CombiningInformation.CompressionType)Enum.Parse(typeof(CombiningInformation.CompressionType), cT.ToUpper());
cQ = dataContainer.compressionQualitiesChoices[1];
compQuality = (CombiningInformation.CompressionQuality)Enum.Parse(typeof(CombiningInformation.CompressionQuality), cQ.ToUpper());
existingArraysProps = new TextureArrayUserSettings
(
defaultRes,
filteringMode,
CompressionType.UNCOMPRESSED,
compQuality,
1
);
}
if (textureArraysPropsFoldout)
{
GUILayout.Space(4);
#region PROPERTIES
#region FILTERING MODE
GUILayout.BeginHorizontal();
content.text = "Filtering Mode";
content.tooltip = "The filtering mode for textures in the added texture arrays.";
GUILayout.Space(16);
style = EditorStyles.label;
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
existingArraysProps.choiceFilteringMode = EditorGUILayout.Popup("", existingArraysProps.choiceFilteringMode, dataContainer.filteringModesChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
fMode = dataContainer.filteringModesChoices[existingArraysProps.choiceFilteringMode];
if (fMode.ToLower().Contains("point")) { filteringMode = FilterMode.Point; }
else if (fMode.ToLower().Contains("Bilinear")) { filteringMode = FilterMode.Bilinear; }
else { filteringMode = FilterMode.Trilinear; }
existingArraysProps.filteringMode = filteringMode;
EditorGUILayout.LabelField("", style, GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion FILTERING MODE
#region ANISOTROPIC FILTERING
GUILayout.Space(-2);
GUILayout.BeginHorizontal();
content = new GUIContent();
prevPadding = new RectOffset(style.padding.left, style.padding.right, style.padding.top, style.padding.bottom);
content.text = "Anisotropic Level";
content.tooltip = "The level of the anisotropic filtering for the textures in the added texture arrays.";
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
floatLevel = Mathf.Abs(GUILayout.HorizontalSlider(existingArraysProps.anisotropicFilteringLevel, 0, 16, GUILayout.Width(142), GUILayout.ExpandWidth(true)));
style = GUI.skin.textField;
existingArraysProps.anisotropicFilteringLevel = Mathf.RoundToInt(floatLevel);
GUILayout.Space(5);
content.text = "";
existingArraysProps.anisotropicFilteringLevel = Mathf.Abs(EditorGUILayout.IntField(content, existingArraysProps.anisotropicFilteringLevel, style, GUILayout.Width(40)));
existingArraysProps.anisotropicFilteringLevel = Mathf.Clamp(existingArraysProps.anisotropicFilteringLevel, (int)0, (int)16);
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion ANISOTROPIC FILTERING
#region COMPRESSION QUALITY
GUILayout.Space(1);
GUILayout.BeginHorizontal();
content.text = "Compression Quality";
content.tooltip = "The compression quality for the textures in the added texture arrays. This option is only valid if the compression type selected is \"ASTC RGB\" ";
style = GUI.skin.label;
GUILayout.Space(16);
EditorGUI.BeginDisabledGroup(dataContainer.compressionTypesChoices[existingArraysProps.choiceCompressionType] != "ASTC_RGB");
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
existingArraysProps.choiceCompressionQuality = EditorGUILayout.Popup("", existingArraysProps.choiceCompressionQuality, dataContainer.compressionQualitiesChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
EditorGUI.EndDisabledGroup();
cQ = dataContainer.compressionQualitiesChoices[existingArraysProps.choiceCompressionQuality];
compQuality = (CombiningInformation.CompressionQuality)Enum.Parse(typeof(CombiningInformation.CompressionQuality), cQ.ToUpper());
existingArraysProps.compressionQuality = compQuality;
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion COMPRESSION QUALITY
#region COMPRESSION TYPE
GUILayout.BeginHorizontal();
content.text = "Compression Type";
content.tooltip = "The compression type for the textures in the added texture arrays.";
style = GUI.skin.label;
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
existingArraysProps.choiceCompressionType = EditorGUILayout.Popup("", existingArraysProps.choiceCompressionType, dataContainer.compressionTypesChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
cT = dataContainer.compressionTypesChoices[existingArraysProps.choiceCompressionType];
compType = (CombiningInformation.CompressionType)Enum.Parse(typeof(CombiningInformation.CompressionType), cT.ToUpper());
existingArraysProps.compressionType = compType;
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion COMPRESSION TYPE
#region COLOR SPACE
GUILayout.BeginHorizontal();
content.text = "Color Space";
content.tooltip = "The color space of the textures in the selected texture arrays.[Usually NON-LINEAR]";
style = GUI.skin.label;
GUILayout.Space(16);
EditorGUILayout.LabelField(content, style, GUILayout.Width(129));
dataContainer.choiceColorSpace = EditorGUILayout.Popup("", dataContainer.choiceColorSpace, dataContainer.colorSpaceChoices, GUILayout.Width(142), GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("", GUILayout.Width(0)); // Blank label to stop UI field from bleeding ro getting too close to the edge
GUILayout.EndHorizontal();
#endregion COLOR SPACE
#endregion PROPERTIES
}
}
EditorGUILayout.EndVertical();
#endregion REGENERATE TEXTURE ARRAYS
}
}
private void SelectionChanged()
{
if (Selection.activeTransform != null && isFeasibleTargetForPolyFew)
{
SetObjectMeshPairsIfNull();
if (dataContainer.currentLodLevelSettings == null || dataContainer.currentLodLevelSettings.Count == 0)
{
dataContainer.currentLodLevelSettings = new List<DataContainer.LODLevelSettings>();
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(0, 0.6f, false, false, false, true, false, 7, 100, false, false, false, new List<float>()));
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(30, 0.4f, false, false, false, true, false, 7, 100, false, false, false, new List<float>()));
dataContainer.currentLodLevelSettings.Add(new DataContainer.LODLevelSettings(60, 0.15f, false, false, false, true, false, 7, 100, false, false, false, new List<float>()));
}
FoldoutAutoLOD = false;
}
if (Selection.gameObjects != null && Selection.gameObjects.Length > 1)
{
if(lastMultiSelectedObjects == null)
{
lastMultiSelectedObjects = new List<GameObject>();
foreach (var item in Selection.gameObjects)
{
lastMultiSelectedObjects.Add(item);
}
}
}
}
private void SetObjectMeshPairsIfNull()
{
dataContainer = Selection.activeGameObject.GetComponent<PolyFew>().dataContainer;
if (dataContainer.objectMeshPairs == null || dataContainer.objectMeshPairs.Count == 0 || !dataContainer.objectMeshPairs.ContainsKey(Selection.activeGameObject))
{
dataContainer.objectMeshPairs = UtilityServices.GetObjectMeshPairs(Selection.activeGameObject, true, true);
TriangleCount = UtilityServices.CountTriangles(ConsiderChildren, dataContainer.objectMeshPairs, Selection.activeGameObject);
RunOnThreads = CheckOnThreads();
}
}
[DidReloadScripts]
public static void ScriptReloaded()
{
/*
if (Selection.gameObjects != null && Selection.gameObjects.Length == 1)
{
ObjectMaterialLinks objMaterialLinks = Selection.gameObjects[0].GetComponent<ObjectMaterialLinks>();
if (objMaterialLinks != null && objMaterialLinks.linkedMaterialEntity != null)
{
var comb = objMaterialLinks.linkedMaterialEntity.combinedMats;
if (comb != null && comb.Count != 0)
{
dataContainer.lastObjMaterialLinks = objMaterialLinks;
dataContainer.relocateMaterialLinks = true;
}
}
}
*/
}
class BuildProcessor : IPreprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(BuildReport report)
{
// Restore pending reductions for all gameobjects in all active scenes
for (int a = 0; a < UnityEngine.SceneManagement.SceneManager.sceneCount; a++)
{
Scene loadedScene = UnityEngine.SceneManagement.SceneManager.GetSceneAt(a);
GameObject[] rootGameObjects = loadedScene.GetRootGameObjects();
if (rootGameObjects != null && rootGameObjects.Length > 0)
{
UtilityServices.RestorePolyFewGameObjects(rootGameObjects);
}
}
}
}
class FileModificationWarning : UnityEditor.AssetModificationProcessor
{
static string[] OnWillSaveAssets(string[] paths)
{
List<string> dirtyLoadedScenesPaths = new List<string>();
for (int a = 0; a < UnityEngine.SceneManagement.SceneManager.sceneCount; a++)
{
Scene loadedScene = UnityEngine.SceneManagement.SceneManager.GetSceneAt(a);
if (loadedScene.isDirty)
{
dirtyLoadedScenesPaths.Add(loadedScene.path);
}
}
if(dirtyLoadedScenesPaths.Count == 0)
{
return paths;
}
int totalScenesProcessed = 0;
foreach (string path in paths)
{
if (dirtyLoadedScenesPaths.Contains(path))
{
totalScenesProcessed++;
Scene scene = UnityEngine.SceneManagement.SceneManager.GetSceneByPath(path);
GameObject[] rootGameObjects = scene.GetRootGameObjects();
if (rootGameObjects != null && rootGameObjects.Length > 0)
{
List<GameObject> allObjectsinScene = new List<GameObject>();
UtilityServices.RestorePolyFewGameObjects(rootGameObjects);
}
}
if(totalScenesProcessed == dirtyLoadedScenesPaths.Count)
{
return paths;
}
}
return paths;
}
static void OnWillCreateAsset(string assetPath)
{
string extension = Path.GetExtension(assetPath);
// We only care about preset files
if (extension != ".preset") { return; }
NormalizePolyFewPreset(assetPath, Selection.activeGameObject.GetComponent<PolyFew>());
}
}
private static async Task NormalizePolyFewPreset(string presetPath, PolyFew targetPolyfew)
{
if(targetPolyfew == null) { return; }
DataContainer targetContainer = targetPolyfew.dataContainer;
if(targetContainer == null) { return; }
bool timeout = false;
long timeoutPeriodMillis = 3600; // If file isn't created in 1 minute timeout
long elapsedTimeMillis = 0;
while (true)
{
if(elapsedTimeMillis >= timeoutPeriodMillis)
{
timeout = true;
break;
}
if (File.Exists(presetPath))
{
break;
}
await Task.Delay(20);
elapsedTimeMillis += 20;
}
if (!timeout)
{
Preset polyfewPreset = AssetDatabase.LoadAssetAtPath<Preset>(presetPath);
if (polyfewPreset.GetTargetTypeName().Contains(nameof(PolyFew)))
{
//Debug.Log($"Preset was made for PolyFew");
UndoRedoOps objectsHistory = targetContainer.objectsHistory;
ObjectMeshPair objectMeshPairs = targetContainer.objectMeshPairs;
LODBackup lodBackup = targetContainer.lodBackup;
List<MaterialProperties> materialsToRestore = targetContainer.materialsToRestore;
ObjectMaterialLinks lastObjMaterialLinks = targetContainer.lastObjMaterialLinks;
targetContainer.objectsHistory = null;
targetContainer.objectMeshPairs = null;
targetContainer.lodBackup = null;
materialsToRestore = null;
lastObjMaterialLinks = null;
polyfewPreset.UpdateProperties(targetPolyfew);
targetContainer.objectsHistory = objectsHistory;
targetContainer.objectMeshPairs = objectMeshPairs;
targetContainer.lodBackup = lodBackup;
targetContainer.materialsToRestore = materialsToRestore;
targetContainer.lastObjMaterialLinks = lastObjMaterialLinks;
}
}
else
{
//Debug.Log("Timeout");
}
}
private static async Task DelayAssignVariablesAfterPreset()
{
Task.Delay(300);
dataContainer.objectsHistory = objectsHistory;
dataContainer.objectMeshPairs = objectMeshPairs;
dataContainer.lodBackup = lodBackup;
dataContainer.materialsToRestore = materialsToRestore;
dataContainer.lastObjMaterialLinks = lastObjMaterialLinks;
}
private TextureArrayUserSettings GetTexArrSettingsFromName(string name)
{
if (name.ToLower().Equals("albedo")) { return dataContainer.textureArraysSettings.diffuseArraySettings; }
else if (name.ToLower().Equals("metallic")) { return dataContainer.textureArraysSettings.metallicArraySettings; }
else if (name.ToLower().Equals("specular")) { return dataContainer.textureArraysSettings.specularArraySettings; }
else if (name.ToLower().Equals("normal")) { return dataContainer.textureArraysSettings.normalArraySettings; }
else if (name.ToLower().Equals("height")) { return dataContainer.textureArraysSettings.heightArraySettings; }
else if (name.ToLower().Equals("occlusion")) { return dataContainer.textureArraysSettings.occlusionArraySettings; }
else if (name.ToLower().Equals("emission")) { return dataContainer.textureArraysSettings.emissiveArraySettings; }
else if (name.ToLower().Equals("detail mask")) { return dataContainer.textureArraysSettings.detailMaskArraySettings; }
else if (name.ToLower().Equals("detail albedo")) { return dataContainer.textureArraysSettings.detailAlbedoArraySettings; }
else if (name.ToLower().Equals("detail normal")) { return dataContainer.textureArraysSettings.detailNormalArraySettings; }
return null;
}
private void ResetTextureArrays()
{
dataContainer.textureArraysSettings = new CombiningInformation.TextureArrayGroup();
var defaultRes = new CombiningInformation.Resolution();
defaultRes.width = Int32.Parse(dataContainer.resolutionsChoices[4]);
defaultRes.height = Int32.Parse(dataContainer.resolutionsChoices[4]);
var fMode = dataContainer.filteringModesChoices[0];
FilterMode filteringMode;
if (fMode.ToLower().Contains("point")) { filteringMode = FilterMode.Point; }
else if (fMode.ToLower().Contains("Bilinear")) { filteringMode = FilterMode.Bilinear; }
else { filteringMode = FilterMode.Trilinear; }
var cT = dataContainer.compressionTypesChoices[0];
var compType = (CompressionType)Enum.Parse(typeof(CombiningInformation.CompressionType), cT.ToUpper());
var cQ = dataContainer.compressionQualitiesChoices[1];
var compQuality = (CompressionQuality)Enum.Parse(typeof(CombiningInformation.CompressionQuality), cQ.ToUpper());
dataContainer.textureArraysSettings.InitializeDefaultArraySettings(defaultRes, filteringMode, compType, compQuality, 1);
dataContainer.choiceDiffuseColorSpace = 0;
}
private void CopyOverPreviewSettings(DataContainer.LODLevelSettings lodLevel)
{
lodLevel.reductionStrength = ReductionStrength;
lodLevel.preserveUVFoldover = PreserveUVFoldover;
lodLevel.preserveUVSeams = PreserveUVSeams;
lodLevel.preserveBorders = PreserveBorders;
lodLevel.useEdgeSort = UseEdgeSort;
lodLevel.regardCurvature = RegardCurvature;
lodLevel.recalculateNormals = RecalculateNormals;
lodLevel.aggressiveness = Aggressiveness;
lodLevel.maxIterations = MaxIterations;
lodLevel.regardTolerance = IsPreservationActive;
if (dataContainer.toleranceSpheres != null && dataContainer.toleranceSpheres.Count > 0)
{
for (int b = 0; b < lodLevel.sphereIntensities.Count; b++)
{
lodLevel.sphereIntensities[b] = dataContainer.toleranceSpheres[b].preservationStrength;
}
}
}
private void ApplyExtraOptionsForLOD(GameObject lodObject)
{
Transform applyTo = lodObject.transform.Find(LOD_PARENT_OBJECT_NAME);
if (RemoveLODBackupComponent != null)
{
LODBackup lodBackup = lodObject.GetComponent<PolyFew>().dataContainer.lodBackup;
lodBackup = null;
}
foreach (Transform child in applyTo.GetComponentsInChildren<Transform>(true))
{
if(CopyParentLayer) { child.gameObject.layer = lodObject.layer; }
if(CopyParentTag) { child.gameObject.tag = lodObject.tag; }
if(CopyParentStaticFlags) { GameObjectUtility.SetStaticEditorFlags(child.gameObject, GameObjectUtility.GetStaticEditorFlags(lodObject)); }
}
}
}
}