Skip to content

Commit 5ad3afe

Browse files
committed
Refactor to use BinaryCodable
1 parent 41abe51 commit 5ad3afe

File tree

2 files changed

+107
-173
lines changed

2 files changed

+107
-173
lines changed

Sources/GameMath/3D Types/3D Physics/3D Colliders/Triangles/CollisionMesh.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ public final class CollisionMesh {
5858
public var triangleCount: Int {
5959
return indices.count
6060
}
61+
62+
private init(components: Components, indices: [TriangleIndices]) {
63+
self.components = components
64+
self.indices = indices
65+
}
6166

6267
public init(collisionTriangles triangles: [CollisionTriangle]) {
6368
assert(triangles.isEmpty == false)
@@ -323,3 +328,54 @@ extension CollisionMesh {
323328
return result
324329
}
325330
}
331+
332+
extension CollisionMesh.Components: BinaryCodable {
333+
public func encode(into data: inout ContiguousArray<UInt8>, version: BinaryCodableVersion) throws {
334+
try self.positions.encode(into: &data, version: version)
335+
try self.normals.encode(into: &data, version: version)
336+
try self.attributes.encode(into: &data, version: version)
337+
}
338+
339+
public init(decoding data: UnsafeRawBufferPointer, at offset: inout Int, version: BinaryCodableVersion) throws {
340+
self.positions = try .init(decoding: data, at: &offset, version: version)
341+
self.normals = try .init(decoding: data, at: &offset, version: version)
342+
self.attributes = try .init(decoding: data, at: &offset, version: version)
343+
}
344+
}
345+
346+
347+
extension CollisionMesh.TriangleIndices: BinaryCodable {
348+
public func encode(into data: inout ContiguousArray<UInt8>, version: BinaryCodableVersion) throws {
349+
try UInt16(self.p1).encode(into: &data, version: version)
350+
try UInt16(self.p2).encode(into: &data, version: version)
351+
try UInt16(self.p3).encode(into: &data, version: version)
352+
try UInt16(self.center).encode(into: &data, version: version)
353+
try UInt16(self.normal).encode(into: &data, version: version)
354+
try UInt16(self.faceNormal).encode(into: &data, version: version)
355+
try UInt16(self.attributes).encode(into: &data, version: version)
356+
}
357+
358+
public init(decoding data: UnsafeRawBufferPointer, at offset: inout Int, version: BinaryCodableVersion) throws {
359+
self.p1 = Int(try UInt16(decoding: data, at: &offset, version: version))
360+
self.p2 = Int(try UInt16(decoding: data, at: &offset, version: version))
361+
self.p3 = Int(try UInt16(decoding: data, at: &offset, version: version))
362+
self.center = Int(try UInt16(decoding: data, at: &offset, version: version))
363+
self.normal = Int(try UInt16(decoding: data, at: &offset, version: version))
364+
self.faceNormal = Int(try UInt16(decoding: data, at: &offset, version: version))
365+
self.attributes = Int(try UInt16(decoding: data, at: &offset, version: version))
366+
}
367+
}
368+
369+
extension CollisionMesh: BinaryCodable {
370+
public func encode(into data: inout ContiguousArray<UInt8>, version: BinaryCodableVersion) throws {
371+
try self.components.encode(into: &data, version: version)
372+
try self.indices.encode(into: &data, version: version)
373+
}
374+
375+
public convenience init(decoding data: UnsafeRawBufferPointer, at offset: inout Int, version: BinaryCodableVersion) throws {
376+
self.init(
377+
components: try .init(decoding: data, at: &offset, version: version),
378+
indices: try .init(decoding: data, at: &offset, version: version)
379+
)
380+
}
381+
}

Sources/GateEngine/Resources/Import & Export/Coding/CollisionMeshCoder.swift

Lines changed: 51 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -7,191 +7,69 @@
77

88
import GameMath
99

10-
public final class CollisionMeshEncoder {
11-
public func encode(_ collisionMesh: CollisionMesh) throws(EncodingError) -> Data {
12-
return try CollisionMeshEncodableRepresenation_v1.encode(collisionMesh)
13-
}
14-
15-
public enum EncodingError: Error {
16-
/// The encoding failed due to an unknown reason
17-
case encodingFailed
18-
}
19-
20-
public init() {
21-
22-
}
23-
}
10+
fileprivate let magic: UInt32 = 0x43_4D_53_48 // "CMSH"
2411

25-
public final class CollisionMeshDecoder {
26-
public func decode(_ data: Data) throws(DecodingError) -> CollisionMesh {
27-
guard data.isEmpty == false else { throw .decodingFailed }
28-
let header = data.withUnsafeBytes { data in
29-
data.load(as: CollisionMeshCodableHeader.self)
30-
}
31-
guard header.isValid else { throw .invalidFormat }
32-
guard let version = header.version else { throw .unsupportedVersion }
33-
guard header.documentLength == data.count else { throw .decodingFailed }
34-
switch version {
35-
case .v1:
36-
return try CollisionMeshEncodableRepresenation_v1.decode(data)
37-
}
38-
}
39-
40-
public enum DecodingError: Error {
41-
/// The data is empty or corrupted.
42-
case decodingFailed
43-
/// The data does not appear to be intended for this decoder.
44-
case invalidFormat
45-
/// This version of GateEngine doesn't support loading this file.
46-
/// - note: It's best to encode and decode with the same GateEngine version.
47-
case unsupportedVersion
48-
}
49-
50-
public init() {
12+
public struct CollisionMeshEncoder {
13+
public func encode(_ collisionMesh: CollisionMesh) throws(GateEngineError) -> Data {
14+
var header = BinaryCodableHeader(magic: magic)
5115

52-
}
53-
}
54-
55-
fileprivate struct CollisionMeshCodableHeader {
56-
static let magic1: UInt32 = 0x45544147 // "GATE"
57-
static let magic2: UInt32 = 0x434D5348 // "CMSH"
58-
let magic1: UInt32
59-
let magic2: UInt32
60-
let _version: UInt8
61-
private let _reserved1: UInt8 = 0
62-
private let _reserved2: UInt8 = 0
63-
private let _reserved3: UInt8 = 0
64-
var documentLength: UInt32 = 0
65-
66-
enum Version: UInt8 {
67-
case v1 = 1
68-
}
69-
70-
var version: Version? {
71-
return Version(rawValue: _version)
72-
}
73-
74-
init(version: Version) {
75-
self.magic1 = Self.magic1
76-
self.magic2 = Self.magic2
77-
self._version = version.rawValue
78-
}
79-
80-
var isValid: Bool {
81-
return self.magic1 == Self.magic1 && self.magic2 == Self.magic2
82-
}
83-
}
84-
85-
fileprivate struct CollisionMeshEncodableRepresenation_v1 {
86-
struct Header {
87-
var version: CollisionMeshCodableHeader
88-
var dataOffsets: DataOffsets
89-
struct DataOffsets {
90-
var indices: UInt32 = 0
91-
var positions: UInt32 = 0
92-
var normals: UInt32 = 0
93-
var attributes: UInt32 = 0
94-
}
16+
guard let version = header.version else { throw .failedToEncode("Malformed header.") }
9517

96-
init(collisionMesh: CollisionMesh) {
97-
self.version = CollisionMeshCodableHeader(version: .v1)
98-
self.dataOffsets = DataOffsets()
18+
var data: ContiguousArray<UInt8> = []
19+
withUnsafeBytes(of: header) {
20+
data.append(contentsOf: $0)
9921
}
100-
}
101-
struct CompactTriangleIndices {
102-
let p1: UInt16
103-
let p2: UInt16
104-
let p3: UInt16
105-
let center: UInt16
106-
let normal: UInt16
107-
let faceNormal: UInt16
108-
let attributes: UInt16
10922

110-
var native: CollisionMesh.TriangleIndices {
111-
return CollisionMesh.TriangleIndices(
112-
p1: Int(p1),
113-
p2: Int(p2),
114-
p3: Int(p3),
115-
center: Int(center),
116-
normal: Int(normal),
117-
faceNormal: Int(faceNormal),
118-
attributes: Int(attributes)
119-
)
23+
do {
24+
try collisionMesh.encode(into: &data, version: version)
25+
}catch let error as GateEngineError {
26+
// rethrow any GateEngineError
27+
throw error
28+
}catch{
29+
// Throw generic failure for other errors
30+
throw GateEngineError.failedToEncode("Unknown error: \(error.localizedDescription)")
12031
}
12132

122-
init(_ triangleIndices: CollisionMesh.TriangleIndices) {
123-
self.p1 = UInt16(triangleIndices.p1)
124-
self.p2 = UInt16(triangleIndices.p2)
125-
self.p3 = UInt16(triangleIndices.p3)
126-
self.center = UInt16(triangleIndices.center)
127-
self.normal = UInt16(triangleIndices.normal)
128-
self.faceNormal = UInt16(triangleIndices.faceNormal)
129-
self.attributes = UInt16(triangleIndices.attributes)
33+
// Write documentLength
34+
header.documentLength = UInt32(data.count)
35+
data.withUnsafeMutableBytes { data in
36+
withUnsafePointer(to: header, { header in
37+
data.baseAddress!.copyMemory(from: header, byteCount: MemoryLayout<BinaryCodableHeader>.size)
38+
})
13039
}
40+
41+
return Data(data)
13142
}
13243

133-
static func encode(_ collisionMesh: CollisionMesh) throws(CollisionMeshEncoder.EncodingError) -> Data {
134-
var header = Header(collisionMesh: collisionMesh)
135-
var data = Data(repeating: 0, count: MemoryLayout<Header>.size)
136-
137-
header.dataOffsets.indices = UInt32(data.count)
138-
collisionMesh.indices.map({CompactTriangleIndices($0)}).withUnsafeBytes { bytes in
139-
data.append(contentsOf: bytes)
140-
}
141-
142-
header.dataOffsets.positions = UInt32(data.count)
143-
collisionMesh.components.positions.withUnsafeBytes { bytes in
144-
data.append(contentsOf: bytes)
145-
}
146-
147-
header.dataOffsets.normals = UInt32(data.count)
148-
collisionMesh.components.normals.withUnsafeBytes { bytes in
149-
data.append(contentsOf: bytes)
150-
}
151-
152-
header.dataOffsets.attributes = UInt32(data.count)
153-
collisionMesh.components.attributes.withUnsafeBytes { bytes in
154-
data.append(contentsOf: bytes)
155-
}
156-
157-
// Update header documentLength
158-
header.version.documentLength = UInt32(data.count)
44+
public init() {
15945

160-
// Replace header bytes
161-
data.withUnsafeMutableBytes { data in
162-
withUnsafeBytes(of: header) { headerBytes in
163-
data.copyMemory(from: headerBytes)
164-
}
46+
}
47+
}
48+
49+
public final class CollisionMeshDecoder {
50+
public func decode(_ data: Data) throws(GateEngineError) -> CollisionMesh {
51+
do {
52+
return try data.withUnsafeBytes({ (data: UnsafeRawBufferPointer) throws -> CollisionMesh in
53+
var offset: Int = 0
54+
let header = data.load(fromByteOffset: offset, as: BinaryCodableHeader.self)
55+
offset += MemoryLayout<BinaryCodableHeader>.size
56+
57+
guard let version = header.validatedVersionMatching(magic: magic, documentLength: UInt32(data.count)) else {
58+
throw GateEngineError.failedToDecode("Malformed header.")
59+
}
60+
61+
return try CollisionMesh(decoding: data, at: &offset, version: version)
62+
})
63+
}catch let error as GateEngineError {
64+
// rethrow any GateEngineError
65+
throw error
66+
}catch{
67+
// Throw generic failure for other errors
68+
throw GateEngineError.failedToDecode("Unknown error: \(error.localizedDescription)")
16569
}
166-
167-
return data
16870
}
169-
170-
static func decode(_ data: Data) throws(CollisionMeshDecoder.DecodingError) -> CollisionMesh {
171-
return data.withUnsafeBytes({ (data: UnsafeRawBufferPointer) -> CollisionMesh in
172-
let header = data.load(as: Header.self)
173-
174-
let indicesOffset = Int(header.dataOffsets.indices)
175-
let positionsOffset = Int(header.dataOffsets.positions)
176-
let normalsOffset = Int(header.dataOffsets.normals)
177-
let attributesOffset = Int(header.dataOffsets.attributes)
178-
179-
let indicesCount = (positionsOffset - indicesOffset) / MemoryLayout<CompactTriangleIndices>.size
180-
let positionsCount = (normalsOffset - positionsOffset) / MemoryLayout<Float>.size
181-
let normalsCount = (attributesOffset - normalsOffset) / MemoryLayout<Float>.size
182-
let attributesCount = (Int(header.version.documentLength) - attributesOffset) / MemoryLayout<UInt64>.size
183-
184-
let indices = UnsafeBufferPointer(start: data.baseAddress!.advanced(by: indicesOffset).assumingMemoryBound(to: CompactTriangleIndices.self), count: indicesCount)
185-
let positions = UnsafeBufferPointer(start: data.baseAddress!.advanced(by: positionsOffset).assumingMemoryBound(to: Float.self), count: positionsCount)
186-
let normals = UnsafeBufferPointer(start: data.baseAddress!.advanced(by: normalsOffset).assumingMemoryBound(to: Float.self), count: normalsCount)
187-
let attributes = UnsafeBufferPointer(start: data.baseAddress!.advanced(by: attributesOffset).assumingMemoryBound(to: UInt64.self), count: attributesCount)
188-
189-
return CollisionMesh(
190-
indices: indices.map({$0.native}),
191-
positions: Array(positions),
192-
normals: Array(normals),
193-
attributes: Array(attributes)
194-
)
195-
})
71+
72+
public init() {
73+
19674
}
19775
}

0 commit comments

Comments
 (0)