The Skung Rockification of Ziggy Skungdust and the SIMD's

This commit is contained in:
a dinosaur 2024-05-13 01:11:03 +10:00
parent b62cd056b5
commit 6c44476062
40 changed files with 2066 additions and 386 deletions

View File

@ -9,7 +9,8 @@ let package = Package(
],
products: [
.executable(name: "Test", targets: ["Test"]),
.library(name: "JolkEngine", targets: ["JolkEngine"])
.library(name: "JolkEngine", targets: ["JolkEngine"]),
.library(name: "Maths", targets: ["Maths"]),
],
dependencies: [
.package(
@ -17,7 +18,7 @@ let package = Package(
.upToNextMajor(from: "1.4.1")),
.package(
url: "https://github.com/apple/swift-collections.git",
.upToNextMinor(from: "1.1.0"))
.upToNextMinor(from: "1.1.0")),
],
targets: [
.executableTarget(
@ -30,12 +31,19 @@ let package = Package(
dependencies: [
.product(name: "SDL", package: "SwiftSDL2"),
.product(name: "Collections", package: "swift-collections"),
"HSLuv"
"HSLuv",
.target(name: "Maths"),
],
swiftSettings: [
.unsafeFlags(["-Xcc", "-DGL_SILENCE_DEPRECATION"])
]
),
.target(name: "HSLuv"),
.target(
name: "Maths",
swiftSettings: [
.unsafeFlags(["-DUSE_SIMD"])
]
),
]
)

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}

View File

@ -1,4 +1,5 @@
import OrderedCollections
import Maths
public struct Model<T: Vertex>: Resource

View File

@ -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

View File

@ -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) }
}
}

View 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)
}
}

View File

@ -1,5 +1,6 @@
import Foundation
import OrderedCollections
import Maths
class G3DbLoader: LoaderProtocol

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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 } }

View 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())
}

View File

@ -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

View File

@ -1,3 +1,6 @@
import Maths
public enum Fog
{
public enum Mode

View File

@ -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
}

View File

@ -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,

View File

@ -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
}
}
}
}

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,62 @@
import Foundation
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) }
@inline(__always) func smoothStep() -> Self
{
let x = self.saturate
return x * x * (3 - 2 * x)
}
func smoothStep(_ a: Self, _ b: Self) -> Self
{
let x = self.smoothStep()
return b * x + a * (1 - x)
}
@inline(__always) func smootherStep() -> Self
{
let x = self.saturate
return x * x * x * (x * (x * 6 - 15) + 10)
}
func smootherStep(_ a: Self, _ b: Self) -> Self
{
let x = self.smootherStep()
return b * x + a * (1 - x)
}
func sqrInterp(_ a: Self, _ b: Self) -> Self
{
let x = self.saturate, xx = x * x
return a * (1 - xx) + b * xx
}
func invSqrInterp(_ a: Self, _ b: Self) -> Self
{
let x = 1 - self.saturate, xx = x * x
return a * xx + b * (1 - xx)
}
}
extension FloatingPoint where Self == Double
{
@inline(__always) var sine: Self { sin(self) }
@inline(__always) var cosine: Self { cos(self) }
@inline(__always) var tangent: Self { tan(self) }
}
extension FloatingPoint where Self == Float
{
@inline(__always) var sine: Self { sinf(self) }
@inline(__always) var cosine: Self { cosf(self) }
@inline(__always) var tangent: Self { tanf(self) }
}

View File

@ -0,0 +1,99 @@
#if USE_SIMD
import simd
#endif
#if USE_SIMD
public typealias Matrix3x3 = simd_float3x3
public extension Matrix3x3
{
typealias T = Float
}
#else
public struct Matrix3x3<T: FloatingPoint>: Equatable
{
public var m00: T, m01: T, m02: T
public var m10: T, m11: T, m12: T
public var m20: T, m21: T, m22: T
public init()
{
self.m00 = 1; self.m01 = 0; self.m02 = 0
self.m10 = 0; self.m11 = 1; self.m12 = 0
self.m20 = 0; self.m21 = 0; self.m22 = 1
}
public init(
_ a00: T, _ a01: T, _ a02: T,
_ a10: T, _ a11: T, _ a12: T,
_ a20: T, _ a21: T, _ a22: T)
{
self.m00 = a00; self.m01 = a01; self.m02 = a02
self.m10 = a10; self.m11 = a11; self.m12 = a12
self.m20 = a20; self.m21 = a21; self.m22 = a22
}
}
public extension Matrix3x3
{
init(_ row0: Vector3<T>, _ row1: Vector3<T>, _ row2: Vector3<T>)
{
self.m00 = row0.x; self.m01 = row0.y; self.m02 = row0.z
self.m10 = row1.x; self.m11 = row1.y; self.m12 = row1.z
self.m20 = row2.x; self.m21 = row2.y; self.m22 = row2.z
}
@inline(__always) var identity: Self
{
Self(
1, 0, 0,
0, 1, 0,
0, 0, 1)
}
@inline(__always) var transpose: Self
{
Self(
m00, m10, m20,
m01, m11, m21,
m02, m12, m22)
}
static func * (lhs: Self, rhs: Self) -> Self
{
Self(
lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10 + lhs.m02 * rhs.m20,
lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11 + lhs.m02 * rhs.m21,
lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02 * rhs.m22,
lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10 + lhs.m12 * rhs.m20,
lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21,
lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22,
lhs.m20 * rhs.m00 + lhs.m21 * rhs.m10 + lhs.m22 * rhs.m20,
lhs.m20 * rhs.m01 + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21,
lhs.m20 * rhs.m02 + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22)
}
}
#endif
public extension Matrix3x3
{
@inline(__always) static func lookAt(from: Vector3<T> = .zero, to: Vector3<T>, up: Vector3<T> = .up) -> Self
{
let direction = (to - from).normalised
let normal = -direction
let bitangent = up.cross(normal).normalised
let tangent = normal.cross(bitangent).normalised
return Self(
.init(bitangent.x, tangent.x, normal.x),
.init(bitangent.y, tangent.y, normal.y),
.init(bitangent.z, tangent.z, normal.z))
}
}
#if USE_SIMD
public typealias Mat3f = Matrix3x3
#else
public typealias Mat3f = Matrix3x3<Float>
public typealias Mat3d = Matrix3x3<Double>
#endif

View File

