Skip to content

Commit

Permalink
Big refactor - try to break things up
Browse files Browse the repository at this point in the history
  • Loading branch information
tanelso2 committed Apr 23, 2024
1 parent 6791e4f commit e53540f
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 129 deletions.
5 changes: 5 additions & 0 deletions src/jsMain/kotlin/LightSource.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class LightSource(
var pos: Array<Float>,
var ambientColor: Array<Float>,
var diffuseColor: Array<Float>,
var specularColor: Array<Float>)
158 changes: 29 additions & 129 deletions src/jsMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,87 +1,53 @@

import com.tanelso2.glmatrix.Mat3
import com.tanelso2.glmatrix.Mat4
import com.tanelso2.glmatrix.Vec3
import org.khronos.webgl.Float32Array
import org.khronos.webgl.WebGLProgram
import org.khronos.webgl.WebGLShader
import org.w3c.dom.HTMLCanvasElement
import kotlinx.browser.document
import kotlinx.browser.window
import kotlin.math.PI
import org.khronos.webgl.WebGLRenderingContext as GL

class WebGLWrapper {
val canvas: HTMLCanvasElement = document.getElementById("webglCanvas") as HTMLCanvasElement
val webgl: GL = canvas.getContext("webgl") as GL
val shaderProgram: WebGLProgram = webgl.createProgram() ?: throw IllegalStateException("Could not initialize shader program")
class Main {
private val canvas: HTMLCanvasElement = document.getElementById("webglCanvas") as HTMLCanvasElement
private val webgl: GL = canvas.getContext("webgl") as GL
private val shaderProgram: ShaderProgram = ShaderProgram(webgl)

val scaleFactor by HTMLInputProperty("scaleInput")
private val scaleFactor by HTMLInputProperty("scaleInput")

val lightPos: Array<Float> by ArrayOfInputsProperty(
private val lightPos: Array<Float> by ArrayOfInputsProperty(
"lightPosX",
"lightPosY",
"lightPosZ"
)

val shininess by HTMLInputProperty("shininessInput")
private val shininess by HTMLInputProperty("shininessInput")

val rotationSpeed by HTMLInputProperty("rotationSpeedInput")

init {
webgl.enable(GL.DEPTH_TEST)
}

val windowWidth = 800
val windowHeight = 600
private val rotationSpeed by HTMLInputProperty("rotationSpeedInput")

private val vertexShaderLocation = "vertex-shader.glsl"
private val fragmentShaderLocation = "frag-shader.glsl"
private val objFileLocation = "teapot.obj"
val resourceList = arrayOf(
private val resourceList = arrayOf(
fragmentShaderLocation,
vertexShaderLocation,
objFileLocation
)

val resourceLoader = ResourceLoader(*resourceList)
val objFileLoader: ObjLoader by lazy { ObjLoader(resourceLoader[objFileLocation]!!) }
private val resourceLoader = ResourceLoader(*resourceList)
private val teapot: TeapotObj by lazy { TeapotObj(webgl, resourceLoader[objFileLocation]!!, shaderProgram)}
private val light: LightSource = LightSource(
lightPos,
arrayOf(0.1f, 0.1f, 0.1f),
arrayOf(0.4f, 0.4f, 0.0f),
arrayOf(1.0f, 1.0f, 1.0f)
)

init {
webgl.enable(GL.DEPTH_TEST)
}

fun setup() {
if(resourceLoader.allLoaded()) {
compileShaderProgram()

setupAttribute("aVertexPosition", objFileLoader.getVertices())
setupAttribute("aVertexNormal", objFileLoader.getVertexNormals())

println("Vertex array size = ${objFileLoader.getVertices().length}")
println("Vertex normal array size = ${objFileLoader.getVertexNormals().length}")


val ambientColor = arrayOf(
0.1f,
0.1f,
0.1f
)
val diffuseColor = arrayOf(
0.4f,
0.4f,
0.0f
)
val specularColor = arrayOf(
1.0f,
1.0f,
1.0f
)

setupUniformVec3Float(lightPos, "uLightPos")
setupUniformVec3Float(ambientColor, "uAmbientColor")
setupUniformVec3Float(diffuseColor, "uDiffuseColor")
setupUniformVec3Float(specularColor, "uSpecularColor")

val faceIndexBuffer = webgl.createBuffer()
webgl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, faceIndexBuffer)
webgl.bufferData(GL.ELEMENT_ARRAY_BUFFER, objFileLoader.getFaces(), GL.STATIC_DRAW)
if (resourceLoader.allLoaded()) {
val vertexShaderSource = resourceLoader[vertexShaderLocation]!!
val fragmentShaderSource = resourceLoader[fragmentShaderLocation]!!
shaderProgram.compile(vertexShaderSource, fragmentShaderSource)

window.requestAnimationFrame { render() }
} else {
Expand All @@ -90,81 +56,15 @@ class WebGLWrapper {
}
}

private fun setupUniformVec3Float(lightPos: Array<Float>, name: String) {
val lightPositionLoc = webgl.getUniformLocation(shaderProgram, name)
webgl.uniform3fv(lightPositionLoc, lightPos)
}

private fun setupAttribute(attributeName: String, attributeArray: Float32Array) {
val vertexPositionAttribute = webgl.getAttribLocation(shaderProgram, attributeName)
webgl.enableVertexAttribArray(vertexPositionAttribute)
val vertexPositionBuffer = webgl.createBuffer()
webgl.bindBuffer(GL.ARRAY_BUFFER, vertexPositionBuffer)
webgl.bufferData(GL.ARRAY_BUFFER, attributeArray, GL.STATIC_DRAW)
webgl.vertexAttribPointer(vertexPositionAttribute, 3, GL.FLOAT, false, 0, 0)
}

private fun compileShaderProgram() {
val vertexShaderSource = resourceLoader[vertexShaderLocation]!!
val vertexShader = getVertexShader(vertexShaderSource)
val fragmentShaderSource = resourceLoader[fragmentShaderLocation]!!
val fragmentShader = getFragmentShader(fragmentShaderSource)
webgl.attachShader(shaderProgram, vertexShader)
webgl.attachShader(shaderProgram, fragmentShader)
webgl.linkProgram(shaderProgram)
println(webgl.getProgramInfoLog(shaderProgram))
webgl.useProgram(shaderProgram)
}

fun getFragmentShader(source: String): WebGLShader = getShader(source, GL.FRAGMENT_SHADER)
fun getVertexShader(source: String): WebGLShader = getShader(source, GL.VERTEX_SHADER)


private fun getShader(shaderSource: String, shaderType: Int): WebGLShader {
val shader = webgl.createShader(shaderType)
webgl.shaderSource(shader, shaderSource)
webgl.compileShader(shader)
if(!(webgl.getShaderParameter(shader, GL.COMPILE_STATUS) as Boolean)) {
println(webgl.getShaderInfoLog(shader))
}
return shader ?: throw IllegalStateException("Shader is null!")
}

var rotation = 0.0

fun draw() {
setupUniformVec3Float(lightPos, "uLightPos")

val shininessUniform = webgl.getUniformLocation(shaderProgram, "shininess")
webgl.uniform1f(shininessUniform, shininess.toFloat())

val pMatrix = Mat4.perspective(PI / 3, 16.0 / 9.0, 0.1, 60.0)

val vMatrix = Mat4.lookAt(Vec3(20,20,20), Vec3(0,0,0), Vec3(0,0,1))

val mMatrix = Mat4()
mMatrix.scale(scaleFactor)
//setupAttributes()
light.pos = lightPos
rotation += rotationSpeed
mMatrix.rotateX(PI / 2)
mMatrix.rotateY(rotation)

val nMatrix = Mat3.fromMat4(vMatrix * mMatrix)
nMatrix.transpose()
nMatrix.invert()

val nMatrixUniform = webgl.getUniformLocation(shaderProgram, "uNMatrix")
webgl.uniformMatrix3fv(nMatrixUniform, false, nMatrix.array)

val pMatrixUniform = webgl.getUniformLocation(shaderProgram, "uPMatrix")
webgl.uniformMatrix4fv(pMatrixUniform, false, pMatrix.array)

val vMatrixUniform = webgl.getUniformLocation(shaderProgram, "uVMatrix")
webgl.uniformMatrix4fv(vMatrixUniform, false, vMatrix.array)

val mMatrixUniform = webgl.getUniformLocation(shaderProgram, "uMMatrix")
webgl.uniformMatrix4fv(mMatrixUniform, false, mMatrix.array)

webgl.drawElements(GL.TRIANGLES, objFileLoader.getNumFaces() * 3, GL.UNSIGNED_SHORT, 0)
teapot.draw(scaleFactor, rotation, shininess, light)
}

fun render() {
Expand All @@ -177,7 +77,7 @@ class WebGLWrapper {

fun main() {
document.body?.onload = {
val wrapper = WebGLWrapper()
val wrapper = Main()
window.requestAnimationFrame { wrapper.setup() }
}
}
53 changes: 53 additions & 0 deletions src/jsMain/kotlin/ShaderProgram.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import com.tanelso2.glmatrix.Mat3
import com.tanelso2.glmatrix.Mat4
import org.khronos.webgl.Float32Array
import org.khronos.webgl.WebGLProgram
import org.khronos.webgl.WebGLRenderingContext
import org.khronos.webgl.WebGLUniformLocation

class ShaderProgram(private val webgl: WebGLRenderingContext) {
val program: WebGLProgram =
webgl.createProgram() ?: throw IllegalStateException("Failed to create shader program")

fun useProgram() {
webgl.useProgram(program)
}

fun compile(vertexShaderSource: String, fragmentShaderSource: String) {
val vertexShader = webgl.getVertexShader(vertexShaderSource)
val fragmentShader = webgl.getFragmentShader(fragmentShaderSource)
webgl.attachShader(program, vertexShader)
webgl.attachShader(program, fragmentShader)
webgl.linkProgram(program)
useProgram()
}

fun setupAttribute(name: String, array: Float32Array) {
val attribute = webgl.getAttribLocation(program, name)
webgl.enableVertexAttribArray(attribute)
val buffer = webgl.createBuffer()
webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, buffer)
webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, array, WebGLRenderingContext.STATIC_DRAW)
webgl.vertexAttribPointer(attribute, 3, WebGLRenderingContext.FLOAT, false, 0, 0)
}

fun getUniformLocation(name: String) : WebGLUniformLocation =
webgl.getUniformLocation(program, name)
?: throw IllegalArgumentException("Could not find uniform named '$name'")

fun setupUniformMat4(name: String, value: Mat4, transpose: Boolean = false) {
webgl.uniformMatrix4fv(getUniformLocation(name), transpose, value.array)
}

fun setupUniformMat3(name: String, value: Mat3, transpose: Boolean = false) {
webgl.uniformMatrix3fv(getUniformLocation(name), transpose, value.array)
}

fun setupUniformFloat(name: String, value: Float) {
webgl.uniform1f(getUniformLocation(name), value)
}

fun setupUniformVec3Float(name: String, value: Array<Float>) {
webgl.uniform3fv(getUniformLocation(name), value)
}
}
70 changes: 70 additions & 0 deletions src/jsMain/kotlin/TeapotObj.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import com.tanelso2.glmatrix.Mat3
import com.tanelso2.glmatrix.Mat4
import com.tanelso2.glmatrix.Vec3
import org.khronos.webgl.WebGLRenderingContext
import kotlin.math.PI

class TeapotObj(private val webgl: WebGLRenderingContext, private val objFileSource: String, private val shaderProgram: ShaderProgram) {
private val objLoader = ObjLoader(objFileSource)
private val faces = objLoader.getFaces()
private val vertices = objLoader.getVertices()
private val vertexNormals = objLoader.getVertexNormals()
private val numFaces = objLoader.getNumFaces()

private val faceIndexBuffer = webgl.createBuffer()
private val pMatrix = Mat4.perspective(PI / 3, 16.0 / 9.0, 0.1, 60.0)
private val vMatrix = Mat4.lookAt(Vec3(20, 20, 20), Vec3(0, 0, 0), Vec3(0, 0, 1))

private fun setupFaceBuffer() {
webgl.bindBuffer(WebGLRenderingContext.ELEMENT_ARRAY_BUFFER, faceIndexBuffer)
webgl.bufferData(WebGLRenderingContext.ELEMENT_ARRAY_BUFFER, faces, WebGLRenderingContext.STATIC_DRAW)
}

private fun setupAttributes() {
shaderProgram.apply {
setupAttribute("aVertexPosition", vertices)
setupAttribute("aVertexNormal", vertexNormals)
}
}

private fun setupMatrices(scaleFactor: Double, rotation: Double) {
val mMatrix = Mat4()
mMatrix.scale(scaleFactor)
mMatrix.rotateX(PI / 2)
mMatrix.rotateY(rotation)

val nMatrix = Mat3.fromMat4(vMatrix * mMatrix)
nMatrix.transpose()
nMatrix.invert()

shaderProgram.apply {
setupUniformMat3("uNMatrix", nMatrix)
setupUniformMat4("uPMatrix", pMatrix)
setupUniformMat4("uVMatrix", vMatrix)
setupUniformMat4("uMMatrix", mMatrix)
}
}

private fun setupLight(light: LightSource) {
shaderProgram.apply {
setupUniformVec3Float("uLightPos", light.pos)
setupUniformVec3Float("uAmbientColor", light.ambientColor)
setupUniformVec3Float("uDiffuseColor", light.diffuseColor)
setupUniformVec3Float("uSpecularColor", light.specularColor)
}
}

fun draw(scaleFactor: Double, rotation: Double, shininess: Double, light: LightSource) {
shaderProgram.useProgram()
setupAttributes()
setupFaceBuffer()
setupMatrices(scaleFactor, rotation)
setupShininess(shininess)
setupLight(light)
webgl.drawElements(WebGLRenderingContext.TRIANGLES, numFaces * 3, WebGLRenderingContext.UNSIGNED_SHORT, 0)
}

private fun setupShininess(shininess: Double) {
shaderProgram.setupUniformFloat("shininess", shininess.toFloat())
}
}
15 changes: 15 additions & 0 deletions src/jsMain/kotlin/WebGLHelpers.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import org.khronos.webgl.WebGLRenderingContext as GL
import org.khronos.webgl.WebGLShader

fun GL.getShader(source: String, shaderType: Int): WebGLShader {
val shader = this.createShader(shaderType)
this.shaderSource(shader, source)
this.compileShader(shader)
if(!(this.getShaderParameter(shader, GL.COMPILE_STATUS) as Boolean)) {
println(this.getShaderInfoLog(shader))
}
return shader ?: throw IllegalStateException("Shader is null!")
}

fun GL.getFragmentShader(source: String): WebGLShader = this.getShader(source, GL.FRAGMENT_SHADER)
fun GL.getVertexShader(source: String): WebGLShader = this.getShader(source, GL.VERTEX_SHADER)

0 comments on commit e53540f

Please sign in to comment.