commit 92603d8fafcdc81d9f5b26474bda1b0b3020c0bd Author: Andrew Golovashevich Date: Thu Mar 27 23:30:16 2025 +0300 Compilable WebGL program diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87fc1b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/.idea/ +/gradle/ +/.gradle/ +/build/ +*.class +*.jar +/out/ +/gradlew* +/.kotlin/ +*.js \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..dc6a373 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,28 @@ +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig + +plugins { + kotlin("multiplatform") version "2.1.10" +} + +repositories { + mavenCentral() +} + +kotlin { + js { + binaries.executable() + browser { + commonWebpackConfig { + outputFileName = "computer-graphics-1.js" + } + } + } + + sourceSets { + jsMain { + dependencies { + implementation(kotlin("stdlib")) + } + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..11a0682 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "cg-js" \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/main.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/main.kt new file mode 100644 index 0000000..a3afb06 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/main.kt @@ -0,0 +1,86 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1 + +import kotlinx.browser.document +import kotlinx.browser.window +import org.khronos.webgl.Float32Array +import org.khronos.webgl.Int32Array +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.HTMLCanvasElement + +fun main() { + val canvas = document.getElementById("canvas")!! as HTMLCanvasElement + + val gl: dynamic = canvas.getContext("webgl2")!! /*as WebGLRenderingContext*/ + + val modelVertexesBuffer = gl.createBuffer() + val textureVertexesBuffer = gl.createBuffer() + val textureTypesBuffer = gl.createBuffer() + + gl.bindBuffer(gl.ARRAY_BUFFER, modelVertexesBuffer) + gl.bufferData( + gl.ARRAY_BUFFER, + Float32Array(model.modelVertexes.toTypedArray()), + gl.STATIC_DRAW + ) + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 3 * 4, 0) + gl.enableVertexAttribArray(0) + + gl.bindBuffer(gl.ARRAY_BUFFER, textureVertexesBuffer) + gl.bufferData( + gl.ARRAY_BUFFER, + Float32Array(model.textureVertexes.toTypedArray()), + gl.STATIC_DRAW + ) + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 2 * 4, 0) + gl.enableVertexAttribArray(1) + + gl.bindBuffer(gl.ARRAY_BUFFER, textureTypesBuffer) + gl.bufferData( + gl.ARRAY_BUFFER, + Int32Array(model.textureTypes.map { t -> t.ordinal }.toTypedArray()), + gl.STATIC_DRAW + ) + gl.vertexAttribIPointer(2, 2, gl.INT, false, 1 * 4, 0) + gl.enableVertexAttribArray(2) + + val vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertexShaderSource) + gl.compileShader(vertexShader) + println("vertex shader compilation status: ${gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)}") + println("vertex shader compilation log: ${gl.getShaderInfoLog(vertexShader)}") + + val fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) + gl.shaderSource(fragmentShader, fragmentShaderSource) + gl.compileShader(fragmentShader) + println("fragment shader compilation status: ${gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)}") + println("fragment shader compilation log: ${gl.getShaderInfoLog(fragmentShader)}") + + + val shaderProgram = gl.createProgram() + gl.attachShader(shaderProgram, vertexShader) + gl.attachShader(shaderProgram, fragmentShader) + gl.linkProgram(shaderProgram) + println("program compilation log: ${gl.getProgramInfoLog(shaderProgram)}") + + gl.useProgram(shaderProgram) + gl.clearColor(0f, 0f, 0f, 1f); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, model.textureTypes.size / 3) + + window.onkeydown = { keyEvent -> + when (keyEvent.code) { + "ArrowDown" -> { + + gl.clearColor(0f, 0f, 0f, 1f); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, model.textureTypes.size / 3) + } + + "ArrowUp" -> {} + "ArrowLeft" -> {} + "ArrowRight" -> {} + } + } +} + + diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model.kt new file mode 100644 index 0000000..7a8f155 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model.kt @@ -0,0 +1,130 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1 + +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.sin +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.ModelTriangles +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Point2F +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.Polygon +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.TextureType + +private fun rot_n(degrees: Int, length: Int): Point2F { + val radians = degrees.toLong().toDouble() * PI / 180; + return Point2F( + x = cos(radians).times(length).toFloat(), + y = sin(radians).times(length).toFloat() + ) +} + + +val model = ModelTriangles.fromPolygons( + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)), + Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)), + Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(-1f, -1f, 1f, rot_n(60, 2)), + Polygon.PolygonVertex(-1f, 1f, 1f, rot_n(0, 2)), + Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(-1f, -1f, 1f, rot_n(60, 2)), + Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)), + Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(-1f, -1f, 1f, rot_n(60, 2)), + Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)), + Polygon.PolygonVertex(1f, -1f, 1f, rot_n(120, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(1f, -1f, -1f, rot_n(180, 2)), + Polygon.PolygonVertex(1f, 1f, -1f, rot_n(240, 2)), + Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(1f, -1f, -1f, rot_n(180, 2)), + Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)), + Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(1f, -1f, -1f, rot_n(180, 2)), + Polygon.PolygonVertex(1f, 0f, 1f, rot_n(120, 1)), + Polygon.PolygonVertex(1f, -1f, 1f, rot_n(120, 2)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(-1f, 1f, -1f, rot_n(300, 2)), + Polygon.PolygonVertex(1f, 1f, -1f, rot_n(240, 2)), + Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(-1f, 1f, -1f, rot_n(300, 2)), + Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)), + Polygon.PolygonVertex(1f, 1f, 0f, rot_n(240, 1)) + ), + + Polygon( + TextureType.HSV, + Polygon.PolygonVertex(-1f, 1f, -1f, rot_n(300, 2)), + Polygon.PolygonVertex(0f, 1f, 1f, rot_n(0, 1)), + Polygon.PolygonVertex(-1f, 1f, 1f, rot_n(0, 2)) + ), + Polygon( + TextureType.WHITE_TRANSPARENCY, + Polygon.PolygonVertex(-1f, -1f, 1f, 0f, 0f), + Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 1f), + Polygon.PolygonVertex(-1f, 1f, 1f, 1f, 0f) + ), + + Polygon( + TextureType.WHITE_TRANSPARENCY, + Polygon.PolygonVertex(-1f, 1f, -1f, 1f, 1f), + Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 1f), + Polygon.PolygonVertex(-1f, 1f, 1f, 1f, 0f) + ), + + Polygon( + TextureType.WHITE_TRANSPARENCY, + Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 0f), + Polygon.PolygonVertex(-1f, 1f, -1f, 0f, 1f), + Polygon.PolygonVertex(1f, 1f, -1f, 1f, 1f) + ), + + Polygon( + TextureType.WHITE_TRANSPARENCY, + Polygon.PolygonVertex(-1f, -1f, -1f, 0f, 0f), + Polygon.PolygonVertex(1f, -1f, -1f, 1f, 0f), + Polygon.PolygonVertex(1f, 1f, -1f, 1f, 1f) + ), + + Polygon( + TextureType.CHESS, + Polygon.PolygonVertex(1f, -1f, 1f, 0f, 0f), + Polygon.PolygonVertex(-1f, -1f, 1f, 0f, 1f), + Polygon.PolygonVertex(1f, -1f, -1f, 1f, 0f) + ), + Polygon( + TextureType.CHESS, + Polygon.PolygonVertex(-1f, -1f, -1f, 1f, 1f), + Polygon.PolygonVertex(-1f, -1f, 1f, 0f, 1f), + Polygon.PolygonVertex(1f, -1f, -1f, 1f, 0f) + ), +) \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/ModelTriangles.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/ModelTriangles.kt new file mode 100644 index 0000000..5ebf993 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/ModelTriangles.kt @@ -0,0 +1,50 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder + + +class ModelTriangles( + val modelVertexes: FloatArray, + val textureVertexes: FloatArray, + val textureTypes: Array +) { + companion object { + private fun MutableCollection.addAll(vararg elems: T) { + this.addAll(elems) + } + + fun fromPolygons(vararg polygons: Polygon): ModelTriangles { + val modelVertexes = ArrayList() + val textureVertexes = ArrayList() + val textureTypes = ArrayList() + + polygons@ for (polygon in polygons) { + val it = polygon.vertexes.iterator() + if (!it.hasNext()) continue@polygons + val root = it.next() + if (!it.hasNext()) continue@polygons + var prev = it.next() + for (curr in it) { + modelVertexes.addAll( + root.modelVertex.x, root.modelVertex.y, root.modelVertex.z, + prev.modelVertex.x, prev.modelVertex.y, prev.modelVertex.z, + curr.modelVertex.x, curr.modelVertex.y, curr.modelVertex.z, + ) + textureVertexes.addAll( + root.textureVertex.x, root.textureVertex.y, + prev.textureVertex.x, prev.textureVertex.y, + curr.textureVertex.x, curr.textureVertex.y, + ) + textureTypes.addAll( + polygon.textureType, polygon.textureType, polygon.textureType + ) + + prev = curr + } + } + return ModelTriangles( + modelVertexes = modelVertexes.toTypedArray().toFloatArray(), + textureVertexes = textureVertexes.toTypedArray().toFloatArray(), + textureTypes = textureTypes.toTypedArray() + ) + } + } +} \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point2F.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point2F.kt new file mode 100644 index 0000000..2127ddf --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point2F.kt @@ -0,0 +1,12 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder + +class Point2F( + val x: Float, + val y: Float, +) { + operator fun component1(): Float = this.x + operator fun component2(): Float = this.y + + override fun toString(): String = + "" +} \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point3F.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point3F.kt new file mode 100644 index 0000000..f5c1105 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Point3F.kt @@ -0,0 +1,14 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder + +class Point3F( + val x: Float, + val y: Float, + val z: Float, +) { + operator fun component1(): Float = this.x + operator fun component2(): Float = this.y + operator fun component3(): Float = this.z + + override fun toString(): String = + "" +} \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Polygon.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Polygon.kt new file mode 100644 index 0000000..3d33a79 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/Polygon.kt @@ -0,0 +1,29 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder + +class Polygon( + val textureType: TextureType, + vararg val vertexes: PolygonVertex +) { + init { + require(this.vertexes.size >= 3) { "Polygon should has at least 3 vertexes" } + } + + class PolygonVertex( + val modelVertex: Point3F, + val textureVertex: Point2F + ) { + operator fun component1() = this.modelVertex + operator fun component2() = this.textureVertex + + constructor(mx: Float, my: Float, mz: Float, tx: Float, ty: Float) : + this(Point3F(mx, my, mz), Point2F(tx, ty)) + + constructor(m: Point3F, tx: Float, ty: Float) : + this(m, Point2F(tx, ty)) + + constructor(mx: Float, my: Float, mz: Float, t: Point2F) : + this(Point3F(mx, my, mz), t) + } + + +} \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/TextureType.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/TextureType.kt new file mode 100644 index 0000000..df32c67 --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/model_builder/TextureType.kt @@ -0,0 +1,7 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1.model_builder + +enum class TextureType { + CHESS, + HSV, + WHITE_TRANSPARENCY, +} \ No newline at end of file diff --git a/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/shaders.kt b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/shaders.kt new file mode 100644 index 0000000..e33f69a --- /dev/null +++ b/src/jsMain/kotlin/ru/landgrafhomyak/bgtu/computer_graphics1/shaders.kt @@ -0,0 +1,47 @@ +package ru.landgrafhomyak.bgtu.computer_graphics1 + +import ru.landgrafhomyak.bgtu.computer_graphics1.model_builder.TextureType + +internal val vertexShaderSource = """ + #version 300 es + + uniform mat4 transform; + layout(location = 0) in vec3 vertex_pos; + layout(location = 1) in vec2 texture_pos; + layout(location = 2) in int texture_type; + out vec2 texture_pos_v2f; + flat out int texture_type_v2f; + + void main() { + gl_Position = vec4(vertex_pos.xyz, 1) * transform; + texture_pos_v2f = texture_pos; + texture_type_v2f = texture_type; + } +""".trimIndent() + +internal val fragmentShaderSource = """ + #version 300 es + + precision mediump float; + in vec2 texture_pos_v2f; + flat in int texture_type_v2f; + out vec4 color; + + void main() { + switch (texture_type_v2f) { + case ${TextureType.WHITE_TRANSPARENCY.ordinal}: + color = vec4(255, 255, 255, 127); + break; + case ${TextureType.CHESS.ordinal}: + if (int( round(floor(texture_pos_v2f.x / 0.1)) + round(floor(texture_pos_v2f.y / 0.1)) ) % 2 == 0) { + color = vec4(0, 0, 0, 255); + } else { + color = vec4(255, 255, 255, 255); + } + break; + case ${TextureType.HSV.ordinal}: + default: + color = vec4(0, 0, 0, 0); + } + } +""".trimIndent() \ No newline at end of file diff --git a/src/jsMain/resources/index.html b/src/jsMain/resources/index.html new file mode 100644 index 0000000..98bd8cb --- /dev/null +++ b/src/jsMain/resources/index.html @@ -0,0 +1,12 @@ + + + + + Title + + + + + + + \ No newline at end of file