@ -0,0 +1,543 @@
#if USE_SIMD
import simd
#endif
#if USE_SIMD
public typealias Matrix4x4 = simd_float4x4
public extension Matrix4x4
{
typealias T = Float
}
#else
public struct Matrix4x4<T: FloatingPoint>: Equatable
{
public typealias Scalar = T
public var m00: T, m01: T, m02: T, m03: T
public var m10: T, m11: T, m12: T, m13: T
public var m20: T, m21: T, m22: T, m23: T
public var m30: T, m31: T, m32: T, m33: T
public init()
{
self.m00 = 1; self.m01 = 0; self.m02 = 0; self.m03 = 0
self.m10 = 0; self.m11 = 1; self.m12 = 0; self.m13 = 0
self.m20 = 0; self.m21 = 0; self.m22 = 1; self.m23 = 0
self.m30 = 0; self.m31 = 0; self.m32 = 0; self.m33 = 1
}
public init(
_ a00: T, _ a01: T, _ a02: T, _ a03: T,
_ a10: T, _ a11: T, _ a12: T, _ a13: T,
_ a20: T, _ a21: T, _ a22: T, _ a23: T,
_ a30: T, _ a31: T, _ a32: T, _ a33: T)
{
self.m00 = a00; self.m01 = a01; self.m02 = a02; self.m03 = a03
self.m10 = a10; self.m11 = a11; self.m12 = a12; self.m13 = a13
self.m20 = a20; self.m21 = a21; self.m22 = a22; self.m23 = a23
self.m30 = a30; self.m31 = a31; self.m32 = a32; self.m33 = a33
}
}
public extension Matrix4x4
{
init(diagonal d: Vector4<T>)
{
self.m00 = d.x; self.m01 = 0; self.m02 = 0; self.m03 = 0
self.m10 = 0; self.m11 = d.y; self.m12 = 0; self.m13 = 0
self.m20 = 0; self.m21 = 0; self.m22 = d.z; self.m23 = 0
self.m30 = 0; self.m31 = 0; self.m32 = 0; self.m33 = d.w
}
init(_ row0: Vector4<T>, _ row1: Vector4<T>, _ row2: Vector4<T>, _ row3: Vector4<T>)
{
self.m00 = row0.x; self.m01 = row0.y; self.m02 = row0.z; self.m03 = row0.w
self.m10 = row1.x; self.m11 = row1.y; self.m12 = row1.z; self.m13 = row1.w
self.m20 = row2.x; self.m21 = row2.y; self.m22 = row2.z; self.m23 = row2.w
self.m30 = row3.x; self.m31 = row3.y; self.m32 = row3.z; self.m33 = row3.w
}
/*
init(_ col0: Vector4<T>, _ col1: Vector4<T>, _ col2: Vector4<T>, _ col3: Vector4<T>)
{
self.m00 = col0.x; self.m01 = col1.x; self.m02 = col2.x; self.m03 = col3.x
self.m10 = col0.y; self.m11 = col1.y; self.m12 = col2.y; self.m13 = col3.y
self.m20 = col0.z; self.m21 = col1.z; self.m22 = col2.z; self.m23 = col3.z
self.m30 = col0.w; self.m31 = col1.w; self.m32 = col2.w; self.m33 = col3.w
}
*/
subscript(index: Int) -> Vector4<T>
{
get
{
switch index
{
case 0: rows.0
case 1: rows.1
case 2: rows.2
case 3: rows.3
default: fatalError()
}
}
}
var rows: (Vector4<T>, Vector4<T>, Vector4<T>, Vector4<T>)
{(
.init(m00, m01, m02, m03),
.init(m10, m11, m12, m13),
.init(m20, m21, m22, m23),
.init(m30, m31, m32, m33))
}
var columns: (Vector4<T>, Vector4<T>, Vector4<T>, Vector4<T>)
{(
.init(m00, m10, m20, m30),
.init(m01, m11, m21, m31),
.init(m02, m12, m22, m32),
.init(m03, m13, m23, m33))
}
var transpose: Self
{
Self(
m00, m10, m20, m30,
m01, m11, m21, m31,
m02, m12, m22, m32,
m03, m13, m23, m33)
}
static func * (lhs: Self, rhs: Self) -> Self
{
let l = rhs, r = lhs
return Self(
l.m00 * r.m00 + l.m01 * r.m10 + l.m02 * r.m20 + l.m03 * r.m30,
l.m00 * r.m01 + l.m01 * r.m11 + l.m02 * r.m21 + l.m03 * r.m31,
l.m00 * r.m02 + l.m01 * r.m12 + l.m02 * r.m22 + l.m03 * r.m32,
l.m00 * r.m03 + l.m01 * r.m13 + l.m02 * r.m23 + l.m03 * r.m33,
l.m10 * r.m00 + l.m11 * r.m10 + l.m12 * r.m20 + l.m13 * r.m30,
l.m10 * r.m01 + l.m11 * r.m11 + l.m12 * r.m21 + l.m13 * r.m31,
l.m10 * r.m02 + l.m11 * r.m12 + l.m12 * r.m22 + l.m13 * r.m32,
l.m10 * r.m03 + l.m11 * r.m13 + l.m12 * r.m23 + l.m13 * r.m33,
l.m20 * r.m00 + l.m21 * r.m10 + l.m22 * r.m20 + l.m23 * r.m30,
l.m20 * r.m01 + l.m21 * r.m11 + l.m22 * r.m21 + l.m23 * r.m31,
l.m20 * r.m02 + l.m21 * r.m12 + l.m22 * r.m22 + l.m23 * r.m32,
l.m20 * r.m03 + l.m21 * r.m13 + l.m22 * r.m23 + l.m23 * r.m33,
l.m30 * r.m00 + l.m31 * r.m10 + l.m32 * r.m20 + l.m33 * r.m30,
l.m30 * r.m01 + l.m31 * r.m11 + l.m32 * r.m21 + l.m33 * r.m31,
l.m30 * r.m02 + l.m31 * r.m12 + l.m32 * r.m22 + l.m33 * r.m32,
l.m30 * r.m03 + l.m31 * r.m13 + l.m32 * r.m23 + l.m33 * r.m33)
}
}
#endif
#if USE_SIMD
public extension Matrix4x4
{
init(quat q: Quaternion<T>)
{
self = .init(simd_quatf(ix: q.x, iy: q.y, iz: q.z, r: q.w)).transpose
/*
let FUCK = simd_matrix4x4(simd_quatf(ix: q.x, iy: q.y, iz: q.z, r: q.w))
self = .init(
FUCK.columns.0.x, FUCK.columns.1.x, FUCK.columns.2.x, FUCK.columns.3.x,
FUCK.columns.0.y, FUCK.columns.1.y, FUCK.columns.2.y, FUCK.columns.3.y,
FUCK.columns.0.z, FUCK.columns.1.z, FUCK.columns.2.z, FUCK.columns.3.z,
FUCK.columns.0.w, FUCK.columns.1.w, FUCK.columns.2.w, FUCK.columns.3.w).transpose
*/
}
}
#else
public extension Matrix4x4 where T == Float
{
init(_ m3: Matrix3x3<T>)
{
self.m00 = m3.m00; self.m01 = m3.m01; self.m02 = m3.m02; self.m03 = 0
self.m10 = m3.m10; self.m11 = m3.m11; self.m12 = m3.m12; self.m13 = 0
self.m20 = m3.m20; self.m21 = m3.m21; self.m22 = m3.m22; self.m23 = 0
self.m30 = 0; self.m31 = 0; self.m32 = 0; self.m33 = 1
}
init(quat q: Quaternion<T>)
{
/*
let qq = Quaternion<T>(q.w * q.w, q.x * q.x, q.y * q.y, q.z * q.z)
return .init(
.init(
qq.w + qq.x - qq.y - qq.z,
2.0 * (q.x * q.y - q.w * q.z),
2.0 * (q.x * q.z + q.w * q.y),
0.0),
.init(
2.0 * (q.x * q.y + q.w * q.z),
qq.w - qq.x + qq.y - qq.z,
2.0 * (q.y * q.z - q.w * q.x),
0.0),
.init(
2.0 * (q.x * q.z - q.w * q.y),
2.0 * (q.y * q.z + q.w * q.x),
qq.w - qq.x - qq.y + qq.z,
0.0),
.init(0.0, 0.0, 0.0, 1.0))
*/
/*
let qww = q.w * q.w
let qxx = q.x * q.x
let qyy = q.y * q.y
let qzz = q.z * q.z
.init(
.init(
qqw + qqx - qqy - qqz,
2.0 * (q.x * q.y - q.w * q.z),
2.0 * (q.x * q.z + q.w * q.y),
0.0),
.init(
2.0 * (q.x * q.y + q.w * q.z),
qqw - qqx + qqy - qqz,
2.0 * (q.y * q.z - q.w * q.x),
0.0),
.init(
2.0 * (q.x * q.z - q.w * q.y),
2.0 * (q.y * q.z + q.w * q.x),
qqw - qqx - qqy + qqz,
0.0),
.init(0.0, 0.0, 0.0, 1.0))
*/
let qww = q.w * q.w, qxx = q.x * q.x, qyy = q.y * q.y, qzz = q.z * q.z
let qxy = q.x * q.y, qxz = q.x * q.z, qyz = q.y * q.z
let qwx = q.w * q.x, qwy = q.w * q.y, qwz = q.w * q.z
self = .init(
.init(qww + qxx - qyy - qzz, 2 * (qxy - qwz), 2 * (qxz + qwy), 0),
.init( 2 * (qxy + qwz), qww - qxx + qyy - qzz, 2 * (qyz - qwx), 0),
.init( 2 * (qxz - qwy), 2 * (qyz + qwx), qww - qxx - qyy + qzz, 0),
.init( 0, 0, 0, 1)).transpose
}
@inline(__always) static var identity: Self { Self(diagonal: .one) }
}
#endif
public extension Matrix4x4
{
// what the fuck lmao
@inline(__always) static func * (lhs: Self, rhs: Vector3<T>) -> Vector3<T>
{
return .init(
lhs.columns.0.x * rhs.x + lhs.columns.0.y * rhs.y + lhs.columns.0.z * rhs.z,
lhs.columns.1.x * rhs.x + lhs.columns.1.y * rhs.y + lhs.columns.1.z * rhs.z,
lhs.columns.2.x * rhs.x + lhs.columns.2.y * rhs.y + lhs.columns.2.z * rhs.z)
}
}
#if USE_SIMD
public extension Matrix4x4
{
@inline(__always) static func translate(_ v: Vector3<T>) -> 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: Vector3<T>) -> 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: T) -> Self
{
Self(
.init(v, 0, 0, 0),
.init(0, v, 0, 0),
.init(0, 0, v, 0),
.init(0, 0, 0, 1))
}
@inline(__always) static func rotate(yawPitch: Vector2<T>) -> Self { return rotate(yaw: yawPitch.x, pitch: yawPitch.y) }
static func rotate(yaw ytheta: T, pitch xtheta: T) -> Self
{
let xc = xtheta.cosine, xs = xtheta.sine
let yc = ytheta.cosine, ys = ytheta.sine
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))
}
@inline(__always) static func rotate(x theta: T) -> Self
{
let c = theta.cosine, s = theta.sine
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: T) -> Self
{
let c = theta.cosine, s = theta.sine
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: T) -> Self
{
let c = theta.cosine, s = theta.sine
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: T, aspect: T, zNear: T, zFar: T) -> Self
{
let h = 1 / (fovY * T(0.5)).tangent
let w = h / aspect
let invClipRange = 1 / (zFar - zNear)
let z = -(zFar + zNear) * invClipRange
let z2 = -(2 * zFar * zNear) * invClipRange
return Self(
.init(w, 0, 0, 0),
.init(0, h, 0, 0),
.init(0, 0, z, -1),
.init(0, 0, z2, 0))
}
static func lookAt(from: Vector3<T> = .zero, to: Vector3<T>, up: Vector3<T> = .up) -> Self
{
let forward = (to - from).normalised
let bitangent = forward.cross(up).normalised
let tangent = bitangent.cross(forward).normalised
let normal = -forward
return Self(
.init(bitangent, 0),
.init( tangent, 0),
.init( normal, 0),
.init(0, 0, 0, 1))
}
}
#else
public extension Matrix4x4 where T == Float
{
@inline(__always) static func translate(_ v: Vector3<T>) -> 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: Vector3<T>) -> 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: T) -> 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: Vector3<T>, angle theta: T) -> 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(theta), tc = cos(theta)
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 = theta.sine, tc = theta.cosine
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: Vector2<T>) -> Self { return rotate(yaw: yawPitch.x, pitch: yawPitch.y) }
static func rotate(yaw ytheta: T, pitch xtheta: T) -> Self
{
let xc = xtheta.cosine, xs = xtheta.sine
let yc = ytheta.cosine, ys = ytheta.sine
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: T, pitch xtheta: T, roll ztheta: T) -> Self
{
//FIXME: this doesn't null against control
let xc = xtheta.cosine, xs = xtheta.sine
let yc = ytheta.cosine, ys = ytheta.sine
let zc = ztheta.cosine, zs = ztheta.sine
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: T) -> Self
{
let c = theta.cosine, s = theta.sine
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: T) -> Self
{
let c = theta.cosine, s = theta.sine
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: T) -> Self
{
let c = theta.cosine, s = theta.sine
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: T, aspect: T, zNear: T, zFar: T) -> Self
{
let h = 1 / (fovY * 0.5).tangent
let w = h / aspect
let invClipRange = 1 / (zFar - zNear)
let z = -(zFar + zNear) * invClipRange
let z2 = -(2 * zFar * zNear) * invClipRange
return Self(
.init(w, 0, 0, 0),
.init(0, h, 0, 0),
.init(0, 0, z, -1),
.init(0, 0, z2, 0))
}
static func lookAt(from: Vector3<T> = .zero, to: Vector3<T>, up: Vector3<T> = .up) -> Self
{
let forward = (to - from).normalised
let bitangent = forward.cross(up).normalised
let tangent = bitangent.cross(forward).normalised
let normal = -forward
return Self(
.init(bitangent, 0),
.init( tangent, 0),
.init( normal, 0),
.init(0, 0, 0, 1))
}
}
#endif
#if USE_SIMD
public typealias Mat4f = Matrix4x4
#else
public typealias Mat4f = Matrix4x4<Float>
public typealias Mat4d = Matrix4x4<Double>
#endif

