NLDClient-yudde/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MaterialUtils.cs

511 lines
19 KiB
C#
Raw Normal View History

2025-04-25 15:07:22 +08:00
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
/// <summary>
/// 材质工具类,处理材质和贴图的复制、替换等操作
/// </summary>
public static class MaterialUtils
{
/// <summary>
/// 处理单个材质及其关联的贴图
/// </summary>
public static void ProcessMaterialAndTexture(Material sourceMat, string targetDir, string prefix,
Dictionary<Material, Material> matMapping,
Dictionary<Texture, Texture> texMapping)
{
if (sourceMat == null || matMapping.ContainsKey(sourceMat))
return;
// 收集原始材质中的所有贴图引用
Dictionary<string, Texture> textureProperties = new Dictionary<string, Texture>();
Dictionary<string, Vector2> textureScales = new Dictionary<string, Vector2>();
Dictionary<string, Vector2> textureOffsets = new Dictionary<string, Vector2>();
// 常见贴图属性名称
string[] texturePropertyNames = new string[]
{
"_MainTex", "_BaseMap", "_AlbedoMap", "_DiffuseMap",
"_BumpMap", "_NormalMap", "_EmissionMap", "_MetallicGlossMap",
"_OcclusionMap", "_DetailAlbedoMap", "_DetailNormalMap", "_SpecGlossMap",
"_ParallaxMap", "_DetailMask", "_MaskMap", "_SmoothnessMap"
};
// 收集原始材质中的所有贴图
foreach (var propName in texturePropertyNames)
{
if (sourceMat.HasProperty(propName))
{
Texture tex = sourceMat.GetTexture(propName);
if (tex != null)
{
textureProperties[propName] = tex;
textureScales[propName] = sourceMat.GetTextureScale(propName);
textureOffsets[propName] = sourceMat.GetTextureOffset(propName);
}
}
}
// 如果没有找到任何贴图属性至少确保我们查看_MainTex
if (textureProperties.Count == 0 && sourceMat.mainTexture != null)
{
textureProperties["_MainTex"] = sourceMat.mainTexture;
textureScales["_MainTex"] = sourceMat.mainTextureScale;
textureOffsets["_MainTex"] = sourceMat.mainTextureOffset;
}
// 为每个唯一贴图创建副本
Dictionary<Texture, Texture> localTexMapping = new Dictionary<Texture, Texture>();
foreach (var texEntry in textureProperties)
{
Texture originalTexture = texEntry.Value;
if (originalTexture == null) continue;
// 检查这个贴图是否已经有映射
if (texMapping.ContainsKey(originalTexture))
{
localTexMapping[originalTexture] = texMapping[originalTexture];
continue;
}
// 否则创建新的贴图副本
string originalTexPath = AssetDatabase.GetAssetPath(originalTexture);
if (string.IsNullOrEmpty(originalTexPath)) continue; // 跳过内置贴图
string texName = $"{prefix}{Path.GetFileNameWithoutExtension(originalTexPath)}";
string texExtension = Path.GetExtension(originalTexPath);
string newTexPath = $"{targetDir}/{texName}{texExtension}";
// 避免重复创建相同的贴图
if (!AssetDatabase.LoadAssetAtPath<Texture>(newTexPath))
{
AssetDatabase.CopyAsset(originalTexPath, newTexPath);
Debug.Log($"已复制贴图: {originalTexPath} -> {newTexPath}");
}
Texture copiedTexture = AssetDatabase.LoadAssetAtPath<Texture>(newTexPath);
if (copiedTexture != null)
{
localTexMapping[originalTexture] = copiedTexture;
texMapping[originalTexture] = copiedTexture;
}
}
// 准备材质路径
string matName = $"{prefix}{sourceMat.name}";
string newMatPath = $"{targetDir}/{matName}.mat";
// 重要: 无论如何都创建新材质,而不是复制,以确保所有引用都被正确更新
Material copiedMat = new Material(sourceMat.shader);
// 设置所有非贴图属性
CopyAllMaterialProperties(sourceMat, copiedMat);
// 删除可能存在的旧材质
if (AssetDatabase.LoadAssetAtPath<Material>(newMatPath) != null)
{
AssetDatabase.DeleteAsset(newMatPath);
}
// 创建新材质资产
AssetDatabase.CreateAsset(copiedMat, newMatPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
// 重新加载刚保存的材质,确保获取正确的引用
copiedMat = AssetDatabase.LoadAssetAtPath<Material>(newMatPath);
if (copiedMat == null)
{
Debug.LogError($"无法加载新创建的材质: {newMatPath}");
return;
}
// 更新所有贴图引用 - 必须在创建资产后进行,确保引用正确保存
bool anyTexChanged = false;
foreach (var texProp in textureProperties)
{
string propName = texProp.Key;
Texture originalTexture = texProp.Value;
if (originalTexture != null && localTexMapping.ContainsKey(originalTexture) &&
copiedMat.HasProperty(propName))
{
Texture newTexture = localTexMapping[originalTexture];
// 特殊处理_MainTex确保它被正确更新
if (propName == "_MainTex")
{
copiedMat.mainTexture = newTexture;
Debug.Log($"显式设置 {copiedMat.name} 的 mainTexture");
}
copiedMat.SetTexture(propName, newTexture);
// 设置缩放和偏移
if (textureScales.ContainsKey(propName) && textureOffsets.ContainsKey(propName))
{
copiedMat.SetTextureScale(propName, textureScales[propName]);
copiedMat.SetTextureOffset(propName, textureOffsets[propName]);
}
Debug.Log($"已更新材质 {copiedMat.name} 的贴图属性 {propName}");
anyTexChanged = true;
}
}
// 如果有任何贴图被更改,确保再次保存材质
if (anyTexChanged)
{
EditorUtility.SetDirty(copiedMat);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
// 再次检查是否所有贴图属性都已正确设置
bool allTexturePropsSet = true;
foreach (var texProp in textureProperties)
{
if (localTexMapping.ContainsKey(texProp.Value) &&
copiedMat.HasProperty(texProp.Key))
{
Texture expectedTexture = localTexMapping[texProp.Value];
Texture actualTexture = copiedMat.GetTexture(texProp.Key);
if (actualTexture != expectedTexture)
{
allTexturePropsSet = false;
Debug.LogError($"材质 {copiedMat.name} 的贴图属性 {texProp.Key} 未能正确设置");
}
}
}
if (allTexturePropsSet)
{
Debug.Log($"材质 {copiedMat.name} 的所有贴图属性已正确设置");
}
else
{
// 强制通过序列化方式修复引用
FixMaterialTextureReferences(copiedMat, localTexMapping);
}
}
// 添加到映射中
matMapping[sourceMat] = copiedMat;
Debug.Log($"已处理材质: {sourceMat.name} -> {newMatPath}");
}
/// <summary>
/// 使用序列化方式强制修复材质中的贴图引用
/// </summary>
public static void FixMaterialTextureReferences(Material material, Dictionary<Texture, Texture> texMapping)
{
string assetPath = AssetDatabase.GetAssetPath(material);
if (string.IsNullOrEmpty(assetPath)) return;
try
{
// 读取材质文件
string materialText = File.ReadAllText(assetPath);
bool changed = false;
// 对于每一个贴图映射查找并替换GUID
foreach (var mapping in texMapping)
{
Texture originalTexture = mapping.Key;
Texture newTexture = mapping.Value;
string originalGuid = string.Empty;
string newGuid = string.Empty;
// 获取原始贴图和新贴图的GUID
string originalPath = AssetDatabase.GetAssetPath(originalTexture);
string newPath = AssetDatabase.GetAssetPath(newTexture);
if (!string.IsNullOrEmpty(originalPath))
{
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(originalTexture, out originalGuid, out long _);
}
if (!string.IsNullOrEmpty(newPath))
{
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(newTexture, out newGuid, out long _);
}
// 如果找到了GUID进行替换
if (!string.IsNullOrEmpty(originalGuid) && !string.IsNullOrEmpty(newGuid))
{
Debug.Log($"在材质 {material.name} 中替换GUID: {originalGuid} -> {newGuid}");
string pattern = $"guid: {originalGuid}";
string replacement = $"guid: {newGuid}";
if (materialText.Contains(pattern))
{
materialText = materialText.Replace(pattern, replacement);
changed = true;
}
}
}
// 如果有修改,写回文件
if (changed)
{
Debug.Log($"使用文本编辑方式修复材质 {material.name} 的贴图引用");
File.WriteAllText(assetPath, materialText);
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
}
catch (System.Exception ex)
{
Debug.LogError($"修复材质贴图引用时出错: {ex.Message}");
}
}
/// <summary>
/// 复制材质的所有属性
/// </summary>
public static void CopyAllMaterialProperties(Material source, Material destination)
{
// 复制浮点数、颜色和矢量属性
Shader shader = source.shader;
int propertyCount = ShaderUtil.GetPropertyCount(shader);
for (int i = 0; i < propertyCount; i++)
{
string propertyName = ShaderUtil.GetPropertyName(shader, i);
ShaderUtil.ShaderPropertyType propertyType = ShaderUtil.GetPropertyType(shader, i);
switch (propertyType)
{
case ShaderUtil.ShaderPropertyType.Color:
destination.SetColor(propertyName, source.GetColor(propertyName));
break;
case ShaderUtil.ShaderPropertyType.Vector:
destination.SetVector(propertyName, source.GetVector(propertyName));
break;
case ShaderUtil.ShaderPropertyType.Float:
case ShaderUtil.ShaderPropertyType.Range:
destination.SetFloat(propertyName, source.GetFloat(propertyName));
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
// 贴图属性在外部处理
break;
}
}
// 复制关键字
foreach (string keyword in source.shaderKeywords)
{
destination.EnableKeyword(keyword);
}
// 复制渲染队列
destination.renderQueue = source.renderQueue;
}
/// <summary>
/// 更新地面对象的材质引用
/// </summary>
public static void UpdateGroundMaterialReferences(GameObject ground, Dictionary<Material, Material> matMapping)
{
Renderer renderer = ground.GetComponent<Renderer>();
if (renderer != null)
{
UpdateRendererMaterialReferences(renderer, matMapping);
}
}
/// <summary>
/// 更新渲染器的材质引用
/// </summary>
public static void UpdateRendererMaterialReferences(Renderer renderer, Dictionary<Material, Material> matMapping)
{
Material[] sharedMaterials = renderer.sharedMaterials;
bool changed = false;
for (int i = 0; i < sharedMaterials.Length; i++)
{
Material originalMat = sharedMaterials[i];
if (originalMat != null && matMapping.ContainsKey(originalMat))
{
sharedMaterials[i] = matMapping[originalMat];
changed = true;
}
}
if (changed)
{
renderer.sharedMaterials = sharedMaterials;
EditorUtility.SetDirty(renderer);
Debug.Log($"已更新 {renderer.gameObject.name} 的材质引用");
}
}
/// <summary>
/// 确保没有原始引用残留
/// </summary>
public static void EnsureNoOriginalReferences(GameObject obj)
{
// 递归处理所有子对象
foreach (Transform child in obj.transform)
{
EnsureNoOriginalReferences(child.gameObject);
}
// 设置对象为脏确保Unity序列化变更
EditorUtility.SetDirty(obj);
// 如果是预制体实例,解包它
if (PrefabUtility.IsPartOfPrefabInstance(obj))
{
2025-05-08 12:36:01 +08:00
// 只有在对象是预制体根节点时才解包
GameObject prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(obj);
if (prefabRoot == obj)
{
PrefabUtility.UnpackPrefabInstance(obj, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
}
2025-04-25 15:07:22 +08:00
}
}
/// <summary>
/// 在整个场景中查找并替换所有材质引用
/// </summary>
public static void FindAndReplaceAllMaterialReferences(Dictionary<Material, Material> matMapping)
{
// 获取场景中的所有渲染器
Renderer[] allRenderers = Object.FindObjectsOfType<Renderer>();
foreach (var renderer in allRenderers)
{
UpdateRendererMaterialReferences(renderer, matMapping);
}
}
/// <summary>
/// 替换材质的Shader为光照贴图支持的Shader
/// </summary>
public static void ReplaceShaderWithLightmapShader(List<Renderer> renderers)
{
// 追踪已处理的材质
var processedMaterials = new HashSet<Material>(); // 避免处理重复材质
// 尝试查找光照贴图Shader
var lightmapShader = Shader.Find("NLD_URP/NLD_Scene_Lightmap");
if (lightmapShader == null)
{
Debug.LogError("找不到NLD_Scene_Lightmap着色器请确保它存在于项目中");
return;
}
Debug.Log($"开始处理 {renderers.Count} 个渲染器用于Shader替换");
foreach (var renderer in renderers)
{
if (renderer == null)
continue;
// 获取渲染器的所有材质
Material[] sharedMaterials = renderer.sharedMaterials;
if (sharedMaterials == null || sharedMaterials.Length == 0)
continue;
for (int i = 0; i < sharedMaterials.Length; i++)
{
Material material = sharedMaterials[i];
if (material == null)
continue;
// 如果已经处理过这个材质,跳过
if (processedMaterials.Contains(material))
continue;
processedMaterials.Add(material);
if (material.shader != lightmapShader)
{
// 记录原始shader名称仅用于日志
string originalShaderName = material.shader != null ? material.shader.name : "未知";
// 保存原始主贴图及其Scale/Offset
Texture mainTexture = null;
Vector2 texScale = Vector2.one;
Vector2 texOffset = Vector2.zero;
Color baseColor = Color.white;
// 检查和保存原始贴图信息
if (material.HasProperty("_MainTex"))
{
mainTexture = material.GetTexture("_MainTex");
texScale = material.GetTextureScale("_MainTex");
texOffset = material.GetTextureOffset("_MainTex");
}
else if (material.HasProperty("_BaseMap"))
{
mainTexture = material.GetTexture("_BaseMap");
texScale = material.GetTextureScale("_BaseMap");
texOffset = material.GetTextureOffset("_BaseMap");
}
else if (material.HasProperty("_AlbedoMap"))
{
mainTexture = material.GetTexture("_AlbedoMap");
texScale = material.GetTextureScale("_AlbedoMap");
texOffset = material.GetTextureOffset("_AlbedoMap");
}
else if (material.HasProperty("_DiffuseMap"))
{
mainTexture = material.GetTexture("_DiffuseMap");
texScale = material.GetTextureScale("_DiffuseMap");
texOffset = material.GetTextureOffset("_DiffuseMap");
}
// 保存原始颜色
if (material.HasProperty("_Color"))
{
baseColor = material.GetColor("_Color");
}
else if (material.HasProperty("_BaseColor"))
{
baseColor = material.GetColor("_BaseColor");
}
// 修改shader
material.shader = lightmapShader;
// 设置贴图
if (mainTexture != null)
{
if (material.HasProperty("_BaseMap"))
{
material.SetTexture("_BaseMap", mainTexture);
material.SetTextureScale("_BaseMap", texScale);
material.SetTextureOffset("_BaseMap", texOffset);
}
if (material.HasProperty("_MainTex"))
{
material.SetTexture("_MainTex", mainTexture);
material.SetTextureScale("_MainTex", texScale);
material.SetTextureOffset("_MainTex", texOffset);
}
}
// 设置颜色
if (material.HasProperty("_BaseColor"))
{
material.SetColor("_BaseColor", baseColor);
}
if (material.HasProperty("_Color"))
{
material.SetColor("_Color", baseColor);
}
material.SetFloat("_Outline", 0.001f); //
Debug.Log($"已替换材质 {material.name} 的Shader: {originalShaderName} -> NLD_Scene_Lightmap");
}
}
}
}
}