7188 lines
319 KiB
C#
7188 lines
319 KiB
C#
|
//////////////////////////////////////////////////////
|
|||
|
// 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)); }
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|