View File

@ -0,0 +1,333 @@
import Foundation
import simd
public struct Quaternion<T: FloatingPoint>: Equatable
{
public var w: T, x: T, y: T, z: T
public init() { self.x = 0; self.y = 0; self.z = 0; self.w = 1 }
public init(_ w: T, _ x: T, _ y: T, _ z: T) { self.w = w; self.x = x; self.y = y; self.z = z }
public static var identity: Self { .init(1, 0, 0, 0) }
}
public extension Quaternion where T: SIMDScalar
{
init(real w: T, imaginary i: Vector3<T>) { self.w = w; self.x = i.x; self.y = i.y; self.z = i.z }
}
#if USE_SIMD
public extension Quaternion where T == Float
{
init(_ q: simd_quatf)
{
self.init(real: q.real, imaginary: q.imag)
}
static func lookAt(from: Vector3<T> = .zero, to: Vector3<T>, up: Vector3<T> = .up) -> Self
{
let m3 = Matrix3x3.lookAt(from: from, to: to, up: up)
let et_blee_eve_nyu = simd_quatf(m3)
return Self(real: et_blee_eve_nyu.real, imaginary: .init(
et_blee_eve_nyu.imag.x,
et_blee_eve_nyu.imag.y,
et_blee_eve_nyu.imag.z))
}
}
#else
public extension Quaternion where T == Float
{
init(_ m3: Matrix3x3<T>)
{
if m3.m22 < 0
{
if m3.m00 > m3.m11
{
let t = 1 + m3.m00 - m3.m11 - m3.m22
self.x = t.squareRoot() * 0.5
if m3.m21 < m3.m12 { self.x = -self.x }
let ix4 = 1 / (4 * self.x)
self.w = (m3.m21 - m3.m12) * ix4
self.y = (m3.m10 + m3.m01) * ix4
self.z = (m3.m02 + m3.m20) * ix4
if t == 1 && self.w == 0 && self.y == 0 && self.z == 0 { self.x = 1 }
}
else
{
let t = 1 - m3.m00 + m3.m11 - m3.m22
self.y = t.squareRoot() * 0.5
if m3.m02 < m3.m20 { self.y = -self.y }
let iy4 = 1 / (4 * self.y)
self.w = (m3.m02 - m3.m20) * iy4
self.x = (m3.m10 + m3.m01) * iy4
self.z = (m3.m21 + m3.m12) * iy4
if t == 1 && self.w == 0 && self.x == 0 && self.z == 0 { self.y = 1 }
}
}
else
{
if m3.m00 < -m3.m11
{
let t = 1 - m3.m00 - m3.m11 + m3.m22
self.z = t.squareRoot() * 0.5
if m3.m10 < m3.m01 { self.z = -self.z }
let iz4 = 1 / (4 * self.z)
self.w = (m3.m10 - m3.m01) * iz4
self.x = (m3.m02 + m3.m20) * iz4
self.y = (m3.m21 + m3.m12) * iz4
if t == 1 && self.w == 0 && self.x == 0 && self.y == 0 { self.z = 1 }
}
else
{
let t = 1 + m3.m00 + m3.m11 + m3.m22
self.w = t.squareRoot() * 0.5
let iw4 = 1 / (4 * self.w)
self.x = (m3.m21 - m3.m12) * iw4
self.y = (m3.m02 - m3.m20) * iw4
self.z = (m3.m10 - m3.m01) * iw4
if t == 1 && self.x == 0 && self.y == 0 && self.z == 0 { self.w = 1 }
}
}
/*
let m3 = m3FUCK.transpose
if m3.m22 < 0
{
if m3.m00 > m3.m11
{
let t = 1 + m3.m00 - m3.m11 - m3.m22
self.x = t.squareRoot() * 0.5
if m3.m12 < m3.m21 { self.x = -self.x }
let ix4 = 1 / (4 * self.x)
self.w = (m3.m12 - m3.m21) * ix4
self.y = (m3.m01 + m3.m10) * ix4
self.z = (m3.m20 + m3.m02) * ix4
if t == 1 && self.w == 0 && self.y == 0 && self.z == 0 { self.x = 1 }
}
else
{
let t = 1 - m3.m00 + m3.m11 - m3.m22
self.y = t.squareRoot() * 0.5
if m3.m20 < m3.m02 { self.y = -self.y }
let iy4 = 1 / (4 * self.y)
self.w = (m3.m20 - m3.m02) * iy4
self.x = (m3.m01 + m3.m10) * iy4
self.z = (m3.m12 + m3.m21) * iy4
if t == 1 && self.w == 0 && self.x == 0 && self.z == 0 { self.y = 1 }
}
}
else
{
if m3.m00 < -m3.m11
{
let t = 1 - m3.m00 - m3.m11 + m3.m22
self.z = t.squareRoot() * 0.5
if m3.m01 < m3.m10 { self.z = -self.z }
let iz4 = 1 / (4 * self.z)
self.w = (m3.m01 - m3.m10) * iz4
self.x = (m3.m20 + m3.m02) * iz4
self.y = (m3.m12 + m3.m21) * iz4
if t == 1 && self.w == 0 && self.x == 0 && self.y == 0 { self.z = 1 }
}
else
{
let t = 1 + m3.m00 + m3.m11 + m3.m22
self.w = t.squareRoot() * 0.5
let iw4 = 1 / (4 * self.w)
self.x = (m3.m12 - m3.m21) * iw4
self.y = (m3.m20 - m3.m02) * iw4
self.z = (m3.m01 - m3.m10) * iw4
if t == 1 && self.x == 0 && self.y == 0 && self.z == 0 { self.w = 1 }
}
}
*/
/*
self.w = (1 + m3.m00 + m3.m11 + m3.m22).squareRoot() * 0.5
let iw4 = 1 / (4 * self.w)
self.x = (m3.m21 - m3.m12) * iw4
self.y = (m3.m02 - m3.m20) * iw4
self.z = (m3.m10 - m3.m01) * iw4
*/
/*
if m3.m22 < 0
{
if m3.m00 > m3.m11
{
self.w = (1 + m3.m00 - m3.m11 - m3.m22).squareRoot() * 0.5
let iw4 = 1 / (4 * self.w)
//self.x = (m3.m01 + m3.m10) * iw4
//self.y = (m3.m20 + m3.m02) * iw4
//self.z = (m3.m12 - m3.m21) * iw4
self.x = (m3.m21 - m3.m12) * iw4
self.y = (m3.m02 - m3.m20) * iw4
self.z = (m3.m10 - m3.m01) * iw4
}
else
{
self.x = (1 - m3.m00 + m3.m11 - m3.m22).squareRoot() * 0.5
let ix4 = 1 / (4 * self.x)
//self.w = (m3.m01 + m3.m10) * ix4
//self.y = (m3.m12 + m3.m21) * ix4
//self.z = (m3.m20 - m3.m02) * ix4
self.z = (m3.m02 - m3.m20) * ix4
self.y = (m3.m21 - m3.m12) * ix4
self.w = (m3.m10 - m3.m01) * ix4
}
}
else
{
if m3.m00 < -m3.m11
{
self.y = (1 - m3.m00 - m3.m11 + m3.m22).squareRoot() * 0.5
let iy4 = 1 / (4 * self.y)
//self.w = (m3.m20 + m3.m02) * iy4
//self.x = (m3.m12 + m3.m21) * iy4
//self.z = (m3.m01 - m3.m10) * iy4
self.z = (m3.m10 - m3.m01) * iy4
self.x = (m3.m21 - m3.m12) * iy4
self.w = (m3.m02 - m3.m20) * iy4
}
else
{
self.z = (1 + m3.m00 + m3.m11 + m3.m22).squareRoot() * 0.5
let iz4 = 1 / (4 * self.z)
//self.w = (m3.m12 - m3.m21) * iz4
//self.x = (m3.m20 - m3.m02) * iz4
//self.y = (m3.m01 - m3.m10) * iz4
self.y = (m3.m10 - m3.m01) * iz4
self.x = (m3.m02 - m3.m20) * iz4
self.w = (m3.m21 - m3.m12) * iz4
}
}
*/
}
static func lookAt(from: Vector3<T> = .zero, to: Vector3<T>, up: Vector3<T> = .up) -> Self
{
Self(Matrix3x3<T>.lookAt(from: from, to: to, up: up))
}
}
#endif
public extension Quaternion where T == Float
{
init(axis v: Vector3<T>, angle theta: T)
{
let tc = (theta * 0.5).cosine, ts = (theta * 0.5).sine
self.init(real: tc, imaginary: v * ts)
}
@inline(__always) func slerp(_ rhs: Self, _ theta: T) -> Self
{
var b = rhs
var cosHalfTheta = self.dot(b)
if abs(cosHalfTheta) >= 1
{
// if a == b or a == -b then theta == 0
return self
}
else if cosHalfTheta < 0
{
b = -b
cosHalfTheta = -cosHalfTheta
}
let halfTheta = acos(cosHalfTheta)
let sinHalfTheta = (1 - cosHalfTheta * cosHalfTheta).squareRoot()
if abs(sinHalfTheta) <= .ulpOfOne
{
// if theta == 180 degrees then result is not fully defined
// we could rotate around any axis normal to a or b
return (self + b) * 0.5
}
let ratioA = ((1 - theta) * halfTheta).sine / sinHalfTheta
let ratioB = (theta * halfTheta).sine / sinHalfTheta
return self * ratioA + b * ratioB
//let piss = simd_quatf(ix: x, iy: y, iz: z, r: w)
//let shit = simd_quatf(ix: b.x, iy: b.y, iz: b.z, r: b.w)
//let cunt = simd_slerp(piss, shit, x)
//return .init(real: cunt.real, imaginary: cunt.imag)
}
// Because swift type checking brain injury
@inline(__always) static func * (a: Self, b: Self) -> Self
{
.init(
a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w)
}
}
public extension Quaternion where T: FloatingPoint
{
@inline(__always) static prefix func - (this: Self) -> Self
{
.init(-this.w, -this.x, -this.y, -this.z)
}
@inline(__always) static func + (lhs: Self, rhs: Self) -> Self
{
.init(lhs.w + rhs.w, lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z)
}
@inline(__always) static func * (lhs: Self, rhs: T) -> Self
{
.init(lhs.w * rhs, lhs.x * rhs, lhs.y * rhs, lhs.z * rhs)
}
@inline(__always) mutating func normalise()
{
let invMag = 1 / self.dot(self).squareRoot()
self.w *= invMag
self.x *= invMag
self.y *= invMag
self.z *= invMag
}
@inline(__always) var normalised: Self
{
let invMag = 1 / self.dot(self).squareRoot()
return self * invMag
}
@inline(__always) func dot(_ b: Self) -> T { x * b.x + y * b.y + z * b.z + w * b.w }
}
#if USE_SIMD
public extension Vector3 where Scalar == Float
{
@inline(__always) static func * (v: Self, q: Quaternion<Scalar>) -> Self
{
return v * simd_float3x3(simd_quatf(ix: q.x, iy: q.y, iz: q.z, r: q.w))
}
}
#else
public extension Vector3 where T: BinaryFloatingPoint
{
@inline(__always) static func * (v: Self, q: Quaternion<T>) -> Self
{
let qw = q.w, qv = Self(q.x, q.y, q.z)
var out = qv * 2.0 * qv.dot(v)
out += v * (qw * qw - qv.dot(qv))
return out + qv.cross(v) * 2.0 * qw
}
}
#endif
public typealias Quatf = Quaternion<Float>
public typealias Quatd = Quaternion<Double>

