概述

3d模型的任意切割一直是游戏开发里的一个很大的问题,模型切割的关键点就只有生成横切面的新顶点以及切口纹理的缝合,理论上解决了这两点,就近乎可以做到以假乱真的程度了。本篇文章就这两点进行描述

详细

一、准备工作

解压缩后得到ClipDemo.unitypackage文件,将此文件导入unity5.45中,双击main场景,运行即可。运行后可以看到一个球体,对着球体拖动鼠标做切割动作,可以看到Hierarchy面板生成多个new Model,即为切割生成的模型,可以在Scene中拖动这些物体,可以看到是切割后的物体。压缩包内容如下:

二、程序实现

如上图所示,当切割模型时,对于切面上的三角面,无非是如图中3种情况(正好切在三角形的某个顶点上几乎不可能,不过也可以考虑在内,这里就不画出来了),所以每个三角形正好被切到的时候,其自身内部应该生成新的顶点(图中-1,-2点)。生成处新的顶点之后,我们需要将原来的一个三角形重新分割为如图绿色的数字标志的三个三角形,也就是原来一个三角形被分为三个三角形。代码如下:

MeshFilter mf = this.gameObject.GetComponent<MeshFilter>();

        //顶点数组转顶点容器
List<Vector3> verticeList = new List<Vector3>();
int verticeCount = mf.mesh.vertices.Length;
for (int verticeIndex = 0; verticeIndex < verticeCount; ++verticeIndex)
{
verticeList.Add(mf.mesh.vertices[verticeIndex]);
}
//三角形数组转三角形容器
List<int> triangleList = new List<int>();
int triangleCount = mf.mesh.triangles.Length;
for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex)
{
triangleList.Add(mf.mesh.triangles[triangleIndex]);
}
//uv坐标数组转uv坐标容器
List<Vector2> uvList = new List<Vector2>();
int uvCount = mf.mesh.uv.Length;
for (int uvIndex = 0; uvIndex < uvCount; ++uvIndex)
{
uvList.Add(mf.mesh.uv[uvIndex]);
}
//顶点颜色数组转顶点颜色容器
List<Vector3> normalList = new List<Vector3>();
int normalCount = mf.mesh.normals.Length;
for (int normalIndex = 0; normalIndex < normalCount; ++normalIndex)
{
normalList.Add(mf.mesh.normals[normalIndex]);
} //检查每个三角面,是否存在两个顶点连接正好在直线上
for (int triangleIndex = 0; triangleIndex < triangleList.Count;)
{
int trianglePoint0 = triangleList[triangleIndex];
int trianglePoint1 = triangleList[triangleIndex + 1];
int trianglePoint2 = triangleList[triangleIndex + 2]; Vector3 point0 = verticeList[trianglePoint0];
Vector3 point1 = verticeList[trianglePoint1];
Vector3 point2 = verticeList[trianglePoint2]; float planeY = 0.3f;
//0-1,1-2相连线段被切割
if ((point0.y - planeY)* (point1.y - planeY) < 0 && (point1.y - planeY) * (point2.y - planeY) < 0)
{
//截断0-1之间的顶点
float k01 = (point1.y - point0.y) / (planeY - point0.y);
float newPointX01 = (point1.x - point0.x) / k01 + point0.x;
float newPointZ01 = (point1.z - point0.z) / k01 + point0.z;
Vector3 newPoint0_1 = new Vector3(newPointX01, planeY, newPointZ01);
verticeList.Add(newPoint0_1);
//uv
if(uvList.Count > 0)
{
Vector2 uv0 = uvList[trianglePoint0];
Vector2 uv1 = uvList[trianglePoint1];
float newUV_x = (uv1.x - uv0.x) / k01 + uv0.x;
float newUV_y = (uv1.y - uv0.y) / k01 + uv0.y;
uvList.Add(new Vector2(newUV_x, newUV_y));
}
//法向量
Vector3 normalX0 = normalList[trianglePoint0];
Vector3 normalX1 = normalList[trianglePoint1];
Vector3 normalX2 = normalList[trianglePoint2];
float newNoramlX01 = (normalX1.x - normalX0.x) / k01 + normalX0.x;
float newNoramlY01 = (normalX1.y - normalX0.y) / k01 + normalX0.y;
float newNoramlZ01 = (normalX1.z - normalX0.z) / k01 + normalX0.z;
normalList.Add(new Vector3(newNoramlX01, newNoramlY01, newNoramlZ01));
//截断1-2之间的顶点
float k12 = (point2.y - point1.y) / (planeY - point1.y);
float newPointX12 = (point2.x - point1.x) / k12 + point1.x;
float newPointZ12 = (point2.z - point1.z) / k12 + point1.z;
Vector3 newPoint1_2 = new Vector3(newPointX12, planeY, newPointZ12);
verticeList.Add(newPoint1_2);
if (uvList.Count > 0)
{
Vector2 uv1 = uvList[trianglePoint1];
Vector2 uv2 = uvList[trianglePoint2];
float newUV_x = (uv2.x - uv1.x) / k12 + uv1.x;
float newUV_y = (uv2.y - uv1.y) / k12 + uv1.y;
uvList.Add(new Vector2(newUV_x, newUV_y));
}
//法向量
float newNoramlX12 = (normalX2.x - normalX1.x) / k12 + normalX1.x;
float newNoramlY12 = (normalX2.y - normalX1.y) / k12 + normalX1.y;
float newNoramlZ12 = (normalX2.z - normalX1.z) / k12 + normalX1.z;
normalList.Add(new Vector3(newNoramlX12, newNoramlY12, newNoramlZ12)); int newVerticeCount = verticeList.Count;
//插入顶点索引,以此构建新三角形
triangleList.Insert(triangleIndex + 1, newVerticeCount - 2);
triangleList.Insert(triangleIndex + 2, newVerticeCount - 1); triangleList.Insert(triangleIndex + 3, newVerticeCount - 1);
triangleList.Insert(triangleIndex + 4, newVerticeCount - 2); triangleList.Insert(triangleIndex + 6, trianglePoint0);
triangleList.Insert(triangleIndex + 7, newVerticeCount - 1);
}
//1-2,2-0相连线段被切割
else if ((point1.y - planeY) * (point2.y - planeY) < 0 && (point2.y - planeY) * (point0.y - planeY) < 0)
{
//截断1-2之间的顶点
float k12 = (point2.y - point1.y) / (planeY - point1.y);
float newPointX12 = (point2.x - point1.x) / k12 + point1.x;
float newPointZ12 = (point2.z - point1.z) / k12 + point1.z;
Vector3 newPoint1_2 = new Vector3(newPointX12, planeY, newPointZ12);
verticeList.Add(newPoint1_2);
if (uvList.Count > 0)
{
Vector2 uv1 = uvList[trianglePoint1];
Vector2 uv2 = uvList[trianglePoint2];
float newUV_x = (uv2.x - uv1.x) / k12 + uv1.x;
float newUV_y = (uv2.y - uv1.y) / k12 + uv1.y;
uvList.Add(new Vector2(newUV_x, newUV_y));
}
//法向量
Vector3 normalX0 = normalList[trianglePoint0];
Vector3 normalX1 = normalList[trianglePoint1];
Vector3 normalX2 = normalList[trianglePoint2];
float newNoramlX12 = (normalX2.x - normalX1.x) / k12 + normalX1.x;
float newNoramlY12 = (normalX2.y - normalX1.y) / k12 + normalX1.y;
float newNoramlZ12 = (normalX2.z - normalX1.z) / k12 + normalX1.z;
normalList.Add(new Vector3(newNoramlX12, newNoramlY12, newNoramlZ12)); //截断0-2之间的顶点
float k02 = (point2.y - point0.y) / (planeY - point0.y);
float newPointX02 = (point2.x - point0.x) / k02 + point0.x;
float newPointZ02 = (point2.z - point0.z) / k02 + point0.z;
Vector3 newPoint0_2 = new Vector3(newPointX02, planeY, newPointZ02);
verticeList.Add(newPoint0_2);
//uv
if (uvList.Count > 0)
{
Vector2 uv0 = uvList[trianglePoint0];
Vector2 uv2 = uvList[trianglePoint2];
float newUV_x = (uv2.x - uv0.x) / k02 + uv0.x;
float newUV_y = (uv2.y - uv0.y) / k02 + uv0.y;
uvList.Add(new Vector2(newUV_x, newUV_y));
}
//法向量
float newNoramlX02 = (normalX1.x - normalX0.x) / k02 + normalX0.x;
float newNoramlY02 = (normalX1.y - normalX0.y) / k02 + normalX0.y;
float newNoramlZ02 = (normalX1.z - normalX0.z) / k02 + normalX0.z;
normalList.Add(new Vector3(newNoramlX02, newNoramlY02, newNoramlZ02)); int newVerticeCount = verticeList.Count;
//插入顶点索引,以此构建新三角形 //{0}
//{1}
triangleList.Insert(triangleIndex + 2, newVerticeCount - 2); triangleList.Insert(triangleIndex + 3, newVerticeCount - 1);
triangleList.Insert(triangleIndex + 4, newVerticeCount - 2);
//{2} triangleList.Insert(triangleIndex + 6, newVerticeCount - 1);
triangleList.Insert(triangleIndex + 7, trianglePoint0);
triangleList.Insert(triangleIndex + 8, newVerticeCount - 2);
}
//0-1,2-0相连线段被切割
else if((point0.y - planeY) * (point1.y - planeY) < 0 && (point2.y - planeY) * (point0.y - planeY) < 0)
{
//截断0-1之间的顶点
float k01 = (point1.y - point0.y) / (planeY - point0.y);
float newPointX01 = (point1.x - point0.x) / k01 + point0.x;
float newPointZ01 = (point1.z - point0.z) / k01 + point0.z;
Vector3 newPoint0_1 = new Vector3(newPointX01, planeY, newPointZ01);
verticeList.Add(newPoint0_1);
//uv
if (uvList.Count > 0)
{
Vector2 uv0 = uvList[trianglePoint0];
Vector2 uv1 = uvList[trianglePoint1];
float newUV_x = (uv1.x - uv0.x) / k01 + uv0.x;
float newUV_y = (uv1.y - uv0.y) / k01 + uv0.y;
uvList.Add(new Vector2(newUV_x, newUV_y));
}
//法向量
Vector3 normalX0 = normalList[trianglePoint0];
Vector3 normalX1 = normalList[trianglePoint1];
Vector3 normalX2 = normalList[trianglePoint2];
float newNoramlX01 = (normalX1.x - normalX0.x) / k01 + normalX0.x;
float newNoramlY01 = (normalX1.y - normalX0.y) / k01 + normalX0.y;
float newNoramlZ01 = (normalX1.z - normalX0.z) / k01 + normalX0.z;
normalList.Add(new Vector3(newNoramlX01, newNoramlY01, newNoramlZ01)); //截断0-2之间的顶点
float k02 = (point2.y - point0.y) / (planeY - point0.y);
float newPointX02 = (point2.x - point0.x) / k02 + point0.x;
float newPointZ02 = (point2.z - point0.z) / k02 + point0.z;
Vector3 newPoint0_2 = new Vector3(newPointX02, planeY, newPointZ02);
verticeList.Add(newPoint0_2);
//uv
if (uvList.Count > 0)
{
Vector2 uv0 = uvList[trianglePoint0];
Vector2 uv2 = uvList[trianglePoint2];
float newUV_x = (uv2.x - uv0.x) / k02 + uv0.x;
float newUV_y = (uv2.y - uv0.y) / k02 + uv0.y;
uvList.Add(new Vector2(newUV_x, newUV_y));
}
//法向量
float newNoramlX02 = (normalX1.x - normalX0.x) / k02 + normalX0.x;
float newNoramlY02 = (normalX1.y - normalX0.y) / k02 + normalX0.y;
float newNoramlZ02 = (normalX1.z - normalX0.z) / k02 + normalX0.z;
normalList.Add(new Vector3(newNoramlX02, newNoramlY02, newNoramlZ02)); int newVerticeCount = verticeList.Count;
//插入顶点索引,以此构建新三角形 //{0}
triangleList.Insert(triangleIndex + 1, newVerticeCount - 2);
triangleList.Insert(triangleIndex + 2, newVerticeCount - 1); triangleList.Insert(triangleIndex + 3, newVerticeCount - 2);
//{1}
//{2} triangleList.Insert(triangleIndex + 6, trianglePoint2);
triangleList.Insert(triangleIndex + 7, newVerticeCount - 1);
triangleList.Insert(triangleIndex + 8, newVerticeCount - 2);
}
//只有0-1被切
else if((point0.y - planeY) * (point1.y - planeY) < 0)
{
Debug.Log("只有01被切");
}
//只有1-2被切
else if ((point1.y - planeY) * (point2.y - planeY) < 0)
{
Debug.Log("只有12被切");
}
//只有2-0被切
else if ((point2.y - planeY) * (point0.y - planeY) < 0)
{
Debug.Log("只有02被切");
}
triangleIndex += 3;
} //筛选出切割面两侧的顶点索引
List<int> triangles1 = new List<int>();
List<int> triangles2 = new List<int>();
for (int triangleIndex = 0; triangleIndex < triangleList.Count; triangleIndex += 3)
{
int trianglePoint0 = triangleList[triangleIndex];
int trianglePoint1 = triangleList[triangleIndex + 1];
int trianglePoint2 = triangleList[triangleIndex + 2]; Vector3 point0 = verticeList[trianglePoint0];
Vector3 point1 = verticeList[trianglePoint1];
Vector3 point2 = verticeList[trianglePoint2];
//切割面
float planeY = 0.3f;
if(point0.y > planeY || point1.y > planeY || point2.y > planeY)
{
triangles1.Add(trianglePoint0);
triangles1.Add(trianglePoint1);
triangles1.Add(trianglePoint2);
}
else
{
triangles2.Add(trianglePoint0);
triangles2.Add(trianglePoint1);
triangles2.Add(trianglePoint2);
}
} //缝合切口
//for (int verticeIndex = verticeCount; verticeIndex < verticeList.Count - 2; ++verticeIndex)
//{
// triangles1.Add(verticeIndex + 2);
// triangles1.Add(verticeIndex);
// triangles1.Add(verticeCount); // triangles2.Add(verticeCount);
// triangles2.Add(verticeIndex);
// triangles2.Add(verticeIndex + 2);
//} mf.mesh.vertices = verticeList.ToArray();
mf.mesh.triangles = triangles1.ToArray();
if (uvList.Count > 0)
{
mf.mesh.uv = uvList.ToArray();
}
mf.mesh.normals = normalList.ToArray(); //分割模型
GameObject newModel = new GameObject("New Model");
MeshFilter meshFilter = newModel.AddComponent<MeshFilter>();
meshFilter.mesh.vertices = mf.mesh.vertices;
meshFilter.mesh.triangles = triangles2.ToArray();
meshFilter.mesh.uv = mf.mesh.uv;
meshFilter.mesh.normals = mf.mesh.normals;
Renderer newRenderer = newModel.AddComponent<MeshRenderer>();
newRenderer.material = this.gameObject.GetComponent<MeshRenderer>().material;

