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

511 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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))
{
// 只有在对象是预制体根节点时才解包
GameObject prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(obj);
if (prefabRoot == obj)
{
PrefabUtility.UnpackPrefabInstance(obj, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
}
}
}
/// <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");
}
}
}
}
}