The Skung Cave commit
This commit is contained in:
@ -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))
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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?
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
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
|
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
|
||||||
|
@ -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
|
||||||
|
@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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])
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
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_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 |
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