Files
CavesOfSwift/Sources/JolkEngine/Application.swift

206 lines
5.1 KiB
Swift
Raw Normal View History

2024-05-05 17:01:56 +10:00
import Foundation
import SDL
public protocol ApplicationImplementation
{
func create(render: inout Renderer)
func quit()
func loadContent(content: inout ContentManager) throws
func resize(width: Int, height: Int)
func update(deltaTime: Float)
func draw(render: inout Renderer, deltaTime: Float)
}
extension ApplicationImplementation
{
public func quit() {}
}
public struct ApplicationConfiguration
{
let resizable: Bool
let vSync: VSyncMode
let windowWidth: Int32
let windowHeight: Int32
let bundle: Bundle
public init(resizable: Bool, vSync: VSyncMode, windowWidth: Int32, windowHeight: Int32, bundle: Bundle)
{
self.resizable = resizable
self.vSync = vSync
self.windowWidth = windowWidth
self.windowHeight = windowHeight
self.bundle = bundle
}
}
//FIXME: Wrap these
public var sdlPad: OpaquePointer? = nil
var joyId: Int32 = -1
public class Application
{
private let implementation: ApplicationImplementation
private let configuration: ApplicationConfiguration
private var window: OpaquePointer?
private var render: Renderer
private var content: ContentManager
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
self.configuration = config
self.render = OpenGL(version: .init(major: 1, minor: 5))
self.content = ContentManager(&self.render, bundle: configuration.bundle)
}
public func run()
{
guard SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) == 0
else { fatalError("SDL_Init: \(String(cString: SDL_GetError()))") }
let winPos = Int32(SDL_WINDOWPOS_UNDEFINED_MASK)
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)
guard window != nil else { fatalError("SDL_CreateWindow: \(String(cString: SDL_GetError()))") }
do
{
try render.create(sdlWindow: window!)
try render.setVsync(mode: configuration.vSync)
}
catch RendererError.sdlError(let message)
{
fatalError("idk error \(message)")
}
catch { fatalError("piss") }
for i in 0...SDL_NumJoysticks()
{
if SDL_IsGameController(i) != SDL_TRUE { continue }
if let open = openController(index: i)
{
sdlPad = open.pad
joyId = open.joy
break
}
}
do
{
implementation.create(render: &render)
content.setLoader(extension: "obj", loader: ObjLoader())
content.setLoader(extension: "g3db", loader: G3DbLoader())
content.setLoader(extensions: ["png", "jpg"], loader: NSImageLoader())
try implementation.loadContent(content: &content)
}
catch
{
fatalError("loadContent()")
}
let timeMul = 1 / Double(SDL_GetPerformanceFrequency());
var prevTime = SDL_GetPerformanceCounter();
resize = displaySize
mainLoop: while true
{
Input.instance.newTick()
var event = SDL_Event()
while SDL_PollEvent(&event) > 0
{
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))
{
case SDL_WINDOWEVENT_RESIZED:
resize = displaySize
default: break
}
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
}
}
case SDL_CONTROLLERDEVICEREMOVED:
if event.cdevice.which == joyId
{
SDL_GameControllerClose(sdlPad)
sdlPad = nil
joyId = -1
}
default: break
}
}
let time = SDL_GetPerformanceCounter();
let deltaTime = Float(Double(time - prevTime) * timeMul)
prevTime = time
implementation.update(deltaTime: min(deltaTime, 1.0 / 15.0))
repaint(deltaTime)
}
implementation.quit()
content.releaseAll()
render.delete()
SDL_DestroyWindow(window)
SDL_Quit()
}
private func repaint(_ deltaTime: Float)
{
if resize != nil
{
render.resize(width: resize!.width, height: resize!.height)
implementation.resize(width: Int(resize!.width), height: Int(resize!.height))
resize = nil
}
render.newFrame()
implementation.draw(render: &render, deltaTime: deltaTime)
SDL_GL_SwapWindow(window)
}
private var displaySize: ResizeEvent
{
var width: Int32 = 0, height: Int32 = 0
SDL_GL_GetDrawableSize(window, &width, &height)
return (width, height)
}
}