切出来的模型新生成的顶点是无序的,但是我们可以连接任意两个无序顶点定为参考向量,然后其他任意顶点与参考向量中的起点连接形成新的向量,求得这两个向量之间的夹角,利用这个夹角大小来排序,如图所示:

顶点的排序算法如下:

//重新排序新生成的顶点,按照角度
List<SortAngle> SortAngleList = new List<SortAngle>();
for (int verticeIndex = verticeCount + 1; verticeIndex < verticeList.Count; verticeIndex++)
{
//计算角度,以0-1为参照
Vector3 vec1to0 = verticeList[verticeCount + 1] - verticeList[verticeCount];
Vector3 indexTo0 = verticeList[verticeIndex] - verticeList[verticeCount]; float moIndexto0 = indexTo0.magnitude;
float mo1to0 = vec1to0.magnitude;
float dotRes = Vector3.Dot(indexTo0, vec1to0);
if (moIndexto0 == 0.0f)
{
continue;
}
float angle = Mathf.Acos(dotRes / (mo1to0 * moIndexto0)); //Vector3.Angle(indexTo0.normalized, vec1to0.normalized);
bool isExis = false;
for (int i = 0; i < SortAngleList.Count; ++i)
{
//同样角度,距离近的被剔除
if (Mathf.Abs(SortAngleList[i].Angle * 180.0f / Mathf.PI - angle * 180.0f / Mathf.PI) < 0.1f)
{
float dis1 = Vector3.Distance(verticeList[SortAngleList[i].Index], verticeList[verticeCount]);
float dis2 = Vector3.Distance(verticeList[verticeIndex], verticeList[verticeCount]);
if (dis2 >= dis1)
{
SortAngleList[i].Index = verticeIndex;
}
isExis = true;
break;
}
}
if (!isExis)
{
//Debug.Log(angle);
SortAngle sortAngle = new SortAngle();
sortAngle.Index = verticeIndex;
sortAngle.Angle = angle;
SortAngleList.Add(sortAngle);
}
}
SortAngleList.Sort();

