The Skung Rockification of Ziggy Skungdust and the SIMD's
This commit is contained in:
@ -93,7 +93,9 @@ public class Application
|
||||
|
||||
content.setLoader(extension: "obj", loader: ObjLoader())
|
||||
content.setLoader(extension: "g3db", loader: G3DbLoader())
|
||||
content.setLoader(extensions: ["cache", "mesh"], loader: VMeshLoader())
|
||||
content.setLoader(extensions: ["png", "jpg"], loader: NSImageLoader())
|
||||
content.setLoader(extension: "dds", loader: DDSLoader())
|
||||
try implementation.loadContent(content: &content)
|
||||
}
|
||||
catch
|
||||
|
@ -42,6 +42,11 @@ public extension Colour
|
||||
a: Float((fromRgba32 >> 0) & 0xFF) / 0xFF)
|
||||
}
|
||||
|
||||
init(grey v: Float)
|
||||
{
|
||||
self.init(r: v, g: v, b: v)
|
||||
}
|
||||
|
||||
func mix(with rhs: Colour, _ amount: Float) -> Colour
|
||||
{
|
||||
let x = amount.saturate
|
||||
|
@ -44,16 +44,10 @@ extension ContentManager
|
||||
|
||||
public mutating func create(texture: Image, params: Texture2DParameters = .init()) throws -> Texture2D
|
||||
{
|
||||
try texture.pixels.withUnsafeBytes
|
||||
{
|
||||
raw in
|
||||
let data = raw.baseAddress!
|
||||
let rendTex = try renderer.createTexture(
|
||||
data: data, width: texture.width, height: texture.height,
|
||||
filter: params.magFilter, mipMode: params.mipMode)
|
||||
resources.append(rendTex)
|
||||
return Texture2D(id: rendTex, width: texture.width, height: texture.height)
|
||||
}
|
||||
let rendTex = try renderer.createTexture(image: texture,
|
||||
filter: params.magFilter, mipMode: params.mipMode)
|
||||
resources.append(rendTex)
|
||||
return Texture2D(id: rendTex, width: texture.width, height: texture.height)
|
||||
}
|
||||
|
||||
public func getResource(_ path: String) throws -> URL
|
||||
@ -153,6 +147,23 @@ extension ContentManager
|
||||
let renderResource = try self.create(mesh: mesh)
|
||||
return renderResource as! T
|
||||
}
|
||||
if T.self == RenderMesh<VMeshVertex>.self
|
||||
{
|
||||
guard let mesh = resource as? Mesh<VMeshVertex> else { throw ContentError.loadFailure }
|
||||
let renderResource = try self.create(mesh: mesh)
|
||||
return renderResource as! T
|
||||
}
|
||||
if T.self == Image.self
|
||||
{
|
||||
guard let image = resource as? Image else { throw ContentError.loadFailure }
|
||||
return image as! T
|
||||
}
|
||||
if T.self == Texture2D.self
|
||||
{
|
||||
guard let image = resource as? Image else { throw ContentError.loadFailure }
|
||||
let texture2D = try self.create(texture: image)
|
||||
return texture2D as! T
|
||||
}
|
||||
throw ContentError.loadFailure
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,30 @@ import Foundation
|
||||
|
||||
public struct Image: Resource
|
||||
{
|
||||
let pixels: Data
|
||||
let width: Int
|
||||
let height: Int
|
||||
let data: Data
|
||||
let format: Format
|
||||
let width: Int, height: Int
|
||||
let mipLevels: Int
|
||||
|
||||
public enum Format
|
||||
{
|
||||
case argb8888, abgr8888
|
||||
case rgb888, bgr888
|
||||
//case l8, l16, a8, al88
|
||||
case s3tc_bc1, s3tc_bc2_premul
|
||||
case s3tc_bc2, s3tc_bc3_premul
|
||||
case s3tc_bc3, rgtc_bc4, rgtc_bc5_3dc
|
||||
}
|
||||
}
|
||||
|
||||
extension Image
|
||||
{
|
||||
init(_ data: Data, format: Format, width: Int, height: Int, mipLevels: Int = 0)
|
||||
{
|
||||
self.data = data
|
||||
self.format = format
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.mipLevels = mipLevels
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import OrderedCollections
|
||||
import Maths
|
||||
|
||||
|
||||
public struct Model<T: Vertex>: Resource
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import Maths
|
||||
import OrderedCollections
|
||||
|
||||
|
||||
@ -25,6 +26,8 @@ public struct ObjModel
|
||||
|
||||
public struct ObjMaterial
|
||||
{
|
||||
var name: String
|
||||
|
||||
var model: IlluminationModel = .lambert
|
||||
var ambient: Colour = .black
|
||||
var diffuse: Colour = .white
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import SDL2
|
||||
import Maths
|
||||
|
||||
|
||||
public class GamePad
|
||||
@ -144,8 +145,8 @@ internal extension GamePad.Buttons
|
||||
{
|
||||
case .east: SDL_CONTROLLER_BUTTON_B
|
||||
case .south: SDL_CONTROLLER_BUTTON_A
|
||||
case .west: SDL_CONTROLLER_BUTTON_Y
|
||||
case .north: SDL_CONTROLLER_BUTTON_X
|
||||
case .north: SDL_CONTROLLER_BUTTON_Y
|
||||
case .west: SDL_CONTROLLER_BUTTON_X
|
||||
case .start: SDL_CONTROLLER_BUTTON_START
|
||||
case .select: SDL_CONTROLLER_BUTTON_BACK
|
||||
case .guide: SDL_CONTROLLER_BUTTON_GUIDE
|
||||
@ -166,3 +167,32 @@ internal extension GamePad.Buttons
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public extension Vector2 where Scalar: FloatingPoint
|
||||
{
|
||||
func cardinalDeadzone(min: Scalar, max: Scalar) -> Self
|
||||
{
|
||||
Self(self.x.axisDeadzone(min, max), self.y.axisDeadzone(min, max))
|
||||
}
|
||||
|
||||
func radialDeadzone(min: Scalar, max: Scalar) -> Self
|
||||
{
|
||||
let magnitude = self.len
|
||||
if magnitude == .zero || magnitude < min { return Self.zero }
|
||||
if magnitude > max { return self / magnitude }
|
||||
let rescale = (magnitude - min) / (max - min)
|
||||
return self / magnitude * rescale
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension FloatingPoint
|
||||
{
|
||||
@inline(__always) internal func axisDeadzone(_ min: Self, _ max: Self) -> Self
|
||||
{
|
||||
let xabs = abs(self)
|
||||
return if xabs <= min { 0 }
|
||||
else if xabs >= max { Self(signOf: self, magnitudeOf: 1) }
|
||||
else { Self(signOf: self, magnitudeOf: xabs - min) / (max - min) }
|
||||
}
|
||||
}
|
||||
|
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())
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
import Foundation
|
||||
import simd
|
||||
|
||||
|
||||
public extension FloatingPoint
|
||||
{
|
||||
@inline(__always) var saturate: Self { min(max(self , 0), 1) }
|
||||
|
||||
@inline(__always) static func lerp(_ a: Self, _ b: Self, _ x: Self) -> Self { a * (1 - x) + b * x }
|
||||
|
||||
@inline(__always) static func deg(fromRad: Self) -> Self { fromRad * (180 / Self.pi) }
|
||||
@inline(__always) static func rad(fromDeg: Self) -> Self { fromDeg * (Self.pi / 180) }
|
||||
|
||||
fileprivate func axisDeadzone(_ min: Self, _ max: Self) -> Self
|
||||
{
|
||||
let xabs = abs(self)
|
||||
return if xabs <= min { 0 }
|
||||
else if xabs >= max { Self(signOf: self, magnitudeOf: 1) }
|
||||
else { Self(signOf: self, magnitudeOf: xabs - min) / (max - min) }
|
||||
}
|
||||
}
|
||||
|
||||
public extension SIMD2 where Scalar: FloatingPoint
|
||||
{
|
||||
@inline(__always) var len2: Scalar { x * x + y * y }
|
||||
//@inline(__always) var len2: Scalar { simd_dot(self, self) }
|
||||
@inline(__always) var len: Scalar { len2.squareRoot() }
|
||||
@inline(__always) var normalised: Self { self / len }
|
||||
|
||||
@inline(__always) func dot(_ b: Self) -> Scalar { x * b.x + y * b.y }
|
||||
@inline(__always) func reflect(_ n: Self) -> Self { self - (n * 2 * self.dot(n)) }
|
||||
@inline(__always) func project(_ n: Self) -> Self { n * self.dot(n) }
|
||||
|
||||
@inline(__always) func cross(_ b: Self) -> Scalar { x * b.y - y * b.x }
|
||||
|
||||
@inline(__always) func lerp(_ b: Self, _ x: Scalar) -> Self
|
||||
{
|
||||
let invX = 1 - x
|
||||
return Self(self.x * invX + b.x * x, self.y * invX + b.y * x)
|
||||
}
|
||||
|
||||
@inline(__always) func distance(_ b: Self) -> Scalar { return (b - self).len }
|
||||
|
||||
func cardinalDeadzone(min: Scalar, max: Scalar) -> Self
|
||||
{
|
||||
Self(self.x.axisDeadzone(min, max), self.y.axisDeadzone(min, max))
|
||||
}
|
||||
|
||||
func radialDeadzone(min: Scalar, max: Scalar) -> Self
|
||||
{
|
||||
let magnitude = self.len
|
||||
if magnitude == .zero || magnitude < min { return Self.zero }
|
||||
if magnitude > max { return self / magnitude }
|
||||
let rescale = (magnitude - min) / (max - min)
|
||||
return self / magnitude * rescale
|
||||
}
|
||||
}
|
||||
|
||||
public extension SIMD3 where Scalar: FloatingPoint
|
||||
{
|
||||
@inline(__always) static var X: Self { Self(1, 0, 0) }
|
||||
@inline(__always) static var Y: Self { Self(0, 1, 0) }
|
||||
@inline(__always) static var Z: Self { Self(0, 0, 1) }
|
||||
|
||||
@inline(__always) static var up: Self { Y }
|
||||
@inline(__always) static var down: Self { -Y }
|
||||
@inline(__always) static var left: Self { -X }
|
||||
@inline(__always) static var right: Self { X }
|
||||
@inline(__always) static var forward: Self { Z }
|
||||
@inline(__always) static var back: Self { -Z }
|
||||
|
||||
@inline(__always) var len2: Scalar { x * x + y * y + z * z }
|
||||
@inline(__always) var len: Scalar { len2.squareRoot() }
|
||||
@inline(__always) var normalised: Self { self / len }
|
||||
|
||||
@inline(__always) mutating func normalise() { self /= len }
|
||||
|
||||
@inline(__always) func lerp(_ b: Self, _ x: Scalar) -> Self
|
||||
{
|
||||
let invX = 1 - x
|
||||
return Self(self.x * invX + b.x * x, self.y * invX + b.y * x, self.z * invX + b.z * x)
|
||||
}
|
||||
|
||||
@inline(__always) func dot(_ b: Self) -> Scalar { x * b.x + y * b.y + z * b.z }
|
||||
@inline(__always) func cross(_ b: Self) -> Self { Self(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x) }
|
||||
|
||||
@inline(__always) func project(_ n: Self) -> Self { n * self.dot(n) }
|
||||
}
|
||||
|
||||
public extension SIMD4 where Scalar: FloatingPoint
|
||||
{
|
||||
@inline(__always) static var X: Self { Self(1, 0, 0, 0) }
|
||||
@inline(__always) static var Y: Self { Self(0, 1, 0, 0) }
|
||||
@inline(__always) static var Z: Self { Self(0, 0, 1, 0) }
|
||||
@inline(__always) static var W: Self { Self(0, 0, 0, 1) }
|
||||
}
|
||||
|
||||
public extension simd_float4x4
|
||||
{
|
||||
@inline(__always) static var identity: Self { Self(diagonal: .one) }
|
||||
|
||||
@inline(__always) static func translate(_ v: Vec3f) -> Self
|
||||
{
|
||||
Self(
|
||||
.init( 1, 0, 0, 0),
|
||||
.init( 0, 1, 0 ,0),
|
||||
.init( 0, 0, 1, 0),
|
||||
.init(v.x, v.y, v.z, 1))
|
||||
}
|
||||
|
||||
@inline(__always) static func scale(_ v: Vec3f) -> Self
|
||||
{
|
||||
Self(
|
||||
.init(v.x, 0, 0, 0),
|
||||
.init( 0, v.y, 0, 0),
|
||||
.init( 0, 0, v.z, 0),
|
||||
.init( 0, 0, 0, 1))
|
||||
}
|
||||
|
||||
@inline(__always) static func scale(scalar v: Float) -> Self
|
||||
{
|
||||
Self(
|
||||
.init(v, 0, 0, 0),
|
||||
.init(0, v, 0, 0),
|
||||
.init(0, 0, v, 0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
static func rotate(axis v: Vec3f, angle theta: Float) -> Self
|
||||
{
|
||||
//FIXME: THIS IS FUCKED UP FOR EVERYTHING OTHER THAN X AXIS ROTATION LOL
|
||||
/*
|
||||
let vv = v * v
|
||||
let xy = v.x * v.y, xz = v.x * v.z, yz = v.y * v.z
|
||||
let ts = sin(angle), tc = cos(angle)
|
||||
return Self(
|
||||
.init(
|
||||
vv.x + (tc * (1 - vv.x)),
|
||||
(xz - (tc * xy)) + (ts * v.z),
|
||||
(xz - (tc * xz)) - (ts * v.y), 0),
|
||||
.init(
|
||||
(xy - (tc * xy)) - (ts * v.z),
|
||||
vv.y + (tc * (1 - vv.y)),
|
||||
(xz - (tc * xz)) + (ts * v.x), 0),
|
||||
.init(
|
||||
(xz - (tc * xz)) + (ts * v.y),
|
||||
(xz - (tc * xz)) - (ts * v.y),
|
||||
vv.z + (tc * (1 - vv.z)), 1),
|
||||
.init(0, 0, 0, 1))
|
||||
*/
|
||||
let vxx = v.x * v.x, vxy = v.x * v.y, vxz = v.x * v.z
|
||||
let vyy = v.y * v.y, vyz = v.y * v.z
|
||||
let vzz = v.z * v.z
|
||||
let ts = sin(theta), tc = cos(theta)
|
||||
let ic = 1 - tc
|
||||
return Self(
|
||||
.init(
|
||||
ic * vxx + tc,
|
||||
ic * vxy - v.z * ts,
|
||||
ic * vxz + v.z * ts,
|
||||
0),
|
||||
.init(
|
||||
ic * vxy + v.z * ts,
|
||||
ic * vyy + tc,
|
||||
ic * vyz - v.x * ts,
|
||||
0),
|
||||
.init(
|
||||
ic * vxz - v.y * ts,
|
||||
ic * vyz + v.x * ts,
|
||||
ic * vzz + tc,
|
||||
0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
@inline(__always) static func rotate(yawPitch: Vec2f) -> Self { return rotate(yaw: yawPitch.x, pitch: yawPitch.y) }
|
||||
|
||||
static func rotate(yaw ytheta: Float, pitch xtheta: Float) -> Self
|
||||
{
|
||||
let xc = cos(xtheta), xs = sin(xtheta)
|
||||
let yc = cos(ytheta), ys = sin(ytheta)
|
||||
|
||||
return Self(
|
||||
.init(yc, ys * xs, -ys * xc, 0),
|
||||
.init( 0, xc, xs, 0),
|
||||
.init(ys, yc * -xs, yc * xc, 0),
|
||||
.init( 0, 0, 0, 1))
|
||||
}
|
||||
|
||||
static func rotate(yaw ytheta: Float, pitch xtheta: Float, roll ztheta: Float) -> Self
|
||||
{
|
||||
//FIXME: this doesn't null against control
|
||||
let xc = cos(xtheta), xs = sin(xtheta)
|
||||
let yc = cos(ytheta), ys = sin(ytheta)
|
||||
let zc = cos(ztheta), zs = sin(ztheta)
|
||||
|
||||
let ysxs = ys * xs, ycxs = yc * xs
|
||||
|
||||
let result = Mat4f(
|
||||
.init(yc * zc + ysxs * zs, yc * -zs + ysxs * zc, -ys * xc, 0),
|
||||
.init( xc * zs, xc * zc, xs, 0),
|
||||
.init(ys * zc - ycxs * zs, ys * -zs - ycxs * zc, yc * xc, 0),
|
||||
.init( 0, 0, 0, 1))
|
||||
let shouldBe = .rotate(z: ztheta) * .rotate(x: xtheta) * .rotate(y: ytheta)
|
||||
|
||||
let epsilon: Float = .ulpOfOne
|
||||
if (result != shouldBe)
|
||||
{
|
||||
assert(abs(result[0][0] - shouldBe[0][0]) <= epsilon) // epsilon
|
||||
assert(result[1][0] == shouldBe[1][0])
|
||||
assert(abs(result[2][0] - shouldBe[2][0]) <= epsilon) // epsilon
|
||||
assert(result[3][0] == shouldBe[3][0])
|
||||
assert(abs(result[0][1] - shouldBe[0][1]) <= epsilon) // epsilon
|
||||
assert(result[1][1] == shouldBe[1][1])
|
||||
assert(abs(result[2][1] - shouldBe[2][1]) <= epsilon) // epsilon
|
||||
assert(result[3][1] == shouldBe[3][1])
|
||||
assert(result[0][2] == shouldBe[0][2])
|
||||
assert(result[1][2] == shouldBe[1][2])
|
||||
assert(result[2][2] == shouldBe[2][2])
|
||||
assert(result[3][2] == shouldBe[3][2])
|
||||
assert(result[0][3] == shouldBe[0][3])
|
||||
assert(result[1][3] == shouldBe[1][3])
|
||||
assert(result[2][3] == shouldBe[2][3])
|
||||
assert(result[3][3] == shouldBe[3][3])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@inline(__always) static func rotate(x theta: Float) -> Self
|
||||
{
|
||||
let c = cos(theta), s = sin(theta)
|
||||
return Self(
|
||||
.init(1, 0, 0, 0),
|
||||
.init(0, c, s, 0),
|
||||
.init(0, -s, c, 0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
@inline(__always) static func rotate(y theta: Float) -> Self
|
||||
{
|
||||
let c = cos(theta), s = sin(theta)
|
||||
return Self(
|
||||
.init(c, 0, -s, 0),
|
||||
.init(0, 1, 0, 0),
|
||||
.init(s, 0, c, 0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
@inline(__always) static func rotate(z theta: Float) -> Self
|
||||
{
|
||||
let c = cos(theta), s = sin(theta)
|
||||
return Self(
|
||||
.init(c, -s, 0, 0),
|
||||
.init(s, c, 0, 0),
|
||||
.init(0, 0, 1, 0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
static func perspective(fovY: Float, aspect: Float, zNear: Float, zFar: Float) -> Self
|
||||
{
|
||||
let h = 1 / tanf(fovY * 0.5)
|
||||
let w = h / aspect
|
||||
let invClipRange = 1 / (zFar - zNear)
|
||||
let z = -(zFar + zNear) * invClipRange
|
||||
let z2 = -(2 * zFar * zNear) * invClipRange
|
||||
return simd_matrix(
|
||||
.init(w, 0, 0, 0),
|
||||
.init(0, h, 0, 0),
|
||||
.init(0, 0, z, -1),
|
||||
.init(0, 0, z2, 0))
|
||||
}
|
||||
|
||||
static func lookAt(from: Vec3f = .zero, to: Vec3f, up: Vec3f = .up) -> Self
|
||||
{
|
||||
let forward = (to - from).normalised
|
||||
let bitangent = forward.cross(up).normalised
|
||||
let tangent = bitangent.cross(forward).normalised
|
||||
let normal = -forward
|
||||
return simd_matrix(
|
||||
.init(bitangent.x, tangent.x, normal.x, 0.0),
|
||||
.init(bitangent.y, tangent.y, normal.y, 0.0),
|
||||
.init(bitangent.z, tangent.z, normal.z, 0.0),
|
||||
.init( 0.0, 0.0, 0.0, 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public typealias Vec2f = SIMD2<Float>
|
||||
public typealias Vec2d = SIMD2<Double>
|
||||
public typealias Vec3f = SIMD3<Float>
|
||||
public typealias Vec3d = SIMD3<Double>
|
||||
public typealias Vec4f = SIMD4<Float>
|
||||
public typealias Vec4d = SIMD4<Double>
|
||||
public typealias Mat4f = simd_float4x4
|
@ -1,3 +1,6 @@
|
||||
import Maths
|
||||
|
||||
|
||||
public enum Fog
|
||||
{
|
||||
public enum Mode
|
||||
|
@ -1,19 +1,21 @@
|
||||
public struct Material
|
||||
{
|
||||
public var id: String
|
||||
public var diffuse: Colour, specular: Colour
|
||||
public var diffuse: Colour, specular: Colour, emmision: Colour
|
||||
public var specularExp: Float
|
||||
public var texture: RenderTexture2D
|
||||
|
||||
public init(id: String = "",
|
||||
diffuse: Colour = .white,
|
||||
specular: Colour = .zero,
|
||||
gloss: Float = 0,
|
||||
specular: Colour = .black,
|
||||
emmision: Colour = .black,
|
||||
gloss: Float = 20,
|
||||
texture: RenderTexture2D = .empty)
|
||||
{
|
||||
self.id = id
|
||||
self.diffuse = diffuse
|
||||
self.specular = specular
|
||||
self.emmision = emmision
|
||||
self.specularExp = gloss
|
||||
self.texture = texture
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
//#set OPENGL3
|
||||
|
||||
import Foundation
|
||||
import Maths
|
||||
import SDL2
|
||||
#if OPENGL3
|
||||
import OpenGL
|
||||
#if OPENGL3
|
||||
import OpenGL.GL
|
||||
#else
|
||||
import OpenGL.GL3
|
||||
#endif
|
||||
@ -41,7 +43,7 @@ class OpenGL: Renderer
|
||||
guard SDL_GL_MakeCurrent(sdlWindow, glCtx) == 0
|
||||
else { throw RendererError.sdlError(message: "SDL_GL_MakeCurrent: \(String(cString: SDL_GetError()))") }
|
||||
|
||||
state.enable([.texture2D, .cullFace, .depthTest, .rescaleNormal, .colourMaterial])
|
||||
state.enable([.texture2D, .cullFace, .depthTest, .rescaleNormal/*, .colourMaterial */])
|
||||
if srgb { state.enable(.frameBufferSrgb) }
|
||||
state.cullFace = .back
|
||||
state.clearDepth = 1
|
||||
@ -132,13 +134,12 @@ class OpenGL: Renderer
|
||||
materials: mesh.materials)
|
||||
}
|
||||
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int) throws -> RenderTexture2D
|
||||
func createTexture(image: Image) throws -> RenderTexture2D
|
||||
{
|
||||
try createTexture(data: data, width: width, height: height, filter: .linear, mipMode: .off)
|
||||
try createTexture(image: image, filter: .linear, mipMode: .off)
|
||||
}
|
||||
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int,
|
||||
filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
|
||||
func createTexture(image: Image, filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
|
||||
{
|
||||
let min: Int32 = switch mipMode
|
||||
{
|
||||
@ -146,14 +147,19 @@ class OpenGL: Renderer
|
||||
case .nearest: filter.glMip
|
||||
case .linear: filter.glLinearMip
|
||||
}
|
||||
return RenderTexture2D(try loadTexture(data: data,
|
||||
width: GLsizei(width), height: GLsizei(height),
|
||||
minFilter: min, magFilter: filter.gl))
|
||||
return try image.data.withUnsafeBytes
|
||||
{ raw in
|
||||
RenderTexture2D(try loadTexture(
|
||||
data: raw.baseAddress!, format: image.format,
|
||||
width: GLsizei(image.width), height: GLsizei(image.height),
|
||||
mipLevels: image.mipLevels,
|
||||
minFilter: min, magFilter: filter.gl))
|
||||
}
|
||||
}
|
||||
|
||||
private func loadTexture(
|
||||
data: UnsafeRawPointer,
|
||||
width: GLsizei, height: GLsizei,
|
||||
data: UnsafeRawPointer, format: Image.Format,
|
||||
width: GLsizei, height: GLsizei, mipLevels: Int,
|
||||
minFilter: Int32 = GL_LINEAR, magFilter: Int32 = GL_LINEAR,
|
||||
wrap: Int32 = GL_REPEAT) throws -> GLuint
|
||||
{
|
||||
@ -165,19 +171,49 @@ class OpenGL: Renderer
|
||||
state.texture2DParameter(active: 0, param: .magFilter, int: magFilter)
|
||||
state.texture2DParameter(active: 0, param: .wrapS, int: wrap)
|
||||
state.texture2DParameter(active: 0, param: .wrapT, int: wrap)
|
||||
let format: GLint = srgb ? GL_SRGB8 : GL_RGBA
|
||||
glTexImage2D(GLenum(GL_TEXTURE_2D),
|
||||
0, format, width, height, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), data)
|
||||
|
||||
let upload = getTextureUploader(format: format)
|
||||
var offset = 0
|
||||
for i in 0...mipLevels
|
||||
{
|
||||
let levelWidth = width &>> i, levelHeight = height &>> i
|
||||
let size = format.computeSize(width: UInt32(levelWidth), height: UInt32(levelHeight))
|
||||
upload(GLint(i), levelWidth, levelHeight, data + offset, GLsizei(size))
|
||||
offset += size
|
||||
}
|
||||
|
||||
// Generate mipmaps if needed
|
||||
if [GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST]
|
||||
.contains(minFilter) { glGenerateMipmap(GLenum(GL_TEXTURE_2D)) }
|
||||
.contains(minFilter) && mipLevels == 0 { glGenerateMipmap(GLenum(GL_TEXTURE_2D)) }
|
||||
|
||||
state.bindTexture2D(active: 0, texture: OpenGLState.defaultTexture)
|
||||
|
||||
return texId
|
||||
}
|
||||
|
||||
private typealias UploadFunc = (GLint, GLsizei, GLsizei, UnsafeRawPointer, GLsizei) -> Void
|
||||
private func getTextureUploader(format: Image.Format) -> UploadFunc
|
||||
{
|
||||
let target = GLenum(GL_TEXTURE_2D)
|
||||
let border: GLint = 0
|
||||
return switch format
|
||||
{
|
||||
case .argb8888: { (level, width, height, data, size) in
|
||||
glTexImage2D(target, level, GLint(self.srgb ? GL_SRGB8 : GL_RGBA),
|
||||
width, height, border, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), data)
|
||||
}
|
||||
case .s3tc_bc1: { (level, width, height, data, size) in
|
||||
glCompressedTexImage2D(target, level, GLenum(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT),
|
||||
width, height, border, size, data)
|
||||
}
|
||||
case .rgtc_bc5_3dc: { (level, width, height, data, size) in
|
||||
glCompressedTexImage2D(target, level, GLenum(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT),
|
||||
width, height, border, size, data)
|
||||
}
|
||||
default: fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func deleteMesh<V: Vertex>(_ mesh: RenderMesh<V>)
|
||||
{
|
||||
@ -210,7 +246,11 @@ class OpenGL: Renderer
|
||||
|
||||
func setMaterial(_ mat: Material)
|
||||
{
|
||||
glColor4f(mat.diffuse.r, mat.diffuse.g, mat.diffuse.b, mat.diffuse.a)
|
||||
//glColor4f(mat.diffuse.r, mat.diffuse.g, mat.diffuse.b, mat.diffuse.a)
|
||||
state.materialDiffuse = srgb ? mat.diffuse.linear : mat.diffuse
|
||||
state.materialSpecular = srgb ? mat.specular.linear : mat.specular
|
||||
state.materialEmmision = srgb ? mat.emmision.linear : mat.emmision
|
||||
state.materialShininess = mat.specularExp
|
||||
state.bindTexture2D(active: 0, texture: mat.texture.isValid ? mat.texture.id : 0)
|
||||
}
|
||||
|
||||
@ -226,14 +266,13 @@ class OpenGL: Renderer
|
||||
|
||||
private func bindMesh<V: Vertex>(mesh: RenderMesh<V>)
|
||||
{
|
||||
state.enableClient([ .vertex, .colour, .normal, .textureCoord ])
|
||||
|
||||
state.arrayBuffer = UInt32(mesh.vboHnd)
|
||||
state.elementArrayBuffer = UInt32(mesh.iboHnd)
|
||||
|
||||
let stride = GLsizei(MemoryLayout<V>.stride)
|
||||
if V.self == VertexPositionNormalTexcoord.self
|
||||
{
|
||||
state.enableClient([ .vertex, .normal, .textureCoord ])
|
||||
glVertexPointer(3, GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalTexcoord.position)!))
|
||||
glNormalPointer(GLenum(GL_FLOAT), stride,
|
||||
@ -243,6 +282,7 @@ class OpenGL: Renderer
|
||||
}
|
||||
else if V.self == VertexPositionNormalColourTexcoord.self
|
||||
{
|
||||
state.enableClient([ .vertex, .colour, .normal, .textureCoord ])
|
||||
glVertexPointer(3, GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalColourTexcoord.position)!))
|
||||
glColorPointer(4, GLenum(GL_FLOAT), stride,
|
||||
@ -252,6 +292,16 @@ class OpenGL: Renderer
|
||||
glTexCoordPointer(2, GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalColourTexcoord.texCoord)!))
|
||||
}
|
||||
else if V.self == VMeshVertex.self
|
||||
{
|
||||
state.enableClient([ .vertex, .normal, .textureCoord ])
|
||||
glVertexPointer(3, GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VMeshVertex.position)!))
|
||||
glTexCoordPointer(2, GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VMeshVertex.texCoord)!))
|
||||
glNormalPointer(GLenum(GL_BYTE), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VMeshVertex.normal)!))
|
||||
}
|
||||
}
|
||||
|
||||
private func unbindMesh()
|
||||
@ -259,7 +309,7 @@ class OpenGL: Renderer
|
||||
state.elementArrayBuffer = OpenGLState.defaultBuffer
|
||||
state.arrayBuffer = OpenGLState.defaultBuffer
|
||||
|
||||
state.disableClient([ .vertex, .normal, .textureCoord ])
|
||||
state.disableClient([ .vertex, .colour, .normal, .textureCoord ])
|
||||
}
|
||||
|
||||
private func draw<V: Vertex>(mesh: RenderMesh<V>)
|
||||
@ -302,7 +352,8 @@ class OpenGL: Renderer
|
||||
|
||||
private func loadMatrix(_ matrix: Mat4f)
|
||||
{
|
||||
withUnsafePointer(to: matrix.columns)
|
||||
//withUnsafePointer(to: matrix.columns)
|
||||
withUnsafePointer(to: matrix)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 16)
|
||||
{ (values: UnsafePointer<GLfloat>) in
|
||||
@ -313,7 +364,8 @@ class OpenGL: Renderer
|
||||
|
||||
private func mulMatrix(_ matrix: Mat4f)
|
||||
{
|
||||
withUnsafePointer(to: matrix.columns)
|
||||
//withUnsafePointer(to: matrix.columns)
|
||||
withUnsafePointer(to: matrix)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 16)
|
||||
{ (values: UnsafePointer<GLfloat>) in
|
||||
@ -360,6 +412,7 @@ class OpenGL: Renderer
|
||||
}
|
||||
|
||||
state.lightModelAmbient = env.ambient
|
||||
state.lightModelLocalViewer = true
|
||||
|
||||
let lightCaps: [OpenGLState.Capability] = [
|
||||
.light0, .light1, .light2, .light3,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Maths
|
||||
import OpenGL
|
||||
|
||||
|
||||
@ -308,23 +309,28 @@ struct OpenGLState
|
||||
}
|
||||
}
|
||||
|
||||
enum CullFace { case front, back, frontAndBack }
|
||||
private var _cullFace: CullFace = .back
|
||||
enum Face { case front, back, frontAndBack }
|
||||
private var _cullFace: Face = .back
|
||||
|
||||
var cullFace: CullFace
|
||||
fileprivate func glFace(_ face: Face) -> GLenum
|
||||
{
|
||||
let face = switch face
|
||||
{
|
||||
case .front: GL_FRONT
|
||||
case .back: GL_BACK
|
||||
case .frontAndBack: GL_FRONT_AND_BACK
|
||||
}
|
||||
return GLenum(face)
|
||||
}
|
||||
|
||||
var cullFace: Face
|
||||
{
|
||||
get { _cullFace }
|
||||
set(newMode)
|
||||
{
|
||||
if newMode != _cullFace
|
||||
{
|
||||
let modeEnum = switch newMode
|
||||
{
|
||||
case .front: GL_FRONT
|
||||
case .back: GL_BACK
|
||||
case .frontAndBack: GL_FRONT_AND_BACK
|
||||
}
|
||||
glCullFace(GLenum(modeEnum))
|
||||
glCullFace(glFace(newMode))
|
||||
_cullFace = newMode
|
||||
}
|
||||
}
|
||||
@ -815,4 +821,97 @@ struct OpenGLState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _materialAmbient = Colour(r: 0.2, g: 0.2, b: 0.2)
|
||||
private var _materialDiffuse = Colour(r: 0.8, g: 0.8, b: 0.8)
|
||||
private var _materialSpecular = Colour.black
|
||||
private var _materialEmmision = Colour.black
|
||||
|
||||
fileprivate func setMaterialColour(_ face: Face, _ pname: Int32, _ colour: Colour)
|
||||
{
|
||||
let face = glFace(.frontAndBack)
|
||||
if _capabilities.contains(.colourMaterial)
|
||||
{
|
||||
glColorMaterial(face, GLenum(pname))
|
||||
glColor4f(colour.r, colour.g, colour.b, colour.a)
|
||||
}
|
||||
else
|
||||
{
|
||||
withUnsafePointer(to: colour)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 4)
|
||||
{
|
||||
glMaterialfv(face, GLenum(pname), $0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var materialAmbient: Colour
|
||||
{
|
||||
get { _materialAmbient }
|
||||
set(newColour)
|
||||
{
|
||||
if newColour != _materialAmbient
|
||||
{
|
||||
setMaterialColour(.frontAndBack, GL_AMBIENT, newColour)
|
||||
_materialAmbient = newColour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var materialDiffuse: Colour
|
||||
{
|
||||
get { _materialDiffuse }
|
||||
set(newColour)
|
||||
{
|
||||
if newColour != _materialDiffuse
|
||||
{
|
||||
setMaterialColour(.frontAndBack, GL_DIFFUSE, newColour)
|
||||
_materialDiffuse = newColour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var materialSpecular: Colour
|
||||
{
|
||||
get { _materialSpecular }
|
||||
set(newColour)
|
||||
{
|
||||
if newColour != _materialSpecular
|
||||
{
|
||||
setMaterialColour(.frontAndBack, GL_SPECULAR, newColour)
|
||||
_materialSpecular = newColour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var materialEmmision: Colour
|
||||
{
|
||||
get { _materialEmmision }
|
||||
set(newColour)
|
||||
{
|
||||
if newColour != _materialEmmision
|
||||
{
|
||||
setMaterialColour(.frontAndBack, GL_EMISSION, newColour)
|
||||
_materialEmmision = newColour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _materialShininess: Float = 0
|
||||
|
||||
var materialShininess: Float
|
||||
{
|
||||
get { _materialShininess }
|
||||
set(newParam)
|
||||
{
|
||||
if newParam != _materialShininess
|
||||
{
|
||||
let face = glFace(.frontAndBack)
|
||||
glMaterialf(face, GLenum(GL_SHININESS), newParam)
|
||||
_materialShininess = newParam
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import Maths
|
||||
import OpenGL.GL
|
||||
|
||||
|
||||
@ -16,9 +17,8 @@ public protocol Renderer
|
||||
func setVsync(mode: VSyncMode) throws
|
||||
|
||||
func createMesh<V: Vertex>(mesh: Mesh<V>) throws -> RenderMesh<V>
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int) throws -> RenderTexture2D
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int,
|
||||
filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
|
||||
func createTexture(image: Image) throws -> RenderTexture2D
|
||||
func createTexture(image: Image, filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
|
||||
|
||||
func deleteMesh<V: Vertex>(_ mesh: RenderMesh<V>)
|
||||
func deleteTexture(_ texture: RenderTexture2D)
|
||||
|
@ -4,7 +4,7 @@ import Foundation
|
||||
extension FileHandle
|
||||
{
|
||||
// FixedWidthInteger or BinaryFloatingPoint
|
||||
func read<T: FixedWidthInteger>(as: T.Type) throws -> T
|
||||
func read<T>(as: T.Type) throws -> T
|
||||
{
|
||||
let size = MemoryLayout<T>.size
|
||||
guard let data = try self.read(upToCount: size)
|
||||
|
Reference in New Issue
Block a user