123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- #if UNITY_EDITOR
- using UnityEngine;
- using UnityEditor;
- using System.Collections.Generic;
- using System.IO;
- using System;
- using Newtonsoft.Json.Linq;
- namespace Quixel
- {
- /// <summary>
- /// Completely rewritten importer with lots of commenting... Should be quite a bit faster than the previous one too.
- /// For those looking to customize, please be aware that some of the methods in here do not use built in Unity API calls.
- /// Ajwad Imran - Technical Artist @ Quixel.
- /// Lee Devonald - (Former) Technical Artist @ Quixel.
- /// </summary>
- public class MegascansImporter : Editor
- {
- private bool plant = false;
- private string assetName;
- private string type;
- private string mapName;
- private string folderNamingConvention;
- private string path;
- private string activeLOD;
- private int dispType;
- private int texPack;
- private int lodFadeMode;
- private int shaderType;
- private bool setupCollision = false;
- private bool applyToSelection;
- private bool addAssetToScene;
- private bool importAllTextures;
- private string texPath;
- private string matPath;
- private Material finalMat;
- private Material billboardMat;
- private bool highPoly = false;
- private bool isAlembic = false;
- private bool hasBillboardLOD = false;
- private bool hasBillboardLODOnly = false;
- /// <summary>
- /// Takes an imported JSON object, and breaks it into relevant components and data.
- /// Then calls relevant functions for actual import of asset.
- /// </summary>
- /// <param name="objectList"></param>
- public string ImportMegascansAssets(JObject objectList)
- {
- var startTime = System.DateTime.Now;
- activeLOD = (string)objectList["activeLOD"];
- string minLOD = (string)objectList["minLOD"];
- assetName = (string)objectList["name"];
- type = (string)objectList["type"];
- isAlembic = false;
- plant = false;
- highPoly = false;
- hasBillboardLOD = false;
- hasBillboardLODOnly = false;
- mapName = "";
- folderNamingConvention = (string)objectList["folderNamingConvention"];
- //get mesh components from the current object.
- JArray meshComps = (JArray)objectList["meshList"];
- //run a check to see if we're using Unity 5 or below, and then if we're trying to import a high poly mesh. if so, let the user know we are aborting the import.
- if (meshComps.Count > 0)
- {
- isAlembic = Path.GetExtension((string)meshComps[0]["path"]) == ".abc";
- }
- hasBillboardLOD = MegascansMeshUtils.ContainsLowestLOD((JArray)objectList["lodList"], minLOD, activeLOD);
- if (type.ToLower().Contains("3dplant"))
- {
- plant = true;
- if (minLOD == activeLOD)
- {
- hasBillboardLODOnly = true;
- }
- }
- try
- {
- LoadPreferences();
- shaderType = MegascansUtilities.GetShaderType();
- MegascansUtilities.CalculateNumberOfOperations(objectList, dispType, texPack, shaderType, hasBillboardLODOnly);
- path = ConstructPath(objectList);
- if (path == null || path == "")
- {
- Debug.Log("Asset: " + (string)objectList["name"] + " already exist in the Project. Please delete/rename the existing folder and re-import this asset.");
- AssetDatabase.Refresh();
- return null;
- }
- }
- catch (Exception ex)
- {
- Debug.Log("Error setting import path.");
- Debug.Log(ex.ToString());
- MegascansUtilities.HideProgressBar();
- }
- try
- {
- //process textures
- ProcessTextures(objectList);
- if (finalMat == null && !(plant && hasBillboardLODOnly))
- {
- Debug.Log("Could not import the textures and create the material.");
- return null;
- }
- else
- {
- if (type.ToLower().Contains("surface") && applyToSelection)
- {
- foreach (MeshRenderer render in MegascansUtilities.GetSelectedMeshRenderers())
- {
- render.material = finalMat;
- }
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Log("Error importing textures.");
- Debug.Log(ex.ToString());
- MegascansUtilities.HideProgressBar();
- }
- //process meshes
- if (meshComps == null && !type.Contains("surface"))
- {
- Debug.LogError("No meshes found. Please double check your export settings.");
- Debug.Log("Import failed.");
- return null;
- }
- if (meshComps.Count > 0)
- {
- if (activeLOD == "high")
- {
- //detect if we're trying to import a high poly mesh...
- string msg = "You are about to import a high poly mesh. \nThese meshes are usually millions of polygons and can cause instability to your project. \nWould you like to proceed?";
- highPoly = EditorUtility.DisplayDialog("WARNING!", msg, "Yes", "No");
- }
- try
- {
- //process meshes and prefabs
- PrefabData prefData = new PrefabData(path, assetName, folderNamingConvention, lodFadeMode, highPoly, addAssetToScene, setupCollision, hasBillboardLOD, isAlembic, false, false, finalMat, billboardMat, new List<string>(), new List<List<string>>());
- MegascansMeshUtils.ProcessMeshes(objectList, path, highPoly, plant, prefData);
- }
- catch (Exception ex)
- {
- Debug.Log("Error importing meshes.");
- Debug.Log(ex.ToString());
- MegascansUtilities.HideProgressBar();
- }
- }
- var endTime = System.DateTime.Now;
- var totalTime = endTime - startTime;
- Debug.Log("Asset Import Time: " + totalTime);
- AssetDatabase.Refresh();
- MegascansUtilities.HideProgressBar();
- Resources.UnloadUnusedAssets();
- GC.Collect();
- return path;
- }
- #region Texture Processing Methods
- void ProcessTextures(JObject objectList)
- {
- texPath = MegascansUtilities.ValidateFolderCreate(path, "Textures");
- matPath = Path.Combine(MegascansUtilities.ValidateFolderCreate(path, "Materials"), folderNamingConvention);
- if (!(plant && hasBillboardLODOnly))
- {
- MegascansUtilities.UpdateProgressBar(1.0f, "Processing Asset " + assetName, "Creating material...");
- finalMat = MegascansMaterialUtils.CreateMaterial(shaderType, matPath, isAlembic, dispType, texPack);
- ImportAllTextures(finalMat, (JArray)objectList["components"]);
- ImportAllTextures(finalMat, (JArray)objectList["packedTextures"]);
- }
- if (plant && hasBillboardLOD)
- {
- texPath = MegascansUtilities.ValidateFolderCreate(texPath, "Billboard");
- matPath += "_Billboard";
- MegascansUtilities.UpdateProgressBar(1.0f, "Processing Asset " + assetName, "Creating material...");
- billboardMat = MegascansMaterialUtils.CreateMaterial(shaderType, matPath, isAlembic, dispType, texPack);
- ImportAllTextures(billboardMat, (JArray)objectList["components-billboard"]);
- ImportAllTextures(billboardMat, (JArray)objectList["packed-billboard"]);
- }
- }
- void ImportAllTextures(Material mat, JArray texturesList)
- {
- try
- {
- List<string> typesOfTexturesAvailable = new List<string>();
- for (int i = 0; i < texturesList.Count; i++)
- {
- typesOfTexturesAvailable.Add((string)texturesList[i]["type"]);
- }
- string destTexPath;
- Texture2D tex;
- for (int i = 0; i < texturesList.Count; i++)
- {
- mapName = (string)texturesList[i]["type"];
- MegascansUtilities.UpdateProgressBar(1.0f, "Processing Asset " + assetName, "Importing texture: " + mapName);
- if ((string)texturesList[i]["type"] == "albedo" || ((string)texturesList[i]["type"] == "diffuse" && !typesOfTexturesAvailable.Contains("albedo")))
- {
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor((string)texturesList[i]["path"], destTexPath);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_MainTex", tex);
- mat.SetTexture("_BaseColorMap", tex);
- if (shaderType == 1)
- {
- mat.SetTexture("_BaseMap", tex);
- mat.SetColor("_BaseColor", Color.white);
- }
- if (MegascansUtilities.AlbedoHasOpacity((JObject)texturesList[i]["channelsData"]))
- {
- float alphaCutoff = 0.33f;
- texPrcsr.AdjustAlphaCutoff();
- if (shaderType > 0)
- {
- mat.SetFloat("_AlphaClip", 1);
- mat.SetFloat("_Cutoff", 0.1f);
- mat.SetFloat("_Mode", 1);
- mat.SetFloat("_Cull", 0);
- mat.EnableKeyword("_ALPHATEST_ON");
- }
- else
- {
- mat.SetInt("_AlphaCutoffEnable", 1);
- mat.SetFloat("_AlphaCutoff", alphaCutoff);
- mat.SetInt("_DoubleSidedEnable", 1);
- mat.SetOverrideTag("RenderType", "TransparentCutout");
- mat.SetInt("_ZTestGBuffer", (int)UnityEngine.Rendering.CompareFunction.Equal);
- mat.SetInt("_CullMode", (int)UnityEngine.Rendering.CullMode.Off);
- mat.SetInt("_CullModeForward", (int)UnityEngine.Rendering.CullMode.Back);
- mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
- mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
- mat.SetInt("_ZWrite", 1);
- mat.renderQueue = 2450;
- mat.SetInt("_ZTestGBuffer", (int)UnityEngine.Rendering.CompareFunction.Equal);
- mat.EnableKeyword("_ALPHATEST_ON");
- mat.EnableKeyword("_DOUBLESIDED_ON");
- mat.DisableKeyword("_BLENDMODE_ALPHA");
- }
- }
- }
- else if ((string)texturesList[i]["type"] == "specular")
- {
- if (texPack > 0)
- {
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor((string)texturesList[i]["path"], destTexPath);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_SpecGlossMap", tex);
- mat.SetTexture("_SpecularColorMap", tex);
- mat.SetColor("_SpecColor", new UnityEngine.Color(1.0f, 1.0f, 1.0f));
- mat.SetColor("_SpecularColor", new UnityEngine.Color(1.0f, 1.0f, 1.0f));
- mat.SetFloat("_WorkflowMode", 0);
- mat.SetFloat("_MaterialID", 4);
- mat.EnableKeyword("_METALLICSPECGLOSSMAP");
- mat.EnableKeyword("_SPECGLOSSMAP");
- mat.EnableKeyword("_SPECULAR_SETUP");
- mat.EnableKeyword("_SPECULARCOLORMAP");
- mat.EnableKeyword("_MATERIAL_FEATURE_SPECULAR_COLOR");
- }
- }
- else if ((string)texturesList[i]["type"] == "masks")
- {
- if (texPack < 1 || shaderType < 1)
- {
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor((string)texturesList[i]["path"], destTexPath, false, false);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_MaskMap", tex);
- mat.SetTexture("_MetallicGlossMap", tex);
- mat.EnableKeyword("_MASKMAP");
- mat.SetFloat("_MaterialID", 1);
- mat.EnableKeyword("_METALLICSPECGLOSSMAP");
- mat.EnableKeyword("_METALLICGLOSSMAP");
- bool hasMetalness;
- bool hasAO;
- bool hasGloss;
- MegascansUtilities.MaskMapComponents((JObject)texturesList[i]["channelsData"], out hasMetalness, out hasAO, out hasGloss);
- if (!hasMetalness)
- {
- mat.SetFloat("_Metallic", 1.0f);
- }
- if (hasAO)
- {
- mat.SetTexture("_OcclusionMap", tex);
- mat.EnableKeyword("_OCCLUSIONMAP");
- }
- }
- }
- else if ((string)texturesList[i]["type"] == "normal")
- {
- string normalMapPath = (string)texturesList[i]["path"];
- if (activeLOD == "high" && !normalMapPath.Contains("NormalBump"))
- {
- for (int x = 0; x < 10; x++)
- {
- string n = normalMapPath.Replace("_LOD" + x.ToString(), "Bump");
- if (File.Exists(n))
- {
- normalMapPath = n;
- break;
- }
- }
- if (normalMapPath.Contains("NormalBump"))
- continue;
- }
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor(normalMapPath, destTexPath, true, false);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_BumpMap", tex);
- mat.SetTexture("_NormalMap", tex);
- mat.EnableKeyword("_NORMALMAP_TANGENT_SPACE");
- mat.EnableKeyword("_NORMALMAP");
- }
- else if ((string)texturesList[i]["type"] == "ao" && texPack > 0)
- {
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor((string)texturesList[i]["path"], destTexPath, false, false);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_OcclusionMap", tex);
- mat.EnableKeyword("_OCCLUSIONMAP");
- }
- else if ((string)texturesList[i]["type"] == "displacement")
- {
- if (dispType > 0)
- {
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor((string)texturesList[i]["path"], destTexPath, false, false);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_HeightMap", tex);
- mat.SetTexture("_ParallaxMap", tex);
- mat.EnableKeyword("_DISPLACEMENT_LOCK_TILING_SCALE");
- if (shaderType == 0)
- mat.EnableKeyword("_HEIGHTMAP");
- if (dispType == 1)
- {
- mat.EnableKeyword("_VERTEX_DISPLACEMENT");
- mat.EnableKeyword("_VERTEX_DISPLACEMENT_LOCK_OBJECT_SCALE");
- }
- else if (dispType == 2)
- {
- mat.EnableKeyword("_PARALLAXMAP");
- mat.EnableKeyword("_PIXEL_DISPLACEMENT");
- mat.EnableKeyword("_PIXEL_DISPLACEMENT_LOCK_OBJECT_SCALE");
- }
- }
- }
- else if ((string)texturesList[i]["type"] == "translucency")
- {
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor((string)texturesList[i]["path"], destTexPath);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_SubsurfaceMaskMap", tex);
- mat.EnableKeyword("_SUBSURFACE_MASK_MAP");
- mat.SetInt("_DiffusionProfile", 1);
- mat.SetFloat("_EnableSubsurfaceScattering", 1);
- if (!typesOfTexturesAvailable.Contains("transmission"))
- {
- mat.SetTexture("_ThicknessMap", tex);
- mat.EnableKeyword("_THICKNESSMAP");
- }
- if (plant)
- {
- mat.SetInt("_DiffusionProfile", 2);
- mat.SetFloat("_CoatMask", 0.0f);
- mat.SetInt("_EnableWind", 1);
- mat.EnableKeyword("_VERTEX_WIND");
- }
- MegascansMaterialUtils.AddSSSSettings(mat, shaderType);
- }
- else if ((string)texturesList[i]["type"] == "transmission")
- {
- destTexPath = Path.Combine(texPath, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor((string)texturesList[i]["path"], destTexPath, false, false);
- tex = texPrcsr.ImportTexture();
- mat.SetTexture("_ThicknessMap", tex);
- mat.EnableKeyword("_THICKNESSMAP");
- mat.SetInt("_DiffusionProfile", 2);
- MegascansMaterialUtils.AddSSSSettings(mat, shaderType);
- }
- else if (importAllTextures)
- {
- mapName = (string)texturesList[i]["type"];
- string mapPath = (string)texturesList[i]["path"];
- string otherTexFolder = MegascansUtilities.ValidateFolderCreate(texPath, "Others");
- destTexPath = Path.Combine(otherTexFolder, (string)texturesList[i]["nameOverride"]);
- MegascansTextureProcessor texPrcsr = new MegascansTextureProcessor(mapPath, destTexPath);
- tex = texPrcsr.ImportTexture();
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Log("Exception::MegascansImporter::ImportAllTextures:: " + ex.ToString());
- MegascansUtilities.HideProgressBar();
- }
- }
- #endregion
- #region Formatting Utilities
- void LoadPreferences()
- {
- path = MegascansUtilities.FixPath(EditorPrefs.GetString("QuixelDefaultPath", "Quixel/Megascans/"));
- dispType = EditorPrefs.GetInt("QuixelDefaultDisplacement");
- texPack = EditorPrefs.GetInt("QuixelDefaultTexPacking");
- shaderType = EditorPrefs.GetInt("QuixelDefaultShader");
- lodFadeMode = EditorPrefs.GetInt("QuixelDefaultLodFadeMode", 1);
- setupCollision = EditorPrefs.GetBool("QuixelDefaultSetupCollision", true);
- applyToSelection = EditorPrefs.GetBool("QuixelDefaultApplyToSelection", false);
- addAssetToScene = EditorPrefs.GetBool("QuixelDefaultAddAssetToScene", false);
- importAllTextures = EditorPrefs.GetBool("QuixelDefaultImportAllTextures", false);
- }
- /// <summary>
- /// Returns the final directory for our asset, creating subfolders where necessary in the 'Assets' directory.
- /// </summary>
- string ConstructPath(JObject objectList)
- {
- /// Make sure path is "Assets/...." not "D:/Unity Projects/My Project/Assets/...." otherwise the AssetDatabase cannot write files to it.
- /// Lastly I also match the path with the Application DataPath in order to make sure this is the right path selected from the Bridge.
- AssetDatabase.Refresh();
- string defPath = "";
- bool addNextPathPart = false;
- if ((string)objectList["exportPath"] != "")
- {
- path = (string)objectList["exportPath"];
- }
- else
- {
- defPath = "Assets";
- addNextPathPart = true;
- }
- string[] pathParts = MegascansUtilities.FixSlashes(path).Split('/');
- List<string> finalPathParts = new List<string>();
- foreach (string part in pathParts)
- {
- if (part == "Assets" && !addNextPathPart)
- {
- addNextPathPart = true;
- }
- if (addNextPathPart)
- {
- finalPathParts.Add(part);
- }
- }
- if (!addNextPathPart)
- {
- return null;
- }
- //First, create the user specified path from the importer settings.
- if (finalPathParts.Count > 0)
- {
- for (int i = 0; i < finalPathParts.Count; i++)
- {
- defPath = MegascansUtilities.ValidateFolderCreate(defPath, finalPathParts[i]); //FixSlashes(Path.Combine(defPath, finalPathParts[i]));//ValidateFolderCreate(defPath, finalPathParts[i]);
- }
- }
- if (!AssetDatabase.IsValidFolder(defPath))
- {
- return null;
- }
- //then create check to see if the asset type subfolder exists, create it if it doesn't.
- defPath = MegascansUtilities.ValidateFolderCreate(defPath, MegascansUtilities.GetAssetType((string)objectList["path"]));
- defPath = MegascansUtilities.ValidateFolderCreate(defPath, folderNamingConvention);
- return defPath;
- }
- #endregion
- }
- }
- #endif
|