Files
CavesOfSwift/Sources/JolkEngine/Application.swift

185 lines
5.1 KiB
Swift

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 windowTitle: String
let bundle: Bundle
public init(resizable: Bool, vSync: VSyncMode, windowWidth: Int32, windowHeight: Int32, title: String, bundle: Bundle)
{
self.resizable = resizable
self.vSync = vSync
self.windowWidth = windowWidth
self.windowHeight = windowHeight
self.windowTitle = title
self.bundle = bundle
}
}
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
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(configuration.windowTitle, 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 idx in 0...SDL_NumJoysticks()
{
if SDL_IsGameController(idx) != SDL_TRUE { continue }
Input.instance.padConnectedEvent(deviceIndex: idx)
}
do
{
implementation.create(render: &render)
content.setLoader(extension: "obj", loader: ObjLoader())
content.setLoader(extension: "g3db", loader: G3DbLoader())
content.setLoader(extensions: ["cache", "mesh"], loader: VMeshLoader())
content.setLoader(extensions: ["png", "jpg"], loader: NSImageLoader())
content.setLoader(extension: "dds", loader: DDSLoader())
try implementation.loadContent(content: &content)
}
catch
{
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_WINDOWEVENT:
switch SDL_WindowEventID(UInt32(event.window.event))
{
case SDL_WINDOWEVENT_RESIZED:
resize = displaySize
default: break
}
case SDL_KEYDOWN:
Input.instance.pressEvent(scan: event.key.keysym.scancode, repeat: event.key.repeat != 0)
case SDL_KEYUP:
Input.instance.releaseEvent(scan: event.key.keysym.scancode)
case SDL_CONTROLLERDEVICEADDED:
if SDL_IsGameController(event.cdevice.which) != SDL_TRUE { break }
Input.instance.padConnectedEvent(deviceIndex: event.cdevice.which)
case SDL_CONTROLLERDEVICEREMOVED:
Input.instance.padRemovedEvent(id: event.cdevice.which)
case SDL_CONTROLLERBUTTONDOWN:
Input.instance.padButtonPressEvent(id: event.cbutton.which, btn: event.cbutton.button)
case SDL_CONTROLLERBUTTONUP:
Input.instance.padButtonReleaseEvent(id: event.cbutton.which, btn: event.cbutton.button)
case SDL_CONTROLLERAXISMOTION:
Input.instance.padAxisEvent(id: event.caxis.which, axis: event.caxis.axis, value: event.caxis.value)
default: break
}
}
let time = SDL_GetPerformanceCounter();
let deltaTime = Float(Double(time &- prevTime) * timeMul)
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)
}
}