Files
CavesOfSwift/Sources/JolkEngine/Loaders/VMeshLoader.swift

119 lines
3.4 KiB
Swift

import Foundation
import Maths
struct VMeshLoader: LoaderProtocol
{
typealias T = Mesh<VMeshVertex>
func load(url: URL) -> T? { try? Self.read(url: url) }
func load(url: URL, content: inout ContentManager) -> T? { load(url: url) }
static func read(url: URL) throws -> T
{
let file = try FileHandle(forReadingFrom: url)
// read header fields
let header = VMeshHeader(
magic: try file.read(upToCount: 4),
version: try file.read(as: UInt8.self).littleEndian,
idxSize: try file.read(as: UInt8.self).littleEndian,
subMeshCount: try file.read(as: UInt16.self).littleEndian,
vertexCount: try file.read(as: UInt32.self).littleEndian,
indexCount: try file.read(as: UInt32.self).littleEndian)
// header sanity checks
guard header.magic == Data("VMSH".utf8)
else { throw NSError() }
guard header.version == 2
else { throw NSError() }
// check index type
let idxSize = switch header.idxSize
{
case 1, 2, 4: header.idxSize
default: throw NSError()
}
// fail on empty data lengths
guard header.vertexCount > 0, header.indexCount > 0
else { throw NSError() }
// read submeshes
var subMeshes = [VMeshSubMesh](repeating: .empty, count: Int(header.subMeshCount))
for i in 0..<Int(header.subMeshCount)
{
guard let nameStr = try file.read(upToCount: 24)
else { throw NSError() }
subMeshes[i] = .init(
name: String(decoding: nameStr, as: UTF8.self),
offset: try file.read(as: UInt32.self),
count: try file.read(as: UInt32.self))
}
// read vertices
var vertices = [VMeshVertex](repeating: .empty, count: Int(header.vertexCount))
for i in 0..<Int(header.vertexCount)
{
vertices[i] = .init(
position: .init(try file.read(as: Float.self), try file.read(as: Float.self), try file.read(as: Float.self)),
//texCoord: .init(try file.read(as: UInt16.self), try file.read(as: UInt16.self)),
texCoord: .init(
Float(try file.read(as: UInt16.self)) / Float(UInt16.max),
1.0 - Float(try file.read(as: UInt16.self)) / Float(UInt16.max)),
normal: .init(try file.read(as: Int8.self), try file.read(as: Int8.self), try file.read(as: Int8.self), try file.read(as: Int8.self)),
tangent: .init(try file.read(as: Int8.self), try file.read(as: Int8.self), try file.read(as: Int8.self), try file.read(as: Int8.self)))
}
let indices = switch idxSize
{
case 1: try (0..<Int(header.indexCount)).map { _ in UInt16(try file.read(as: UInt8.self)) }
case 2: try (0..<Int(header.indexCount)).map { _ in UInt16(try file.read(as: UInt16.self)) }
case 4: try (0..<Int(header.indexCount)).map { _ in UInt16(try file.read(as: UInt32.self)) }
default: fatalError()
}
// create & return mesh
return .init(
vertices: vertices, indices: indices,
subMeshes: .init(uniqueKeysWithValues:
subMeshes.map { ($0.name, .init(start: Int($0.offset), length: Int($0.count))) }
)
)
}
struct VMeshHeader
{
let magic: Data?
let version, idxSize: UInt8
let subMeshCount: UInt16
let vertexCount, indexCount: UInt32
}
}
public struct VMeshVertex: Vertex
{
let position: Vec3f
//let texCoord: SIMD2<UInt16>
let texCoord: Vec2f
let normal: SIMD4<Int8>
let tangent: SIMD4<Int8>
}
extension VMeshVertex
{
static let empty: Self = .init(position: .init(), texCoord: .init(), normal: .init(), tangent: .init())
}
struct VMeshSubMesh
{
let name: String
let offset: UInt32, count: UInt32
}
extension VMeshSubMesh
{
static let empty: Self = .init(name: .init(), offset: .init(), count: .init())
}