import Foundation public protocol ContentLoaderParametersProtocol { associatedtype T: Resource } public struct ContentManager { private var loaders = Dictionary() 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(extensions exts: [String], loader: Loader) where Loader.T == T { for i in exts { setLoader(extension: i, loader: loader) } } public mutating func setLoader(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[..(_ 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(_ 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(_ 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)\"") } }