Files
CavesOfSwift/Sources/JolkEngine/Content/ContentManager.swift
2024-05-05 17:01:56 +10:00

166 lines
4.5 KiB
Swift

import Foundation
public protocol ContentLoaderParametersProtocol
{
associatedtype T: Resource
}
public struct ContentManager
{
private var loaders = Dictionary<String, any LoaderProtocol>()
private var resources = [any RendererResource]()
private let renderer: Renderer
private let bundle: Bundle
}
extension ContentManager
{
internal init(_ renderer: inout Renderer, bundle: Bundle)
{
self.renderer = renderer
self.bundle = bundle
}
public mutating func setLoader<T: Resource, Loader: LoaderProtocol>(extensions exts: [String], loader: Loader) where Loader.T == T
{
for i in exts
{
setLoader(extension: i, loader: loader)
}
}
public mutating func setLoader<T: Resource, Loader: LoaderProtocol>(extension ext: String, loader: Loader) where Loader.T == T
{
loaders[ext] = loader
}
public mutating func create(mesh: Mesh) throws -> RenderMesh
{
let rendMesh = try renderer.createMesh(mesh: mesh)
resources.append(rendMesh)
return rendMesh
}
public mutating func create(texture: Image, params: Texture2DParameters = .init()) throws -> Texture2D
{
try texture.pixels.withUnsafeBytes
{
raw in
let data = raw.baseAddress!
let rendTex = try renderer.createTexture(
data: data, width: texture.width, height: texture.height,
filter: params.magFilter, mipMode: params.mipMode)
resources.append(rendTex)
return Texture2D(id: rendTex, width: texture.width, height: texture.height)
}
}
public func getResource(_ path: String) throws -> URL
{
guard let extIndex = path.lastIndex(of: ".")
else { throw ContentError.badPath }
let name = String(path[..<extIndex]), ext = String(path[extIndex...])
guard let resourceUrl: URL = bundle.url(
forResource: name,
withExtension: ext)
else { throw ContentError.resourceNotFound }
return resourceUrl
}
public mutating func load<T: Resource>(_ path: String) throws -> T
{
let resourceUrl = try getResource(path)
let loader = loaders[resourceUrl.pathExtension]
guard let resource = loader?.load(url: resourceUrl)
else { throw ContentError.loadFailure }
if T.self == Mesh.self
{
guard let mesh = resource as? Mesh else { throw ContentError.loadFailure }
return mesh as! T
}
if T.self == Image.self
{
guard let image = resource as? Image else { throw ContentError.loadFailure }
return image as! T
}
if T.self == Texture2D.self
{
guard let image = resource as? Image else { throw ContentError.loadFailure }
let texture2D = try self.create(texture: image)
return texture2D as! T
}
throw ContentError.loadFailure
}
public mutating func load<T: Resource, Params: ContentLoaderParametersProtocol>(_ path: String, params: Params) throws
-> T where Params.T == T
{
let resourceUrl = try getResource(path)
let loader = loaders[resourceUrl.pathExtension]
guard let resource = loader?.load(url: resourceUrl)
else { throw ContentError.loadFailure }
if T.self == Mesh.self
{
guard let mesh = resource as? Mesh else { throw ContentError.loadFailure }
return mesh as! T
}
if T.self == Image.self
{
guard let image = resource as? Image else { throw ContentError.loadFailure }
return image as! T
}
if T.self == Texture2D.self
{
guard let image = resource as? Image else { throw ContentError.loadFailure }
let texture2D = try self.create(texture: image, params: params as! Texture2DParameters)
return texture2D as! T
}
throw ContentError.loadFailure
}
public mutating func load<T: RendererResource>(_ path: String) throws -> T
{
let resourceUrl = try getResource(path)
let loader = loaders[resourceUrl.pathExtension]
guard let resource = loader?.load(url: resourceUrl)
else { throw ContentError.loadFailure }
if T.self == RenderMesh.self
{
guard let mesh = resource as? Mesh else { throw ContentError.loadFailure }
let renderResource = try self.create(mesh: mesh)
return renderResource as! T
}
throw ContentError.loadFailure
}
internal mutating func releaseAll()
{
for resource in resources.reversed()
{
if resource is RenderMesh
{
renderer.deleteMesh(resource as! RenderMesh)
}
else if resource is RenderTexture2D
{
renderer.deleteTexture(resource as! RenderTexture2D)
}
}
resources.removeAll()
}
}
public enum ContentError: Error
{
case badPath //else { fatalError("Malformed resource path \"\(path)\"") }
case resourceNotFound //else { fatalError("Resource \"\(path)\" doesn't exist") }
case loadFailure //else { fatalError("Failed to load resource \"\(path)\"") }
}