The Skung Cave commit

This commit is contained in:
2024-05-09 20:52:01 +10:00
parent 446c444728
commit 9461fe08cf
35 changed files with 11988 additions and 9097 deletions

View File

@ -23,23 +23,21 @@ public struct ApplicationConfiguration
let vSync: VSyncMode let vSync: VSyncMode
let windowWidth: Int32 let windowWidth: Int32
let windowHeight: Int32 let windowHeight: Int32
let windowTitle: String
let bundle: Bundle let bundle: Bundle
public init(resizable: Bool, vSync: VSyncMode, windowWidth: Int32, windowHeight: Int32, bundle: Bundle) public init(resizable: Bool, vSync: VSyncMode, windowWidth: Int32, windowHeight: Int32, title: String, bundle: Bundle)
{ {
self.resizable = resizable self.resizable = resizable
self.vSync = vSync self.vSync = vSync
self.windowWidth = windowWidth self.windowWidth = windowWidth
self.windowHeight = windowHeight self.windowHeight = windowHeight
self.windowTitle = title
self.bundle = bundle self.bundle = bundle
} }
} }
//FIXME: Wrap these
public var sdlPad: OpaquePointer? = nil
var joyId: Int32 = -1
public class Application public class Application
{ {
private let implementation: ApplicationImplementation private let implementation: ApplicationImplementation
@ -52,14 +50,6 @@ public class Application
typealias ResizeEvent = (width: Int32, height: Int32) typealias ResizeEvent = (width: Int32, height: Int32)
private var resize: ResizeEvent? = nil private var resize: ResizeEvent? = nil
func openController(index: Int32) -> (pad: OpaquePointer, joy: Int32)?
{
guard let sdlPad = SDL_GameControllerOpen(index) else { return nil }
let joyId = SDL_JoystickGetDeviceInstanceID(index)
print("Using gamepad #\(joyId), \"\(String(cString: SDL_GameControllerName(sdlPad)))\"")
return (pad: sdlPad, joy: joyId)
}
public init(application: ApplicationImplementation, config: ApplicationConfiguration) public init(application: ApplicationImplementation, config: ApplicationConfiguration)
{ {
self.implementation = application self.implementation = application
@ -77,7 +67,7 @@ public class Application
let winWidth: Int32 = configuration.windowWidth, winHeight: Int32 = configuration.windowHeight let winWidth: Int32 = configuration.windowWidth, winHeight: Int32 = configuration.windowHeight
var winFlags = SDL_WINDOW_RESIZABLE.rawValue | SDL_WINDOW_ALLOW_HIGHDPI.rawValue var winFlags = SDL_WINDOW_RESIZABLE.rawValue | SDL_WINDOW_ALLOW_HIGHDPI.rawValue
if render is OpenGL { winFlags |= SDL_WINDOW_OPENGL.rawValue } if render is OpenGL { winFlags |= SDL_WINDOW_OPENGL.rawValue }
window = SDL_CreateWindow("Something", winPos, winPos, winWidth, winHeight, winFlags) window = SDL_CreateWindow(configuration.windowTitle, winPos, winPos, winWidth, winHeight, winFlags)
guard window != nil else { fatalError("SDL_CreateWindow: \(String(cString: SDL_GetError()))") } guard window != nil else { fatalError("SDL_CreateWindow: \(String(cString: SDL_GetError()))") }
do do
@ -91,15 +81,10 @@ public class Application
} }
catch { fatalError("piss") } catch { fatalError("piss") }
for i in 0...SDL_NumJoysticks() for idx in 0...SDL_NumJoysticks()
{ {
if SDL_IsGameController(i) != SDL_TRUE { continue } if SDL_IsGameController(idx) != SDL_TRUE { continue }
if let open = openController(index: i) Input.instance.padConnectedEvent(index: idx)
{
sdlPad = open.pad
joyId = open.joy
break
}
} }
do do
@ -131,13 +116,6 @@ public class Application
switch SDL_EventType(event.type) switch SDL_EventType(event.type)
{ {
case SDL_QUIT: break mainLoop case SDL_QUIT: break mainLoop
case SDL_KEYDOWN:
if event.key.repeat == 0
{
Input.instance.pressEvent(scan: event.key.keysym.scancode)
}
case SDL_KEYUP:
Input.instance.releaseEvent(scan: event.key.keysym.scancode)
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
switch SDL_WindowEventID(UInt32(event.window.event)) switch SDL_WindowEventID(UInt32(event.window.event))
{ {
@ -145,28 +123,27 @@ public class Application
resize = displaySize resize = displaySize
default: break default: break
} }
case SDL_KEYDOWN:
Input.instance.pressEvent(scan: event.key.keysym.scancode, repeat: event.key.repeat != 0)
case SDL_KEYUP:
Input.instance.releaseEvent(scan: event.key.keysym.scancode)
case SDL_CONTROLLERDEVICEADDED: case SDL_CONTROLLERDEVICEADDED:
if sdlPad == nil && SDL_IsGameController(event.cdevice.which) == SDL_TRUE if SDL_IsGameController(event.cdevice.which) != SDL_TRUE { break }
{ Input.instance.padConnectedEvent(index: event.cdevice.which)
if let open = openController(index: event.cdevice.which)
{
sdlPad = open.pad
joyId = open.joy
}
}
case SDL_CONTROLLERDEVICEREMOVED: case SDL_CONTROLLERDEVICEREMOVED:
if event.cdevice.which == joyId Input.instance.padRemovedEvent(index: event.cdevice.which)
{ case SDL_CONTROLLERBUTTONDOWN:
SDL_GameControllerClose(sdlPad) Input.instance.padButtonPressEvent(id: event.cbutton.which, btn: event.cbutton.button)
sdlPad = nil case SDL_CONTROLLERBUTTONUP:
joyId = -1 Input.instance.padButtonReleaseEvent(id: event.cbutton.which, btn: event.cbutton.button)
} case SDL_CONTROLLERAXISMOTION:
Input.instance.padAxisEvent(id: event.caxis.which, axis: event.caxis.axis, value: event.caxis.value)
default: break default: break
} }
} }
let time = SDL_GetPerformanceCounter(); let time = SDL_GetPerformanceCounter();
let deltaTime = Float(Double(time - prevTime) * timeMul) let deltaTime = Float(Double(time &- prevTime) * timeMul)
prevTime = time prevTime = time
implementation.update(deltaTime: min(deltaTime, 1.0 / 15.0)) implementation.update(deltaTime: min(deltaTime, 1.0 / 15.0))

View File

