diff --git a/src/Julia.kt b/src/Julia.kt new file mode 100644 index 0000000..05993e2 --- /dev/null +++ b/src/Julia.kt @@ -0,0 +1,140 @@ +import com.persesgames.shader.ShaderProgram +import com.persesgames.shader.VertextAttributeInfo +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext +import kotlin.browser.window + +/** + * User: rnentjes + * Date: 21-5-16 + * Time: 17:06 + */ + +private val vertexShader = """ + attribute vec2 a_position; + + uniform vec4 u_viewWindow; + + varying vec2 v_coord; + + void main(void) { + v_coord = a_position * u_viewWindow.zw + u_viewWindow.xy; + + gl_Position = vec4(a_position, 0.0, 1.0); + } +""" + +private val fragmentShader = """ + precision mediump float; + + uniform vec2 u_julia; + uniform float u_iteratorOffset; + + varying vec2 v_coord; + + void main(void) { + float xx = v_coord.x; + float yy = v_coord.y; + float xt = 0.0; + + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0); + + for (int iteration = 0; iteration < 1000; iteration++) { + if (xx*xx + yy*yy > 4.0) { + float mu = u_iteratorOffset + float(iteration) + 1.0 - log(log(xx*xx + yy*yy)) / log(2.0); + + float it = mod(mu * 7.0, 768.0); + + float red = min(it, 255.0) / 255.0; + float green = max(0.0, min(it, 511.0) - 256.0) / 255.0; + float blue = max(0.0, min(it, 767.0) - 512.0) / 255.0; + + gl_FragColor = vec4( red, green, blue, 1.0); + break; + } + xt = xx*xx - yy*yy + u_julia.x; + yy = 2.0*xx*yy + u_julia.y; + xx = xt; + } + } +""" + +class JuliaData { + var juliaX: Float = 0f + var juliaY: Float = 0f + + var offsetX: Float = 0f + var offsetY: Float = 0f + var scaleX: Float = 1f + var scaleY: Float = 1f + + var iteratorOffset: Float = 0f +} + +class Julia(val html: HTMLElements) { + val webgl = html.webgl + val shaderProgram: ShaderProgram + val data: JuliaData = JuliaData() + val attribBuffer: WebGLBuffer + val vertices: Float32Array + val start = Date().getTime() - 20000 + + init { + val array: Array = arrayOf( + -1f,-1f, + 1f,-1f, + 1f, 1f, + 1f, 1f, + -1f, 1f, + -1f,-1f + ) + + vertices = Float32Array(array.size) + vertices.set(array, 0) + + val setter = { program: ShaderProgram, data: JuliaData -> + program.setUniform2f("u_julia", data.juliaX, data.juliaY) + program.setUniform4f("u_viewWindow", data.offsetX, data.offsetY, data.scaleX, data.scaleY) + program.setUniform1f("u_iteratorOffset", data.iteratorOffset) + } + + val vainfo = arrayOf( + VertextAttributeInfo("a_position", 2) + ) + + shaderProgram = ShaderProgram(webgl, WebGLRenderingContext.TRIANGLES, vertexShader, fragmentShader, vainfo, setter) + + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun render() { + html.resize() + + webgl.clearColor(1f, 1f, 1f, 1f) + webgl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + var time = (start - (Date().getTime())) / 1000.0 + + data.juliaX = -0.391f + (Math.sin(time / 31) / 10f).toFloat() + data.juliaY = -0.587f + (Math.cos(time / 23.07) / 10f).toFloat() + + data.scaleX = 1.3f - Math.sin(time / 10.0).toFloat() * 0.9f + data.scaleY = 1.3f - Math.sin(time / 10.0).toFloat() * 0.9f + + data.iteratorOffset = 0f //time.toFloat() * 10f + + shaderProgram.begin(attribBuffer, data) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, vertices, WebGLRenderingContext.DYNAMIC_DRAW); + webgl.drawArrays(shaderProgram.drawType, 0, 6) + + shaderProgram.end() + + window.requestAnimationFrame { + render() + } + } +} diff --git a/src/Main.kt b/src/Main.kt new file mode 100644 index 0000000..2b2907d --- /dev/null +++ b/src/Main.kt @@ -0,0 +1,197 @@ +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window + +/** + * User: rnentjes + * Date: 21-5-16 + * Time: 17:06 + */ + +class HTMLElements { + val container: HTMLElement + val canvas: HTMLCanvasElement + var webgl: WebGLRenderingContext + + var windowWidth = 0 + var windowHeight = 0 + + init { + container = document.createElement("div") as HTMLElement + + canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: relative;") + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 2000px; height: 1000px;" ) + + document.body!!.appendChild(container) + container.appendChild(canvas) + + webgl = canvas.getContext("webgl") as WebGLRenderingContext + } + + fun resize() { + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (this.windowWidth != windowWidth || + this.windowHeight != windowHeight) { + + this.windowWidth = windowWidth + this.windowHeight = windowHeight + canvas.setAttribute("width", "${windowWidth}px") + canvas.setAttribute("height", "${windowHeight}px") + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 5; width: ${windowWidth}px; height: ${windowHeight}px;") + webgl.viewport(0, 0, windowWidth, windowHeight) + } + } + + fun getColor(mu: Double) { + var clr1 = mu.toInt() + var t2 = mu - clr1 + var t1 = 1 - t2 + clr1 = clr1 % 768 + +// int clr1 = (int)mu; +// double t2 = mu - clr1; +// double t1 = 1 - t2; +// clr1 = clr1 % Colors.Count; +// int clr2 = (clr1 + 1) % Colors.Count; +// +// byte r = (byte)(Colors[clr1].R * t1 + Colors[clr2].R * t2); +// byte g = (byte)(Colors[clr1].G * t1 + Colors[clr2].G * t2); +// byte b = (byte)(Colors[clr1].B * t1 + Colors[clr2].B * t2); +// +// return Color.FromArgb(255, r, g, b); + } + + fun drawMandel() { + /* + For each pixel (Px, Py) on the screen, do: + { + x0 = scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1)) + y0 = scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1)) + x = 0.0 + y = 0.0 + iteration = 0 + max_iteration = 1000 + while (x*x + y*y < 2*2 AND iteration < max_iteration) { + xtemp = x*x - y*y + x0 + y = 2*x*y + y0 + x = xtemp + iteration = iteration + 1 + } + color = palette[iteration] + plot(Px, Py, color) + }*/ + + var xs: Double + var ys: Double + var xx: Double + var yy: Double + var xt: Double + var iteration: Int + val max_iteration: Int = 767 + val halfWindowHeight = windowHeight / 2 + var red: Int + var green: Int + var blue: Int + var fillStyle: String + var mu: Double + + for (x in 0..windowWidth) { + for (y in 0..windowHeight) { + xs = (4.0 / windowWidth.toFloat()) * x - 2.0 + ys = 4.0 - ((4.0 / windowHeight) * y) - 2.0 + + xx = 0.0 + yy = 0.0 + iteration = 0 + while(xx*xx + yy*yy < 4 && iteration < max_iteration) { + xt = xx*xx - yy*yy + xs + yy = 2*xx*yy + ys + xx = xt + iteration++ + } + if (iteration == max_iteration) { + fillStyle = "rgb(0, 0, 0)" + } else { + //mu = iteration + 1 - Math.log(Math.log(xx*xx + yy*yy)) / Math.log(2.0); + iteration = (iteration * 13) % 768 + red = Math.min(iteration, 255) + green = Math.max(0, Math.min(iteration, 511) - 256) + blue = Math.max(0, Math.min(iteration, 767) - 512) + fillStyle = "rgb($red, $green, $blue)" + } + + //canvas2d.fillStyle = fillStyle + //canvas2d.fillRect(x.toDouble(), y.toDouble(), 1.0, 1.0) + } + } + + } + + fun drawJulia(xc: Double, yc: Double) { + var xx: Double + var yy: Double + var xt: Double + var iteration: Int + val max_iteration: Int = 511 + var red: Int + var green: Int + var blue: Int + var fillStyle: String + + for (x in 0..windowWidth) { + for (y in 0..windowHeight) { + xx = (4.0 / windowWidth.toFloat()) * x - 2.0 + yy = 4.0 - ((4.0 / windowHeight) * y) - 2.0 + + iteration = 0 + while(xx*xx + yy*yy < 4 && iteration < max_iteration) { + xt = xx*xx - yy*yy + xc + yy = 2*xx*yy + yc + xx = xt + iteration++ + } + if (iteration == max_iteration) { + fillStyle = "rgb(0, 0, 0)" + } else { + //mu = iteration + 1 - Math.log(Math.log(xx*xx + yy*yy)) / Math.log(2.0); + iteration = (iteration * 31) % 512 + red = Math.min(iteration, 255) + green = Math.max(0, Math.min(iteration, 511) - 256) + blue = Math.max(0, Math.min(iteration, 767) - 512) + fillStyle = "rgb($red, $green, $blue)" + } + + //canvas2d.fillStyle = fillStyle + //canvas2d.fillRect(x.toDouble(), y.toDouble(), 1.0, 1.0) + } + } + + } + + fun render() { + webgl.clearColor(1f, 1f, 1f, 1f) + webgl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + + +// window.requestAnimationFrame { +// render() +// } + } +} + +fun main(args: Array) { + val html = HTMLElements() + + val mandelBrot = MandelBrot(html) + val julia = Julia(html) + + julia.render() + //html.drawJulia(-0.493, -0.587) +} \ No newline at end of file diff --git a/src/MandelBrot.kt b/src/MandelBrot.kt index 22232b9..7199cf3 100644 --- a/src/MandelBrot.kt +++ b/src/MandelBrot.kt @@ -1,7 +1,8 @@ -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document +import com.persesgames.shader.ShaderProgram +import com.persesgames.shader.VertextAttributeInfo +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext import kotlin.browser.window /** @@ -10,35 +11,87 @@ import kotlin.browser.window * Time: 17:06 */ -class HTMLElements { - val container: HTMLElement - val canvas: HTMLCanvasElement - val canvas2d: CanvasRenderingContext2D +private val vertexShader = """ + attribute vec2 a_position; - var windowWidth = window.innerWidth.toInt() - var windowHeight = window.innerHeight.toInt() + uniform vec4 u_viewWindow; + + varying vec2 v_coord; + + void main(void) { + v_coord = a_position + u_viewWindow.xy; + + gl_Position = vec4(a_position, 0.0, 1.0); + } +""" + +private val fragmentShader = """ + precision mediump float; + + varying vec2 v_coord; + + void main(void) { + float xx = 0.0; + float yy = 0.0; + float xt = 0.0; + + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0); + + for (int iteration = 0; iteration < 1000; iteration++) { + if (xx*xx + yy*yy > 4.0) { + float it = mod(float(iteration) * 13.0, 768.0); + float red = min(it, 255.0) / 255.0; + float green = max(0.0, min(it, 511.0) - 256.0); + float blue = max(0.0, min(it, 767.0) - 512.0); + gl_FragColor = vec4( red, green, blue, 1.0); + break; + } + xt = xx*xx - yy*yy + v_coord.x; + yy = 2.0*xx*yy + v_coord.y; + xx = xt; + } + } +""" + +class ShaderData { + var offsetX: Float = 0f + var offsetY: Float = 0f +} + +class MandelBrot(val html: HTMLElements) { + val webgl = html.webgl + val shaderProgram: ShaderProgram + val data: ShaderData = ShaderData() + val attribBuffer: WebGLBuffer + val vertices: Float32Array + val start = Date().getTime() init { - container = document.createElement("div") as HTMLElement + val array: Array = arrayOf( + -1f,-1f, + 1f,-1f, + 1f, 1f, + 1f, 1f, + -1f, 1f, + -1f,-1f + ) - canvas = document.createElement("canvas") as HTMLCanvasElement + vertices = Float32Array(array.size) + vertices.set(array, 0) - container.setAttribute("style", "position: relative;") - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 2000px; height: 1000px;" ) + val setter = { program: ShaderProgram, data: ShaderData -> + program.setUniform4f("u_viewWindow", data.offsetX, data.offsetY, 0f, 0f) + } - document.body!!.appendChild(container) - container.appendChild(canvas) + val vainfo = arrayOf( + VertextAttributeInfo("a_position", 2) + ) - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } + shaderProgram = ShaderProgram(webgl, WebGLRenderingContext.TRIANGLES, vertexShader, fragmentShader, vainfo, setter) - fun resize() { - windowWidth = window.innerWidth.toInt() - windowHeight = window.innerHeight.toInt() - canvas.setAttribute("width", "${windowWidth}px") - canvas.setAttribute("height", "${windowHeight}px") - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 5; width: ${windowWidth}px; height: ${windowHeight}px;" ) + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); } fun getColor(mu: Double) { @@ -60,8 +113,8 @@ class HTMLElements { // return Color.FromArgb(255, r, g, b); } - fun drawMandel() { - /* +/* fun drawMandel() { + *//* For each pixel (Px, Py) on the screen, do: { x0 = scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1)) @@ -78,7 +131,7 @@ class HTMLElements { } color = palette[iteration] plot(Px, Py, color) - }*/ + }*//* var xs: Double var ys: Double @@ -94,11 +147,10 @@ class HTMLElements { var fillStyle: String var mu: Double - println("Window width: $windowWidth, height: $windowHeight, half: $halfWindowHeight") for (x in 0..windowWidth) { - for (y in 0..halfWindowHeight) { + for (y in 0..windowHeight) { xs = (4.0 / windowWidth.toFloat()) * x - 2.0 - ys = 2.0 - ((2.0 / halfWindowHeight) * y) + ys = 4.0 - ((4.0 / windowHeight) * y) - 2.0 xx = 0.0 yy = 0.0 @@ -112,8 +164,7 @@ class HTMLElements { if (iteration == max_iteration) { fillStyle = "rgb(0, 0, 0)" } else { - mu = iteration + 1 - - Math.log(Math.log(xx*xx + yy*yy)) / Math.log(2.0); + //mu = iteration + 1 - Math.log(Math.log(xx*xx + yy*yy)) / Math.log(2.0); iteration = (iteration * 13) % 768 red = Math.min(iteration, 255) green = Math.max(0, Math.min(iteration, 511) - 256) @@ -121,19 +172,74 @@ class HTMLElements { fillStyle = "rgb($red, $green, $blue)" } - canvas2d.fillStyle = fillStyle - canvas2d.fillRect(x.toDouble(), y.toDouble(), 1.0, 1.0) - canvas2d.fillRect(x.toDouble(), windowHeight - y.toDouble(), 1.0, 1.0) + //canvas2d.fillStyle = fillStyle + //canvas2d.fillRect(x.toDouble(), y.toDouble(), 1.0, 1.0) } } + }*/ + +/* fun drawJulia(xc: Double, yc: Double) { + var xx: Double + var yy: Double + var xt: Double + var iteration: Int + val max_iteration: Int = 511 + var red: Int + var green: Int + var blue: Int + var fillStyle: String + + for (x in 0..windowWidth) { + for (y in 0..windowHeight) { + xx = (4.0 / windowWidth.toFloat()) * x - 2.0 + yy = 4.0 - ((4.0 / windowHeight) * y) - 2.0 + + iteration = 0 + while(xx*xx + yy*yy < 4 && iteration < max_iteration) { + xt = xx*xx - yy*yy + xc + yy = 2*xx*yy + yc + xx = xt + iteration++ + } + if (iteration == max_iteration) { + fillStyle = "rgb(0, 0, 0)" + } else { + //mu = iteration + 1 - Math.log(Math.log(xx*xx + yy*yy)) / Math.log(2.0); + iteration = (iteration * 31) % 512 + red = Math.min(iteration, 255) + green = Math.max(0, Math.min(iteration, 511) - 256) + blue = Math.max(0, Math.min(iteration, 767) - 512) + fillStyle = "rgb($red, $green, $blue)" + } + + //canvas2d.fillStyle = fillStyle + //canvas2d.fillRect(x.toDouble(), y.toDouble(), 1.0, 1.0) + } + } + + }*/ + + fun render() { + html.resize() + + webgl.clearColor(1f, 1f, 1f, 1f) + webgl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + var time = (start - (Date().getTime())) / 1000.0 + + data.offsetX = Math.sin(time).toFloat() + data.offsetY = Math.cos(time).toFloat() + + shaderProgram.begin(attribBuffer, data) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, vertices, WebGLRenderingContext.DYNAMIC_DRAW); + webgl.drawArrays(shaderProgram.drawType, 0, 6) + + shaderProgram.end() + + window.requestAnimationFrame { + render() + } } } - -fun main(args: Array) { - val html = HTMLElements() - - html.resize() - - html.drawMandel() -} \ No newline at end of file diff --git a/src/ShaderProgram.kt b/src/ShaderProgram.kt new file mode 100644 index 0000000..1ab8738 --- /dev/null +++ b/src/ShaderProgram.kt @@ -0,0 +1,116 @@ +package com.persesgames.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgram( + val webgl: WebGLRenderingContext, + val drawType: Int, + vertexShaderSource: String, + fragmentShaderSource: String, + val vainfo: Array, + val setter: (program: ShaderProgram, data: T) -> Unit) { + + var shaderProgram: WebGLProgram + var vertex: WebGLShader + var fragment: WebGLShader + + var verticesBlockSize = 0 + var drawLength = 0 + + init { + vertex = compileShader(vertexShaderSource, WebGLRenderingContext.VERTEX_SHADER) + fragment = compileShader(fragmentShaderSource, WebGLRenderingContext.FRAGMENT_SHADER) + + shaderProgram = webgl.createProgram() ?: throw IllegalStateException("Unable to request shader program from webgl context!") + webgl.attachShader(shaderProgram, vertex) + webgl.attachShader(shaderProgram, fragment) + webgl.linkProgram(shaderProgram) + + if (webgl.getProgramParameter(shaderProgram, WebGLRenderingContext.LINK_STATUS) == false) { + println(webgl.getProgramInfoLog(shaderProgram)) + throw IllegalStateException("Unable to compile shader program!") + } + + webgl.useProgram(shaderProgram) + + this.verticesBlockSize = 0; + + // set attribute locations... + for (info in vainfo.iterator()) { + info.location = webgl.getAttribLocation(shaderProgram, info.locationName) + info.offset = verticesBlockSize; + + verticesBlockSize += info.numElements; + println("attrib: ${info.locationName}, info.location: ${info.location}, info.offset: ${info.offset}"); + } + + when(drawType) { + WebGLRenderingContext.TRIANGLES -> { + drawLength = verticesBlockSize * 3 + } + else -> { + drawLength = verticesBlockSize + } + } + + println("verticesBlockSize $verticesBlockSize"); + + println("ShaderProgram constructor done"); + } + + private fun compileShader(source: String, type: Int): WebGLShader { + val result: WebGLShader + + result = webgl.createShader(type) ?: throw IllegalStateException("Unable to request shader from webgl context!") + webgl.shaderSource(result, source) + webgl.compileShader(result) + + if (webgl.getShaderParameter(result, WebGLRenderingContext.COMPILE_STATUS) == false) { + throw IllegalStateException("Unable to compile shader!\n${source}\n\n${webgl.getShaderInfoLog(result)}") + } + + return result; + } + + fun begin(attribBuffer: WebGLBuffer, userdata: T) { + webgl.useProgram(shaderProgram); + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + + // set attribute locations... + for (info in vainfo.iterator()) { + webgl.enableVertexAttribArray(info.location); + webgl.vertexAttribPointer(info.location, info.numElements, WebGLRenderingContext.FLOAT, false, verticesBlockSize * 4, info.offset * 4); + } + + setter(this, userdata) + } + + fun end() { + for (info in vainfo.iterator()) { + webgl.disableVertexAttribArray(info.location); + } + webgl.useProgram(null) + } + + fun getAttribLocation(location: String) = webgl.getAttribLocation(shaderProgram, location); + + fun getUniformLocation(location: String) = webgl.getUniformLocation(shaderProgram, location); + + fun setUniform1f(location: String, value: Float) { webgl.uniform1f(getUniformLocation(location), value); } + fun setUniform2f(location: String, v1: Float, v2: Float) { webgl.uniform2f(getUniformLocation(location), v1, v2); } + fun setUniform4f(location: String, v1: Float, v2: Float, v3: Float, v4: Float) { webgl.uniform4f(getUniformLocation(location), v1, v2, v3, v4); } + fun setUniform1i(location: String, value: Int) { webgl.uniform1i(getUniformLocation(location), value); } + fun setUniformMatrix4fv(location: String, value: Float32Array) { webgl.uniformMatrix4fv(getUniformLocation(location), false, value); } + +} diff --git a/web/js/generated/mandelbrot.js b/web/js/generated/mandelbrot.js index a6b46bd..adac934 100644 --- a/web/js/generated/mandelbrot.js +++ b/web/js/generated/mandelbrot.js @@ -1,24 +1,194 @@ (function (Kotlin) { 'use strict'; - var _ = Kotlin.defineRootPackage(null, /** @lends _ */ { + var _ = Kotlin.defineRootPackage(function () { + this.vertexShader_rfnujb$ = '\n attribute vec2 a_position;\n\n uniform vec4 u_viewWindow;\n\n varying vec2 v_coord;\n\n void main(void) {\n v_coord = a_position + u_viewWindow.xy;\n\n gl_Position = vec4(a_position, 0.0, 1.0);\n }\n'; + this.fragmentShader_jzx1nv$ = '\n precision mediump float;\n\n varying vec2 v_coord;\n\n void main(void) {\n float xx = 0.0;\n float yy = 0.0;\n float xt = 0.0;\n\n gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0);\n\n for (int iteration = 0; iteration < 1000; iteration++) {\n if (xx*xx + yy*yy > 4.0) {\n float it = mod(float(iteration) * 13.0, 768.0);\n float red = min(it, 255.0) / 255.0;\n float green = max(0.0, min(it, 511.0) - 256.0);\n float blue = max(0.0, min(it, 767.0) - 512.0);\n gl_FragColor = vec4( red, green, blue, 1.0);\n break;\n }\n xt = xx*xx - yy*yy + v_coord.x;\n yy = 2.0*xx*yy + v_coord.y;\n xx = xt;\n }\n }\n'; + this.vertexShader_rfnujb$ = '\n attribute vec2 a_position;\n\n uniform vec4 u_viewWindow;\n\n varying vec2 v_coord;\n\n void main(void) {\n v_coord = a_position * u_viewWindow.zw + u_viewWindow.xy;\n\n gl_Position = vec4(a_position, 0.0, 1.0);\n }\n'; + this.fragmentShader_jzx1nv$ = '\n precision mediump float;\n\n uniform vec2 u_julia;\n uniform float u_iteratorOffset;\n\n varying vec2 v_coord;\n\n void main(void) {\n float xx = v_coord.x;\n float yy = v_coord.y;\n float xt = 0.0;\n\n gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0);\n\n for (int iteration = 0; iteration < 1000; iteration++) {\n if (xx*xx + yy*yy > 4.0) {\n float mu = u_iteratorOffset + float(iteration) + 1.0 - log(log(xx*xx + yy*yy)) / log(2.0);\n\n float it = mod(mu * 7.0, 768.0);\n\n float red = min(it, 255.0) / 255.0;\n float green = max(0.0, min(it, 511.0) - 256.0) / 255.0;\n float blue = max(0.0, min(it, 767.0) - 512.0) / 255.0;\n\n gl_FragColor = vec4( red, green, blue, 1.0);\n break;\n }\n xt = xx*xx - yy*yy + u_julia.x;\n yy = 2.0*xx*yy + u_julia.y;\n xx = xt;\n }\n }\n'; + }, /** @lends _ */ { + com: Kotlin.definePackage(null, /** @lends _.com */ { + persesgames: Kotlin.definePackage(null, /** @lends _.com.persesgames */ { + shader: Kotlin.definePackage(null, /** @lends _.com.persesgames.shader */ { + VertextAttributeInfo: Kotlin.createClass(null, function (locationName, numElements) { + this.locationName = locationName; + this.numElements = numElements; + this.location = 0; + this.offset = 0; + }), + ShaderProgram: Kotlin.createClass(null, function (webgl, drawType, vertexShaderSource, fragmentShaderSource, vainfo, setter) { + var tmp$0, tmp$1, tmp$2; + this.webgl = webgl; + this.drawType = drawType; + this.vainfo = vainfo; + this.setter = setter; + this.verticesBlockSize = 0; + this.drawLength = 0; + this.vertex = this.compileShader(vertexShaderSource, WebGLRenderingContext.VERTEX_SHADER); + this.fragment = this.compileShader(fragmentShaderSource, WebGLRenderingContext.FRAGMENT_SHADER); + tmp$0 = this.webgl.createProgram(); + if (tmp$0 == null) + throw new Kotlin.IllegalStateException('Unable to request shader program from webgl context!'); + this.shaderProgram = tmp$0; + this.webgl.attachShader(this.shaderProgram, this.vertex); + this.webgl.attachShader(this.shaderProgram, this.fragment); + this.webgl.linkProgram(this.shaderProgram); + if (Kotlin.equals(this.webgl.getProgramParameter(this.shaderProgram, WebGLRenderingContext.LINK_STATUS), false)) { + Kotlin.println(this.webgl.getProgramInfoLog(this.shaderProgram)); + throw new Kotlin.IllegalStateException('Unable to compile shader program!'); + } + this.webgl.useProgram(this.shaderProgram); + this.verticesBlockSize = 0; + tmp$1 = Kotlin.modules['stdlib'].kotlin.collections.iterator_123wqf$(Kotlin.arrayIterator(this.vainfo)); + while (tmp$1.hasNext()) { + var info = tmp$1.next(); + info.location = this.webgl.getAttribLocation(this.shaderProgram, info.locationName); + info.offset = this.verticesBlockSize; + this.verticesBlockSize += info.numElements; + Kotlin.println('attrib: ' + info.locationName + ', info.location: ' + info.location + ', info.offset: ' + info.offset); + } + tmp$2 = this.drawType; + if (tmp$2 === WebGLRenderingContext.TRIANGLES) + this.drawLength = this.verticesBlockSize * 3; + else { + this.drawLength = this.verticesBlockSize; + } + Kotlin.println('verticesBlockSize ' + this.verticesBlockSize); + Kotlin.println('ShaderProgram constructor done'); + }, /** @lends _.com.persesgames.shader.ShaderProgram.prototype */ { + compileShader: function (source, type) { + var tmp$0; + var result; + tmp$0 = this.webgl.createShader(type); + if (tmp$0 == null) + throw new Kotlin.IllegalStateException('Unable to request shader from webgl context!'); + result = tmp$0; + this.webgl.shaderSource(result, source); + this.webgl.compileShader(result); + if (Kotlin.equals(this.webgl.getShaderParameter(result, WebGLRenderingContext.COMPILE_STATUS), false)) { + throw new Kotlin.IllegalStateException('Unable to compile shader!' + '\n' + source + '\n' + '\n' + Kotlin.toString(this.webgl.getShaderInfoLog(result))); + } + return result; + }, + begin_t0l48p$: function (attribBuffer, userdata) { + var tmp$0; + this.webgl.useProgram(this.shaderProgram); + this.webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + tmp$0 = Kotlin.modules['stdlib'].kotlin.collections.iterator_123wqf$(Kotlin.arrayIterator(this.vainfo)); + while (tmp$0.hasNext()) { + var info = tmp$0.next(); + this.webgl.enableVertexAttribArray(info.location); + this.webgl.vertexAttribPointer(info.location, info.numElements, WebGLRenderingContext.FLOAT, false, this.verticesBlockSize * 4, info.offset * 4); + } + this.setter(this, userdata); + }, + end: function () { + var tmp$0; + tmp$0 = Kotlin.modules['stdlib'].kotlin.collections.iterator_123wqf$(Kotlin.arrayIterator(this.vainfo)); + while (tmp$0.hasNext()) { + var info = tmp$0.next(); + this.webgl.disableVertexAttribArray(info.location); + } + this.webgl.useProgram(null); + }, + getAttribLocation_61zpoe$: function (location) { + return this.webgl.getAttribLocation(this.shaderProgram, location); + }, + getUniformLocation_61zpoe$: function (location) { + return this.webgl.getUniformLocation(this.shaderProgram, location); + }, + setUniform1f_9sobi5$: function (location, value) { + this.webgl.uniform1f(this.getUniformLocation_61zpoe$(location), value); + }, + setUniform2f_9xt0da$: function (location, v1, v2) { + this.webgl.uniform2f(this.getUniformLocation_61zpoe$(location), v1, v2); + }, + setUniform4f_kjn4ou$: function (location, v1, v2, v3, v4) { + this.webgl.uniform4f(this.getUniformLocation_61zpoe$(location), v1, v2, v3, v4); + }, + setUniform1i_bm4lxs$: function (location, value) { + this.webgl.uniform1i(this.getUniformLocation_61zpoe$(location), value); + }, + setUniformMatrix4fv_pphpxd$: function (location, value) { + this.webgl.uniformMatrix4fv(this.getUniformLocation_61zpoe$(location), false, value); + } + }) + }) + }) + }), + ShaderData: Kotlin.createClass(null, function () { + this.offsetX = 0.0; + this.offsetY = 0.0; + }), + MandelBrot: Kotlin.createClass(null, function (html) { + var tmp$0; + this.html = html; + this.webgl = this.html.webgl; + this.data = new _.ShaderData(); + this.start = (new Date()).getTime(); + var array = [-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0]; + this.vertices = new Float32Array(array.length); + this.vertices.set(array, 0); + var setter = _.MandelBrot.MandelBrot$f; + var vainfo = [new _.com.persesgames.shader.VertextAttributeInfo('a_position', 2)]; + this.shaderProgram = new _.com.persesgames.shader.ShaderProgram(this.webgl, WebGLRenderingContext.TRIANGLES, _.vertexShader_rfnujb$, _.fragmentShader_jzx1nv$, vainfo, setter); + tmp$0 = this.webgl.createBuffer(); + if (tmp$0 == null) + throw new Kotlin.IllegalStateException('Unable to create webgl buffer!'); + this.attribBuffer = tmp$0; + this.webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, this.attribBuffer); + }, /** @lends _.MandelBrot.prototype */ { + getColor_14dthe$: function (mu) { + var clr1 = mu | 0; + var t2 = mu - clr1; + var t1 = 1 - t2; + clr1 = clr1 % 768; + }, + render: function () { + this.html.resize(); + this.webgl.clearColor(1.0, 1.0, 1.0, 1.0); + this.webgl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT); + var time = (this.start - (new Date()).getTime()) / 1000.0; + this.data.offsetX = Math.sin(time); + this.data.offsetY = Math.cos(time); + this.shaderProgram.begin_t0l48p$(this.attribBuffer, this.data); + this.webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, this.vertices, WebGLRenderingContext.DYNAMIC_DRAW); + this.webgl.drawArrays(this.shaderProgram.drawType, 0, 6); + this.shaderProgram.end(); + window.requestAnimationFrame(_.MandelBrot.render$f(this)); + } + }, /** @lends _.MandelBrot */ { + render$f: function (this$MandelBrot) { + return function (it) { + this$MandelBrot.render(); + }; + }, + MandelBrot$f: function (program, data) { + program.setUniform4f_kjn4ou$('u_viewWindow', data.offsetX, data.offsetY, 0.0, 0.0); + } + }), HTMLElements: Kotlin.createClass(null, function () { var tmp$0, tmp$1, tmp$2, tmp$3; - this.windowWidth = window.innerWidth | 0; - this.windowHeight = window.innerHeight | 0; + this.windowWidth = 0; + this.windowHeight = 0; this.container = Kotlin.isType(tmp$0 = document.createElement('div'), HTMLElement) ? tmp$0 : Kotlin.throwCCE(); this.canvas = Kotlin.isType(tmp$1 = document.createElement('canvas'), HTMLCanvasElement) ? tmp$1 : Kotlin.throwCCE(); this.container.setAttribute('style', 'position: relative;'); this.canvas.setAttribute('style', 'position: absolute; left: 0px; top: 0px; z-index: 10; width: 2000px; height: 1000px;'); ((tmp$2 = document.body) != null ? tmp$2 : Kotlin.throwNPE()).appendChild(this.container); this.container.appendChild(this.canvas); - this.canvas2d = Kotlin.isType(tmp$3 = this.canvas.getContext('2d'), CanvasRenderingContext2D) ? tmp$3 : Kotlin.throwCCE(); + this.webgl = Kotlin.isType(tmp$3 = this.canvas.getContext('webgl'), WebGLRenderingContext) ? tmp$3 : Kotlin.throwCCE(); }, /** @lends _.HTMLElements.prototype */ { resize: function () { - this.windowWidth = window.innerWidth | 0; - this.windowHeight = window.innerHeight | 0; - this.canvas.setAttribute('width', this.windowWidth.toString() + 'px'); - this.canvas.setAttribute('height', this.windowHeight.toString() + 'px'); - this.canvas.setAttribute('style', 'position: absolute; left: 0px; top: 0px; z-index: 5; width: ' + this.windowWidth + 'px; height: ' + this.windowHeight + 'px;'); + var windowWidth = window.innerWidth | 0; + var windowHeight = window.innerHeight | 0; + if (this.windowWidth !== windowWidth || this.windowHeight !== windowHeight) { + this.windowWidth = windowWidth; + this.windowHeight = windowHeight; + this.canvas.setAttribute('width', windowWidth.toString() + 'px'); + this.canvas.setAttribute('height', windowHeight.toString() + 'px'); + this.canvas.setAttribute('style', 'position: absolute; left: 0px; top: 0px; z-index: 5; width: ' + windowWidth + 'px; height: ' + windowHeight + 'px;'); + this.webgl.viewport(0, 0, windowWidth, windowHeight); + } }, getColor_14dthe$: function (mu) { var clr1 = mu | 0; @@ -41,13 +211,12 @@ var blue; var fillStyle; var mu; - Kotlin.println('Window width: ' + this.windowWidth + ', height: ' + this.windowHeight + ', half: ' + halfWindowHeight); tmp$0 = this.windowWidth; for (var x = 0; x <= tmp$0; x++) { - tmp$1 = halfWindowHeight; + tmp$1 = this.windowHeight; for (var y = 0; y <= tmp$1; y++) { xs = 4.0 / this.windowWidth * x - 2.0; - ys = 2.0 - 2.0 / halfWindowHeight * y; + ys = 4.0 - 4.0 / this.windowHeight * y - 2.0; xx = 0.0; yy = 0.0; iteration = 0; @@ -61,25 +230,118 @@ fillStyle = 'rgb(0, 0, 0)'; } else { - mu = iteration + 1 - Math.log(Math.log(xx * xx + yy * yy)) / Math.log(2.0); iteration = iteration * 13 % 768; red = Math.min(iteration, 255); green = Math.max(0, Math.min(iteration, 511) - 256); blue = Math.max(0, Math.min(iteration, 767) - 512); fillStyle = 'rgb(' + red + ', ' + green + ', ' + blue + ')'; } - this.canvas2d.fillStyle = fillStyle; - this.canvas2d.fillRect(x, y, 1.0, 1.0); - this.canvas2d.fillRect(x, this.windowHeight - y, 1.0, 1.0); } } + }, + drawJulia_lu1900$: function (xc, yc) { + var tmp$0, tmp$1; + var xx; + var yy; + var xt; + var iteration; + var max_iteration = 511; + var red; + var green; + var blue; + var fillStyle; + tmp$0 = this.windowWidth; + for (var x = 0; x <= tmp$0; x++) { + tmp$1 = this.windowHeight; + for (var y = 0; y <= tmp$1; y++) { + xx = 4.0 / this.windowWidth * x - 2.0; + yy = 4.0 - 4.0 / this.windowHeight * y - 2.0; + iteration = 0; + while (xx * xx + yy * yy < 4 && iteration < max_iteration) { + xt = xx * xx - yy * yy + xc; + yy = 2 * xx * yy + yc; + xx = xt; + iteration++; + } + if (iteration === max_iteration) { + fillStyle = 'rgb(0, 0, 0)'; + } + else { + iteration = iteration * 31 % 512; + red = Math.min(iteration, 255); + green = Math.max(0, Math.min(iteration, 511) - 256); + blue = Math.max(0, Math.min(iteration, 767) - 512); + fillStyle = 'rgb(' + red + ', ' + green + ', ' + blue + ')'; + } + } + } + }, + render: function () { + this.webgl.clearColor(1.0, 1.0, 1.0, 1.0); + this.webgl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT); } }), main_kand9s$: function (args) { var html = new _.HTMLElements(); - html.resize(); - html.drawMandel(); - } + var mandelBrot = new _.MandelBrot(html); + var julia = new _.Julia(html); + julia.render(); + }, + JuliaData: Kotlin.createClass(null, function () { + this.juliaX = 0.0; + this.juliaY = 0.0; + this.offsetX = 0.0; + this.offsetY = 0.0; + this.scaleX = 1.0; + this.scaleY = 1.0; + this.iteratorOffset = 0.0; + }), + Julia: Kotlin.createClass(null, function (html) { + var tmp$0; + this.html = html; + this.webgl = this.html.webgl; + this.data = new _.JuliaData(); + this.start = (new Date()).getTime() - 20000; + var array = [-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0]; + this.vertices = new Float32Array(array.length); + this.vertices.set(array, 0); + var setter = _.Julia.Julia$f; + var vainfo = [new _.com.persesgames.shader.VertextAttributeInfo('a_position', 2)]; + this.shaderProgram = new _.com.persesgames.shader.ShaderProgram(this.webgl, WebGLRenderingContext.TRIANGLES, _.vertexShader_rfnujb$, _.fragmentShader_jzx1nv$, vainfo, setter); + tmp$0 = this.webgl.createBuffer(); + if (tmp$0 == null) + throw new Kotlin.IllegalStateException('Unable to create webgl buffer!'); + this.attribBuffer = tmp$0; + this.webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, this.attribBuffer); + }, /** @lends _.Julia.prototype */ { + render: function () { + this.html.resize(); + this.webgl.clearColor(1.0, 1.0, 1.0, 1.0); + this.webgl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT); + var time = (this.start - (new Date()).getTime()) / 1000.0; + this.data.juliaX = -0.39100000262260437 + Math.sin(time / 31) / 10.0; + this.data.juliaY = -0.5870000123977661 + Math.cos(time / 23.07) / 10.0; + this.data.scaleX = 1.2999999523162842 - Math.sin(time / 10.0) * 0.8999999761581421; + this.data.scaleY = 1.2999999523162842 - Math.sin(time / 10.0) * 0.8999999761581421; + this.data.iteratorOffset = 0.0; + this.shaderProgram.begin_t0l48p$(this.attribBuffer, this.data); + this.webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, this.vertices, WebGLRenderingContext.DYNAMIC_DRAW); + this.webgl.drawArrays(this.shaderProgram.drawType, 0, 6); + this.shaderProgram.end(); + window.requestAnimationFrame(_.Julia.render$f(this)); + } + }, /** @lends _.Julia */ { + render$f: function (this$Julia) { + return function (it) { + this$Julia.render(); + }; + }, + Julia$f: function (program, data) { + program.setUniform2f_9xt0da$('u_julia', data.juliaX, data.juliaY); + program.setUniform4f_kjn4ou$('u_viewWindow', data.offsetX, data.offsetY, data.scaleX, data.scaleY); + program.setUniform1f_9sobi5$('u_iteratorOffset', data.iteratorOffset); + } + }) }); Kotlin.defineModule('mandelbrot', _); _.main_kand9s$([]);