Skip to content

Commit 342d6cc

Browse files
committed
Expand encoding, importing, and exporting
1 parent d1a059a commit 342d6cc

17 files changed

+841
-303
lines changed

Sources/GateEngine/Resources/Geometry/Geometry.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,18 @@ public protocol GeometryImporter: ResourceImporter {
138138
public struct GeometryImporterOptions: Equatable, Hashable, Sendable {
139139
public var subobjectName: String? = nil
140140
public var applyRootTransform: Bool = false
141+
public var makeInstancesReal: Bool = false
141142

142143
/// Unique to each importer
143144
public var option1: Bool = false
144145

145146
public static func with(name: String? = nil, applyRootTransform: Bool = false) -> Self {
146147
return GeometryImporterOptions(subobjectName: name, applyRootTransform: applyRootTransform)
147148
}
149+
150+
public static func with(name: String? = nil, makeInstancesReal: Bool = false) -> Self {
151+
return GeometryImporterOptions(subobjectName: name, makeInstancesReal: makeInstancesReal)
152+
}
148153

149154
public static var applyRootTransform: GeometryImporterOptions {
150155
return GeometryImporterOptions(applyRootTransform: true)

Sources/GateEngine/Resources/Geometry/Raw/RawGeometry.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,29 @@ extension Array where Element == RawGeometry {
291291
return triangles
292292
}
293293
}
294+
295+
extension RawGeometry: BinaryCodable {
296+
public func encode(into data: inout ContiguousArray<UInt8>, version: BinaryCodableVersion) throws {
297+
try Float.encodeArray(positions, into: &data, version: version)
298+
try uvSets.count.encode(into: &data, version: version)
299+
for uvSet in uvSets {
300+
try Float.encodeArray(uvSet, into: &data, version: version)
301+
}
302+
try Float.encodeArray(normals, into: &data, version: version)
303+
try Float.encodeArray(tangents, into: &data, version: version)
304+
try Float.encodeArray(colors, into: &data, version: version)
305+
try UInt16.encodeArray(indices, into: &data, version: version)
306+
}
307+
308+
public init(decoding data: UnsafeRawBufferPointer, at offset: inout Int, version: BinaryCodableVersion) throws {
309+
self.positions = try Float.decodeArray(data, offset: &offset, version: version)
310+
let uvSetsCount = try Int(decoding: data, at: &offset, version: version)
311+
self.uvSets = try (0..<uvSetsCount).map({_ in
312+
try Float.decodeArray(data, offset: &offset, version: version)
313+
})
314+
self.normals = try Float.decodeArray(data, offset: &offset, version: version)
315+
self.tangents = try Float.decodeArray(data, offset: &offset, version: version)
316+
self.colors = try Float.decodeArray(data, offset: &offset, version: version)
317+
self.indices = try UInt16.decodeArray(data, offset: &offset, version: version)
318+
}
319+
}

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

Lines changed: 50 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -5,185 +5,69 @@
55
* http://stregasgate.com
66
*/
77

8-
public struct RawGeometryEncoder {
9-
public func encode(_ value: RawGeometry) throws(EncodingError) -> Data {
10-
return try RawGeometryEncodableRepresenation_v1.encode(value)
11-
}
12-
13-
public enum EncodingError: Error {
14-
/// The encoding failed due to an unknown reason
15-
case encodingFailed
16-
}
17-
18-
public init() {
19-
20-
}
21-
}
8+
fileprivate let magic: UInt32 = 0x47_45_4F_4D // "GEOM"
229

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

50-
}
51-
}
52-
53-
fileprivate struct RawGeometryCodableHeader {
54-
static let magic1: UInt32 = 0x45544147 // "GATE"
55-
static let magic2: UInt32 = 0x47574152 // "RAWG"
56-
let magic1: UInt32
57-
let magic2: UInt32
58-
let _version: UInt8
59-
private let _reserved1: UInt8 = 0
60-
private let _reserved2: UInt8 = 0
61-
private let _reserved3: UInt8 = 0
62-
let documentLength: UInt32 = 0
63-
64-
enum Version: UInt8 {
65-
case v1 = 1
66-
}
67-
68-
var version: Version? {
69-
return Version(rawValue: _version)
70-
}
71-
72-
init(version: Version) {
73-
self.magic1 = Self.magic1
74-
self.magic2 = Self.magic2
75-
self._version = version.rawValue
76-
}
77-
78-
var isValid: Bool {
79-
return self.magic1 == Self.magic1 && self.magic2 == Self.magic2
80-
}
81-
}
82-
83-
fileprivate struct RawGeometryEncodableRepresenation_v1 {
84-
struct Header {
85-
var version: RawGeometryCodableHeader
86-
var positionValueCount: UInt32
87-
var uvSetCount: UInt8
88-
var normalValuesCount: UInt32
89-
var tangentValuesCount: UInt32
90-
var colorValuesCount: UInt32
91-
var indicesCount: UInt32
14+
guard let version = header.version else { throw .failedToEncode("Malformed header.") }
9215

93-
init(rawGeometry: RawGeometry) {
94-
self.version = RawGeometryCodableHeader(version: .v1)
95-
self.positionValueCount = UInt32(rawGeometry.positions.count)
96-
self.uvSetCount = UInt8(rawGeometry.uvSets.count)
97-
self.normalValuesCount = UInt32(rawGeometry.normals.count)
98-
self.tangentValuesCount = UInt32(rawGeometry.tangents.count)
99-
self.colorValuesCount = UInt32(rawGeometry.colors.count)
100-
self.indicesCount = UInt32(rawGeometry.indices.count)
101-
}
102-
}
103-
104-
static func encode(_ rawGeometry: RawGeometry) throws(RawGeometryEncoder.EncodingError) -> Data {
105-
var data = Data()
106-
withUnsafeBytes(of: Header(rawGeometry: rawGeometry)) {
16+
var data: ContiguousArray<UInt8> = []
17+
withUnsafeBytes(of: header) {
10718
data.append(contentsOf: $0)
10819
}
109-
rawGeometry.positions.withUnsafeBytes { positions in
110-
data.append(contentsOf: positions)
111-
}
112-
for uvSetIndex in rawGeometry.uvSets.indices {
113-
let uvsCount = UInt32(rawGeometry.uvSets[uvSetIndex].count)
114-
withUnsafeBytes(of: uvsCount) { count in
115-
data.append(contentsOf: count)
116-
}
117-
rawGeometry.uvSets[uvSetIndex].withUnsafeBytes { uvs in
118-
data.append(contentsOf: uvs)
119-
}
120-
}
121-
rawGeometry.normals.withUnsafeBytes { normals in
122-
data.append(contentsOf: normals)
123-
}
124-
rawGeometry.tangents.withUnsafeBytes { tangents in
125-
data.append(contentsOf: tangents)
126-
}
127-
rawGeometry.colors.withUnsafeBytes { colors in
128-
data.append(contentsOf: colors)
129-
}
130-
rawGeometry.indices.withUnsafeBytes { indices in
131-
data.append(contentsOf: indices)
20+
21+
do {
22+
try rawGeometry.encode(into: &data, version: version)
23+
}catch let error as GateEngineError {
24+
// rethrow any GateEngineError
25+
throw error
26+
}catch{
27+
// Throw generic failure for other errors
28+
throw GateEngineError.failedToEncode("Unknown error: \(error.localizedDescription)")
13229
}
13330

13431
// Write documentLength
32+
header.documentLength = UInt32(data.count)
13533
data.withUnsafeMutableBytes { data in
136-
withUnsafePointer(to: UInt32(data.count), { count in
137-
data.baseAddress!.advanced(by: 12).copyMemory(from: count, byteCount: MemoryLayout<UInt32>.size)
34+
withUnsafePointer(to: header, { header in
35+
data.baseAddress!.copyMemory(from: header, byteCount: MemoryLayout<BinaryCodableHeader>.size)
13836
})
13937
}
14038

141-
return data
39+
return Data(data)
14240
}
14341

144-
static func decode(_ data: Data) throws(RawGeometryDecoder.DecodingError) -> RawGeometry {
145-
return data.withUnsafeBytes({ (data: UnsafeRawBufferPointer) -> RawGeometry in
146-
var offset: Int = 0
147-
let header = data.load(fromByteOffset: offset, as: Header.self)
148-
offset += MemoryLayout<Header>.size
149-
150-
let positionsCount = Int(header.positionValueCount)
151-
let positions: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: positionsCount))
152-
offset += MemoryLayout<Float>.size * positionsCount
153-
154-
var uvSets: [[Float]] = []
155-
for _ in 0 ..< header.uvSetCount {
156-
let uvCount: Int = Int(data.load(fromByteOffset: offset, as: UInt32.self))
157-
offset += MemoryLayout<UInt32>.size
158-
let uvs: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: uvCount))
159-
uvSets.append(uvs)
160-
offset += MemoryLayout<Float>.size * uvCount
161-
}
162-
163-
let normalsCount = Int(header.normalValuesCount)
164-
let normals: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: normalsCount))
165-
offset += MemoryLayout<Float>.size * normalsCount
166-
167-
let tangentsCount: Int = Int(header.tangentValuesCount)
168-
let tangents: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: tangentsCount))
169-
offset += MemoryLayout<Float>.size * tangentsCount
170-
171-
let colorsCount: Int = Int(header.colorValuesCount)
172-
let colors: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: colorsCount))
173-
offset += MemoryLayout<Float>.size * colorsCount
174-
175-
let indicesCount: Int = Int(header.indicesCount)
176-
let indices: [UInt16] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: UInt16.self), count: indicesCount))
177-
offset += MemoryLayout<UInt16>.size * indicesCount
178-
179-
return RawGeometry(
180-
positions: positions,
181-
uvSets: uvSets,
182-
normals: normals,
183-
tangents: tangents,
184-
colors: colors,
185-
indices: indices
186-
)
187-
})
42+
public init() {
43+
44+
}
45+
}
46+
47+
public struct RawGeometryDecoder {
48+
public func decode(_ data: Data) throws(GateEngineError) -> RawGeometry {
49+
do {
50+
return try data.withUnsafeBytes({ (data: UnsafeRawBufferPointer) throws -> RawGeometry in
51+
var offset: Int = 0
52+
let header = data.load(fromByteOffset: offset, as: BinaryCodableHeader.self)
53+
offset += MemoryLayout<BinaryCodableHeader>.size
54+
55+
guard let version = header.validatedVersionMatching(magic: magic, documentLength: UInt32(data.count)) else {
56+
throw GateEngineError.failedToDecode("Malformed header.")
57+
}
58+
59+
return try RawGeometry(decoding: data, at: &offset, version: version)
60+
})
61+
}catch let error as GateEngineError {
62+
// rethrow any GateEngineError
63+
throw error
64+
}catch{
65+
// Throw generic failure for other errors
66+
throw GateEngineError.failedToDecode("Unknown error: \(error.localizedDescription)")
67+
}
68+
}
69+
70+
public init() {
71+
18872
}
18973
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright © 2025 Dustin Collins (Strega's Gate)
3+
* All Rights Reserved.
4+
*
5+
* http://stregasgate.com
6+
*/
7+
8+
fileprivate let magic: UInt32 = 0x53_41_4E_49 // "SANI"
9+
10+
public struct RawSkeletalAnimationEncoder {
11+
public func encode(_ rawSkeletalAnimation: RawSkeletalAnimation) throws(GateEngineError) -> Data {
12+
var header = BinaryCodableHeader(magic: magic)
13+
14+
guard let version = header.version else { throw .failedToEncode("Malformed header.") }
15+
16+
var data: ContiguousArray<UInt8> = []
17+
withUnsafeBytes(of: header) {
18+
data.append(contentsOf: $0)
19+
}
20+
21+
do {
22+
try rawSkeletalAnimation.encode(into: &data, version: version)
23+
}catch let error as GateEngineError {
24+
// rethrow any GateEngineError
25+
throw error
26+
}catch{
27+
// Throw generic failure for other errors
28+
throw GateEngineError.failedToEncode("Unknown error: \(error.localizedDescription)")
29+
}
30+
31+
// Write documentLength
32+
header.documentLength = UInt32(data.count)
33+
data.withUnsafeMutableBytes { data in
34+
withUnsafePointer(to: header, { header in
35+
data.baseAddress!.copyMemory(from: header, byteCount: MemoryLayout<BinaryCodableHeader>.size)
36+
})
37+
}
38+
39+
return Data(data)
40+
}
41+
42+
public init() {
43+
44+
}
45+
}
46+
47+
public struct RawSkeletalAnimationDecoder {
48+
public func decode(_ data: Data) throws(GateEngineError) -> RawSkeletalAnimation {
49+
do {
50+
return try data.withUnsafeBytes({ (data: UnsafeRawBufferPointer) throws -> RawSkeletalAnimation in
51+
var offset: Int = 0
52+
let header = data.load(fromByteOffset: offset, as: BinaryCodableHeader.self)
53+
offset += MemoryLayout<BinaryCodableHeader>.size
54+
55+
guard let version = header.validatedVersionMatching(magic: magic, documentLength: UInt32(data.count)) else {
56+
throw GateEngineError.failedToDecode("Malformed header.")
57+
}
58+
59+
return try RawSkeletalAnimation(decoding: data, at: &offset, version: version)
60+
})
61+
}catch let error as GateEngineError {
62+
// rethrow any GateEngineError
63+
throw error
64+
}catch{
65+
// Throw generic failure for other errors
66+
throw GateEngineError.failedToDecode("Unknown error: \(error.localizedDescription)")
67+
}
68+
}
69+
70+
public init() {
71+
72+
}
73+
}

0 commit comments

Comments
 (0)