@ -35,7 +35,7 @@ extension ContentManager
loaders[ext] = loader loaders[ext] = loader
} }
public mutating func create(mesh: Mesh) throws -> RenderMesh public mutating func create<V: Vertex>(mesh: Mesh<V>) throws -> RenderMesh<V>
{ {
let rendMesh = try renderer.createMesh(mesh: mesh) let rendMesh = try renderer.createMesh(mesh: mesh)
resources.append(rendMesh) resources.append(rendMesh)
@ -72,13 +72,18 @@ extension ContentManager
{ {
let resourceUrl = try getResource(path) let resourceUrl = try getResource(path)
let loader = loaders[resourceUrl.pathExtension] let loader = loaders[resourceUrl.pathExtension.lowercased()]
guard let resource = loader?.load(url: resourceUrl) guard let resource = loader?.load(url: resourceUrl)
else { throw ContentError.loadFailure } else { throw ContentError.loadFailure }
if T.self == Mesh.self if T.self == Mesh<VertexPositionNormalTexcoord>.self
{ {
guard let mesh = resource as? Mesh else { throw ContentError.loadFailure } guard let mesh = resource as? Mesh<VertexPositionNormalTexcoord> else { throw ContentError.loadFailure }
return mesh as! T
}
if T.self == Mesh<VertexPositionNormalColourTexcoord>.self
{
guard let mesh = resource as? Mesh<VertexPositionNormalTexcoord> else { throw ContentError.loadFailure }
return mesh as! T return mesh as! T
} }
if T.self == Image.self if T.self == Image.self
@ -100,13 +105,18 @@ extension ContentManager
{ {
let resourceUrl = try getResource(path) let resourceUrl = try getResource(path)
let loader = loaders[resourceUrl.pathExtension] let loader = loaders[resourceUrl.pathExtension.lowercased()]
guard let resource = loader?.load(url: resourceUrl) guard let resource = loader?.load(url: resourceUrl)
else { throw ContentError.loadFailure } else { throw ContentError.loadFailure }
if T.self == Mesh.self if T.self == Mesh<VertexPositionNormalTexcoord>.self
{ {
guard let mesh = resource as? Mesh else { throw ContentError.loadFailure } guard let mesh = resource as? Mesh<VertexPositionNormalTexcoord> else { throw ContentError.loadFailure }
return mesh as! T
}
if T.self == Mesh<VertexPositionNormalColourTexcoord>.self
{
guard let mesh = resource as? Mesh<VertexPositionNormalTexcoord> else { throw ContentError.loadFailure }
return mesh as! T return mesh as! T
} }
if T.self == Image.self if T.self == Image.self
@ -131,9 +141,15 @@ extension ContentManager
guard let resource = loader?.load(url: resourceUrl) guard let resource = loader?.load(url: resourceUrl)
else { throw ContentError.loadFailure } else { throw ContentError.loadFailure }
if T.self == RenderMesh.self if T.self == RenderMesh<VertexPositionNormalTexcoord>.self
{ {
guard let mesh = resource as? Mesh else { throw ContentError.loadFailure } guard let mesh = resource as? Mesh<VertexPositionNormalTexcoord> else { throw ContentError.loadFailure }
let renderResource = try self.create(mesh: mesh)
return renderResource as! T
}
if T.self == RenderMesh<VertexPositionNormalColourTexcoord>.self
{
guard let mesh = resource as? Mesh<VertexPositionNormalColourTexcoord> else { throw ContentError.loadFailure }
let renderResource = try self.create(mesh: mesh) let renderResource = try self.create(mesh: mesh)
return renderResource as! T return renderResource as! T
} }
@ -144,9 +160,13 @@ extension ContentManager
{ {
for resource in resources.reversed() for resource in resources.reversed()
{ {
if resource is RenderMesh if resource is RenderMesh<VertexPositionNormalTexcoord>
{ {
renderer.deleteMesh(resource as! RenderMesh) renderer.deleteMesh(resource as! RenderMesh<VertexPositionNormalTexcoord>)
}
else if resource is RenderMesh<VertexPositionNormalColourTexcoord>
{
renderer.deleteMesh(resource as! RenderMesh<VertexPositionNormalColourTexcoord>)
} }
else if resource is RenderTexture2D else if resource is RenderTexture2D
{ {

View File

@ -6,4 +6,5 @@ public protocol LoaderProtocol
associatedtype T: Resource associatedtype T: Resource
func load(url: URL) -> T? func load(url: URL) -> T?
func load(url: URL, content: inout ContentManager) -> T?
} }

View File

@ -1,54 +1,86 @@
import OrderedCollections import OrderedCollections
public struct Model: Resource public struct Model<T: Vertex>: Resource
{ {
public let meshes: [Mesh] public let meshes: [Mesh<T>]
} }
public struct Mesh: Resource public struct Mesh<T: Vertex>: Resource
{ {
public typealias Index = UInt16 public typealias Index = UInt16
public struct Vertex: Equatable
{
public let position: Vec3f
public let normal: Vec3f
public let texCoord: Vec2f
public init(position: Vec3f, normal: Vec3f, texCoord: Vec2f)
{
self.position = position
self.normal = normal
self.texCoord = texCoord
}
}
public struct SubMesh public struct SubMesh
{ {
public let start, length: Int public let start, length: Int
public let material: Int //hack
public init(start: Int, length: Int) public init(start: Int, length: Int, material: Int = -1)
{ {
self.start = start self.start = start
self.length = length self.length = length
self.material = material
} }
} }
public let vertices: [Vertex] public let vertices: [T]
public let indices: [Index] public let indices: [Index]
public let subMeshes: OrderedDictionary<String, SubMesh> public let subMeshes: OrderedDictionary<String, SubMesh>
public let materials: [Material] // hack
} }
public extension Mesh public extension Mesh
{ {
static let empty = Self(vertices: .init(), indices: .init(), subMeshes: .init()) static var empty: Self { Self(vertices: .init(), indices: .init(), subMeshes: .init(), materials: .init()) }
init(vertices: [Vertex], indices: [Index]) init(vertices: [T], indices: [Index])
{ {
self.init( self.init(
vertices: vertices, vertices: vertices,
indices: indices, indices: indices,
subMeshes: .init()) subMeshes: .init(),
materials: .init())
}
init(vertices: [T], indices: [Index], subMeshes: OrderedDictionary<String, SubMesh>)
{
self.init(
vertices: vertices,
indices: indices,
subMeshes: subMeshes,
materials: .init())
}
}
public protocol Vertex: Equatable {}
public struct VertexPositionNormalTexcoord: Vertex
{
public let position: Vec3f
public let normal: Vec3f
public let texCoord: Vec2f
public init(position: Vec3f, normal: Vec3f, texCoord: Vec2f)
{
self.position = position
self.normal = normal
self.texCoord = texCoord
}
}
public struct VertexPositionNormalColourTexcoord: Vertex
{
public let position: Vec3f
public let normal: Vec3f
public let colour: Colour
public let texCoord: Vec2f
public init(position: Vec3f, colour: Colour, normal: Vec3f, texCoord: Vec2f)
{
self.position = position
self.colour = colour
self.normal = normal
self.texCoord = texCoord
} }
} }

View File

@ -1,15 +1,18 @@
import Foundation import Foundation
import OrderedCollections
public struct ObjModel public struct ObjModel
{ {
public var positions = [Vec3f]() public var positions = [Vec3f]()
public var colours = [Vec3f]()
public var normals = [Vec3f]() public var normals = [Vec3f]()
public var texCoords = [Vec2f]() public var texCoords = [Vec2f]()
public var objects = Dictionary<String, Object>() public var objects = OrderedDictionary<String, Object>()
public var materials = Dictionary<String, ObjMaterial>()
public struct Object { public var faces = [Face]() }
public struct Object { public var meshes = [Mesh]() }
public struct Mesh { public var material = "", faces = [Face]() }
public struct Index { public let p: Int, n: Int, t: Int } public struct Index { public let p: Int, n: Int, t: Int }
public enum Face public enum Face
{ {

View File

@ -0,0 +1,157 @@
import Foundation
import SDL2
public class GamePad
{
public enum Axes
{
case leftStickX, leftStickY
case rightStickX, rightStickY
case leftTrigger, rightTrigger
}
public enum Buttons
{
case east, south, north, west
case select, start, guide, mic, touchPad
case leftStick, rightStick
case leftBumper, rightBumper
case dpadLeft, dpadRight, dpadUp, dpadDown
case paddle1, paddle2, paddle3, paddle4
}
public struct State
{
public func down(_ btn: Buttons) -> Bool { btnState(btn) & GamePad._DOWN == GamePad._DOWN }
public func pressed(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._PRESS }
public func released(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._RELEASE }
public func axis(_ axis: Axes) -> Float
{
let axisRescale = 1.0 / Float(Int16.max)
return Float(rawAxis(axis)) * axisRescale
}
@inline(__always) func rawAxis(_ axis: Axes) -> Int16
{
_axes[Int(axis.sdlEnum.rawValue)]
}
@inline(__always) private func btnState(_ btn: Buttons) -> UInt8
{
_btns[Int(btn.sdlEnum.rawValue)]
}
private let _btns: [UInt8], _axes: [Int16]
internal init(btns: [UInt8], axes: [Int16])
{
self._btns = btns
self._axes = axes
}
}
public var state: State
{
.init(btns: _btns, axes: _axes)
}
@inline(__always) public static var current: GamePad?
{
let input = Input.instance
return if let id = input.firstPadID { input.getPad(id: id) } else { nil }
}
private static let _UP = UInt8(0b00), _DOWN = UInt8(0b10), _IMPULSE = UInt8(0b01)
private static let _PRESS = _DOWN | _IMPULSE
private static let _RELEASE = _UP | _IMPULSE
private var _btns = [UInt8](repeating: 0, count: Int(SDL_CONTROLLER_BUTTON_MAX.rawValue))
private var _axes = [Int16](repeating: 0, count: Int(SDL_CONTROLLER_AXIS_MAX.rawValue))
private var _sdlPad: OpaquePointer
internal init(pad: OpaquePointer)
{
self._sdlPad = pad
}
internal func newTick()
{
_btns = _btns.map({ $0 & ~Self._IMPULSE })
}
internal func close()
{
SDL_GameControllerClose(_sdlPad)
}
internal func buttonPressEvent(_ btn: UInt8)
{
_btns[Int(btn)] = Self._PRESS
}
internal func buttonReleaseEvent(_ btn: UInt8)
{
_btns[Int(btn)] = Self._RELEASE
}
internal func axisEvent(_ axis: UInt8, _ value: Int16)
{
_axes[Int(axis)] = value
}
}
public extension GamePad.State
{
var leftStick: Vec2f { Vec2f(axis(.leftStickX), axis(.leftStickY)) }
var rightStick: Vec2f { Vec2f(axis(.rightStickX), axis(.rightStickY)) }
}
internal extension GamePad.Axes
{
var sdlEnum: SDL_GameControllerAxis
{
switch self
{
case .leftStickX: SDL_CONTROLLER_AXIS_LEFTX
case .leftStickY: SDL_CONTROLLER_AXIS_LEFTY
case .rightStickX: SDL_CONTROLLER_AXIS_RIGHTX
case .rightStickY: SDL_CONTROLLER_AXIS_RIGHTY
case .leftTrigger: SDL_CONTROLLER_AXIS_TRIGGERLEFT
case .rightTrigger: SDL_CONTROLLER_AXIS_TRIGGERRIGHT
}
}
}
internal extension GamePad.Buttons
{
var sdlEnum: SDL_GameControllerButton
{
switch self
{
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 .start: SDL_CONTROLLER_BUTTON_START
case .select: SDL_CONTROLLER_BUTTON_BACK
case .guide: SDL_CONTROLLER_BUTTON_GUIDE
case .mic: SDL_CONTROLLER_BUTTON_MISC1
case .touchPad: SDL_CONTROLLER_BUTTON_TOUCHPAD
case .leftStick: SDL_CONTROLLER_BUTTON_LEFTSTICK
case .rightStick: SDL_CONTROLLER_BUTTON_RIGHTSTICK
case .leftBumper: SDL_CONTROLLER_BUTTON_LEFTSHOULDER
case .rightBumper: SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
case .dpadUp: SDL_CONTROLLER_BUTTON_DPAD_UP
case .dpadDown: SDL_CONTROLLER_BUTTON_DPAD_DOWN
case .dpadLeft: SDL_CONTROLLER_BUTTON_DPAD_LEFT
case .dpadRight: SDL_CONTROLLER_BUTTON_DPAD_RIGHT
case .paddle1: SDL_CONTROLLER_BUTTON_PADDLE1
case .paddle2: SDL_CONTROLLER_BUTTON_PADDLE2
case .paddle3: SDL_CONTROLLER_BUTTON_PADDLE3
case .paddle4: SDL_CONTROLLER_BUTTON_PADDLE4
}
}
}

View File

@ -0,0 +1,62 @@
import SDL2
public class Input
{
private static let _instance = Input()
public static var instance: Input { _instance }
public var keyboard: Keyboard { _keyboard }
public func getPad(id: Int) -> GamePad? { _pads[Int32(id)] }
public var firstPadID: Int? { _firstJoyID >= 0 ? Int(_firstJoyID) : nil }
private var _keyboard = Keyboard()
private var _pads = Dictionary<Int32, GamePad>()
private var _firstJoyID: Int32 = -1
private init() {}
internal func pressEvent(scan: SDL_Scancode, repeat r: Bool) { keyboard.pressEvent(scan: scan, repeat: r) }
internal func releaseEvent(scan: SDL_Scancode) { keyboard.releaseEvent(scan: scan) }
internal func padConnectedEvent(index: Int32)
{
guard let sdlPad = SDL_GameControllerOpen(index)
else { return }
let id = SDL_JoystickGetDeviceInstanceID(index)
_pads[id] = GamePad(pad: sdlPad)
print("Using gamepad #\(id), \"\(String(cString: SDL_GameControllerName(sdlPad)))\"")
if _firstJoyID < 0 { _firstJoyID = id }
}
internal func padRemovedEvent(index: Int32)
{
let id = SDL_JoystickGetDeviceInstanceID(index)
_pads.removeValue(forKey: id)
if id == _firstJoyID
{
_firstJoyID = _pads.keys.first ?? -1
}
}
internal func padButtonPressEvent(id: Int32, btn: UInt8)
{
_pads[id]?.buttonPressEvent(btn)
}
internal func padButtonReleaseEvent(id: Int32, btn: UInt8)
{
_pads[id]?.buttonReleaseEvent(btn)
}
internal func padAxisEvent(id: Int32, axis: UInt8, value: Int16)
{
_pads[id]?.axisEvent(axis, value)
}
internal func newTick()
{
_keyboard.newTick()
for pad in _pads.values { pad.newTick() }
}
}

View File

@ -0,0 +1,137 @@
import SDL2
public class Keyboard
{
public enum Keys
{
case a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
case right, left, down, up
}
public func keyDown(_ key: Keys) -> Bool
{
_keys[Int(key.sdlScancode.rawValue)] & Self._DOWN == Self._DOWN
}
public func keyPressed(_ key: Keys, repeat rep: Bool = false) -> Bool
{
var state = _keys[Int(key.sdlScancode.rawValue)]
if rep { state &= ~Self._REPEAT }
return state == Self._PRESS
}
public func keyReleased(_ key: Keys) -> Bool
{
_keys[Int(key.sdlKeycode.rawValue)] == Self._RELEASE
}
private static let _UP = UInt8(0b000), _DOWN = UInt8(0b010), _IMPULSE = UInt8(0b001)
private static let _REPEAT = UInt8(0b100)
private static let _PRESS: UInt8 = _DOWN | _IMPULSE
private static let _RELEASE: UInt8 = _UP | _IMPULSE
private var _keys = [UInt8](repeating: _UP, count: Int(SDL_NUM_SCANCODES.rawValue))
internal func newTick()
{
_keys = _keys.map({ $0 & ~(Self._IMPULSE | Self._REPEAT) })
}
internal func pressEvent(scan: SDL_Scancode, repeat rep: Bool)
{
var newState = Self._PRESS
if rep { newState |= Self._REPEAT }
_keys[Int(scan.rawValue)] = newState
}
internal func releaseEvent(scan: SDL_Scancode)
{
_keys[Int(scan.rawValue)] = Self._RELEASE
}
}
extension Keyboard.Keys
{
internal var sdlKeycode: SDL_KeyCode
{
switch self
{
case .a: SDLK_a
case .b: SDLK_b
case .c: SDLK_c
case .d: SDLK_d
case .e: SDLK_e
case .f: SDLK_f
case .g: SDLK_g
case .h: SDLK_h
case .i: SDLK_i
case .j: SDLK_j
case .k: SDLK_k
case .l: SDLK_l
case .m: SDLK_m
case .n: SDLK_n
case .o: SDLK_o
case .p: SDLK_p
case .q: SDLK_q
case .r: SDLK_r
case .s: SDLK_s
case .t: SDLK_t
case .u: SDLK_u
case .v: SDLK_v
case .w: SDLK_w
case .x: SDLK_x
case .y: SDLK_y
case .z: SDLK_z
case .left: SDLK_LEFT
case .right: SDLK_RIGHT
case .up: SDLK_UP
case .down: SDLK_DOWN
}
}
internal var sdlScancode: SDL_Scancode
{
switch self
{
case .a: SDL_SCANCODE_A
case .b: SDL_SCANCODE_B
case .c: SDL_SCANCODE_C
case .d: SDL_SCANCODE_D
case .e: SDL_SCANCODE_E
case .f: SDL_SCANCODE_F
case .g: SDL_SCANCODE_G
case .h: SDL_SCANCODE_H
case .i: SDL_SCANCODE_I
case .j: SDL_SCANCODE_J
case .k: SDL_SCANCODE_K
case .l: SDL_SCANCODE_L
case .m: SDL_SCANCODE_M
case .n: SDL_SCANCODE_N
case .o: SDL_SCANCODE_O
case .p: SDL_SCANCODE_P
case .q: SDL_SCANCODE_Q
case .r: SDL_SCANCODE_R
case .s: SDL_SCANCODE_S
case .t: SDL_SCANCODE_T
case .u: SDL_SCANCODE_U
case .v: SDL_SCANCODE_V
case .w: SDL_SCANCODE_W
case .x: SDL_SCANCODE_X
case .y: SDL_SCANCODE_Y
case .z: SDL_SCANCODE_Z
case .left: SDL_SCANCODE_LEFT
case .right: SDL_SCANCODE_RIGHT
case .up: SDL_SCANCODE_UP
case .down: SDL_SCANCODE_DOWN
}
}
}
extension SDL_KeyCode
{
internal init(scancode: SDL_Scancode)
{
self.init(scancode.rawValue | UInt32(SDLK_SCANCODE_MASK))
}
}

View File

@ -4,7 +4,7 @@ import OrderedCollections
class G3DbLoader: LoaderProtocol class G3DbLoader: LoaderProtocol
{ {
typealias T = Mesh typealias T = Mesh<VertexPositionNormalTexcoord>
func load(url: URL) -> T? func load(url: URL) -> T?
{ {
@ -12,6 +12,8 @@ class G3DbLoader: LoaderProtocol
else { return Optional.none } else { return Optional.none }
return try? reader.read() return try? reader.read()
} }
func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) }
} }
@ -27,15 +29,15 @@ fileprivate struct G3DbReader
self.reader = UBJsonReader(file: file) self.reader = UBJsonReader(file: file)
} }
mutating func read() throws -> Mesh mutating func read() throws -> Mesh<VertexPositionNormalTexcoord>
{ {
let model = try readModel() let model = try readModel()
let mesh = model.meshes[0] let mesh = model.meshes[0]
var vertices = [Mesh.Vertex]() var vertices = [VertexPositionNormalTexcoord]()
var indices = [UInt16]() var indices = [UInt16]()
var subMeshes = OrderedDictionary<String, Mesh.SubMesh>() var subMeshes = OrderedDictionary<String, Mesh<VertexPositionNormalTexcoord>.SubMesh>()
let attributeSize = let attributeSize =
{ (attrib: G3DAttribute) in { (attrib: G3DAttribute) in

View File

@ -16,6 +16,8 @@ struct NSImageLoader: LoaderProtocol
return image return image
} }
func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) }
static func loadImage(url: URL) throws -> Image static func loadImage(url: URL) throws -> Image
{ {
try autoreleasepool try autoreleasepool

View File

@ -2,10 +2,9 @@ import Foundation
import OrderedCollections import OrderedCollections
/*
extension ObjMaterial extension ObjMaterial
{ {
func convert() -> Material func convert(content: UnsafeMutablePointer<ContentManager>? = nil) -> Material
{ {
var m = Material() var m = Material()
m.diffuse = self.diffuse.setAlpha(self.alpha) m.diffuse = self.diffuse.setAlpha(self.alpha)
@ -14,37 +13,61 @@ extension ObjMaterial
m.specular = self.specular m.specular = self.specular
m.specularExp = self.specularExp m.specularExp = self.specularExp
} }
if let content = content
{
if let albedo = self.diffuseMap
{
let filter = albedo.flags.contains(.blendHoriz) || albedo.flags.contains(.blendVert)
m.texture = (try? content.pointee.load(albedo.path,
params: Texture2DParameters(
minFilter: filter ? .linear : .point,
magFilter: filter ? .linear : .point,
wrapMode: albedo.flags.contains(.clamp) ? .clampBorder : .repeating,
mipMode: .linear)))?.id ?? RenderTexture2D.empty
}
}
return m return m
} }
} }
*/
public struct ObjLoader: LoaderProtocol public struct ObjLoader: LoaderProtocol
{ {
public typealias T = Mesh typealias V = VertexPositionNormalColourTexcoord
public typealias T = Mesh<VertexPositionNormalColourTexcoord>
public init() {} public init() {}
public func load(url: URL) -> T? public func load(url: URL) -> T?
{ {
try? Self.read(url: url) return try? Self.read(model: try ObjReader.read(url: url), content: nil)
} }
private static func read(url: URL) throws -> Mesh public func load(url: URL, content: inout ContentManager) -> T?
{ {
return try read(model: try ObjReader.read(url: url)) return try? Self.read(model: try ObjReader.read(url: url), content: &content)
} }
public static func read(model: ObjModel) throws -> Mesh public static func read(model: ObjModel, content: UnsafeMutablePointer<ContentManager>?) throws -> T
{ {
var subMeshes: OrderedDictionary<String, Mesh.SubMesh> = .init() var subMeshes = OrderedDictionary<String, T.SubMesh>()
var vertices = [Mesh.Vertex]() var materials = OrderedDictionary<String, Material>()
var vertices = [V]()
var indices = [UInt16]() var indices = [UInt16]()
for (key, mtl) in model.materials
{
materials[key] = mtl.convert(content: content)
}
let readIndex = let readIndex =
{ (v: ObjModel.Index) -> UInt16 in { (v: ObjModel.Index) -> UInt16 in
let vertex = Mesh.Vertex( let colour = model.colours.isEmpty ? .white : Colour(
r: model.colours[v.p][0],
g: model.colours[v.p][1],
b: model.colours[v.p][2]).linear
let vertex = V(
position: model.positions[v.p], position: model.positions[v.p],
colour: colour,
normal: model.normals[v.n], normal: model.normals[v.n],
texCoord: model.texCoords[v.t]) texCoord: model.texCoords[v.t])
if let index = vertices.firstIndex(of: vertex) if let index = vertices.firstIndex(of: vertex)
@ -61,32 +84,39 @@ public struct ObjLoader: LoaderProtocol
} }
} }
for mesh in model.objects for object in model.objects.filter({ $0.key != "Collision3D" })
{ {
let start = indices.count var id = 0
for face: ObjModel.Face in mesh.value.faces for mesh: ObjModel.Mesh in object.value.meshes
{ {
switch face let start = indices.count
for face: ObjModel.Face in mesh.faces
{ {
case .triangle(let v1, let v2, let v3): switch face
for v in [ v1, v2, v3 ] { _ = readIndex(v) } {
case .quad(let v1, let v2, let v3, let v4): case .triangle(let v1, let v2, let v3):
let n1 = readIndex(v1) for v in [ v1, v2, v3 ] { _ = readIndex(v) }
_ = readIndex(v2) case .quad(let v1, let v2, let v3, let v4):
indices.append(readIndex(v3)) let n1 = readIndex(v1)
_ = readIndex(v4) _ = readIndex(v2)
indices.append(n1) indices.append(readIndex(v3))
case .ngon(_): fallthrough _ = readIndex(v4)
default: break indices.append(n1)
case .ngon(_): fallthrough
default: break
}
} }
} let length = indices.count - start
let length = indices.count - start if length > 0
if length > 0 {
{ subMeshes["\(object.key)_\(id)"] = .init(
subMeshes[mesh.key] = .init(start: start, length: length) start: start, length: length,
material: materials.index(forKey: mesh.material) ?? -1)
}
id += 1
} }
} }
return Mesh(vertices: vertices, indices: indices, subMeshes: subMeshes) return Mesh(vertices: vertices, indices: indices, subMeshes: subMeshes, materials: materials.values.map { $0 })
} }
} }

View File

@ -8,25 +8,46 @@ public struct ObjReader
var file = try ObjDocumentReader(filePath: url) var file = try ObjDocumentReader(filePath: url)
var model = ObjModel() var model = ObjModel()
var materials = Dictionary<String, ObjMaterial>()
var name: String? = nil var name: String? = nil
var object = ObjModel.Object() var object = ObjModel.Object()
var mesh = ObjModel.Mesh()
file.string(tag: "mtllib") { s in file.string(tag: "mtllib") { s in
let mats = try ObjMtlLoader.read(url: url.deletingLastPathComponent().appendingPathComponent(s[0])) let mats = try ObjMtlLoader.read(url: url.deletingLastPathComponent().appendingPathComponent(s[0]))
materials.merge(mats, uniquingKeysWith: { (_, new) in new } ) model.materials.merge(mats, uniquingKeysWith: { (_, new) in new } )
} }
file.string(tag: "o", count: 1) { s in file.string(tag: "o", count: 1) { s in
if !object.faces.isEmpty if !mesh.faces.isEmpty { object.meshes.append(mesh) }
if !object.meshes.isEmpty
{ {
model.objects[name!] = object model.objects[name!] = object
object = .init() object = .init()
mesh = .init()
} }
name = String(s[0]) name = String(s[0])
} }
file.float(tag: "v", count: 3) { v in model.positions.append(Vec3f(v[0], v[1], v[2])) } file.float(tag: "v") { v in
if v.count == 6
{
if model.colours.isEmpty && !model.positions.isEmpty
{
for _ in 0..<model.positions.count
{
model.colours.append(.one)
}
}
model.colours.append(Vec3f(v[3], v[4], v[5]))
}
else if !model.colours.isEmpty && v.count == 3
{
model.colours.append(.one)
}
else if v.count != 3 { throw ObjLoaderError.badTagParameters }
model.positions.append(Vec3f(v[0], v[1], v[2]))
}
file.float(tag: "vn", count: 3) { v in model.normals.append(Vec3f(v[0], v[1], v[2])) } file.float(tag: "vn", count: 3) { v in model.normals.append(Vec3f(v[0], v[1], v[2])) }
file.float(tag: "vt", count: 2) { v in model.texCoords.append(Vec2f(v[0], v[1])) } file.float(tag: "vt", count: 2) { v in model.texCoords.append(Vec2f(v[0], v[1])) }
file.int(tag: "l") { i in mesh.faces.append(.line(p1: i[0], p2: i[1])) }
file.raw(tag: "f") { raw in file.raw(tag: "f") { raw in
let readIndex = let readIndex =
{ (raw: Substring) in { (raw: Substring) in
@ -40,14 +61,14 @@ public struct ObjReader
if raw.count == 3 if raw.count == 3
{ {
for raw in raw { _ = try readIndex(raw) } for raw in raw { _ = try readIndex(raw) }
object.faces.append(.triangle( mesh.faces.append(.triangle(
v1: try readIndex(raw[raw.startIndex]), v1: try readIndex(raw[raw.startIndex]),
v2: try readIndex(raw[raw.startIndex + 1]), v2: try readIndex(raw[raw.startIndex + 1]),
v3: try readIndex(raw[raw.startIndex + 2]))) v3: try readIndex(raw[raw.startIndex + 2])))
} }
else if raw.count == 4 else if raw.count == 4
{ {
object.faces.append(.quad( mesh.faces.append(.quad(
v1: try readIndex(raw[raw.startIndex]), v1: try readIndex(raw[raw.startIndex]),
v2: try readIndex(raw[raw.startIndex + 1]), v2: try readIndex(raw[raw.startIndex + 1]),
v3: try readIndex(raw[raw.startIndex + 2]), v3: try readIndex(raw[raw.startIndex + 2]),
@ -55,17 +76,25 @@ public struct ObjReader
} }
else if raw.count >= 5 else if raw.count >= 5
{ {
object.faces.append(.ngon(try raw.map { try readIndex($0) })) mesh.faces.append(.ngon(try raw.map { try readIndex($0) }))
} }
else { throw ObjLoaderError.badTagParameters } else { throw ObjLoaderError.badTagParameters }
} }
file.int(tag: "l") { i in object.faces.append(.line(p1: i[0], p2: i[1])) } file.string(tag: "usemtl", count: 1) { s in
if !mesh.faces.isEmpty
{
object.meshes.append(mesh)
mesh = .init()
}
mesh.material = s[0]
}
// file.int(tag: "s", count: 1) { i in } //TODO: Smoothing groups
try file.read() try file.read()
if !object.faces.isEmpty if !mesh.faces.isEmpty { object.meshes.append(mesh) }
{ if !object.meshes.isEmpty { model.objects[name!] = object }
model.objects[name!] = object
}
return model return model
} }
} }

View File

@ -39,6 +39,8 @@ public extension SIMD2 where Scalar: FloatingPoint
return Self(self.x * invX + b.x * x, self.y * invX + b.y * 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 func cardinalDeadzone(min: Scalar, max: Scalar) -> Self
{ {
Self(self.x.axisDeadzone(min, max), self.y.axisDeadzone(min, max)) Self(self.x.axisDeadzone(min, max), self.y.axisDeadzone(min, max))

View File

@ -76,13 +76,19 @@ class OpenGL: Renderer
} }
} }
var wireframe: Bool
{
get { state.polygonMode == .line }
set(mode) { state.polygonMode = mode ? .line : .fill }
}
func setVsync(mode: VSyncMode) throws func setVsync(mode: VSyncMode) throws
{ {
guard SDL_GL_SetSwapInterval(mode.sdlInterval) == 0 guard SDL_GL_SetSwapInterval(mode.sdlInterval) == 0
else { throw RendererError.sdlError(message: "SDL_GL_SetSwapInterval: \(String(cString: SDL_GetError()))") } else { throw RendererError.sdlError(message: "SDL_GL_SetSwapInterval: \(String(cString: SDL_GetError()))") }
} }
func createMesh(mesh: Mesh) throws -> RenderMesh func createMesh<V: Vertex>(mesh: Mesh<V>) throws -> RenderMesh<V>
{ {
var buffers = [GLuint](repeating: 0, count: 2) var buffers = [GLuint](repeating: 0, count: 2)
buffers.withUnsafeMutableBufferPointer buffers.withUnsafeMutableBufferPointer
@ -94,35 +100,36 @@ class OpenGL: Renderer
state.elementArrayBuffer = buffers[1] state.elementArrayBuffer = buffers[1]
glBufferData(GLenum(GL_ARRAY_BUFFER), glBufferData(GLenum(GL_ARRAY_BUFFER),
MemoryLayout<Mesh.Vertex>.stride * mesh.vertices.count, MemoryLayout<V>.stride * mesh.vertices.count,
mesh.vertices, GLenum(GL_STATIC_DRAW)) mesh.vertices, GLenum(GL_STATIC_DRAW))
glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER),
MemoryLayout<Mesh.Index>.stride * mesh.indices.count, MemoryLayout<V>.stride * mesh.indices.count,
mesh.indices, GLenum(GL_STATIC_DRAW)) mesh.indices, GLenum(GL_STATIC_DRAW))
state.elementArrayBuffer = OpenGLState.defaultBuffer state.elementArrayBuffer = OpenGLState.defaultBuffer
state.arrayBuffer = OpenGLState.defaultBuffer state.arrayBuffer = OpenGLState.defaultBuffer
var subMeshes = [Mesh.SubMesh]() var subMeshes = [Mesh<V>.SubMesh]()
if mesh.subMeshes.isEmpty if mesh.subMeshes.isEmpty
{ {
subMeshes.append(Mesh.SubMesh(start: 0, length: mesh.indices.count)) subMeshes.append(Mesh<V>.SubMesh(start: 0, length: mesh.indices.count))
} }
else else
{ {
for subMesh in mesh.subMeshes for subMesh in mesh.subMeshes
{ {
if ["Collision", "Collision3D"].contains(subMesh.key) { continue } subMeshes.append(Mesh<V>.SubMesh(
subMeshes.append(Mesh.SubMesh(
start: subMesh.value.start, start: subMesh.value.start,
length: subMesh.value.length)) length: subMesh.value.length,
material: subMesh.value.material))
} }
} }
return RenderMesh( return RenderMesh(
vbo: Int(buffers[0]), vbo: Int(buffers[0]),
ibo: Int(buffers[1]), ibo: Int(buffers[1]),
subMeshes: subMeshes) subMeshes: subMeshes,
materials: mesh.materials)
} }
func createTexture(data: UnsafeRawPointer, width: Int, height: Int) throws -> RenderTexture2D func createTexture(data: UnsafeRawPointer, width: Int, height: Int) throws -> RenderTexture2D
@ -172,7 +179,7 @@ class OpenGL: Renderer
} }
func deleteMesh(_ mesh: RenderMesh) func deleteMesh<V: Vertex>(_ mesh: RenderMesh<V>)
{ {
var buffers = [GLuint](repeating: 0, count: 2) var buffers = [GLuint](repeating: 0, count: 2)
buffers[0] = GLuint(mesh.iboHnd) buffers[0] = GLuint(mesh.iboHnd)
@ -208,42 +215,64 @@ class OpenGL: Renderer
} }
private func draw(subMesh: Mesh.SubMesh) private func draw<V: Vertex>(subMesh: Mesh<V>.SubMesh)
{ {
glDrawElements( glDrawElements(
GLenum(GL_TRIANGLES), GLenum(GL_TRIANGLES),
GLsizei(subMesh.length), GLsizei(subMesh.length),
GLenum(GL_UNSIGNED_SHORT), GLenum(GL_UNSIGNED_SHORT),
.init(bitPattern: MemoryLayout<Mesh.Index>.stride * subMesh.start)); .init(bitPattern: MemoryLayout<Mesh<V>.Index>.stride * subMesh.start));
} }
private func draw(mesh: RenderMesh) private func bindMesh<V: Vertex>(mesh: RenderMesh<V>)
{ {
state.enableClient([ .vertex, .normal, .textureCoord ]) state.enableClient([ .vertex, .colour, .normal, .textureCoord ])
state.arrayBuffer = UInt32(mesh.vboHnd) state.arrayBuffer = UInt32(mesh.vboHnd)
state.elementArrayBuffer = UInt32(mesh.iboHnd) state.elementArrayBuffer = UInt32(mesh.iboHnd)
let stride = GLsizei(MemoryLayout<Mesh.Vertex>.stride) let stride = GLsizei(MemoryLayout<V>.stride)
glVertexPointer(3, GLenum(GL_FLOAT), stride, if V.self == VertexPositionNormalTexcoord.self
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \Mesh.Vertex.position)!))
glNormalPointer(GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \Mesh.Vertex.normal)!))
glTexCoordPointer(2, GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \Mesh.Vertex.texCoord)!))
for subMesh in mesh.subMeshes
{ {
draw(subMesh: subMesh) glVertexPointer(3, GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalTexcoord.position)!))
glNormalPointer(GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalTexcoord.normal)!))
glTexCoordPointer(2, GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalTexcoord.texCoord)!))
} }
else if V.self == VertexPositionNormalColourTexcoord.self
{
glVertexPointer(3, GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalColourTexcoord.position)!))
glColorPointer(4, GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalColourTexcoord.colour)!))
glNormalPointer(GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalColourTexcoord.normal)!))
glTexCoordPointer(2, GLenum(GL_FLOAT), stride,
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \VertexPositionNormalColourTexcoord.texCoord)!))
}
}
private func unbindMesh()
{
state.elementArrayBuffer = OpenGLState.defaultBuffer state.elementArrayBuffer = OpenGLState.defaultBuffer
state.arrayBuffer = OpenGLState.defaultBuffer state.arrayBuffer = OpenGLState.defaultBuffer
state.disableClient([ .vertex, .normal, .textureCoord ]) state.disableClient([ .vertex, .normal, .textureCoord ])
} }
func draw(mesh: RenderMesh, model: Mat4f, environment env: Environment) private func draw<V: Vertex>(mesh: RenderMesh<V>)
{
bindMesh(mesh: mesh)
for subMesh in mesh.subMeshes
{
draw(subMesh: subMesh)
}
unbindMesh()
}
func draw<V: Vertex>(mesh: RenderMesh<V>, model: Mat4f, environment env: Environment)
{ {
if (mesh.subMeshes.isEmpty) { return } if (mesh.subMeshes.isEmpty) { return }
@ -255,7 +284,7 @@ class OpenGL: Renderer
glPopMatrix() glPopMatrix()
} }
func draw(mesh: RenderMesh, environment env: Environment) func draw<V: Vertex>(mesh: RenderMesh<V>, environment env: Environment)
{ {
if (mesh.subMeshes.isEmpty) { return } if (mesh.subMeshes.isEmpty) { return }
@ -263,6 +292,14 @@ class OpenGL: Renderer
draw(mesh: mesh) draw(mesh: mesh)
} }
func draw<V: Vertex>(mesh: RenderMesh<V>, subMesh: Mesh<V>.SubMesh, environment env: Environment)
{
applyEnvironment(env)
bindMesh(mesh: mesh)
draw(subMesh: subMesh)
unbindMesh()
}
private func loadMatrix(_ matrix: Mat4f) private func loadMatrix(_ matrix: Mat4f)
{ {
withUnsafePointer(to: matrix.columns) withUnsafePointer(to: matrix.columns)

View File

@ -793,4 +793,26 @@ struct OpenGLState
} }
} }
} }
enum PolygonMode { case point, line, fill }
private var _polygonMode = PolygonMode.fill
var polygonMode: PolygonMode
{
get { _polygonMode }
set(newMode)
{
if newMode != _polygonMode
{
let modeEnum = switch newMode
{
case .point: GL_POINT
case .line: GL_LINE
case .fill: GL_FILL
}
glPolygonMode(GLenum(GL_FRONT_AND_BACK), GLenum(modeEnum));
_polygonMode = newMode
}
}
}
} }

