Newer
Older
Hierarchical-Task-Network-Unity-3D / Assets / Quixel / Scripts / BridgeImporter / MegascansUtilities.cs
#if UNITY_EDITOR

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using Newtonsoft.Json.Linq;

namespace Quixel {
    public class MegascansUtilities : MonoBehaviour {
        
        #region Other_Utils
        /// <summary>
        /// Check whether the child folder you're trying to make already exists, if not, create it and return the directory.
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="child"></param>
        /// <returns></returns>
        public static string ValidateFolderCreate (string parent, string child) {
            string tempPath = FixSlashes (Path.Combine (parent, child));
            if (!AssetDatabase.IsValidFolder (tempPath)) {
                string newPath = AssetDatabase.CreateFolder (parent, child);
                return AssetDatabase.GUIDToAssetPath (newPath);
            }
            return FixSlashes (tempPath);
        }

        public static string FixSlashes (string txt) {
            txt = txt.Replace ("\\", "/");
            txt = txt.Replace (@"\\", "/");
            return txt;
        }

        /// <summary>
        /// Remove spaces and remove special characters.
        /// </summary>
        public static string FixPath (string orgPath) {
            string path = orgPath.Trim ();
            string[] pathFolders = path.Split ('/');

            for (int j = 0; j < pathFolders.Length - 1; j++) {
                pathFolders[j] = pathFolders[j].Trim ();
            }

            path = pathFolders[0];
            for (int i = 1; i < pathFolders.Length; i++) {
                path += "/" + pathFolders[i];
            }
            return path;
        }

        static bool isLegacyProject = false;
        static bool identifiedPipeline = false;

        /// <summary>
        /// This function attempts to auto-detect which template the project is using. Defaults to Legacy/Standard if all else fails.
        /// </summary>
        public static int GetShaderType()
        {
            int shaderType = EditorPrefs.GetInt("QuixelDefaultShader");
            return shaderType == 3 ? (int)getCurrentPipeline() : shaderType;
        }

        public static bool isLegacy () {
            if (!identifiedPipeline) {
                string[] versionParts = Application.unityVersion.Split ('.');
                int majorVersion = int.Parse (versionParts[0]);
                int minorVersion = int.Parse (versionParts[1]);
                isLegacyProject = (majorVersion < 2018 || (majorVersion == 2018 && minorVersion < 3));
            }
            return isLegacyProject;

        }

