The Skung Rockification of Ziggy Skungdust and the SIMD's
This commit is contained in:
239
Sources/JolkEngine/Loaders/DDSLoader.swift
Normal file
239
Sources/JolkEngine/Loaders/DDSLoader.swift
Normal file
@ -0,0 +1,239 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
struct DDSLoader: LoaderProtocol
|
||||
{
|
||||
typealias T = Image
|
||||
|
||||
func load(url: URL) -> T?
|
||||
{
|
||||
guard let image = try? DDSLoader.read(url: url) else { return nil }
|
||||
return image
|
||||
}
|
||||
|
||||
func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) }
|
||||
|
||||
static func read(url: URL) throws -> T
|
||||
{
|
||||
let file = try FileHandle(forReadingFrom: url)
|
||||
let header = DDSHeader(
|
||||
magic: .init(try file.read(upToCount: 4)),
|
||||
size: try file.read(as: UInt32.self).littleEndian,
|
||||
flags: .init(rawValue: try file.read(as: UInt32.self).littleEndian),
|
||||
height: try file.read(as: UInt32.self).littleEndian,
|
||||
width: try file.read(as: UInt32.self).littleEndian,
|
||||
pitch: try file.read(as: UInt32.self).littleEndian,
|
||||
depth: try file.read(as: UInt32.self).littleEndian,
|
||||
mipNum: try file.read(as: UInt32.self).littleEndian,
|
||||
reserved1: try (0..<11).map { _ in try file.read(as: UInt32.self) },
|
||||
pixFmt: DDSPixelFormat(
|
||||
size: try file.read(as: UInt32.self).littleEndian,
|
||||
flags: .init(rawValue: try file.read(as: UInt32.self).littleEndian),
|
||||
fourCC: .init(try file.read(upToCount: 4)),
|
||||
bits: try file.read(as: UInt32.self).littleEndian,
|
||||
rMask: try file.read(as: UInt32.self).littleEndian,
|
||||
gMask: try file.read(as: UInt32.self).littleEndian,
|
||||
bMask: try file.read(as: UInt32.self).littleEndian,
|
||||
aMask: try file.read(as: UInt32.self).littleEndian),
|
||||
caps1: try file.read(as: UInt32.self).littleEndian,
|
||||
caps2: try file.read(as: UInt32.self).littleEndian,
|
||||
caps3: try file.read(as: UInt32.self).littleEndian,
|
||||
caps4: try file.read(as: UInt32.self).littleEndian,
|
||||
reserved2: try file.read(as: UInt32.self))
|
||||
|
||||
guard header.magic == FourCC("DDS "),
|
||||
header.size == 124 // MemoryLayout<DDSHeader>.size
|
||||
else { throw NSError() /* ("DDS Header size mismatch") */ }
|
||||
|
||||
//#if MemoryLayout<DDSHeaderDX10>.size != 20
|
||||
//#error("DDS DX10 Extended Header size mismatch")
|
||||
//#endif
|
||||
|
||||
let format = try Image.Format.resolve(pixFmt: header.pixFmt)
|
||||
let size = (0...header.mipNum).reduce(0) { size, i in
|
||||
let width = header.width &>> i, height = header.height &>> i
|
||||
guard width > 0 && height > 0 else { return size }
|
||||
return size + format.computeSize(width: width, height: height)
|
||||
}
|
||||
|
||||
guard let data = try file.read(upToCount: size)
|
||||
else { throw NSError() }
|
||||
|
||||
return .init(data,
|
||||
format: format,
|
||||
width: Int(header.width), height: Int(header.height),
|
||||
mipLevels: Int(header.mipNum))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension Image.Format
|
||||
{
|
||||
static func resolve(pixFmt: DDSPixelFormat) throws -> Self
|
||||
{
|
||||
switch pixFmt
|
||||
{
|
||||
case DDSPixelFormat(.rgb, bits: 24, r: 0xFF0000, g: 0x00FF00, b: 0x0000FF): .rgb888
|
||||
case DDSPixelFormat(.rgb, bits: 24, r: 0x0000FF, g: 0x00FF00, b: 0xFF0000): .bgr888
|
||||
case DDSPixelFormat(.rgba, bits: 32, r: 0x00FF0000, g: 0x0000FF00, b: 0x000000FF, a: 0xFF000000): .argb8888
|
||||
case DDSPixelFormat(.rgba, bits: 32, r: 0x000000FF, g: 0x0000FF00, b: 0x00FF0000, a: 0xFF000000): .abgr8888
|
||||
//case DDSPixelFormat(.luminance, bits: 8, r: 0xFF): .l8
|
||||
//case DDSPixelFormat(.alpha, bits: 8, a: 0xFF): .a8
|
||||
//case DDSPixelFormat(.luminance, bits: 16, a: 0xFFFF): .l16
|
||||
//case DDSPixelFormat(.luminanceA, bits: 16, r: 0x00FF, a: 0xFF00): .al88
|
||||
case DDSPixelFormat(.fourCC, fourCC: .init("DXT1")): .s3tc_bc1
|
||||
case DDSPixelFormat(.fourCC, fourCC: .init("DXT2")): .s3tc_bc2_premul
|
||||
case DDSPixelFormat(.fourCC, fourCC: .init("DXT3")): .s3tc_bc2
|
||||
case DDSPixelFormat(.fourCC, fourCC: .init("DXT4")): .s3tc_bc3_premul
|
||||
case DDSPixelFormat(.fourCC, fourCC: .init("DXT5")): .s3tc_bc3
|
||||
case DDSPixelFormat(.fourCC, fourCC: .init("ATI1")): .rgtc_bc4
|
||||
case DDSPixelFormat(.fourCC, fourCC: .init("ATI2")): .rgtc_bc5_3dc
|
||||
default: throw NSError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension Image.Format
|
||||
{
|
||||
private func dxtcSize(_ w: UInt32, _ h: UInt32) -> Int
|
||||
{
|
||||
let mul = [.s3tc_bc1, .rgtc_bc4].contains(self) ? 8 : 16
|
||||
let cw = Int(w + 3) / 4
|
||||
let ch = Int(h + 3) / 4
|
||||
return cw * ch * mul
|
||||
}
|
||||
|
||||
func computeSize(width w: UInt32, height h: UInt32) -> Int
|
||||
{
|
||||
switch self
|
||||
{
|
||||
//case .l8, .a8: Int(w) * Int(h)
|
||||
//case .l16, .al88: Int(w) * Int(h) * 2
|
||||
case .rgb888, .bgr888: Int(w) * Int(h) * 3
|
||||
case .argb8888, .abgr8888: Int(w) * Int(h) * 4
|
||||
case .s3tc_bc1, .s3tc_bc2_premul, .s3tc_bc2, .s3tc_bc3_premul, .s3tc_bc3, .rgtc_bc4, .rgtc_bc5_3dc:
|
||||
dxtcSize(w, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DDSPixelFormat: Equatable
|
||||
{
|
||||
let size: UInt32
|
||||
let flags: Flags
|
||||
let fourCC: FourCC
|
||||
let bits: UInt32
|
||||
let rMask: UInt32, gMask: UInt32, bMask: UInt32, aMask: UInt32
|
||||
|
||||
struct Flags: OptionSet
|
||||
{
|
||||
let rawValue: UInt32
|
||||
|
||||
static let alphaPix = Self(rawValue: 0x00000001)
|
||||
static let alpha = Self(rawValue: 0x00000002)
|
||||
static let fourCC = Self(rawValue: 0x00000004)
|
||||
static let rgb = Self(rawValue: 0x00000040)
|
||||
static let yuv = Self(rawValue: 0x00000200)
|
||||
static let indexed1 = Self(rawValue: 0x00000800)
|
||||
static let indexed2 = Self(rawValue: 0x00001000)
|
||||
static let indexed4 = Self(rawValue: 0x00000008)
|
||||
static let indexed8 = Self(rawValue: 0x00000020)
|
||||
static let luminance = Self(rawValue: 0x00020000)
|
||||
static let premult = Self(rawValue: 0x00008000)
|
||||
|
||||
static let nvNormal = Self(rawValue: 0x80000000)
|
||||
static let nvSrgb = Self(rawValue: 0x40000000)
|
||||
|
||||
static let rgba: Self = [ .rgb, .alphaPix ]
|
||||
static let luminanceA: Self = [ .luminance, .alphaPix ]
|
||||
}
|
||||
}
|
||||
|
||||
extension DDSPixelFormat
|
||||
{
|
||||
init(
|
||||
_ flags: Flags,
|
||||
fourCC: FourCC = .init(),
|
||||
bits: UInt32 = 0,
|
||||
r: UInt32 = 0, g: UInt32 = 0, b: UInt32 = 0, a: UInt32 = 0)
|
||||
{
|
||||
self.size = UInt32(MemoryLayout<DDSPixelFormat>.size)
|
||||
assert(self.size == 32, "DDS pixel format size mismatch")
|
||||
self.flags = flags
|
||||
self.fourCC = fourCC
|
||||
self.bits = bits
|
||||
self.rMask = r
|
||||
self.gMask = g
|
||||
self.bMask = b
|
||||
self.aMask = a
|
||||
}
|
||||
}
|
||||
|
||||
struct FourCC: Equatable
|
||||
{
|
||||
let rawValue: UInt32
|
||||
|
||||
init()
|
||||
{
|
||||
self.rawValue = 0
|
||||
}
|
||||
|
||||
init(rawValue: UInt32)
|
||||
{
|
||||
self.rawValue = rawValue.bigEndian
|
||||
}
|
||||
|
||||
init(_ data: Data?)
|
||||
{
|
||||
assert(data != nil)
|
||||
assert(data!.count == 4)
|
||||
self.rawValue = data!.withUnsafeBytes { $0.load(as: UInt32.self).bigEndian }
|
||||
}
|
||||
|
||||
init(_ a: Character, _ b: Character, _ c: Character, _ d: Character)
|
||||
{
|
||||
assert(a.isASCII && b.isASCII && c.isASCII && d.isASCII)
|
||||
var u = UInt32(a.asciiValue!)
|
||||
u |= UInt32(b.asciiValue!) << 8
|
||||
u |= UInt32(c.asciiValue!) << 16
|
||||
u |= UInt32(d.asciiValue!) << 24
|
||||
self.rawValue = u.bigEndian
|
||||
}
|
||||
|
||||
init(_ abcd: String)
|
||||
{
|
||||
assert(abcd.count == 4)
|
||||
self.init(
|
||||
abcd[abcd.index(abcd.startIndex, offsetBy: 0)],
|
||||
abcd[abcd.index(abcd.startIndex, offsetBy: 1)],
|
||||
abcd[abcd.index(abcd.startIndex, offsetBy: 2)],
|
||||
abcd[abcd.index(abcd.startIndex, offsetBy: 3)])
|
||||
}
|
||||
}
|
||||
|
||||
struct DDSHeader
|
||||
{
|
||||
let magic: FourCC
|
||||
let size: UInt32
|
||||
let flags: Flags
|
||||
let height: UInt32, width: UInt32, pitch: UInt32, depth: UInt32
|
||||
let mipNum: UInt32
|
||||
let reserved1: [UInt32]
|
||||
let pixFmt: DDSPixelFormat
|
||||
let caps1: UInt32, caps2: UInt32, caps3: UInt32, caps4: UInt32
|
||||
let reserved2: UInt32
|
||||
|
||||
struct Flags: OptionSet
|
||||
{
|
||||
let rawValue: UInt32
|
||||
|
||||
static let caps = Self(rawValue: 0x000001)
|
||||
static let height = Self(rawValue: 0x000002)
|
||||
static let width = Self(rawValue: 0x000004)
|
||||
static let pitch = Self(rawValue: 0x000008)
|
||||
static let pixFmt = Self(rawValue: 0x001000)
|
||||
static let mipNum = Self(rawValue: 0x020000)
|
||||
static let linSz = Self(rawValue: 0x080000)
|
||||
static let depth = Self(rawValue: 0x800000)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import OrderedCollections
|
||||
import Maths
|
||||
|
||||
|
||||
class G3DbLoader: LoaderProtocol
|
||||
|
@ -43,10 +43,9 @@ struct NSImageLoader: LoaderProtocol
|
||||
context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
|
||||
|
||||
guard let data = context.data else { throw ImageLoaderError.loadFailed("what") }
|
||||
return Image(
|
||||
pixels: Data(bytes: data, count: 4 * image.width * image.height),
|
||||
width: image.width,
|
||||
height: image.height)
|
||||
return Image(Data(bytes: data, count: 4 * image.width * image.height),
|
||||
format: .argb8888,
|
||||
width: image.width, height: image.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,17 @@ extension ObjMaterial
|
||||
func convert(content: UnsafeMutablePointer<ContentManager>? = nil) -> Material
|
||||
{
|
||||
var m = Material()
|
||||
m.id = self.name
|
||||
m.diffuse = self.diffuse.setAlpha(self.alpha)
|
||||
if ![ .colour, .lambert, .shadowOnly ].contains(self.model)
|
||||
{
|
||||
m.specular = self.specular
|
||||
m.specularExp = self.specularExp
|
||||
}
|
||||
else
|
||||
{
|
||||
m.specularExp = self.specularExp
|
||||
}
|
||||
if let content = content
|
||||
{
|
||||
if let albedo = self.diffuseMap
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import Maths
|
||||
|
||||
|
||||
public struct ObjReader
|
||||
@ -115,8 +116,8 @@ fileprivate struct ObjMtlLoader
|
||||
{
|
||||
materials[name] = mat!
|
||||
}
|
||||
mat = .init()
|
||||
name = String(s[0])
|
||||
mat = .init(name: name)
|
||||
}
|
||||
|
||||
file.preHandle { s in if s != "newmtl" && mat == nil { throw ObjLoaderError.unexpectedTag } }
|
||||
|
118
Sources/JolkEngine/Loaders/VMeshLoader.swift
Normal file
118
Sources/JolkEngine/Loaders/VMeshLoader.swift
Normal file
@ -0,0 +1,118 @@
|
||||
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())
|
||||
}
|
Reference in New Issue
Block a user