Skip to content

Commit 8ffc541

Browse files
committed
Add texture importing for GLTF
1 parent 0e14db6 commit 8ffc541

File tree

2 files changed

+74
-19
lines changed

2 files changed

+74
-19
lines changed

Sources/GateEngine/Resources/Import & Export/Importers/GLTransmissionFormat.swift

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private class GLTF: Decodable {
3333
let scenes: [Scene]
3434
struct Scene: Decodable {
3535
let name: String
36-
let nodes: [Int]
36+
let nodes: [Int]?
3737
}
3838

3939
let nodes: [Node]
@@ -80,6 +80,14 @@ private class GLTF: Decodable {
8080
return transform
8181
}
8282
}
83+
84+
let images: [Image]?
85+
struct Image: Decodable {
86+
let name: String
87+
let mimeType: String
88+
let uri: String?
89+
let bufferView: Int?
90+
}
8391

8492
let meshes: [Mesh]?
8593
struct Mesh: Decodable {
@@ -216,7 +224,9 @@ private class GLTF: Decodable {
216224
}
217225

218226
lazy var cachedBuffers: [Data?] = Array(repeating: nil, count: buffers.count)
219-
func buffer(at index: Int) async -> Data? {
227+
func buffer(at index: Int) -> Data? {
228+
// Buffer 0 is pre-cached for glb files
229+
// So `existing` will always be present for index 0 of a glb file
220230
if let existing = cachedBuffers[index] {
221231
return existing
222232
}
@@ -227,7 +237,7 @@ private class GLTF: Decodable {
227237
let base64String = uri[uri.index(after: index)...]
228238
buffer = Data(base64Encoded: String(base64String))
229239
} else {
230-
buffer = try? await Platform.current.loadResource(
240+
buffer = try? Platform.current.synchronousLoadResource(
231241
from: self.baseURL!.appendingPathComponent(uri).path
232242
)
233243
}
@@ -242,7 +252,7 @@ private class GLTF: Decodable {
242252
let bufferView = bufferViews[accessor.bufferView]
243253
let count = accessor.count * accessor.primitiveCount
244254

245-
return await buffer(at: bufferView.buffer)?.withUnsafeBytes {
255+
return buffer(at: bufferView.buffer)?.withUnsafeBytes {
246256
switch accessor.componentType {
247257
case .uint8:
248258
typealias Scalar = UInt8
@@ -326,7 +336,7 @@ private class GLTF: Decodable {
326336
let bufferView = bufferViews[accessor.bufferView]
327337
let count = accessor.count * accessor.primitiveCount
328338

329-
return await buffer(at: bufferView.buffer)?.withUnsafeBytes {
339+
return buffer(at: bufferView.buffer)?.withUnsafeBytes {
330340
switch accessor.componentType {
331341
case .uint8:
332342
typealias Scalar = UInt8
@@ -561,16 +571,16 @@ extension GLTransmissionFormat: GeometryImporter {
561571
}
562572

563573
if geometries.count == 1 {
564-
if options.applyRootTransform {
565-
let transform = gltf.nodes[gltf.scenes[gltf.scene].nodes[0]].transform.createMatrix()
574+
if options.applyRootTransform, let nodeIndex = gltf.scenes[gltf.scene].nodes?[0] {
575+
let transform = gltf.nodes[nodeIndex].transform.createMatrix()
566576
return geometries[0] * transform
567577
}else{
568578
return geometries[0]
569579
}
570580
}else{
571581
var geometry = RawGeometry(geometries: geometries)
572-
if options.applyRootTransform {
573-
let transform = gltf.nodes[gltf.scenes[gltf.scene].nodes[0]].transform.createMatrix()
582+
if options.applyRootTransform, let nodeIndex = gltf.scenes[gltf.scene].nodes?[0] {
583+
let transform = gltf.nodes[nodeIndex].transform.createMatrix()
574584
geometry = geometry * transform
575585
}
576586
return geometry
@@ -595,9 +605,11 @@ extension GLTransmissionFormat: SkinImporter {
595605
}
596606
return nil
597607
}
598-
for index in gltf.scenes[gltf.scene].nodes {
599-
if let value = findIn(index) {
600-
return value
608+
if let sceneNodes = gltf.scenes[gltf.scene].nodes {
609+
for index in sceneNodes {
610+
if let value = findIn(index) {
611+
return value
612+
}
601613
}
602614
}
603615
return nil
@@ -608,7 +620,7 @@ extension GLTransmissionFormat: SkinImporter {
608620
in gltf: GLTF
609621
) async -> [Matrix4x4]? {
610622
guard
611-
let buffer = await gltf.buffer(at: bufferView.buffer)?.advanced(
623+
let buffer = gltf.buffer(at: bufferView.buffer)?.advanced(
612624
by: bufferView.byteOffset
613625
)
614626
else { return nil }
@@ -708,12 +720,14 @@ extension GLTransmissionFormat: SkeletonImporter {
708720
}
709721
return nil
710722
}
711-
for index in gltf.scenes[gltf.scene].nodes {
712-
if let value = findIn(index) {
713-
return value
723+
if let sceneNodes = gltf.scenes[gltf.scene].nodes {
724+
for index in sceneNodes {
725+
if let value = findIn(index) {
726+
return value
727+
}
714728
}
715729
}
716-
return gltf.scenes[gltf.scene].nodes.first
730+
return gltf.scenes[gltf.scene].nodes?.first
717731
}
718732

719733
public func process(data: Data, baseURL: URL, options: SkeletonImporterOptions) async throws -> Skeleton.Joint {
@@ -881,7 +895,7 @@ extension GLTransmissionFormat: ObjectAnimation3DImporter {
881895

882896
var objectAnimation = ObjectAnimation3D.Animation()
883897

884-
var timeMax: Float = -1_000_000_000
898+
var timeMax: Float = .nan
885899

886900
for channel in animation.channels {
887901
let sampler = animation.samplers[channel.sampler]
@@ -948,3 +962,43 @@ extension GLTransmissionFormat: ObjectAnimation3DImporter {
948962
return ObjectAnimation3DBackend(name: animation.name, duration: timeMax, animation: objectAnimation)
949963
}
950964
}
965+
966+
extension GLTransmissionFormat: TextureImporter {
967+
// TODO: Supports only PNG. Add other formats (JPEG, WebP, ...)
968+
public func loadTexture(options: TextureImporterOptions) throws(GateEngineError) -> (data: Data, size: GameMath.Size2) {
969+
let imageData: Data
970+
func loadImageData(image: GLTF.Image) throws(GateEngineError) -> Data {
971+
if let uri = image.uri {
972+
return try Platform.current.synchronousLoadResource(
973+
from: self.gltf.baseURL!.appendingPathComponent(uri).path
974+
)
975+
}else if let bufferIndex = image.bufferView {
976+
let view = self.gltf.bufferViews[bufferIndex]
977+
978+
if let buffer = self.gltf.buffer(at: view.buffer) {
979+
return Data(buffer[view.byteOffset..<view.byteOffset+view.byteLength])
980+
}else{
981+
throw .failedToDecode("The file does not contain a buffer with index: \(view.buffer)")
982+
}
983+
}else{
984+
throw .failedToDecode("The gltf file is using an unsupported feature or may be corrupt.")
985+
}
986+
}
987+
if let name = options.subobjectName {
988+
if let image = self.gltf.images?.first(where: {$0.name.caseInsensitiveCompare(name) == .orderedSame}) {
989+
imageData = try loadImageData(image: image)
990+
}else{
991+
throw .failedToLoad("No subobject found with name: \(name)")
992+
}
993+
}else{
994+
if let image = self.gltf.images?.first {
995+
imageData = try loadImageData(image: image)
996+
}else{
997+
throw .failedToLoad("No images found in file.")
998+
}
999+
}
1000+
1001+
let image = try PNGDecoder().decode(imageData)
1002+
return (image.data, Size2(Float(image.width), Float(image.height)))
1003+
}
1004+
}

Sources/GateEngine/Resources/ResourceManager.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ public extension ResourceImporter {
5959
extension ResourceManager {
6060
struct Importers {
6161
internal var textureImporters: [any TextureImporter.Type] = [
62-
PNGImporter.self
62+
PNGImporter.self,
63+
GLTransmissionFormat.self,
6364
]
6465

6566
internal var geometryImporters: [any GeometryImporter.Type] = [

0 commit comments

Comments
 (0)