        //attempt to auto-detect a settings file for Lightweight or HD pipelines
        public static Pipeline getCurrentPipeline () {
            Pipeline currentPipeline = Pipeline.HDRP;

            //attempt to auto-detect a settings file for Lightweight or HD pipelines
            if (AssetDatabase.IsValidFolder ("Assets/Settings")) {
                if (AssetDatabase.LoadAssetAtPath ("Assets/Settings/HDRenderPipelineAsset.asset", typeof (ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRP High Quality.asset", typeof(ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRP Low Quality.asset", typeof(ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRP Medium Quality.asset", typeof(ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRPHighQuality.asset", typeof(ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRPLowQuality.asset", typeof(ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath("Assets/Settings/HDRPMediumQuality.asset", typeof(ScriptableObject))) {
                    currentPipeline = Pipeline.HDRP;
                } else if (AssetDatabase.LoadAssetAtPath ("Assets/Settings/Lightweight_RenderPipeline.asset", typeof (ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath ("Assets/Settings/LWRP-HighQuality.asset", typeof (ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath ("Assets/Settings/LWRP-LowQuality.asset", typeof (ScriptableObject)) ||
                    AssetDatabase.LoadAssetAtPath ("Assets/Settings/LWRP-MediumQuality.asset", typeof (ScriptableObject))) {
                    currentPipeline = Pipeline.LWRP;
                } else if (AssetDatabase.LoadAssetAtPath("Assets/Settings/UniversalRP-HighQuality.asset", typeof(ScriptableObject)) ||
                  AssetDatabase.LoadAssetAtPath("Assets/Settings/UniversalRP-LowQuality.asset", typeof(ScriptableObject)) ||
                  AssetDatabase.LoadAssetAtPath("Assets/Settings/UniversalRP-MediumQuality.asset", typeof(ScriptableObject)))
                {
                    currentPipeline = Pipeline.LWRP;
                }
                else {
                    currentPipeline = Pipeline.Standard;
                }
            } else {
                if (AssetDatabase.FindAssets ("HDRenderPipelineAsset").Length > 0 ||
                    AssetDatabase.FindAssets("HDRPHighQuality").Length > 0 ||
                    AssetDatabase.FindAssets("HDRP High Quality").Length > 0) {
                    currentPipeline = Pipeline.HDRP;
                } else if (AssetDatabase.FindAssets ("Lightweight_RenderPipeline").Length > 0 ||
                    AssetDatabase.FindAssets ("LWRP-HighQuality").Length > 0 ||
                    AssetDatabase.FindAssets ("LWRP-LowQuality").Length > 0 ||
                    AssetDatabase.FindAssets ("LWRP-MediumQuality").Length > 0) {
                    currentPipeline = Pipeline.LWRP;
                } else if (AssetDatabase.FindAssets("UniversalRP-HighQuality").Length > 0 ||
                   AssetDatabase.FindAssets("UniversalRP-LowQuality").Length > 0 ||
                   AssetDatabase.FindAssets("UniversalRP-MediumQuality").Length > 0)
                {
                    currentPipeline = Pipeline.LWRP;
                } else {
                    currentPipeline = Pipeline.Standard;
                }
            }

            return currentPipeline;
        }

        // Tells if an asset type is scatter or not. 
        public static bool isScatterAsset(JObject assetJson, List<string> importedMeshpaths)
        {
            try
            {
                string[] tags = assetJson["tags"].ToObject<string[]>();
                string[] categories = assetJson["categories"].ToObject<string[]>();
                int childCount = GetMeshChildrenCount(importedMeshpaths);

                foreach(string tag in tags)
                {
                    if (tag.ToLower() == "scatter")
                    {
                        return (childCount > 1); //Returns false if the is only one variation of asset.
                    } else if (tag.ToLower() == "cmb_asset")
                    {
                        return (childCount > 1); //Returns false if the is only one variation of asset.
                    }
                }

                foreach (string category in categories)
                {
                    if (category.ToLower() == "scatter")
                    {
                        return (childCount > 1); //Returns false if the is only one variation of asset.
                    }
                    else if (category.ToLower() == "cmb_asset")
                    {
                        return (childCount > 1); //Returns false if the is only one variation of asset.
                    }
                }

                return (childCount > 1);
            }
            catch (Exception ex)
            {
                Debug.Log("Exception::MegascansUtilities::IsScatterAsset:: " + ex.ToString());
                HideProgressBar();
            }

            return false;
        }

        public static int GetMeshChildrenCount(List<string> importedMeshpaths)
        {
            try
            {
                if(importedMeshpaths.Count > 0)
                {
                    UnityEngine.Object loadedGeometry = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(importedMeshpaths[0]);
                    GameObject testGO = (GameObject)Instantiate(loadedGeometry);
                    int count = testGO.transform.childCount;
                    DestroyImmediate(testGO);
                    return count;
                }
            } catch (Exception ex)
            {
                Debug.Log("Exception::MegascansUtilities::GetMeshChildrenCount:: " + ex.ToString());
                HideProgressBar();
            }

            return 1;
        }

        /// <summary>
        /// Determine which asset type we're creating. Surfaces, 3D_Assets, 3D_Scatter_Assets, 3D_Plants.
        /// </summary>
        public static string GetAssetType(string jsonPath)
        {
            string t = "Surfaces";
            if (jsonPath.ToLower().Contains("3d"))
            {
                t = "3D_Assets";
            }
            if (jsonPath.ToLower().Contains("debris") ||
                jsonPath.ToLower().Contains("dbrs") ||
                jsonPath.ToLower().Contains("scatter") ||
                jsonPath.ToLower().Contains("sctr"))
            {
                t = "3D_Scatter_Assets";
            }
            if (jsonPath.ToLower().Contains("3dplant"))
            {
                t = "3D_Plants";
            }
            if (jsonPath.ToLower().Contains("decals") || jsonPath.ToLower().Contains("decal"))
            {
                t = "Decals";
            }
            else if (jsonPath.ToLower().Contains("atlas"))
            {
                t = "Atlases";
            }
            return t;
        }

        public static void CopyFileToProject(string sourcePath, string destPath)
        {
            try
            {
                if (File.Exists(sourcePath))
                {
                    File.Copy(sourcePath, destPath, true);
                    AssetDatabase.ImportAsset(destPath);
                    AssetDatabase.Refresh();
                } else
                {
                    Debug.Log("Exception::MegascansImageUtils::ImportTexture:: Source file does not exist.");
                }
            }
            catch (Exception ex)
            {
                Debug.Log("Exception::MegascansMeshUtils::Importing file to project:: " + ex.ToString());
                HideProgressBar();
            }
        }
        #endregion

        #region Selection Helpers
        /// <summary>
        /// Retrieves selected folders in Project view.
        /// </summary>
        public static List<string> GetSelectedFolders (List<UnityEngine.Object> selections) {
            List<string> folders = new List<string> ();

            foreach (UnityEngine.Object obj in selections) //Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
            {
                string path = AssetDatabase.GetAssetPath (obj);
                if (!string.IsNullOrEmpty (path)) {
                    folders.Add (path);
                }
            }
            return folders;
        }

        /// <summary>
        /// Retrieves selected material.
        /// </summary>
        public static Material GetSelectedMaterial(string fileType = ".mat")
        {
            foreach (Material obj in Selection.GetFiltered(typeof(Material), SelectionMode.Assets))
            {
                string path = AssetDatabase.GetAssetPath(obj);
                if (path.Contains(fileType))
                {
                    return (Material)AssetDatabase.LoadAssetAtPath(path, typeof(Material));
                }
            }
            return null;
        }

        /// <summary>
        /// Retrieves selected GameObjects with MeshRenderer component in Scene view.
        /// </summary>
        public static List<MeshRenderer> GetSelectedMeshRenderers()
        {
            List<MeshRenderer> meshRenderers = new List<MeshRenderer>();

            foreach (GameObject g in Selection.gameObjects)
            {
                if (g.GetComponent<MeshRenderer>() != null)
                {
                    meshRenderers.Add(g.GetComponent<MeshRenderer>());
                }
            }

            return meshRenderers;
        }

        /// <summary>
        /// Recursively gather all files under the given path including all its subfolders.
        /// </summary>
        public static List<string> GetFiles (string path, string fileType = null) {
            List<string> files = new List<string> ();
            Queue<string> queue = new Queue<string> ();
            queue.Enqueue (path);
            while (queue.Count > 0) {
                path = queue.Dequeue ();
                foreach (string subDir in Directory.GetDirectories (path)) {
                    queue.Enqueue (subDir);
                }
                foreach (string s in Directory.GetFiles (path)) {
                    if (fileType != null && s.Contains (fileType)) {
                        if (s.Contains (fileType)) {
                            files.Add (s);
                        }
                    } else {
                        files.Add (s);
                    }

                }
            }
            return files;
        }
        #endregion

        #region HDRP Features PreDefined Macro Setup
        /*
#if (UNITY_2018_2 || UNITY_2018_3 || UNITY_2018_4 || UNITY_2019 || UNITY_2020 || UNITY_2021)
        [MenuItem ("Window/Quixel/Enable HDRP Features")]
        private static void EnableHDRP () {
            Debug.Log ("HDRP enabled.");
            AddDefineIfNecessary ("HDRP", EditorUserBuildSettings.selectedBuildTargetGroup);
        }

        [MenuItem ("Window/Quixel/Disable HDRP Features")]
        private static void DisableHDRP () {
            Debug.Log ("HDRP disabled.");
            RemoveDefineIfNecessary ("HDRP", EditorUserBuildSettings.selectedBuildTargetGroup);
        }
#endif
        */
        public static void AddDefineIfNecessary (string _define, BuildTargetGroup _buildTargetGroup) {
            var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup (_buildTargetGroup);

            if (defines == null) { defines = _define; } else if (defines.Length == 0) { defines = _define; } else { if (defines.IndexOf (_define, 0) < 0) { defines += ";" + _define; } }

            PlayerSettings.SetScriptingDefineSymbolsForGroup (_buildTargetGroup, defines);
        }

        public static void RemoveDefineIfNecessary (string _define, BuildTargetGroup _buildTargetGroup) {
            var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup (_buildTargetGroup);

            if (defines.StartsWith (_define + ";")) {
                // First of multiple defines.
                defines = defines.Remove (0, _define.Length + 1);
            } else if (defines.StartsWith (_define)) {
                // The only define.
                defines = defines.Remove (0, _define.Length);
            } else if (defines.EndsWith (";" + _define)) {
                // Last of multiple defines.
                defines = defines.Remove (defines.Length - _define.Length - 1, _define.Length + 1);
            } else {
                // Somewhere in the middle or not defined.
                var index = defines.IndexOf (_define, 0, System.StringComparison.Ordinal);
                if (index >= 0) { defines = defines.Remove (index, _define.Length + 1); }
            }

            PlayerSettings.SetScriptingDefineSymbolsForGroup (_buildTargetGroup, defines);
        }
        #endregion

        #region Progress Bar Utils

        static float maxNumberOfOperations = 0;
        static float currentOperationCount = 0;

        public static void CalculateNumberOfOperations(JObject assetData, int dispType, int texPack, int shaderType, bool hasBillboardLODOnly)
        {
            JArray meshComps = (JArray)assetData["meshList"];
            int prefabCount = meshComps.Count;

            JArray lodList = (JArray)assetData["lodList"];
            int meshCount = meshComps.Count;

            JArray textureComps = (JArray)assetData["components"];

            List<string> texTypes = new List<string>();

            for (int i = 0; i < textureComps.Count; ++i)
            {
                texTypes.Add((string)textureComps[i]["type"]);
            }

            int texCount = 0;

            if (texTypes.Contains("albedo") || texTypes.Contains("diffuse"))
                texCount++;

            if (texTypes.Contains("normal"))
                texCount++;

            if (texTypes.Contains("displacement") && dispType != 0)
                texCount++;

            if (texTypes.Contains("translucency"))
                texCount++;

            if (texTypes.Contains("ao") && shaderType == 2)
                texCount++;

            if (texPack == 0)
            {
                if (texTypes.Contains("metalness") || texTypes.Contains("roughness") || texTypes.Contains("gloss") || texTypes.Contains("ao") || texTypes.Contains("displacement"))
                    texCount++;
            }
            else if (texPack == 1)
            {
                if ((texTypes.Contains("metalness") || texTypes.Contains("roughness") || texTypes.Contains("gloss") || texTypes.Contains("ao") || texTypes.Contains("displacement")) && shaderType == 0)
                    texCount++;

                if (texTypes.Contains("specular"))
                    texCount++;
            }

            string type = (string)assetData["type"];
            if (type.ToLower().Contains("3dplant") && !hasBillboardLODOnly)
            {
                texCount *= 2;
            }

            maxNumberOfOperations = (float)(prefabCount + meshCount + texCount);
            maxNumberOfOperations += 1.0f; //For the material
        }

        public static void UpdateProgressBar(float change = 0, string header = "Import Megascans Asset", string message = "Processing Asset")
        {
            currentOperationCount += change;
            if (currentOperationCount != maxNumberOfOperations)
                EditorUtility.DisplayProgressBar(header, message, (currentOperationCount / maxNumberOfOperations));
            else
                HideProgressBar();
        }

        public static void HideProgressBar()
        {
            currentOperationCount = 0;
            maxNumberOfOperations = 0;
            EditorUtility.ClearProgressBar();
        }

        public static void PrintProgressBarStats()
        {
            Debug.Log("Current: " + currentOperationCount);
            Debug.Log("Max: " + maxNumberOfOperations);
        }

        #endregion

        #region LOD Heights
        
        public static List<float> getLODHeightList(int numberOfFiles)
        {
            switch (numberOfFiles)
            {
                case 1:
                    return new List<float> { 0.01f };
                case 2:
                    return new List<float> { 0.4f, 0.01f };
                case 3:
                    return new List<float> { 0.5f, 0.2f, 0.01f };
                case 4:
                    return new List<float> { 0.5f, 0.3f, 0.18f, 0.01f };
                case 5:
                    return new List<float> { 0.5f, 0.3f, 0.2f, 0.1f, 0.01f };
                case 6:
                    return new List<float> { 0.55f, 0.35f, 0.24f, 0.15f, 0.07f, 0.01f };
                case 7:
                    return new List<float> { 0.6f, 0.4f, 0.3f, 0.21f, 0.13f, 0.06f, 0.01f };
                default:
                    return new List<float> { 0.65f, 0.45f, 0.35f, 0.26f, 0.18f, 0.11f, 0.06f, 0.01f };
            }
        }

        #endregion

        #region Channel packed textures data verification

        public static bool AlbedoHasOpacity(JObject channelData)
        {
            return channelData == null ? false : verifyMapInList((JArray)channelData["Alpha"], "opacity");
        }

        public static bool SpecularHasGloss(JObject channelData)
        {
            return channelData == null ? false : verifyMapInList((JArray)channelData["Alpha"], "gloss");
        }

        public static void MaskMapComponents(JObject channelData, out bool hasMetalness, out bool hasAO, out bool hasGloss)
        {
            hasMetalness = channelData == null ? false : verifyMapInList((JArray)channelData["Red"], "metalness");
            hasAO = channelData == null ? false : verifyMapInList((JArray)channelData["Green"], "ao");
            hasGloss = channelData == null ? false : verifyMapInList((JArray)channelData["Alpha"], "gloss");
        }

        public static bool verifyMapInList(JArray channelData, string texType)
        {
            for (int i = 0; i < channelData.Count; i++)
            {
                string packedTex = (string)channelData[i];
                if (packedTex.ToLower() == texType.ToLower())
                {
                    return true;
                }
            }
            return false;

        }
        #endregion
    }
}

#endif

public enum Pipeline {
    HDRP,
    LWRP,
    Standard
}