init dump
This commit is contained in:
81
Sources/JolkEngine/Renderer/Environment.swift
Normal file
81
Sources/JolkEngine/Renderer/Environment.swift
Normal file
@ -0,0 +1,81 @@
|
||||
public enum Fog
|
||||
{
|
||||
public enum Mode
|
||||
{
|
||||
case distance
|
||||
case depth
|
||||
}
|
||||
|
||||
public enum RangeType
|
||||
{
|
||||
case linear
|
||||
case smooth
|
||||
}
|
||||
|
||||
public enum FactorType
|
||||
{
|
||||
case exp
|
||||
case exp2
|
||||
}
|
||||
|
||||
case none
|
||||
case range(colour: Colour, mode: Mode, type: RangeType, start: Float, end: Float)
|
||||
case factor(colour: Colour, mode: Mode, type: FactorType, density: Float)
|
||||
}
|
||||
|
||||
public enum Light
|
||||
{
|
||||
case directional(colour: Colour, direction: Vec3f)
|
||||
case point(colour: Colour, position: Vec3f, intensity: Float)
|
||||
}
|
||||
|
||||
public struct Environment
|
||||
{
|
||||
public var fog: Fog = .none
|
||||
public var ambient: Colour = .black
|
||||
public var lights: [Light] = .init()
|
||||
|
||||
public init() {}
|
||||
|
||||
public init(fog: Fog, ambient: Colour, lights: [Light])
|
||||
{
|
||||
self.fog = fog
|
||||
self.ambient = ambient
|
||||
self.lights = lights
|
||||
}
|
||||
}
|
||||
|
||||
public extension Environment
|
||||
{
|
||||
enum FogFalloff
|
||||
{
|
||||
case range(type: Fog.RangeType, start: Float, end: Float)
|
||||
case factor(type: Fog.FactorType, density: Float)
|
||||
}
|
||||
|
||||
mutating func setFog(colour: Colour, mode: Fog.Mode, falloff: FogFalloff)
|
||||
{
|
||||
fog = switch falloff
|
||||
{
|
||||
case .range(let type, let start, let end):
|
||||
.range(colour: colour, mode: mode, type: type, start: start, end: end)
|
||||
case .factor(let type, let density):
|
||||
.factor(colour: colour, mode: mode, type: type, density: density)
|
||||
}
|
||||
}
|
||||
|
||||
mutating func addAmbientLight(colour: Colour)
|
||||
{
|
||||
ambient = colour
|
||||
}
|
||||
|
||||
mutating func addDirectionalLight(direction: Vec3f, colour: Colour)
|
||||
{
|
||||
lights.append(.directional(colour: colour, direction: direction))
|
||||
}
|
||||
|
||||
mutating func addPointLight(position: Vec3f, colour: Colour, intensity: Float)
|
||||
{
|
||||
lights.append(.point(colour: colour, position: position, intensity: intensity))
|
||||
}
|
||||
}
|
20
Sources/JolkEngine/Renderer/Material.swift
Normal file
20
Sources/JolkEngine/Renderer/Material.swift
Normal file
@ -0,0 +1,20 @@
|
||||
public struct Material
|
||||
{
|
||||
public var id: String
|
||||
public var diffuse: Colour, specular: Colour
|
||||
public var specularExp: Float
|
||||
public var texture: RenderTexture2D
|
||||
|
||||
public init(id: String = "",
|
||||
diffuse: Colour = .white,
|
||||
specular: Colour = .zero,
|
||||
gloss: Float = 0,
|
||||
texture: RenderTexture2D = .empty)
|
||||
{
|
||||
self.id = id
|
||||
self.diffuse = diffuse
|
||||
self.specular = specular
|
||||
self.specularExp = gloss
|
||||
self.texture = texture
|
||||
}
|
||||
}
|
440
Sources/JolkEngine/Renderer/OpenGL.swift
Normal file
440
Sources/JolkEngine/Renderer/OpenGL.swift
Normal file
@ -0,0 +1,440 @@
|
||||
//#set OPENGL3
|
||||
|
||||
import Foundation
|
||||
import SDL2
|
||||
#if OPENGL3
|
||||
import OpenGL
|
||||
#else
|
||||
import OpenGL.GL3
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class OpenGL: Renderer
|
||||
{
|
||||
private let srgb = true
|
||||
|
||||
private var glCtx: SDL_GLContext? = nil
|
||||
private var state = OpenGLState()
|
||||
|
||||
struct Version { let major, minor: Int32 }
|
||||
let glVersion: Version
|
||||
|
||||
init(version: Version)
|
||||
{
|
||||
self.glVersion = version
|
||||
}
|
||||
|
||||
func create(sdlWindow: OpaquePointer) throws
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, Int32(SDL_GL_CONTEXT_PROFILE_COMPATIBILITY.rawValue))
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glVersion.major)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glVersion.minor)
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0)
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0)
|
||||
|
||||
if let context = SDL_GL_CreateContext(sdlWindow) { glCtx = context }
|
||||
else { throw RendererError.sdlError(message: "SDL_GL_CreateContext: \(String(cString: SDL_GetError()))") }
|
||||
|
||||
guard SDL_GL_MakeCurrent(sdlWindow, glCtx) == 0
|
||||
else { throw RendererError.sdlError(message: "SDL_GL_MakeCurrent: \(String(cString: SDL_GetError()))") }
|
||||
|
||||
state.enable([.texture2D, .cullFace, .depthTest, .rescaleNormal, .colourMaterial])
|
||||
if srgb { state.enable(.frameBufferSrgb) }
|
||||
state.cullFace = .back
|
||||
state.clearDepth = 1
|
||||
state.depthFunc = .less
|
||||
state.setHint(target: .fog, hint: .dontCare)
|
||||
}
|
||||
|
||||
func delete()
|
||||
{
|
||||
SDL_GL_DeleteContext(glCtx)
|
||||
}
|
||||
|
||||
func resize(width: Int32, height: Int32)
|
||||
{
|
||||
glViewport(0, 0, width, height)
|
||||
}
|
||||
|
||||
func newFrame()
|
||||
{
|
||||
glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
|
||||
}
|
||||
|
||||
private var _clearColour = Colour.black
|
||||
var clearColour: Colour
|
||||
{
|
||||
get { _clearColour }
|
||||
set(newColour)
|
||||
{
|
||||
|
||||
state.clearColour(srgb ? newColour.linear : newColour)
|
||||
_clearColour = newColour
|
||||
}
|
||||
}
|
||||
|
||||
func setVsync(mode: VSyncMode) throws
|
||||
{
|
||||
guard SDL_GL_SetSwapInterval(mode.sdlInterval) == 0
|
||||
else { throw RendererError.sdlError(message: "SDL_GL_SetSwapInterval: \(String(cString: SDL_GetError()))") }
|
||||
}
|
||||
|
||||
func createMesh(mesh: Mesh) throws -> RenderMesh
|
||||
{
|
||||
var buffers = [GLuint](repeating: 0, count: 2)
|
||||
buffers.withUnsafeMutableBufferPointer
|
||||
{
|
||||
glGenBuffers(2, $0.baseAddress!)
|
||||
}
|
||||
|
||||
state.arrayBuffer = buffers[0]
|
||||
state.elementArrayBuffer = buffers[1]
|
||||
|
||||
glBufferData(GLenum(GL_ARRAY_BUFFER),
|
||||
MemoryLayout<Mesh.Vertex>.stride * mesh.vertices.count,
|
||||
mesh.vertices, GLenum(GL_STATIC_DRAW))
|
||||
glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER),
|
||||
MemoryLayout<Mesh.Index>.stride * mesh.indices.count,
|
||||
mesh.indices, GLenum(GL_STATIC_DRAW))
|
||||
|
||||
state.elementArrayBuffer = OpenGLState.defaultBuffer
|
||||
state.arrayBuffer = OpenGLState.defaultBuffer
|
||||
|
||||
var subMeshes = [Mesh.SubMesh]()
|
||||
if mesh.subMeshes.isEmpty
|
||||
{
|
||||
subMeshes.append(Mesh.SubMesh(start: 0, length: mesh.indices.count))
|
||||
}
|
||||
else
|
||||
{
|
||||
for subMesh in mesh.subMeshes
|
||||
{
|
||||
if ["Collision", "Collision3D"].contains(subMesh.key) { continue }
|
||||
subMeshes.append(Mesh.SubMesh(
|
||||
start: subMesh.value.start,
|
||||
length: subMesh.value.length))
|
||||
}
|
||||
}
|
||||
|
||||
return RenderMesh(
|
||||
vbo: Int(buffers[0]),
|
||||
ibo: Int(buffers[1]),
|
||||
subMeshes: subMeshes)
|
||||
}
|
||||
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int) throws -> RenderTexture2D
|
||||
{
|
||||
try createTexture(data: data, width: width, height: height, filter: .linear, mipMode: .off)
|
||||
}
|
||||
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int,
|
||||
filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
|
||||
{
|
||||
let min: Int32 = switch mipMode
|
||||
{
|
||||
case .off: filter.gl
|
||||
case .nearest: filter.glMip
|
||||
case .linear: filter.glLinearMip
|
||||
}
|
||||
return RenderTexture2D(try loadTexture(data: data,
|
||||
width: GLsizei(width), height: GLsizei(height),
|
||||
minFilter: min, magFilter: filter.gl))
|
||||
}
|
||||
|
||||
private func loadTexture(
|
||||
data: UnsafeRawPointer,
|
||||
width: GLsizei, height: GLsizei,
|
||||
minFilter: Int32 = GL_LINEAR, magFilter: Int32 = GL_LINEAR,
|
||||
wrap: Int32 = GL_REPEAT) throws -> GLuint
|
||||
{
|
||||
// Create & upload raw bytes as texture
|
||||
var texId: GLuint = 0
|
||||
glGenTextures(1, &texId)
|
||||
state.bindTexture2D(active: 0, texture: texId)
|
||||
state.texture2DParameter(active: 0, param: .minFilter, int: minFilter)
|
||||
state.texture2DParameter(active: 0, param: .magFilter, int: magFilter)
|
||||
state.texture2DParameter(active: 0, param: .wrapS, int: wrap)
|
||||
state.texture2DParameter(active: 0, param: .wrapT, int: wrap)
|
||||
let format: GLint = srgb ? GL_SRGB8 : GL_RGBA
|
||||
glTexImage2D(GLenum(GL_TEXTURE_2D),
|
||||
0, format, width, height, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), data)
|
||||
|
||||
// Generate mipmaps if needed
|
||||
if [GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST]
|
||||
.contains(minFilter) { glGenerateMipmap(GLenum(GL_TEXTURE_2D)) }
|
||||
|
||||
state.bindTexture2D(active: 0, texture: OpenGLState.defaultTexture)
|
||||
|
||||
return texId
|
||||
}
|
||||
|
||||
|
||||
func deleteMesh(_ mesh: RenderMesh)
|
||||
{
|
||||
var buffers = [GLuint](repeating: 0, count: 2)
|
||||
buffers[0] = GLuint(mesh.iboHnd)
|
||||
buffers[1] = GLuint(mesh.vboHnd)
|
||||
|
||||
glDeleteBuffers(1, buffers)
|
||||
}
|
||||
|
||||
func deleteTexture(_ texture: RenderTexture2D)
|
||||
{
|
||||
var texId = GLuint(texture.id)
|
||||
glDeleteTextures(1, &texId)
|
||||
}
|
||||
|
||||
|
||||
func setProjection(matrix: Mat4f)
|
||||
{
|
||||
state.matrixMode = .projection
|
||||
loadMatrix(matrix)
|
||||
}
|
||||
|
||||
func setView(matrix: Mat4f)
|
||||
{
|
||||
state.matrixMode = .modelView
|
||||
loadMatrix(matrix)
|
||||
}
|
||||
|
||||
|
||||
func setMaterial(_ mat: Material)
|
||||
{
|
||||
glColor4f(mat.diffuse.r, mat.diffuse.g, mat.diffuse.b, mat.diffuse.a)
|
||||
state.bindTexture2D(active: 0, texture: mat.texture.isValid ? mat.texture.id : 0)
|
||||
}
|
||||
|
||||
|
||||
private func draw(subMesh: Mesh.SubMesh)
|
||||
{
|
||||
glDrawElements(
|
||||
GLenum(GL_TRIANGLES),
|
||||
GLsizei(subMesh.length),
|
||||
GLenum(GL_UNSIGNED_SHORT),
|
||||
.init(bitPattern: MemoryLayout<Mesh.Index>.stride * subMesh.start));
|
||||
}
|
||||
|
||||
private func draw(mesh: RenderMesh)
|
||||
{
|
||||
state.enableClient([ .vertex, .normal, .textureCoord ])
|
||||
|
||||
state.arrayBuffer = UInt32(mesh.vboHnd)
|
||||
state.elementArrayBuffer = UInt32(mesh.iboHnd)
|
||||
|
||||
let stride = GLsizei(MemoryLayout<Mesh.Vertex>.stride)
|
||||
glVertexPointer(3, GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \Mesh.Vertex.position)!))
|
||||
glNormalPointer(GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \Mesh.Vertex.normal)!))
|
||||
glTexCoordPointer(2, GLenum(GL_FLOAT), stride,
|
||||
UnsafeRawPointer.init(bitPattern: MemoryLayout.offset(of: \Mesh.Vertex.texCoord)!))
|
||||
|
||||
for subMesh in mesh.subMeshes
|
||||
{
|
||||
draw(subMesh: subMesh)
|
||||
}
|
||||
|
||||
state.elementArrayBuffer = OpenGLState.defaultBuffer
|
||||
state.arrayBuffer = OpenGLState.defaultBuffer
|
||||
|
||||
state.disableClient([ .vertex, .normal, .textureCoord ])
|
||||
}
|
||||
|
||||
func draw(mesh: RenderMesh, model: Mat4f, environment env: Environment)
|
||||
{
|
||||
if (mesh.subMeshes.isEmpty) { return }
|
||||
|
||||
applyEnvironment(env)
|
||||
state.matrixMode = .modelView
|
||||
glPushMatrix()
|
||||
mulMatrix(model)
|
||||
draw(mesh: mesh)
|
||||
glPopMatrix()
|
||||
}
|
||||
|
||||
func draw(mesh: RenderMesh, environment env: Environment)
|
||||
{
|
||||
if (mesh.subMeshes.isEmpty) { return }
|
||||
|
||||
applyEnvironment(env)
|
||||
draw(mesh: mesh)
|
||||
}
|
||||
|
||||
private func loadMatrix(_ matrix: Mat4f)
|
||||
{
|
||||
withUnsafePointer(to: matrix.columns)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 16)
|
||||
{ (values: UnsafePointer<GLfloat>) in
|
||||
glLoadMatrixf(values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func mulMatrix(_ matrix: Mat4f)
|
||||
{
|
||||
withUnsafePointer(to: matrix.columns)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 16)
|
||||
{ (values: UnsafePointer<GLfloat>) in
|
||||
glMultMatrixf(values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func applyEnvironment(_ env: Environment)
|
||||
{
|
||||
let coordSource =
|
||||
{ (mode: Fog.Mode) -> OpenGLState.FogCoordinateSource in
|
||||
switch mode
|
||||
{
|
||||
case .depth: .fragmentDepth
|
||||
case .distance: .coordinate
|
||||
}
|
||||
}
|
||||
|
||||
switch env.fog
|
||||
{
|
||||
case .none:
|
||||
state.disable(.fog)
|
||||
case .range(let colour, let mode, _, let start, let end):
|
||||
state.fogMode = .linear
|
||||
state.fogColour = srgb ? colour.linear : colour
|
||||
state.fogCoodinateSource = coordSource(mode)
|
||||
state.fogStart = start
|
||||
state.fogEnd = end
|
||||
state.enable(.fog)
|
||||
break
|
||||
case .factor(let colour, let mode, let type, let density):
|
||||
state.fogMode = switch type
|
||||
{
|
||||
case .exp: .exp
|
||||
case .exp2: .exp2
|
||||
}
|
||||
state.fogColour = srgb ? colour.linear : colour
|
||||
state.fogCoodinateSource = coordSource(mode)
|
||||
state.fogDensity = density
|
||||
state.enable(.fog)
|
||||
break
|
||||
}
|
||||
|
||||
state.lightModelAmbient = env.ambient
|
||||
|
||||
let lightCaps: [OpenGLState.Capability] = [
|
||||
.light0, .light1, .light2, .light3,
|
||||
.light4, .light5, .light6, .light7 ]
|
||||
if !env.lights.isEmpty
|
||||
{
|
||||
state.enable(.lighting)
|
||||
for i in 0..<8
|
||||
{
|
||||
if i < env.lights.count
|
||||
{
|
||||
switch env.lights[i]
|
||||
{
|
||||
case .directional(let colour, let direction):
|
||||
state.lightPosition[i] = Vec4f(direction, 0)
|
||||
state.lightDiffuse[i] = srgb ? colour.linear : colour
|
||||
case .point(let colour, let position, let intensity):
|
||||
state.lightPosition[i] = Vec4f(position, 1)
|
||||
state.lightDiffuse[i] = srgb ? colour.linear : colour
|
||||
state.lightConstantAttenuation[i] = 0
|
||||
state.lightLinearAttenuation[i] = intensity
|
||||
state.lightQuadraticAttenuation[i] = 0
|
||||
}
|
||||
state.enable(lightCaps[i])
|
||||
}
|
||||
else
|
||||
{
|
||||
state.disable(lightCaps[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.disable(.lighting)
|
||||
for cap in lightCaps { state.disable(cap) }
|
||||
}
|
||||
}
|
||||
|
||||
func drawGizmos(lines: [Line])
|
||||
{
|
||||
state.disable([ .texture2D, .depthTest ])
|
||||
var gizmoEnv = Environment()
|
||||
gizmoEnv.fog = .none
|
||||
applyEnvironment(gizmoEnv)
|
||||
|
||||
glBegin(GLenum(GL_LINES))
|
||||
for line in lines
|
||||
{
|
||||
let colour = srgb ? line.colour.linear : line.colour
|
||||
glColor4f(colour.r, colour.g, colour.b, colour.a)
|
||||
glVertex3f(line.from.x, line.from.y, line.from.z)
|
||||
glVertex3f(line.to.x, line.to.y, line.to.z)
|
||||
}
|
||||
glEnd()
|
||||
state.enable([ .texture2D, .depthTest ])
|
||||
}
|
||||
}
|
||||
|
||||
extension VSyncMode
|
||||
{
|
||||
fileprivate var sdlInterval: Int32
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .off: 0
|
||||
case .on: 1
|
||||
case .adaptive: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FilterMode
|
||||
{
|
||||
fileprivate var gl: Int32
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .point: GL_NEAREST
|
||||
case .linear: GL_LINEAR
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var glMip: Int32
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .point: GL_NEAREST_MIPMAP_NEAREST
|
||||
case .linear: GL_LINEAR_MIPMAP_NEAREST
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var glLinearMip: Int32
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .point: GL_NEAREST_MIPMAP_LINEAR
|
||||
case .linear: GL_LINEAR_MIPMAP_LINEAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WrapMode
|
||||
{
|
||||
fileprivate var gl: Int32
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .clamping: GL_CLAMP
|
||||
case .clampBorder: GL_CLAMP_TO_BORDER
|
||||
case .clampEdge: GL_CLAMP_TO_EDGE
|
||||
case .mirrored: GL_MIRRORED_REPEAT
|
||||
case .repeating: GL_REPEAT
|
||||
}
|
||||
}
|
||||
}
|
796
Sources/JolkEngine/Renderer/OpenGLState.swift
Normal file
796
Sources/JolkEngine/Renderer/OpenGLState.swift
Normal file
@ -0,0 +1,796 @@
|
||||
import OpenGL
|
||||
|
||||
|
||||
struct OpenGLState
|
||||
{
|
||||
struct Capability: OptionSet
|
||||
{
|
||||
let rawValue: UInt32
|
||||
|
||||
static let alphaTest = Self(rawValue: 1 << 0)
|
||||
static let blend = Self(rawValue: 1 << 1)
|
||||
static let colourMaterial = Self(rawValue: 1 << 2)
|
||||
static let colourSum = Self(rawValue: 1 << 3)
|
||||
static let cullFace = Self(rawValue: 1 << 4)
|
||||
static let depthTest = Self(rawValue: 1 << 5)
|
||||
static let dither = Self(rawValue: 1 << 6)
|
||||
static let fog = Self(rawValue: 1 << 7)
|
||||
static let light0 = Self(rawValue: 1 << 8)
|
||||
static let light1 = Self(rawValue: 1 << 9)
|
||||
static let light2 = Self(rawValue: 1 << 10)
|
||||
static let light3 = Self(rawValue: 1 << 11)
|
||||
static let light4 = Self(rawValue: 1 << 12)
|
||||
static let light5 = Self(rawValue: 1 << 13)
|
||||
static let light6 = Self(rawValue: 1 << 14)
|
||||
static let light7 = Self(rawValue: 1 << 15)
|
||||
static let lighting = Self(rawValue: 1 << 16)
|
||||
//static let lineSmooth = Self(rawValue: 1 << 17)
|
||||
static let lineStipple = Self(rawValue: 1 << 18)
|
||||
static let multiSample = Self(rawValue: 1 << 19)
|
||||
//static let pointSmooth = Self(rawValue: 1 << 20)
|
||||
static let scissorTest = Self(rawValue: 1 << 21)
|
||||
static let stencilTest = Self(rawValue: 1 << 22)
|
||||
//static let texture1D = Self(rawValue: 1 << 23)
|
||||
static let texture2D = Self(rawValue: 1 << 24)
|
||||
static let clipPlane0 = Self(rawValue: 1 << 25)
|
||||
static let clipPlane1 = Self(rawValue: 1 << 26)
|
||||
static let clipPlane2 = Self(rawValue: 1 << 27)
|
||||
static let clipPlane3 = Self(rawValue: 1 << 28)
|
||||
static let clipPlane4 = Self(rawValue: 1 << 29)
|
||||
static let clipPlane5 = Self(rawValue: 1 << 30)
|
||||
//static let colourLogicOp = Self(rawValue: 1 << 31)
|
||||
|
||||
static let sampleCoverage = Self(rawValue: 1 << 17)
|
||||
static let alphaToOne = Self(rawValue: 1 << 20)
|
||||
static let rescaleNormal = Self(rawValue: 1 << 23)
|
||||
static let frameBufferSrgb = Self(rawValue: 1 << 31)
|
||||
|
||||
static let all: [Self] = [ .alphaTest, .blend, .colourMaterial, .colourSum, .cullFace, .depthTest, .dither,
|
||||
.fog, .light0, .light1, .light2, .light3, .light4, .light5, .light6, .light7, .lighting, /*.lineSmooth,*/
|
||||
.lineStipple, /*.pointSmooth,*/ .multiSample, .scissorTest, .stencilTest, /*.texture1D,*/ .texture2D,
|
||||
.clipPlane0, .clipPlane1, .clipPlane2, .clipPlane3, .clipPlane4, .clipPlane5, /*.colourLogicOp,*/
|
||||
|
||||
.sampleCoverage, .alphaToOne, .rescaleNormal, .frameBufferSrgb ]
|
||||
static let initial: Self = [ .dither, .multiSample ]
|
||||
|
||||
var glCap: GLenum
|
||||
{
|
||||
let capEnum = switch self
|
||||
{
|
||||
case .alphaTest: GL_ALPHA_TEST
|
||||
case .blend: GL_BLEND
|
||||
case .colourMaterial: GL_COLOR_MATERIAL
|
||||
case .colourSum: GL_COLOR_SUM
|
||||
case .cullFace: GL_CULL_FACE
|
||||
case .depthTest: GL_DEPTH_TEST
|
||||
case .dither: GL_DITHER
|
||||
case .fog: GL_FOG
|
||||
case .light0: GL_LIGHT0
|
||||
case .light1: GL_LIGHT1
|
||||
case .light2: GL_LIGHT2
|
||||
case .light3: GL_LIGHT3
|
||||
case .light4: GL_LIGHT4
|
||||
case .light5: GL_LIGHT5
|
||||
case .light6: GL_LIGHT6
|
||||
case .light7: GL_LIGHT7
|
||||
case .lighting: GL_LIGHTING
|
||||
//case .lineSmooth: GL_LINE_SMOOTH
|
||||
case .lineStipple: GL_LINE_STIPPLE
|
||||
case .multiSample: GL_MULTISAMPLE
|
||||
//case .pointSmooth: GL_POINT_SMOOTH
|
||||
case .scissorTest: GL_SCISSOR_TEST
|
||||
case .stencilTest: GL_STENCIL_TEST
|
||||
//case .texture1D: GL_TEXTURE_1D
|
||||
case .texture2D: GL_TEXTURE_2D
|
||||
case .clipPlane0: GL_CLIP_PLANE0
|
||||
case .clipPlane1: GL_CLIP_PLANE1
|
||||
case .clipPlane2: GL_CLIP_PLANE2
|
||||
case .clipPlane3: GL_CLIP_PLANE3
|
||||
case .clipPlane4: GL_CLIP_PLANE4
|
||||
case .clipPlane5: GL_CLIP_PLANE5
|
||||
//case .colourLogicOp: GL_COLOR_LOGIC_OP
|
||||
case .sampleCoverage: GL_SAMPLE_COVERAGE
|
||||
case .alphaToOne: GL_SAMPLE_ALPHA_TO_ONE
|
||||
case .rescaleNormal: GL_RESCALE_NORMAL
|
||||
case .frameBufferSrgb: GL_FRAMEBUFFER_SRGB
|
||||
default: fatalError()
|
||||
}
|
||||
return GLenum(capEnum)
|
||||
}
|
||||
}
|
||||
|
||||
private var _capabilities: Capability = .initial
|
||||
|
||||
private mutating func toggle(_ cap: Capability, _ en: Bool)
|
||||
{
|
||||
if en
|
||||
{
|
||||
if !_capabilities.contains(cap)
|
||||
{
|
||||
glEnable(cap.glCap)
|
||||
_capabilities.insert(cap)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if _capabilities.contains(cap)
|
||||
{
|
||||
glDisable(cap.glCap)
|
||||
_capabilities.remove(cap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutating func enable(_ caps: Capability)
|
||||
{
|
||||
if Capability.all.contains(caps)
|
||||
{
|
||||
toggle(caps, true)
|
||||
}
|
||||
else
|
||||
{
|
||||
for i in Capability.all
|
||||
{
|
||||
if i.intersection(caps) == i { toggle(i, true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutating func disable(_ caps: Capability)
|
||||
{
|
||||
if Capability.all.contains(caps)
|
||||
{
|
||||
toggle(caps, false)
|
||||
}
|
||||
else
|
||||
{
|
||||
for i in Capability.all
|
||||
{
|
||||
if i.intersection(caps) == i { toggle(i, false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ClientState: OptionSet
|
||||
{
|
||||
let rawValue: UInt32
|
||||
static let colour = Self(rawValue: 1 << 0)
|
||||
static let edgeFlag = Self(rawValue: 1 << 1)
|
||||
static let fogCoord = Self(rawValue: 1 << 2)
|
||||
static let index = Self(rawValue: 1 << 3)
|
||||
static let normal = Self(rawValue: 1 << 4)
|
||||
static let secondaryColour = Self(rawValue: 1 << 5)
|
||||
static let textureCoord = Self(rawValue: 1 << 6)
|
||||
static let vertex = Self(rawValue: 1 << 7)
|
||||
|
||||
static let all: [Self] = [ .colour, .edgeFlag, .fogCoord, .index, .normal, .secondaryColour, .textureCoord, .vertex ]
|
||||
static let initial: Self = []
|
||||
|
||||
var glCap: GLenum
|
||||
{
|
||||
let capEnum = switch self
|
||||
{
|
||||
case .colour: GL_COLOR_ARRAY
|
||||
case .edgeFlag: GL_EDGE_FLAG_ARRAY
|
||||
case .fogCoord: GL_FOG_COORD_ARRAY
|
||||
case .index: GL_INDEX_ARRAY
|
||||
case .normal: GL_NORMAL_ARRAY
|
||||
case .secondaryColour: GL_SECONDARY_COLOR_ARRAY
|
||||
case .textureCoord: GL_TEXTURE_COORD_ARRAY
|
||||
case .vertex: GL_VERTEX_ARRAY
|
||||
default: fatalError()
|
||||
}
|
||||
return GLenum(capEnum)
|
||||
}
|
||||
}
|
||||
|
||||
private var _clientState: ClientState = .initial
|
||||
|
||||
private mutating func toggleClientState(_ cap: ClientState, _ en: Bool)
|
||||
{
|
||||
if en
|
||||
{
|
||||
if !_clientState.contains(cap)
|
||||
{
|
||||
glEnableClientState(cap.glCap)
|
||||
_clientState.insert(cap)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if _clientState.contains(cap)
|
||||
{
|
||||
glDisableClientState(cap.glCap)
|
||||
_clientState.remove(cap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutating func enableClient(_ caps: ClientState)
|
||||
{
|
||||
if ClientState.all.contains(caps)
|
||||
{
|
||||
toggleClientState(caps, true)
|
||||
}
|
||||
else
|
||||
{
|
||||
for i in ClientState.all
|
||||
{
|
||||
if i.intersection(caps) == i { toggleClientState(i, true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutating func disableClient(_ caps: ClientState)
|
||||
{
|
||||
if ClientState.all.contains(caps)
|
||||
{
|
||||
toggleClientState(caps, false)
|
||||
}
|
||||
else
|
||||
{
|
||||
for i in ClientState.all
|
||||
{
|
||||
if i.intersection(caps) == i { toggleClientState(i, false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _activeTexture2D = 0
|
||||
|
||||
private mutating func setActiveTexture2D(_ active: Int)
|
||||
{
|
||||
if active != _activeTexture2D
|
||||
{
|
||||
let slots = [ GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3, GL_TEXTURE4, GL_TEXTURE5, GL_TEXTURE6,
|
||||
GL_TEXTURE7, GL_TEXTURE8, GL_TEXTURE9, GL_TEXTURE10, GL_TEXTURE11, GL_TEXTURE12, GL_TEXTURE13,
|
||||
GL_TEXTURE14, GL_TEXTURE15, GL_TEXTURE16, GL_TEXTURE17, GL_TEXTURE18, GL_TEXTURE19, GL_TEXTURE20,
|
||||
GL_TEXTURE21, GL_TEXTURE22, GL_TEXTURE23, GL_TEXTURE24, GL_TEXTURE25, GL_TEXTURE26, GL_TEXTURE27,
|
||||
GL_TEXTURE28, GL_TEXTURE29, GL_TEXTURE30, GL_TEXTURE31 ]
|
||||
|
||||
glActiveTexture(GLenum(slots[active]))
|
||||
_activeTexture2D = active
|
||||
}
|
||||
}
|
||||
|
||||
static let defaultTexture: UInt32 = 0
|
||||
private var _texture2D: GLuint = GLuint(defaultTexture)
|
||||
|
||||
mutating func bindTexture2D(active: Int, texture: UInt32)
|
||||
{
|
||||
if GLuint(texture) != _texture2D
|
||||
{
|
||||
setActiveTexture2D(active)
|
||||
glBindTexture(GLenum(GL_TEXTURE_2D), GLuint(texture))
|
||||
_texture2D = GLuint(texture)
|
||||
}
|
||||
}
|
||||
|
||||
enum TexureParameter { case magFilter, minFilter, wrapS, wrapT }
|
||||
|
||||
mutating func texture2DParameter(active: Int, param: TexureParameter, int value: Int32)
|
||||
{
|
||||
setActiveTexture2D(active)
|
||||
let pname = switch param
|
||||
{
|
||||
case .magFilter: GL_TEXTURE_MAG_FILTER
|
||||
case .minFilter: GL_TEXTURE_MIN_FILTER
|
||||
case .wrapS: GL_TEXTURE_WRAP_S
|
||||
case .wrapT: GL_TEXTURE_WRAP_T
|
||||
}
|
||||
glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(pname), GLint(value))
|
||||
}
|
||||
|
||||
private var _clearDepth: GLclampd = 1
|
||||
|
||||
var clearDepth: Double
|
||||
{
|
||||
get { Double(_clearDepth) }
|
||||
set(newDepth)
|
||||
{
|
||||
if GLclampd(newDepth) != _clearDepth
|
||||
{
|
||||
glClearDepth(newDepth)
|
||||
_clearDepth = GLclampd(newDepth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _clearColour: Colour = .zero
|
||||
|
||||
mutating func clearColour(_ newColour: Colour)
|
||||
{
|
||||
if newColour != _clearColour
|
||||
{
|
||||
glClearColor(newColour.r, newColour.g, newColour.b, newColour.a)
|
||||
_clearColour = newColour
|
||||
}
|
||||
}
|
||||
|
||||
enum CullFace { case front, back, frontAndBack }
|
||||
private var _cullFace: CullFace = .back
|
||||
|
||||
var cullFace: CullFace
|
||||
{
|
||||
get { _cullFace }
|
||||
set(newMode)
|
||||
{
|
||||
if newMode != _cullFace
|
||||
{
|
||||
let modeEnum = switch newMode
|
||||
{
|
||||
case .front: GL_FRONT
|
||||
case .back: GL_BACK
|
||||
case .frontAndBack: GL_FRONT_AND_BACK
|
||||
}
|
||||
glCullFace(GLenum(modeEnum))
|
||||
_cullFace = newMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DepthFunc { case never, less, equal, lessOrEqual, greater, notEqual, greaterOrEqual, always }
|
||||
private var _depthFunc: DepthFunc = .less
|
||||
|
||||
var depthFunc: DepthFunc
|
||||
{
|
||||
get { _depthFunc }
|
||||
set(newFunc)
|
||||
{
|
||||
if newFunc != _depthFunc
|
||||
{
|
||||
let funcEnum = switch newFunc
|
||||
{
|
||||
case .never: GL_NEVER
|
||||
case .less: GL_LESS
|
||||
case .equal: GL_EQUAL
|
||||
case .lessOrEqual: GL_LEQUAL
|
||||
case .greater: GL_GREATER
|
||||
case .notEqual: GL_NOTEQUAL
|
||||
case .greaterOrEqual: GL_GEQUAL
|
||||
case .always: GL_ALWAYS
|
||||
}
|
||||
glDepthFunc(GLenum(funcEnum))
|
||||
_depthFunc = newFunc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum FogMode { case linear, exp, exp2 }
|
||||
private var _fogMode: FogMode = .exp
|
||||
|
||||
var fogMode: FogMode
|
||||
{
|
||||
get { _fogMode }
|
||||
set(newMode)
|
||||
{
|
||||
if newMode != _fogMode
|
||||
{
|
||||
let modeEnum = switch newMode
|
||||
{
|
||||
case .linear: GL_LINEAR
|
||||
case .exp: GL_EXP
|
||||
case .exp2: GL_EXP2
|
||||
}
|
||||
glFogi(GLenum(GL_FOG_MODE), modeEnum)
|
||||
_fogMode = newMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _fogDensity: Float = 1
|
||||
|
||||
var fogDensity: Float
|
||||
{
|
||||
get { _fogDensity }
|
||||
set(newDensity)
|
||||
{
|
||||
if newDensity != _fogDensity
|
||||
{
|
||||
glFogf(GLenum(GL_FOG_DENSITY), newDensity)
|
||||
_fogDensity = newDensity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _fogStart: Float = 0
|
||||
|
||||
var fogStart: Float
|
||||
{
|
||||
get { _fogStart }
|
||||
set(newStart)
|
||||
{
|
||||
if newStart != _fogStart
|
||||
{
|
||||
glFogf(GLenum(GL_FOG_START), newStart)
|
||||
_fogStart = newStart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _fogEnd: Float = 1
|
||||
|
||||
var fogEnd: Float
|
||||
{
|
||||
get { _fogEnd }
|
||||
set(newEnd)
|
||||
{
|
||||
if newEnd != _fogEnd
|
||||
{
|
||||
glFogf(GLenum(GL_FOG_END), newEnd)
|
||||
_fogEnd = newEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _fogColour: Colour = .zero
|
||||
|
||||
var fogColour: Colour
|
||||
{
|
||||
get { _fogColour }
|
||||
set(newColour)
|
||||
{
|
||||
if newColour != _fogColour
|
||||
{
|
||||
withUnsafePointer(to: newColour)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 4)
|
||||
{
|
||||
glFogfv(GLenum(GL_FOG_COLOR), $0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum FogCoordinateSource { case coordinate, fragmentDepth }
|
||||
private var _fogCoordinateSource: FogCoordinateSource = .fragmentDepth
|
||||
|
||||
var fogCoodinateSource: FogCoordinateSource
|
||||
{
|
||||
get { _fogCoordinateSource }
|
||||
set(newCoordinateSource)
|
||||
{
|
||||
if newCoordinateSource != _fogCoordinateSource
|
||||
{
|
||||
let coordinateSourceEnum = switch newCoordinateSource
|
||||
{
|
||||
case .coordinate: GL_FOG_COORD
|
||||
case .fragmentDepth: GL_FRAGMENT_DEPTH
|
||||
}
|
||||
glFogi(GLenum(GL_FOG_COORD_SRC), coordinateSourceEnum)
|
||||
_fogCoordinateSource = newCoordinateSource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Hint
|
||||
{
|
||||
case fog
|
||||
case generateMipmap
|
||||
case lineSmooth
|
||||
case perspectiveCorrection
|
||||
case pointSmooth
|
||||
case polygonSmooth
|
||||
case textureCompression
|
||||
}
|
||||
enum HintBehaviour { case fastest, nicest, dontCare }
|
||||
|
||||
func setHint(target: Hint, hint: HintBehaviour)
|
||||
{
|
||||
let target = switch target
|
||||
{
|
||||
case .fog: GL_FOG_HINT
|
||||
case .generateMipmap: GL_GENERATE_MIPMAP_HINT
|
||||
case .lineSmooth: GL_LINE_SMOOTH_HINT
|
||||
case .perspectiveCorrection: GL_PERSPECTIVE_CORRECTION_HINT
|
||||
case .pointSmooth: GL_POINT_SMOOTH_HINT
|
||||
case .polygonSmooth: GL_POLYGON_SMOOTH_HINT
|
||||
case .textureCompression: GL_TEXTURE_COMPRESSION_HINT
|
||||
}
|
||||
let mode = switch hint
|
||||
{
|
||||
case .fastest: GL_FASTEST
|
||||
case .nicest: GL_NICEST
|
||||
case .dontCare: GL_DONT_CARE
|
||||
}
|
||||
glHint(GLenum(target), GLenum(mode))
|
||||
}
|
||||
|
||||
struct LightAmbientProperty
|
||||
{
|
||||
private var _lightAmbient = [Colour](repeating: .black, count: 8)
|
||||
|
||||
subscript(index: Int) -> Colour
|
||||
{
|
||||
get { _lightAmbient[index] }
|
||||
set(newAmbient)
|
||||
{
|
||||
if newAmbient != _lightAmbient[index]
|
||||
{
|
||||
withUnsafePointer(to: newAmbient)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 4)
|
||||
{
|
||||
// glLightModelfv(GLenum(GL_LIGHT_MODEL_AMBIENT), ambientColour)
|
||||
glLightfv(GLenum(GL_LIGHT0 + Int32(index)), GLenum(GL_AMBIENT), $0)
|
||||
}
|
||||
}
|
||||
_lightAmbient[index] = newAmbient
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lightAmbient = LightAmbientProperty()
|
||||
|
||||
struct LightDiffuseProperty
|
||||
{
|
||||
private var _lightDiffuse: [Colour] = [ .white, .black, .black, .black, .black, .black, .black, .black ]
|
||||
|
||||
subscript(index: Int) -> Colour
|
||||
{
|
||||
get { _lightDiffuse[index] }
|
||||
set(newDiffuse)
|
||||
{
|
||||
if newDiffuse != _lightDiffuse[index]
|
||||
{
|
||||
withUnsafePointer(to: newDiffuse)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 4)
|
||||
{
|
||||
glLightfv(GLenum(GL_LIGHT0 + Int32(index)), GLenum(GL_DIFFUSE), $0)
|
||||
}
|
||||
}
|
||||
_lightDiffuse[index] = newDiffuse
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lightDiffuse = LightDiffuseProperty()
|
||||
|
||||
struct LightSpecularProperty
|
||||
{
|
||||
private var _lightSpecular: [Colour] = [ .white, .black, .black, .black, .black, .black, .black, .black ]
|
||||
|
||||
subscript(index: Int) -> Colour
|
||||
{
|
||||
get { _lightSpecular[index] }
|
||||
set(newSpecular)
|
||||
{
|
||||
if newSpecular != _lightSpecular[index]
|
||||
{
|
||||
withUnsafePointer(to: newSpecular)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 4)
|
||||
{
|
||||
glLightfv(GLenum(GL_LIGHT0 + Int32(index)), GLenum(GL_SPECULAR), $0)
|
||||
}
|
||||
}
|
||||
_lightSpecular[index] = newSpecular
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lightSpecular = LightSpecularProperty()
|
||||
|
||||
struct LightPositionProperty
|
||||
{
|
||||
private var _lightPosition = [Vec4f](repeating: .Z, count: 8)
|
||||
|
||||
subscript(index: Int) -> Vec4f
|
||||
{
|
||||
get { _lightPosition[index] }
|
||||
set(newPosition)
|
||||
{
|
||||
withUnsafePointer(to: newPosition)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 4)
|
||||
{
|
||||
glLightfv(GLenum(GL_LIGHT0 + Int32(index)), GLenum(GL_POSITION), $0)
|
||||
}
|
||||
}
|
||||
_lightPosition[index] = newPosition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lightPosition = LightPositionProperty()
|
||||
|
||||
//private var _lightSpotDirection = [Vec4f](repeating: .zero, count: 8)
|
||||
//private var _lightSpotExponent = [Vec4f](repeating: .zero, count: 8)
|
||||
//private var _lightSpotCutoff = [Vec4f](repeating: .zero, count: 8)
|
||||
|
||||
struct LightConstantAttenuationProperty
|
||||
{
|
||||
private var _lightConstantAttenuation = [Float](repeating: 1, count: 8)
|
||||
|
||||
subscript(index: Int) -> Float
|
||||
{
|
||||
get { _lightConstantAttenuation[index] }
|
||||
set(newConstantAttenuation)
|
||||
{
|
||||
if newConstantAttenuation != _lightConstantAttenuation[index]
|
||||
{
|
||||
glLightf(GLenum(GL_LIGHT0 + Int32(index)), GLenum(GL_CONSTANT_ATTENUATION), newConstantAttenuation)
|
||||
_lightConstantAttenuation[index] = newConstantAttenuation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lightConstantAttenuation = LightConstantAttenuationProperty()
|
||||
|
||||
struct LightLinearAttenuationProperty
|
||||
{
|
||||
private var _lightLinearAttenuation = [Float](repeating: 0, count: 8)
|
||||
|
||||
subscript(index: Int) -> Float
|
||||
{
|
||||
get { _lightLinearAttenuation[index] }
|
||||
set(newLinearAttenuation)
|
||||
{
|
||||
if newLinearAttenuation != _lightLinearAttenuation[index]
|
||||
{
|
||||
glLightf(GLenum(GL_LIGHT0 + Int32(index)), GLenum(GL_LINEAR_ATTENUATION), newLinearAttenuation)
|
||||
_lightLinearAttenuation[index] = newLinearAttenuation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lightLinearAttenuation = LightLinearAttenuationProperty()
|
||||
|
||||
struct LightQuadraticAttenuation
|
||||
{
|
||||
private var _lightQuadraticAttenuation = [Float](repeating: 0, count: 8)
|
||||
|
||||
subscript(index: Int) -> Float
|
||||
{
|
||||
get { _lightQuadraticAttenuation[index] }
|
||||
set(newQuadraticAttenuation)
|
||||
{
|
||||
if newQuadraticAttenuation != _lightQuadraticAttenuation[index]
|
||||
{
|
||||
glLightf(GLenum(GL_LIGHT0 + Int32(index)), GLenum(GL_QUADRATIC_ATTENUATION), newQuadraticAttenuation)
|
||||
_lightQuadraticAttenuation[index] = newQuadraticAttenuation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lightQuadraticAttenuation = LightQuadraticAttenuation()
|
||||
|
||||
var _lightModelAmbient = Colour(r: 0.2, g: 0.2, b: 0.2, a: 1)
|
||||
|
||||
var lightModelAmbient: Colour
|
||||
{
|
||||
get { _lightModelAmbient }
|
||||
set(newAmbient)
|
||||
{
|
||||
if newAmbient != _lightModelAmbient
|
||||
{
|
||||
|
||||
withUnsafePointer(to: newAmbient)
|
||||
{
|
||||
$0.withMemoryRebound(to: GLfloat.self, capacity: 4)
|
||||
{
|
||||
glLightModelfv(GLenum(GL_LIGHT_MODEL_AMBIENT), $0)
|
||||
}
|
||||
}
|
||||
_lightModelAmbient = newAmbient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum LightModelColourControl { case single, separateSpecular }
|
||||
var _lightModelColourControl: LightModelColourControl = .single
|
||||
|
||||
var lightModelColourControl: LightModelColourControl
|
||||
{
|
||||
get { _lightModelColourControl }
|
||||
set(newControl)
|
||||
{
|
||||
if newControl != _lightModelColourControl
|
||||
{
|
||||
let param = switch newControl
|
||||
{
|
||||
case .single: GL_SINGLE_COLOR
|
||||
case .separateSpecular: GL_SEPARATE_SPECULAR_COLOR
|
||||
}
|
||||
glLightModelf(GLenum(GL_LIGHT_MODEL_COLOR_CONTROL), GLfloat(param))
|
||||
_lightModelColourControl = newControl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _lightModelLocalViewer = false
|
||||
|
||||
var lightModelLocalViewer: Bool
|
||||
{
|
||||
get { _lightModelLocalViewer }
|
||||
set(newMode)
|
||||
{
|
||||
if newMode != _lightModelLocalViewer
|
||||
{
|
||||
glLightModeli(GLenum(GL_LIGHT_MODEL_LOCAL_VIEWER), newMode ? 1 : 0)
|
||||
_lightModelLocalViewer = newMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _lightModeTwoSide = false
|
||||
|
||||
var lightModeTwoSide: Bool
|
||||
{
|
||||
get { _lightModeTwoSide }
|
||||
set(newMode)
|
||||
{
|
||||
if newMode != _lightModeTwoSide
|
||||
{
|
||||
glLightModeli(GLenum(GL_LIGHT_MODEL_TWO_SIDE), newMode ? 1 : 0)
|
||||
_lightModeTwoSide = newMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum MatrixMode { case modelView, projection, texture, colour }
|
||||
private var _matrixMode: MatrixMode = .modelView
|
||||
|
||||
var matrixMode: MatrixMode
|
||||
{
|
||||
get { _matrixMode }
|
||||
set(newMode)
|
||||
{
|
||||
if newMode != _matrixMode
|
||||
{
|
||||
let modeEnum = switch newMode
|
||||
{
|
||||
case .modelView: GL_MODELVIEW
|
||||
case .projection: GL_PROJECTION
|
||||
case .texture: GL_TEXTURE
|
||||
case .colour: GL_COLOR
|
||||
}
|
||||
glMatrixMode(GLenum(modeEnum))
|
||||
_matrixMode = newMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static let defaultBuffer: UInt32 = 0
|
||||
|
||||
private var _arrayBuffer = GLuint(defaultBuffer)
|
||||
|
||||
var arrayBuffer: UInt32
|
||||
{
|
||||
get { UInt32(_arrayBuffer) }
|
||||
set(newBuf)
|
||||
{
|
||||
if newBuf != _arrayBuffer
|
||||
{
|
||||
_arrayBuffer = GLuint(newBuf)
|
||||
glBindBuffer(GLenum(GL_ARRAY_BUFFER), _arrayBuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _elementArrayBuffer = GLuint(defaultBuffer)
|
||||
|
||||
var elementArrayBuffer: UInt32
|
||||
{
|
||||
get { UInt32(_elementArrayBuffer) }
|
||||
set(newBuf)
|
||||
{
|
||||
if newBuf != _elementArrayBuffer
|
||||
{
|
||||
_elementArrayBuffer = GLuint(newBuf)
|
||||
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), _elementArrayBuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
134
Sources/JolkEngine/Renderer/Renderer.swift
Normal file
134
Sources/JolkEngine/Renderer/Renderer.swift
Normal file
@ -0,0 +1,134 @@
|
||||
import Foundation
|
||||
import OpenGL.GL
|
||||
|
||||
|
||||
public protocol Renderer
|
||||
{
|
||||
func create(sdlWindow: OpaquePointer) throws
|
||||
func delete()
|
||||
|
||||
func newFrame()
|
||||
func resize(width: Int32, height: Int32)
|
||||
|
||||
var clearColour: Colour { get set }
|
||||
|
||||
func setVsync(mode: VSyncMode) throws
|
||||
|
||||
func createMesh(mesh: Mesh) throws -> RenderMesh
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int) throws -> RenderTexture2D
|
||||
func createTexture(data: UnsafeRawPointer, width: Int, height: Int,
|
||||
filter: FilterMode, mipMode: MipMode) throws -> RenderTexture2D
|
||||
|
||||
func deleteMesh(_ mesh: RenderMesh)
|
||||
func deleteTexture(_ texture: RenderTexture2D)
|
||||
|
||||
func setProjection(matrix: Mat4f)
|
||||
func setView(matrix: Mat4f)
|
||||
|
||||
func setMaterial(_ mat: Material)
|
||||
|
||||
func draw(mesh: RenderMesh, model: Mat4f, environment: Environment)
|
||||
func draw(mesh: RenderMesh, environment: Environment)
|
||||
|
||||
func drawGizmos(lines: [Line])
|
||||
}
|
||||
|
||||
public enum RendererError: Error
|
||||
{
|
||||
case sdlError(message: String)
|
||||
}
|
||||
|
||||
public enum VSyncMode
|
||||
{
|
||||
case off
|
||||
case on
|
||||
case adaptive
|
||||
}
|
||||
|
||||
public enum FilterMode
|
||||
{
|
||||
case point
|
||||
case linear
|
||||
}
|
||||
|
||||
public enum MipMode
|
||||
{
|
||||
case off
|
||||
case nearest
|
||||
case linear
|
||||
}
|
||||
|
||||
public enum WrapMode
|
||||
{
|
||||
case clamping
|
||||
case clampBorder
|
||||
case clampEdge
|
||||
case mirrored
|
||||
case repeating
|
||||
}
|
||||
|
||||
public protocol RendererResource
|
||||
{
|
||||
associatedtype T: Resource
|
||||
|
||||
static var empty: Self { get }
|
||||
|
||||
var isValid: Bool { get }
|
||||
}
|
||||
|
||||
public struct RenderMesh: RendererResource
|
||||
{
|
||||
public typealias T = Mesh
|
||||
|
||||
public static var empty: RenderMesh { .init() }
|
||||
|
||||
public var isValid: Bool { _valid }
|
||||
|
||||
private let _valid: Bool;
|
||||
let vboHnd: Int, iboHnd: Int
|
||||
let subMeshes: [Mesh.SubMesh]
|
||||
|
||||
private init()
|
||||
{
|
||||
self._valid = false
|
||||
self.vboHnd = 0
|
||||
self.iboHnd = 0
|
||||
self.subMeshes = []
|
||||
}
|
||||
|
||||
init(vbo: Int, ibo: Int, subMeshes: [Mesh.SubMesh])
|
||||
{
|
||||
self._valid = true
|
||||
self.vboHnd = vbo
|
||||
self.iboHnd = ibo
|
||||
self.subMeshes = subMeshes
|
||||
}
|
||||
}
|
||||
|
||||
public struct RenderTexture2D: RendererResource
|
||||
{
|
||||
public typealias T = Texture2D
|
||||
|
||||
public static var empty: Self { .init(0) }
|
||||
|
||||
public var isValid: Bool { id != 0 }
|
||||
|
||||
let id: UInt32
|
||||
|
||||
init(_ id: UInt32)
|
||||
{
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
public struct Line
|
||||
{
|
||||
let from: Vec3f, to: Vec3f, colour: Colour
|
||||
|
||||
public init(from: Vec3f, to: Vec3f, colour: Colour)
|
||||
{
|
||||
self.from = from
|
||||
self.to = to
|
||||
self.colour = colour
|
||||
}
|
||||
}
|
31
Sources/JolkEngine/Renderer/Texture2D.swift
Normal file
31
Sources/JolkEngine/Renderer/Texture2D.swift
Normal file
@ -0,0 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
public struct Texture2D: Resource
|
||||
{
|
||||
public let id: RenderTexture2D
|
||||
public let width: Int, height: Int
|
||||
}
|
||||
|
||||
extension Texture2D
|
||||
{
|
||||
public static let empty = Self(id: .empty, width: 0, height: 0)
|
||||
}
|
||||
|
||||
|
||||
public struct Texture2DParameters: ContentLoaderParametersProtocol
|
||||
{
|
||||
public typealias T = Texture2D
|
||||
|
||||
var minFilter: FilterMode, magFilter: FilterMode
|
||||
var wrapMode: WrapMode
|
||||
var mipMode: MipMode
|
||||
|
||||
public init(minFilter: FilterMode = .linear, magFilter: FilterMode = .linear, wrapMode: WrapMode = .repeating, mipMode: MipMode = .off)
|
||||
{
|
||||
self.minFilter = minFilter
|
||||
self.magFilter = magFilter
|
||||
self.wrapMode = wrapMode
|
||||
self.mipMode = mipMode
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user