View File

@ -11,15 +11,16 @@ public protocol Renderer
func resize(width: Int32, height: Int32) func resize(width: Int32, height: Int32)
var clearColour: Colour { get set } var clearColour: Colour { get set }
var wireframe: Bool { get set }
func setVsync(mode: VSyncMode) throws func setVsync(mode: VSyncMode) throws
func createMesh(mesh: Mesh) throws -> RenderMesh 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) throws -> RenderTexture2D
func createTexture(data: UnsafeRawPointer, width: Int, height: Int, func createTexture(data: UnsafeRawPointer, width: Int, height: Int,
filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
func deleteMesh(_ mesh: RenderMesh) func deleteMesh<V: Vertex>(_ mesh: RenderMesh<V>)
func deleteTexture(_ texture: RenderTexture2D) func deleteTexture(_ texture: RenderTexture2D)
func setProjection(matrix: Mat4f) func setProjection(matrix: Mat4f)
@ -27,8 +28,9 @@ public protocol Renderer
func setMaterial(_ mat: Material) func setMaterial(_ mat: Material)
func draw(mesh: RenderMesh, model: Mat4f, environment: Environment) func draw<V: Vertex>(mesh: RenderMesh<V>, model: Mat4f, environment: Environment)
func draw(mesh: RenderMesh, environment: Environment) func draw<V: Vertex>(mesh: RenderMesh<V>, environment: Environment)
func draw<V: Vertex>(mesh: RenderMesh<V>, subMesh: Mesh<V>.SubMesh, environment: Environment)
func drawGizmos(lines: [Line]) func drawGizmos(lines: [Line])
} }
@ -76,9 +78,9 @@ public protocol RendererResource
var isValid: Bool { get } var isValid: Bool { get }
} }
public struct RenderMesh: RendererResource public struct RenderMesh<V: Vertex>: RendererResource
{ {
public typealias T = Mesh public typealias T = Mesh<V>
public static var empty: RenderMesh { .init() } public static var empty: RenderMesh { .init() }
@ -86,7 +88,8 @@ public struct RenderMesh: RendererResource
private let _valid: Bool; private let _valid: Bool;
let vboHnd: Int, iboHnd: Int let vboHnd: Int, iboHnd: Int
let subMeshes: [Mesh.SubMesh] public let subMeshes: [Mesh<V>.SubMesh]
public let materials: [Material] // HACK !!!!
private init() private init()
{ {
@ -94,14 +97,16 @@ public struct RenderMesh: RendererResource
self.vboHnd = 0 self.vboHnd = 0
self.iboHnd = 0 self.iboHnd = 0
self.subMeshes = [] self.subMeshes = []
self.materials = []
} }
init(vbo: Int, ibo: Int, subMeshes: [Mesh.SubMesh]) init(vbo: Int, ibo: Int, subMeshes: [Mesh<V>.SubMesh], materials: [Material] = .init())
{ {
self._valid = true self._valid = true
self.vboHnd = vbo self.vboHnd = vbo
self.iboHnd = ibo self.iboHnd = ibo
self.subMeshes = subMeshes self.subMeshes = subMeshes
self.materials = materials
} }
} }

