using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEditor; /// /// 材质工具类,处理材质和贴图的复制、替换等操作 /// public static class MaterialUtils { /// /// 处理单个材质及其关联的贴图 /// public static void ProcessMaterialAndTexture(Material sourceMat, string targetDir, string prefix, Dictionary matMapping, Dictionary texMapping) { if (sourceMat == null || matMapping.ContainsKey(sourceMat)) return; // 收集原始材质中的所有贴图引用 Dictionary textureProperties = new Dictionary(); Dictionary textureScales = new Dictionary(); Dictionary textureOffsets = new Dictionary(); // 常见贴图属性名称 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 localTexMapping = new Dictionary(); 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(newTexPath)) { AssetDatabase.CopyAsset(originalTexPath, newTexPath); Debug.Log($"已复制贴图: {originalTexPath} -> {newTexPath}"); } Texture copiedTexture = AssetDatabase.LoadAssetAtPath(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(newMatPath) != null) { AssetDatabase.DeleteAsset(newMatPath); } // 创建新材质资产 AssetDatabase.CreateAsset(copiedMat, newMatPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // 重新加载刚保存的材质,确保获取正确的引用 copiedMat = AssetDatabase.LoadAssetAtPath(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}"); } /// /// 使用序列化方式强制修复材质中的贴图引用 /// public static void FixMaterialTextureReferences(Material material, Dictionary 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}"); } } /// /// 复制材质的所有属性 /// 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; } /// /// 更新地面对象的材质引用 /// public static void UpdateGroundMaterialReferences(GameObject ground, Dictionary matMapping) { Renderer renderer = ground.GetComponent(); if (renderer != null) { UpdateRendererMaterialReferences(renderer, matMapping); } } /// /// 更新渲染器的材质引用 /// public static void UpdateRendererMaterialReferences(Renderer renderer, Dictionary 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} 的材质引用"); } } /// /// 确保没有原始引用残留 /// 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); } } } /// /// 在整个场景中查找并替换所有材质引用 /// public static void FindAndReplaceAllMaterialReferences(Dictionary matMapping) { // 获取场景中的所有渲染器 Renderer[] allRenderers = Object.FindObjectsOfType(); foreach (var renderer in allRenderers) { UpdateRendererMaterialReferences(renderer, matMapping); } } /// /// 替换材质的Shader为光照贴图支持的Shader /// public static void ReplaceShaderWithLightmapShader(List renderers) { // 追踪已处理的材质 var processedMaterials = new HashSet(); // 避免处理重复材质 // 尝试查找光照贴图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"); } } } } }