using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using BzKovSoft.ObjectSlicer; using UnityEditor; using UnityEngine; public class ModelSlicerUtil { private static ModelSlicerUtil _instance; public static ModelSlicerUtil Instance => _instance ??= new ModelSlicerUtil(); private Material _defaultSliceMaterial; private bool _debugMode = false; private bool _saveMeshesToAssets = false; private string _meshSavePath = "SlicedMeshes"; private bool _addTimestampToMeshName = true; /// /// 对模型进行切割处理 /// public async Task SliceModels( List rootsToSlice, GameObject groundObj, Material sliceMaterial, Vector3 planeDirection = default, bool ignoreObjectBoundsCheck = false, bool asynchronously = false, bool preserveOriginalModels = false, Transform slicerRoot = null, bool saveMeshesToAssets = false, string meshSavePath = "SlicedMeshes", bool addTimestampToMeshName = true) { // 设置选项 _debugMode = preserveOriginalModels; _saveMeshesToAssets = saveMeshesToAssets; _meshSavePath = string.IsNullOrEmpty(meshSavePath) ? "SlicedMeshes" : meshSavePath; _addTimestampToMeshName = addTimestampToMeshName; _defaultSliceMaterial = sliceMaterial; if (rootsToSlice == null || rootsToSlice.Count == 0 || groundObj == null) { Debug.LogWarning("切割模型参数不完整,跳过切割处理"); return; } // 准备保存目录 if (_saveMeshesToAssets) EnsureSaveDirectoryExists(); // 处理切割 Dictionary originalToProcessedMap = new Dictionary(); Plane slicePlane = CreateSlicePlane(groundObj, planeDirection); List sliceTasks = new List(); // 处理每个根节点 foreach (var root in rootsToSlice) { if (root == null) continue; Transform processedModel = await ProcessRootObject(root, slicePlane, ignoreObjectBoundsCheck, asynchronously, sliceTasks, slicerRoot); if (processedModel != null) originalToProcessedMap[root] = processedModel; } // 等待所有切割任务完成 if (sliceTasks.Count > 0) await Task.WhenAll(sliceTasks); // 处理替换或保存 if (!_debugMode) ReplaceOriginalModels(originalToProcessedMap); if (_saveMeshesToAssets) SaveMeshes(originalToProcessedMap); } /// /// 保存处理后的网格 /// private void SaveMeshes(Dictionary originalToProcessedMap) { int savedCount = 0; foreach (var pair in originalToProcessedMap) { savedCount += SaveProcessedMeshes(pair.Value); } if (savedCount > 0) { Debug.Log($"已成功保存 {savedCount} 个切割后的网格到路径: Assets/{_meshSavePath}"); AssetDatabase.Refresh(); } else { Debug.LogWarning($"没有网格被保存到路径: Assets/{_meshSavePath}"); } } /// /// 确保保存目录存在 /// private void EnsureSaveDirectoryExists() { string fullPath = Path.Combine(Application.dataPath, _meshSavePath); if (!Directory.Exists(fullPath)) { try { Directory.CreateDirectory(fullPath); Debug.Log($"创建网格保存目录: {fullPath}"); } catch (Exception ex) { Debug.LogError($"创建网格保存目录失败: {ex.Message}"); _saveMeshesToAssets = false; } } } /// /// 保存处理后模型的所有网格 /// private int SaveProcessedMeshes(Transform processedRoot) { if (processedRoot == null) return 0; int savedCount = 0; MeshFilter[] meshFilters = processedRoot.GetComponentsInChildren(true); string timestamp = _addTimestampToMeshName ? $"_{DateTime.Now:yyyyMMdd_HHmmss}" : ""; foreach (var meshFilter in meshFilters) { if (meshFilter == null || meshFilter.sharedMesh == null) continue; string objectPath = GetHierarchyPath(meshFilter.transform, processedRoot); string meshName = $"{objectPath}{timestamp}"; if (SaveMeshAsset(meshFilter.sharedMesh, meshName)) savedCount++; } return savedCount; } /// /// 获取物体在层级中的路径 /// private string GetHierarchyPath(Transform transform, Transform stopAt) { if (transform == null) return "UnknownMesh"; List pathParts = new List(); Transform current = transform; while (current != null && current != stopAt) { pathParts.Insert(0, SanitizeFileName(current.name)); current = current.parent; } if (stopAt != null) pathParts.Insert(0, SanitizeFileName(stopAt.name)); return string.Join("_", pathParts); } /// /// 净化文件名,删除不允许的字符 /// private string SanitizeFileName(string fileName) { char[] invalidChars = Path.GetInvalidFileNameChars(); foreach (char c in invalidChars) { fileName = fileName.Replace(c, '_'); } return fileName; } /// /// 将网格保存为资源文件 /// private bool SaveMeshAsset(Mesh mesh, string meshName) { try { Mesh meshToSave = UnityEngine.Object.Instantiate(mesh); meshToSave.name = meshName; string assetPath = $"Assets/{_meshSavePath}/{meshName}.asset"; assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath); AssetDatabase.CreateAsset(meshToSave, assetPath); Debug.Log($"已保存网格: {assetPath}"); return true; } catch (Exception ex) { Debug.LogError($"保存网格 {meshName} 失败: {ex.Message}"); return false; } } /// /// 创建切割平面 /// private Plane CreateSlicePlane(GameObject groundObj, Vector3 planeDirection) { Vector3 planePosition = groundObj.transform.position; Vector3 planeNormal = (planeDirection == Vector3.zero) ? groundObj.transform.up : planeDirection.normalized; Plane slicePlane = new Plane(planeNormal, planePosition); #if UNITY_EDITOR Debug.DrawRay(planePosition, planeNormal * 5f, Color.red, 10f); Debug.DrawRay(planePosition, Vector3.right * 5f, Color.green, 10f); Debug.DrawRay(planePosition, Vector3.forward * 5f, Color.blue, 10f); #endif return slicePlane; } /// /// 处理根对象,返回处理后的模型 /// private async Task ProcessRootObject(Transform root, Plane slicePlane, bool ignoreObjectBoundsCheck, bool asynchronously, List sliceTasks, Transform slicerRoot) { GameObject copy = null; try { // 创建原始对象的副本 Transform parent = _debugMode ? slicerRoot : null; copy = UnityEngine.Object.Instantiate(root.gameObject, root.position, root.rotation, parent); // 处理副本 await SliceObjectHierarchy(copy.transform, slicePlane, ignoreObjectBoundsCheck, asynchronously, sliceTasks, slicerRoot); return copy.transform; } catch (Exception ex) { Debug.LogError($"处理物体 {root.name} 时发生错误: {ex.Message}"); if (copy != null) UnityEngine.Object.DestroyImmediate(copy); return null; } } /// /// 替换原始模型 /// private void ReplaceOriginalModels(Dictionary originalToProcessedMap) { foreach (var pair in originalToProcessedMap) { Transform original = pair.Key; Transform processed = pair.Value; if (original == null || processed == null) continue; try { Transform originalParent = original.parent; int originalSiblingIndex = original.GetSiblingIndex(); string originalName = original.name; processed.SetParent(originalParent); processed.SetSiblingIndex(originalSiblingIndex); processed.name = originalName; UnityEngine.Object.DestroyImmediate(original.gameObject); } catch (Exception ex) { Debug.LogError($"替换原始模型时发生错误: {ex.Message}"); } } } /// /// 递归切割对象层次结构中的所有对象 /// private async Task SliceObjectHierarchy(Transform root, Plane plane, bool ignoreObjectBoundsCheck, bool asynchronously, List sliceTasks, Transform slicerRoot = null) { if (root == null || root.gameObject == null) return; GameObject rootObj = root.gameObject; // 预收集子物体 Transform[] childrenCopy = new Transform[root.childCount]; for (int i = 0; i < root.childCount; i++) { childrenCopy[i] = root.GetChild(i); } // 切割当前物体 bool hasMeshFilter = rootObj.GetComponent() != null && rootObj.GetComponent().sharedMesh != null; bool hasRenderer = rootObj.GetComponent() != null; if (hasMeshFilter && hasRenderer) { try { await SliceGameObject(rootObj, plane, ignoreObjectBoundsCheck, asynchronously, sliceTasks, slicerRoot); } catch (Exception ex) { Debug.LogError($"切割物体 {rootObj.name} 时发生错误: {ex.Message}"); } } // 如果物体被销毁,停止处理子物体 if (rootObj == null || root == null) return; // 递归处理子物体 foreach (Transform child in childrenCopy) { if (child == null || child.gameObject == null) continue; try { await SliceObjectHierarchy(child, plane, ignoreObjectBoundsCheck, asynchronously, sliceTasks, slicerRoot); } catch (Exception ex) { Debug.LogError($"处理子物体 {child.name} 时发生错误: {ex.Message}"); } } } /// /// 对单个游戏对象执行切割 /// private async Task SliceGameObject(GameObject gameObject, Plane plane, bool ignoreObjectBoundsCheck, bool asynchronously, List sliceTasks, Transform slicerRoot = null) { if (gameObject == null) return; Vector3 originalScale = gameObject.transform.localScale; Transform targetParent = _debugMode && slicerRoot != null ? slicerRoot : gameObject.transform.parent; // 获取原始材质 Material originalMaterial = null; Renderer renderer = gameObject.GetComponent(); if (renderer != null && renderer.sharedMaterials.Length > 0) { originalMaterial = renderer.sharedMaterials[0]; } // 设置切割组件 BzSliceableObject sliceable = gameObject.GetComponent(); bool addedComponent = false; if (sliceable == null) { sliceable = gameObject.AddComponent(); sliceable.defaultSliceMaterial = _defaultSliceMaterial ?? originalMaterial; sliceable.asynchronously = false; addedComponent = true; } try { // 检查是否需要切割 if (!ignoreObjectBoundsCheck) { MeshFilter meshFilter = gameObject.GetComponent(); if (meshFilter != null && meshFilter.sharedMesh != null && !CheckObjectIntersectsPlane(gameObject, meshFilter, plane)) { return; } } // 执行切割 BzSliceTryResult result = await sliceable.SliceAsync(plane, null); // 处理结果 ProcessSliceResult(gameObject, result, originalScale, targetParent, originalMaterial); } catch (Exception ex) { Debug.LogError($"切割物体 {gameObject.name} 时发生错误: {ex.Message}"); } finally { // 清理组件 if (addedComponent && gameObject != null) { try { UnityEngine.Object.DestroyImmediate(sliceable); } catch (Exception) { /* 忽略组件删除错误 */ } } } } /// /// 检查物体是否与平面相交 /// private bool CheckObjectIntersectsPlane(GameObject gameObject, MeshFilter meshFilter, Plane plane) { Mesh mesh = meshFilter.sharedMesh; if (mesh == null) return false; Matrix4x4 localToWorld = gameObject.transform.localToWorldMatrix; Vector3[] vertices = mesh.vertices; bool hasPositive = false; bool hasNegative = false; // 采样检查顶点 int step = Mathf.Max(1, vertices.Length / 100); for (int i = 0; i < vertices.Length; i += step) { Vector3 worldVertex = localToWorld.MultiplyPoint3x4(vertices[i]); float distance = plane.GetDistanceToPoint(worldVertex); if (distance > 0.01f) hasPositive = true; else if (distance < -0.01f) hasNegative = true; if (hasPositive && hasNegative) return true; } // 如果完全在平面下方,移除物体 if (!hasPositive && hasNegative) { UnityEngine.Object.DestroyImmediate(gameObject); return false; } // 如果完全在平面上方,无需切割 return false; } /// /// 处理切割结果 /// private void ProcessSliceResult(GameObject originalObject, BzSliceTryResult result, Vector3 originalScale, Transform targetParent, Material originalMaterial) { if (originalObject == null || result == null || !result.sliced || result.resultObjects == null || result.resultObjects.Length == 0) { return; } string originalName = originalObject.name; Vector3 originalPosition = originalObject.transform.position; Quaternion originalRotation = originalObject.transform.rotation; // 处理切割结果 foreach (var resultObj in result.resultObjects) { if (resultObj == null || resultObj.gameObject == null) continue; try { if (resultObj.side) { // 处理上半部分 GameObject upperPart = resultObj.gameObject; upperPart.name = _debugMode ? originalName + "_Upper" : originalName; upperPart.transform.localScale = originalScale; // 处理网格和材质 RemoveSliceFaceSubmesh(upperPart); EnsureSingleMaterial(upperPart, originalMaterial); FixMeshUVs(upperPart); // 设置位置 upperPart.transform.SetParent(null); upperPart.transform.position = originalPosition; upperPart.transform.rotation = originalRotation; if (targetParent != null) { upperPart.transform.SetParent(targetParent); } // 移除临时组件 BzSliceableObject sliceable = upperPart.GetComponent(); if (sliceable != null) { UnityEngine.Object.DestroyImmediate(sliceable); } } else { // 销毁下半部分 UnityEngine.Object.DestroyImmediate(resultObj.gameObject); } } catch (Exception ex) { Debug.LogError($"处理切割结果时出错: {ex.Message}"); } } } /// /// 删除切割面生成的submesh,并重新构建网格 /// private void RemoveSliceFaceSubmesh(GameObject gameObject) { MeshFilter[] meshFilters = gameObject.GetComponentsInChildren(true); foreach (var meshFilter in meshFilters) { if (meshFilter == null || meshFilter.sharedMesh == null) continue; Mesh originalMesh = meshFilter.sharedMesh; int submeshCount = originalMesh.subMeshCount; // 只有多个submesh时处理 if (submeshCount <= 1) continue; try { // 获取第一个submesh数据 int[] triangles = originalMesh.GetTriangles(0); Vector3[] originalVertices = originalMesh.vertices; Vector2[] originalUVs = originalMesh.uv; Vector3[] originalNormals = originalMesh.normals; Vector4[] originalTangents = originalMesh.tangents; Color[] originalColors = originalMesh.colors; // 收集使用的顶点 HashSet usedVertexIndices = new HashSet(); for (int i = 0; i < triangles.Length; i++) { usedVertexIndices.Add(triangles[i]); } // 建立索引映射 Dictionary indexRemapping = new Dictionary(); int newVertexIndex = 0; foreach (int oldIndex in usedVertexIndices) { indexRemapping[oldIndex] = newVertexIndex++; } // 创建新的顶点数据 Vector3[] newVertices = new Vector3[usedVertexIndices.Count]; Vector2[] newUVs = new Vector2[usedVertexIndices.Count]; Vector3[] newNormals = originalNormals != null && originalNormals.Length > 0 ? new Vector3[usedVertexIndices.Count] : null; Vector4[] newTangents = originalTangents != null && originalTangents.Length > 0 ? new Vector4[usedVertexIndices.Count] : null; Color[] newColors = originalColors != null && originalColors.Length > 0 ? new Color[usedVertexIndices.Count] : null; // 重映射数据 foreach (var kvp in indexRemapping) { int oldIndex = kvp.Key; int newIndex = kvp.Value; // 复制顶点 newVertices[newIndex] = originalVertices[oldIndex]; // 复制UV if (originalUVs != null && originalUVs.Length > oldIndex) { newUVs[newIndex] = originalUVs[oldIndex]; } else if (originalUVs != null && originalUVs.Length > 0) { newUVs[newIndex] = originalUVs[0]; } else { newUVs[newIndex] = Vector2.zero; } // 复制法线 if (newNormals != null && originalNormals.Length > oldIndex) { newNormals[newIndex] = originalNormals[oldIndex]; } // 复制切线 if (newTangents != null && originalTangents.Length > oldIndex) { newTangents[newIndex] = originalTangents[oldIndex]; } // 复制颜色 if (newColors != null && originalColors.Length > oldIndex) { newColors[newIndex] = originalColors[oldIndex]; } } // 重映射三角形 int[] newTriangles = new int[triangles.Length]; for (int i = 0; i < triangles.Length; i++) { newTriangles[i] = indexRemapping[triangles[i]]; } // 创建新网格 Mesh newMesh = new Mesh(); newMesh.name = originalMesh.name + "_CleanedMesh"; newMesh.vertices = newVertices; newMesh.uv = newUVs; if (newNormals != null) newMesh.normals = newNormals; if (newTangents != null) newMesh.tangents = newTangents; if (newColors != null) newMesh.colors = newColors; newMesh.triangles = newTriangles; // 更新网格属性 if (newNormals == null) newMesh.RecalculateNormals(); newMesh.RecalculateBounds(); // 应用新网格 meshFilter.sharedMesh = newMesh; } catch (Exception ex) { Debug.LogError($"重构网格时出错: {ex.Message}"); } } } /// /// 确保物体及其子物体只有单一材质 /// private void EnsureSingleMaterial(GameObject gameObject, Material originalMaterial) { Renderer[] renderers = gameObject.GetComponentsInChildren(true); foreach (var renderer in renderers) { if (renderer == null) continue; Material[] materials = renderer.sharedMaterials; if (materials == null || materials.Length == 0) continue; // 选择要使用的材质 Material materialToUse = originalMaterial; if (materialToUse == null && materials[0] != null) { materialToUse = materials[0]; } if (materialToUse == null) continue; // 设置单一材质 renderer.sharedMaterial = materialToUse; // 强制处理多材质情况 if (renderer.sharedMaterials.Length > 1) { renderer.sharedMaterials = new Material[1] { materialToUse }; } } } /// /// 修复切割生成的网格UV坐标 /// private void FixMeshUVs(GameObject gameObject) { MeshFilter[] meshFilters = gameObject.GetComponentsInChildren(true); foreach (var meshFilter in meshFilters) { if (meshFilter == null || meshFilter.sharedMesh == null) continue; Mesh mesh = meshFilter.sharedMesh; Mesh uniqueMesh = UnityEngine.Object.Instantiate(mesh); int vertexCount = uniqueMesh.vertices.Length; Vector2[] uvs = new Vector2[vertexCount]; // 填充UV数据 if (uniqueMesh.uv != null && uniqueMesh.uv.Length > 0) { Vector2[] oldUVs = uniqueMesh.uv; int copyLength = Mathf.Min(oldUVs.Length, vertexCount); // 复制已有UV for (int i = 0; i < copyLength; i++) { uvs[i] = oldUVs[i]; } // 填充剩余UV if (oldUVs.Length < vertexCount) { Vector2 lastUV = oldUVs[oldUVs.Length - 1]; for (int i = oldUVs.Length; i < vertexCount; i++) { uvs[i] = lastUV; } } } // 应用UV uniqueMesh.uv = uvs; // 更新其他属性 if (uniqueMesh.normals == null || uniqueMesh.normals.Length != vertexCount) { uniqueMesh.RecalculateNormals(); } if (uniqueMesh.tangents == null || uniqueMesh.tangents.Length != vertexCount) { Vector4[] tangents = new Vector4[vertexCount]; for (int i = 0; i < vertexCount; i++) { tangents[i] = new Vector4(1, 0, 0, 1); } uniqueMesh.tangents = tangents; } // 应用修改后的网格 meshFilter.sharedMesh = uniqueMesh; } } }