当前位置: 首页 > news >正文

【Unity3D】利用IJob、Burst优化处理切割物体

参考文章: 

【Unity】切割网格

【Unity3D】ECS入门学习(一)导入及基础学习_unity ecs教程-CSDN博客 

【Unity3D】ECS入门学习(十二)IJob、IJobFor、IJobParallelFor_unity ijobparallelfor-CSDN博客

工程资源地址: 

BStandShaderResources/job_burst_cut_demo.unitypackage at master · AMikeW/BStandShaderResources · GitHub

优化前

使用Job、Burst会将切割数据处理放到一个非主线程处理,并行加速计算。
麻烦的地方是所有List<T>都要改成NativeArray,所有Dictionary<K,V>都要改成NativeHashMap<K,V>,以及不能传递GameObject,Transform等,也就是不能有任何的引用类型对象出现在继承了IJob接口的脚本,而且CutJob是一个struct结构体。

注意:在CutManager脚本,必须引入:using Unity.Jobs; 才能使用job.Schedule接口方法,因为这玩意并不是IJor的方法,毕竟IJor只是个接口,它是一个扩展方法。

namespace Unity.Jobs
{//// 摘要://     Extension methods for Jobs using the IJob interface.public static class IJobExtensions{public static void Run<T>(this T jobData) where T : struct, IJob;public static JobHandle Schedule<T>(this T jobData, JobHandle dependsOn = default) where T : struct, IJob;}
}

  只会切割CutGo标签的物体 

开启Job

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;/// <summary>
/// 切割平面信息
/// </summary>
public class CutInfo
{public Vector3 cutPoint;      //对应射线碰撞点public Vector3 cutHorizontal; //对应dirpublic Vector3 cutVertical;//对应verticalpublic Vector3 cutNormal;  //对应normal
}/// <summary>
/// 必须挂在摄像机上才会有GL线条
/// 功能:凸网格切割网格(不支持凹网格)
/// 使用方法:按F开启和禁用切割功能,鼠标指向物体出现辅助线条,滑轮滚动控制切割线条旋转,鼠标左击进行切割物体
/// </summary>
public class CutManager : MonoBehaviour
{private static CutManager _instance;public static CutManager Instance{get { return _instance; }}bool isOn;public bool blockCut;Transform hitTarget;Vector3 hitPos;Vector3 dir;Vector3 cutVerticalDir;Vector3 cutNormalDir;float angle;float scrollSpeed = 25.0f;bool isCutting;Mesh targetMesh;const string TagName = "CutGo";public Transform generatePoint;//使用GL画线所需材质public Material mater;private float force = 0.8f;public float destroyTime = 0.5f;public List<CutInfo> cutInfos; //记录所有正常的切割(防止出现极其相似的切割平面造成奇怪的bug)public Material dissolveMat;private GameObject lastCutObject;private Vector3 cutStartPos;private Vector3 cutEndPos;private Action<float> cutCallback;public bool isUseJob;//试图尝试使用常驻的变量,但测试发现会导致数据混乱,具体表现是切割第二次时,整个模型的顶点乱了,如果使用TempJob临时的则不会发生//NativeArray<Vector3> _tempVert1;//NativeArray<Vector3> _tempVert2;//NativeArray<Vector3> _tempNormal1;//NativeArray<Vector3> _tempNormal2;//NativeArray<int> _tempTriangles1;//NativeArray<int> _tempTriangles2;//NativeArray<Vector2> _uvs1;//NativeArray<Vector2> _uvs2;//NativeHashMap<int, int> _tempIndex1;//NativeHashMap<int, int> _tempIndex2;//NativeArray<int> _temp1CountArray;//NativeArray<int> _temp2CountArray;//NativeArray<Vector3> _localPos;//NativeArray<Vector3> _allPos;//NativeArray<Vector3> targetMeshVert;//NativeArray<Vector3> targetMeshNormal;//NativeArray<int> targetMeshTriangles;private void Awake(){_instance = this;}private void OnDestroy(){//targetMeshVert.Dispose();//targetMeshNormal.Dispose();//targetMeshTriangles.Dispose();//_tempVert1.Dispose();//_tempNormal1.Dispose();//_tempTriangles1.Dispose();//_uvs1.Dispose();//_tempVert2.Dispose();//_tempNormal2.Dispose();//_tempTriangles2.Dispose();//_uvs2.Dispose();//_temp1CountArray.Dispose();//_temp2CountArray.Dispose();//_tempIndex1.Dispose();//_tempIndex2.Dispose();//_localPos.Dispose();//_allPos.Dispose();}void Start(){isOn = false;tempVert1 = new List<Vector3>();tempNormal1 = new List<Vector3>();tempTriangles1 = new List<int>();tempIndex1 = new Dictionary<int, int>();uvs1 = new List<Vector2>();tempVert2 = new List<Vector3>();tempNormal2 = new List<Vector3>();tempTriangles2 = new List<int>();tempIndex2 = new Dictionary<int, int>();uvs2 = new List<Vector2>();localPos = new List<Vector3>();allPos = new List<Vector3>();cutInfos = new List<CutInfo>();//targetMeshVert = new Unity.Collections.NativeArray<Vector3>(5000, Unity.Collections.Allocator.Persistent);//targetMeshNormal = new Unity.Collections.NativeArray<Vector3>(2500, Unity.Collections.Allocator.Persistent);//targetMeshTriangles = new Unity.Collections.NativeArray<int>(9000, Unity.Collections.Allocator.Persistent);//_tempVert1 = new Unity.Collections.NativeArray<Vector3>(5000, Unity.Collections.Allocator.Persistent);//_tempNormal1 = new Unity.Collections.NativeArray<Vector3>(2500, Unity.Collections.Allocator.Persistent);//_tempTriangles1 = new Unity.Collections.NativeArray<int>(9000, Unity.Collections.Allocator.Persistent);//_uvs1 = new Unity.Collections.NativeArray<Vector2>(5000, Unity.Collections.Allocator.Persistent);//_tempIndex1 = new Unity.Collections.NativeHashMap<int, int>(9000, Unity.Collections.Allocator.Persistent);//_temp1CountArray = new NativeArray<int>(5, Allocator.Persistent);//_tempVert2 = new Unity.Collections.NativeArray<Vector3>(5000, Unity.Collections.Allocator.Persistent);//_tempNormal2 = new Unity.Collections.NativeArray<Vector3>(2500, Unity.Collections.Allocator.Persistent);//_tempTriangles2 = new Unity.Collections.NativeArray<int>(9000, Unity.Collections.Allocator.Persistent);//_uvs2 = new Unity.Collections.NativeArray<Vector2>(5000, Unity.Collections.Allocator.Persistent);//_tempIndex2 = new Unity.Collections.NativeHashMap<int, int>(9000, Unity.Collections.Allocator.Persistent);//_temp2CountArray = new NativeArray<int>(5, Allocator.Persistent);//_localPos = new NativeArray<Vector3>(1000, Allocator.Persistent);//_allPos = new NativeArray<Vector3>(1000, Allocator.Persistent);}void Update(){ControlIsOn();ControlCutSpace();}void OnPostRender(){if (!isOn || !isCutting)return;if (!mater){Debug.LogError("Please Assign a material on the inspector");return;}GL.PushMatrix();mater.SetPass(0);GL.Color(Color.yellow);GL.Begin(GL.LINES);GL.Vertex(hitPos + cutVerticalDir * 12f + dir * 1f);GL.Vertex(hitPos - cutVerticalDir * 12f + dir * 1f);GL.Vertex(hitPos + dir * 1f);GL.Vertex(hitPos + cutNormalDir * 0.2f + dir * 1f);GL.End();GL.PopMatrix();}void ControlIsOn(){isOn = true;}void ControlCutSpace(){//射线发射射线计算出射线交点,以射线为轴计算出另外2个轴向 (红线为射线,绿线为垂直于射线和法线的线, 黄线为法线)        Ray ray = Camera.main.ScreenPointToRay(InputController.GetPosition());Debug.DrawRay(Camera.main.transform.position, InputController.GetPosition(), Color.red);RaycastHit hit;if (Physics.Raycast(ray, out hit)){if (hit.transform.tag != TagName){TryCut();cutStartPos = Vector3.zero;cutEndPos = Vector3.zero;return;}if (cutStartPos == null || cutStartPos == Vector3.zero){cutStartPos = hit.point;}isCutting = true;hitTarget = hit.transform;hitPos = hit.point;cutEndPos = hitPos;MeshFilter meshFilter = hit.transform.GetComponent<MeshFilter>();if (meshFilter != null){targetMesh = hit.transform.GetComponent<MeshFilter>().mesh;}else{Debug.LogWarning("尝试切割没有网格的物品");cutStartPos = Vector3.zero;cutEndPos = Vector3.zero;return;}//dir = (Camera.main.transform.position - hitPos).normalized;//cutVerticalDir = (Vector3.Dot(dir, Vector3.up) * -dir + Vector3.up).normalized;//dir和cutVerticalDir保持垂直使用鼠标滚轮旋转切割平面//if (Input.GetAxis("Mouse ScrollWheel") < 0)//{//    angle += scrollSpeed * Time.deltaTime;//}//else if (Input.GetAxis("Mouse ScrollWheel") > 0)//{//    angle -= scrollSpeed * Time.deltaTime;//}//cutVerticalDir = Quaternion.AngleAxis(Mathf.Rad2Deg * angle, dir) * cutVerticalDir; //围绕dir轴旋转//cutNormalDir = Vector3.Cross(dir, cutVerticalDir).normalized; //计算出切割面的法线#if DEBUG//Debug.DrawRay(hitPos, cutVerticalDir, Color.green);//Debug.DrawRay(hitPos, dir, Color.red);//Debug.DrawRay(hitPos, cutNormalDir, Color.yellow);
#endif            }else{TryCut();cutStartPos = Vector3.zero;cutEndPos = Vector3.zero;isCutting = false;}滑动切割//Ray ray1 = Camera.main.ScreenPointToRay(InputController.GetPosition());//Physics.Raycast(ray);//if (Input.GetKeyDown(KeyCode.Mouse0))//{//    CutInfo tempCutInfo = new CutInfo();//    tempCutInfo.cutHorizontal = dir;//    tempCutInfo.cutVertical = cutVerticalDir;//    tempCutInfo.cutNormal = cutNormalDir;//    Cutting();//}}IEnumerator TryCutting1(){for (int i = 0; i < 3; i++){if ((cutEndPos != null && cutEndPos != Vector3.zero) && (cutStartPos != null && cutStartPos != Vector3.zero)){isCutting = true;var point = (cutStartPos + cutEndPos) / 2;dir = (Camera.main.transform.position - point).normalized;var tempDir = (cutEndPos - cutStartPos).normalized;cutVerticalDir = (Vector3.Dot(dir, tempDir) * -dir + tempDir).normalized;//dir和cutVerticalDir保持垂直cutNormalDir = Vector3.Cross(dir, cutVerticalDir).normalized; //计算出切割面的法线Cutting();yield return new WaitForSeconds(0.1f);}}cutEndPos = Vector3.zero;cutStartPos = Vector3.zero;}private void TryCut(){if (!blockCut && (cutEndPos != null && cutEndPos != Vector3.zero) && (cutStartPos != null && cutStartPos != Vector3.zero)){isCutting = true;var point = (cutStartPos + cutEndPos) / 2;dir = (Camera.main.transform.position - point).normalized;var tempDir = (cutEndPos - cutStartPos).normalized;cutVerticalDir = (Vector3.Dot(dir, tempDir) * -dir + tempDir).normalized;//dir和cutVerticalDir保持垂直cutNormalDir = Vector3.Cross(dir, cutVerticalDir).normalized; //计算出切割面的法线Cutting();}cutEndPos = Vector3.zero;cutStartPos = Vector3.zero;isCutting = false;}private bool IsValidCutInfo(Vector3 dir, Vector3 vertical, Vector3 normal){float limitValue = 0.15f;foreach (var v in cutInfos){if (Mathf.Abs(dir.x - v.cutHorizontal.x) < limitValue && Mathf.Abs(dir.y - v.cutHorizontal.y) < limitValue && Mathf.Abs(dir.z - v.cutHorizontal.z) < limitValue&& Mathf.Abs(vertical.x - v.cutVertical.x) < limitValue && Mathf.Abs(vertical.y - v.cutVertical.y) < limitValue && Mathf.Abs(vertical.z - v.cutVertical.z) < limitValue&& Mathf.Abs(normal.x - v.cutNormal.x) < limitValue && Mathf.Abs(normal.y - v.cutNormal.y) < limitValue && Mathf.Abs(normal.z - v.cutNormal.z) < limitValue){return false;}}return true;}List<Vector3> tempVert1;List<Vector3> tempNormal1;List<int> tempTriangles1;Dictionary<int, int> tempIndex1;List<Vector2> uvs1;List<Vector3> tempVert2;List<Vector3> tempNormal2;List<int> tempTriangles2;Dictionary<int, int> tempIndex2;List<Vector2> uvs2;int[] triangles;List<Vector3> localPos;List<Vector3> allPos;void Cutting(){if (!isCutting || !isOn || targetMesh == null || hitTarget == null)return;tempVert1.Clear();tempNormal1.Clear();tempTriangles1.Clear();tempIndex1.Clear();uvs1.Clear();tempVert2.Clear();tempNormal2.Clear();tempTriangles2.Clear();tempIndex2.Clear();uvs2.Clear();allPos.Clear();System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();if (isUseJob){CutJob job = new CutJob();var vertices = targetMesh.vertices;var normals = targetMesh.normals;var triangles = targetMesh.triangles;var _tempVert1 = new Unity.Collections.NativeArray<Vector3>(5000, Unity.Collections.Allocator.TempJob);var _tempNormal1 = new Unity.Collections.NativeArray<Vector3>(2500, Unity.Collections.Allocator.TempJob);var _tempTriangles1 = new Unity.Collections.NativeArray<int>(9000, Unity.Collections.Allocator.TempJob);var _uvs1 = new Unity.Collections.NativeArray<Vector2>(5000, Unity.Collections.Allocator.TempJob);var _tempIndex1 = new Unity.Collections.NativeHashMap<int, int>(9000, Unity.Collections.Allocator.TempJob);var _temp1CountArray = new NativeArray<int>(5, Allocator.TempJob);var _tempVert2 = new Unity.Collections.NativeArray<Vector3>(5000, Unity.Collections.Allocator.TempJob);var _tempNormal2 = new Unity.Collections.NativeArray<Vector3>(2500, Unity.Collections.Allocator.TempJob);var _tempTriangles2 = new Unity.Collections.NativeArray<int>(9000, Unity.Collections.Allocator.TempJob);var _uvs2 = new Unity.Collections.NativeArray<Vector2>(5000, Unity.Collections.Allocator.TempJob);var _tempIndex2 = new Unity.Collections.NativeHashMap<int, int>(9000, Unity.Collections.Allocator.TempJob);var _temp2CountArray = new NativeArray<int>(5, Allocator.TempJob);var _localPos = new NativeArray<Vector3>(1000, Allocator.TempJob);var _allPos = new NativeArray<Vector3>(1000, Allocator.TempJob);var targetMeshVert = new Unity.Collections.NativeArray<Vector3>(vertices.Length, Unity.Collections.Allocator.TempJob);var targetMeshNormal = new Unity.Collections.NativeArray<Vector3>(normals.Length, Unity.Collections.Allocator.TempJob);var targetMeshTriangles = new Unity.Collections.NativeArray<int>(triangles.Length, Unity.Collections.Allocator.TempJob);for (int i = 0; i < vertices.Length; i++){targetMeshVert[i] = vertices[i];}job.targetMeshVert = targetMeshVert;for (int i = 0; i < normals.Length; i++){targetMeshNormal[i] = normals[i];}job.targetMeshNormal = targetMeshNormal;for (int i = 0; i < triangles.Length; i++){targetMeshTriangles[i] = triangles[i];}job.targetMeshTriangles_Count = triangles.Length;job.targetMeshTriangles = targetMeshTriangles;//job.hitTarget = hitTarget;job.hitTarget_LocalScale = hitTarget.localScale;job.hitTarget_o2w = hitTarget.localToWorldMatrix;job.hitTarget_w2o = hitTarget.worldToLocalMatrix;job.hitPos = hitPos;job.dir = dir;job.cutVerticalDir = cutVerticalDir;job.cutNormalDir = cutNormalDir;job.tempVert1_Count = 0;job.tempVert1 = _tempVert1;job.tempNormal1_Count = 0;job.tempNormal1 = _tempNormal1;job.tempTriangles1_Count = 0;job.tempTriangles1 = _tempTriangles1;job.uvs1_Count = 0;job.uvs1 = _uvs1;job.tempIndex1 = _tempIndex1;job.temp1CountArray = _temp1CountArray;job.tempVert2_Count = 0;job.tempVert2 = _tempVert2;job.tempNormal2_Count = 0;job.tempNormal2 = _tempNormal2;job.tempTriangles2_Count = 0;job.tempTriangles2 = _tempTriangles2;job.uvs2_Count = 0;job.uvs2 = _uvs2;job.tempIndex2 = _tempIndex2;job.temp2CountArray = _temp2CountArray;job.localPos = _localPos;job.allPos = _allPos;JobHandle jobHandle = job.Schedule();jobHandle.Complete();//数据转化 切割后的2个模型数据(顶点、法线、索引、UV)int temp1_VertCount = job.temp1CountArray[0];int temp1_NormalCount = job.temp1CountArray[1];int temp1_TrianglesCount = job.temp1CountArray[2];int temp1_UvsCount = job.temp1CountArray[3];int temp2_VertCount = job.temp2CountArray[0];int temp2_NormalCount = job.temp2CountArray[1];int temp2_TrianglesCount = job.temp2CountArray[2];int temp2_UvsCount = job.temp2CountArray[3];for (int i = 0; i < temp1_VertCount; i++){tempVert1.Add(_tempVert1[i]);}for (int i = 0; i < temp2_VertCount; i++){tempVert2.Add(_tempVert2[i]);}for (int i = 0; i < temp1_NormalCount; i++){tempNormal1.Add(_tempNormal1[i]);}for (int i = 0; i < temp2_NormalCount; i++){tempNormal2.Add(_tempNormal2[i]);}for (int i = 0; i < temp1_TrianglesCount; i++){tempTriangles1.Add(_tempTriangles1[i]);}for (int i = 0; i < temp2_TrianglesCount; i++){tempTriangles2.Add(_tempTriangles2[i]);}for (int i = 0; i < temp1_UvsCount; i++){uvs1.Add(_uvs1[i]);}for (int i = 0; i < temp2_UvsCount; i++){uvs2.Add(_uvs2[i]);}//>>>>>>>>>>>>>>>>>>>>>>>>>>>>第一个切割出的物体开始构建Mesh originMesh = new Mesh(), newMesh = new Mesh();       originMesh.vertices = tempVert1.ToArray();originMesh.normals = tempNormal1.ToArray();originMesh.triangles = tempTriangles1.ToArray();originMesh.uv = uvs1.ToArray();hitTarget.GetComponent<MeshFilter>().mesh = originMesh;Collider collider = hitTarget.GetComponent<Collider>();if (collider != null){Destroy(collider);}//hitTarget.gameObject.AddComponent<BoxCollider>();MeshCollider meshCollider = hitTarget.gameObject.AddComponent<MeshCollider>();hitTarget.gameObject.tag = TagName;hitTarget.gameObject.layer = 11;hitTarget.SetParent(generatePoint);float obj1Volume = CalculateVolumeHelper.CalculateSumVolume(hitTarget.transform.lossyScale, hitTarget.GetComponent<MeshFilter>());//>>>>>>>>>>>>>>>>>>>>>>>>>>切割出的第二个物体开始构建newMesh.vertices = tempVert2.ToArray();newMesh.normals = tempNormal2.ToArray();newMesh.triangles = tempTriangles2.ToArray();newMesh.uv = uvs2.ToArray();GameObject newobj = new GameObject();newobj.transform.position = hitTarget.position;newobj.transform.rotation = hitTarget.rotation;newobj.transform.localScale = hitTarget.localScale;newobj.AddComponent<MeshFilter>().mesh = newMesh;newobj.AddComponent<MeshRenderer>();Material mat = hitTarget.GetComponent<MeshRenderer>().material;newobj.GetComponent<MeshRenderer>().material = new Material(mat);newobj.tag = TagName;newobj.layer = 11;newobj.transform.SetParent(generatePoint);//顶点少的情况可以使用它 否则会报错超出顶点限制MeshCollider meshCollider2 = newobj.AddComponent<MeshCollider>();try{meshCollider.convex = true;meshCollider2.convex = true;}catch (Exception e){Debug.LogWarning(e.Message);}float obj2Volume = CalculateVolumeHelper.CalculateSumVolume(newobj.transform.lossyScale, newobj.GetComponent<MeshFilter>());//this.cutCallback(Mathf.Min(obj1Volume, obj2Volume));//比较两者体积,较小的一个进行添加刚体 自由落体...if (obj1Volume < obj2Volume){//hitTarget物体掉落 消失Rigidbody rigidbody1 = hitTarget.gameObject.GetOrAddComponent<Rigidbody>();rigidbody1.AddForce((hitTarget.InverseTransformDirection(cutNormalDir) * force), ForceMode.Impulse); //可去掉这个力            CutObjectDestroyBySelf cutObjectDestroyBySelf = hitTarget.gameObject.GetComponent<CutObjectDestroyBySelf>();if (!cutObjectDestroyBySelf){cutObjectDestroyBySelf = hitTarget.gameObject.AddComponent<CutObjectDestroyBySelf>();cutObjectDestroyBySelf.time = destroyTime;}cutObjectDestroyBySelf.SetMat(dissolveMat);Rigidbody newobjRigidbody = newobj.GetComponent<Rigidbody>();if (newobjRigidbody){Destroy(newobjRigidbody);}lastCutObject = newobj;}else{Rigidbody rigidbody2 = newobj.GetOrAddComponent<Rigidbody>();rigidbody2.AddForce(-newobj.transform.InverseTransformDirection(cutNormalDir) * force, ForceMode.Impulse);//可去掉这个力            CutObjectDestroyBySelf cutObjectDestroyBySelf = newobj.GetComponent<CutObjectDestroyBySelf>();if (!cutObjectDestroyBySelf){cutObjectDestroyBySelf = newobj.AddComponent<CutObjectDestroyBySelf>();cutObjectDestroyBySelf.time = destroyTime;}cutObjectDestroyBySelf.SetMat(dissolveMat);Rigidbody hitTargetRigidbody = hitTarget.gameObject.GetComponent<Rigidbody>();if (hitTargetRigidbody){Destroy(hitTargetRigidbody);}lastCutObject = hitTarget.gameObject;}//Destroy(newobj, 5f);targetMeshVert.Dispose();targetMeshNormal.Dispose();targetMeshTriangles.Dispose();_tempVert1.Dispose();_tempNormal1.Dispose();_tempTriangles1.Dispose();_uvs1.Dispose();_tempVert2.Dispose();_tempNormal2.Dispose();_tempTriangles2.Dispose();_uvs2.Dispose();_temp1CountArray.Dispose();_temp2CountArray.Dispose();_tempIndex1.Dispose();_tempIndex2.Dispose();_localPos.Dispose();_allPos.Dispose();}else{triangles = targetMesh.triangles;for (int i = 0; i < triangles.Length; i += 3){int index1 = triangles[i];int index2 = triangles[i + 1];int index3 = triangles[i + 2];Vector3 vertex1 = targetMesh.vertices[index1];Vector3 vertex2 = targetMesh.vertices[index2];Vector3 vertex3 = targetMesh.vertices[index3];float vert1 = Vector3.Dot(cutNormalDir, (hitTarget.TransformPoint(vertex1) - hitPos).normalized);float vert2 = Vector3.Dot(cutNormalDir, (hitTarget.TransformPoint(vertex2) - hitPos).normalized);float vert3 = Vector3.Dot(cutNormalDir, (hitTarget.TransformPoint(vertex3) - hitPos).normalized);if (vert1 >= 0 && vert2 >= 0 && vert3 >= 0){//同面if (!tempIndex1.ContainsKey(index1)) //过滤相同顶点{tempVert1.Add(vertex1);tempNormal1.Add(targetMesh.normals[index1]);tempIndex1.Add(index1, tempVert1.Count - 1); //旧索引为key,新索引为value}if (!tempIndex1.ContainsKey(index2)) //过滤相同顶点{tempVert1.Add(vertex2);tempNormal1.Add(targetMesh.normals[index2]);tempIndex1.Add(index2, tempVert1.Count - 1); //旧索引为key,新索引为value}if (!tempIndex1.ContainsKey(index3)) //过滤相同顶点{tempVert1.Add(vertex3);tempNormal1.Add(targetMesh.normals[index3]);tempIndex1.Add(index3, tempVert1.Count - 1); //旧索引为key,新索引为value}tempTriangles1.Add(tempIndex1[index1]); //使用旧索引index1 获取对应的新索引tempTriangles1.Add(tempIndex1[index2]); //使用旧索引index2 获取对应的新索引tempTriangles1.Add(tempIndex1[index3]); //使用旧索引index3 获取对应的新索引if (tempIndex1[index1] == tempIndex1[index2] || tempIndex1[index1] == tempIndex1[index3] || tempIndex1[index2] == tempIndex1[index3]){Debug.LogError("[1]問題");}}else if (vert1 <= 0 && vert2 <= 0 && vert3 <= 0){//另一个同面                if (!tempIndex2.ContainsKey(index1)) //过滤相同顶点{tempVert2.Add(vertex1);tempNormal2.Add(targetMesh.normals[index1]);tempIndex2.Add(index1, tempVert2.Count - 1); //旧索引为key,新索引为value}if (!tempIndex2.ContainsKey(index2)) //过滤相同顶点{tempVert2.Add(vertex2);tempNormal2.Add(targetMesh.normals[index2]);tempIndex2.Add(index2, tempVert2.Count - 1); //旧索引为key,新索引为value}if (!tempIndex2.ContainsKey(index3)) //过滤相同顶点{tempVert2.Add(vertex3);tempNormal2.Add(targetMesh.normals[index3]);tempIndex2.Add(index3, tempVert2.Count - 1); //旧索引为key,新索引为value}tempTriangles2.Add(tempIndex2[index1]); //使用旧索引index1 获取对应的新索引tempTriangles2.Add(tempIndex2[index2]); //使用旧索引index2 获取对应的新索引tempTriangles2.Add(tempIndex2[index3]); //使用旧索引index3 获取对应的新索引if (tempIndex2[index1] == tempIndex2[index2] || tempIndex2[index1] == tempIndex2[index3] || tempIndex2[index2] == tempIndex2[index3]){Debug.LogError("[2]問題");}}else{//continue;localPos.Clear();//不同面情况 (PS:不存在3点不同面情况)bool isV1V2Sample = (vert1 > 0 && vert2 > 0 || vert1 < 0 && vert2 < 0);bool isV2V3Sample = (vert2 > 0 && vert3 > 0 || vert2 < 0 && vert3 < 0);bool isV3V1Sample = (vert3 > 0 && vert1 > 0 || vert3 < 0 && vert1 < 0);Vector3 normal = Vector3.Cross(vertex2 - vertex1, vertex3 - vertex2);//1. index1 和 index2 顶点不同面if (!(isV1V2Sample)){CaculateIntersectionPoint(vertex1, vertex2);}//2. index2 和 index3 顶点不同面if (!(isV2V3Sample)){CaculateIntersectionPoint(vertex2, vertex3);}//3. index3 和 index1 顶点不同面if (!(isV3V1Sample)){CaculateIntersectionPoint(vertex3, vertex1);}//此时localPos保存2个交点, allPos是保存所有交点的if (isV1V2Sample){if (vert1 > 0 && vert2 > 0){if (index1 == index2){Debug.LogError(">>>>>>>>>>>>>>>>>>>>>1 : " + index1);}ConnectPointToTriangle(index1, index2, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, index3);ConnectPointToTriangle(index3, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2);}else{if (index1 == index2){Debug.LogError(">>>>>>>>>>>>>>>>>>>>>2 : " + index1);}ConnectPointToTriangle(index1, index2, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, index3);ConnectPointToTriangle(index3, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1);}}if (isV2V3Sample){if (vert2 > 0 && vert3 > 0){if (index2 == index3){Debug.LogError(">>>>>>>>>>>>>>>>>>>>>3 : " + index2);}ConnectPointToTriangle(index2, index3, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, index1);ConnectPointToTriangle(index1, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2);}else{if (index2 == index3){Debug.LogError(">>>>>>>>>>>>>>>>>>>>>4 : " + index2);}ConnectPointToTriangle(index2, index3, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, index1);ConnectPointToTriangle(index1, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1);}}if (isV3V1Sample){if (vert3 > 0 && vert1 > 0){if (index3 == index1){Debug.LogError(">>>>>>>>>>>>>>>>>>>>>5 : " + index3);}ConnectPointToTriangle(index3, index1, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, index2);ConnectPointToTriangle(index2, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2);}else{if (index3 == index1){Debug.LogError(">>>>>>>>>>>>>>>>>>>>>6 : " + index3);}ConnectPointToTriangle(index3, index1, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, index2);ConnectPointToTriangle(index2, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1);}}}}//补全截面FillCutSurface();//注释下面两行代码,UV纹理全没但报错没了(目前UV组成有问题可能会导致模型某一面完全透明)//BUG已查明:上面代码生成新2个网格数据 会存在三角面的3个顶点位置数据一样,导致UV无法正常出现,因此此时简单地做个后处理 剔除这些异常三角面 ...      //bool isFilter1 = TempFuncFilterErrorTriangle(ref tempVert1, ref tempTriangles1, ref tempNormal1); //依然有问题 待修复...//bool isFilter2 = TempFuncFilterErrorTriangle(ref tempVert2, ref tempTriangles2, ref tempNormal2);CalculateUV(hitTarget.transform.localScale, tempVert1, tempTriangles1, tempNormal1, ref uvs1);CalculateUV(hitTarget.transform.localScale, tempVert2, tempTriangles2, tempNormal2, ref uvs2);//>>>>>>>>>>>>>>>>>>>>>>>>>>>>第一个切割出的物体开始构建Mesh originMesh = new Mesh(), newMesh = new Mesh();originMesh.vertices = tempVert1.ToArray();originMesh.normals = tempNormal1.ToArray();originMesh.triangles = tempTriangles1.ToArray();originMesh.uv = uvs1.ToArray();hitTarget.GetComponent<MeshFilter>().mesh = originMesh;Collider collider = hitTarget.GetComponent<Collider>();if (collider != null){Destroy(collider);}//hitTarget.gameObject.AddComponent<BoxCollider>();MeshCollider meshCollider = hitTarget.gameObject.AddComponent<MeshCollider>();hitTarget.gameObject.tag = TagName;hitTarget.gameObject.layer = 11;hitTarget.SetParent(generatePoint);float obj1Volume = CalculateVolumeHelper.CalculateSumVolume(hitTarget.transform.lossyScale, hitTarget.GetComponent<MeshFilter>());//>>>>>>>>>>>>>>>>>>>>>>>>>>切割出的第二个物体开始构建newMesh.vertices = tempVert2.ToArray();newMesh.normals = tempNormal2.ToArray();newMesh.triangles = tempTriangles2.ToArray();newMesh.uv = uvs2.ToArray();GameObject newobj = new GameObject();newobj.transform.position = hitTarget.position;newobj.transform.rotation = hitTarget.rotation;newobj.transform.localScale = hitTarget.localScale;newobj.AddComponent<MeshFilter>().mesh = newMesh;newobj.AddComponent<MeshRenderer>();Material mat = hitTarget.GetComponent<MeshRenderer>().material;newobj.GetComponent<MeshRenderer>().material = new Material(mat);newobj.tag = TagName;newobj.layer = 11;newobj.transform.SetParent(generatePoint);//顶点少的情况可以使用它 否则会报错超出顶点限制MeshCollider meshCollider2 = newobj.AddComponent<MeshCollider>();try{meshCollider.convex = true;meshCollider2.convex = true;}catch (Exception e){Debug.LogWarning(e.Message);}float obj2Volume = CalculateVolumeHelper.CalculateSumVolume(newobj.transform.lossyScale, newobj.GetComponent<MeshFilter>());//this.cutCallback(Mathf.Min(obj1Volume, obj2Volume));//比较两者体积,较小的一个进行添加刚体 自由落体...if (obj1Volume < obj2Volume){//hitTarget物体掉落 消失Rigidbody rigidbody1 = hitTarget.gameObject.GetOrAddComponent<Rigidbody>();rigidbody1.AddForce((hitTarget.InverseTransformDirection(cutNormalDir) * force), ForceMode.Impulse); //可去掉这个力            CutObjectDestroyBySelf cutObjectDestroyBySelf = hitTarget.gameObject.GetComponent<CutObjectDestroyBySelf>();if (!cutObjectDestroyBySelf){cutObjectDestroyBySelf = hitTarget.gameObject.AddComponent<CutObjectDestroyBySelf>();cutObjectDestroyBySelf.time = destroyTime;}cutObjectDestroyBySelf.SetMat(dissolveMat);Rigidbody newobjRigidbody = newobj.GetComponent<Rigidbody>();if (newobjRigidbody){Destroy(newobjRigidbody);}lastCutObject = newobj;}else{Rigidbody rigidbody2 = newobj.GetOrAddComponent<Rigidbody>();rigidbody2.AddForce(-newobj.transform.InverseTransformDirection(cutNormalDir) * force, ForceMode.Impulse);//可去掉这个力            CutObjectDestroyBySelf cutObjectDestroyBySelf = newobj.GetComponent<CutObjectDestroyBySelf>();if (!cutObjectDestroyBySelf){cutObjectDestroyBySelf = newobj.AddComponent<CutObjectDestroyBySelf>();cutObjectDestroyBySelf.time = destroyTime;}cutObjectDestroyBySelf.SetMat(dissolveMat);Rigidbody hitTargetRigidbody = hitTarget.gameObject.GetComponent<Rigidbody>();if (hitTargetRigidbody){Destroy(hitTargetRigidbody);}lastCutObject = hitTarget.gameObject;}//Destroy(newobj, 5f);}sw.Stop();Debug.Log($"代码块执行时间: {sw.ElapsedMilliseconds} 毫秒"); // 输出执行时间}bool IsEqualFloat(float a, float b){float num = a - b;if (num < 0.01f && num > -0.01f){return true;}return false;}bool IsEqualTwoPoint(Vector3 a, Vector3 b){if (IsEqualFloat(a.x, b.x) && IsEqualFloat(a.y, b.y) && IsEqualFloat(a.z, b.z)){return true;}return false;}//临时方法过滤异常三角面bool TempFuncFilterErrorTriangle(ref List<Vector3> vertices, ref List<int> triangles, ref List<Vector3> normals){bool isFilter = false;List<Vector3> newVertices = new List<Vector3>();List<int> newTriangles = new List<int>();List<Vector3> newNormals = new List<Vector3>();int index = 0;//剔除三角面的3个顶点位置数据全相同的三角面for (int i = 0; i < triangles.Count / 3; i++){int i0 = triangles[i * 3];int i1 = triangles[i * 3 + 1];int i2 = triangles[i * 3 + 2];Vector3 v0 = vertices[i0];Vector3 v1 = vertices[i1];Vector3 v2 = vertices[i2];if (IsEqualTwoPoint(v0, v1) || IsEqualTwoPoint(v1, v2) || IsEqualTwoPoint(v2, v0)){isFilter = true;//Debug.Log("有过滤!");}else{newVertices.Add(v0);newVertices.Add(v1);newVertices.Add(v2);newTriangles.Add(index++);newTriangles.Add(index++);newTriangles.Add(index++);newNormals.Add(normals[i0]);newNormals.Add(normals[i1]);newNormals.Add(normals[i2]);}}//if (isFilter)//{//    Debug.Log(vertices.Count + ", " + newVertices.Count + "," + triangles.Count + "," + normals.Count);//}vertices = newVertices;triangles = newTriangles;normals = newNormals;return isFilter;}//计算交点void CaculateIntersectionPoint(Vector3 v1, Vector3 v2){Vector3 localIntersectionPointPos = hitTarget.InverseTransformPoint(MathHelper.GetIntersectionPoint(cutNormalDir, hitPos, hitTarget.TransformPoint(v1), hitTarget.TransformPoint(v2)));localPos.Add(localIntersectionPointPos);allPos.Add(localIntersectionPointPos);}void ConnectPointToTriangle(int index, Vector3 p1, Vector3 p2, Vector3 normal, ref List<Vector3> tempVert, ref List<Vector3> tempNormal, ref List<int> tempTriangle, ref Dictionary<int, int> tempIndex){//可能还未添加if (!tempIndex.ContainsKey(index)){tempVert.Add(targetMesh.vertices[index]);tempNormal.Add(targetMesh.normals[index]);tempIndex.Add(index, tempVert.Count - 1);}Vector3 v = targetMesh.vertices[index];Vector3 vp1 = p1 - v;Vector3 p1p2 = p2 - p1;Vector3 p2v = v - p2;tempVert.Add(p1);int p1Index = tempVert.Count - 1;tempVert.Add(p2);int p2Index = tempVert.Count - 1;tempNormal.Add(targetMesh.normals[index]);tempNormal.Add(targetMesh.normals[index]);int vIndex = tempIndex[index];//v -> p1 -> p2if (Vector3.Dot(normal, Vector3.Cross(vp1, p1p2)) > 0){tempTriangle.Add(vIndex);tempTriangle.Add(p1Index);tempTriangle.Add(p2Index);}else{//p2 -> p1 -> vtempTriangle.Add(p2Index);tempTriangle.Add(p1Index);tempTriangle.Add(vIndex);}}void ConnectPointToTriangle(int index1, int index2, Vector3 p1, Vector3 p2, Vector3 normal, ref List<Vector3> tempVert, ref List<Vector3> tempNormal, ref List<int> tempTriangle, ref Dictionary<int, int> tempIndex, int index3){//可能还未添加if (!tempIndex.ContainsKey(index1)){tempVert.Add(targetMesh.vertices[index1]);tempNormal.Add(targetMesh.normals[index1]);tempIndex.Add(index1, tempVert.Count - 1);}if (!tempIndex.ContainsKey(index2)){tempVert.Add(targetMesh.vertices[index2]);tempNormal.Add(targetMesh.normals[index2]);tempIndex.Add(index2, tempVert.Count - 1);}//1.切割点放入tempVert tempNormaltempVert.Add(p1);int p1Index = tempVert.Count - 1;tempVert.Add(p2);int p2Index = tempVert.Count - 1;tempNormal.Add(targetMesh.normals[index1]);tempNormal.Add(targetMesh.normals[index2]);Vector3 v1 = targetMesh.vertices[index1];Vector3 v2 = targetMesh.vertices[index2];//试错方式进行连接Vector3 v1v2 = v2 - v1;Vector3 v2p1 = p1 - v2;Vector3 p1v1 = v1 - p1;//说明是正确的顺时针if (Vector3.Dot(normal, Vector3.Cross(v1v2, v2p1)) > 0){//获取到真正的索引int v1Index = tempIndex[index1];int v2Index = tempIndex[index2];//v1->v2->p1tempTriangle.Add(v1Index);tempTriangle.Add(v2Index);tempTriangle.Add(p1Index);if (v1Index == v2Index || v1Index == p1Index || v2Index == p1Index){Debug.LogError("if(v1Index == v2Index || v1Index == p1Index || v2Index == p1Index) 222");}//Vector3 //1. v2 -> p2, p2->p1 , p1 -> v2  //证明不与另一个三角面相交if (!MathHelper.IsIntectsect(v2, p2, v1, v2) && !MathHelper.IsIntectsect(p2, p1, v1, v2) && !MathHelper.IsIntectsect(p1, v2, v1, v2)&& !MathHelper.IsIntectsect(v2, p2, v2, p1) && !MathHelper.IsIntectsect(p2, p1, v2, p1) && !MathHelper.IsIntectsect(p1, v2, v2, p1)&& !MathHelper.IsIntectsect(v2, p2, p1, v1) && !MathHelper.IsIntectsect(p2, p1, p1, v1) && !MathHelper.IsIntectsect(p1, v2, p1, v1)){Vector3 _v2p2 = p2 - v2;Vector3 _p2p1 = p1 - p2;Vector3 _p1v2 = v2 - p1;//(v2 -> p2 -> p1)if (Vector3.Dot(normal, Vector3.Cross(_v2p2, _p2p1)) > 0){tempTriangle.Add(v2Index);tempTriangle.Add(p2Index);tempTriangle.Add(p1Index);if (v2Index == p2Index || p2Index == p1Index || p1Index == v2Index){Debug.LogError("if (v2Index == p2Index || p2Index == p1Index || p1Index == v2Index) 1111");}}else{//p1 -> p2 -> v2 (反转)tempTriangle.Add(p1Index);tempTriangle.Add(p2Index);tempTriangle.Add(v2Index);if (p1Index == p2Index || p2Index == v2Index || v2Index == p1Index){Debug.LogError(" if (p1Index == p2Index || p2Index == v2Index || v2Index == p1Index) 8888");}}}else if (!MathHelper.IsIntectsect(v1, p2, v1, v2) && !MathHelper.IsIntectsect(p2, p1, v1, v2) && !MathHelper.IsIntectsect(p1, v1, v1, v2)&& !MathHelper.IsIntectsect(v1, p2, v2, p1) && !MathHelper.IsIntectsect(p2, p1, v2, p1) && !MathHelper.IsIntectsect(p1, v1, v2, p1)&& !MathHelper.IsIntectsect(v1, p2, p1, v1) && !MathHelper.IsIntectsect(p2, p1, p1, v1) && !MathHelper.IsIntectsect(p1, v1, p1, v1)){//2. v1->p2, p2->p1, p1->v1Vector3 _v1p2 = p2 - v1;Vector3 _p2p1 = p1 - p2;Vector3 _p1v1 = v1 - p1;//(v1 -> p2 -> p1)if (Vector3.Dot(normal, Vector3.Cross(_v1p2, _p2p1)) > 0){tempTriangle.Add(v1Index);tempTriangle.Add(p2Index);tempTriangle.Add(p1Index);if (v1Index == p2Index || p2Index == p1Index || p1Index == v1Index){Debug.LogError(" if (p1Index == p2Index || p2Index == v2Index || v2Index == p1Index) 8888");}}else{//p1 -> p2 -> v1 (反转)tempTriangle.Add(p1Index);tempTriangle.Add(p2Index);tempTriangle.Add(v1Index);if (p1Index == p2Index || p2Index == v1Index || v1Index == p1Index){Debug.LogError("if (p1Index == p2Index || p2Index == v1Index || v1Index == p1Index) 66666");}}}else{//发现是p1,p2是相同的...v1,v2非常相近的情况 没有很大影响 暂时忽略Debug.Log(v1);Debug.Log(v2);Debug.Log(p1 + "\n");Debug.Log(v2);Debug.Log(p2);Debug.Log(p1 + "\n");Debug.Log(v1);Debug.Log(p2);Debug.Log(p1 + "\n");Debug.LogWarning("绝对不会出现的....但是出现过,具体原因未知"); //?? 一般切割2D的平面会出现这种情况,但实际每有很大影响 }}else{//Debug.DrawLine(v1, v2, Color.blue);//Debug.DrawLine(p1, p1, Color.green);//Debug.DrawLine(v2, p1, Color.red);//出现了v1和v2相同情况,p1和p2也相同..然后就这样了//确实是会存在顶点相同的情况这种情况无法构成面应该忽略!if (v1 == p1 || v2 == p1 || v1 == v2){//正常现象 或者你可以控制。。交点确实是有可能存在和v1,v2相同的情况...//Debug.LogError(">>>>111//正常现象 或者你可以控制。。交点确实是有可能存在和v1,v2相同的情况...");}else{//当点之间非常相近时会出现这种情况,暂时忽略。。 或者是API的问题//Debug.Log(index1);//Debug.Log(index2);//Debug.Log(index3);//Debug.Log("v1:" + v1 + "," + (v1 == p1));//Debug.Log("v2:" + v2 + "," + (v2 == p1));//Debug.Log("v3:" + targetMesh.vertices[index3]);//Debug.Log("p1:" + p1);//Debug.Log("p2:" + p2);//Debug.Log("Cross:" + Vector3.Dot(normal, Vector3.Cross(v1v2, v2p1)));p1 -> v2 -> v1 相反//Debug.LogWarning("从逻辑上看,不可能进入! 但是进去了 不知道为什么...");  //????}}}void FillCutSurface(){//Debug.Log("allPos.Count = " + allPos.Count);if (allPos.Count <= 0){//Debug.LogError("切割面的顶点全都没有..."); //?????? 算正常吧???....return;}Vector3 center = (allPos[0] + allPos[allPos.Count / 2]) * 0.5f;Vector3 normal = hitTarget.InverseTransformDirection(cutNormalDir);tempVert1.Add(center);int center1Index = tempVert1.Count - 1;tempNormal1.Add(-normal);tempVert2.Add(center);int center2Index = tempVert2.Count - 1;tempNormal2.Add(normal);for (int i = 0; i < allPos.Count; i += 2){//排除相同顶点的情况,只要有三角面2点位置相同,那就无法构成三角面 忽略...(不然会出问题)if (allPos[i] == allPos[i + 1] || allPos[i] == center || center == allPos[i + 1]){continue;}tempVert1.Add(allPos[i]);int tempVert1AllPos1Index = tempVert1.Count - 1;tempVert1.Add(allPos[i + 1]);int tempVert1AllPos2Index = tempVert1.Count - 1;tempNormal1.Add(-normal);tempNormal1.Add(-normal);Vector3 a1 = allPos[i] - center;//Vector3 a2 = allPos[i + 1] - allPos[i];Vector3 a2 = allPos[i + 1] - center;Vector3 crossA1A2 = Vector3.Cross(a1, a2);if (Vector3.Dot(-normal, crossA1A2) >= 0){tempTriangles1.Add(center1Index);tempTriangles1.Add(tempVert1AllPos1Index);tempTriangles1.Add(tempVert1AllPos2Index);if (center1Index == tempVert1AllPos1Index || center1Index == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index){Debug.Log(center + "," + allPos[i] + "," + allPos[i + 1]+ "\n " + tempVert1.LastIndexOf(center) + "," + tempVert1AllPos1Index + "," + tempVert1AllPos2Index + "," + allPos.Count);Debug.LogError("tempVert1.LastIndexOf(center) == tempVert1AllPos1Index || tempVert1.LastIndexOf(center) == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index 9999999999");}}else{tempTriangles1.Add(tempVert1AllPos2Index);tempTriangles1.Add(tempVert1AllPos1Index);tempTriangles1.Add(center1Index);if (center1Index == tempVert1AllPos1Index || center1Index == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index){Debug.Log(center + "," + allPos[i] + "," + allPos[i + 1] + "\n" +tempVert1.LastIndexOf(center) + "," + tempVert1AllPos1Index + "," + tempVert1AllPos2Index + "," + allPos.Count);Debug.LogError("if (tempVert1.LastIndexOf(center) == tempVert1AllPos1Index || tempVert1.LastIndexOf(center) == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index) -----------");}}tempVert2.Add(allPos[i]);int tempV1Index = tempVert2.Count - 1;tempVert2.Add(allPos[i + 1]);int tempV2Index = tempVert2.Count - 1;tempNormal2.Add(normal);tempNormal2.Add(normal);if (Vector3.Dot(normal, crossA1A2) >= 0){tempTriangles2.Add(center2Index);tempTriangles2.Add(tempV1Index);tempTriangles2.Add(tempV2Index);if (center2Index == tempV1Index || center2Index == tempV2Index || tempV1Index == tempV2Index){Debug.Log(center + "," + allPos[i] + "," + allPos[i + 1] + "\n" +tempVert2.LastIndexOf(center) + "," + tempV1Index + "," + tempV2Index + "," + allPos.Count);Debug.LogError("tempVert2.LastIndexOf(center) == tempV1Index || tempVert2.LastIndexOf(center) == tempV2Index || tempV1Index == tempV2Index /");}}else{tempTriangles2.Add(tempV2Index);tempTriangles2.Add(tempV1Index);tempTriangles2.Add(center2Index);if (center2Index == tempV1Index || center2Index == tempV2Index || tempV1Index == tempV2Index){Debug.Log(center + "," + allPos[i] + "," + allPos[i + 1] + "\n" +tempVert2.LastIndexOf(center) + "," + tempV1Index + "," + tempV2Index + "," + allPos.Count);Debug.LogError("tempVert2.LastIndexOf(center) == tempV1Index || tempVert2.LastIndexOf(center) == tempV2Index || tempV1Index == tempV2Index qqqqqqq");}}}}/// <summary>/// 计算Box的UV方法 /// </summary>void CalculateUV(Vector3 size, List<Vector3> vertices, List<int> triangles, List<Vector3> normals, ref List<Vector2> uvs){uvs = new List<Vector2>();for (int i = 0; i < vertices.Count; i++){uvs.Add(new Vector2(0, 0));}for (int i = 0; i < triangles.Count / 3; i++){int i0 = triangles[i * 3];int i1 = triangles[i * 3 + 1];int i2 = triangles[i * 3 + 2];//Vector3 v0 = vertices[i0] - center + size / 2f;//Vector3 v1 = vertices[i1] - center + size / 2f;//Vector3 v2 = vertices[i2] - center + size / 2f;Vector3 v0 = vertices[i0];Vector3 v1 = vertices[i1];Vector3 v2 = vertices[i2];string str = string.Format("原始数据:({0},{1},{2}) index:({3},{4},{5})", v0.ToString(), v1.ToString(), v2.ToString(), i0, i1, i2);// 除以size.x,y,z是为了缩小范围到[0,1] UV的范围v0 = new Vector3(v0.x / size.x, v0.y / size.y, v0.z / size.z);v1 = new Vector3(v1.x / size.x, v1.y / size.y, v1.z / size.z);v2 = new Vector3(v2.x / size.x, v2.y / size.y, v2.z / size.z);//Vector3 a = v0 - v1;//Vector3 b = v2 - v1; Vector3 a = v1 - v0;Vector3 b = v2 - v1;//我老感觉这法线计算错了...v0->v1-v2             Vector3 dir = normals[i0]; //Vector3.Cross(a, b);  //改用顶点法线作为法线float x = Mathf.Abs(Vector3.Dot(dir, Vector3.right));float y = Mathf.Abs(Vector3.Dot(dir, Vector3.up));float z = Mathf.Abs(Vector3.Dot(dir, Vector3.forward));//法线倾向于X轴,用Z作为X,Y作为Yif (x > y && x > z){uvs[i0] = new Vector2(v0.z, v0.y);uvs[i1] = new Vector2(v1.z, v1.y);uvs[i2] = new Vector2(v2.z, v2.y);}else if (y > z && y > x){//法线倾向于Y轴,用X作为X,Z作为Yuvs[i0] = new Vector2(v0.x, v0.z);uvs[i1] = new Vector2(v1.x, v1.z);uvs[i2] = new Vector2(v2.x, v2.z);}else if (z > x && z > y){//法线倾向于Z轴,用X作为X,Y作为Yuvs[i0] = new Vector2(v0.x, v0.y);uvs[i1] = new Vector2(v1.x, v1.y);uvs[i2] = new Vector2(v2.x, v2.y);}else{//防止出现UV不正常情况uvs[i0] = new Vector2(0, 0);uvs[i1] = new Vector2(1, 1);uvs[i2] = new Vector2(0, 0);//Debug.LogWarning("UV出问题啦..." + x + ", " + y + "," + z + "\n"//    + v0 + ", " + v1 + "," + v2 + " \n"//    + a + ", " + b + "\n"//    + dir + "\n"//    + str);//虽然已经处理了异常三角面,但仍然会出现(x,y,z)全为0的情况...先放任不管看看效果...}}}public void ClearCutInfos(){cutInfos?.Clear();}public void DestroyLastCutObject(){if (lastCutObject != null){Destroy(lastCutObject);}}public void SetLastCutObject(GameObject obj){lastCutObject = obj;}public void AddCutCallback(Action<float> action){cutCallback = action;}
}
using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;[BurstCompile]
public struct CutJob : IJob
{public int targetMeshTriangles_Count;public NativeArray<Vector3> targetMeshVert;public NativeArray<Vector3> targetMeshNormal;public NativeArray<int> targetMeshTriangles;public Matrix4x4 hitTarget_o2w;public Matrix4x4 hitTarget_w2o;public Vector3 hitTarget_LocalScale;int localPos_Count;int allPos_Count;public NativeArray<Vector3> localPos;public NativeArray<Vector3> allPos;public int tempVert1_Count;public int tempNormal1_Count;public int tempTriangles1_Count;public int uvs1_Count;public NativeArray<Vector3> tempVert1;public NativeArray<Vector3> tempNormal1;public NativeArray<int> tempTriangles1;public NativeHashMap<int, int> tempIndex1;public NativeArray<Vector2> uvs1;public NativeArray<int> temp1CountArray;public int tempVert2_Count;public int tempNormal2_Count;public int tempTriangles2_Count;public int uvs2_Count;public NativeArray<Vector3> tempVert2;public NativeArray<Vector3> tempNormal2;public NativeArray<int> tempTriangles2;public NativeHashMap<int, int> tempIndex2;public NativeArray<Vector2> uvs2;public NativeArray<int> temp2CountArray;public Vector3 hitPos;public Vector3 dir;public Vector3 cutVerticalDir;public Vector3 cutNormalDir;public void Execute(){Cutting();}#region 切割void Cutting(){//tempVert1.Clear();//tempNormal1.Clear();//tempTriangles1.Clear();//tempIndex1.Clear();//uvs1.Clear();//tempVert2.Clear();//tempNormal2.Clear();//tempTriangles2.Clear();//tempIndex2.Clear();//uvs2.Clear();//allPos.Clear();for (int i = 0; i < targetMeshTriangles.Length; i += 3){int index1 = targetMeshTriangles[i];int index2 = targetMeshTriangles[i + 1];int index3 = targetMeshTriangles[i + 2];Vector3 vertex1 = targetMeshVert[index1];Vector3 vertex2 = targetMeshVert[index2];Vector3 vertex3 = targetMeshVert[index3];float vert1 = Vector3.Dot(cutNormalDir, (hitTarget_o2w.MultiplyPoint(vertex1) - hitPos).normalized);float vert2 = Vector3.Dot(cutNormalDir, (hitTarget_o2w.MultiplyPoint(vertex2) - hitPos).normalized);float vert3 = Vector3.Dot(cutNormalDir, (hitTarget_o2w.MultiplyPoint(vertex3) - hitPos).normalized);if (vert1 >= 0 && vert2 >= 0 && vert3 >= 0){//同面if (!tempIndex1.ContainsKey(index1)) //过滤相同顶点{tempVert1[tempVert1_Count++] = vertex1;tempNormal1[tempNormal1_Count++] = targetMeshNormal[index1];tempIndex1.Add(index1, tempVert1_Count - 1); //旧索引为key,新索引为value}if (!tempIndex1.ContainsKey(index2)) //过滤相同顶点{tempVert1[tempVert1_Count++] = vertex2;tempNormal1[tempNormal1_Count++] = targetMeshNormal[index2];tempIndex1.Add(index2, tempVert1_Count - 1); //旧索引为key,新索引为value}if (!tempIndex1.ContainsKey(index3)) //过滤相同顶点{tempVert1[tempVert1_Count++] = vertex3;tempNormal1[tempNormal1_Count++] = targetMeshNormal[index3];tempIndex1.Add(index3, tempVert1_Count - 1); //旧索引为key,新索引为value}tempTriangles1[tempTriangles1_Count++] = tempIndex1[index1]; //使用旧索引index1 获取对应的新索引tempTriangles1[tempTriangles1_Count++] = tempIndex1[index2]; //使用旧索引index2 获取对应的新索引tempTriangles1[tempTriangles1_Count++] = tempIndex1[index3]; //使用旧索引index3 获取对应的新索引if (tempIndex1[index1] == tempIndex1[index2] || tempIndex1[index1] == tempIndex1[index3] || tempIndex1[index2] == tempIndex1[index3]){//Debug.LogError("[1]問題");}}else if (vert1 <= 0 && vert2 <= 0 && vert3 <= 0){//另一个同面                if (!tempIndex2.ContainsKey(index1)) //过滤相同顶点{tempVert2[tempVert2_Count++] = (vertex1);tempNormal2[tempNormal2_Count++] = (targetMeshNormal[index1]);tempIndex2.Add(index1, tempVert2_Count - 1); //旧索引为key,新索引为value}if (!tempIndex2.ContainsKey(index2)) //过滤相同顶点{tempVert2[tempVert2_Count++] = (vertex2);tempNormal2[tempNormal2_Count++] = (targetMeshNormal[index2]);tempIndex2.Add(index2, tempVert2_Count - 1); //旧索引为key,新索引为value}if (!tempIndex2.ContainsKey(index3)) //过滤相同顶点{tempVert2[tempVert2_Count++] = (vertex3);tempNormal2[tempNormal2_Count++] = (targetMeshNormal[index3]);tempIndex2.Add(index3, tempVert2_Count - 1); //旧索引为key,新索引为value}tempTriangles2[tempTriangles2_Count++] = (tempIndex2[index1]); //使用旧索引index1 获取对应的新索引tempTriangles2[tempTriangles2_Count++] = (tempIndex2[index2]); //使用旧索引index2 获取对应的新索引tempTriangles2[tempTriangles2_Count++] = (tempIndex2[index3]); //使用旧索引index3 获取对应的新索引if (tempIndex2[index1] == tempIndex2[index2] || tempIndex2[index1] == tempIndex2[index3] || tempIndex2[index2] == tempIndex2[index3]){//Debug.LogError("[2]問題");}}else{//continue;localPos_Count = 0;//不同面情况 (PS:不存在3点不同面情况)bool isV1V2Sample = (vert1 > 0 && vert2 > 0 || vert1 < 0 && vert2 < 0);bool isV2V3Sample = (vert2 > 0 && vert3 > 0 || vert2 < 0 && vert3 < 0);bool isV3V1Sample = (vert3 > 0 && vert1 > 0 || vert3 < 0 && vert1 < 0);Vector3 normal = Vector3.Cross(vertex2 - vertex1, vertex3 - vertex2);//1. index1 和 index2 顶点不同面if (!(isV1V2Sample)){CaculateIntersectionPoint(vertex1, vertex2);}//2. index2 和 index3 顶点不同面if (!(isV2V3Sample)){CaculateIntersectionPoint(vertex2, vertex3);}//3. index3 和 index1 顶点不同面if (!(isV3V1Sample)){CaculateIntersectionPoint(vertex3, vertex1);}//此时localPos保存2个交点, allPos是保存所有交点的if (isV1V2Sample){if (vert1 > 0 && vert2 > 0){if (index1 == index2){//Debug.LogError(">>>>>>>>>>>>>>>>>>>>>1 : " + index1);}ConnectPointToTriangle(index1, index2, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, index3, ref tempVert1_Count, ref tempNormal1_Count, ref tempTriangles1_Count);ConnectPointToTriangle(index3, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, ref tempVert2_Count, ref tempNormal2_Count, ref tempTriangles2_Count);}else{if (index1 == index2){//Debug.LogError(">>>>>>>>>>>>>>>>>>>>>2 : " + index1);}ConnectPointToTriangle(index1, index2, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, index3, ref tempVert2_Count, ref tempNormal2_Count, ref tempTriangles2_Count);ConnectPointToTriangle(index3, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, ref tempVert1_Count, ref tempNormal1_Count, ref tempTriangles1_Count);}}if (isV2V3Sample){if (vert2 > 0 && vert3 > 0){if (index2 == index3){//Debug.LogError(">>>>>>>>>>>>>>>>>>>>>3 : " + index2);}ConnectPointToTriangle(index2, index3, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, index1, ref tempVert1_Count, ref tempNormal1_Count, ref tempTriangles1_Count);ConnectPointToTriangle(index1, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, ref tempVert2_Count, ref tempNormal2_Count, ref tempTriangles2_Count);}else{if (index2 == index3){//Debug.LogError(">>>>>>>>>>>>>>>>>>>>>4 : " + index2);}ConnectPointToTriangle(index2, index3, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, index1, ref tempVert2_Count, ref tempNormal2_Count, ref tempTriangles2_Count);ConnectPointToTriangle(index1, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, ref tempVert1_Count, ref tempNormal1_Count, ref tempTriangles1_Count);}}if (isV3V1Sample){if (vert3 > 0 && vert1 > 0){if (index3 == index1){//Debug.LogError(">>>>>>>>>>>>>>>>>>>>>5 : " + index3);}ConnectPointToTriangle(index3, index1, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, index2, ref tempVert1_Count, ref tempNormal1_Count, ref tempTriangles1_Count);ConnectPointToTriangle(index2, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, ref tempVert2_Count, ref tempNormal2_Count, ref tempTriangles2_Count);}else{if (index3 == index1){//Debug.LogError(">>>>>>>>>>>>>>>>>>>>>6 : " + index3);}ConnectPointToTriangle(index3, index1, localPos[0], localPos[1], normal, ref tempVert2, ref tempNormal2, ref tempTriangles2, ref tempIndex2, index2, ref tempVert2_Count, ref tempNormal2_Count, ref tempTriangles2_Count);ConnectPointToTriangle(index2, localPos[0], localPos[1], normal, ref tempVert1, ref tempNormal1, ref tempTriangles1, ref tempIndex1, ref tempVert1_Count, ref tempNormal1_Count, ref tempTriangles1_Count);}}}}//补全截面FillCutSurface();//注释下面两行代码,UV纹理全没但报错没了(目前UV组成有问题可能会导致模型某一面完全透明)//BUG已查明:上面代码生成新2个网格数据 会存在三角面的3个顶点位置数据一样,导致UV无法正常出现,因此此时简单地做个后处理 剔除这些异常三角面 ...      //bool isFilter1 = TempFuncFilterErrorTriangle(ref tempVert1, ref tempTriangles1, ref tempNormal1); //依然有问题 待修复...//bool isFilter2 = TempFuncFilterErrorTriangle(ref tempVert2, ref tempTriangles2, ref tempNormal2);CalculateUV(hitTarget_LocalScale, ref tempVert1, ref tempTriangles1, ref tempNormal1, ref uvs1, ref tempVert1_Count, ref tempTriangles1_Count, ref tempNormal1_Count, ref uvs1_Count);CalculateUV(hitTarget_LocalScale, ref tempVert2, ref tempTriangles2, ref tempNormal2, ref uvs2, ref tempVert2_Count, ref tempTriangles2_Count, ref tempNormal2_Count, ref uvs2_Count);temp1CountArray[0] = tempVert1_Count;temp1CountArray[1] = tempNormal1_Count;temp1CountArray[2] = tempTriangles1_Count;temp1CountArray[3] = uvs1_Count;temp2CountArray[0] = tempVert2_Count;temp2CountArray[1] = tempNormal2_Count;temp2CountArray[2] = tempTriangles2_Count;temp2CountArray[3] = uvs2_Count;}bool IsEqualFloat(float a, float b){float num = a - b;if (num < 0.01f && num > -0.01f){return true;}return false;}//计算交点void CaculateIntersectionPoint(Vector3 v1, Vector3 v2){Vector3 localIntersectionPointPos = hitTarget_w2o.MultiplyPoint(MathHelper.GetIntersectionPoint(cutNormalDir, hitPos, hitTarget_o2w.MultiplyPoint(v1), hitTarget_o2w.MultiplyPoint(v2)));localPos[localPos_Count++] = (localIntersectionPointPos);allPos[allPos_Count++] = (localIntersectionPointPos);}void ConnectPointToTriangle(int index, Vector3 p1, Vector3 p2, Vector3 normal, ref NativeArray<Vector3> tempVert, ref NativeArray<Vector3> tempNormal, ref NativeArray<int> tempTriangle, ref NativeHashMap<int, int> tempIndex, ref int tempVertCount, ref int tempNormalCount, ref int tempTriangleCount){//可能还未添加if (!tempIndex.ContainsKey(index)){tempVert[tempVertCount++] = (targetMeshVert[index]);tempNormal[tempNormalCount++] = (targetMeshNormal[index]);tempIndex.Add(index, tempVertCount - 1);}Vector3 v = targetMeshVert[index];Vector3 vp1 = p1 - v;Vector3 p1p2 = p2 - p1;Vector3 p2v = v - p2;tempVert[tempVertCount++] = (p1);int p1Index = tempVertCount - 1;tempVert[tempVertCount++] = (p2);int p2Index = tempVertCount - 1;tempNormal[tempNormalCount++] = (targetMeshNormal[index]);tempNormal[tempNormalCount++] = (targetMeshNormal[index]);int vIndex = tempIndex[index];//v -> p1 -> p2if (Vector3.Dot(normal, Vector3.Cross(vp1, p1p2)) > 0){tempTriangle[tempTriangleCount++] = (vIndex);tempTriangle[tempTriangleCount++] = (p1Index);tempTriangle[tempTriangleCount++] = (p2Index);}else{//p2 -> p1 -> vtempTriangle[tempTriangleCount++] = (p2Index);tempTriangle[tempTriangleCount++] = (p1Index);tempTriangle[tempTriangleCount++] = (vIndex);}}void ConnectPointToTriangle(int index1, int index2, Vector3 p1, Vector3 p2, Vector3 normal, ref NativeArray<Vector3> tempVert, ref NativeArray<Vector3> tempNormal, ref NativeArray<int> tempTriangle, ref NativeHashMap<int, int> tempIndex, int index3, ref int tempVertCount, ref int tempNormalCount, ref int tempTriangleCount){//可能还未添加if (!tempIndex.ContainsKey(index1)){tempVert[tempVertCount++] = (targetMeshVert[index1]);tempNormal[tempNormalCount++] = (targetMeshNormal[index1]);tempIndex.Add(index1, tempVertCount - 1);}if (!tempIndex.ContainsKey(index2)){tempVert[tempVertCount++] = (targetMeshVert[index2]);tempNormal[tempNormalCount++] = (targetMeshNormal[index2]);tempIndex.Add(index2, tempVertCount - 1);}//1.切割点放入tempVert tempNormaltempVert[tempVertCount++] = (p1);int p1Index = tempVertCount - 1;tempVert[tempVertCount++] = (p2);int p2Index = tempVertCount - 1;tempNormal[tempNormalCount++] = (targetMeshNormal[index1]);tempNormal[tempNormalCount++] = (targetMeshNormal[index2]);Vector3 v1 = targetMeshVert[index1];Vector3 v2 = targetMeshVert[index2];//试错方式进行连接Vector3 v1v2 = v2 - v1;Vector3 v2p1 = p1 - v2;Vector3 p1v1 = v1 - p1;//说明是正确的顺时针if (Vector3.Dot(normal, Vector3.Cross(v1v2, v2p1)) > 0){//获取到真正的索引int v1Index = tempIndex[index1];int v2Index = tempIndex[index2];//v1->v2->p1tempTriangle[tempTriangleCount++] = (v1Index);tempTriangle[tempTriangleCount++] = (v2Index);tempTriangle[tempTriangleCount++] = (p1Index);if (v1Index == v2Index || v1Index == p1Index || v2Index == p1Index){//Debug.LogError("if(v1Index == v2Index || v1Index == p1Index || v2Index == p1Index) 222");}//Vector3 //1. v2 -> p2, p2->p1 , p1 -> v2  //证明不与另一个三角面相交if (!MathHelper.IsIntectsect(v2, p2, v1, v2) && !MathHelper.IsIntectsect(p2, p1, v1, v2) && !MathHelper.IsIntectsect(p1, v2, v1, v2)&& !MathHelper.IsIntectsect(v2, p2, v2, p1) && !MathHelper.IsIntectsect(p2, p1, v2, p1) && !MathHelper.IsIntectsect(p1, v2, v2, p1)&& !MathHelper.IsIntectsect(v2, p2, p1, v1) && !MathHelper.IsIntectsect(p2, p1, p1, v1) && !MathHelper.IsIntectsect(p1, v2, p1, v1)){Vector3 _v2p2 = p2 - v2;Vector3 _p2p1 = p1 - p2;Vector3 _p1v2 = v2 - p1;//(v2 -> p2 -> p1)if (Vector3.Dot(normal, Vector3.Cross(_v2p2, _p2p1)) > 0){tempTriangle[tempTriangleCount++] = (v2Index);tempTriangle[tempTriangleCount++] = (p2Index);tempTriangle[tempTriangleCount++] = (p1Index);if (v2Index == p2Index || p2Index == p1Index || p1Index == v2Index){//Debug.LogError("if (v2Index == p2Index || p2Index == p1Index || p1Index == v2Index) 1111");}}else{//p1 -> p2 -> v2 (反转)tempTriangle[tempTriangleCount++] = (p1Index);tempTriangle[tempTriangleCount++] = (p2Index);tempTriangle[tempTriangleCount++] = (v2Index);if (p1Index == p2Index || p2Index == v2Index || v2Index == p1Index){//Debug.LogError(" if (p1Index == p2Index || p2Index == v2Index || v2Index == p1Index) 8888");}}}else if (!MathHelper.IsIntectsect(v1, p2, v1, v2) && !MathHelper.IsIntectsect(p2, p1, v1, v2) && !MathHelper.IsIntectsect(p1, v1, v1, v2)&& !MathHelper.IsIntectsect(v1, p2, v2, p1) && !MathHelper.IsIntectsect(p2, p1, v2, p1) && !MathHelper.IsIntectsect(p1, v1, v2, p1)&& !MathHelper.IsIntectsect(v1, p2, p1, v1) && !MathHelper.IsIntectsect(p2, p1, p1, v1) && !MathHelper.IsIntectsect(p1, v1, p1, v1)){//2. v1->p2, p2->p1, p1->v1Vector3 _v1p2 = p2 - v1;Vector3 _p2p1 = p1 - p2;Vector3 _p1v1 = v1 - p1;//(v1 -> p2 -> p1)if (Vector3.Dot(normal, Vector3.Cross(_v1p2, _p2p1)) > 0){tempTriangle[tempTriangleCount++] = (v1Index);tempTriangle[tempTriangleCount++] = (p2Index);tempTriangle[tempTriangleCount++] = (p1Index);if (v1Index == p2Index || p2Index == p1Index || p1Index == v1Index){//Debug.LogError(" if (p1Index == p2Index || p2Index == v2Index || v2Index == p1Index) 8888");}}else{//p1 -> p2 -> v1 (反转)tempTriangle[tempTriangleCount++] = (p1Index);tempTriangle[tempTriangleCount++] = (p2Index);tempTriangle[tempTriangleCount++] = (v1Index);if (p1Index == p2Index || p2Index == v1Index || v1Index == p1Index){//Debug.LogError("if (p1Index == p2Index || p2Index == v1Index || v1Index == p1Index) 66666");}}}else{//发现是p1,p2是相同的...v1,v2非常相近的情况 没有很大影响 暂时忽略//Debug.Log(v1);//Debug.Log(v2);//Debug.Log(p1 + "\n");//Debug.Log(v2);//Debug.Log(p2);//Debug.Log(p1 + "\n");//Debug.Log(v1);//Debug.Log(p2);//Debug.Log(p1 + "\n");//Debug.LogWarning("绝对不会出现的....但是出现过,具体原因未知"); //?? 一般切割2D的平面会出现这种情况,但实际每有很大影响 }}else{//Debug.DrawLine(v1, v2, Color.blue);//Debug.DrawLine(p1, p1, Color.green);//Debug.DrawLine(v2, p1, Color.red);//出现了v1和v2相同情况,p1和p2也相同..然后就这样了//确实是会存在顶点相同的情况这种情况无法构成面应该忽略!if (v1 == p1 || v2 == p1 || v1 == v2){//正常现象 或者你可以控制。。交点确实是有可能存在和v1,v2相同的情况...//Debug.LogError(">>>>111//正常现象 或者你可以控制。。交点确实是有可能存在和v1,v2相同的情况...");}else{//当点之间非常相近时会出现这种情况,暂时忽略。。 或者是API的问题//Debug.Log(index1);//Debug.Log(index2);//Debug.Log(index3);//Debug.Log("v1:" + v1 + "," + (v1 == p1));//Debug.Log("v2:" + v2 + "," + (v2 == p1));//Debug.Log("v3:" + targetMeshVert[index3]);//Debug.Log("p1:" + p1);//Debug.Log("p2:" + p2);//Debug.Log("Cross:" + Vector3.Dot(normal, Vector3.Cross(v1v2, v2p1)));p1 -> v2 -> v1 相反//Debug.LogWarning("从逻辑上看,不可能进入! 但是进去了 不知道为什么...");  //????}}}void FillCutSurface(){//Debug.Log("allPos_Count = " + allPos_Count);if (allPos_Count <= 0){//Debug.LogError("切割面的顶点全都没有..."); //?????? 算正常吧???....return;}Vector3 center = (allPos[0] + allPos[allPos_Count / 2]) * 0.5f;Vector3 normal = hitTarget_w2o.MultiplyVector(cutNormalDir); // hitTarget.InverseTransformDirection(cutNormalDir);tempVert1[tempVert1_Count++] = (center);int center1Index = tempVert1_Count - 1;tempNormal1[tempNormal1_Count++] = (-normal);tempVert2[tempVert2_Count++] = (center);int center2Index = tempVert2_Count - 1;tempNormal2[tempNormal2_Count++] = (normal);for (int i = 0; i < allPos_Count; i += 2){//排除相同顶点的情况,只要有三角面2点位置相同,那就无法构成三角面 忽略...(不然会出问题)if (allPos[i] == allPos[i + 1] || allPos[i] == center || center == allPos[i + 1]){continue;}tempVert1[tempVert1_Count++] = (allPos[i]);int tempVert1AllPos1Index = tempVert1_Count - 1;tempVert1[tempVert1_Count++] = (allPos[i + 1]);int tempVert1AllPos2Index = tempVert1_Count - 1;tempNormal1[tempNormal1_Count++] = (-normal);tempNormal1[tempNormal1_Count++] = (-normal);Vector3 a1 = allPos[i] - center;//Vector3 a2 = allPos[i + 1] - allPos[i];Vector3 a2 = allPos[i + 1] - center;Vector3 crossA1A2 = Vector3.Cross(a1, a2);if (Vector3.Dot(-normal, crossA1A2) >= 0){tempTriangles1[tempTriangles1_Count++] = (center1Index);tempTriangles1[tempTriangles1_Count++] = (tempVert1AllPos1Index);tempTriangles1[tempTriangles1_Count++] = (tempVert1AllPos2Index);if (center1Index == tempVert1AllPos1Index || center1Index == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index){//Debug.Log(//     center + "," + allPos[i] + "," + allPos[i + 1]//     + "\n " + tempVert1.IndexOf(center) + "," + tempVert1AllPos1Index + "," + tempVert1AllPos2Index + "," + allPos_Count);//Debug.LogError("tempVert1.LastIndexOf(center) == tempVert1AllPos1Index || tempVert1.LastIndexOf(center) == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index 9999999999");}}else{tempTriangles1[tempTriangles1_Count++] = (tempVert1AllPos2Index);tempTriangles1[tempTriangles1_Count++] = (tempVert1AllPos1Index);tempTriangles1[tempTriangles1_Count++] = (center1Index);if (center1Index == tempVert1AllPos1Index || center1Index == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index){//Debug.Log(//    center + "," + allPos[i] + "," + allPos[i + 1] + "\n" +//    tempVert1.IndexOf(center) + "," + tempVert1AllPos1Index + "," + tempVert1AllPos2Index + "," + allPos_Count);//Debug.LogError("if (tempVert1.LastIndexOf(center) == tempVert1AllPos1Index || tempVert1.LastIndexOf(center) == tempVert1AllPos2Index || tempVert1AllPos1Index == tempVert1AllPos2Index) -----------");}}tempVert2[tempVert2_Count++] = (allPos[i]);int tempV1Index = tempVert2_Count - 1;tempVert2[tempVert2_Count++] = (allPos[i + 1]);int tempV2Index = tempVert2_Count - 1;tempNormal2[tempNormal2_Count++] = (normal);tempNormal2[tempNormal2_Count++] = (normal);if (Vector3.Dot(normal, crossA1A2) >= 0){tempTriangles2[tempTriangles2_Count++] = (center2Index);tempTriangles2[tempTriangles2_Count++] = (tempV1Index);tempTriangles2[tempTriangles2_Count++] = (tempV2Index);if (center2Index == tempV1Index || center2Index == tempV2Index || tempV1Index == tempV2Index){//Debug.Log(//    center + "," + allPos[i] + "," + allPos[i + 1] + "\n" +//    tempVert2.IndexOf(center) + "," + tempV1Index + "," + tempV2Index + "," + allPos_Count);//Debug.LogError("tempVert2.LastIndexOf(center) == tempV1Index || tempVert2.LastIndexOf(center) == tempV2Index || tempV1Index == tempV2Index /");}}else{tempTriangles2[tempTriangles2_Count++] = (tempV2Index);tempTriangles2[tempTriangles2_Count++] = (tempV1Index);tempTriangles2[tempTriangles2_Count++] = (center2Index);if (center2Index == tempV1Index || center2Index == tempV2Index || tempV1Index == tempV2Index){//Debug.Log(//    center + "," + allPos[i] + "," + allPos[i + 1] + "\n" +//    tempVert2.IndexOf(center) + "," + tempV1Index + "," + tempV2Index + "," + allPos_Count);//Debug.LogError("tempVert2.LastIndexOf(center) == tempV1Index || tempVert2.LastIndexOf(center) == tempV2Index || tempV1Index == tempV2Index qqqqqqq");}}}}/// <summary>/// 计算Box的UV方法 /// </summary>void CalculateUV(Vector3 size, ref NativeArray<Vector3> vertices, ref NativeArray<int> triangles, ref NativeArray<Vector3> normals, ref NativeArray<Vector2> uvs, ref int verticesCount, ref int trianglesCount, ref int normalsCount, ref int uvsCount){for (int i = 0; i < verticesCount; i++){uvs[uvsCount++] = (new Vector2(0, 0));}for (int i = 0; i < trianglesCount / 3; i++){int i0 = triangles[i * 3];int i1 = triangles[i * 3 + 1];int i2 = triangles[i * 3 + 2];//Vector3 v0 = vertices[i0] - center + size / 2f;//Vector3 v1 = vertices[i1] - center + size / 2f;//Vector3 v2 = vertices[i2] - center + size / 2f;Vector3 v0 = vertices[i0];Vector3 v1 = vertices[i1];Vector3 v2 = vertices[i2];//string str = string.Format("原始数据:({0},{1},{2}) index:({3},{4},{5})", v0.ToString(), v1.ToString(), v2.ToString(), i0, i1, i2);// 除以size.x,y,z是为了缩小范围到[0,1] UV的范围v0 = new Vector3(v0.x / size.x, v0.y / size.y, v0.z / size.z);v1 = new Vector3(v1.x / size.x, v1.y / size.y, v1.z / size.z);v2 = new Vector3(v2.x / size.x, v2.y / size.y, v2.z / size.z);//Vector3 a = v0 - v1;//Vector3 b = v2 - v1; Vector3 a = v1 - v0;Vector3 b = v2 - v1;//我老感觉这法线计算错了...v0->v1-v2             Vector3 dir = normals[i0]; //Vector3.Cross(a, b);  //改用顶点法线作为法线float x = Mathf.Abs(Vector3.Dot(dir, Vector3.right));float y = Mathf.Abs(Vector3.Dot(dir, Vector3.up));float z = Mathf.Abs(Vector3.Dot(dir, Vector3.forward));//法线倾向于X轴,用Z作为X,Y作为Yif (x > y && x > z){uvs[i0] = new Vector2(v0.z, v0.y);uvs[i1] = new Vector2(v1.z, v1.y);uvs[i2] = new Vector2(v2.z, v2.y);}else if (y > z && y > x){//法线倾向于Y轴,用X作为X,Z作为Yuvs[i0] = new Vector2(v0.x, v0.z);uvs[i1] = new Vector2(v1.x, v1.z);uvs[i2] = new Vector2(v2.x, v2.z);}else if (z > x && z > y){//法线倾向于Z轴,用X作为X,Y作为Yuvs[i0] = new Vector2(v0.x, v0.y);uvs[i1] = new Vector2(v1.x, v1.y);uvs[i2] = new Vector2(v2.x, v2.y);}else{//防止出现UV不正常情况uvs[i0] = new Vector2(0, 0);uvs[i1] = new Vector2(1, 1);uvs[i2] = new Vector2(0, 0);//Debug.LogWarning("UV出问题啦..." + x + ", " + y + "," + z + "\n"//    + v0 + ", " + v1 + "," + v2 + " \n"//    + a + ", " + b + "\n"//    + dir + "\n"//    + str);//虽然已经处理了异常三角面,但仍然会出现(x,y,z)全为0的情况...先放任不管看看效果...}}}#endregion
}

相关文章:

【Unity3D】利用IJob、Burst优化处理切割物体

参考文章&#xff1a; 【Unity】切割网格 【Unity3D】ECS入门学习&#xff08;一&#xff09;导入及基础学习_unity ecs教程-CSDN博客 【Unity3D】ECS入门学习&#xff08;十二&#xff09;IJob、IJobFor、IJobParallelFor_unity ijobparallelfor-CSDN博客 工程资源地址&…...

初学stm32 --- ADC模拟/数字转换器工作原理

目录 常见的ADC类型 并联比较型工作示意图 逐次逼近型工作示意图 ADC的特性参数 STM32各系列ADC的主要特性 ADC框图简介 参考电压/模拟部分电压 输入通道&#xff08; F1为例&#xff09; 转换序列&#xff08;F1为例&#xff09; 规则组和注入组执行优先级对比 规则…...

【MySQL】SQL菜鸟教程(一)

1.常见命令 1.1 总览 命令作用SELECT从数据库中提取数据UPDATE更新数据库中的数据DELETE从数据库中删除数据INSERT INTO向数据库中插入新数据CREATE DATABASE创建新数据库ALTER DATABASE修改数据库CREATE TABLE创建新表ALTER TABLE变更数据表DROP TABLE删除表CREATE INDEX创建…...

BM25(Best Match 25):信息检索的排序函数;稠密矩阵检索技术:RoBERTa

BM25(Best Match 25):信息检索的排序函数 常用于搜索引擎等场景,以下是关于它检索的内容及举例说明: 一、BM25检索的内容 文本数据: 文档集合:可以是大量的网页、学术论文、新闻文章、书籍内容等各种形式的文本集合。例如,一个学术搜索引擎可能会使用BM25对包含数百万…...

高级软件工程-复习

高级软件工程复习 坐标国科大&#xff0c;下面是老师说的考试重点。 Ruby编程语言的一些特征需要了解要能读得懂Ruby程序Git的基本命令操作知道Rails的MVC工作机理需要清楚&#xff0c;Model, Controller, View各司什么职责明白BDD的User Story需要会写&#xff0c;SMART要求能…...

Java常用设计模式

单例模式 单例模式就是: 在程序运行期间, 某些类有且最多只有一个实例对象 饿汉模式(静态常量) 饥饿模式又称为饿汉模式, 指的是JVM在加载类的时候就完成类对象的创建 //饿汉式(静态常量) public class Singleton1 {//构造器私有化&#xff0c;外部不能newprivate Singleto…...

29.Java 集合线程安全(ArrayList 类线程安全问题处理方案、HashSet 、HashMap 类线程安全问题处理方案)

一、ArrayList 类线程安全问题 1、概述 ArrayList 类存在线程安全问题 2、异常演示 ListNoSafeTest.java&#xff0c;演示 ArrayList 类线程安全问题 package com.my.listsafe;import java.util.ArrayList; import java.util.UUID;public class ListNoSafeTest {public st…...

解锁企业数字化转型新力量:OpenCoze(开源扣子)

在当今数字化浪潮席卷之下&#xff0c;企业对于高效管理和协同运作的需求愈发迫切&#xff0c;而开源技术正逐渐成为众多企业破局的关键利器。今天&#xff0c;想给大家介绍一款极具潜力的开源项目 ——OpenCoze&#xff0c;中文名称 “开源扣子”。 一、OpenCoze 是什么&…...

Docker 使用Dockerfile创建镜像

创建并且生成镜像 在当前目录下创建一个名为Dockerfile文件 vi Dockerfile填入下面配置 # 使用 CentOS 作为基础镜像 FROM centos:7# 设置工作目录 WORKDIR /app# 复制项目文件到容器中 COPY bin/ /app/bin/ COPY config/ /app/config/ COPY lib/ /app/lib/ COPY plugin/ /a…...

linux网络 | https前置知识 | 数据加密与解密、数据摘要

前言:本节内容讲述https的相关内容。 https博主会着重讲解https如何让一个请求和一个响应能够安全的进行交互。 https博主将用两篇文章进行讲解。本篇是两篇中第一篇。会把http的安全问题引出来&#xff0c; 然后说一下https的基本解决方法。 下面废话不多说&#xff0c; 开始我…...

