119 lines
3.4 KiB
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())
|
|
}
|