unity模型任意无限切割插件

概述

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

详细

一、准备工作

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

QQ截图20170825144223.png

二、程序实现

2.jpg

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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;

 

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

顶点的排序算法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//重新排序新生成的顶点,按照角度
        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();

三、运行效果

20170809161556089.png

20170810124411379.png

20170810124421277.png

四、项目截图

3.jpg

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

 

posted on   demo例子集  阅读(8595)  评论(0编辑  收藏  举报

努力加载评论中...

导航

点击右上角即可分享
微信分享提示