The Skung Cave commit
This commit is contained in:
@ -23,23 +23,21 @@ public struct ApplicationConfiguration
|
||||
let vSync: VSyncMode
|
||||
let windowWidth: Int32
|
||||
let windowHeight: Int32
|
||||
let windowTitle: String
|
||||
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.vSync = vSync
|
||||
self.windowWidth = windowWidth
|
||||
self.windowHeight = windowHeight
|
||||
self.windowTitle = title
|
||||
self.bundle = bundle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//FIXME: Wrap these
|
||||
public var sdlPad: OpaquePointer? = nil
|
||||
var joyId: Int32 = -1
|
||||
|
||||
public class Application
|
||||
{
|
||||
private let implementation: ApplicationImplementation
|
||||
@ -52,14 +50,6 @@ public class Application
|
||||
typealias ResizeEvent = (width: Int32, height: Int32)
|
||||
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)
|
||||
{
|
||||
self.implementation = application
|
||||
@ -77,7 +67,7 @@ public class Application
|
||||
let winWidth: Int32 = configuration.windowWidth, winHeight: Int32 = configuration.windowHeight
|
||||
var winFlags = SDL_WINDOW_RESIZABLE.rawValue | SDL_WINDOW_ALLOW_HIGHDPI.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()))") }
|
||||
|
||||
do
|
||||
@ -91,15 +81,10 @@ public class Application
|
||||
}
|
||||
catch { fatalError("piss") }
|
||||
|
||||
for i in 0...SDL_NumJoysticks()
|
||||
for idx in 0...SDL_NumJoysticks()
|
||||
{
|
||||
if SDL_IsGameController(i) != SDL_TRUE { continue }
|
||||
if let open = openController(index: i)
|
||||
{
|
||||
sdlPad = open.pad
|
||||
joyId = open.joy
|
||||
break
|
||||
}
|
||||
if SDL_IsGameController(idx) != SDL_TRUE { continue }
|
||||
Input.instance.padConnectedEvent(index: idx)
|
||||
}
|
||||
|
||||
do
|
||||
@ -131,13 +116,6 @@ public class Application
|
||||
switch SDL_EventType(event.type)
|
||||
{
|
||||
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:
|
||||
switch SDL_WindowEventID(UInt32(event.window.event))
|
||||
{
|
||||
@ -145,28 +123,27 @@ public class Application
|
||||
resize = displaySize
|
||||
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:
|
||||
if sdlPad == nil && SDL_IsGameController(event.cdevice.which) == SDL_TRUE
|
||||
{
|
||||
if let open = openController(index: event.cdevice.which)
|
||||
{
|
||||
sdlPad = open.pad
|
||||
joyId = open.joy
|
||||
}
|
||||
}
|
||||
if SDL_IsGameController(event.cdevice.which) != SDL_TRUE { break }
|
||||
Input.instance.padConnectedEvent(index: event.cdevice.which)
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
if event.cdevice.which == joyId
|
||||
{
|
||||
SDL_GameControllerClose(sdlPad)
|
||||
sdlPad = nil
|
||||
joyId = -1
|
||||
}
|
||||
Input.instance.padRemovedEvent(index: event.cdevice.which)
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
Input.instance.padButtonPressEvent(id: event.cbutton.which, btn: event.cbutton.button)
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
let time = SDL_GetPerformanceCounter();
|
||||
let deltaTime = Float(Double(time - prevTime) * timeMul)
|
||||
let deltaTime = Float(Double(time &- prevTime) * timeMul)
|
||||
prevTime = time
|
||||
|
||||
implementation.update(deltaTime: min(deltaTime, 1.0 / 15.0))
|
||||
|
@ -35,7 +35,7 @@ extension ContentManager
|
||||
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)
|
||||
resources.append(rendMesh)
|
||||
@ -72,13 +72,18 @@ extension ContentManager
|
||||
{
|
||||
let resourceUrl = try getResource(path)
|
||||
|
||||
let loader = loaders[resourceUrl.pathExtension]
|
||||
let loader = loaders[resourceUrl.pathExtension.lowercased()]
|
||||
guard let resource = loader?.load(url: resourceUrl)
|
||||
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
|
||||
}
|
||||
if T.self == Image.self
|
||||
@ -100,13 +105,18 @@ extension ContentManager
|
||||
{
|
||||
let resourceUrl = try getResource(path)
|
||||
|
||||
let loader = loaders[resourceUrl.pathExtension]
|
||||
let loader = loaders[resourceUrl.pathExtension.lowercased()]
|
||||
guard let resource = loader?.load(url: resourceUrl)
|
||||
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
|
||||
}
|
||||
if T.self == Image.self
|
||||
@ -131,9 +141,15 @@ extension ContentManager
|
||||
guard let resource = loader?.load(url: resourceUrl)
|
||||
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)
|
||||
return renderResource as! T
|
||||
}
|
||||
@ -144,9 +160,13 @@ extension ContentManager
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -6,4 +6,5 @@ public protocol LoaderProtocol
|
||||
associatedtype T: Resource
|
||||
|
||||
func load(url: URL) -> T?
|
||||
func load(url: URL, content: inout ContentManager) -> T?
|
||||
}
|
||||
|
@ -1,16 +1,61 @@
|
||||
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 struct Vertex: Equatable
|
||||
public struct SubMesh
|
||||
{
|
||||
public let start, length: Int
|
||||
public let material: Int //hack
|
||||
|
||||
public init(start: Int, length: Int, material: Int = -1)
|
||||
{
|
||||
self.start = start
|
||||
self.length = length
|
||||
self.material = material
|
||||
}
|
||||
}
|
||||
|
||||
public let vertices: [T]
|
||||
public let indices: [Index]
|
||||
public let subMeshes: OrderedDictionary<String, SubMesh>
|
||||
public let materials: [Material] // hack
|
||||
}
|
||||
|
||||
public extension Mesh
|
||||
{
|
||||
static var empty: Self { Self(vertices: .init(), indices: .init(), subMeshes: .init(), materials: .init()) }
|
||||
|
||||
init(vertices: [T], indices: [Index])
|
||||
{
|
||||
self.init(
|
||||
vertices: vertices,
|
||||
indices: indices,
|
||||
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
|
||||
@ -24,31 +69,18 @@ public struct Mesh: Resource
|
||||
}
|
||||
}
|
||||
|
||||
public struct SubMesh
|
||||
public struct VertexPositionNormalColourTexcoord: Vertex
|
||||
{
|
||||
public let start, length: Int
|
||||
public let position: Vec3f
|
||||
public let normal: Vec3f
|
||||
public let colour: Colour
|
||||
public let texCoord: Vec2f
|
||||
|
||||
public init(start: Int, length: Int)
|
||||
public init(position: Vec3f, colour: Colour, normal: Vec3f, texCoord: Vec2f)
|
||||
{
|
||||
self.start = start
|
||||
self.length = length
|
||||
}
|
||||
}
|
||||
|
||||
public let vertices: [Vertex]
|
||||
public let indices: [Index]
|
||||
public let subMeshes: OrderedDictionary<String, SubMesh>
|
||||
}
|
||||
|
||||
public extension Mesh
|
||||
{
|
||||
static let empty = Self(vertices: .init(), indices: .init(), subMeshes: .init())
|
||||
|
||||
init(vertices: [Vertex], indices: [Index])
|
||||
{
|
||||
self.init(
|
||||
vertices: vertices,
|
||||
indices: indices,
|
||||
subMeshes: .init())
|
||||
self.position = position
|
||||
self.colour = colour
|
||||
self.normal = normal
|
||||
self.texCoord = texCoord
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
import Foundation
|
||||
import OrderedCollections
|
||||
|
||||
|
||||
public struct ObjModel
|
||||
{
|
||||
public var positions = [Vec3f]()
|
||||
public var colours = [Vec3f]()
|
||||
public var normals = [Vec3f]()
|
||||
public var texCoords = [Vec2f]()
|
||||
public var objects = Dictionary<String, Object>()
|
||||
|
||||
public struct Object { public var faces = [Face]() }
|
||||
public var objects = OrderedDictionary<String, Object>()
|
||||
public var materials = Dictionary<String, ObjMaterial>()
|
||||
|
||||
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 enum Face
|
||||
{
|
||||
|
157
Sources/JolkEngine/Input/GamePad.swift
Normal file
157
Sources/JolkEngine/Input/GamePad.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
62
Sources/JolkEngine/Input/Input.swift
Normal file
62
Sources/JolkEngine/Input/Input.swift
Normal 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() }
|
||||
}
|
||||
}
|
137
Sources/JolkEngine/Input/Keyboard.swift
Normal file
137
Sources/JolkEngine/Input/Keyboard.swift
Normal 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))
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import OrderedCollections
|
||||
|
||||
class G3DbLoader: LoaderProtocol
|
||||
{
|
||||
typealias T = Mesh
|
||||
typealias T = Mesh<VertexPositionNormalTexcoord>
|
||||
|
||||
func load(url: URL) -> T?
|
||||
{
|
||||
@ -12,6 +12,8 @@ class G3DbLoader: LoaderProtocol
|
||||
else { return Optional.none }
|
||||
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)
|
||||
}
|
||||
|
||||
mutating func read() throws -> Mesh
|
||||
mutating func read() throws -> Mesh<VertexPositionNormalTexcoord>
|
||||
{
|
||||
let model = try readModel()
|
||||
|
||||
let mesh = model.meshes[0]
|
||||
|
||||
var vertices = [Mesh.Vertex]()
|
||||
var vertices = [VertexPositionNormalTexcoord]()
|
||||
var indices = [UInt16]()
|
||||
var subMeshes = OrderedDictionary<String, Mesh.SubMesh>()
|
||||
var subMeshes = OrderedDictionary<String, Mesh<VertexPositionNormalTexcoord>.SubMesh>()
|
||||
|
||||
let attributeSize =
|
||||
{ (attrib: G3DAttribute) in
|
||||
|
@ -16,6 +16,8 @@ struct NSImageLoader: LoaderProtocol
|
||||
return image
|
||||
}
|
||||
|
||||
func load(url: URL, content: inout ContentManager) -> T? { return load(url: url) }
|
||||
|
||||
static func loadImage(url: URL) throws -> Image
|
||||
{
|
||||
try autoreleasepool
|
||||
|
@ -2,10 +2,9 @@ import Foundation
|
||||
import OrderedCollections
|
||||
|
||||
|
||||
/*
|
||||
extension ObjMaterial
|
||||
{
|
||||
func convert() -> Material
|
||||
func convert(content: UnsafeMutablePointer<ContentManager>? = nil) -> Material
|
||||
{
|
||||
var m = Material()
|
||||
m.diffuse = self.diffuse.setAlpha(self.alpha)
|
||||
@ -14,37 +13,61 @@ extension ObjMaterial
|
||||
m.specular = self.specular
|
||||
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
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public struct ObjLoader: LoaderProtocol
|
||||
{
|
||||
public typealias T = Mesh
|
||||
typealias V = VertexPositionNormalColourTexcoord
|
||||
public typealias T = Mesh<VertexPositionNormalColourTexcoord>
|
||||
|
||||
public init() {}
|
||||
|
||||
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 vertices = [Mesh.Vertex]()
|
||||
var subMeshes = OrderedDictionary<String, T.SubMesh>()
|
||||
var materials = OrderedDictionary<String, Material>()
|
||||
var vertices = [V]()
|
||||
var indices = [UInt16]()
|
||||
|
||||
for (key, mtl) in model.materials
|
||||
{
|
||||
materials[key] = mtl.convert(content: content)
|
||||
}
|
||||
|
||||
let readIndex =
|
||||
{ (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],
|
||||
colour: colour,
|
||||
normal: model.normals[v.n],
|
||||
texCoord: model.texCoords[v.t])
|
||||
if let index = vertices.firstIndex(of: vertex)
|
||||
@ -61,10 +84,13 @@ public struct ObjLoader: LoaderProtocol
|
||||
}
|
||||
}
|
||||
|
||||
for mesh in model.objects
|
||||
for object in model.objects.filter({ $0.key != "Collision3D" })
|
||||
{
|
||||
var id = 0
|
||||
for mesh: ObjModel.Mesh in object.value.meshes
|
||||
{
|
||||
let start = indices.count
|
||||
for face: ObjModel.Face in mesh.value.faces
|
||||
for face: ObjModel.Face in mesh.faces
|
||||
{
|
||||
switch face
|
||||
{
|
||||
@ -83,10 +109,14 @@ public struct ObjLoader: LoaderProtocol
|
||||
let length = indices.count - start
|
||||
if length > 0
|
||||
{
|
||||
subMeshes[mesh.key] = .init(start: start, length: length)
|
||||
subMeshes["\(object.key)_\(id)"] = .init(
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
@ -8,25 +8,46 @@ public struct ObjReader
|
||||
var file = try ObjDocumentReader(filePath: url)
|
||||
|
||||
var model = ObjModel()
|
||||
var materials = Dictionary<String, ObjMaterial>()
|
||||
var name: String? = nil
|
||||
var object = ObjModel.Object()
|
||||
var mesh = ObjModel.Mesh()
|
||||
|
||||
file.string(tag: "mtllib") { s in
|
||||
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
|
||||
if !object.faces.isEmpty
|
||||
if !mesh.faces.isEmpty { object.meshes.append(mesh) }
|
||||
if !object.meshes.isEmpty
|
||||
{
|
||||
model.objects[name!] = object
|
||||
object = .init()
|
||||
mesh = .init()
|
||||
}
|
||||
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: "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
|
||||
let readIndex =
|
||||
{ (raw: Substring) in
|
||||
@ -40,14 +61,14 @@ public struct ObjReader
|
||||
if raw.count == 3
|
||||
{
|
||||
for raw in raw { _ = try readIndex(raw) }
|
||||
object.faces.append(.triangle(
|
||||
mesh.faces.append(.triangle(
|
||||
v1: try readIndex(raw[raw.startIndex]),
|
||||
v2: try readIndex(raw[raw.startIndex + 1]),
|
||||
v3: try readIndex(raw[raw.startIndex + 2])))
|
||||
}
|
||||
else if raw.count == 4
|
||||
{
|
||||
object.faces.append(.quad(
|
||||
mesh.faces.append(.quad(
|
||||
v1: try readIndex(raw[raw.startIndex]),
|
||||
v2: try readIndex(raw[raw.startIndex + 1]),
|
||||
v3: try readIndex(raw[raw.startIndex + 2]),
|
||||
@ -55,17 +76,25 @@ public struct ObjReader
|
||||
}
|
||||
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 }
|
||||
}
|
||||
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()
|
||||
|
||||
if !object.faces.isEmpty
|
||||
{
|
||||
model.objects[name!] = object
|
||||
}
|
||||
if !mesh.faces.isEmpty { object.meshes.append(mesh) }
|
||||
if !object.meshes.isEmpty { model.objects[name!] = object }
|
||||
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ public extension SIMD2 where Scalar: FloatingPoint
|
||||
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))
|
||||
|
@ -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
|
||||
{
|
||||
guard SDL_GL_SetSwapInterval(mode.sdlInterval) == 0
|
||||
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)
|
||||
buffers.withUnsafeMutableBufferPointer
|
||||
@ -94,35 +100,36 @@ class OpenGL: Renderer
|
||||
state.elementArrayBuffer = buffers[1]
|
||||
|
||||
glBufferData(GLenum(GL_ARRAY_BUFFER),
|
||||
MemoryLayout<Mesh.Vertex>.stride * mesh.vertices.count,
|
||||
MemoryLayout<V>.stride * mesh.vertices.count,
|
||||
mesh.vertices, GLenum(GL_STATIC_DRAW))
|
||||
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))
|
||||
|
||||
state.elementArrayBuffer = OpenGLState.defaultBuffer
|
||||
state.arrayBuffer = OpenGLState.defaultBuffer
|
||||
|
||||
var subMeshes = [Mesh.SubMesh]()
|
||||
var subMeshes = [Mesh<V>.SubMesh]()
|
||||
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
|
||||
{
|
||||
for subMesh in mesh.subMeshes
|
||||
{
|
||||
if ["Collision", "Collision3D"].contains(subMesh.key) { continue }
|
||||
subMeshes.append(Mesh.SubMesh(
|
||||
subMeshes.append(Mesh<V>.SubMesh(
|
||||
start: subMesh.value.start,
|
||||
length: subMesh.value.length))
|
||||
length: subMesh.value.length,
|
||||
material: subMesh.value.material))
|
||||
}
|
||||
}
|
||||
|
||||
return RenderMesh(
|
||||
vbo: Int(buffers[0]),
|
||||
ibo: Int(buffers[1]),
|
||||
subMeshes: subMeshes)
|
||||
subMeshes: subMeshes,
|
||||
materials: mesh.materials)
|
||||
}
|
||||
|
||||
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)
|
||||
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(
|
||||
GLenum(GL_TRIANGLES),
|
||||
GLsizei(subMesh.length),
|
||||
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.elementArrayBuffer = UInt32(mesh.iboHnd)
|
||||
|
||||
let stride = GLsizei(MemoryLayout<Mesh.Vertex>.stride)
|
||||
glVertexPointer(3, GLenum(GL_FLOAT), stride,
|
||||
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
|
||||
let stride = GLsizei(MemoryLayout<V>.stride)
|
||||
if V.self == VertexPositionNormalTexcoord.self
|
||||
{
|
||||
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.arrayBuffer = OpenGLState.defaultBuffer
|
||||
|
||||
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 }
|
||||
|
||||
@ -255,7 +284,7 @@ class OpenGL: Renderer
|
||||
glPopMatrix()
|
||||
}
|
||||
|
||||
func draw(mesh: RenderMesh, environment env: Environment)
|
||||
func draw<V: Vertex>(mesh: RenderMesh<V>, environment env: Environment)
|
||||
{
|
||||
if (mesh.subMeshes.isEmpty) { return }
|
||||
|
||||
@ -263,6 +292,14 @@ class OpenGL: Renderer
|
||||
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)
|
||||
{
|
||||
withUnsafePointer(to: matrix.columns)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,15 +11,16 @@ public protocol Renderer
|
||||
func resize(width: Int32, height: Int32)
|
||||
|
||||
var clearColour: Colour { get set }
|
||||
var wireframe: Bool { get set }
|
||||
|
||||
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,
|
||||
filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
|
||||
|
||||
func deleteMesh(_ mesh: RenderMesh)
|
||||
func deleteMesh<V: Vertex>(_ mesh: RenderMesh<V>)
|
||||
func deleteTexture(_ texture: RenderTexture2D)
|
||||
|
||||
func setProjection(matrix: Mat4f)
|
||||
@ -27,8 +28,9 @@ public protocol Renderer
|
||||
|
||||
func setMaterial(_ mat: Material)
|
||||
|
||||
func draw(mesh: RenderMesh, model: Mat4f, environment: Environment)
|
||||
func draw(mesh: RenderMesh, environment: Environment)
|
||||
func draw<V: Vertex>(mesh: RenderMesh<V>, model: Mat4f, 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])
|
||||
}
|
||||
@ -76,9 +78,9 @@ public protocol RendererResource
|
||||
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() }
|
||||
|
||||
@ -86,7 +88,8 @@ public struct RenderMesh: RendererResource
|
||||
|
||||
private let _valid: Bool;
|
||||
let vboHnd: Int, iboHnd: Int
|
||||
let subMeshes: [Mesh.SubMesh]
|
||||
public let subMeshes: [Mesh<V>.SubMesh]
|
||||
public let materials: [Material] // HACK !!!!
|
||||
|
||||
private init()
|
||||
{
|
||||
@ -94,14 +97,16 @@ public struct RenderMesh: RendererResource
|
||||
self.vboHnd = 0
|
||||
self.iboHnd = 0
|
||||
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.vboHnd = vbo
|
||||
self.iboHnd = ibo
|
||||
self.subMeshes = subMeshes
|
||||
self.materials = materials
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
Sources/Test/CaveScene.blend
Normal file
BIN
Sources/Test/CaveScene.blend
Normal file
Binary file not shown.
@ -14,6 +14,7 @@ import JolkEngine
|
||||
vSync: .on,
|
||||
windowWidth: 1280,
|
||||
windowHeight: 720,
|
||||
title: "Caves of Swift",
|
||||
bundle: Bundle.module)
|
||||
).run()
|
||||
}
|
||||
@ -21,105 +22,20 @@ import JolkEngine
|
||||
|
||||
class CavesOfJolk: ApplicationImplementation
|
||||
{
|
||||
private lazy var scene = Scene1()
|
||||
//private lazy var scene = CaveScene()
|
||||
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 frameTimer: Float = 1
|
||||
|
||||
func create(render: inout 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)
|
||||
scene.setup(render: &render)
|
||||
}
|
||||
|
||||
func loadContent(content: inout 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)
|
||||
|
||||
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)
|
||||
try scene.loadContent(content: &content)
|
||||
}
|
||||
|
||||
func resize(width: Int, height: Int)
|
||||
@ -130,10 +46,7 @@ class CavesOfJolk: ApplicationImplementation
|
||||
|
||||
func update(deltaTime: Float)
|
||||
{
|
||||
colin.update(deltaTime: deltaTime, world: world)
|
||||
jolkCube.update(deltaTime: deltaTime, world: world)
|
||||
|
||||
lightTheta += deltaTime
|
||||
scene.update(deltaTime: deltaTime)
|
||||
|
||||
frameCount += 1
|
||||
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)
|
||||
{
|
||||
// Setup camera
|
||||
render.setProjection(matrix:
|
||||
.perspective(fovY: Float.rad(fromDeg: colin.fov), aspect: aspect, zNear: 0.1, zFar: 100))
|
||||
render.setView(matrix: colin.transform)
|
||||
if Input.instance.keyboard.keyPressed(.w, repeat: true) { render.wireframe = !render.wireframe }
|
||||
|
||||
// Update point lights
|
||||
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)
|
||||
}
|
||||
scene.draw(render: &render, deltaTime: deltaTime, aspectRatio: aspect)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
import JolkEngine
|
||||
|
||||
|
||||
class Collision
|
||||
struct Collision
|
||||
{
|
||||
struct Edge { let p: Vec2f, n: Vec2f, w: Float }
|
||||
|
||||
@ -66,16 +66,16 @@ class Collision
|
||||
|
||||
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
|
||||
{
|
||||
case .triangle(let v1, let v2, let v3):
|
||||
let t = Triangle(obj.positions[v1.p], obj.positions[v2.p], obj.positions[v3.p])
|
||||
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))
|
||||
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])
|
||||
|
@ -2,7 +2,7 @@ import JolkEngine
|
||||
|
||||
protocol Actor
|
||||
{
|
||||
func update(deltaTime: Float, world: Collision)
|
||||
mutating func update(deltaTime: Float, world: Collision)
|
||||
|
||||
var position: Vec3f { get }
|
||||
var transform: Mat4f { get }
|
||||
|
@ -4,9 +4,10 @@ import OpenGL.GL
|
||||
import JolkEngine
|
||||
|
||||
|
||||
class Colin: Actor
|
||||
struct Colin: Actor
|
||||
{
|
||||
var position: Vec3f { _pos }
|
||||
mutating func setPosition(_ new: Vec3f) { _pos = new }
|
||||
|
||||
var transform: Mat4f
|
||||
{
|
||||
@ -16,10 +17,10 @@ class Colin: Actor
|
||||
}
|
||||
|
||||
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 }
|
||||
|
||||
private var time: Float = 0.0
|
||||
//private var jumpVel: Float = 0.0
|
||||
private var velocity: 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 } }
|
||||
@ -27,13 +28,11 @@ class Colin: Actor
|
||||
private var ofsAngle: Vec2f = .zero
|
||||
private var _fov: Float = 60.0
|
||||
|
||||
private var nutted = false
|
||||
private var colinMode = false
|
||||
private var backPressed = false
|
||||
|
||||
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
|
||||
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 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
|
||||
_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
|
||||
|
||||
let axisRescale = 1.0 / Float(INT16_MAX)
|
||||
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 pad = GamePad.current?.state
|
||||
|
||||
let stick = Vec2f(
|
||||
getAxis(SDL_CONTROLLER_AXIS_LEFTX),
|
||||
getAxis(SDL_CONTROLLER_AXIS_LEFTY))
|
||||
.cardinalDeadzone(min: 0.1, max: 1)
|
||||
let stick = pad?.leftStick.cardinalDeadzone(min: 0.1, max: 1)
|
||||
|
||||
let turnSpeed = Float.pi * 2 * 0.1
|
||||
if Input.instance.keyDown(.left)
|
||||
let turnSpeed = Float.pi * 2 * 0.2
|
||||
if Input.instance.keyboard.keyDown(.left)
|
||||
{
|
||||
_angle -= turnSpeed * deltaTime
|
||||
}
|
||||
if Input.instance.keyDown(.right)
|
||||
if Input.instance.keyboard.keyDown(.right)
|
||||
{
|
||||
_angle += turnSpeed * deltaTime
|
||||
}
|
||||
if stick != .zero
|
||||
if let stick = stick
|
||||
{
|
||||
_angle += stick.x * 2.0 * turnSpeed * deltaTime
|
||||
}
|
||||
|
||||
var moveVec: Vec2f = .zero
|
||||
|
||||
let speed: Float = 2
|
||||
let speed: Float = 4
|
||||
let forward = Vec2f(sin(self._angle), -cos(self._angle))
|
||||
if Input.instance.keyDown(.up)
|
||||
if Input.instance.keyboard.keyDown(.up)
|
||||
{
|
||||
moveVec += forward * speed
|
||||
}
|
||||
if Input.instance.keyDown(.down)
|
||||
if Input.instance.keyboard.keyDown(.down)
|
||||
{
|
||||
moveVec -= forward * speed
|
||||
}
|
||||
if stick != .zero
|
||||
if let stick = stick
|
||||
{
|
||||
moveVec -= forward * stick.y * speed
|
||||
}
|
||||
|
||||
if Input.instance.keyDown(.c)
|
||||
if Input.instance.keyboard.keyDown(.c)
|
||||
{
|
||||
colinMode = !colinMode
|
||||
}
|
||||
|
||||
let lookCone = Float.pi / 2.0
|
||||
let dst = Vec2f(
|
||||
getAxis(SDL_CONTROLLER_AXIS_RIGHTX),
|
||||
getAxis(SDL_CONTROLLER_AXIS_RIGHTY))
|
||||
.radialDeadzone(min: 0.1, max: 1) * lookCone
|
||||
ofsAngle = ofsAngle.lerp(dst, 16 * deltaTime)
|
||||
let dst = pad?.rightStick.radialDeadzone(min: 0.1, max: 1) ?? .zero
|
||||
ofsAngle = ofsAngle.lerp(dst * lookCone, 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)
|
||||
|
||||
if let pad = pad
|
||||
{
|
||||
let right = Vec2f(cos(self._angle), sin(self._angle))
|
||||
if getButton(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
|
||||
{
|
||||
moveVec -= right * speed
|
||||
}
|
||||
if getButton(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
|
||||
{
|
||||
moveVec += right * speed
|
||||
if pad.down(.leftBumper) { moveVec -= right * speed }
|
||||
if pad.down(.rightBumper) { moveVec += right * speed }
|
||||
if pad.pressed(.select) { colinMode = !colinMode }
|
||||
}
|
||||
|
||||
let back = getButton(SDL_CONTROLLER_BUTTON_BACK)
|
||||
if !backPressed && back { colinMode = !colinMode }
|
||||
backPressed = back
|
||||
|
||||
if getButton(SDL_CONTROLLER_BUTTON_A) || Input.instance.keyDown(.n)
|
||||
if pad?.pressed(.south) ?? false || Input.instance.keyboard.keyDown(.n)
|
||||
{
|
||||
// play nut.wav
|
||||
nutted = true
|
||||
}
|
||||
else { nutted = false }
|
||||
|
||||
//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
|
||||
//if _pos.y > 0.0 { jumpVel -= 5.4 * deltaTime }
|
||||
velocity.y -= 5.4 * deltaTime
|
||||
|
@ -3,7 +3,7 @@ import simd
|
||||
import JolkEngine
|
||||
|
||||
|
||||
class JolkCube: Actor
|
||||
struct JolkCube: Actor
|
||||
{
|
||||
private var _pos: Vec3f
|
||||
private var theta: Float = 0.0
|
||||
@ -15,7 +15,7 @@ class JolkCube: Actor
|
||||
|
||||
var position: Vec3f { _pos }
|
||||
|
||||
func update(deltaTime: Float, world: Collision)
|
||||
mutating func update(deltaTime: Float, world: Collision)
|
||||
{
|
||||
theta += 15 * deltaTime
|
||||
_pos.y = 1 + sin(theta * 0.25) * 0.25
|
||||
|
46
Sources/Test/Resources/Models/CaveScene.mtl
Normal file
46
Sources/Test/Resources/Models/CaveScene.mtl
Normal 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
|
5215
Sources/Test/Resources/Models/CaveScene.obj
Normal file
5215
Sources/Test/Resources/Models/CaveScene.obj
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,16 @@ map_Kd cobblestone.png
|
||||
map_Ks cobblestone_specular.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
|
||||
Ns 167.965591
|
||||
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 |
BIN
Sources/Test/Resources/Textures/PackedDirt.JPG
Normal file
BIN
Sources/Test/Resources/Textures/PackedDirt.JPG
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
BIN
Sources/Test/Resources/Textures/PackedDirt_N.jpg
Normal file
BIN
Sources/Test/Resources/Textures/PackedDirt_N.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
BIN
Sources/Test/Resources/Textures/SkyBox-Clouds-Med-MidMorning.PNG
Normal file
BIN
Sources/Test/Resources/Textures/SkyBox-Clouds-Med-MidMorning.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 484 KiB |
BIN
Sources/Test/Resources/Textures/grass.png
Normal file
BIN
Sources/Test/Resources/Textures/grass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 MiB |
58
Sources/Test/Scenes/CaveScene.swift
Normal file
58
Sources/Test/Scenes/CaveScene.swift
Normal 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) }
|
||||
}
|
||||
}
|
10
Sources/Test/Scenes/Scene.swift
Normal file
10
Sources/Test/Scenes/Scene.swift
Normal 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)
|
||||
}
|
194
Sources/Test/Scenes/Scene1.swift
Normal file
194
Sources/Test/Scenes/Scene1.swift
Normal 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.
Reference in New Issue
Block a user