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

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

namespace Quixel
{
    //This class imports the geometry and create the prefabs.
    public class MegascansMeshUtils : MonoBehaviour
    {
        /// <summary>
        /// Import meshes, start from highest LOD and import the chain.
        /// </summary>
        public static void ProcessMeshes(JObject assetJson, string assetFolderPath, bool highpoly, bool hasVariations, PrefabData prefabData)
        {
            try
            {
                bool createPrefabs = EditorPrefs.GetBool("QuixelDefaultSetupPrefabs", true);
                bool importLODs = EditorPrefs.GetBool("QuixelDefaultImportLODs", true);
                bool setupLODs = EditorPrefs.GetBool("QuixelDefaultSetupLOD", true);
                prefabData.setupLODs = (importLODs && setupLODs); //Only do LOD setup if lower lods were imported and LOD grouping is enabled.

                //get mesh components from the current object. Also, meshComps.Count can give us the number of variations ;)
                JArray meshComps = (JArray)assetJson["meshList"];

                JArray lodList = (JArray)assetJson["lodList"];
                string activeLOD = (string)assetJson["activeLOD"];
                string minLOD = (string)assetJson["minLOD"];

                string modelsFolderPath = MegascansUtilities.ValidateFolderCreate(assetFolderPath, "Models");

                if (hasVariations)
                {
                    List<List<string>> importedGeometryPaths3DPlant = new List<List<string>>();

                    for (int i = 1; i <= meshComps.Count; i++)
                    {
                        List<string> importedGeometryPaths = new List<string>();
                        bool lodMatched = false; // This flag helps to import the lower lods once the active lod is found.
                        foreach (JObject mesh in lodList)
                        {
                            if ((int)mesh["variation"] == i)
                            {
                                string currentLOD = (string)mesh["lod"];
                                if (lodMatched || currentLOD == activeLOD || highpoly)
                                {
                                    lodMatched = true;
                                    if ((currentLOD == "high") && !highpoly)
                                    {
                                        continue;
                                    }
                                    //get the path of the highest LOD to be imported.
                                    string sourcePath = (string)mesh["path"];
                                    string destPath = Path.Combine(modelsFolderPath, (string)mesh["nameOverride"]);
                                    ImportMesh(sourcePath, destPath);
                                    importedGeometryPaths.Add(destPath);
                                    if(!importLODs)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                        importedGeometryPaths3DPlant.Add(importedGeometryPaths);
                    }
                    prefabData.importedGeometryPaths3DPlant = importedGeometryPaths3DPlant;
                    if(createPrefabs)
                        CreatePrefab3DPlant(prefabData);
                }
                else
                {
                    List<string> importedGeometryPaths3D = new List<string>();
                    bool lodMatched = false; // This flag helps to import the lower lods once the active lod is found.
                    foreach (JObject mesh in lodList)
                    {
                        string currentLOD = (string)mesh["lod"];
                        if (lodMatched || (currentLOD == activeLOD) || highpoly)
                        {
                            lodMatched = true;
                            if ((currentLOD == "high") && !highpoly)
                            {
                                continue;
                            }
                            //get the path of the highest LOD to be imported.
                            string sourcePath = (string)mesh["path"];
                            string destPath = Path.Combine(modelsFolderPath, (string)mesh["nameOverride"]);
                            ImportMesh(sourcePath, destPath);
                            importedGeometryPaths3D.Add(destPath);
                            if (!importLODs)
                            {
                                break;
                            }
                        }

                    }
                    prefabData.importedGeometryPaths3D = importedGeometryPaths3D;
                    if (createPrefabs)
                    {
                        if (MegascansUtilities.isScatterAsset(assetJson, importedGeometryPaths3D))
                        {
                            CreatePrefabsScatter(prefabData);
                        }
                        else
                        {
                            CreatePrefab3D(prefabData);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Log("Exception::MegascansMeshUtils::Processing Meshes:: " + ex.ToString());
                MegascansUtilities.HideProgressBar();
            }
        }

        static void ImportMesh(string sourcePath, string destPath)
        {
            MegascansUtilities.UpdateProgressBar(1.0f, "Importing Megascans Asset", "Importing Mesh...");
            MegascansUtilities.CopyFileToProject(sourcePath, destPath);
        }

        /// <summary>
        /// Generates prefabs from imported meshes.
        /// Used for normal 3D assets
        /// </summary>
        public static void CreatePrefab3D(PrefabData prefabData)
        {
            try {
                string prefabPath = MegascansUtilities.ValidateFolderCreate(prefabData.assetPath, "Prefabs");
                string prefabName = prefabData.modelNamingConvention;

                //Setting up prefab gameobject
                GameObject prefabGameObject = new GameObject();
                prefabGameObject.name = prefabName;
                prefabGameObject.isStatic = true;
                if (prefabData.setupLODs)
                {
                    prefabGameObject.AddComponent<LODGroup>();
                    prefabGameObject.GetComponent<LODGroup>().fadeMode = (LODFadeMode)prefabData.lodFadeMode; //Casting lod fade mode to enum.
                    prefabGameObject.GetComponent<LODGroup>().animateCrossFading = true;
                }

                List<LOD> lodsForPrefab = new List<LOD>();
                int numberOfFiles = prefabData.importedGeometryPaths3D.Count;

                List<float> lodHeights = MegascansUtilities.getLODHeightList(numberOfFiles);
                //Instantiate all the meshes in the scene, add them to the material/collider to them.
                for (int x = 0; (x < numberOfFiles && x < 8); x++)
                {
                    UnityEngine.Object loadedGeometry = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(prefabData.importedGeometryPaths3D[x]);
                    //Highpoly mesh check.
                    if (loadedGeometry.name.ToLower().Contains("highpoly") && !prefabData.highpoly)
                    {
                        continue;
                    }
                    GameObject geometryObject = Instantiate(loadedGeometry) as GameObject;
                    Renderer[] r;
                    //Parent all the objects to the prefab game object.
                    if (geometryObject.transform.childCount > 0 && !prefabData.isAlembic)
                    {
                        r = new Renderer[geometryObject.transform.childCount];
                        for (int j = 0; j < geometryObject.transform.childCount; ++j)
                        {
                            //Parent the child gameobject (geometry) to the prefab game object.
                            GameObject geometryChildObject = geometryObject.transform.GetChild(j).gameObject; //Cache a reference to the child gameobject of the geometry.
                            geometryChildObject.transform.parent = prefabGameObject.transform;
                            geometryChildObject.transform.localPosition = Vector3.zero;
                            geometryChildObject.name = geometryChildObject.name.Replace("(Clone)", "");
                            r[j] = geometryChildObject.GetComponentInChildren<Renderer>();
                        }
                        //Destroy the empty parent container which was holding the meshes.
                        DestroyImmediate(geometryObject);
                    }
                    else if (prefabData.isAlembic) //if the instantiated mesh is an alembic asset.
                    {
                        //Parent the child gameobject (geometry) to the prefab game object.
                        geometryObject.transform.parent = prefabGameObject.transform;
                        geometryObject.transform.localPosition = Vector3.zero;
                        geometryObject.name = geometryObject.name.Replace("(Clone)", "");
                        r = geometryObject.GetComponentsInChildren<Renderer>();
                    }
                    else //if the instantiated mesh does not have any children
                    {
                        //Parent the child gameobject (geometry) to the prefab game object.
                        geometryObject.transform.parent = prefabGameObject.transform;
                        geometryObject.transform.localPosition = Vector3.zero;
                        geometryObject.name = geometryObject.name.Replace("(Clone)", "");
                        r = geometryObject.GetComponentsInChildren<Renderer>();
                    }

                    foreach (Renderer ren in r)
                    {
                        ren.material = prefabData.finalMat;
                        //Apply collision
                        if (prefabData.setupCollision)
                            ren.gameObject.AddComponent<MeshCollider>().sharedMesh = ren.gameObject.GetComponent<MeshFilter>().sharedMesh;
                    }

                    if (prefabData.setupLODs)
                    {
                        lodsForPrefab.Add(new LOD(lodHeights[0], r));
                        lodHeights.RemoveAt(0);
                    } else //We only set the prefab with 1 LOD if setup LODs is unchecked.
                        break;
                }
                //Set LODs in the LOD group
                if (prefabData.setupLODs)
                {
                    prefabGameObject.GetComponent<LODGroup>().SetLODs(lodsForPrefab.ToArray());
                    prefabGameObject.GetComponent<LODGroup>().RecalculateBounds();
                }
                //Prefab saving
                string prefLocation = prefabPath + "/" + prefabName + ".prefab";
                prefLocation = prefLocation.Replace("(Clone)", "");
                SavePrefab(prefabGameObject, prefLocation, prefabData.addAssetToScene);
            }
            catch (Exception ex)
            {
                Debug.Log("Exception::MegascansMeshUtils::3D Asset Prefab:: " + ex.ToString());
                MegascansUtilities.HideProgressBar();
            }
        }

        /// <summary>
        /// Creates prefabs from the newer assets on bridge, has an option for billboard materials on plants.
        /// </summary>
        /// <param name="hasBillboard"></param>
        /// <returns></returns>
        public static void CreatePrefab3DPlant(PrefabData prefabData)
        {
            try
            {
                string prefabPath = MegascansUtilities.ValidateFolderCreate(prefabData.assetPath, "Prefabs");
                List<GameObject> prefabObjects = new List<GameObject>();

                for (int i = 0; i < prefabData.importedGeometryPaths3DPlant.Count; i++)
                {
                    string prefabName = prefabData.modelNamingConvention + "_Var" + (i + 1).ToString();
                    //Setting up prefab gameobject
                    GameObject prefabGameObject = new GameObject();
                    prefabGameObject.name = prefabName;
                    prefabGameObject.isStatic = true;

                    if (prefabData.setupLODs)
                    {
                        prefabGameObject.AddComponent<LODGroup>();
                        prefabGameObject.GetComponent<LODGroup>().fadeMode = (LODFadeMode)prefabData.lodFadeMode; //Casting lod fade mode to enum.
                        prefabGameObject.GetComponent<LODGroup>().animateCrossFading = true;
                    }

                    List<LOD> lodsForPrefab = new List<LOD>();
                    int numberOfFiles = prefabData.importedGeometryPaths3DPlant[i].Count;

                    List<float> lodHeights = MegascansUtilities.getLODHeightList(numberOfFiles);
                    //Instantiate all the meshes in the scene, add them to the material/collider to them.
                    for (int x = 0; (x < numberOfFiles && x < 8); x++)
                    {
                        UnityEngine.Object loadedGeometry = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(prefabData.importedGeometryPaths3DPlant[i][x]);
                        //Highpoly mesh check.
                        if (loadedGeometry.name.ToLower().Contains("highpoly") && !prefabData.highpoly)
                        {
                            continue;
                        }
                        GameObject geometryObject = Instantiate(loadedGeometry) as GameObject;
                        Renderer[] r;
                        //Parent all the objects to the prefab game object.
                        if (geometryObject.transform.childCount > 0 && !prefabData.isAlembic)
                        {
                            r = new Renderer[geometryObject.transform.childCount];
                            for (int j = 0; j < geometryObject.transform.childCount; ++j)
                            {
                                //Parent the child gameobject (geometry) to the prefab game object.
                                GameObject geometryChildObject = geometryObject.transform.GetChild(j).gameObject; //Cache a reference to the child gameobject of the geometry.
                                geometryChildObject.transform.parent = prefabGameObject.transform;
                                geometryChildObject.transform.localPosition = Vector3.zero;
                                geometryChildObject.name = geometryChildObject.name.Replace("(Clone)", "");
                                r[j] = geometryChildObject.GetComponent<Renderer>();
                            }
                            //Destroy the empty parent container which was holding the meshes.
                            DestroyImmediate(geometryObject);
                        }
                        else if (prefabData.isAlembic) //if the instantiated mesh is an alembic asset.
                        {
                            //Parent the child gameobject (geometry) to the prefab game object.
                            geometryObject.transform.parent = prefabGameObject.transform;
                            geometryObject.transform.localPosition = Vector3.zero;
                            geometryObject.name = geometryObject.name.Replace("(Clone)", "");
                            r = geometryObject.GetComponentsInChildren<Renderer>();
                        }
                        else //if the instantiated mesh does not have any children
                        {
                            r = new Renderer[1];
                            //Parent the child gameobject (geometry) to the prefab game object.
                            geometryObject.transform.parent = prefabGameObject.transform;
                            geometryObject.transform.localPosition = Vector3.zero;
                            geometryObject.name = geometryObject.name.Replace("(Clone)", "");
                            r[0] = geometryObject.GetComponent<Renderer>();
                        }

                        foreach (Renderer ren in r)
                        {
                            ren.material = prefabData.finalMat;
                            //Billboard material application
                            if (prefabData.hasBillboardLOD && x == (numberOfFiles - 1))
                            {
                                ren.material = prefabData.billboardMat;
                            }

                            //Apply collision
                            if (prefabData.setupCollision)
                                ren.gameObject.AddComponent<MeshCollider>().sharedMesh = ren.gameObject.GetComponent<MeshFilter>().sharedMesh;
                        }

                        if (prefabData.setupLODs)
                        {
                            lodsForPrefab.Add(new LOD(lodHeights[0], r));
                            lodHeights.RemoveAt(0);
                        }
                        else
                            break;
                    }
                    //Set LODs in the LOD group
                    if (prefabData.setupLODs)
                    {
                        prefabGameObject.GetComponent<LODGroup>().SetLODs(lodsForPrefab.ToArray());
                        prefabGameObject.GetComponent<LODGroup>().RecalculateBounds();
                    }
                    //Prefab saving
                    string prefLocation = prefabPath + "/" + prefabName + ".prefab";
                    prefLocation = prefLocation.Replace("(Clone)", "");
                    GameObject prefabObject = SavePrefab(prefabGameObject, prefLocation, prefabData.addAssetToScene);
                    if (prefabObject)
                        prefabObjects.Add(prefabObject);
                }

                //Setting up variation holder gameobject
                if (prefabData.addAssetToScene)
                {
                    GameObject plantsParent = new GameObject(prefabData.assetName);
                    plantsParent.isStatic = true;
                    foreach (GameObject variation in prefabObjects)
                    {
                        variation.transform.parent = plantsParent.transform;
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Log("Exception::MegascansMeshUtils::3D Plant Prefab:: " + ex.ToString());
                MegascansUtilities.HideProgressBar();
            }
        }

        /// <summary>
        /// Creates prefabs for the 3D Scatter assets.
        /// </summary>
        /// <param name="hasBillboard"></param>
        /// <returns></returns>
        public static void CreatePrefabsScatter(PrefabData prefabData)
        {
            try
            {
                string prefabPath = MegascansUtilities.ValidateFolderCreate(prefabData.assetPath, "Prefabs");
                int numberOfVariations = MegascansUtilities.GetMeshChildrenCount(prefabData.importedGeometryPaths3D);
                List<GameObject> prefabObjects = new List<GameObject>();

                for (int i = 0; i < numberOfVariations; i++)
                {
                    string prefabName = prefabData.modelNamingConvention + "_Var" + (i + 1).ToString();

                    //Setting up prefab gameobject
                    GameObject prefabGameObject = new GameObject();
                    prefabGameObject.name = prefabName;
                    prefabGameObject.isStatic = true;
                    if (prefabData.setupLODs)
                    {
                        prefabGameObject.AddComponent<LODGroup>();
                        prefabGameObject.GetComponent<LODGroup>().fadeMode = (LODFadeMode)prefabData.lodFadeMode; //Casting lod fade mode to enum.
                        prefabGameObject.GetComponent<LODGroup>().animateCrossFading = true;
                    }

                    List<LOD> lodsForPrefab = new List<LOD>();
                    int numberOfFiles = prefabData.importedGeometryPaths3D.Count;

                    List<float> lodHeights = MegascansUtilities.getLODHeightList(numberOfFiles);
                    //Instantiate all the meshes in the scene, add them to the material/collider to them.
                    for (int x = 0; (x < numberOfFiles && x < 8); x++)
                    {
                        UnityEngine.Object loadedGeometry = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(prefabData.importedGeometryPaths3D[x]);
                        //Highpoly mesh check.
                        if (loadedGeometry.name.ToLower().Contains("highpoly") && !prefabData.highpoly)
                        {
                            continue;
                        }
                        GameObject geometryObject = Instantiate(loadedGeometry) as GameObject;
                        Renderer[] r;
                        if (prefabData.isAlembic) //if the instantiated mesh is an alembic asset.
                        {
                            //Get all variations in a LOD
                            List<Transform> varsInLOD = new List<Transform>();
                            foreach (Transform var in geometryObject.transform)
                            {
                                varsInLOD.Add(var);
                            }
                            //Delete all the other variations in the LOD object
                            for (int y = 0; y < varsInLOD.Count; y++)
                            {
                                //If variation does not match one currently being processed.
                                if (y != i)
                                {
                                    DestroyImmediate(varsInLOD[y].gameObject);
                                }
                            }
                            //Parent the child gameobject (geometry) to the prefab game object.
                            geometryObject.transform.parent = prefabGameObject.transform;
                            geometryObject.transform.localPosition = Vector3.zero;
                            geometryObject.name = geometryObject.name.Replace("(Clone)", "");
                            r = geometryObject.GetComponentsInChildren<Renderer>();
                        }
                        else//if the instantiated mesh is a scatter type asset.
                        {
                            //Get all variations in a LOD
                            List<Transform> varsInLOD = new List<Transform>();
                            foreach (Transform var in geometryObject.transform)
                            {
                                varsInLOD.Add(var);
                            }
                            //Delete all the other variations in the LOD object
                            for(int y = 0; y < varsInLOD.Count; y++)
                            {
                                //If variation does not match one currently being processed.
                                if(y!=i)
                                {
                                    DestroyImmediate(varsInLOD[y].gameObject);
                                }
                            }
                            //Parent the child gameobject (geometry) to the prefab game object.
                            geometryObject.transform.parent = prefabGameObject.transform;
                            geometryObject.transform.localPosition = Vector3.zero;
                            geometryObject.name = geometryObject.name.Replace("(Clone)", "");
                            r = geometryObject.GetComponentsInChildren<Renderer>();
                        }

                        foreach (Renderer ren in r)
                        {
                            ren.material = prefabData.finalMat;
                            //Apply collision
                            if (prefabData.setupCollision)
                                ren.gameObject.AddComponent<MeshCollider>().sharedMesh = ren.gameObject.GetComponent<MeshFilter>().sharedMesh;
                        }

                        if (prefabData.setupLODs)
                        {
                            lodsForPrefab.Add(new LOD(lodHeights[0], r));
                            lodHeights.RemoveAt(0);
                        }
                        else
                            break;
                    }
                    //Set LODs in the LOD group
                    if (prefabData.setupLODs)
                    {
                        prefabGameObject.GetComponent<LODGroup>().SetLODs(lodsForPrefab.ToArray());
                        prefabGameObject.GetComponent<LODGroup>().RecalculateBounds();
                    }
                    //Prefab saving
                    string prefLocation = prefabPath + "/" + prefabName + ".prefab";
                    prefLocation = prefLocation.Replace("(Clone)", "");
                    GameObject prefabObject = SavePrefab(prefabGameObject, prefLocation, prefabData.addAssetToScene);
                    if (prefabObject)
                        prefabObjects.Add(prefabObject);
                }

                //Setting up variation holder gameobject
                if (prefabData.addAssetToScene)
                {
                    GameObject scatterParent = new GameObject(prefabData.assetName);
                    scatterParent.isStatic = true;
                    foreach (GameObject variation in prefabObjects)
                    {
                        variation.transform.parent = scatterParent.transform;
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Log("Exception::MegascansMeshUtils::3D Asset Prefab:: " + ex.ToString());
                MegascansUtilities.HideProgressBar();
            }
        }

        static GameObject SavePrefab(GameObject prefabGo, string savePath, bool addAssetToScene = false)
        {
            try
            {
                //Set all children objects of the prefab to static
                Transform[] allChildren = prefabGo.GetComponentsInChildren<Transform>();
                foreach (Transform child in allChildren)
                {
                    child.gameObject.isStatic = true;
                }

                GameObject newPrefabObject = prefabGo;
                MegascansUtilities.UpdateProgressBar(1.0f, "Importing Megascans Asset", "Creating Prefab...");
#if UNITY_5 || UNITY_2017 || UNITY_2018
                UnityEngine.Object existingPrefab = AssetDatabase.LoadAssetAtPath(savePath, typeof(UnityEngine.Object));
                if (!existingPrefab)
                    PrefabUtility.CreatePrefab(savePath, prefabGo);
                else
                    PrefabUtility.ReplacePrefab(prefabGo, existingPrefab, ReplacePrefabOptions.ReplaceNameBased);
#else
                PrefabUtility.SaveAsPrefabAsset(prefabGo, savePath);
#endif
                DestroyImmediate(prefabGo);
                if (addAssetToScene)
                {
                    UnityEngine.Object prefabObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(savePath);
                    newPrefabObject = (GameObject)PrefabUtility.InstantiatePrefab(prefabObject);
                    newPrefabObject.isStatic = true;
                }
                return newPrefabObject;
            }
            catch (Exception ex)
            {
                Debug.Log("Exception::MegascansMeshUtils::Saving Prefab:: " + ex.ToString());
                MegascansUtilities.HideProgressBar();
                return null;
            }
        }


        public static bool ContainsLowestLOD(JArray lodList, string minLOD, string activeLOD)
        {
            bool importLODs = EditorPrefs.GetBool("QuixelDefaultImportLODs", true);
            if (importLODs)
            {
                for (int i = 0; i < lodList.Count; i++)
                {
                    JObject meshData = (JObject)lodList[i];
                    if ((string)meshData["lod"] == minLOD)
                        return true;
                }

                return false;
            } else
            {
                return (activeLOD.ToLower() == minLOD.ToLower());
            }
        }
    }

    public struct PrefabData
    {
        public string assetPath;
        public string assetName;
        public string modelNamingConvention;
        public int lodFadeMode;
        public bool highpoly;
        public bool addAssetToScene;
        public bool setupCollision;
        public bool hasBillboardLOD;
        public bool isAlembic;
        public bool isScatterAsset;
        public bool setupLODs;
        public Material finalMat;
        public Material billboardMat;
        public List<string> importedGeometryPaths3D;
        public List<List<string>> importedGeometryPaths3DPlant;

        public PrefabData(string assetPath, string assetName, string modelNamingConvention, int lodFadeMode, bool highpoly, bool addAssetToScene, bool setupCollision, bool hasBillboardLOD, bool isAlembic, bool isScatterAsset, bool setupLODs, Material finalMat, Material billboardMat, List<string> importedGeometryPaths3D, List<List<string>> importedGeometryPaths3DPlant)
        {
            this.assetPath = assetPath;
            this.assetName = assetName;
            this.modelNamingConvention = modelNamingConvention;
            this.lodFadeMode = lodFadeMode;
            this.highpoly = highpoly;
            this.addAssetToScene = addAssetToScene;
            this.setupCollision = setupCollision;
            this.hasBillboardLOD = hasBillboardLOD;
            this.isAlembic = isAlembic;
            this.isScatterAsset = isScatterAsset;
            this.setupLODs = setupLODs;
            this.finalMat = finalMat;
            this.billboardMat = billboardMat;
            this.importedGeometryPaths3D = importedGeometryPaths3D;
            this.importedGeometryPaths3DPlant = importedGeometryPaths3DPlant;
        }

    }

}
#endif