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+ }
0 commit comments