diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/PostProcessor.cs b/ProjectNLD/Assets/Editor/Scene/PostProcesser/PostProcessor.cs index ceefa5191b4..39260338e55 100644 --- a/ProjectNLD/Assets/Editor/Scene/PostProcesser/PostProcessor.cs +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/PostProcessor.cs @@ -4,7 +4,6 @@ using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; -using UnityEngine.EventSystems; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using System.IO; @@ -262,7 +261,7 @@ public abstract class PostProcessor { if (!sceneRoots.Contains(obj.name)) { - DebugUtil.LogWarning("{1} 场景有其他节点:{0},请检查节点规范", obj.name, currentScene.name); + Debug.LogWarning($"{currentScene.name} 场景有其他节点:{obj.name},请检查节点规范"); } } @@ -285,7 +284,7 @@ public abstract class PostProcessor var art = CombinedObjectsRoot; // 收集渲染器 - var renderers = CollectRenderers(); + var renderers = MeshCombineUtils.CollectRenderers(PostProcessInstructor.rootsToCombine); Debug.Log($"合批前收集到 {renderers.Count} 个渲染器"); @@ -299,7 +298,7 @@ public abstract class PostProcessor { // 按照距离对渲染器进行分组 Dictionary> rendererGroups = - GroupRenderersByCircularDistance(renderers, PostProcessInstructor.batchGroupDistance); + MeshCombineUtils.GroupRenderersByCircularDistance(renderers, PostProcessInstructor.batchGroupDistance); SceneCombineUtil.Instance.CombineGameObjectsByGroups(rendererGroups, combinedRoot.transform, CombinedDir, (int)PostProcessInstructor.postProcessTextureUnitSize, @@ -347,595 +346,21 @@ public abstract class PostProcessor } } - /// - /// 根据距离对渲染器进行分组 - /// - private Dictionary> GroupRenderersByCircularDistance(List renderers, - float maxDistance) - { - var groups = new Dictionary>(); - var groupIndex = 0; - - // 已分组的渲染器 - var processedRenderers = new HashSet(); - - // 对所有渲染器进行处理 - while (processedRenderers.Count < renderers.Count) - { - // 找到第一个未处理的渲染器作为新组的起点 - Renderer startRenderer = null; - foreach (var renderer in renderers) - { - if (!processedRenderers.Contains(renderer)) - { - startRenderer = renderer; - break; - } - } - - if (startRenderer == null) - break; - - // 创建新组 - var currentGroup = new List(); - currentGroup.Add(startRenderer); - processedRenderers.Add(startRenderer); - - bool addedNew = true; - while (addedNew) - { - addedNew = false; - - // 计算当前组的中心点 - var groupCenter = CalculateGroupCenter(currentGroup); - - // 查找距离中心点在阈值内的未处理渲染器 - foreach (var renderer in renderers) - { - if (processedRenderers.Contains(renderer)) - continue; - - var distance = Vector3.Distance(groupCenter, renderer.transform.position); - if (!(distance <= maxDistance)) continue; - currentGroup.Add(renderer); - processedRenderers.Add(renderer); - addedNew = true; - } - } - - groups.Add($"Group_{groupIndex}", currentGroup); - groupIndex++; - } - - Debug.Log($"基于圆形区域分组:将{renderers.Count}个渲染器分成{groups.Count}个组,半径为{maxDistance}"); - return groups; - } - - /// - /// 计算渲染组的中心点 - /// - private Vector3 CalculateGroupCenter(List group) - { - if (group.Count == 0) - return Vector3.zero; - - var sum = Vector3.zero; - foreach (var renderer in group) - { - sum += renderer.transform.position; - } - - return sum / group.Count; - } - - /// - /// 收集渲染器 - /// - /// - private List CollectRenderers() - { - List allRenderers = new(); - foreach (var root in PostProcessInstructor.rootsToCombine) - { - if (!root) - { - continue; - } - - var render = root.GetComponentsInChildren(); - if (render is not { Length: > 0 }) - { - continue; - } - - allRenderers.AddRange(render); - } - - return allRenderers; - } - - #region 地面切图 + #region 地面处理 /// /// 修改地面贴图及材质的路径 用于打包 /// private void ChangeMapTextureAndMatPath() { - // 获取地面对象 var ground = PostProcessInstructor.ground; - if (ground == null) + if (_unpackedCombinedObjectsRoot != null && HasClone) { - Debug.LogError("找不到地面对象,无法修改地面贴图和材质路径"); - return; + ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject; } - // 获取地面的渲染器 - var renderer = ground.GetComponent(); - if (renderer == null || renderer.sharedMaterial == null) - { - Debug.LogError("地面对象缺少渲染器或材质"); - return; - } - - // 获取目标目录 - string targetDir = GroundSplitDir; - if (!AssetDatabase.IsValidFolder(targetDir)) - { - string parentDir = Path.GetDirectoryName(targetDir); - if (!AssetDatabase.IsValidFolder(parentDir)) - { - AssetDatabase.CreateFolder(Path.GetDirectoryName(parentDir), Path.GetFileName(parentDir)); - } - - AssetDatabase.CreateFolder(parentDir, Path.GetFileName(targetDir)); - AssetDatabase.Refresh(); - } - - // 获取前缀 - string prefix = GetScenePrefix(); - - // 第一步:复制地面引用的材质和贴图 - Dictionary matMapping = new Dictionary(); - Dictionary texMapping = new Dictionary(); - - // 处理主地面材质 - ProcessMaterialAndTexture(renderer.sharedMaterial, targetDir, prefix, matMapping, texMapping); - - // 处理groundRoot下的所有材质 - foreach (var rootName in groundRoot) - { - var rootObj = FindChildRecursive(CombinedObjectsRoot, rootName); - if (rootObj != null) - { - var renderers = rootObj.GetComponentsInChildren(true); - foreach (var r in renderers) - { - foreach (var mat in r.sharedMaterials) - { - if (mat != null) - { - ProcessMaterialAndTexture(mat, targetDir, prefix, matMapping, texMapping); - } - } - } - } - } - - // 第三步:更新场景中地面对象的材质引用 - UpdateGroundMaterialReferences(ground, matMapping); - - // 检查其他地面节点 - foreach (var rootName in groundRoot) - { - var rootObj = FindChildRecursive(CombinedObjectsRoot, rootName); - if (rootObj != null) - { - var renderers = rootObj.GetComponentsInChildren(true); - foreach (var r in renderers) - { - UpdateRendererMaterialReferences(r, matMapping); - } - } - } - - // 解除预制体对原始贴图的引用 - if (_unpackedCombinedObjectsRoot != null) - { - // 如果Art节点是从预制体解包的,确保彻底断开所有引用 - EnsureNoOriginalReferences(_unpackedCombinedObjectsRoot.gameObject); - } - - // 在整个场景中查找任何可能仍然引用原始材质或贴图的渲染器 - FindAndReplaceAllMaterialReferences(matMapping); - - // 脏标记整个场景,确保所有改动都被保存 - EditorSceneManager.MarkSceneDirty(PostProcessInstructor.gameObject.scene); - - // 保存所有更改 - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - - Debug.Log("地面贴图和材质路径修改完成"); - } - - /// - /// 处理单个材质及其关联的贴图 - /// - private 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}"); - } - - /// - /// 使用序列化方式强制修复材质中的贴图引用 - /// - private 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}"); - } - } - - /// - /// 复制材质的所有属性 - /// - private 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; - } - - /// - /// 更新地面对象的材质引用 - /// - private void UpdateGroundMaterialReferences(GameObject ground, Dictionary matMapping) - { - Renderer renderer = ground.GetComponent(); - if (renderer != null) - { - UpdateRendererMaterialReferences(renderer, matMapping); - } - } - - /// - /// 更新渲染器的材质引用 - /// - private 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} 的材质引用"); - } - } - - /// - /// 确保没有原始引用残留 - /// - private void EnsureNoOriginalReferences(GameObject obj) - { - // 递归处理所有子对象 - foreach (Transform child in obj.transform) - { - EnsureNoOriginalReferences(child.gameObject); - } - - // 设置对象为脏,确保Unity序列化变更 - EditorUtility.SetDirty(obj); - - // 如果是预制体实例,解包它 - if (PrefabUtility.IsPartOfPrefabInstance(obj)) - { - PrefabUtility.UnpackPrefabInstance(obj, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); - } - } - - /// - /// 在整个场景中查找并替换所有材质引用 - /// - private void FindAndReplaceAllMaterialReferences(Dictionary matMapping) - { - // 获取场景中的所有渲染器 - Renderer[] allRenderers = Object.FindObjectsOfType(); - - foreach (var renderer in allRenderers) - { - UpdateRendererMaterialReferences(renderer, matMapping); - } - - // 处理场景序列化数据中可能的引用 - Scene scene = PostProcessInstructor.gameObject.scene; - List rootObjects = new List(); - scene.GetRootGameObjects(rootObjects); - - foreach (var root in rootObjects) - { - // 标记所有对象为脏以确保序列化 - EditorUtility.SetDirty(root); - } + GroundUtils.ChangeMapTextureAndMatPath(ground, GroundSplitDir, GetScenePrefix(), + groundRoot, CombinedObjectsRoot, _unpackedCombinedObjectsRoot); } /// @@ -946,23 +371,16 @@ public abstract class PostProcessor var ground = PostProcessInstructor.ground; if (_unpackedCombinedObjectsRoot != null && HasClone) { - ground = _unpackedCombinedObjectsRoot.Find("Ground").gameObject; + ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject; } if (ground != null) { - Object.DestroyImmediate(ground.GetComponent()); - var boxCollider = ground.gameObject.AddComponent(); - boxCollider.size = new Vector3(1, 1, 1); - boxCollider.center = new Vector3(0, 0, 0.5f); - - EditorSceneManager.MarkSceneDirty(ground.gameObject.scene); - EditorSceneManager.SaveOpenScenes(); - AssetDatabase.Refresh(); + SceneUtils.AddBoxColliderToGround(ground); } else { - DebugUtil.LogError($"场景{ground.scene.name}地面节点不存在,无法添加BoxCollider"); + Debug.LogError($"场景地面节点不存在,无法添加BoxCollider"); } } @@ -974,105 +392,8 @@ public abstract class PostProcessor var ground = PostProcessInstructor.ground; if (ground == null) return; - var renderer = ground.GetComponent(); - if (renderer == null) - return; - var mat = renderer.sharedMaterial; - if (mat == null) - return; - var texture = mat.mainTexture as Texture2D; - if (texture == null) - return; - var prefix = GetScenePrefix(); - var groundParent = Object.Instantiate(ground, ground.transform.parent, true); - groundParent.name = "Ground"; - - // 计算原始地面的大小 - var originalScale = ground.transform.localScale; - var originalWidth = originalScale.x; - var originalHeight = originalScale.y; - - var originPos = -new Vector3(originalWidth / 2f, originalHeight / 2f, 0); - groundParent.transform.localScale = Vector3.one; - - // 确保目录存在 - if (!AssetDatabase.IsValidFolder(GroundSplitDir)) - { - var parentDir = Path.GetDirectoryName(GroundSplitDir); - if (!AssetDatabase.IsValidFolder(parentDir)) - { - AssetDatabase.CreateFolder(Path.GetDirectoryName(parentDir), Path.GetFileName(parentDir)); - } - - AssetDatabase.CreateFolder(parentDir, Path.GetFileName(GroundSplitDir)); - } - - var textures = - SplitTextureUtil.SplitTexture(texture, GROUND_SPLIT_WIDTH, GROUND_SPLIT_HEIGHT, GroundSplitDir, prefix); - - // 计算每个分割块的大小(基于原始地面大小) - var rowCount = textures.GetLength(0); - var columnCount = textures.GetLength(1); - var blockWidth = originalWidth / columnCount; - var blockHeight = originalHeight / rowCount; - - for (int row = 0; row < rowCount; row++) - { - for (int column = 0; column < columnCount; column++) - { - var splitTexture = textures[row, column]; - if (splitTexture == null) - { - Debug.LogError($"Failed to load split texture at row {row}, column {column}"); - continue; - } - - var splitGround = Object.Instantiate(ground, groundParent.transform, true); - Object.DestroyImmediate(splitGround.GetComponent()); - var splitName = $"{prefix}Ground_{row}_{column}"; - splitGround.name = splitName; - - // 设置固定的quad大小,不随贴图尺寸变化 - splitGround.transform.localScale = new Vector3(blockWidth, blockHeight, 1); - - // 创建并保存材质 - var splitMat = Object.Instantiate(mat); - splitMat.mainTexture = splitTexture; - var matPath = $"{GroundSplitDir}/{splitName}.mat"; - AssetDatabase.CreateAsset(splitMat, matPath); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - - // 重新加载保存后的材质 - var savedMat = AssetDatabase.LoadAssetAtPath(matPath); - if (savedMat == null) - { - Debug.LogError($"Failed to load saved material at {matPath}"); - continue; - } - - var meshRenderer = splitGround.GetComponent(); - if (meshRenderer != null) - { - meshRenderer.sharedMaterial = savedMat; - meshRenderer.shadowCastingMode = ShadowCastingMode.Off; - } - - // 计算位置,使用固定的block大小 - splitGround.transform.localPosition = - originPos + new Vector3(column * blockWidth + blockWidth / 2f, row * blockHeight + blockHeight / 2f, - 0); - } - } - - Object.DestroyImmediate(ground); - Object.DestroyImmediate(groundParent.GetComponent()); - Object.DestroyImmediate(groundParent.GetComponent()); - var boxCollider = groundParent.gameObject.AddComponent(); - boxCollider.size = new Vector3(200, 200, 1); - boxCollider.center = new Vector3(0, 0, 0.5f); - Object.DestroyImmediate(groundParent.GetComponent()); + GroundUtils.SplitGroundTexture(ground, GroundSplitDir, GetScenePrefix(), GROUND_SPLIT_WIDTH, GROUND_SPLIT_HEIGHT); } #endregion @@ -1082,12 +403,7 @@ public abstract class PostProcessor /// private void SetFogPosition() { - var fog = PostProcessInstructor.fog; - if (fog == null) - return; - var transform = fog.transform; - var position = transform.position; - transform.position = new Vector3(position.x, position.y + 0.01f, position.z); + SceneUtils.SetFogPosition(PostProcessInstructor.fog); } /// @@ -1095,19 +411,7 @@ public abstract class PostProcessor /// private void RemoveRedundant() { - var postScene = PostProcessInstructor.gameObject.scene; - - foreach (var rootObj in postScene.GetRootGameObjects()) - { - EventSystem[] eventSystems = rootObj.GetComponentsInChildren(); - if (null == eventSystems) - continue; - - for (var i = 0; i < eventSystems.Length; ++i) - { - Object.DestroyImmediate(eventSystems[i].gameObject); - } - } + SceneUtils.RemoveRedundantEventSystems(PostProcessInstructor.gameObject.scene); } private void DestroyUselessObject() @@ -1145,7 +449,6 @@ public abstract class PostProcessor private void SetCameraParam() { var postScene = PostProcessInstructor.gameObject.scene; - var scene = _processInstructor.gameObject.scene; foreach (var rootObj in scene.GetRootGameObjects()) @@ -1194,21 +497,21 @@ public abstract class PostProcessor Debug.Log("开始灯光烘焙过程..."); // 1. 从CombinedObjectsRoot下查找灯光 改为烘焙灯光 - PrepareLight(); + LightmapUtils.PrepareLight(CombinedObjectsRoot, realTimeLights, bakedLights); // 2. 处理参与烘焙的节点 - PreProcessBake(bakeReplaceShaderRoot, true, true); - PreProcessBake(groundRoot, false); + LightmapUtils.PreProcessBake(bakeReplaceShaderRoot, CombinedObjectsRoot, true, true, allBakeRenderers, needReplaceRenderers); + LightmapUtils.PreProcessBake(groundRoot, CombinedObjectsRoot, false, false, allBakeRenderers, needReplaceRenderers); ProcessGround(3000); Debug.Log($"总共收集到 {needReplaceRenderers.Count} 个渲染器需要替换shader"); Debug.Log($"总共收集到 {allBakeRenderers.Count} 个渲染器参与烘焙"); // 3.替换Shader为支持光照贴图的Shader - ReplaceShaderWithLightmapShader(needReplaceRenderers); + MaterialUtils.ReplaceShaderWithLightmapShader(needReplaceRenderers); // 4. 开始烘焙 - Bake(); + LightmapUtils.Bake(SceneManager.GetActiveScene()); // 5. 关闭所有烘焙物体的阴影投射 foreach (var renderer in allBakeRenderers) @@ -1218,475 +521,36 @@ public abstract class PostProcessor } // 6. 打开实时灯光 - RevertLight(); + LightmapUtils.RevertLight(realTimeLights, bakedLights); // 7.还原地面RenderQueue ProcessGround(2000); - // 获取当前场景的烘焙设置 - var activeScene = SceneManager.GetActiveScene(); - // 8. 复制光照烘焙文件到目标路径 - CopyLightmapFiles(activeScene); + LightmapUtils.CopyLightmapFiles(SceneManager.GetActiveScene(), LightBakeDir); // 9. 保存场景 - EditorSceneManager.MarkSceneDirty(activeScene); + EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); EditorSceneManager.SaveOpenScenes(); Debug.Log("灯光烘焙处理完成"); } - /// - /// 移动光照烘焙相关文件到指定目录 - /// - private void CopyLightmapFiles(Scene scene) - { - string sceneName = scene.name; - - Debug.Log("开始移动光照烘焙文件..."); - - try - { - // 确保目标目录存在 - string lightmapDirAssetPath = GetAssetPath(LightBakeDir); - if (!AssetDatabase.IsValidFolder(lightmapDirAssetPath)) - { - // 创建父目录结构 - string[] folders = lightmapDirAssetPath.Split('/'); - string currentPath = folders[0]; // Assets - - for (int i = 1; i < folders.Length; i++) - { - string folderName = folders[i]; - string parentPath = currentPath; - currentPath = $"{currentPath}/{folderName}"; - - if (!AssetDatabase.IsValidFolder(currentPath)) - { - AssetDatabase.CreateFolder(parentPath, folderName); - } - } - - AssetDatabase.Refresh(); - } - - // 1. 移动LightingSettings.asset文件 - string lightingSettingsPath = $"Assets/{sceneName}_LightingSettings.asset"; - if (AssetDatabase.LoadAssetAtPath(lightingSettingsPath) != null) - { - string destPath = $"{lightmapDirAssetPath}/{sceneName}_LightingSettings.asset"; - - // 如果目标已存在,先删除 - if (AssetDatabase.LoadAssetAtPath(destPath) != null) - { - AssetDatabase.DeleteAsset(destPath); - } - - AssetDatabase.MoveAsset(lightingSettingsPath, destPath); - Debug.Log($"已移动: {lightingSettingsPath} -> {destPath}"); - } - - // 2. 移动整个光照目录 - string lightmapDirName = sceneName; - string lightmapSourceDir = $"{Path.GetDirectoryName(scene.path)}/{lightmapDirName}"; - string lightmapSourceDirAssetPath = GetAssetPath(lightmapSourceDir); - - if (AssetDatabase.IsValidFolder(lightmapSourceDirAssetPath)) - { - string destLightmapDir = $"{lightmapDirAssetPath}/{lightmapDirName}"; - - // 如果目标已存在,先删除 - if (AssetDatabase.IsValidFolder(destLightmapDir)) - { - AssetDatabase.DeleteAsset(destLightmapDir); - AssetDatabase.Refresh(); - } - - // 直接移动整个目录 - string result = AssetDatabase.MoveAsset(lightmapSourceDirAssetPath, destLightmapDir); - - if (string.IsNullOrEmpty(result)) - { - Debug.Log($"已成功移动光照数据目录: {lightmapSourceDirAssetPath} -> {destLightmapDir}"); - } - else - { - Debug.LogError($"移动光照数据目录失败: {result}"); - } - } - else - { - Debug.LogWarning($"找不到光照数据目录: {lightmapSourceDirAssetPath}"); - } - } - catch (System.Exception e) - { - Debug.LogError($"移动光照文件时出错: {e.Message}"); - } - - AssetDatabase.Refresh(); - Debug.Log("光照烘焙文件移动完成"); - } - - // 获取Unity资源路径的简化方法 - private string GetAssetPath(string path) - { - // 已经是Assets路径格式 - if (path.StartsWith("Assets")) - return path; - - // 绝对路径转相对Assets路径 - string fullPath = Path.GetFullPath(path); - string assetsFullPath = Path.GetFullPath(Application.dataPath); - - if (fullPath.StartsWith(assetsFullPath)) - return "Assets" + fullPath.Substring(assetsFullPath.Length).Replace('\\', '/'); - - return path; - } - - // 替换材质的Shader为光照贴图支持的Shader - private 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"); - } - } - } - } - - // 递归查找子节点的辅助方法 - private Transform FindChildRecursive(Transform parent, string childName) - { - // 首先检查直接子节点 - Transform child = parent.Find(childName); - if (child != null) - return child; - - // 如果直接子节点中没找到,递归查找每个子节点 - for (int i = 0; i < parent.childCount; i++) - { - child = FindChildRecursive(parent.GetChild(i), childName); - if (child != null) - return child; - } - - return null; - } - - private void SetStaticFlagsRecursive(GameObject obj, StaticEditorFlags flags) - { - // 设置当前物体的静态标志 - GameObjectUtility.SetStaticEditorFlags(obj, flags); - - // 递归设置所有子物体 - for (int i = 0; i < obj.transform.childCount; i++) - { - Transform child = obj.transform.GetChild(i); - SetStaticFlagsRecursive(child.gameObject, flags); - } - } - - private void PreProcessBake(List rootNames, bool castShadow, bool needReplaceShader = false) - { - foreach (var rootName in rootNames) - { - var rootObj = FindChildRecursive(CombinedObjectsRoot, rootName); - if (rootObj != null) - { - SetStaticFlagsRecursive(rootObj.gameObject, - StaticEditorFlags.ContributeGI); - var renderers = rootObj.GetComponentsInChildren(true); - if (renderers != null && renderers.Length > 0) - { - Debug.Log($"从{rootName}收集到 {renderers.Length} 个渲染器用于烘焙"); - foreach (var renderer in renderers) - { - DebugUtil.Log($"处理从{rootName}收集到物体{renderer.gameObject.name}的渲染器"); - if (renderer.receiveShadows) - { - renderer.shadowCastingMode = castShadow ? ShadowCastingMode.On : ShadowCastingMode.Off; - allBakeRenderers.Add(renderer); - if (needReplaceShader) - needReplaceRenderers.Add(renderer); - } - } - } - else - { - Debug.Log($"从{rootName}收集不到渲染器"); - } - } - } - } - private void ProcessGround(int renderQueue) { if (PostProcessInstructor.ground != null) { - GameObjectUtility.SetStaticEditorFlags(PostProcessInstructor.ground, - StaticEditorFlags.ContributeGI); - var renderer = PostProcessInstructor.ground.GetComponent(); - if (renderer != null) - { - renderer.shadowCastingMode = ShadowCastingMode.Off; - var materials = renderer.sharedMaterials; - foreach (var material in materials) - { - if (material != null) - { - renderer.shadowCastingMode = ShadowCastingMode.Off; - material.renderQueue = renderQueue; - Debug.Log($"将PostProcessInstructor.ground的材质 {material.name} 的RenderQueue改为{renderQueue}"); - } - } - } - - // 刷新资源以确保RenderQueue修改已保存 - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); + SceneUtils.SetGroundRenderQueue(PostProcessInstructor.ground, renderQueue); } } - private void PrepareLight() - { - var lights = CombinedObjectsRoot.GetComponentsInChildren(); - foreach (var light in lights) - { - light.transform.position = new Vector3(0, 0, 0); - // 复制灯光对象 - var bakeLightObj = Object.Instantiate(light.gameObject, light.transform.parent); - var bakeLight = bakeLightObj.GetComponent(); - - // 设置为烘焙模式 - bakeLightObj.name = light.name + "_Bake"; - bakeLight.lightmapBakeType = LightmapBakeType.Baked; - - // 取消实时灯光的static - GameObjectUtility.SetStaticEditorFlags(light.gameObject, 0); - // 设置烘焙灯光的static - GameObjectUtility.SetStaticEditorFlags(bakeLightObj, StaticEditorFlags.ContributeGI); - - // 添加到列表 - bakedLights.Add(bakeLight); - realTimeLights.Add(light); - - DebugUtil.Log("处理一次灯光"); - } - - // 关闭实时灯光 - foreach (var realTimeLight in realTimeLights) - { - if (realTimeLight != null) - realTimeLight.gameObject.SetActive(false); - } - - // 开启烘焙灯光 - foreach (var bakeLight in bakedLights) - { - if (bakeLight != null) - bakeLight.gameObject.SetActive(true); - } - } - - private void RevertLight() - { - // 关闭实时灯光 - foreach (var realTimeLight in realTimeLights) - { - if (realTimeLight != null) - realTimeLight.gameObject.SetActive(true); - } - - DebugUtil.Log("摧毁{0}烘焙灯光", bakedLights.Count); - // 开启烘焙灯光 - foreach (var bakeLight in bakedLights) - { - if (bakeLight != null) - Object.DestroyImmediate(bakeLight.gameObject); - } - } - - private void Bake() - { - // 设置烘焙参数并开始烘焙 - var lightingSettings = new LightingSettings(); - - // 获取当前场景的烘焙设置 - var activeScene = SceneManager.GetActiveScene(); - DebugUtil.Log($"烘焙时 场景是{activeScene.name}"); - if (Lightmapping.TryGetLightingSettings(out var existingSettings)) - { - lightingSettings = existingSettings; - } - else - { - lightingSettings.name = $"{activeScene.name}_LightingSettings"; - AssetDatabase.CreateAsset(lightingSettings, $"Assets/{lightingSettings.name}.asset"); - Lightmapping.lightingSettings = lightingSettings; - } - - // 参数 - lightingSettings.mixedBakeMode = MixedLightingMode.Subtractive; - lightingSettings.lightmapper = LightingSettings.Lightmapper.ProgressiveGPU; - lightingSettings.directSampleCount = 1; - lightingSettings.indirectSampleCount = 1; - lightingSettings.environmentSampleCount = 1; - lightingSettings.lightProbeSampleCountMultiplier = 8; - lightingSettings.maxBounces = 1; - lightingSettings.filteringMode = LightingSettings.FilterMode.Auto; - lightingSettings.lightmapResolution = 40; - lightingSettings.lightmapPadding = 2; - lightingSettings.lightmapMaxSize = 1024; - lightingSettings.lightmapCompression = LightmapCompression.HighQuality; - lightingSettings.indirectScale = 0; - - Lightmapping.lightingSettings = lightingSettings; - - // 强制场景保存以确保设置生效 - EditorSceneManager.MarkSceneDirty(activeScene); - EditorSceneManager.SaveOpenScenes(); - - Debug.Log("开始烘焙灯光..."); - - Lightmapping.Bake(); - - Debug.Log("灯光烘焙完成"); - } - private void UnpackPostSceneArt() { - if (CombinedObjectsRoot != null && PrefabUtility.IsPartOfPrefabInstance(CombinedObjectsRoot)) + if (CombinedObjectsRoot != null && PrefabUtility.IsPartOfPrefabInstance(CombinedObjectsRoot.gameObject)) { - Debug.Log($"解包场景{CombinedObjectsRoot.gameObject.scene.name} 的预制体: {CombinedObjectsRoot.name}"); - // 保存对象的引用 _unpackedCombinedObjectsRoot = CombinedObjectsRoot; - - // 正确调用,不获取返回值 - PrefabUtility.UnpackPrefabInstance(CombinedObjectsRoot.gameObject, - PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); - - EditorSceneManager.MarkSceneDirty(CombinedObjectsRoot.gameObject.scene); - EditorSceneManager.SaveOpenScenes(); - - // 强制刷新 - AssetDatabase.Refresh(); + SceneUtils.UnpackPrefab(CombinedObjectsRoot.gameObject); } } diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/GroundUtils.cs b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/GroundUtils.cs new file mode 100644 index 00000000000..077ea0fa7aa --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/GroundUtils.cs @@ -0,0 +1,215 @@ +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine.Rendering; +using Object = UnityEngine.Object; + +/// +/// 地面处理工具类 +/// +public static class GroundUtils +{ + /// + /// 修改地面贴图及材质的路径 用于打包 + /// + public static void ChangeMapTextureAndMatPath(GameObject ground, string targetDir, string prefix, + List groundRootNames, Transform rootTransform, Transform unpackedRoot) + { + if (ground == null) + { + Debug.LogError("找不到地面对象,无法修改地面贴图和材质路径"); + return; + } + + // 获取地面的渲染器 + var renderer = ground.GetComponent(); + if (renderer == null || renderer.sharedMaterial == null) + { + Debug.LogError("地面对象缺少渲染器或材质"); + return; + } + + // 确保目标目录存在 + EnsureDirectoryExists(targetDir); + + // 处理材质和贴图 + Dictionary matMapping = new Dictionary(); + Dictionary texMapping = new Dictionary(); + + // 处理主地面材质 + MaterialUtils.ProcessMaterialAndTexture(renderer.sharedMaterial, targetDir, prefix, matMapping, texMapping); + + // 处理groundRoot下的所有材质 + foreach (var rootName in groundRootNames) + { + var rootObj = SceneUtils.FindChildRecursive(rootTransform, rootName); + if (rootObj != null) + { + var renderers = rootObj.GetComponentsInChildren(true); + foreach (var r in renderers) + { + foreach (var mat in r.sharedMaterials) + { + if (mat != null) + { + MaterialUtils.ProcessMaterialAndTexture(mat, targetDir, prefix, matMapping, texMapping); + } + } + } + } + } + + // 更新场景中地面对象的材质引用 + MaterialUtils.UpdateGroundMaterialReferences(ground, matMapping); + + // 检查其他地面节点 + foreach (var rootName in groundRootNames) + { + var rootObj = SceneUtils.FindChildRecursive(rootTransform, rootName); + if (rootObj != null) + { + var renderers = rootObj.GetComponentsInChildren(true); + foreach (var r in renderers) + { + MaterialUtils.UpdateRendererMaterialReferences(r, matMapping); + } + } + } + + // 解除预制体对原始贴图的引用 + if (unpackedRoot != null) + { + // 如果Art节点是从预制体解包的,确保彻底断开所有引用 + MaterialUtils.EnsureNoOriginalReferences(unpackedRoot.gameObject); + } + + // 在整个场景中查找任何可能仍然引用原始材质或贴图的渲染器 + MaterialUtils.FindAndReplaceAllMaterialReferences(matMapping); + + // 脏标记整个场景,确保所有改动都被保存 + EditorSceneManager.MarkSceneDirty(ground.scene); + + // 保存所有更改 + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + Debug.Log("地面贴图和材质路径修改完成"); + } + + /// + /// 确保目录存在 + /// + private static void EnsureDirectoryExists(string dirPath) + { + if (!AssetDatabase.IsValidFolder(dirPath)) + { + string parentDir = Path.GetDirectoryName(dirPath); + if (!AssetDatabase.IsValidFolder(parentDir)) + { + AssetDatabase.CreateFolder(Path.GetDirectoryName(parentDir), Path.GetFileName(parentDir)); + } + + AssetDatabase.CreateFolder(parentDir, Path.GetFileName(dirPath)); + AssetDatabase.Refresh(); + } + } + + /// + /// 切割地面贴图 + /// + public static void SplitGroundTexture(GameObject ground, string groundSplitDir, string prefix, int splitWidth, int splitHeight) + { + if (ground == null) + return; + var renderer = ground.GetComponent(); + if (renderer == null) + return; + var mat = renderer.sharedMaterial; + if (mat == null) + return; + var texture = mat.mainTexture as Texture2D; + if (texture == null) + return; + + var groundParent = Object.Instantiate(ground, ground.transform.parent, true); + groundParent.name = "Ground"; + + // 计算原始地面的大小 + var originalScale = ground.transform.localScale; + var originalWidth = originalScale.x; + var originalHeight = originalScale.y; + + var originPos = -new Vector3(originalWidth / 2f, originalHeight / 2f, 0); + groundParent.transform.localScale = Vector3.one; + + // 确保目录存在 + EnsureDirectoryExists(groundSplitDir); + + var textures = SplitTextureUtil.SplitTexture(texture, splitWidth, splitHeight, groundSplitDir, prefix); + + // 计算每个分割块的大小(基于原始地面大小) + var rowCount = textures.GetLength(0); + var columnCount = textures.GetLength(1); + var blockWidth = originalWidth / columnCount; + var blockHeight = originalHeight / rowCount; + + for (int row = 0; row < rowCount; row++) + { + for (int column = 0; column < columnCount; column++) + { + var splitTexture = textures[row, column]; + if (splitTexture == null) + { + Debug.LogError($"Failed to load split texture at row {row}, column {column}"); + continue; + } + + var splitGround = Object.Instantiate(ground, groundParent.transform, true); + Object.DestroyImmediate(splitGround.GetComponent()); + var splitName = $"{prefix}Ground_{row}_{column}"; + splitGround.name = splitName; + + // 设置固定的quad大小,不随贴图尺寸变化 + splitGround.transform.localScale = new Vector3(blockWidth, blockHeight, 1); + + // 创建并保存材质 + var splitMat = Object.Instantiate(mat); + splitMat.mainTexture = splitTexture; + var matPath = $"{groundSplitDir}/{splitName}.mat"; + AssetDatabase.CreateAsset(splitMat, matPath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + // 重新加载保存后的材质 + var savedMat = AssetDatabase.LoadAssetAtPath(matPath); + if (savedMat == null) + { + Debug.LogError($"Failed to load saved material at {matPath}"); + continue; + } + + var meshRenderer = splitGround.GetComponent(); + if (meshRenderer != null) + { + meshRenderer.sharedMaterial = savedMat; + meshRenderer.shadowCastingMode = ShadowCastingMode.Off; + } + + // 计算位置,使用固定的block大小 + splitGround.transform.localPosition = + originPos + new Vector3(column * blockWidth + blockWidth / 2f, row * blockHeight + blockHeight / 2f, + 0); + } + } + + Object.DestroyImmediate(ground); + Object.DestroyImmediate(groundParent.GetComponent()); + Object.DestroyImmediate(groundParent.GetComponent()); + var boxCollider = groundParent.gameObject.AddComponent(); + boxCollider.size = new Vector3(200, 200, 1); + boxCollider.center = new Vector3(0, 0, 0.5f); + Object.DestroyImmediate(groundParent.GetComponent()); + } +} \ No newline at end of file diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/GroundUtils.cs.meta b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/GroundUtils.cs.meta new file mode 100644 index 00000000000..39503a662b5 --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/GroundUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 065916795ee704f07b75513739284fc4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/LightmapUtils.cs b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/LightmapUtils.cs new file mode 100644 index 00000000000..ad0f8500bde --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/LightmapUtils.cs @@ -0,0 +1,250 @@ +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEditor; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEngine.Rendering; + +/// +/// 灯光烘焙工具类 +/// +public static class LightmapUtils +{ + /// + /// 处理参与烘焙的节点 + /// + public static void PreProcessBake(List rootNames, Transform rootTransform, bool castShadow, bool needReplaceShader, + List allBakeRenderers, List needReplaceRenderers) + { + foreach (var rootName in rootNames) + { + var rootObj = SceneUtils.FindChildRecursive(rootTransform, rootName); + if (rootObj != null) + { + SceneUtils.SetStaticFlagsRecursive(rootObj.gameObject, StaticEditorFlags.ContributeGI); + var renderers = rootObj.GetComponentsInChildren(true); + if (renderers != null && renderers.Length > 0) + { + Debug.Log($"从{rootName}收集到 {renderers.Length} 个渲染器用于烘焙"); + foreach (var renderer in renderers) + { + Debug.Log($"处理从{rootName}收集到物体{renderer.gameObject.name}的渲染器"); + if (renderer.receiveShadows) + { + renderer.shadowCastingMode = castShadow ? ShadowCastingMode.On : ShadowCastingMode.Off; + allBakeRenderers.Add(renderer); + if (needReplaceShader) + needReplaceRenderers.Add(renderer); + } + } + } + else + { + Debug.Log($"从{rootName}收集不到渲染器"); + } + } + } + } + + /// + /// 准备灯光:将实时灯光改为烘焙灯光 + /// + public static void PrepareLight(Transform root, List realTimeLights, List bakedLights) + { + var lights = root.GetComponentsInChildren(); + foreach (var light in lights) + { + light.transform.position = new Vector3(0, 0, 0); + // 复制灯光对象 + var bakeLightObj = Object.Instantiate(light.gameObject, light.transform.parent); + var bakeLight = bakeLightObj.GetComponent(); + + // 设置为烘焙模式 + bakeLightObj.name = light.name + "_Bake"; + bakeLight.lightmapBakeType = LightmapBakeType.Baked; + + // 取消实时灯光的static + GameObjectUtility.SetStaticEditorFlags(light.gameObject, 0); + // 设置烘焙灯光的static + GameObjectUtility.SetStaticEditorFlags(bakeLightObj, StaticEditorFlags.ContributeGI); + + // 添加到列表 + bakedLights.Add(bakeLight); + realTimeLights.Add(light); + } + + // 关闭实时灯光 + foreach (var realTimeLight in realTimeLights) + { + if (realTimeLight != null) + realTimeLight.gameObject.SetActive(false); + } + + // 开启烘焙灯光 + foreach (var bakeLight in bakedLights) + { + if (bakeLight != null) + bakeLight.gameObject.SetActive(true); + } + } + + /// + /// 恢复实时灯光 + /// + public static void RevertLight(List realTimeLights, List bakedLights) + { + // 开启实时灯光 + foreach (var realTimeLight in realTimeLights) + { + if (realTimeLight != null) + realTimeLight.gameObject.SetActive(true); + } + + Debug.Log($"销毁{bakedLights.Count}个烘焙灯光"); + // 销毁烘焙灯光 + foreach (var bakeLight in bakedLights) + { + if (bakeLight != null) + Object.DestroyImmediate(bakeLight.gameObject); + } + } + + /// + /// 执行烘焙 + /// + public static void Bake(Scene activeScene) + { + // 设置烘焙参数并开始烘焙 + var lightingSettings = new LightingSettings(); + + Debug.Log($"烘焙时 场景是{activeScene.name}"); + if (Lightmapping.TryGetLightingSettings(out var existingSettings)) + { + lightingSettings = existingSettings; + } + else + { + lightingSettings.name = $"{activeScene.name}_LightingSettings"; + AssetDatabase.CreateAsset(lightingSettings, $"Assets/{lightingSettings.name}.asset"); + Lightmapping.lightingSettings = lightingSettings; + } + + // 设置烘焙参数 + lightingSettings.mixedBakeMode = MixedLightingMode.Subtractive; + lightingSettings.lightmapper = LightingSettings.Lightmapper.ProgressiveGPU; + lightingSettings.directSampleCount = 1; + lightingSettings.indirectSampleCount = 1; + lightingSettings.environmentSampleCount = 1; + lightingSettings.lightProbeSampleCountMultiplier = 8; + lightingSettings.maxBounces = 1; + lightingSettings.filteringMode = LightingSettings.FilterMode.Auto; + lightingSettings.lightmapResolution = 40; + lightingSettings.lightmapPadding = 2; + lightingSettings.lightmapMaxSize = 1024; + lightingSettings.lightmapCompression = LightmapCompression.HighQuality; + lightingSettings.indirectScale = 0; + + Lightmapping.lightingSettings = lightingSettings; + + // 强制场景保存以确保设置生效 + EditorSceneManager.MarkSceneDirty(activeScene); + EditorSceneManager.SaveOpenScenes(); + + Debug.Log("开始烘焙灯光..."); + Lightmapping.Bake(); + Debug.Log("灯光烘焙完成"); + } + + /// + /// 移动光照烘焙相关文件到指定目录 + /// + public static void CopyLightmapFiles(Scene scene, string lightmapDirPath) + { + string sceneName = scene.name; + + Debug.Log("开始移动光照烘焙文件..."); + + try + { + // 确保目标目录存在 + string lightmapDirAssetPath = SceneUtils.GetAssetPath(lightmapDirPath); + if (!AssetDatabase.IsValidFolder(lightmapDirAssetPath)) + { + // 创建父目录结构 + string[] folders = lightmapDirAssetPath.Split('/'); + string currentPath = folders[0]; // Assets + + for (int i = 1; i < folders.Length; i++) + { + string folderName = folders[i]; + string parentPath = currentPath; + currentPath = $"{currentPath}/{folderName}"; + + if (!AssetDatabase.IsValidFolder(currentPath)) + { + AssetDatabase.CreateFolder(parentPath, folderName); + } + } + + AssetDatabase.Refresh(); + } + + // 1. 移动LightingSettings.asset文件 + string lightingSettingsPath = $"Assets/{sceneName}_LightingSettings.asset"; + if (AssetDatabase.LoadAssetAtPath(lightingSettingsPath) != null) + { + string destPath = $"{lightmapDirAssetPath}/{sceneName}_LightingSettings.asset"; + + // 如果目标已存在,先删除 + if (AssetDatabase.LoadAssetAtPath(destPath) != null) + { + AssetDatabase.DeleteAsset(destPath); + } + + AssetDatabase.MoveAsset(lightingSettingsPath, destPath); + Debug.Log($"已移动: {lightingSettingsPath} -> {destPath}"); + } + + // 2. 移动整个光照目录 + string lightmapDirName = sceneName; + string lightmapSourceDir = $"{Path.GetDirectoryName(scene.path)}/{lightmapDirName}"; + string lightmapSourceDirAssetPath = SceneUtils.GetAssetPath(lightmapSourceDir); + + if (AssetDatabase.IsValidFolder(lightmapSourceDirAssetPath)) + { + string destLightmapDir = $"{lightmapDirAssetPath}/{lightmapDirName}"; + + // 如果目标已存在,先删除 + if (AssetDatabase.IsValidFolder(destLightmapDir)) + { + AssetDatabase.DeleteAsset(destLightmapDir); + AssetDatabase.Refresh(); + } + + // 直接移动整个目录 + string result = AssetDatabase.MoveAsset(lightmapSourceDirAssetPath, destLightmapDir); + + if (string.IsNullOrEmpty(result)) + { + Debug.Log($"已成功移动光照数据目录: {lightmapSourceDirAssetPath} -> {destLightmapDir}"); + } + else + { + Debug.LogError($"移动光照数据目录失败: {result}"); + } + } + else + { + Debug.LogWarning($"找不到光照数据目录: {lightmapSourceDirAssetPath}"); + } + } + catch (System.Exception e) + { + Debug.LogError($"移动光照文件时出错: {e.Message}"); + } + + AssetDatabase.Refresh(); + Debug.Log("光照烘焙文件移动完成"); + } +} \ No newline at end of file diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/LightmapUtils.cs.meta b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/LightmapUtils.cs.meta new file mode 100644 index 00000000000..ed4c88002c1 --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/LightmapUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82ef00a42a40b4cffacc73f85be50b34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MaterialUtils.cs b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MaterialUtils.cs new file mode 100644 index 00000000000..5ad69716afb --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MaterialUtils.cs @@ -0,0 +1,506 @@ +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)) + { + 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"); + } + } + } + } +} \ No newline at end of file diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MaterialUtils.cs.meta b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MaterialUtils.cs.meta new file mode 100644 index 00000000000..4e54e38ddc2 --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MaterialUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9c4dd253ebf784d1e9cfa4d3d68d1181 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MeshCombineUtils.cs b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MeshCombineUtils.cs new file mode 100644 index 00000000000..c359c880006 --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MeshCombineUtils.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using UnityEngine; + +/// +/// 网格合并工具类 +/// +public static class MeshCombineUtils +{ + /// + /// 收集渲染器 + /// + public static List CollectRenderers(List roots) + { + List allRenderers = new(); + foreach (var root in roots) + { + if (!root) + { + continue; + } + + var render = root.GetComponentsInChildren(); + if (render is not { Length: > 0 }) + { + continue; + } + + allRenderers.AddRange(render); + } + + return allRenderers; + } + + /// + /// 根据距离对渲染器进行分组 + /// + public static Dictionary> GroupRenderersByCircularDistance(List renderers, + float maxDistance) + { + var groups = new Dictionary>(); + var groupIndex = 0; + + // 已分组的渲染器 + var processedRenderers = new HashSet(); + + // 对所有渲染器进行处理 + while (processedRenderers.Count < renderers.Count) + { + // 找到第一个未处理的渲染器作为新组的起点 + Renderer startRenderer = null; + foreach (var renderer in renderers) + { + if (!processedRenderers.Contains(renderer)) + { + startRenderer = renderer; + break; + } + } + + if (startRenderer == null) + break; + + // 创建新组 + var currentGroup = new List(); + currentGroup.Add(startRenderer); + processedRenderers.Add(startRenderer); + + bool addedNew = true; + while (addedNew) + { + addedNew = false; + + // 计算当前组的中心点 + var groupCenter = CalculateGroupCenter(currentGroup); + + // 查找距离中心点在阈值内的未处理渲染器 + foreach (var renderer in renderers) + { + if (processedRenderers.Contains(renderer)) + continue; + + var distance = Vector3.Distance(groupCenter, renderer.transform.position); + if (!(distance <= maxDistance)) continue; + currentGroup.Add(renderer); + processedRenderers.Add(renderer); + addedNew = true; + } + } + + groups.Add($"Group_{groupIndex}", currentGroup); + groupIndex++; + } + + Debug.Log($"基于圆形区域分组:将{renderers.Count}个渲染器分成{groups.Count}个组,半径为{maxDistance}"); + return groups; + } + + /// + /// 计算渲染组的中心点 + /// + private static Vector3 CalculateGroupCenter(List group) + { + if (group.Count == 0) + return Vector3.zero; + + var sum = Vector3.zero; + foreach (var renderer in group) + { + sum += renderer.transform.position; + } + + return sum / group.Count; + } +} \ No newline at end of file diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MeshCombineUtils.cs.meta b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MeshCombineUtils.cs.meta new file mode 100644 index 00000000000..bb794d96d34 --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/MeshCombineUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3be33ff74463a4d3c8296b795a31f868 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/SceneUtils.cs b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/SceneUtils.cs new file mode 100644 index 00000000000..afdb8e6ba7f --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/SceneUtils.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; +using UnityEngine.Rendering; + +/// +/// 场景操作工具类 +/// +public static class SceneUtils +{ + /// + /// 递归查找子节点 + /// + public static Transform FindChildRecursive(Transform parent, string childName) + { + // 首先检查直接子节点 + Transform child = parent.Find(childName); + if (child != null) + return child; + + // 如果直接子节点中没找到,递归查找每个子节点 + for (int i = 0; i < parent.childCount; i++) + { + child = FindChildRecursive(parent.GetChild(i), childName); + if (child != null) + return child; + } + + return null; + } + + /// + /// 递归设置GameObject的静态标志 + /// + public static void SetStaticFlagsRecursive(GameObject obj, StaticEditorFlags flags) + { + // 设置当前物体的静态标志 + GameObjectUtility.SetStaticEditorFlags(obj, flags); + + // 递归设置所有子物体 + for (int i = 0; i < obj.transform.childCount; i++) + { + Transform child = obj.transform.GetChild(i); + SetStaticFlagsRecursive(child.gameObject, flags); + } + } + + /// + /// 给地面添加BoxCollider + /// + public static void AddBoxColliderToGround(GameObject ground) + { + if (ground != null) + { + Object.DestroyImmediate(ground.GetComponent()); + var boxCollider = ground.gameObject.AddComponent(); + boxCollider.size = new Vector3(1, 1, 1); + boxCollider.center = new Vector3(0, 0, 0.5f); + + EditorSceneManager.MarkSceneDirty(ground.gameObject.scene); + EditorSceneManager.SaveOpenScenes(); + AssetDatabase.Refresh(); + } + else + { + Debug.LogError("地面节点不存在,无法添加BoxCollider"); + } + } + + /// + /// 设置地面渲染队列 + /// + public static void SetGroundRenderQueue(GameObject ground, int renderQueue) + { + if (ground != null) + { + GameObjectUtility.SetStaticEditorFlags(ground, StaticEditorFlags.ContributeGI); + var renderer = ground.GetComponent(); + if (renderer != null) + { + renderer.shadowCastingMode = ShadowCastingMode.Off; + var materials = renderer.sharedMaterials; + foreach (var material in materials) + { + if (material != null) + { + renderer.shadowCastingMode = ShadowCastingMode.Off; + material.renderQueue = renderQueue; + Debug.Log($"将地面的材质 {material.name} 的RenderQueue改为{renderQueue}"); + } + } + } + + // 刷新资源以确保RenderQueue修改已保存 + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + } + + /// + /// 解包预制体 + /// + public static void UnpackPrefab(GameObject obj) + { + if (obj != null && PrefabUtility.IsPartOfPrefabInstance(obj)) + { + Debug.Log($"解包场景{obj.scene.name} 的预制体: {obj.name}"); + + PrefabUtility.UnpackPrefabInstance(obj, + PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); + + EditorSceneManager.MarkSceneDirty(obj.scene); + EditorSceneManager.SaveOpenScenes(); + + // 强制刷新 + AssetDatabase.Refresh(); + } + } + + /// + /// 移除Scene中的重复EventSystem + /// + public static void RemoveRedundantEventSystems(Scene scene) + { + foreach (var rootObj in scene.GetRootGameObjects()) + { + UnityEngine.EventSystems.EventSystem[] eventSystems = rootObj.GetComponentsInChildren(); + if (null == eventSystems) + continue; + + for (var i = 0; i < eventSystems.Length; ++i) + { + Object.DestroyImmediate(eventSystems[i].gameObject); + } + } + } + + /// + /// 设置雾的位置 + /// + public static void SetFogPosition(GameObject fog) + { + if (fog == null) + return; + var transform = fog.transform; + var position = transform.position; + transform.position = new Vector3(position.x, position.y + 0.01f, position.z); + } + + /// + /// 获取标准化的资源路径 + /// + public static string GetAssetPath(string path) + { + // 已经是Assets路径格式 + if (path.StartsWith("Assets")) + return path; + + // 绝对路径转相对Assets路径 + string fullPath = Path.GetFullPath(path); + string assetsFullPath = Path.GetFullPath(Application.dataPath); + + if (fullPath.StartsWith(assetsFullPath)) + return "Assets" + fullPath.Substring(assetsFullPath.Length).Replace('\\', '/'); + + return path; + } +} \ No newline at end of file diff --git a/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/SceneUtils.cs.meta b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/SceneUtils.cs.meta new file mode 100644 index 00000000000..4222e2e0629 --- /dev/null +++ b/ProjectNLD/Assets/Editor/Scene/PostProcesser/Utils/SceneUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf8205ca3560b49cea7493bb6d55f615 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: