unity---Mesh网格编程(六)

news/2024/10/17 23:30:34/

目录

1.模型切割

2.代码


1.模型切割

如图,对3D模型的Mesh网格进行切割,会经过若干个三角面。而切割后,将会产生新的面来组成左右两边的物体。

 要记录每个顶点与顶点下标,新的面要顺时针绘制,

 

 

2.代码

using System.Collections.Generic;
using UnityEngine;public class CutMode : MonoBehaviour
{private Material cutMaterial;private Vector3 _startPos;private Vector3 _endPos;private Vector3 _hitPos;private Vector3 _dir, _upDir, _planeNormal;private Mesh _mesh;private Transform _hitTrans;private MeshFilter _leftMeshFilter;/// 三角形三个顶点的坐标信息缓存(世界坐标)private Vector3[] _triangleTemp = new Vector3[3];/// 三角形三个点乘结果的缓存private float[] _resultTemp = new float[3];//左侧(和平面法向量同侧)模型数据private List<Vector3> _leftVertices = new List<Vector3>();private List<int> _leftTriangles = new List<int>();private List<Vector3> _leftNormals = new List<Vector3>();public List<Vector2> uvs_left;/// key:原模型顶点下标  value:现模型的顶点下标private Dictionary<int, int> _leftIndexMapping = new Dictionary<int, int>();//右侧(和平面法向量反向)模型数据private List<Vector3> _rightVertices = new List<Vector3>();private List<int> _rightTriangles = new List<int>();private List<Vector3> _rightNormals = new List<Vector3>();public List<Vector2> uvs_right;/// key:原模型顶点下标  value:现模型的顶点下标private Dictionary<int, int> _rightIndexMapping = new Dictionary<int, int>();/// 切面上新生成的顶点private List<Vector3> _rectionVertexs = new List<Vector3>();private void Update(){if (Input.GetMouseButtonDown(0)){_startPos = Input.mousePosition;}if (Input.GetMouseButtonUp(0)){_endPos = Input.mousePosition;Ray();}}private void Ray(){//两点间的中心点var center = (_endPos + _startPos) * 0.5f;var ray = Camera.main.ScreenPointToRay(center);RaycastHit hit;if (Physics.Raycast(ray, out hit)){_hitTrans = hit.transform;if (_hitTrans.tag.Equals("cutObj")){Debug.Log("我切到了" + _hitTrans.name);cutMaterial = _hitTrans.GetComponent<MeshRenderer>().materials[0];}else{return;}_hitPos = hit.point;_leftMeshFilter = _hitTrans.GetComponent<MeshFilter>();_mesh = hit.transform.GetComponent<MeshFilter>().mesh;//相机到物体的方向向量   Vector3.normalized归一化向量_dir = (hit.point - Camera.main.transform.position).normalized;//垂直于_dir的方向向量  Vector3.Dot(v1,v2)点乘--->计算v1在v2上的投影长度(标量)--法向量_upDir = (-_dir * Vector3.Dot(Vector3.up, _dir) + Vector3.up).normalized;//平面        Vector3.Cross叉乘_planeNormal = Vector3.Cross(_dir, _upDir);//计算滑动方向与角度Vector3 sildeDir = _endPos - _startPos;Vector3 baseDir = sildeDir.y < 0 ? -Vector3.up : Vector3.up;float angle = Vector3.Angle(sildeDir, baseDir);if (sildeDir.y < 0){angle = sildeDir.x > 0 ? angle : -angle;}else{angle = sildeDir.x > 0 ? -angle : angle;}//角度转弧度angle *= Mathf.Deg2Rad;//sin  cos 需传入的参数为弧度_upDir = _upDir * Mathf.Cos(angle) + _planeNormal * Mathf.Sin(angle);_planeNormal = Vector3.Cross(_dir, _upDir);Cut();}else{_hitPos = Vector3.zero;_mesh = null;}}private void Cut(){if (_mesh == null)return;ClearData();CalculateVertexInfo();GenerateSectionInfo();GenerateMesh();}private void ClearData(){_leftVertices.Clear();_leftTriangles.Clear();_leftNormals.Clear();_leftIndexMapping.Clear();_rightNormals.Clear();_rightTriangles.Clear();_rightVertices.Clear();_rightIndexMapping.Clear();_rectionVertexs.Clear();}private void GenerateMesh(){GenerateLeftMesh();GenerateRightMesh();}private void GenerateLeftMesh(){Mesh mesh = new Mesh();mesh.name = "leftMesh";mesh.vertices = _leftVertices.ToArray();mesh.triangles = _leftTriangles.ToArray();mesh.normals = _leftNormals.ToArray();//ToDo mesh.uv = _leftMeshFilter.mesh = mesh;}private int newObjNum = 0;private void GenerateRightMesh(){Mesh mesh = new Mesh();mesh.name = "rightMesh";mesh.vertices = _rightVertices.ToArray();mesh.normals = _rightNormals.ToArray();mesh.triangles = _rightTriangles.ToArray();//ToDo mesh.uv = GameObject newGo = new GameObject();newGo.name = "newObj" + newObjNum;newGo.transform.tag = "cutObj";newGo.transform.position = _hitTrans.position;newGo.transform.rotation = _hitTrans.rotation;newGo.AddComponent<MeshFilter>().mesh = mesh;//newGo.AddComponent<MeshRenderer>().material = _hitTrans.GetComponent<MeshRenderer>().material;newGo.AddComponent<MeshRenderer>().material = cutMaterial;newGo.AddComponent<Rigidbody>();newGo.AddComponent<MeshCollider>().convex = true;newObjNum++;}/// <summary>/// 分别计算并存储切开的两个部分的顶点信息/// </summary>private void CalculateVertexInfo(){var triangles = _mesh.triangles;for (int i = 0; i < triangles.Length; i += 3){//三个顶点在原triangles中下标是 i i+1 i+2GetDotResult(i, triangles);if (_resultTemp[0] >= 0 && _resultTemp[1] >= 0 && _resultTemp[2] >= 0){//左侧SaveOldVertex(i, true);}else if (_resultTemp[0] <= 0 && _resultTemp[1] <= 0 && _resultTemp[2] <= 0){//右侧SaveOldVertex(i, false);}else{//被切割的三角形部分int differentIndex = GetDifferentSidePointIndex();//当前点在triangles的下标int p0_Index = i + differentIndex;int p1_index = (differentIndex + 1) % 3 + i;//先算出c1点进行存储SavePointOnSection(_mesh.triangles[p0_Index], _mesh.triangles[p1_index]);int p2_index = (differentIndex + 2) % 3 + i;//再算出c2点进行存储SavePointOnSection(_mesh.triangles[p0_Index], _mesh.triangles[p2_index]);SaveCutTriangleVertex(_resultTemp[differentIndex], p0_Index, p1_index, p2_index);}}}private void SaveCutTriangleVertex(float result, int p0, int p1, int p2){if (result >= 0){SaveOldVertex(p0, _leftVertices, _leftNormals, _leftIndexMapping);SaveSectionVertexWithOnePoint(p0, _leftTriangles, _leftVertices, _leftNormals, _leftIndexMapping);SaveOldVertex(p1, _rightVertices, _rightNormals, _rightIndexMapping);SaveOldVertex(p2, _rightVertices, _rightNormals, _rightIndexMapping);SaveSectionVertexWithTwoPoint(p1, p2, _rightTriangles, _rightVertices, _rightNormals, _rightIndexMapping);}else{SaveOldVertex(p0, _rightVertices, _rightNormals, _rightIndexMapping);SaveSectionVertexWithOnePoint(p0, _rightTriangles, _rightVertices, _rightNormals, _rightIndexMapping);SaveOldVertex(p1, _leftVertices, _leftNormals, _leftIndexMapping);SaveOldVertex(p2, _leftVertices, _leftNormals, _leftIndexMapping);SaveSectionVertexWithTwoPoint(p1, p2, _leftTriangles, _leftVertices, _leftNormals, _leftIndexMapping);}}private void SaveSectionVertexWithOnePoint(int index,List<int> curTriangles,List<Vector3> curVertices,List<Vector3> curNormals,Dictionary<int, int> indexMapping){int vertexIndex = _mesh.triangles[index];//存储c1curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 2]);//存储c2curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 1]);curNormals.Add(_mesh.normals[vertexIndex]);curNormals.Add(_mesh.normals[vertexIndex]);curTriangles.Add(indexMapping[vertexIndex]);curTriangles.Add(curVertices.Count - 2);curTriangles.Add(curVertices.Count - 1);}private void SaveSectionVertexWithTwoPoint(int index1,int index2,List<int> curTriangles,List<Vector3> curVertices,List<Vector3> curNormals,Dictionary<int, int> indexMapping){int vertexIndex1 = _mesh.triangles[index1];int vertexIndex2 = _mesh.triangles[index2];//存储c1curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 2]);//存储c2curVertices.Add(_rectionVertexs[_rectionVertexs.Count - 1]);curNormals.Add(_mesh.normals[vertexIndex1]);curNormals.Add(_mesh.normals[vertexIndex2]);//c1-p1-p2curTriangles.Add(curVertices.Count - 2);curTriangles.Add(indexMapping[vertexIndex1]);curTriangles.Add(indexMapping[vertexIndex2]);//p2-c2-c1curTriangles.Add(indexMapping[vertexIndex2]);curTriangles.Add(curVertices.Count - 1);curTriangles.Add(curVertices.Count - 2);}/// <summary>/// 返回值是对应点在_resultTemp中的下标/// </summary>private int GetDifferentSidePointIndex(){List<int> temp1 = new List<int>(2);List<int> temp2 = new List<int>(2);for (int i = 0; i < _resultTemp.Length; i++){if (_resultTemp[i] > 0){temp1.Add(i);}else{temp2.Add(i);}}if (temp1.Count == 1){return temp1[0];}else{return temp2[0];}}//参数是 原模型vertices下标private void SavePointOnSection(int index1, int index2){Vector3 side = _mesh.vertices[index2] - _mesh.vertices[index1];//方向向量 --- 本地坐标系转世界坐标系Vector3 dir = _hitTrans.TransformDirection(side.normalized);Vector3 startPos = _hitTrans.TransformPoint(_mesh.vertices[index1]);float lengthOnNormal = Vector3.Dot(_hitPos, _planeNormal) - Vector3.Dot(startPos, _planeNormal);float length = lengthOnNormal / Vector3.Dot(dir, _planeNormal);Vector3 target = startPos + dir * length;_rectionVertexs.Add(_hitTrans.InverseTransformPoint(target));}private void GetDotResult(int index, int[] triangles){for (int i = 0; i < _triangleTemp.Length; i++){_triangleTemp[i] = _hitTrans.TransformPoint(_mesh.vertices[triangles[index + i]]);_resultTemp[i] = Vector3.Dot(_planeNormal, _triangleTemp[i] - _hitPos);}}private void SaveOldVertex(int index, bool isLeft){if (isLeft){SaveTriangleVertex(index, _leftTriangles, _leftVertices, _leftNormals, _leftIndexMapping);}else{SaveTriangleVertex(index, _rightTriangles, _rightVertices, _rightNormals, _rightIndexMapping);}}private void SaveTriangleVertex(int index,List<int> curTriangles,List<Vector3> curVertices,List<Vector3> curNormals,Dictionary<int, int> indexMapping){for (int i = 0; i < 3; i++){SaveOldVertex(index + i, curVertices, curNormals, indexMapping);curTriangles.Add(indexMapping[_mesh.triangles[index + i]]);}}private void SaveOldVertex(int index,List<Vector3> curVertices,List<Vector3> curNormals,Dictionary<int, int> indexMapping){int vertexIndex = _mesh.triangles[index];if (!indexMapping.ContainsKey(vertexIndex)){curVertices.Add(_mesh.vertices[vertexIndex]);curNormals.Add(_mesh.normals[vertexIndex]);indexMapping.Add(vertexIndex, curVertices.Count - 1);}}//生成切面信息private void GenerateSectionInfo(){Vector3 center = (_rectionVertexs[0] + _rectionVertexs[_rectionVertexs.Count / 2]) * 0.5f;Vector3 centerNormal = _hitTrans.InverseTransformDirection(_planeNormal);SaveSectionCenter(center, centerNormal);int leftCenterIndex = _leftVertices.Count - 1;int rightCenterIndex = _rightVertices.Count - 1;for (int i = 0; i < _rectionVertexs.Count; i += 2){Vector3 v1 = _rectionVertexs[i];Vector3 v2 = _rectionVertexs[i + 1];Vector3 normal = Vector3.Cross(v1 - center, v2 - center);SaveSectionVertexInfo(i, -centerNormal, _leftVertices, _leftNormals);SaveLeftSectionTriangle(_planeNormal, normal, leftCenterIndex, _leftTriangles, _leftVertices);SaveSectionVertexInfo(i, centerNormal, _rightVertices, _rightNormals);SaveRightSectionTriangle(_planeNormal, normal, rightCenterIndex, _rightTriangles, _rightVertices);}}private void SaveLeftSectionTriangle(Vector3 planeNormal, Vector3 normal, int centerIndex, List<int> triangles, List<Vector3> vertices){if (Vector3.Dot(planeNormal, normal) < 0){//左侧切面 三角形法向量方向和planeNormal方向相反,才能正常显示// 0 1 2triangles.Add(centerIndex);triangles.Add(vertices.Count - 2);triangles.Add(vertices.Count - 1);}else{// 0 2 1triangles.Add(centerIndex);triangles.Add(vertices.Count - 1);triangles.Add(vertices.Count - 2);}}private void SaveRightSectionTriangle(Vector3 planeNormal, Vector3 normal, int centerIndex, List<int> triangles, List<Vector3> vertices){if (Vector3.Dot(planeNormal, normal) > 0){//右侧切面 三角形法向量方向和planeNormal方向相同,才能正常显示// 0 1 2triangles.Add(centerIndex);triangles.Add(vertices.Count - 2);triangles.Add(vertices.Count - 1);}else{// 0 2 1triangles.Add(centerIndex);triangles.Add(vertices.Count - 1);triangles.Add(vertices.Count - 2);}}private void SaveSectionVertexInfo(int index, Vector3 normal, List<Vector3> vertices, List<Vector3> normals){vertices.Add(_rectionVertexs[index]);vertices.Add(_rectionVertexs[index + 1]);normals.Add(normal);normals.Add(normal);}private void SaveSectionCenter(Vector3 center, Vector3 normal){_leftVertices.Add(center);_leftNormals.Add(-normal);_rightVertices.Add(center);_rightNormals.Add(normal);}
}


