init dump
This commit is contained in:
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
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user