Binary file not shown.

View File

@ -14,6 +14,7 @@ import JolkEngine
vSync: .on, vSync: .on,
windowWidth: 1280, windowWidth: 1280,
windowHeight: 720, windowHeight: 720,
title: "Caves of Swift",
bundle: Bundle.module) bundle: Bundle.module)
).run() ).run()
} }
@ -21,105 +22,20 @@ import JolkEngine
class CavesOfJolk: ApplicationImplementation class CavesOfJolk: ApplicationImplementation
{ {
private lazy var scene = Scene1()
//private lazy var scene = CaveScene()
private var aspect: Float = 0 private var aspect: Float = 0
private var drawEdges = false
var env = Environment()
var worldMesh = RenderMesh.empty, cube = RenderMesh.empty, suzanne = RenderMesh.empty, toybox = RenderMesh.empty
var texture = Texture2D.empty, jolkTex = Texture2D.empty, suzanneDiffuse = Texture2D.empty
private lazy var world = Collision()
private lazy var colin = Colin()
private lazy var jolkCube = JolkCube(position: .init(0, 1, -4))
private var lightTheta: Float = 0
private var frameCount = 0 private var frameCount = 0
private var frameTimer: Float = 1 private var frameTimer: Float = 1
func create(render: inout Renderer) func create(render: inout Renderer)
{ {
render.clearColour = XnaColour.CornflowerBlue scene.setup(render: &render)
env.setFog(
colour: XnaColour.CornflowerBlue,
//mode: .depth, falloff: .range(type: .linear, start: 4, end: 38))
//mode: .depth, falloff: .range(type: .linear, start: 1.25, end: 24.5))
//mode: .distance, falloff: .factor(type: .exp2, density: 0.1))
mode: .depth, falloff: .factor(type: .exp2, density: 0.0222))
env.addAmbientLight(colour: XnaColour.DarkSlateGray.lighten(by: -0.666))
env.addDirectionalLight(
direction: -Vec3f(1, -1, -1).normalised,
colour: XnaColour.BlanchedAlmond
.lighten(by: -0.02)
.mix(with: XnaColour.LightSlateGray, 0.5)
.mix(with: XnaColour.White, 0.125))
env.addPointLight(
position: Vec3f(3, 0.33, -5),
colour: XnaColour.Green.mix(with: XnaColour.Gray, 0.5),
intensity: 2)
env.addPointLight(
position: Vec3f(5.5, 0.33, -6),
colour: XnaColour.Red.mix(with: XnaColour.Gray, 0.5),
intensity: 2)
env.addPointLight(
position: Vec3f(4, 0.33, -7),
colour: XnaColour.Blue.mix(with: XnaColour.Gray, 0.5),
intensity: 2)
} }
func loadContent(content: inout ContentManager) throws func loadContent(content: inout ContentManager) throws
{ {
try loadWorld(&content) try scene.loadContent(content: &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: .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: .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)),
.init(position: Vec3f(-1, 1, 1), normal: .left, texCoord: Vec2f(1, 1)),
.init(position: Vec3f(-1, -1, -1), normal: .down, texCoord: Vec2f(0, 0)),
.init(position: Vec3f( 1, -1, -1), normal: .down, texCoord: Vec2f(1, 0)),
.init(position: Vec3f(-1, -1, 1), normal: .down, texCoord: Vec2f(0, 1)),
.init(position: Vec3f( 1, -1, 1), normal: .down, texCoord: Vec2f(1, 1)),
.init(position: Vec3f(-1, 1, 1), normal: .up, texCoord: Vec2f(0, 0)),
.init(position: Vec3f( 1, 1, 1), normal: .up, texCoord: Vec2f(1, 0)),
.init(position: Vec3f(-1, 1, -1), normal: .up, texCoord: Vec2f(0, 1)),
.init(position: Vec3f( 1, 1, -1), normal: .up, texCoord: Vec2f(1, 1))
],
indices: [
0, 1, 2, 2, 1, 3,
4, 5, 6, 6, 5, 7,
8, 9, 10, 10, 9, 11,
12, 13, 14, 14, 13, 15,
16, 17, 18, 18, 17, 19,
20, 21, 22, 22, 21, 23 ]))
suzanne = try content.load("suzanne.g3db")
toybox = try content.load("toybox.g3db")
let linearMipped = Texture2DParameters(
minFilter: .linear, magFilter: .linear,
wrapMode: .repeating,
mipMode: .linear)
let linearClamp = Texture2DParameters(
minFilter: .linear, magFilter: .linear,
wrapMode: .clampEdge)
texture = try content.load("cobblestone.png", params: linearMipped)
jolkTex = try content.load("jolkmeup.jpg", params: linearClamp)
suzanneDiffuse = try content.load("suzanne_diffuse.png", params: linearMipped)
} }
func resize(width: Int, height: Int) func resize(width: Int, height: Int)
@ -130,10 +46,7 @@ class CavesOfJolk: ApplicationImplementation
func update(deltaTime: Float) func update(deltaTime: Float)
{ {
colin.update(deltaTime: deltaTime, world: world) scene.update(deltaTime: deltaTime)
jolkCube.update(deltaTime: deltaTime, world: world)
lightTheta += deltaTime
frameCount += 1 frameCount += 1
frameTimer -= deltaTime frameTimer -= deltaTime
@ -145,132 +58,10 @@ class CavesOfJolk: ApplicationImplementation
} }
} }
private func loadWorld(_ content: inout ContentManager) throws
{
//let world: Mesh = try content.load("World.obj")
let obj = try ObjReader.read(url: try content.getResource("World.obj"))
let mesh: Mesh = try ObjLoader.read(model: obj)
worldMesh = try content.create(mesh: mesh)
if let collision = obj.objects["Collision3D"]
{
world.build(obj: obj, collision: collision)
}
}
/*
if let collision = world.subMeshes["Collision"]
{
edges.reserveCapacity(collision.length / 6)
let get2dVertex =
{ i in
let vertex = world.vertices[Int(world.indices[i])]
let position = Vec2f(vertex.position.x, vertex.position.z)
let normal = Vec2f(vertex.normal.x, vertex.normal.z)
return (position, normal)
}
for i in stride(from: collision.start, to: collision.start + collision.length, by: 3)
{
let (v0p, v0n) = get2dVertex(i)
let (v1p, v1n) = get2dVertex(i + 1)
let (v2p, v2n) = get2dVertex(i + 2)
let (p0, p1) = if v0p == v2p || v1p == v2p { (v0p, v1p) }
else if v0p == v1p { (v0p, v2p) }
else { throw NSError() }
let edge = Edge(p: p0.lerp(p1, 0.5), n: (v0n + v1n + v2n).normalised, w: (p0 - p1).len)
if !edges.contains(where: { edge.p == $0.p && edge.w == $0.w })
{
edges.append(edge)
}
}
}
*/
func draw(render: inout Renderer, deltaTime: Float) func draw(render: inout Renderer, deltaTime: Float)
{ {
// Setup camera if Input.instance.keyboard.keyPressed(.w, repeat: true) { render.wireframe = !render.wireframe }
render.setProjection(matrix:
.perspective(fovY: Float.rad(fromDeg: colin.fov), aspect: aspect, zNear: 0.1, zFar: 100))
render.setView(matrix: colin.transform)
// Update point lights scene.draw(render: &render, deltaTime: deltaTime, aspectRatio: aspect)
var theta = Vec3f(repeating: lightTheta) * Vec3f(0.12, 0.011, 0.056) * 6
var i = env.lights.startIndex
while i != env.lights.endIndex
{
if case .point(let colour, _, _) = env.lights[i]
{
env.lights[i] = .point(
colour: colour,
position: Vec3f(
4 + 6 * cos(theta[0]),
0.33 + 0.33 * sin(theta[1]),
-6 + 3 * sin(theta[0] * 2)),
intensity: 3.1 + 1.53 * cos(theta[2]))
let spacing = 0.5 * Float.pi * (2 / 3.0)
theta += spacing * Vec3f(1, 0.98, 0.5566)
}
i = env.lights.index(after: i)
}
// Draw world
render.setMaterial(Material(
specular: XnaColour.BlanchedAlmond.mix(with: .black, 0.12),
texture: texture.id))
render.draw(mesh: worldMesh, environment: env)
// Draw jolked up shit
render.setMaterial(Material(
specular: XnaColour.Gray,
gloss: 20.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.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 Input.instance.keyPressed(.c) { drawEdges = !drawEdges }
if drawEdges
{
/*
var lines = [Line](
repeating: .init(from: .init(), to: .init(), colour: .zero),
count: edges.count * 3)
var i: Int = 0
for edge in edges
{
let tangent = Vec2f(edge.n.y, -edge.n.x)
let a = edge.p - tangent * edge.w * 0.5
let b = edge.p + tangent * edge.w * 0.5
lines[i] = Line(
from: Vec3f(a.x, 0, a.y),
to: Vec3f(b.x, 0, b.y),
colour: Colour(r: 0.1, g: 0.9, b: 0.1))
lines[i + 1] = Line(
from: Vec3f(edge.p.x, 0, edge.p.y),
to: Vec3f(edge.p.x + edge.n.x * 0.25, 0, edge.p.y + edge.n.y * 0.25),
colour: Colour(r: 0.9, g: 0.1, b: 0.1))
let deltaPos = Vec2f(colin.position.x, colin.position.z) - edge.p
let something = tangent * deltaPos.cross(edge.n)
lines[i + 2] = Line(
from: Vec3f(edge.p.x + something.x, 0.0, edge.p.y + something.y),
to: Vec3f(edge.p.x + something.x + edge.n.x * 0.5, 0.0, edge.p.y + something.y + edge.n.y * 0.5),
colour: XnaColour.Azure)
i += 3
}
render.drawGizmos(lines: lines)
*/
world.draw(render, position: colin.position)
}
} }
} }