View File

@ -0,0 +1,98 @@
import Foundation
#if USE_SIMD
import simd
#endif
#if USE_SIMD
public typealias Vector2 = SIMD2
#else
public struct Vector2<T: FloatingPoint>: Equatable
{
public typealias Scalar = T
public var x, y: T
public init() { self.x = 0; self.y = 0 }
public init(_ x: T, _ y: T) { self.x = x; self.y = y }
}
public extension Vector2
{
// constants
@inline(__always) static var zero: Self { Self(0, 0) }
@inline(__always) static var one: Self { Self(1, 1) }
subscript(index: Int) -> T
{
get
{
switch index
{
case 0: x
case 1: y
default: fatalError()
}
}
}
// relational
@inline(__always) static func == (lhs: Self, rhs: Self) -> Bool { lhs.x == rhs.x && lhs.y == rhs.y }
@inline(__always) static func != (lhs: Self, rhs: Self) -> Bool { rhs.x != rhs.x || lhs.y != rhs.y }
// arithmetic
@inline(__always) static func + (lhs: Self, rhs: Self) -> Self { Self(lhs.x + rhs.x, lhs.y + rhs.y) }
@inline(__always) static func - (lhs: Self, rhs: Self) -> Self { Self(lhs.x - rhs.x, lhs.y - rhs.y) }
@inline(__always) static func * (lhs: Self, rhs: Self) -> Self { Self(lhs.x * rhs.x, lhs.y * rhs.y) }
@inline(__always) static func / (lhs: Self, rhs: Self) -> Self { Self(lhs.x / rhs.x, lhs.y / rhs.y) }
// scalar arithmetic
@inline(__always) static func * (lhs: Self, rhs: T) -> Self { Self(lhs.x * rhs, lhs.y * rhs) }
@inline(__always) static func / (lhs: Self, rhs: T) -> Self { Self(lhs.x / rhs, lhs.y / rhs) }
@inline(__always) static func * (lhs: T, rhs: Self) -> Self { Self(lhs * rhs.x, lhs * rhs.y) }
@inline(__always) static func / (lhs: T, rhs: Self) -> Self { Self(lhs / rhs.x, lhs / rhs.y) }
// compound arithmetic
@inline(__always) static func += (lhs: inout Self, rhs: Self) { lhs.x += rhs.x; lhs.y += rhs.y }
@inline(__always) static func -= (lhs: inout Self, rhs: Self) { lhs.x -= rhs.x; lhs.y -= rhs.y }
@inline(__always) static func *= (lhs: inout Self, rhs: Self) { lhs.x *= rhs.x; lhs.y *= rhs.y }
@inline(__always) static func /= (lhs: inout Self, rhs: Self) { lhs.x /= rhs.x; lhs.y /= rhs.y }
// compound scalar arithmetic
@inline(__always) static func *= (lhs: inout Self, rhs: T) { lhs.x *= rhs; lhs.y *= rhs }
@inline(__always) static func /= (lhs: inout Self, rhs: T) { lhs.x /= rhs; lhs.y /= rhs }
// unary
@inline(__always) static prefix func + (vec: Self) -> Self { vec }
@inline(__always) static prefix func - (vec: Self) -> Self { Self(-vec.x, -vec.y) }
}
#endif
public extension Vector2 where Scalar: FloatingPoint
{
@inline(__always) var len2: Scalar { x * x + y * y }
@inline(__always) var len: Scalar { len2.squareRoot() }
@inline(__always) var normalised: Self { let invLen = 1 / len; return self * invLen }
@inline(__always) mutating func normalise() { let invLen = 1 / len; self *= invLen }
@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 }
}
public typealias Vec2f = Vector2<Float>
public typealias Vec2d = Vector2<Double>