http://www.ppmy.cn/news/784.html

相关文章

云服务连续三年增长150%,网宿科技开拓新赛道

摘要&#xff1a;开拓云服务市场&#xff0c;网宿科技的打法。 提到网宿科技&#xff0c;很多人还停留在传统IT服务商的印象中。其实&#xff0c;网宿科技已经在一条新赛道加速前行&#xff0c;这就是云服务。 “借助亚马逊云科技的持续赋能&#xff0c;网宿科技积累了丰富的云…

【数据结构趣味多】时间复杂度和空间复杂度

算法效率分析分为两种&#xff1a;第一种是时间效率&#xff0c;第二种是空间效率。时间效率被称为时间复杂度&#xff0c;而空间效率被称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度&#xff0c;而空间复杂度主要衡量一个算法所需要的额外空间&#xff0c; 在计…

React基础

文章目录1.简介1.1 react与vue1.1.1 相同点1.1.2 不同点1.1.3 函数式组件的特点&#xff08;什么是函数式组件&#xff09;a.幂等b.无副作用用&#xff1a;1.1.4 虚拟dom的作用1.1.5 vue当中template与render的关系&#xff1a;1.2 MVC、MVVM、MVP模式1.2.1 MVC1.2.2 MVVM1.2.3…

MySQL介绍与安装(超详细)

