Skip to content

Commit c639e19

Browse files
committed
Add CollisionMesh container
Efficiently stores collision triangles
1 parent a00c42c commit c639e19

File tree

1 file changed

+325
-0
lines changed

1 file changed

+325
-0
lines changed
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
/*
2+
* Copyright © 2025 Dustin Collins (Strega's Gate)
3+
* All Rights Reserved.
4+
*
5+
* http://stregasgate.com
6+
*/
7+
8+
/// An optimized container for CollisionTriangles
9+
public final class CollisionMesh {
10+
@usableFromInline
11+
package struct Components {
12+
@usableFromInline
13+
package var positions: [Float]
14+
@usableFromInline
15+
package var normals: [Float]
16+
@usableFromInline
17+
package var attributes: [UInt64]
18+
19+
package init(positions: [Float], normals: [Float], attributes: [UInt64]) {
20+
self.positions = positions
21+
self.normals = normals
22+
self.attributes = attributes
23+
}
24+
}
25+
@usableFromInline
26+
package struct TriangleIndices {
27+
@usableFromInline
28+
package let p1: Int
29+
@usableFromInline
30+
package let p2: Int
31+
@usableFromInline
32+
package let p3: Int
33+
@usableFromInline
34+
package let center: Int
35+
@usableFromInline
36+
package let normal: Int
37+
@usableFromInline
38+
package let faceNormal: Int
39+
@usableFromInline
40+
package let attributes: Int
41+
42+
package init(p1: Int, p2: Int, p3: Int, center: Int, normal: Int, faceNormal: Int, attributes: Int) {
43+
self.p1 = p1
44+
self.p2 = p2
45+
self.p3 = p3
46+
self.center = center
47+
self.normal = normal
48+
self.faceNormal = faceNormal
49+
self.attributes = attributes
50+
}
51+
}
52+
@usableFromInline
53+
package var components: Components
54+
@usableFromInline
55+
package var indices: [TriangleIndices]
56+
57+
@inlinable
58+
public var triangleCount: Int {
59+
return indices.count
60+
}
61+
62+
public init(collisionTriangles triangles: [CollisionTriangle]) {
63+
assert(triangles.isEmpty == false)
64+
65+
var positions: [Position3] = []
66+
var normals: [Direction3] = []
67+
var attributes: [UInt64] = []
68+
var indicies: [TriangleIndices] = []
69+
70+
for triangle in triangles {
71+
func indexByAppendingValue<V: Equatable>(_ value: V, to array: inout [V]) -> Int {
72+
var arrayIndex: Int
73+
if let existingIndex = array.firstIndex(of: value) {
74+
arrayIndex = existingIndex
75+
}else{
76+
arrayIndex = array.count
77+
array.append(value)
78+
}
79+
return arrayIndex
80+
}
81+
82+
let faceNormal = Direction3((triangle.p2 - triangle.p1).cross(triangle.p3 - triangle.p1)).normalized
83+
let center = (triangle.p1 + triangle.p2 + triangle.p3) / 3
84+
indicies.append(
85+
TriangleIndices(
86+
p1: indexByAppendingValue(triangle.p1, to: &positions) * 3,
87+
p2: indexByAppendingValue(triangle.p2, to: &positions) * 3,
88+
p3: indexByAppendingValue(triangle.p3, to: &positions) * 3,
89+
center: indexByAppendingValue(center, to: &positions) * 3,
90+
normal: indexByAppendingValue(triangle.normal, to: &normals) * 3,
91+
faceNormal: indexByAppendingValue(faceNormal, to: &normals) * 3,
92+
attributes: indexByAppendingValue(triangle.rawAttributes, to: &attributes)
93+
)
94+
)
95+
}
96+
97+
self.components = Components(
98+
positions: positions.valuesArray(),
99+
normals: normals.valuesArray(),
100+
attributes: attributes
101+
)
102+
self.indices = indicies
103+
}
104+
105+
package init(indices: [TriangleIndices], positions: [Float], normals: [Float], attributes: [UInt64]) {
106+
self.indices = indices
107+
self.components = Components(positions: positions, normals: normals, attributes: attributes)
108+
}
109+
110+
public func generateCollisionTriangles() -> [CollisionTriangle] {
111+
var triangles: [CollisionTriangle] = []
112+
triangles.reserveCapacity(triangleCount)
113+
for index in 0 ..< triangleCount {
114+
self.withTriangle(atIndex: index) { triangle in
115+
triangles.append(
116+
CollisionTriangle(
117+
p1: triangle.p1,
118+
p2: triangle.p2,
119+
p3: triangle.p3,
120+
normal: triangle.faceNormal,
121+
rawAttributes: triangle.rawAttributes
122+
)
123+
)
124+
}
125+
}
126+
return triangles
127+
}
128+
}
129+
130+
public extension CollisionMesh {
131+
struct Triangle<CollisionAttributes: CollisionAttributesGroup>: ~Copyable {
132+
@usableFromInline
133+
internal var mesh: CollisionMesh
134+
@usableFromInline
135+
internal let indices: TriangleIndices
136+
137+
@inlinable
138+
public var p1: Position3 {
139+
let baseIndex = indices.p1
140+
let x = mesh.components.positions[baseIndex]
141+
let y = mesh.components.positions[baseIndex + 1]
142+
let z = mesh.components.positions[baseIndex + 2]
143+
return Position3(x, y, z)
144+
}
145+
@inlinable
146+
public var p2: Position3 {
147+
let baseIndex = indices.p2
148+
let x = mesh.components.positions[baseIndex]
149+
let y = mesh.components.positions[baseIndex + 1]
150+
let z = mesh.components.positions[baseIndex + 2]
151+
return Position3(x, y, z)
152+
}
153+
@inlinable
154+
public var p3: Position3 {
155+
let baseIndex = indices.p3
156+
let x = mesh.components.positions[baseIndex]
157+
let y = mesh.components.positions[baseIndex + 1]
158+
let z = mesh.components.positions[baseIndex + 2]
159+
return Position3(x, y, z)
160+
}
161+
@inlinable
162+
public var center: Position3 {
163+
let baseIndex = indices.center
164+
let x = mesh.components.positions[baseIndex]
165+
let y = mesh.components.positions[baseIndex + 1]
166+
let z = mesh.components.positions[baseIndex + 2]
167+
return Position3(x, y, z)
168+
}
169+
170+
@inlinable
171+
public var normal: Direction3 {
172+
let baseIndex = indices.normal
173+
let x = mesh.components.normals[baseIndex]
174+
let y = mesh.components.normals[baseIndex + 1]
175+
let z = mesh.components.normals[baseIndex + 2]
176+
return Direction3(x, y, z)
177+
}
178+
@inlinable
179+
public var faceNormal: Direction3 {
180+
let baseIndex = indices.faceNormal
181+
let x = mesh.components.normals[baseIndex]
182+
let y = mesh.components.normals[baseIndex + 1]
183+
let z = mesh.components.normals[baseIndex + 2]
184+
return Direction3(x, y, z)
185+
}
186+
187+
@inlinable
188+
public var attributes: CollisionAttributes {
189+
let rawValue = mesh.components.attributes[indices.attributes]
190+
return CollisionAttributes(rawValue: rawValue)
191+
}
192+
193+
@inlinable
194+
public var rawAttributes: UInt64 {
195+
return mesh.components.attributes[indices.attributes]
196+
}
197+
198+
@inlinable
199+
public var plane: Plane3D {
200+
return Plane3D(origin: center, normal: normal)
201+
}
202+
203+
@usableFromInline
204+
internal init(mesh: CollisionMesh, triangleIndex: Int) {
205+
self.mesh = mesh
206+
self.indices = mesh.indices[triangleIndex]
207+
}
208+
}
209+
210+
struct MutableTriangle<CollisionAttributes: CollisionAttributesGroup>: ~Copyable {
211+
@usableFromInline
212+
internal var mesh: CollisionMesh
213+
@usableFromInline
214+
internal let indices: TriangleIndices
215+
216+
@inlinable
217+
public var p1: Position3 {
218+
get {
219+
let baseIndex = Int(indices.p1)
220+
let x = mesh.components.positions[baseIndex]
221+
let y = mesh.components.positions[baseIndex + 1]
222+
let z = mesh.components.positions[baseIndex + 2]
223+
return Position3(x, y, z)
224+
}
225+
nonmutating set {
226+
let baseIndex = Int(indices.p1)
227+
mesh.components.positions[baseIndex] = newValue.x
228+
mesh.components.positions[baseIndex + 1] = newValue.y
229+
mesh.components.positions[baseIndex + 2] = newValue.z
230+
}
231+
}
232+
233+
@inlinable
234+
public var p2: Position3 {
235+
get {
236+
let baseIndex = Int(indices.p2)
237+
let x = mesh.components.positions[baseIndex]
238+
let y = mesh.components.positions[baseIndex + 1]
239+
let z = mesh.components.positions[baseIndex + 2]
240+
return Position3(x, y, z)
241+
}
242+
nonmutating set {
243+
let baseIndex = Int(indices.p2)
244+
mesh.components.positions[baseIndex] = newValue.x
245+
mesh.components.positions[baseIndex + 1] = newValue.y
246+
mesh.components.positions[baseIndex + 2] = newValue.z
247+
}
248+
}
249+
250+
@inlinable
251+
public var p3: Position3 {
252+
get {
253+
let baseIndex = Int(indices.p3)
254+
let x = mesh.components.positions[baseIndex]
255+
let y = mesh.components.positions[baseIndex + 1]
256+
let z = mesh.components.positions[baseIndex + 2]
257+
return Position3(x, y, z)
258+
}
259+
nonmutating set {
260+
let baseIndex = Int(indices.p3)
261+
mesh.components.positions[baseIndex] = newValue.x
262+
mesh.components.positions[baseIndex + 1] = newValue.y
263+
mesh.components.positions[baseIndex + 2] = newValue.z
264+
}
265+
}
266+
267+
@inlinable
268+
public var normal: Direction3 {
269+
get {
270+
let baseIndex = Int(indices.normal)
271+
let x = mesh.components.normals[baseIndex]
272+
let y = mesh.components.normals[baseIndex + 1]
273+
let z = mesh.components.normals[baseIndex + 2]
274+
return Direction3(x, y, z)
275+
}
276+
nonmutating set {
277+
let baseIndex = Int(indices.normal)
278+
mesh.components.normals[baseIndex] = newValue.x
279+
mesh.components.normals[baseIndex + 1] = newValue.y
280+
mesh.components.normals[baseIndex + 2] = newValue.z
281+
}
282+
}
283+
284+
@inlinable
285+
public var attributes: CollisionAttributes {
286+
get {
287+
let rawValue = mesh.components.attributes[Int(indices.attributes)]
288+
return CollisionAttributes(rawValue: rawValue)
289+
}
290+
nonmutating set {
291+
mesh.components.attributes[Int(indices.attributes)] = newValue.rawValue
292+
}
293+
}
294+
295+
@usableFromInline
296+
internal init(mesh: CollisionMesh, triangleIndex: Int) {
297+
self.mesh = mesh
298+
self.indices = mesh.indices[triangleIndex]
299+
}
300+
}
301+
}
302+
303+
extension CollisionMesh {
304+
@inlinable
305+
public func withTriangle<A: CollisionAttributesGroup, ResultType>(
306+
atIndex index: Int,
307+
with attributesType: A.Type = BasicCollisionAttributes.self,
308+
_ provideTriangle: (_ triangle: borrowing Triangle<A>) -> ResultType
309+
) -> ResultType {
310+
let triangle = Triangle<A>(mesh: self, triangleIndex: index)
311+
let result = provideTriangle(triangle)
312+
return result
313+
}
314+
315+
@inlinable
316+
public func editTriangle<A: CollisionAttributesGroup, ResultType>(
317+
atIndex index: Int,
318+
with attributesType: A.Type = BasicCollisionAttributes.self,
319+
_ editTriangle: (_ triangle: borrowing MutableTriangle<A>) -> ResultType
320+
) -> ResultType {
321+
let triangle = MutableTriangle<A>(mesh: self, triangleIndex: index)
322+
let result = editTriangle(triangle)
323+
return result
324+
}
325+
}

0 commit comments

Comments
 (0)