概述

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. hdu 4119 Isabella's Message 模拟题

    Isabella's Message Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.p ...

  2. ookla 网络上传下载 PING 测试

    1.登陆http://www.ookla.com/ 2.点选Explore Speedtest Custon 3. 1.start for free 或者 2.measure your network ...

  3. Windows Server 2008 Standard Enterprise Datacenter各个版本区别

    -- Windows Server 2008 Standard 包含1个虚拟实例许可,5个客户端访问授权,售价999美元. -- Windows Server 2008 Enterprise 包含4个 ...

  4. Sound.loadCompressedDataFromByteArray

    前不久Adobe发布了Flash Player 11的release版本, Flash Player 11 带来了很多新特性, 最最红火的就是Stage3D了,但是这里讲的是这个版本的另一个新特性, ...

  5. [Android Studio] Android Studio使用教程(一)

    转载:http://blog.csdn.net/hyr83960944/article/details/37509113 今年的Google全球开发者 大会虽然没有新的Android系统和设备,但是还 ...

  6. Informatica 常用组件Source Qualifier之四 SQL Query

    源限定符转换提供 SQL 查询选项以覆盖默认的查询.您可以输入您的源数据库支持的 SQL 语句.输入查询之前,请连接您要在映射中使用的所有输入和输出端口. 编辑 SQL 查询时,您可以生成并编辑默认查 ...

  7. Orchard运用 - 特定主题添加独立代码文件

    今天继续跟大家分享捣鼓Orchard的一些心得.其实有时一些问题或者Bugs还是蛮好解决的,主要看你采取哪种方式方法.比如有时我们为了扩展某些特性或功能,你可以搭建一个全新的模块来完成,如果临时的或简 ...

  8. SQL CROSS JOIN

    最近在讲到T-SQL查询的Join部分时,一下子没有想起来CROSS JOIN的用法,因为其实平常也确实基本不用到.特意找了一个例子,以供参考 CROSS JOIN又称为笛卡尔乘积,实际上是把两个表乘 ...

  9. Zclip:复制页面内容到剪贴板兼容各浏览器

    WEB开发中,要让用户复制页面中的一段代码.URL地址等信息,为了避免用户拖动鼠标再进行右键复制操作而可能出现的差错,我们可以直接在页面中放置一个复制按钮,只需要轻轻一点这个复制按钮,内容将会被复制, ...

  10. 最佳实践扩展Windows窗体DataGridView控件 .net 4.5 附示例代码

    Windows窗体DataGridView控件的性能调优.net 4.5   在处理大量数据时, DataGridView 控制可以消耗大量的内存开销,除非你仔细地使用它. 在客户有限的内存,你可以避 ...