Skip to content

Commit e0109e6

Browse files
committed
initial
1 parent d8f3556 commit e0109e6

File tree

4 files changed

+307
-0
lines changed

4 files changed

+307
-0
lines changed

MeshSlicerForCulling.cs

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
// Copyright (c) 2018 Archy Piragkov. All Rights Reserved. Licensed under the MIT license
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using UnityEngine;
5+
using System.Linq;
6+
7+
8+
namespace Artics.EditorUtils
9+
{
10+
#region Slicer
11+
/// <summary>
12+
/// Slices big meshes into smaller parts to get better performance with occlusion culling.
13+
///
14+
/// I wrote this component for my project and I think it can be useful for your projects too :) Currently, it supports only slicing by X and Y axes. It's don't separates triangles if they are in different parts, so use it only for high poly meshes Maybe I'll improve in the future.
15+
16+
/// Using is simple.
17+
/// 1. Drag here gameobject which contains MeshFilter object of mesh which you want to slice.
18+
/// 2. Set a default material for new parts
19+
/// 3. Set number of parts by vertical and horizontal
20+
/// 4. Press thew "Slice bounded" button
21+
///
22+
///You can also use second methotd, but it's rather a crutch. I just wrote it for the test and decided to left him.
23+
///
24+
///To test bounds of any mesh - drag it gameobject to BoundsVisualizer and press visualize bounds.
25+
/// </summary>
26+
public class MeshSlicerForCulling : MonoBehaviour
27+
{
28+
#region Variables
29+
30+
public GameObject Container;
31+
public Material DefaultMaterial;
32+
33+
public int Parts;
34+
public int Limit;
35+
36+
public GameObject BoundsVisualizer;
37+
38+
protected Bounds[] Bounds;
39+
protected Bounds VisualizeBounds;
40+
41+
#endregion
42+
43+
#region BoundsMethod
44+
45+
[NaughtyAttributes.Button("Slice bounded")]
46+
public void SliceBounded()
47+
{
48+
transform.position = Container.transform.position;
49+
50+
GameObject newParent = new GameObject($"[Sliced] {Container.name}");
51+
newParent.transform.position = Container.transform.position;
52+
var baseMesh = Container.GetComponent<MeshFilter>().sharedMesh;
53+
54+
var meshCache = new MeshCache() { Instance = baseMesh, Vertices = baseMesh.vertices, Triangles = baseMesh.triangles, Normals = baseMesh.normals, UVs = baseMesh.uv };
55+
56+
//bounds preparations
57+
int boundsParts = Parts * Parts;
58+
var boundsArr = new Bounds[boundsParts];
59+
60+
baseMesh.RecalculateBounds();
61+
var meshBounds = baseMesh.bounds;
62+
63+
Vector3 size = meshBounds.size / Parts;
64+
size.z = meshBounds.size.z;
65+
66+
//bounds calculations
67+
for (int x = 0; x < Parts; x++)
68+
for (int y = 0; y < Parts; y++)
69+
boundsArr[x * Parts + y] = new Bounds(meshBounds.min + new Vector3(size.x * x, size.y * y, 0) + size * 0.5f, size);
70+
71+
Bounds = boundsArr;
72+
73+
//triangle-bounds mask
74+
var trianglesMask = new int[meshCache.Triangles.Length];
75+
76+
for (int i = 0; i < trianglesMask.Length; i++)
77+
trianglesMask[i] = -1;
78+
79+
//creatig meshes
80+
for (int i = 0; i < boundsParts; i++)
81+
{
82+
FindstrianglesInBounds(boundsArr[i], i, trianglesMask, meshCache);
83+
CreateMeshFromBoundMask(i, trianglesMask, meshCache, newParent.transform);
84+
}
85+
86+
}
87+
88+
public void FindstrianglesInBounds(Bounds bounds, int id, int[] trianglesMask, MeshCache cache)
89+
{
90+
for (int i = 0; i < trianglesMask.Length; i += 3)
91+
if (trianglesMask[i] != -1 && bounds.Contains(cache.Vertices[cache.Triangles[i]]) || bounds.Contains(cache.Vertices[cache.Triangles[i + 1]]) || bounds.Contains(cache.Vertices[cache.Triangles[i + 2]]))
92+
trianglesMask[i] = trianglesMask[i + 1] = trianglesMask[i + 2] = id;
93+
}
94+
95+
96+
public void CreateMeshFromBoundMask(int id, int[] boundsMask, MeshCache cache, Transform parent = null)
97+
{
98+
var boundedTriangles = new List<int>();
99+
100+
for (int i = 0; i < boundsMask.Length; i++)
101+
if (boundsMask[i] == id)
102+
boundedTriangles.Add(cache.Triangles[i]);
103+
104+
if (boundedTriangles.Count == 0)
105+
{
106+
Debug.LogWarning($"Sector:{id} - no triangles found");
107+
return;
108+
}
109+
110+
int TrianglesPartAmmount = boundedTriangles.Count;
111+
var vertexesDictionary = new Dictionary<int, Vector3>();
112+
var triangles = cache.Triangles;
113+
var vertices = cache.Vertices;
114+
115+
for (int i = 0; i < TrianglesPartAmmount; i++)
116+
vertexesDictionary[boundedTriangles[i]] = vertices[boundedTriangles[i]];
117+
118+
//new vertices
119+
var newVertices = vertexesDictionary.Values.ToArray();
120+
121+
//new triangles
122+
var keysList = vertexesDictionary.Keys.ToList();
123+
var newTriangles = new int[TrianglesPartAmmount];
124+
125+
for (int i = 0; i < TrianglesPartAmmount; i++)
126+
newTriangles[i] = keysList.IndexOf(boundedTriangles[i]);
127+
128+
int verticesPartAmmount = keysList.Count;
129+
//uv
130+
var oldUV = cache.UVs;
131+
var newUV = new Vector2[verticesPartAmmount];
132+
133+
for (int i = 0; i < verticesPartAmmount; i++)
134+
newUV[i] = oldUV[keysList[i]];
135+
136+
//normals
137+
var oldNormals = cache.Normals;
138+
var newNormals = new Vector3[verticesPartAmmount];
139+
140+
for (int i = 0; i < verticesPartAmmount; i++)
141+
newNormals[i] = oldNormals[keysList[i]];
142+
143+
//creating GameObject
144+
145+
var child = new GameObject($"Sector {id}");
146+
child.transform.parent = parent;
147+
child.transform.localPosition = Vector3.zero;
148+
149+
var newMesh = new Mesh();
150+
newMesh.vertices = newVertices;
151+
newMesh.triangles = newTriangles;
152+
newMesh.uv = newUV;
153+
newMesh.normals = newNormals;
154+
155+
newMesh.RecalculateBounds();
156+
157+
158+
child.AddComponent<MeshFilter>().mesh = newMesh;
159+
child.AddComponent<MeshRenderer>().material = DefaultMaterial;
160+
}
161+
162+
#endregion
163+
164+
#region PolygonMethod
165+
166+
[NaughtyAttributes.Button("Slice")]
167+
public void SimpleSlice()
168+
{
169+
GameObject newParent = new GameObject();
170+
newParent.transform.position = Container.transform.position;
171+
172+
var baseMesh = Container.GetComponent<MeshFilter>().sharedMesh;
173+
174+
int facesNum = baseMesh.triangles.Length / 3;
175+
int partFacesAmmount = facesNum / Parts;
176+
int TrianglesPartAmmount = partFacesAmmount * 3;
177+
178+
for (int i = 0; i < Parts - 1; i++)
179+
CreateMeshFromRange(i * partFacesAmmount, (i + 1) * partFacesAmmount, baseMesh, newParent.transform);
180+
181+
CreateMeshFromRange(partFacesAmmount * (Parts - 1), facesNum, baseMesh, newParent.transform);
182+
}
183+
184+
public void CreateMeshFromRange(int startTriangle, int endTriangle, Mesh baseMesh, Transform parent = null)
185+
{
186+
int trianglesPartAmmount = (endTriangle - startTriangle) * 3;
187+
int startId = startTriangle * 3;
188+
int endId = endTriangle * 3;
189+
var vertexesDictionary = new Dictionary<int, Vector3>();
190+
var triangles = baseMesh.triangles;
191+
var vertices = baseMesh.vertices;
192+
193+
for (int i = startId; i < endId; i++)
194+
vertexesDictionary[triangles[i]] = vertices[triangles[i]];
195+
196+
//new vertices
197+
var newVertices = vertexesDictionary.Values.ToArray();
198+
199+
//new triangles
200+
var keysList = vertexesDictionary.Keys.ToList();
201+
var newTriangles = new int[trianglesPartAmmount];
202+
203+
for (int i = 0; i < trianglesPartAmmount; i++)
204+
newTriangles[i] = keysList.IndexOf(triangles[startId + i]);
205+
206+
int verticesPartAmmount = keysList.Count;
207+
//uv
208+
var oldUV = baseMesh.uv;
209+
var newUV = new Vector2[verticesPartAmmount];
210+
211+
for (int i = 0; i < verticesPartAmmount; i++)
212+
newUV[i] = oldUV[keysList[i]];
213+
214+
//normals
215+
var oldNormals = baseMesh.normals;
216+
var newNormals = new Vector3[verticesPartAmmount];
217+
218+
for (int i = 0; i < verticesPartAmmount; i++)
219+
newNormals[i] = oldNormals[keysList[i]];
220+
221+
//creating GameObject
222+
223+
var child = new GameObject();
224+
child.transform.parent = parent;
225+
child.transform.localPosition = Vector3.zero;
226+
227+
var newMesh = new Mesh();
228+
newMesh.vertices = newVertices;
229+
newMesh.triangles = newTriangles;
230+
newMesh.uv = newUV;
231+
newMesh.normals = newNormals;
232+
233+
newMesh.RecalculateBounds();
234+
235+
child.AddComponent<MeshFilter>().mesh = newMesh;
236+
child.AddComponent<MeshRenderer>().material = DefaultMaterial;
237+
}
238+
239+
#endregion
240+
241+
#region Visualizing
242+
243+
[NaughtyAttributes.Button("Visualize bounds")]
244+
public void CalcBoundsVisualizer()
245+
{
246+
VisualizeBounds = BoundsVisualizer.GetComponent<MeshFilter>().sharedMesh.bounds;
247+
}
248+
249+
private void OnDrawGizmos()
250+
{
251+
Vector3 pos = transform.position;
252+
253+
if (Bounds != null)
254+
for (int i = 0; i < Bounds.Length; i++)
255+
if (i < Limit)
256+
Gizmos.DrawWireCube(Bounds[i].center + pos, Bounds[i].size);
257+
258+
if (BoundsVisualizer != null)
259+
{
260+
Gizmos.color = Color.red;
261+
Gizmos.DrawWireCube(VisualizeBounds.center + BoundsVisualizer.transform.position, VisualizeBounds.size);
262+
}
263+
}
264+
265+
#endregion
266+
}
267+
268+
#endregion
269+
270+
#region MeshCache
271+
272+
public class MeshCache
273+
{
274+
public Mesh Instance;
275+
public int[] Triangles;
276+
public Vector3[] Vertices;
277+
public Vector2[] UVs;
278+
public Vector3[] Normals;
279+
}
280+
281+
#endregion
282+
}

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,27 @@
11
# Unity3d-mesh-slicer-for-culling
22
Slices big meshes into smaller parts to get better performance with occlusion culling.
3+
4+
<p align="center">
5+
<img align="center" width="60%" src="/images/scr2.png">
6+
</p>
7+
8+
I wrote this component for my project and I think it can be useful for your projects too :) Currently, it supports only slicing by X and Y axes. It's don't separates triangles if they are in different parts, so use it only for high poly meshes Maybe I'll improve in the future.
9+
10+
Using is simple.
11+
12+
<p align="center">
13+
<img align="center" width="40%" src="/Screenshots/scr1.png">
14+
</p>
15+
16+
1. Drag here gameobject which contains MeshFilter object of mesh which you want to slice.
17+
18+
2. Set a default material for new parts
19+
20+
3. Set number of parts by vertical and horizontal
21+
22+
4. Press this button
23+
24+
25+
You can also use second methotd, but it's rather a crutch. I just wrote it for the test and decided to left him.
26+
27+
To test bounds of any mesh - drag it gameobject to BoundsVisualizer and press visualize bounds.

images/scr1.png

88.6 KB
Loading

images/scr2.png

2.49 MB
Loading

0 commit comments

Comments
 (0)