ProjectDDD/Assets/_DDD/_Scripts/AssetPostprocessors/AssetPostprocessorEnvironment.cs
2025-08-12 14:05:18 +09:00

327 lines
13 KiB
C#

#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<EnvPrefabType, HashSet<string>> PrefabTargetPaths = new()
{
{EnvPrefabType.Prop, new HashSet<string>()},
{EnvPrefabType.Item, new HashSet<string>() },
{EnvPrefabType.Geometry, new HashSet<string>() },
{EnvPrefabType.Ground, new HashSet<string>() },
};
private static readonly Dictionary<EnvPrefabType, string> 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<Sprite>(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<Material>(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<Texture>(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<string>();
}
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<GameObject>(prefabPath) != null) return;
if (InstantiatePrefabByType(folderName, prefabPath, prefabType, out var instancePrefab)) return;
if (mat != null)
{
var renderer = instancePrefab.GetComponentInChildren<Renderer>();
if (renderer != null) renderer.sharedMaterial = mat;
}
SavePrefabInstance(prefabPath, instancePrefab);
}
private static void CreateSpritePrefabVariantIfNotExist(string folderName, Sprite sprite, string prefabPath,
EnvPrefabType prefabType)
{
if (AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath) != null) return;
if (InstantiatePrefabByType(folderName, prefabPath, prefabType, out var instancePrefab)) return;
if (sprite != null)
{
var renderer = instancePrefab.GetComponentInChildren<SpriteRenderer>();
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<GameObject>(basePrefabPath);
if (basePrefab == null)
{
Debug.LogWarning($"Base prefab not found: {basePrefabPath}");
instancePrefab = null;
return true;
}
string prefix = prefabType.ToString() + "_";
instancePrefab = AssetDatabase.LoadAssetAtPath<GameObject>(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