112
Sources/Maths/Vector3.swift Normal file
View File

@ -0,0 +1,112 @@
#if USE_SIMD
import simd
#endif
#if USE_SIMD
public typealias Vector3 = SIMD3
public extension Vector3
{
typealias T = Scalar
}
#else
public struct Vector3<T: FloatingPoint>: Equatable
{
public typealias Scalar = T
public var x, y, z: T
public init() { self.x = 0; self.y = 0; self.z = 0 }
public init(_ x: T, _ y: T, _ z: T) { self.x = x; self.y = y; self.z = z }
}
public extension Vector3
{
// constants
@inline(__always) static var zero: Self { Self(0, 0, 0) }
@inline(__always) static var one: Self { Self(1, 1, 1) }
subscript(index: Int) -> T
{
get
{
switch index
{
case 0: x
case 1: y
case 2: z
default: fatalError()
}
}
}
init(repeating v: T) { self.x = v; self.y = v; self.z = v }
// relational
@inline(__always) static func == (lhs: Self, rhs: Self) -> Bool { lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z }
@inline(__always) static func != (lhs: Self, rhs: Self) -> Bool { lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z }
// arithmetic
@inline(__always) static func + (lhs: Self, rhs: Self) -> Self { Self(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z) }
@inline(__always) static func - (lhs: Self, rhs: Self) -> Self { Self(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z) }
@inline(__always) static func * (lhs: Self, rhs: Self) -> Self { Self(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z) }
@inline(__always) static func / (lhs: Self, rhs: Self) -> Self { Self(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z) }
// scalar arithmetic
@inline(__always) static func * (lhs: Self, rhs: T) -> Self { Self(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs) }
@inline(__always) static func / (lhs: Self, rhs: T) -> Self { Self(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs) }
@inline(__always) static func * (lhs: T, rhs: Self) -> Self { Self(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z) }
@inline(__always) static func / (lhs: T, rhs: Self) -> Self { Self(lhs / rhs.x, lhs / rhs.y, lhs / rhs.z) }
// compound arithmetic
@inline(__always) static func += (lhs: inout Self, rhs: Self) { lhs.x += rhs.x; lhs.y += rhs.y; lhs.z += rhs.z }
@inline(__always) static func -= (lhs: inout Self, rhs: Self) { lhs.x -= rhs.x; lhs.y -= rhs.y; lhs.z -= rhs.z }
@inline(__always) static func *= (lhs: inout Self, rhs: Self) { lhs.x *= rhs.x; lhs.y *= rhs.y; lhs.z *= rhs.z }
@inline(__always) static func /= (lhs: inout Self, rhs: Self) { lhs.x /= rhs.x; lhs.y /= rhs.y; lhs.z /= rhs.z }
// compound scalar arithmetic
@inline(__always) static func *= (lhs: inout Self, rhs: T) { lhs.x *= rhs; lhs.y *= rhs; lhs.z *= rhs }
@inline(__always) static func /= (lhs: inout Self, rhs: T) { lhs.x /= rhs; lhs.y /= rhs; lhs.z /= rhs }
// unary
@inline(__always) static prefix func + (vec: Self) -> Self { vec }
@inline(__always) static prefix func - (vec: Self) -> Self { Self(-vec.x, -vec.y, -vec.z) }
}
#endif
public extension Vector3 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 { let invLen = 1 / len; return self * invLen }
@inline(__always) mutating func normalise() { let invLen = 1 / len; self *= invLen }
@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 typealias Vec3f = Vector3<Float>
public typealias Vec3d = Vector3<Double>