01 Oracle自学环境搭建(Windows系统)

1 Oracle12C安装 1.1 下载 官网地址&#xff1a;https://www.oracle.com/ 进入官网→Resource→Customer Downloads 如果没有登录&#xff0c;会提示登录后后才能下载 选择适合自己的版本&#xff08;我电脑是Windows系统 64位&#xff09; 选择需要的安装包进行下载 双击下载…...

负载均衡原理及算法

什么是负载均衡&#xff1f; 负载均衡 指的是将用户请求分摊到不同的服务器上处理&#xff0c;以提高系统整体的并发处理能力以及可靠性。负载均衡服务可以有由专门的软件或者硬件来完成&#xff0c;一般情况下&#xff0c;硬件的性能更好&#xff0c;软件的价格更便宜&#x…...

STM32第5章、IWDG

一、简介 IWDG&#xff1a;全称是Independent watchdog&#xff0c;即独立看门狗。本质上是一个能产生系统复位信号的计数器。 特性&#xff1a; 是一个递减计数器。 时钟信号由独立的RC振荡器提供&#xff0c;可在待机和停止模式下运行。 看门狗被激活后&#xff0c;当递减计…...

[python3]Uvicorn库

Uvicorn 是一个用于运行 ASGI&#xff08;Asynchronous Server Gateway Interface&#xff09;应用程序的轻量级服务器。ASGI 是 Python Web 应用程序接口的一种扩展&#xff0c;它不仅支持传统的同步 Web 请求处理&#xff0c;还支持异步请求处理、WebSockets 以及 HTTP/2。 h…...