View File

@ -2,7 +2,7 @@ import Foundation
import JolkEngine import JolkEngine
class Collision struct Collision
{ {
struct Edge { let p: Vec2f, n: Vec2f, w: Float } struct Edge { let p: Vec2f, n: Vec2f, w: Float }
@ -66,16 +66,16 @@ class Collision
enum Winding { case none, cw, ccw } enum Winding { case none, cw, ccw }
func build(obj: ObjModel, collision: ObjModel.Object) mutating func build(obj: ObjModel, collision: ObjModel.Object)
{ {
for face in collision.faces for face in collision.meshes.flatMap({ $0.faces })
{ {
switch face switch face
{ {
case .triangle(let v1, let v2, let v3): case .triangle(let v1, let v2, let v3):
let t = Triangle(obj.positions[v1.p], obj.positions[v2.p], obj.positions[v3.p]) let t = Triangle(obj.positions[v1.p], obj.positions[v2.p], obj.positions[v3.p])
let n = t.normal let n = t.normal
if abs(n.y) < 0.25 { continue } if abs(n.y) == 0 { continue }
edge3d.append(.triangle(normal: n, origin: (t.a + t.b + t.c) / 3.0, tri: t)) edge3d.append(.triangle(normal: n, origin: (t.a + t.b + t.c) / 3.0, tri: t))
case .quad(let v1, let v2, let v3, let v4): case .quad(let v1, let v2, let v3, let v4):
let q = Quad(obj.positions[v1.p], obj.positions[v2.p], obj.positions[v3.p], obj.positions[v4.p]) let q = Quad(obj.positions[v1.p], obj.positions[v2.p], obj.positions[v3.p], obj.positions[v4.p])

View File

@ -2,7 +2,7 @@ import JolkEngine
protocol Actor protocol Actor
{ {
func update(deltaTime: Float, world: Collision) mutating func update(deltaTime: Float, world: Collision)
var position: Vec3f { get } var position: Vec3f { get }
var transform: Mat4f { get } var transform: Mat4f { get }

View File

@ -4,22 +4,23 @@ import OpenGL.GL
import JolkEngine import JolkEngine
class Colin: Actor struct Colin: Actor
{ {
var position: Vec3f { _pos } var position: Vec3f { _pos }
mutating func setPosition(_ new: Vec3f) { _pos = new }
var transform: Mat4f var transform: Mat4f
{ {
//.rotate(yaw: angle.x, pitch: angle.y, roll: sin(time)) * //.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)) * //.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) * .translate(-position - Vec3f(0, 1, 0))
} }
var angle: Vec2f { return Vec2f(ofsAngle.x + _angle, ofsAngle.y) } var angle: Vec2f { return Vec2f(ofsAngle.x + _angle, ofsAngle.y) }
mutating func setAngle(_ new: Vec2f) { _angle = new.x; ofsAngle = .init(0.0, new.y) }
var fov: Float { return _fov } var fov: Float { return _fov }
private var time: Float = 0.0 private var time: Float = 0.0
//private var jumpVel: Float = 0.0
private var velocity: Vec3f = .zero private var velocity: Vec3f = .zero
private var _pos: Vec3f = .zero private var _pos: Vec3f = .zero
private var _pos2D: Vec2f { get { Vec2f(_pos.x, _pos.z) } set(new) { _pos.x = new.x; _pos.z = new.y } } private var _pos2D: Vec2f { get { Vec2f(_pos.x, _pos.z) } set(new) { _pos.x = new.x; _pos.z = new.y } }
@ -27,13 +28,11 @@ class Colin: Actor
private var ofsAngle: Vec2f = .zero private var ofsAngle: Vec2f = .zero
private var _fov: Float = 60.0 private var _fov: Float = 60.0
private var nutted = false
private var colinMode = false private var colinMode = false
private var backPressed = false
private let colinWidth: Float = 0.2 private let colinWidth: Float = 0.2
private func move2D(new: Vec2f, edges: [Collision.Edge]) private mutating func move2D(new: Vec2f, edges: [Collision.Edge])
{ {
let velocity = new - _pos2D let velocity = new - _pos2D
var lastPos = _pos2D var lastPos = _pos2D
@ -79,7 +78,7 @@ class Colin: Actor
} }
} }
private func response3D(plainPos o: Vec3f, plainNorm n: Vec3f) private mutating func response3D(plainPos o: Vec3f, plainNorm n: Vec3f)
{ {
let height: Float = 1.12 let height: Float = 1.12
//let diff = _pos - lastPos //let diff = _pos - lastPos
@ -97,7 +96,7 @@ class Colin: Actor
} }
} }
private func move3D(_ deltaTime: Float, world: Collision) private mutating func move3D(_ deltaTime: Float, world: Collision)
{ {
var lastPos = _pos var lastPos = _pos
_pos += velocity * deltaTime _pos += velocity * deltaTime
@ -169,91 +168,73 @@ class Colin: Actor
} }
} }
func update(deltaTime: Float, world: Collision) mutating func update(deltaTime: Float, world: Collision)
{ {
time += deltaTime time += deltaTime
let axisRescale = 1.0 / Float(INT16_MAX) let pad = GamePad.current?.state
let getAxis = { (axis: SDL_GameControllerAxis) in
Float(SDL_GameControllerGetAxis(sdlPad, axis)) * axisRescale }
let getButton = { (btn: SDL_GameControllerButton) in
SDL_GameControllerGetButton(sdlPad, btn) == SDL_PRESSED }
let stick = Vec2f( let stick = pad?.leftStick.cardinalDeadzone(min: 0.1, max: 1)
getAxis(SDL_CONTROLLER_AXIS_LEFTX),
getAxis(SDL_CONTROLLER_AXIS_LEFTY))
.cardinalDeadzone(min: 0.1, max: 1)
let turnSpeed = Float.pi * 2 * 0.1 let turnSpeed = Float.pi * 2 * 0.2
if Input.instance.keyDown(.left) if Input.instance.keyboard.keyDown(.left)
{ {
_angle -= turnSpeed * deltaTime _angle -= turnSpeed * deltaTime
} }
if Input.instance.keyDown(.right) if Input.instance.keyboard.keyDown(.right)
{ {
_angle += turnSpeed * deltaTime _angle += turnSpeed * deltaTime
} }
if stick != .zero if let stick = stick
{ {
_angle += stick.x * 2.0 * turnSpeed * deltaTime _angle += stick.x * 2.0 * turnSpeed * deltaTime
} }
var moveVec: Vec2f = .zero var moveVec: Vec2f = .zero
let speed: Float = 2 let speed: Float = 4
let forward = Vec2f(sin(self._angle), -cos(self._angle)) let forward = Vec2f(sin(self._angle), -cos(self._angle))
if Input.instance.keyDown(.up) if Input.instance.keyboard.keyDown(.up)
{ {
moveVec += forward * speed moveVec += forward * speed
} }
if Input.instance.keyDown(.down) if Input.instance.keyboard.keyDown(.down)
{ {
moveVec -= forward * speed moveVec -= forward * speed
} }
if stick != .zero if let stick = stick
{ {
moveVec -= forward * stick.y * speed moveVec -= forward * stick.y * speed
} }
if Input.instance.keyDown(.c) if Input.instance.keyboard.keyDown(.c)
{ {
colinMode = !colinMode colinMode = !colinMode
} }
let lookCone = Float.pi / 2.0 let lookCone = Float.pi / 2.0
let dst = Vec2f( let dst = pad?.rightStick.radialDeadzone(min: 0.1, max: 1) ?? .zero
getAxis(SDL_CONTROLLER_AXIS_RIGHTX), ofsAngle = ofsAngle.lerp(dst * lookCone, 16 * deltaTime)
getAxis(SDL_CONTROLLER_AXIS_RIGHTY))
.radialDeadzone(min: 0.1, max: 1) * lookCone
ofsAngle = ofsAngle.lerp(dst, 16 * deltaTime)
let targetFov = Float.lerp(60, 20, getAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) let targetFov = Float.lerp(60, 20, pad?.axis(.rightTrigger) ?? .zero)
_fov = Float.lerp(_fov, targetFov, 20 * deltaTime) _fov = Float.lerp(_fov, targetFov, 20 * deltaTime)
let right = Vec2f(cos(self._angle), sin(self._angle)) if let pad = pad
if getButton(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{ {
moveVec -= right * speed let right = Vec2f(cos(self._angle), sin(self._angle))
} if pad.down(.leftBumper) { moveVec -= right * speed }
if getButton(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) if pad.down(.rightBumper) { moveVec += right * speed }
{ if pad.pressed(.select) { colinMode = !colinMode }
moveVec += right * speed
} }
let back = getButton(SDL_CONTROLLER_BUTTON_BACK) if pad?.pressed(.south) ?? false || Input.instance.keyboard.keyDown(.n)
if !backPressed && back { colinMode = !colinMode }
backPressed = back
if getButton(SDL_CONTROLLER_BUTTON_A) || Input.instance.keyDown(.n)
{ {
// play nut.wav // play nut.wav
nutted = true
} }
else { nutted = false }
//move2D(new: _pos2D + velocity, edges: edges) //move2D(new: _pos2D + velocity, edges: edges)
if Input.instance.keyDown(.z) || getButton(SDL_CONTROLLER_BUTTON_B) { velocity.y = 2.0 } if Input.instance.keyboard.keyDown(.z) || pad?.down(.east) ?? false { velocity.y = 2.0 }
//_pos.y += jumpVel * deltaTime //_pos.y += jumpVel * deltaTime
//if _pos.y > 0.0 { jumpVel -= 5.4 * deltaTime } //if _pos.y > 0.0 { jumpVel -= 5.4 * deltaTime }
velocity.y -= 5.4 * deltaTime velocity.y -= 5.4 * deltaTime

View File

@ -3,7 +3,7 @@ import simd
import JolkEngine import JolkEngine
class JolkCube: Actor struct JolkCube: Actor
{ {
private var _pos: Vec3f private var _pos: Vec3f
private var theta: Float = 0.0 private var theta: Float = 0.0
@ -15,7 +15,7 @@ class JolkCube: Actor
var position: Vec3f { _pos } var position: Vec3f { _pos }
func update(deltaTime: Float, world: Collision) mutating func update(deltaTime: Float, world: Collision)
{ {
theta += 15 * deltaTime theta += 15 * deltaTime
_pos.y = 1 + sin(theta * 0.25) * 0.25 _pos.y = 1 + sin(theta * 0.25) * 0.25

View File

@ -0,0 +1,46 @@
# Blender 3.6.2 MTL File: 'CaveScene.blend'
# www.blender.org
newmtl Grass
Ns 360.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd grass.png
newmtl GravelFloor
Ns 360.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd PackedDirt.JPG
newmtl Material
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd SkyBox-Clouds-Med-MidMorning.PNG
newmtl RockWall
Ns 360.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd 1024x1024_seamless_texture_10.jpg

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,16 @@ map_Kd cobblestone.png
map_Ks cobblestone_specular.png map_Ks cobblestone_specular.png
map_Bump -bm 1.000000 cobblestone_normal.png map_Bump -bm 1.000000 cobblestone_normal.png
newmtl Grass
Ns 30.581900
Ka 1.000000 1.000000 1.000000
Ks 0.007389 0.007389 0.007389
Ke 0.000000 0.000000 0.000000
Ni 11.849999
d 1.000000
illum 2
map_Kd grass.png
newmtl Material newmtl Material
Ns 167.965591 Ns 167.965591
Ka 1.000000 1.000000 1.000000 Ka 1.000000 1.000000 1.000000

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -0,0 +1,58 @@
import Foundation
import simd
import JolkEngine
struct CaveScene: Scene
{
private var colin = Colin()
private var world = Collision()
private var worldModel = RenderMesh<VertexPositionNormalColourTexcoord>.empty
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))
}
mutating func loadContent(content: inout JolkEngine.ContentManager) throws
{
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)
if let collision = obj.objects["Collision3D"]
{
world.build(obj: obj, collision: collision)
}
}
mutating func update(deltaTime: Float)
{
colin.update(deltaTime: deltaTime, world: world)
if Input.instance.keyboard.keyPressed(.c) { drawEdges = !drawEdges }
}
func draw(render: inout Renderer, deltaTime: Float, aspectRatio aspect: Float)
{
render.setProjection(matrix: .perspective(
fovY: Float.rad(fromDeg: colin.fov),
aspect: aspect, zNear: 0.1, zFar: 4000.0))
render.setView(matrix: colin.transform)
let env = Environment()
let drawRanges =
{ [render](range: any Sequence<Int>) in
for i in range
{
let subMesh = worldModel.subMeshes[i]
render.setMaterial(worldModel.materials[subMesh.material])
render.draw(mesh: worldModel, subMesh: subMesh, environment: env)
}
}
if colin.position.x < 14 { drawRanges([ 0, 1, 2, 6 ]) }
else { drawRanges(3...5) }
if drawEdges { world.draw(render, position: colin.position) }
}
}

View File

@ -0,0 +1,10 @@
import JolkEngine
protocol Scene
{
mutating func setup(render: inout Renderer)
mutating func loadContent(content: inout ContentManager) throws
mutating func update(deltaTime: Float)
func draw(render: inout Renderer, deltaTime: Float, aspectRatio aspect: Float)
}

View File

@ -0,0 +1,194 @@
import JolkEngine
import Foundation
import simd
struct Scene1: Scene
{
private var env = Environment()
private var worldMesh = RenderMesh<VertexPositionNormalColourTexcoord>.empty
private var cube = RenderMesh<VertexPositionNormalTexcoord>.empty
private var suzanne = RenderMesh<VertexPositionNormalTexcoord>.empty
private var toybox = RenderMesh<VertexPositionNormalTexcoord>.empty
private var jolkTex = Texture2D.empty, suzanneDiffuse = Texture2D.empty
private var drawEdges = false
private var world = Collision()
private var colin = Colin()
private var jolkCube = JolkCube(position: .init(0, 1, -4))
private var lightTheta: Float = 0
mutating func setup(render: inout any JolkEngine.Renderer)
{
render.clearColour = XnaColour.CornflowerBlue
env.setFog(
colour: XnaColour.CornflowerBlue,
//mode: .depth, falloff: .range(type: .linear, start: 4, end: 38))
//mode: .depth, falloff: .range(type: .linear, start: 1.25, end: 24.5))
//mode: .distance, falloff: .factor(type: .exp2, density: 0.1))
mode: .depth, falloff: .factor(type: .exp2, density: 0.0222))
env.addAmbientLight(colour: XnaColour.DarkSlateGray.lighten(by: -0.666))
env.addDirectionalLight(
direction: -Vec3f(1, -1, -1).normalised,
colour: XnaColour.BlanchedAlmond
.lighten(by: -0.02)
.mix(with: XnaColour.LightSlateGray, 0.5)
.mix(with: XnaColour.White, 0.125))
env.addPointLight(
position: Vec3f(3, 0.33, -5),
colour: XnaColour.Green.mix(with: XnaColour.Gray, 0.5),
intensity: 2)
env.addPointLight(
position: Vec3f(5.5, 0.33, -6),
colour: XnaColour.Red.mix(with: XnaColour.Gray, 0.5),
intensity: 2)
env.addPointLight(
position: Vec3f(4, 0.33, -7),
colour: XnaColour.Blue.mix(with: XnaColour.Gray, 0.5),
intensity: 2)
}
mutating func loadContent(content: inout JolkEngine.ContentManager) throws
{
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: .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: .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)),
.init(position: Vec3f(-1, 1, 1), normal: .left, texCoord: Vec2f(1, 1)),
.init(position: Vec3f(-1, -1, -1), normal: .down, texCoord: Vec2f(0, 0)),
.init(position: Vec3f( 1, -1, -1), normal: .down, texCoord: Vec2f(1, 0)),
.init(position: Vec3f(-1, -1, 1), normal: .down, texCoord: Vec2f(0, 1)),
.init(position: Vec3f( 1, -1, 1), normal: .down, texCoord: Vec2f(1, 1)),
.init(position: Vec3f(-1, 1, 1), normal: .up, texCoord: Vec2f(0, 0)),
.init(position: Vec3f( 1, 1, 1), normal: .up, texCoord: Vec2f(1, 0)),
.init(position: Vec3f(-1, 1, -1), normal: .up, texCoord: Vec2f(0, 1)),
.init(position: Vec3f( 1, 1, -1), normal: .up, texCoord: Vec2f(1, 1))
],
indices: [
0, 1, 2, 2, 1, 3,
4, 5, 6, 6, 5, 7,
8, 9, 10, 10, 9, 11,
12, 13, 14, 14, 13, 15,
16, 17, 18, 18, 17, 19,
20, 21, 22, 22, 21, 23 ]))
suzanne = try content.load("suzanne.g3db")
toybox = try content.load("toybox.g3db")
let linearMipped = Texture2DParameters(
minFilter: .linear, magFilter: .linear,
wrapMode: .repeating,
mipMode: .linear)
let linearClamp = Texture2DParameters(
minFilter: .linear, magFilter: .linear,
wrapMode: .clampEdge)
jolkTex = try content.load("jolkmeup.jpg", params: linearClamp)
suzanneDiffuse = try content.load("suzanne_diffuse.png", params: linearMipped)
}
private mutating func loadWorld(_ content: inout ContentManager) throws
{
let name = "World.obj"
let obj = try ObjReader.read(url: try content.getResource(name))
let mesh: Mesh = try ObjLoader.read(model: obj, content: &content)
worldMesh = try content.create(mesh: mesh)
if let collision = obj.objects["Collision3D"]
{
world.build(obj: obj, collision: collision)
}
}
mutating func update(deltaTime: Float)
{
colin.update(deltaTime: deltaTime, world: world)
jolkCube.update(deltaTime: deltaTime, world: world)
// Update point lights
lightTheta += deltaTime
var theta = Vec3f(repeating: lightTheta) * Vec3f(0.12, 0.011, 0.056) * 6
var i = env.lights.startIndex
while i != env.lights.endIndex
{
if case .point(let colour, _, _) = env.lights[i]
{
env.lights[i] = .point(
colour: colour,
position: Vec3f(
4 + 6 * cos(theta[0]),
0.33 + 0.33 * sin(theta[1]),
-6 + 3 * sin(theta[0] * 2)),
intensity: 3.1 + 1.53 * cos(theta[2]))
let spacing = 0.5 * Float.pi * (2 / 3.0)
theta += spacing * Vec3f(1, 0.98, 0.5566)
}
i = env.lights.index(after: i)
}
if Input.instance.keyboard.keyPressed(.c) { drawEdges = !drawEdges }
}
func draw(render: inout Renderer, deltaTime: Float, aspectRatio aspect: Float)
{
// Setup camera
render.setProjection(matrix:
.perspective(fovY: Float.rad(fromDeg: colin.fov), aspect: aspect, zNear: 0.1, zFar: 100))
render.setView(matrix: colin.transform)
// 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())
}
render.draw(mesh: worldMesh, subMesh: s, environment: env)
}
// Draw jolked up shit
render.setMaterial(Material(
specular: XnaColour.Gray,
gloss: 20.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.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)
}
}
}

Binary file not shown.