init dump

This commit is contained in:
2024-05-05 17:01:56 +10:00
commit 608cf45822
53 changed files with 19224 additions and 0 deletions

View 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))
}
}

View 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
}
}

View 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
}
}
}

View 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)
}
}
}
}

View 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
}
}

View 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
}
}