View File

@ -0,0 +1,56 @@
#if USE_SIMD
import simd
#endif
#if USE_SIMD
public typealias Vector4 = SIMD4
#else
public struct Vector4<T: FloatingPoint>: Equatable
{
public typealias Scalar = T
public var x, y, z, w: T
public init() { self.x = 0; self.y = 0; self.z = 0; self.w = 0 }
public init(_ x: T, _ y: T, _ z: T, _ w: T) { self.x = x; self.y = y; self.z = z; self.w = w }
}
public extension Vector4
{
// constants
@inline(__always) static var zero: Self { Self(0, 0, 0, 0) }
@inline(__always) static var one: Self { Self(1, 1, 1, 1) }
subscript(index: Int) -> T
{
get
{
switch index
{
case 0: x
case 1: y
case 2: z
case 3: w
default: fatalError()
}
}
}
init(_ xyz: Vector3<T>, _ w: T) { self.x = xyz.x; self.y = xyz.y; self.z = xyz.z; self.w = w }
}
#endif
public extension Vector4 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 typealias Vec4f = Vector4<Float>
public typealias Vec4d = Vector4<Double>

View File

@ -22,8 +22,8 @@ import JolkEngine
class CavesOfJolk: ApplicationImplementation
{
private lazy var scene = Scene1()
//private lazy var scene = CaveScene()
//private lazy var scene = Scene1()
private lazy var scene = CaveScene()
private var aspect: Float = 0
private var frameCount = 0
private var frameTimer: Float = 1

View File

@ -1,4 +1,5 @@
import Foundation
import Maths
import JolkEngine

View File

@ -1,4 +1,6 @@
import JolkEngine
import Maths
protocol Actor
{

View File

@ -1,4 +1,5 @@
import SDL2
import Maths
import simd
import OpenGL.GL
import JolkEngine
@ -13,7 +14,19 @@ struct Colin: Actor
{
//.rotate(yaw: angle.x, pitch: angle.y, roll: sin(time)) *
//.scale(Vec3f(1.0 + 0.25 * cos(time), 1.0 + 0.25 * sin(time), 1.0)) *
.rotate(yawPitch: angle) * .translate(-position - Vec3f(0, 1, 0))
.rotate(yawPitch: angle)
//.rotate(x: angle.y) * .rotate(y: angle.x)
//.rotate(axis: .left, angle: angle.y) * .rotate(y: angle.x)
//.rotate(axis: .left, angle: angle.y) * .rotate(axis: .up, angle: angle.x)
//Mat4f(quat: Quatf(axis: .X, angle: angle.y) * Quatf(axis: .Y, angle: angle.x))
//simd_matrix4x4(simd_quatf(angle: angle.y, axis: .X) * simd_quatf(angle: angle.x, axis: .Y))
//.lookAt(to: Mat4f.rotate(yawPitch: angle) * -Vec3f.Z * 20.0)
//Mat4f(quat: Quatf.lookAt(to: .rotate(yawPitch: angle).transpose * -Vec3f.Z)).transpose
//Mat4f(Mat3f.lookAt(to: .rotate(yawPitch: angle) * -Vec3f.Z))
//.lookAt(from: _pos, to: .zero)
* .translate(-position - Vec3f(0, 1, 0))
}
var angle: Vec2f { return Vec2f(ofsAngle.x + _angle, ofsAngle.y) }
@ -45,13 +58,13 @@ struct Colin: Actor
for edge in edges
{
let diff = _pos2D - lastPos
if simd_dot(edge.n, diff) > 0 && simd_dot(edge.n, velocity) > 0 { continue }
if edge.n.dot(diff) > 0 && edge.n.dot(velocity) > 0 { continue }
let deltaPos = _pos2D - edge.p
let something = deltaPos.cross(edge.n)
if abs(something) * 2.0 < edge.w
{
let dot = simd_dot(edge.n, deltaPos)
let dot = edge.n.dot(deltaPos)
if dot > 0 && dot < colinWidth
{
lastPos = _pos2D
@ -246,5 +259,10 @@ struct Colin: Actor
// jumpVel = max(jumpVel, 0.0)
// _pos.y = 0.0
//}
//let piss = Quatf(axis: .X, angle: angle.y) * Quatf(axis: .Y, angle: angle.x)
//let shit = simd_quatf(angle: angle.y, axis: .X) * simd_quatf(angle: angle.x, axis: .Y)
//print(piss.w - shit.real, piss.x - shit.imag.x, piss.y - shit.imag.y, piss.z - shit.imag.z)
}
}

View File

@ -1,4 +1,5 @@
import Foundation
import Maths
import simd
import JolkEngine

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,5 @@
import Foundation
import Maths
import simd
import JolkEngine
@ -8,12 +9,18 @@ struct CaveScene: Scene
private var colin = Colin()
private var world = Collision()
private var worldModel = RenderMesh<VertexPositionNormalColourTexcoord>.empty
private var rockModel = RenderMesh<VMeshVertex>.empty, axisModel = RenderMesh<VMeshVertex>.empty
private var rockAlbedo = Texture2D.empty, rockNormal = Texture2D.empty, axisAlbedo = Texture2D.empty
private var skung = Quatf.init(), skungFrom = Quatf.init(), skungTo = Quatf.init()
private var skungx: Float = 0.0
private var axisPos = Vec3f(2.0, 0.5, 0.0)
private var drawEdges = false
mutating func setup(render: inout any JolkEngine.Renderer)
{
colin.setPosition(Vec3f(3.55475903, 0.0667395443, 0.221960306))
colin.setAngle(Vec2f(-1.47447872, 0.0))
skungTo = .lookAt(from: axisPos, to: colin.position + .up)
}
mutating func loadContent(content: inout JolkEngine.ContentManager) throws
@ -21,6 +28,11 @@ struct CaveScene: Scene
let obj = try ObjReader.read(url: try content.getResource("CaveScene.obj"))
let mesh: Mesh = try ObjLoader.read(model: obj, content: &content)
worldModel = try content.create(mesh: mesh)
rockModel = try content.load("rock.mesh")
rockAlbedo = try content.load("rock.dds")
rockNormal = try content.load("rocknorm.dds")
axisModel = try content.load("Axis.obj.cache")
axisAlbedo = try content.load("AxisTex.png")
if let collision = obj.objects["Collision3D"]
{
@ -31,6 +43,23 @@ struct CaveScene: Scene
mutating func update(deltaTime: Float)
{
colin.update(deltaTime: deltaTime, world: world)
skungx = skungx + deltaTime
if skungx >= 1
{
skungx = fmod(skungx, 1.0)
skungFrom = skungTo
skungTo = .lookAt(from: axisPos, to: colin.position + .up)
}
skung = skungFrom.slerp(skungTo, skungx.smoothStep())
if let pad = GamePad.current?.state
{
if pad.down(.north)
{
axisPos += .forward * skung * deltaTime
}
}
if Input.instance.keyboard.keyPressed(.c) { drawEdges = !drawEdges }
}
@ -41,7 +70,7 @@ struct CaveScene: Scene
aspect: aspect, zNear: 0.1, zFar: 4000.0))
render.setView(matrix: colin.transform)
let env = Environment()
var env = Environment()
let drawRanges =
{ [render](range: any Sequence<Int>) in
for i in range
@ -53,6 +82,25 @@ struct CaveScene: Scene
}
if colin.position.x < 14 { drawRanges([ 0, 1, 2, 6 ]) }
else { drawRanges(3...5) }
//print(Mat4f.rotate(yawPitch: colin.angle) * -Vec3f.Z)
env.addDirectionalLight(direction: -.init(-0.6199881, 0.7049423, 0.34448668), colour: .white)
render.setMaterial(.init(
//specular: .init(grey: 0.5),
specular: .white,
gloss: 1,
texture: axisAlbedo.id))
render.draw(mesh: axisModel, model: .translate(axisPos) * Mat4f(quat: skung), environment: env)
render.setMaterial(.init(
diffuse: XnaColour.DarkGray.lighten(by: -0.1),
//specular: .init(grey: 0.9),
specular: .white,
emmision: XnaColour.Green.mix(with: .black, 0.876),
gloss: 20,
texture: rockAlbedo.id))
render.draw(mesh: rockModel, model: .translate(.init(27.622196, 0.8187093, -19.366903)), environment: env)
if drawEdges { world.draw(render, position: colin.position) }
}
}

