511 lines
19 KiB
C#
511 lines
19 KiB
C#
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");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} |