@@ -107,10 +107,7 @@ public struct RawGeometry: Codable, Sendable, Equatable, Hashable {
107107 }
108108
109109 public func flipped( ) -> RawGeometry {
110- return RawGeometry (
111- triangles: self . generateTriangles ( ) . map ( { $0. flipped ( ) } ) ,
112- optimizeDistance: nil
113- )
110+ return RawGeometry ( triangles: self . generateTriangles ( ) . map ( { $0. flipped ( ) } ) )
114111 }
115112
116113 /// Creates a new `Geometry` from element array values.
@@ -143,10 +140,22 @@ public struct RawGeometry: Codable, Sendable, Equatable, Hashable {
143140 self . colors = colors
144141 self . indices = indices
145142 }
143+
144+ public enum Optimization {
145+ /// Keeps every vertex as is, including duplicates.
146+ /// This option is required for skins as the indices are pre computed
147+ case dontOptimize
148+ /// Compares each vertex using equality. If equal, they are considered the same and will be folded into a single vertex.
149+ case byEquality
150+ /// Compares the vertex components. If the difference between components is within `threshold` they are considered the same and will be folded into a single vertex.
151+ case byThreshold( _ threshold: Float )
152+ /// Checks the result of the provided comparator. If true, the vertices will be folded into a single vertex. The vertex kept is always lhs.
153+ case usingComparator( _ comparator: ( _ lhs: Vertex , _ rhs: Vertex ) -> Bool )
154+ }
146155
147156 /// Create `Geometry` from counter-clockwise wound `Triangles` and optionanly attempts to optimize the arrays by distance.
148157 /// Optimization is extremely slow and may result in loss of data. It should be used to pre-optimize assets and should not be used at runtime.
149- public init ( triangles: [ Triangle ] , optimizeDistance : Float ? = nil ) {
158+ public init ( triangles: [ Triangle ] , optimization : Optimization = . dontOptimize ) {
150159 assert ( triangles. isEmpty == false )
151160
152161 self . positions = [ ]
@@ -166,32 +175,90 @@ public struct RawGeometry: Codable, Sendable, Equatable, Hashable {
166175
167176 let inVertices : [ Vertex ] = triangles. vertices
168177
169- var similars : [ UInt16 ? ] ? = nil
170- if let threshold = optimizeDistance {
171- similars = Array ( repeating: nil , count: inVertices. count)
178+ var optimizedIndicies : [ UInt16 ]
179+ switch optimization {
180+ case . dontOptimize:
181+ assert ( inVertices. count < UInt16 . max, " Exceeded the maximum number of indices ( \( UInt16 . max) ) for a single geometry. This geometry needs to be spilt up. " )
182+ optimizedIndicies = Array ( 0 ..< UInt16 ( inVertices. count) )
183+ case . byEquality:
184+ optimizedIndicies = Array ( repeating: 0 , count: inVertices. count)
185+ for index in 0 ..< inVertices. count {
186+ assert ( index < UInt16 . max, " Exceeded the maximum number of indices ( \( UInt16 . max) ) for a single geometry. This geometry needs to be spilt up. " )
187+ let vertex = inVertices [ index]
188+ if let similarIndex = inVertices. firstIndex ( where: { $0 == vertex} ) {
189+ optimizedIndicies [ index] = UInt16 ( similarIndex)
190+ } else {
191+ optimizedIndicies [ index] = UInt16 ( index)
192+ }
193+ }
194+ case . byThreshold( let threshold) :
195+ optimizedIndicies = Array ( repeating: 0 , count: inVertices. count)
196+ for index in 0 ..< inVertices. count {
197+ assert ( index < UInt16 . max, " Exceeded the maximum number of indices ( \( UInt16 . max) ) for a single geometry. This geometry needs to be spilt up. " )
198+ let vertex = inVertices [ index]
199+ if let similarIndex = inVertices. firstIndex ( where: { $0. isSimilar ( to: vertex, threshold: threshold) } ) {
200+ optimizedIndicies [ index] = UInt16 ( similarIndex)
201+ } else {
202+ optimizedIndicies [ index] = UInt16 ( index)
203+ }
204+ }
205+ case . usingComparator( let comparator) :
206+ optimizedIndicies = Array ( repeating: 0 , count: inVertices. count)
172207 for index in 0 ..< inVertices. count {
208+ assert ( index < UInt16 . max, " Exceeded the maximum number of indices ( \( UInt16 . max) ) for a single geometry. This geometry needs to be spilt up. " )
173209 let vertex = inVertices [ index]
174- if let similarIndex = Array ( inVertices [ ..< index ] ) . firstIndex ( where: {
175- $0 . isSimilar ( to : vertex , threshold : threshold )
176- } ) {
177- similars ? [ index] = UInt16 ( similarIndex )
210+ if let similarIndex = inVertices. firstIndex ( where: { comparator ( $0 , vertex ) } ) {
211+ optimizedIndicies [ index ] = UInt16 ( similarIndex )
212+ } else {
213+ optimizedIndicies [ index] = UInt16 ( index )
178214 }
179215 }
180216 }
181-
217+
218+ // The next real indices index
182219 var nextIndex = 0
183- for vertexIndex in inVertices. indices {
184- if let similarIndex = similars ? [ vertexIndex] {
185- indices. append ( similarIndex)
186- } else {
187- let vertex = inVertices [ vertexIndex]
188- positions. append ( contentsOf: vertex. storage [ 0 ..< 3 ] )
189- normals. append ( contentsOf: vertex. storage [ 3 ..< 6 ] )
220+ if case . dontOptimize = optimization {
221+ for vertex in inVertices {
222+ self . positions. append ( contentsOf: vertex. storage [ 0 ..< 3 ] )
223+ self . normals. append ( contentsOf: vertex. storage [ 3 ..< 6 ] )
224+ uvSet1. append ( contentsOf: vertex. storage [ 6 ..< 8 ] )
225+ uvSet2. append ( contentsOf: vertex. storage [ 8 ..< 10 ] )
226+ self . tangents. append ( contentsOf: vertex. storage [ 10 ..< 13 ] )
227+ self . colors. append ( contentsOf: vertex. storage [ 13 ..< 17 ] )
228+
229+ self . indices. append ( UInt16 ( nextIndex) )
230+ // Increment the next real indicies index
231+ nextIndex += 1
232+ }
233+ } else {
234+ // Store the optimized vertex index using the actual indicies index
235+ // so we can look up the real index for repeated verticies
236+ var indicesMap : [ UInt16 : UInt16 ] = [ : ]
237+ indicesMap. reserveCapacity ( inVertices. count)
238+ for vertexIndexInt in inVertices. indices {
239+ // Obtain the optimized vertexIndex for this vertex
240+ let vertexIndex : UInt16 = optimizedIndicies [ vertexIndexInt]
241+
242+ // Check our map to see if this vertex was already added
243+ if let index = indicesMap [ vertexIndex] {
244+ // Add the repeated index to the indices and continue to the next
245+ self . indices. append ( index)
246+ continue
247+ }
248+
249+ let vertex = inVertices [ vertexIndexInt]
250+ self . positions. append ( contentsOf: vertex. storage [ 0 ..< 3 ] )
251+ self . normals. append ( contentsOf: vertex. storage [ 3 ..< 6 ] )
190252 uvSet1. append ( contentsOf: vertex. storage [ 6 ..< 8 ] )
191253 uvSet2. append ( contentsOf: vertex. storage [ 8 ..< 10 ] )
192- tangents. append ( contentsOf: vertex. storage [ 10 ..< 13 ] )
193- colors. append ( contentsOf: vertex. storage [ 13 ..< 17 ] )
194- indices. append ( UInt16 ( nextIndex) )
254+ self . tangents. append ( contentsOf: vertex. storage [ 10 ..< 13 ] )
255+ self . colors. append ( contentsOf: vertex. storage [ 13 ..< 17 ] )
256+
257+ let index = UInt16 ( nextIndex)
258+ self . indices. append ( index)
259+ // Update the map
260+ indicesMap [ vertexIndex] = index
261+ // Increment the next real indicies index
195262 nextIndex += 1
196263 }
197264 }
@@ -203,7 +270,7 @@ public struct RawGeometry: Codable, Sendable, Equatable, Hashable {
203270 for geom in geometries {
204271 triangles. append ( contentsOf: geom. generateTriangles ( ) )
205272 }
206- self . init ( triangles: triangles, optimizeDistance : nil )
273+ self . init ( triangles: triangles)
207274 }
208275
209276 /// Creates a new `Geometry` by merging multiple geometry. This is usful for loading files that store geometry speretly base don material if you intend to only use a single material for them all.
@@ -212,7 +279,7 @@ public struct RawGeometry: Codable, Sendable, Equatable, Hashable {
212279 for geometry in geometries {
213280 triangles. append ( contentsOf: geometry. generateTriangles ( ) )
214281 }
215- self . init ( triangles: triangles, optimizeDistance : nil )
282+ self . init ( triangles: triangles)
216283 }
217284
218285 public func hash( into hasher: inout Hasher ) {
0 commit comments