init dump
This commit is contained in:
165
Sources/JolkEngine/Content/ContentManager.swift
Normal file
165
Sources/JolkEngine/Content/ContentManager.swift
Normal file
@ -0,0 +1,165 @@
|
||||
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)\"") }
|
||||
}
|
9
Sources/JolkEngine/Content/Image.swift
Normal file
9
Sources/JolkEngine/Content/Image.swift
Normal file
@ -0,0 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
public struct Image: Resource
|
||||
{
|
||||
let pixels: Data
|
||||
let width: Int
|
||||
let height: Int
|
||||
}
|
9
Sources/JolkEngine/Content/LoaderProtocol.swift
Normal file
9
Sources/JolkEngine/Content/LoaderProtocol.swift
Normal file
@ -0,0 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
public protocol LoaderProtocol
|
||||
{
|
||||
associatedtype T: Resource
|
||||
|
||||
func load(url: URL) -> T?
|
||||
}
|
54
Sources/JolkEngine/Content/Mesh.swift
Normal file
54
Sources/JolkEngine/Content/Mesh.swift
Normal file
@ -0,0 +1,54 @@
|
||||
import OrderedCollections
|
||||
|
||||
|
||||
public struct Model: Resource
|
||||
{
|
||||
public let meshes: [Mesh]
|
||||
}
|
||||
|
||||
public struct Mesh: Resource
|
||||
{
|
||||
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 let start, length: Int
|
||||
|
||||
public init(start: Int, length: Int)
|
||||
{
|
||||
self.start = start
|
||||
self.length = length
|
||||
}
|
||||
}
|
||||
|
||||
public let vertices: [Vertex]
|
||||
public let indices: [Index]
|
||||
public let subMeshes: OrderedDictionary<String, SubMesh>
|
||||
}
|
||||
|
||||
public extension Mesh
|
||||
{
|
||||
static let empty = Self(vertices: .init(), indices: .init(), subMeshes: .init())
|
||||
|
||||
init(vertices: [Vertex], indices: [Index])
|
||||
{
|
||||
self.init(
|
||||
vertices: vertices,
|
||||
indices: indices,
|
||||
subMeshes: .init())
|
||||
}
|
||||
}
|
120
Sources/JolkEngine/Content/ObjModel.swift
Normal file
120
Sources/JolkEngine/Content/ObjModel.swift
Normal file
@ -0,0 +1,120 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
public struct ObjModel
|
||||
{
|
||||
public var positions = [Vec3f]()
|
||||
public var normals = [Vec3f]()
|
||||
public var texCoords = [Vec2f]()
|
||||
public var objects = Dictionary<String, Object>()
|
||||
|
||||
public struct Object { public var faces = [Face]() }
|
||||
|
||||
public struct Index { public let p: Int, n: Int, t: Int }
|
||||
public enum Face
|
||||
{
|
||||
case line(p1: Int, p2: Int)
|
||||
case triangle(v1: Index, v2: Index, v3: Index)
|
||||
case quad(v1: Index, v2: Index, v3: Index, v4: Index)
|
||||
case ngon(_ v: [Index])
|
||||
}
|
||||
}
|
||||
|
||||
public struct ObjMaterial
|
||||
{
|
||||
var model: IlluminationModel = .lambert
|
||||
var ambient: Colour = .black
|
||||
var diffuse: Colour = .white
|
||||
var specular: Colour = .zero
|
||||
var specularExp: Float = 30
|
||||
var refraction: Float = 1.0
|
||||
var alpha: Float = 1.0
|
||||
|
||||
var ambientMap: TextureMap? = nil
|
||||
var diffuseMap: TextureMap? = nil
|
||||
var specularMap: TextureMap? = nil
|
||||
var specularExpMap: TextureMap? = nil
|
||||
var dissolveMap: TextureMap? = nil
|
||||
var bumpMap: TextureMap? = nil
|
||||
var displacementMap: TextureMap? = nil
|
||||
var decalMap: TextureMap? = nil
|
||||
|
||||
// PBR extensions
|
||||
var roughness: Float = 0.5
|
||||
var metallic: Float = 0.0
|
||||
var sheen: Float = 0.0
|
||||
var clearcoatThickness: Float = 0.0
|
||||
var clearcoatRoughness: Float = 0.03 // dunno what the default should be so here's the blender one
|
||||
var emmision: Vec3f = .zero
|
||||
var anisotropy: Float = 0.0
|
||||
var anisotropyRotation: Float = 0.0
|
||||
|
||||
var roughnessMap: TextureMap? = nil
|
||||
var metallicMap: TextureMap? = nil
|
||||
var sheenMap: TextureMap? = nil
|
||||
var emmisionMap: TextureMap? = nil
|
||||
var normalMap: TextureMap? = nil
|
||||
|
||||
// Ft Fresnel reflectance
|
||||
// Ft Fresnel transmittance
|
||||
// Ns Specular exponent/shininess
|
||||
// Ia Ambient light
|
||||
// I Light intensity
|
||||
// Ir Intensity from reflected direction (reflection map and/or raytracing)
|
||||
// It Intensity from transmitted direction
|
||||
// Ka Ambient reflectance
|
||||
// Kd Diffuse reflectance
|
||||
// Ks Specular reflectance
|
||||
// Tf Transmission filter
|
||||
// H Unit vector bisector between L and V
|
||||
// L Unit light vector
|
||||
// N Unit surface normal
|
||||
// V Unit view vector
|
||||
//
|
||||
// j = piss
|
||||
// Fr = Fresnel
|
||||
// lambert = KaIa + Kd(N * Lj)Ij
|
||||
// blinnPhong = lambert + Ks((H * Hj) ^ Ns)Ij
|
||||
//
|
||||
// Notes: Clara.io only usees 0 and 1, maya always exports 4, blender can export 1, 3, 6
|
||||
public enum IlluminationModel
|
||||
{
|
||||
case colour // Kd only
|
||||
case lambert
|
||||
case blinnPhong
|
||||
case reflectionRaytrace // + Ir = (intensity of reflection map) + (ray trace)
|
||||
case transparentGlassReflectionRaytrace // + Ir = (glass) + (intensity of reflection map) + (raytrace)
|
||||
case reflectionRaytraceFresnel // Fr(Lj * Hj, Ks, Ns)Ij + Fr(N * V, Ks, Ns)Ir
|
||||
case transparentRefractionReflectionRaytrace // + Ir + (1 - Ks)TfIt
|
||||
case transparentRefractionReflectionRaytraceFresnel // Fr(Lj * Hj, Ks, Ns)Ij + Fr(N * V, Ks, Ns)Ir
|
||||
case reflection // + Ir = (intensity of reflection map)
|
||||
case transparentGlassReflection // + Ir = (glass) + (intensity of reflection map) + (raytrace)
|
||||
case shadowOnly
|
||||
}
|
||||
|
||||
public struct TextureMap
|
||||
{
|
||||
var path: String = .init()
|
||||
var flags: Flags = [ .blendHoriz, .blendVert ]
|
||||
var blendMul: Float = 1.0
|
||||
var boost: Float = 0.0 // non-negative
|
||||
var imfChan: ImfChan = .l // decal ? .m : .l
|
||||
var mmBaseGain: (Float, Float) = (0.0, 1.0)
|
||||
var offset: Vec3f = .zero
|
||||
var scale: Vec3f = .one
|
||||
var turbulence: Vec3f = .zero
|
||||
var textureResolution: Int = 0
|
||||
|
||||
struct Flags: OptionSet
|
||||
{
|
||||
let rawValue: UInt8
|
||||
|
||||
static let blendHoriz: Self = Self(rawValue: 1 << 0)
|
||||
static let blendVert: Self = Self(rawValue: 1 << 1)
|
||||
static let colourCorrection: Self = Self(rawValue: 1 << 2)
|
||||
static let clamp: Self = Self(rawValue: 1 << 3)
|
||||
}
|
||||
|
||||
enum ImfChan { case r, g, b, m, l, z }
|
||||
}
|
||||
}
|
4
Sources/JolkEngine/Content/Resource.swift
Normal file
4
Sources/JolkEngine/Content/Resource.swift
Normal file
@ -0,0 +1,4 @@
|
||||
public protocol Resource
|
||||
{
|
||||
|
||||
}
|
Reference in New Issue
Block a user