View File

@ -1,5 +1,6 @@
import JolkEngine
import Foundation
import Maths
import simd
@ -57,18 +58,18 @@ struct Scene1: Scene
try loadWorld(&content)
cube = try content.create(mesh: .init(
vertices: [
.init(position: Vec3f(-1, -1, 1), normal: .forward, texCoord: Vec2f(0, 0)),
.init(position: Vec3f( 1, -1, 1), normal: .forward, texCoord: Vec2f(1, 0)),
.init(position: Vec3f(-1, 1, 1), normal: .forward, texCoord: Vec2f(0, 1)),
.init(position: Vec3f( 1, 1, 1), normal: .forward, texCoord: Vec2f(1, 1)),
.init(position: Vec3f(-1, -1, 1), normal: .back, texCoord: Vec2f(0, 0)),
.init(position: Vec3f( 1, -1, 1), normal: .back, texCoord: Vec2f(1, 0)),
.init(position: Vec3f(-1, 1, 1), normal: .back, texCoord: Vec2f(0, 1)),
.init(position: Vec3f( 1, 1, 1), normal: .back, texCoord: Vec2f(1, 1)),
.init(position: Vec3f( 1, -1, 1), normal: .right, texCoord: Vec2f(0, 0)),
.init(position: Vec3f( 1, -1, -1), normal: .right, texCoord: Vec2f(1, 0)),
.init(position: Vec3f( 1, 1, 1), normal: .right, texCoord: Vec2f(0, 1)),
.init(position: Vec3f( 1, 1, -1), normal: .right, texCoord: Vec2f(1, 1)),
.init(position: Vec3f( 1, -1, -1), normal: .back, texCoord: Vec2f(0, 0)),
.init(position: Vec3f(-1, -1, -1), normal: .back, texCoord: Vec2f(1, 0)),
.init(position: Vec3f( 1, 1, -1), normal: .back, texCoord: Vec2f(0, 1)),
.init(position: Vec3f(-1, 1, -1), normal: .back, texCoord: Vec2f(1, 1)),
.init(position: Vec3f( 1, -1, -1), normal: .forward, texCoord: Vec2f(0, 0)),
.init(position: Vec3f(-1, -1, -1), normal: .forward, texCoord: Vec2f(1, 0)),
.init(position: Vec3f( 1, 1, -1), normal: .forward, texCoord: Vec2f(0, 1)),
.init(position: Vec3f(-1, 1, -1), normal: .forward, texCoord: Vec2f(1, 1)),
.init(position: Vec3f(-1, -1, -1), normal: .left, texCoord: Vec2f(0, 0)),
.init(position: Vec3f(-1, -1, 1), normal: .left, texCoord: Vec2f(1, 0)),
.init(position: Vec3f(-1, 1, -1), normal: .left, texCoord: Vec2f(0, 1)),
@ -158,37 +159,33 @@ struct Scene1: Scene
// Draw world
for s in worldMesh.subMeshes
{
//render.setMaterial(Material(
// specular: XnaColour.BlanchedAlmond.mix(with: .black, 0.12),
// texture: texture.id))
if s.material != -1
{
render.setMaterial(worldMesh.materials[s.material])
}
else
{
render.setMaterial(.init())
}
let material = s.material != -1
? worldMesh.materials[s.material]
: .init(specular: XnaColour.BlanchedAlmond.mix(with: .black, 0.12))
render.setMaterial(material)
render.draw(mesh: worldMesh, subMesh: s, environment: env)
}
// Draw jolked up shit
render.setMaterial(Material(
specular: XnaColour.Gray,
gloss: 20.0,
gloss: 28.0,
texture: jolkTex.id))
render.draw(mesh: cube, model: jolkCube.transform, environment: env)
render.setMaterial(Material(texture: suzanneDiffuse.id))
render.draw(mesh: suzanne, model: .translate(.up + Vec3f(3.0, 0.0, -3.5) * 2.5), environment: env)
render.setMaterial(Material(
specular: .init(grey: 0.5),
gloss: 15.0,
texture: suzanneDiffuse.id))
let suzannePos = .up + Vec3f(3.0, 0.0, -3.5) * 2.5
render.draw(mesh: suzanne, model:
.translate(suzannePos) *
.lookAt(from: suzannePos, to: colin.position + .up) * .rotate(y: .pi), environment: env)
render.draw(mesh: toybox,
model: .translate(Vec3f(6.0, 0.667, -3.5) * Vec3f(2.5, 1, 2.5))
* .rotate(y: lightTheta * 0.5) * .scale(scalar: 0.25), environment: env)
if drawEdges
{
world.draw(render, position: colin.position)
}
if drawEdges { world.draw(render, position: colin.position) }
}
}