openEuler 22.04使用yum源最快速度部署k8s 1.20集群

本文目的 openEuler的官方源里有kubernetes 1.20&#xff0c;使用yum源安装是最快部署一个k8s集群的办法 硬件环境 主机名系统架构ipmasteropenEuler release 22.03 (LTS-SP2)arm192.168.3.11edgeopenEuler release 22.03 (LTS-SP2)arm192.168.3.12deviceopenEuler release 22.…...

【深度学习】数据预处理

为了能用深度学习来解决现实世界的问题&#xff0c;我们经常从预处理原始数据开始&#xff0c; 而不是从那些准备好的张量格式数据开始。 在Python中常用的数据分析工具中&#xff0c;我们通常使用pandas软件包。 像庞大的Python生态系统中的许多其他扩展包一样&#xff0c;pan…...

Oracle:ORA-00904: “10“: 标识符无效报错详解

1.报错Oracle语句如下 SELECT YK_CKGY.ID,YK_CKGY.DJH,YK_CKGY.BLRQ,YK_CKGY.ZBRQ,YK_CKGY.SHRQ,YK_CKGY.YT,YK_CKGY.ZDR,YK_CKGY.SHR,YK_CKGY.BZ,YK_CKGY.JZRQ,YK_CKGY.ZT,YK_CKGY.CKLX,(case YK_CKGY.CKLXwhen 09 then药房调借when 02 then科室退药when 03 then损耗出库when…...

CentOS 7 下 Nginx 的详细安装与配置

1、安装方式 1.1、通过编译方式安装 下载Nginx1.16.1的安装包 https://nginx.org/download/nginx-1.16.1.tar.gz 下载后上传至/home目录下。 1.2、通过yum方式安装 这种方式安装更简单。 2、通过编译源码包安装Nginx 2.1、安装必要依赖 sudo yum -y install gcc gcc-c sudo…...

Vue.js:现代前端开发的灵活框架

大家好&#xff01;我是 [数擎 AI]&#xff0c;一位热爱探索新技术的前端开发者&#xff0c;在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情&#xff0c;欢迎关注我的文章&#xff0c;我们一起成长、进步&#xff01; 开发领域&#xff1a;前端开发 | A…...

VideoPlayer插件的功能和用法

文章目录 1. 概念介绍2. 使用方法2.1 实现步骤2.2 具体细节3. 示例代码4. 内容总结我们在上一章回中介绍了"如何获取文件类型"相关的内容,本章回中将介绍如何播放视频.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 播放视频是我们常用的功能,不过Flutter官方…...

GPT-SoVITS学习01

1.什么是TTS TTS&#xff08;Text-To-Speech&#xff09;这是一种文字转语音的语音合成。类似的还有SVC&#xff08;歌声转换&#xff09;、SVS&#xff08;歌声合成&#xff09;等。 2.配置要求 GPT-SoVITS对电脑配置有较高的要求。 训练&#xff1a;对于Windows电脑&#…...

C语言程序环境和预处理详解

本章重点&#xff1a; 程序的翻译环境 程序的执行环境 详解&#xff1a;C语言程序的编译链接 预定义符号介绍 预处理指令 #define 宏和函数的对比 预处理操作符#和##的介绍 命令定义 预处理指令 #include 预处理指令 #undef 条件编译 程序的翻译环境和执行环…...

DBeaver执行本地的sql语句文件避免直接在客户端运行卡顿

直接在客户端运行 SQL 语句和通过加载本地文件执行 SQL 语句可能会出现不同的性能表现&#xff0c;原因可能包括以下几点&#xff1a; 客户端资源使用&#xff1a; 当你在客户端界面直接输入和执行 SQL 语句时&#xff0c;客户端可能会消耗资源来维护用户界面、语法高亮、自动完…...

【Linux】5.Linux常见指令以及权限理解(3)

文章目录 3. Linux指令如何把自己的公网IP配置到XShell里面日志3.9 时间相关的指令3.10 Cal指令3.11 find指令&#xff1a;&#xff08;灰常重要&#xff09;3.12 grep指令3.13 zip/unzip指令&#xff1a;3.14 tar指令&#xff08;重要&#xff09;&#xff1a;打包/解包&#…...

QT鼠标、键盘事件

一、鼠标 鼠标点击 mousePressEvent 鼠标释放 mouseReleaseEvent 鼠标移动 mouseMoveEvent 鼠标双击 mouseDoubleClickEvent 鼠标滚轮 QWheelEvent 二、键盘 键盘按下 keyPressEvent 键盘松开keyReleaseEvent 一、鼠标 #include <QMouseEvent> 鼠标点击 mouse…...

LabVIEW启动时Access Violation 0xC0000005错误

问题描述 在启动LabVIEW时&#xff0c;可能出现程序崩溃并提示以下错误&#xff1a;Error 0xC0000005 (Access Violation) ​ Access Violation错误通常是由于权限不足、文件冲突或驱动问题引起的。以下是解决此问题的全面优化方案&#xff1a; 解决步骤 1. 以管理员身份运行…...

WPF中组件之间传递参数的方法研究

在 WPF (Windows Presentation Foundation) 中&#xff0c;组件&#xff08;或称为控件&#xff09;之间传递参数的方法有很多种。不同的传递方式适用于不同的应用场景&#xff0c;具体选择取决于应用需求、性能、可维护性等因素。以下是几种常见的传递参数的方法&#xff0c;并…...

本地大模型工具哪家强?对比Ollama、LocalLLM、LM Studio

前言 对于AIGC的初学者&#xff0c; 你一定想尝试在本地搭建一个私有的开源大模型&#xff0c;比如常见的chatglm、llama或者qwen。在实践过程你会发现&#xff0c;每个模型单独配置环境&#xff0c;下载模型文件&#xff0c;还要确保它们互不干扰。这不仅耗时耗力&#xff0c…...

dify 常见问题总结 2025 持续更新

任何 Dify 问题评论区留言。 问题总结 Q&#xff1a;模型在回答时出现异常情况该如何处理&#xff1f; A&#xff1a; 可以通过记录异常情况并分析其原因来进行处理。通常可以调整提示词、重新训练模型或增加异常处理机制来改进模型的表现。 关键词&#xff1a;提示词、模型、…...

贪心算法笔记

贪心算法笔记 大概内容 贪心就是对于一个问题有很多个步骤,我们在每一个步骤中都选取最优的那一个,最后得出答案。就是在一些函数中可行,但是有些比如二次函数,因为它的转折点不一定最优,就是不可行的。那么如何判断贪心呢?有这么几种 看时间复杂度,一般的就是 O ( n…...

切比雪夫插值

切比雪夫插值是一种基于切比雪夫节点的多项式插值方法&#xff0c;其优势是减少插值误差(特别是龙格现象&#xff1a;表现为高维插值时在边缘处插值误差骤增)。本文对其基本操作进行说明。 1. 切比雪夫节点 切比雪夫插值的核心是使用切比雪夫节点作为插值点。切比雪夫节点是切…...

西电-神经网络基础与应用-复习笔记

此为24年秋研究生课程复习笔记 导论 神经网络的研究方法分为 连接主义&#xff0c;生理学派&#xff0c;模拟神经计算。高度的并行、分布性&#xff0c;很强的鲁棒和容错性。便于实现人脑的感知功能(音频图像的识别和处理)。符号主义&#xff0c;心理学派&#xff0c;基于符号…...

【面试题】简单聊一下什么是云原生、什么是k8s、容器,容器与虚机相比优势

云原生&#xff08;Cloud Native&#xff09; 定义&#xff1a;云原生是一种构建和运行应用程序的方法&#xff0c;旨在充分利用云计算的优势。它涵盖了一系列技术和理念&#xff0c;包括容器化、微服务架构、自动化部署与管理等。特点&#xff1a;云原生应用程序被设计为可弹性…...

Vue 3 Diff 算法过程及基本实现方式

Vue 3 的 Diff 算法 Vue 3 使用的是一种高效的 DOM Diff 算法&#xff0c;主要用于在虚拟 DOM 树发生变化时&#xff0c;计算最小的操作以更新真实 DOM。相比 Vue 2&#xff0c;Vue 3 的 Diff 算法做了很多优化。 Diff 算法的背景与目的 虚拟 DOM 树的对比&#xff1a;在 Vue…...

EasyCVR视频汇聚平台如何配置webrtc播放地址?

EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。平台支持多协议接入&#xff0c;能将接入到视频流转码为多格式进行分发&#xff0c;包括RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、W…...

PowerApps助力PowerBI实现数据写回

原文发布日期: 2019-08-01 06:03:50 0000 注&#xff1a;本文旨在介绍Power BI如何利用PowerApps实现用户在前端对数据源进行增删查改&#xff0c;关于此&#xff0c;你也可以在Google上找到更详细但较零散的资料 正文 在SSAS多维数据集中&#xff0c;开发者可以给数据开启&q…...

数据结构:DisjointSet

Disjoint Sets意思是一系列没有重复元素的集合。一种常见的实现叫做&#xff0c;Disjoint-set Forest可以以接近常数的时间复杂度查询元素所属集合&#xff0c;用来确定两个元素是否同属一个集合等&#xff0c;是效率最高的常见数据结构之一。 Wiki链接&#xff1a;https://en…...

React 元素渲染

React 元素渲染 React 是一个用于构建用户界面的 JavaScript 库&#xff0c;它允许开发人员创建大型应用程序&#xff0c;这些应用程序可以随着时间的推移而高效地更新和渲染。React 的核心概念之一是元素渲染&#xff0c;它描述了如何将 JavaScript 对象转换为 DOM&#xff0…...

【Leetcode 每日一题】3270. 求出数字答案

问题背景 给你三个 正 整数 n u m 1 num_1 num1​&#xff0c; n u m 2 num_2 num2​ 和 n u m 3 num_3 num3​。 数字 n u m 1 num_1 num1​&#xff0c; n u m 2 num_2 num2​ 和 n u m 3 num_3 num3​ 的数字答案 k e y key key 是一个四位数&#xff0c;定义如下&…...

eNSP之家----ACL实验入门实例详解(Access Control List访问控制列表)(重要重要重要的事说三遍)

ACL实验&#xff08;Access Control List访问控制列表&#xff09;是一种基于包过滤的访问控制技术&#xff0c;它可以根据设定的条件对接口上的数据包进行过滤&#xff0c;允许其通过或丢弃。访问控制列表被广泛地应用于路由器和三层交换机。 准备工作 在eNSP里面部署设备&a…...

【git】-2 分支管理

目录 一、分支的概念 二、查看、创建、切换分支 1、查看分支-git branch 2、创建分支- git branch 分支名 3、切换分支- git checkout 分支名 三、git指针 -实现分支和版本间的切换 四、普通合并分支 git merge 文件名 五、冲突分支合并 ​​​​​​【git】-初始gi…...

硬件设计-齐纳管

目录 摘要 详情 齐纳管的工作电流、 摘要 齐纳管&#xff08;Zener Diode&#xff09;是一种特殊的二极管&#xff0c;它能够在特定的反向电压下保持电流稳定。正常情况下&#xff0c;二极管只允许正向电流通过&#xff0c;而阻止反向电流流过。而齐纳管在一定的反向电压下可…...

Github出现复杂问题 无法合并 分支冲突太多 如何复原

目录 问题再现 解决思路 当然我所指的是在 main 分支开一个新的分支 删除本地文件夹 重新克隆 开一个新分支 切换分支 下载远程分支 文件覆盖 合并到主分支 ​​​​​​​问题再现 太复杂了 无法更改 编译器现状 全部崩溃了 无法更改 即使创建一个新的分支也无济于…...

《分布式光纤传感:架设于桥梁监测领域的 “智慧光网” 》

桥梁作为交通基础设施的重要组成部分&#xff0c;其结构健康状况直接关系到交通运输的安全和畅通。随着桥梁建设规模的不断扩大和服役年限的增长&#xff0c;桥梁结构的安全隐患日益凸显&#xff0c;传统的监测方法已难以满足对桥梁结构健康实时、全面、准确监测的需求。分布式…...

java_抽象类最佳实践-模板设计模式

基本介绍 模板设计模式可解决的问题 最佳实践 Template类 package com.hspedu.abstract_; abstract public class Template { //抽象类-模板设计模式public abstract void job();//抽象方法public void calculateTime() {//实现方法&#xff0c;调用 job 方法//得到开始的时间…...

linux网络 | http结尾、理解长连接短链接与cookie

前言&#xff1a;本节是http章节的最后一部分&#xff0c;主要解释一些小概念。讲解到了HTTP的方法&#xff0c;表单&#xff0c; 重定向等等。 现在废话不多说&#xff0c; 开始我们的学习吧。 ps&#xff1a;本节内容都是概念&#xff0c; 知道就行&#xff0c; 友友们放心观…...

dtdug汇编指令练习

r 通用寄存器 m 代表内存 imm 代表立即数 r8 代表8位通用寄存器 m8 代表8位内存 imm8 代表8位立即数 mov指令练习 MOV 的语法: mov 目标操作数&#xff0c;源操作数 作用:拷贝源操作数到目标操作数 1、源操作数可以是立即数、通用寄存器、段寄存器、或者内存单元. 2、目标操作数…...

Windows自动化Python pyautogui RPA操作

依赖包 import time import pyautogui import pyperclip import os import psutil from pywinauto.application import Application睡眠&#xff1a; pyautogui.sleep(1)鼠标事件&#xff1a; pyautogui.moveTo(100, 100, duration0.25) pyautogui.click(100, 100, duration0.…...

Ollama私有化部署大语言模型LLM

目录 一、Ollama介绍 二、安装Ollama 1、标准安装 2、国内加速 三、升级Ollama版本 四、使用Ollama 1、启动ollama服务 systemctl start ollama.service ollama serve 2、使用ollama命令 ollama run 运行模型 ollama ps 查看正在运行的模型 ollama list 查看(本地)…...

ubuntu/kali安装c-jwt-cracker

1.下载安装包 可以去GitHub下载解压&#xff0c;我这直接在kali克隆下来了。&#xff08;网络不好可能克隆不下来&#xff09; git clone https://github.com/brendan-rius/c-jwt-cracker.git 2.如果下载的压缩包就需要进行解压&#xff0c;克隆的直接进入目录就好了。 unzi…...