数据库介绍 数据库(database)简称DB&#xff0c;实际上就是一个文件集合&#xff0c;是一个存储数据的仓库&#xff0c;本质就是一个文件系统&#xff0c;数据库是按照特定的格式把数据存储起来&#xff0c;用户可以对存储的数据进行增删改查等操作。 数据库存储数据特点 ●…

Java处理数据成为树状结构

如题所示&#xff0c;项目中需要将部分数据处理成为树状结构&#xff0c;实现过程如下&#xff1a; 注&#xff1a;也可以使用sql达到该目的&#xff0c;但此处数据不多&#xff0c;故在代码中处理&#xff0c;主要是sql处理不是很会 // 获取需要封装的数据List<Data> d…

java(面向对象)的23种设计模式(11)——观察者模式

一、定义 观察者模式&#xff1a;指多个对象间存在一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 换种说法&#xff0c;定义两种对象&#xff0c;观察者和目标对象&#xff0c;多个观察者同时监听一个目标对…

pikachu平台SQL注入

pikachu平台SQL注入 日常心累、速通pikachu注入相关 目录pikachu平台SQL注入使用到的名词解释1. 数字型注入 --使用bp处理数据包2. 字符型注入 --hackbar处理3. 搜索型注入4. xx型注入5. insert/update注入6. delete注入7. http头注入8. 布尔盲注9. 时间盲注10. 宽字节注入使用…

Shell基础语法——命令

内建命令&#xff08;内置命令&#xff09; 所谓 Shell 内建命令&#xff0c;就是由 Bash 自身提供的命令&#xff0c;而不是文件系统中的某个可执行文件。可以使用 type 来确定一个命令是否是内建命令。 通常来说&#xff0c;内建命令会比外部命令执行得更快&#xff0c;执行…