三、运行效果

四、项目截图

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

unity模型任意无限切割插件的更多相关文章

  1. C# 实现 任意多边形切割折线算法

    1.    内容简介 本文旨在解决任意多边形切割折线,获取切割之后的折线集合. 本文实现的算法内容包括:判断两条线段是否相交,如若相交,获取交点集合.对线上的点集,按斜率方向排序.判断点是否在多边形内 ...

  2. Infinite Scroll - jQuery & WP 无限滚动插件

    无限滚动(Infinite Scroll)也称为自动分页.滚动分页和无限分页.常用在图片.文章或其它列表形式的网页中,用来在滚动网页的时候自动加载下一页的内容.Infinite Scroll  这款  ...

  3. unity sprite怎么获取切割后的图

    学习了一段时间的unity,对里面的组件有一个大致的了解,但是具体操作来说还不是很熟悉,今天看了一片关于unity sprite怎么获取切割后的图的文章,感觉还不错. 假设有一张png/tga图集,导 ...

  4. 任意多边形切割/裁剪(附C#代码实现)

    本实现主要参考了发表于2003年<软件学报>的<一个有效的多边形裁剪算法>(刘勇奎,高云,黄有群)这篇论文,所使用的理论与算法大都基于本文,对论文中部分阐述进行了详细解释,并提 ...

  5. Unity模型导入的若干问题

    原地址:http://tieba.baidu.com/p/2807225555 我觉得有三点感觉在导出时比较重要的1.单位的设置3dsmax系统单位设置就设成厘米显示单位公制也设成厘米这样在max里的 ...

  6. 怎么给Unity写一个原生的插件

    本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/50266889 作者:car ...

  7. 【VR视频播放】解决Unity模型贴图反转的问题

    使用UV贴图网模型上贴的时候, 会出现图片反过来的情况. 根本原因是因为, 一般系统的屏幕坐标系(例如Android)是左上角为原点(0,0), 但是Unity的贴图是以左下角为原点(0,0) 方法有 ...

  8. Matlab 沿三维任意方向切割CT图的仿真计算

    一.数据来源 头部组织的数据.此处直接引用了matlab自带的mri数据.实际场景中,可以通过CT得到的数据进行转换得到 插入异物的数据.此处我假设插入异物为一根细铁丝.模拟为空间中的一条曲线.这个曲 ...

  9. Unity中内嵌网页插件UniWebView使用总结

    目前有三种方式可以实现在Unity工程中实现内嵌网页的功能: 1.  UnityWebCore:只支持Windows平台,调用浏览器内核,将网页渲染到mesh,作为gameObject. 2.  Un ...

随机推荐

  1. List与ArrayList的区别

    http://www.cnblogs.com/aisiteru/articles/1151874.html List是一个接口,而ListArray是一个类. ListArray继承并实现了List. ...

  2. Objective-C 关于静态方法与实例方法

    objective-c中非常重要的语法知识,在此归纳总结一下. 类方法,也称静态方法,指的是用static关键字修饰的方法.此方法属类本身的方法,不属于类的某一个实例(对象).类方法中不可直接使用实例 ...

  3. 借助JRebel使Tomcat支持热部署

    JRebel是一个J2EE热部署的工具.使用它可以减少浪费8-18%的开发时间在项目的构建和部署上.虽然Java也提供了HotSpot的JVM,但是如果你修改的类中有方法名称变动的话,HotSpot就 ...

  4. Android中MVC模型(复合模式)

    mvc是model,view,controller的缩写,mvc包括三个部分: 1.模型(model)对象:是应用程序的主体部分,全部的业务逻辑都应该写在该层. 2.视图(view)对象:是应用程序中 ...

  5. linux创建swap分区

    创建交换分区 root@zabbix-server:~# mkdir /swap root@zabbix-server:~# cd /swap/ root@zabbix-server:/swap# l ...

  6. iOS设计模式:静态工厂相关

    工厂方法模式 定义创建对象的接口,让子类决定实例化哪一个类,工厂方法使得一个类的实例化延迟到其子类. *最初的定义出现于<设计模式>(Addison-Wesley,1994) 注意:我讲解 ...

  7. 神经网络可以拟合任意函数的视觉证明A visual proof that neural nets can compute any function

    One of the most striking facts about neural networks is that they can compute any function at all. T ...

  8. Python学习(八)异常处理

    Python 异常处理 程序出错时,会抛出异常,这想必在之前学习过程中已经见过不少. 这边具体说明下Python 的标准异常.如何捕捉异常.抛出异常 以及自定义异常. python 标准异常 我们先来 ...

  9. jenkins的docker

    参考:https://store.docker.com/images/jenkins?tab=description https://my.oschina.net/jayqqaa12/blog/633 ...

  10. Unity3D新手教学,让你十二小时,从入门到掌握!(三 ) [转]

    版权声明:本文为Aries原创文章,转载请标明出处.如有不足之处欢迎提出意见或建议,联系QQ531193915 这一讲,我会教大家如何写碰撞检测的代码,然后还会教大家如何使用我介绍给大家的第一个Uni ...