#if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; using Unity.VisualScripting; using UnityEditor; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Settings; using UnityEngine; using Object = UnityEngine.Object; namespace DDD { public static class AssetPostprocessorEnvironment { private const string ShaderName = "Universal Render Pipeline/LitEnvironment"; private const string BaseMapPropertyName = "_BaseMap"; private const string NormalMapPropertyName = "_NormalMap"; private static readonly Dictionary> PrefabTargetPaths = new() { {EnvPrefabType.Prop, new HashSet()}, {EnvPrefabType.Item, new HashSet() }, {EnvPrefabType.Geometry, new HashSet() }, {EnvPrefabType.Ground, new HashSet() }, }; private static readonly Dictionary BasePrefabPaths = new() { { EnvPrefabType.Prop, "Assets/_DDD/_Raw/Environments/Env_Mesh_Prop.prefab" }, { EnvPrefabType.Item, "Assets/_DDD/_Raw/Environments/Env_Unlit_Item.prefab" }, { EnvPrefabType.Geometry, "Assets/_DDD/_Raw/Environments/Env_Mesh_Geometry.prefab" }, { EnvPrefabType.Ground, "Assets/_DDD/_Raw/Environments/Env_Mesh_Ground.prefab" }, }; public enum EnvPrefabType : uint { Prop, Item, Geometry, Ground, } public static void OnPreprocessTexture(TextureImporter importer) { var path = importer.assetPath; string fileNameUpper = Utils.FileName(path).ToUpper(); if (IsTextureMatchingPropertyBySuffix(fileNameUpper, NormalMapPropertyName)) { importer.textureType = TextureImporterType.NormalMap; } else if (IsTextureMatchingPropertyBySuffix(fileNameUpper, BaseMapPropertyName)) { importer.textureType = TextureImporterType.Sprite; importer.spriteImportMode = SpriteImportMode.Single; } else { importer.textureType = TextureImporterType.Default; importer.sRGBTexture = true; } importer.mipmapEnabled = true; } public static void OnAdd(string path) { AddTargetPath(path); } public static void OnRemove(string path, string movePath = "") { AddTargetPath(path); } private static string GetEnvironmentPath(EnvPrefabType prefabType) { string prefabPath = PathConstants.RawEnvironmentsPathUpper + prefabType.ToString(); prefabPath = prefabPath.ToUpper(); return prefabPath; } private static void AddTargetPath(string path) { string upperPath = path.ToUpper(); if (upperPath.Contains(ExtenstionConstants.PngExtensionUpper) == false) { // Not a texture. return; } for (uint i = 0; i < PrefabTargetPaths.Count; i++) { var prefabType = (EnvPrefabType)i; var targetPaths = PrefabTargetPaths[prefabType]; if (upperPath.Contains(GetEnvironmentPath(prefabType))) { targetPaths.Add(path); } } } private static string GetPrefabPrefix(EnvPrefabType prefabType) { return prefabType.ToString() + "_"; } private static string GetMaterialPrefix() { return "Mat_"; } public static void BuildMaterialAndPrefab(string path, EnvPrefabType prefabType) { var di = new DirectoryInfo(path); if (!di.Exists) return; string folderName = di.Name; string rawRoot = PathConstants.RawFolderPath; // "/_Raw" string addrRoot = PathConstants.AddressablesFolderPath; // "/_Addressables" string destDir = path.Replace(rawRoot, addrRoot); string materialPath = $"{destDir}/{GetMaterialPrefix()}{folderName}{ExtenstionConstants.MaterialExtenstionLower}"; string prefabPath = $"{destDir}/{GetPrefabPrefix(prefabType)}{folderName}{ExtenstionConstants.PrefabExtenstionLower}"; Utils.MakeFolderFromFilePath(materialPath); bool bShouldCreateMaterial = false; bShouldCreateMaterial = prefabType != EnvPrefabType.Item; // Add conditions if needed. if (bShouldCreateMaterial) { // 머티리얼 생성 또는 로드 var mat = CreateOrLoadMaterial(path, materialPath, out var files, out var shader); MatchTexturesToShaderProperties(shader, files, mat); AssetDatabase.ImportAsset(materialPath, ImportAssetOptions.ForceUpdate); AssetDatabase.SaveAssets(); CreateMaterialPrefabVariantIfNotExist(folderName, mat, prefabPath, prefabType); } else // Set sprite to renderer { var files = Directory.GetFiles(path, $"*{ExtenstionConstants.PngExtensionLower}", SearchOption.TopDirectoryOnly); foreach (var file in files) { string texName = Path.GetFileNameWithoutExtension(file).ToUpper(); Sprite tex = AssetDatabase.LoadAssetAtPath(file); if (tex == null) continue; // 셰이더 프로퍼티명과 텍스처 파일명의 접미사 매칭 if (IsTextureMatchingPropertyBySuffix(texName, BaseMapPropertyName)) { CreateSpritePrefabVariantIfNotExist(folderName, tex, prefabPath, prefabType); } } } } private static Material CreateOrLoadMaterial(string path, string materialPath, out string[] files, out Shader shader) { Material mat = AssetDatabase.LoadAssetAtPath(materialPath); if (mat == null) { mat = new Material(Shader.Find(ShaderName)); AssetDatabase.CreateAsset(mat, materialPath); } // PNG 텍스처 매핑 - 셰이더 프로퍼티와 텍스처 접미사 매칭 files = Directory.GetFiles(path, $"*{ExtenstionConstants.PngExtensionLower}", SearchOption.TopDirectoryOnly); shader = mat.shader; return mat; } private static void MatchTexturesToShaderProperties(Shader shader, string[] files, Material mat) { for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); i++) { if (ShaderUtil.GetPropertyType(shader, i) != ShaderUtil.ShaderPropertyType.TexEnv) continue; string propertyName = ShaderUtil.GetPropertyName(shader, i); foreach (var file in files) { string texName = Path.GetFileNameWithoutExtension(file).ToUpper(); var tex = AssetDatabase.LoadAssetAtPath(file); if (tex == null) continue; // 셰이더 프로퍼티명과 텍스처 파일명의 접미사 매칭 if (IsTextureMatchingPropertyBySuffix(texName, propertyName)) { mat.SetTexture(propertyName, tex); break; // 매칭되면 다음 프로퍼티로 } } } } private static string[] GetPropertyAliases(string propertyName) { if (propertyName == "_BaseMap") { return new[] {"_Base", "_BC", "BaseColor"}; } if (propertyName == "_NormalMap") { return new[] {"_Normal", "_Bump", "_BumpMap", "_N"}; } if (propertyName == "_MOHS") { return new[] {"_Mask"}; } if (propertyName == "_Emiss") { return new[] {"_Emissive", "_Emission", "_E"}; } return Array.Empty(); } private static bool IsTextureMatchingPropertyBySuffix(string textureName, string propertyName) { string[] aliases = GetPropertyAliases(propertyName); foreach (var alias in aliases) { if (textureName.ToUpper().EndsWith(alias.ToUpper())) { return true; } } // 셰이더 프로퍼티명에서 접미사 추출 (예: _BaseMap -> BASEMAP) string propertySuffix = propertyName.TrimStart('_').ToUpper(); // 텍스처 파일명이 해당 접미사로 끝나는지 확인 return textureName.EndsWith($"_{propertySuffix}"); } private static void CreateMaterialPrefabVariantIfNotExist(string folderName, Material mat, string prefabPath, EnvPrefabType prefabType) { if (AssetDatabase.LoadAssetAtPath(prefabPath) != null) return; if (InstantiatePrefabByType(folderName, prefabPath, prefabType, out var instancePrefab)) return; if (mat != null) { var renderer = instancePrefab.GetComponentInChildren(); if (renderer != null) renderer.sharedMaterial = mat; } SavePrefabInstance(prefabPath, instancePrefab); } private static void CreateSpritePrefabVariantIfNotExist(string folderName, Sprite sprite, string prefabPath, EnvPrefabType prefabType) { if (AssetDatabase.LoadAssetAtPath(prefabPath) != null) return; if (InstantiatePrefabByType(folderName, prefabPath, prefabType, out var instancePrefab)) return; if (sprite != null) { var renderer = instancePrefab.GetComponentInChildren(); if (renderer != null) renderer.sprite = sprite; } SavePrefabInstance(prefabPath, instancePrefab); } private static void SavePrefabInstance(string prefabPath, GameObject instancePrefab) { Utils.MakeFolderFromFilePath(prefabPath); PrefabUtility.SaveAsPrefabAssetAndConnect(instancePrefab, prefabPath, InteractionMode.AutomatedAction); Object.DestroyImmediate(instancePrefab); AssetDatabase.ImportAsset(prefabPath, ImportAssetOptions.ForceUpdate); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } private static bool InstantiatePrefabByType(string folderName, string prefabPath, EnvPrefabType prefabType, out GameObject instancePrefab) { // EnvPrefabType에 따라 베이스 프리팹 결정 string basePrefabPath = GetBasePrefabPath(prefabType); var basePrefab = AssetDatabase.LoadAssetAtPath(basePrefabPath); if (basePrefab == null) { Debug.LogWarning($"Base prefab not found: {basePrefabPath}"); instancePrefab = null; return true; } string prefix = prefabType.ToString() + "_"; instancePrefab = AssetDatabase.LoadAssetAtPath(prefabPath); if (instancePrefab == null) { instancePrefab = (GameObject)PrefabUtility.InstantiatePrefab(basePrefab); instancePrefab.name = $"{prefix}{folderName}"; } return false; } private static string GetBasePrefabPath(EnvPrefabType prefabType) { if (BasePrefabPaths.TryGetValue(prefabType, out var path)) { return path; } Debug.LogError($"Base prefab path not found: {prefabType}"); return ""; } public static void BuildTarget() { foreach (var envTargets in PrefabTargetPaths) { var envType = envTargets.Key; var TargetPaths= envTargets.Value; foreach (var path in TargetPaths) { BuildMaterialAndPrefab(Utils.FolderPath(path), envType); } TargetPaths.Clear(); } } } } #endif