diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt new file mode 100644 index 0000000..36356bd --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt @@ -0,0 +1,67 @@ +package games.perses.shader + +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext + +/** + * User: rnentjes + * Date: 14-5-16 + * Time: 11:57 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgramMesh( + val shaderProgram: ShaderProgram +) { + val webgl = shaderProgram.webgl + val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) + var currentIndex: Int = 0 + val attribBuffer: WebGLBuffer + var counter = 0 + + init { + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun queue(vararg vertices: Float) { + queueArray(vertices as Array) + } + + fun queueArray(vertices: Array) { + data.set(vertices, currentIndex) + currentIndex += vertices.size + + if (bufferFull()) { + //println("Skipped draw call, to many values!") + currentIndex = 0 + } + } + + fun remaining() = data.length - currentIndex + + fun bufferFull() = currentIndex == data.length + + fun render(userdata: T) { + counter++ + if (currentIndex > 0) { + if (currentIndex % shaderProgram.verticesBlockSize != 0) { + throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") + } + + shaderProgram.begin(attribBuffer, userdata) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) + webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) + currentIndex = 0 + + shaderProgram.end() + } + } +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt new file mode 100644 index 0000000..36356bd --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt @@ -0,0 +1,67 @@ +package games.perses.shader + +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext + +/** + * User: rnentjes + * Date: 14-5-16 + * Time: 11:57 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgramMesh( + val shaderProgram: ShaderProgram +) { + val webgl = shaderProgram.webgl + val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) + var currentIndex: Int = 0 + val attribBuffer: WebGLBuffer + var counter = 0 + + init { + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun queue(vararg vertices: Float) { + queueArray(vertices as Array) + } + + fun queueArray(vertices: Array) { + data.set(vertices, currentIndex) + currentIndex += vertices.size + + if (bufferFull()) { + //println("Skipped draw call, to many values!") + currentIndex = 0 + } + } + + fun remaining() = data.length - currentIndex + + fun bufferFull() = currentIndex == data.length + + fun render(userdata: T) { + counter++ + if (currentIndex > 0) { + if (currentIndex % shaderProgram.verticesBlockSize != 0) { + throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") + } + + shaderProgram.begin(attribBuffer, userdata) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) + webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) + currentIndex = 0 + + shaderProgram.end() + } + } +} diff --git a/src/main/kotlin/games/perses/sound/Music.kt b/src/main/kotlin/games/perses/sound/Music.kt new file mode 100644 index 0000000..90bac23 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Music.kt @@ -0,0 +1,47 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 13:02 + */ + +object Music { + val playing: MutableSet = HashSet() + + fun load(url: String): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + + return audio + } + + fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + audio.volume = volume + audio.play() + + audio.onended = { + if (looping) { + audio.currentTime = 0.0 + audio.play() + } else { + //println("REMOVING: $audio") + audio.remove() + playing.remove(audio) + } + } + + return audio + } + + fun stopAll() { + + } +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt new file mode 100644 index 0000000..36356bd --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt @@ -0,0 +1,67 @@ +package games.perses.shader + +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext + +/** + * User: rnentjes + * Date: 14-5-16 + * Time: 11:57 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgramMesh( + val shaderProgram: ShaderProgram +) { + val webgl = shaderProgram.webgl + val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) + var currentIndex: Int = 0 + val attribBuffer: WebGLBuffer + var counter = 0 + + init { + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun queue(vararg vertices: Float) { + queueArray(vertices as Array) + } + + fun queueArray(vertices: Array) { + data.set(vertices, currentIndex) + currentIndex += vertices.size + + if (bufferFull()) { + //println("Skipped draw call, to many values!") + currentIndex = 0 + } + } + + fun remaining() = data.length - currentIndex + + fun bufferFull() = currentIndex == data.length + + fun render(userdata: T) { + counter++ + if (currentIndex > 0) { + if (currentIndex % shaderProgram.verticesBlockSize != 0) { + throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") + } + + shaderProgram.begin(attribBuffer, userdata) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) + webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) + currentIndex = 0 + + shaderProgram.end() + } + } +} diff --git a/src/main/kotlin/games/perses/sound/Music.kt b/src/main/kotlin/games/perses/sound/Music.kt new file mode 100644 index 0000000..90bac23 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Music.kt @@ -0,0 +1,47 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 13:02 + */ + +object Music { + val playing: MutableSet = HashSet() + + fun load(url: String): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + + return audio + } + + fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + audio.volume = volume + audio.play() + + audio.onended = { + if (looping) { + audio.currentTime = 0.0 + audio.play() + } else { + //println("REMOVING: $audio") + audio.remove() + playing.remove(audio) + } + } + + return audio + } + + fun stopAll() { + + } +} diff --git a/src/main/kotlin/games/perses/sound/Sounds.kt b/src/main/kotlin/games/perses/sound/Sounds.kt new file mode 100644 index 0000000..806fd54 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Sounds.kt @@ -0,0 +1,61 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:34 + */ + +class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { + var channels: Array + var nextChannel: Int = 0 + + init { + //println("CREATING: $name") + channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) + + for (audio in channels) { + audio.src = url + audio.pause() + audio.load() + audio.volume = volume + } + } + + fun play() { + //println("PLAYING: $name - $nextChannel") + channels[nextChannel].currentTime = 0.0 + channels[nextChannel].play() + + nextChannel = (nextChannel + 1) % channels.size + } + + fun pause() { + for (audio in channels) { + audio.pause() + } + } +} + +object Sounds { + val sounds: MutableMap = HashMap() + + fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { + sounds.put(name, Sound(name, url, volume, channels)) + } + + fun play(name: String, volume: Float = 0.75f) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.play() + } + + fun pause(name: String) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.pause() + } +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt new file mode 100644 index 0000000..36356bd --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt @@ -0,0 +1,67 @@ +package games.perses.shader + +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext + +/** + * User: rnentjes + * Date: 14-5-16 + * Time: 11:57 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgramMesh( + val shaderProgram: ShaderProgram +) { + val webgl = shaderProgram.webgl + val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) + var currentIndex: Int = 0 + val attribBuffer: WebGLBuffer + var counter = 0 + + init { + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun queue(vararg vertices: Float) { + queueArray(vertices as Array) + } + + fun queueArray(vertices: Array) { + data.set(vertices, currentIndex) + currentIndex += vertices.size + + if (bufferFull()) { + //println("Skipped draw call, to many values!") + currentIndex = 0 + } + } + + fun remaining() = data.length - currentIndex + + fun bufferFull() = currentIndex == data.length + + fun render(userdata: T) { + counter++ + if (currentIndex > 0) { + if (currentIndex % shaderProgram.verticesBlockSize != 0) { + throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") + } + + shaderProgram.begin(attribBuffer, userdata) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) + webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) + currentIndex = 0 + + shaderProgram.end() + } + } +} diff --git a/src/main/kotlin/games/perses/sound/Music.kt b/src/main/kotlin/games/perses/sound/Music.kt new file mode 100644 index 0000000..90bac23 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Music.kt @@ -0,0 +1,47 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 13:02 + */ + +object Music { + val playing: MutableSet = HashSet() + + fun load(url: String): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + + return audio + } + + fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + audio.volume = volume + audio.play() + + audio.onended = { + if (looping) { + audio.currentTime = 0.0 + audio.play() + } else { + //println("REMOVING: $audio") + audio.remove() + playing.remove(audio) + } + } + + return audio + } + + fun stopAll() { + + } +} diff --git a/src/main/kotlin/games/perses/sound/Sounds.kt b/src/main/kotlin/games/perses/sound/Sounds.kt new file mode 100644 index 0000000..806fd54 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Sounds.kt @@ -0,0 +1,61 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:34 + */ + +class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { + var channels: Array + var nextChannel: Int = 0 + + init { + //println("CREATING: $name") + channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) + + for (audio in channels) { + audio.src = url + audio.pause() + audio.load() + audio.volume = volume + } + } + + fun play() { + //println("PLAYING: $name - $nextChannel") + channels[nextChannel].currentTime = 0.0 + channels[nextChannel].play() + + nextChannel = (nextChannel + 1) % channels.size + } + + fun pause() { + for (audio in channels) { + audio.pause() + } + } +} + +object Sounds { + val sounds: MutableMap = HashMap() + + fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { + sounds.put(name, Sound(name, url, volume, channels)) + } + + fun play(name: String, volume: Float = 0.75f) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.play() + } + + fun pause(name: String) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.pause() + } +} diff --git a/src/main/kotlin/games/perses/sprite/SpriteBatch.kt b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt new file mode 100644 index 0000000..3de1163 --- /dev/null +++ b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt @@ -0,0 +1,26 @@ +package games.perses.sprite + +import games.perses.texture.Texture +import games.perses.texture.Textures + +/** + * User: rnentjes + * Date: 20-4-16 + * Time: 13:48 + */ + +class Sprite(val textureName: String) { + val texture: Texture by lazy { Textures.get(textureName) } +} + +class SpriteBatch { + + fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { + sprite.texture.queueDraw(x, y, scale, rotation) + } + + fun render() { + Textures.render() + } + +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt new file mode 100644 index 0000000..36356bd --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt @@ -0,0 +1,67 @@ +package games.perses.shader + +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext + +/** + * User: rnentjes + * Date: 14-5-16 + * Time: 11:57 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgramMesh( + val shaderProgram: ShaderProgram +) { + val webgl = shaderProgram.webgl + val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) + var currentIndex: Int = 0 + val attribBuffer: WebGLBuffer + var counter = 0 + + init { + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun queue(vararg vertices: Float) { + queueArray(vertices as Array) + } + + fun queueArray(vertices: Array) { + data.set(vertices, currentIndex) + currentIndex += vertices.size + + if (bufferFull()) { + //println("Skipped draw call, to many values!") + currentIndex = 0 + } + } + + fun remaining() = data.length - currentIndex + + fun bufferFull() = currentIndex == data.length + + fun render(userdata: T) { + counter++ + if (currentIndex > 0) { + if (currentIndex % shaderProgram.verticesBlockSize != 0) { + throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") + } + + shaderProgram.begin(attribBuffer, userdata) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) + webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) + currentIndex = 0 + + shaderProgram.end() + } + } +} diff --git a/src/main/kotlin/games/perses/sound/Music.kt b/src/main/kotlin/games/perses/sound/Music.kt new file mode 100644 index 0000000..90bac23 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Music.kt @@ -0,0 +1,47 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 13:02 + */ + +object Music { + val playing: MutableSet = HashSet() + + fun load(url: String): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + + return audio + } + + fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + audio.volume = volume + audio.play() + + audio.onended = { + if (looping) { + audio.currentTime = 0.0 + audio.play() + } else { + //println("REMOVING: $audio") + audio.remove() + playing.remove(audio) + } + } + + return audio + } + + fun stopAll() { + + } +} diff --git a/src/main/kotlin/games/perses/sound/Sounds.kt b/src/main/kotlin/games/perses/sound/Sounds.kt new file mode 100644 index 0000000..806fd54 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Sounds.kt @@ -0,0 +1,61 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:34 + */ + +class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { + var channels: Array + var nextChannel: Int = 0 + + init { + //println("CREATING: $name") + channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) + + for (audio in channels) { + audio.src = url + audio.pause() + audio.load() + audio.volume = volume + } + } + + fun play() { + //println("PLAYING: $name - $nextChannel") + channels[nextChannel].currentTime = 0.0 + channels[nextChannel].play() + + nextChannel = (nextChannel + 1) % channels.size + } + + fun pause() { + for (audio in channels) { + audio.pause() + } + } +} + +object Sounds { + val sounds: MutableMap = HashMap() + + fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { + sounds.put(name, Sound(name, url, volume, channels)) + } + + fun play(name: String, volume: Float = 0.75f) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.play() + } + + fun pause(name: String) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.pause() + } +} diff --git a/src/main/kotlin/games/perses/sprite/SpriteBatch.kt b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt new file mode 100644 index 0000000..3de1163 --- /dev/null +++ b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt @@ -0,0 +1,26 @@ +package games.perses.sprite + +import games.perses.texture.Texture +import games.perses.texture.Textures + +/** + * User: rnentjes + * Date: 20-4-16 + * Time: 13:48 + */ + +class Sprite(val textureName: String) { + val texture: Texture by lazy { Textures.get(textureName) } +} + +class SpriteBatch { + + fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { + sprite.texture.queueDraw(x, y, scale, rotation) + } + + fun render() { + Textures.render() + } + +} diff --git a/src/main/kotlin/games/perses/text/Texts.kt b/src/main/kotlin/games/perses/text/Texts.kt new file mode 100644 index 0000000..4caa6f7 --- /dev/null +++ b/src/main/kotlin/games/perses/text/Texts.kt @@ -0,0 +1,30 @@ +package games.perses.text + +import games.perses.game.Game + +/** + * Created by rnentjes on 16-5-16. + */ + +object Texts { + + fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { + var yy = y + if (yy < 0) { + yy += Game.view.height + } + Game.html.canvas2d.fillStyle = fillStyle + Game.html.canvas2d.font = font + Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) + } + + fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { + drawText( + Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), + Game.view.gameToScreenCoordY(Game.view.height / 2f - top), + message, + font, + fillStyle + ) + } +} diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt new file mode 100644 index 0000000..36356bd --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt @@ -0,0 +1,67 @@ +package games.perses.shader + +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext + +/** + * User: rnentjes + * Date: 14-5-16 + * Time: 11:57 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgramMesh( + val shaderProgram: ShaderProgram +) { + val webgl = shaderProgram.webgl + val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) + var currentIndex: Int = 0 + val attribBuffer: WebGLBuffer + var counter = 0 + + init { + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun queue(vararg vertices: Float) { + queueArray(vertices as Array) + } + + fun queueArray(vertices: Array) { + data.set(vertices, currentIndex) + currentIndex += vertices.size + + if (bufferFull()) { + //println("Skipped draw call, to many values!") + currentIndex = 0 + } + } + + fun remaining() = data.length - currentIndex + + fun bufferFull() = currentIndex == data.length + + fun render(userdata: T) { + counter++ + if (currentIndex > 0) { + if (currentIndex % shaderProgram.verticesBlockSize != 0) { + throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") + } + + shaderProgram.begin(attribBuffer, userdata) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) + webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) + currentIndex = 0 + + shaderProgram.end() + } + } +} diff --git a/src/main/kotlin/games/perses/sound/Music.kt b/src/main/kotlin/games/perses/sound/Music.kt new file mode 100644 index 0000000..90bac23 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Music.kt @@ -0,0 +1,47 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 13:02 + */ + +object Music { + val playing: MutableSet = HashSet() + + fun load(url: String): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + + return audio + } + + fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + audio.volume = volume + audio.play() + + audio.onended = { + if (looping) { + audio.currentTime = 0.0 + audio.play() + } else { + //println("REMOVING: $audio") + audio.remove() + playing.remove(audio) + } + } + + return audio + } + + fun stopAll() { + + } +} diff --git a/src/main/kotlin/games/perses/sound/Sounds.kt b/src/main/kotlin/games/perses/sound/Sounds.kt new file mode 100644 index 0000000..806fd54 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Sounds.kt @@ -0,0 +1,61 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:34 + */ + +class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { + var channels: Array + var nextChannel: Int = 0 + + init { + //println("CREATING: $name") + channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) + + for (audio in channels) { + audio.src = url + audio.pause() + audio.load() + audio.volume = volume + } + } + + fun play() { + //println("PLAYING: $name - $nextChannel") + channels[nextChannel].currentTime = 0.0 + channels[nextChannel].play() + + nextChannel = (nextChannel + 1) % channels.size + } + + fun pause() { + for (audio in channels) { + audio.pause() + } + } +} + +object Sounds { + val sounds: MutableMap = HashMap() + + fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { + sounds.put(name, Sound(name, url, volume, channels)) + } + + fun play(name: String, volume: Float = 0.75f) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.play() + } + + fun pause(name: String) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.pause() + } +} diff --git a/src/main/kotlin/games/perses/sprite/SpriteBatch.kt b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt new file mode 100644 index 0000000..3de1163 --- /dev/null +++ b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt @@ -0,0 +1,26 @@ +package games.perses.sprite + +import games.perses.texture.Texture +import games.perses.texture.Textures + +/** + * User: rnentjes + * Date: 20-4-16 + * Time: 13:48 + */ + +class Sprite(val textureName: String) { + val texture: Texture by lazy { Textures.get(textureName) } +} + +class SpriteBatch { + + fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { + sprite.texture.queueDraw(x, y, scale, rotation) + } + + fun render() { + Textures.render() + } + +} diff --git a/src/main/kotlin/games/perses/text/Texts.kt b/src/main/kotlin/games/perses/text/Texts.kt new file mode 100644 index 0000000..4caa6f7 --- /dev/null +++ b/src/main/kotlin/games/perses/text/Texts.kt @@ -0,0 +1,30 @@ +package games.perses.text + +import games.perses.game.Game + +/** + * Created by rnentjes on 16-5-16. + */ + +object Texts { + + fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { + var yy = y + if (yy < 0) { + yy += Game.view.height + } + Game.html.canvas2d.fillStyle = fillStyle + Game.html.canvas2d.font = font + Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) + } + + fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { + drawText( + Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), + Game.view.gameToScreenCoordY(Game.view.height / 2f - top), + message, + font, + fillStyle + ) + } +} diff --git a/src/main/kotlin/games/perses/texture/Sprites.kt b/src/main/kotlin/games/perses/texture/Sprites.kt new file mode 100644 index 0000000..4061eea --- /dev/null +++ b/src/main/kotlin/games/perses/texture/Sprites.kt @@ -0,0 +1,7 @@ +package games.perses.texture + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:44 + */ diff --git a/src/main/kotlin/com/persesgames/color/Color.kt b/src/main/kotlin/com/persesgames/color/Color.kt deleted file mode 100644 index 7ae1752..0000000 --- a/src/main/kotlin/com/persesgames/color/Color.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.persesgames.color - -/** - * User: rnentjes - * Date: 8-2-17 - * Time: 12:28 - * - * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion - */ - -object Color { - - /** - * Converts an HSL color value to RGB. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes h, s, and l are contained in the set [0, 1] and - * returns r, g, and b in the set [0, 255]. - - * @param h The hue - * * - * @param s The saturation - * * - * @param l The lightness - * * - * @return int array, the RGB representation - */ - fun hslToRgb(h: Float, s: Float, l: Float): IntArray { - val r: Float - val g: Float - val b: Float - - if (s == 0f) { - b = l - g = b - r = g // achromatic - } else { - val q = if (l < 0.5f) l * (1 + s) else l + s - l * s - val p = 2 * l - q - r = hueToRgb(p, q, h + 1f / 3f) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1f / 3f) - } - val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) - - return rgb - } - - /** Helper method that converts hue to rgb */ - fun hueToRgb(p: Float, q: Float, t: Float): Float { - var lt = t - if (lt < 0f) { - lt += 1f - } - if (lt > 1f) { - lt -= 1f - } - if (lt < 1f / 6f) { - return p + (q - p) * 6f * lt - } - if (lt < 1f / 2f) { - return q - } - if (lt < 2f / 3f) { - return p + (q - p) * (2f / 3f - lt) * 6f - } - return p - } - - /** - * Converts an RGB color value to HSL. Conversion formula - * adapted from http://en.wikipedia.org/wiki/HSL_color_space. - * Assumes pR, pG, and bpBare contained in the set [0, 255] and - * returns h, s, and l in the set [0, 1]. - - * @param pR The red color value - * * - * @param pG The green color value - * * - * @param pB The blue color value - * * - * @return float array, the HSL representation - */ - fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { - val r = pR / 255f - val g = pG / 255f - val b = pB / 255f - - val max = if (r > g && r > b) r else if (g > b) g else b - val min = if (r < g && r < b) r else if (g < b) g else b - - var h: Float - val s: Float - val l: Float - l = (max + min) / 2.0f - - if (max == min) { - s = 0.0f - h = s - } else { - val d = max - min - s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) - - if (r > g && r > b) - h = (g - b) / d + (if (g < b) 6.0f else 0.0f) - else if (g > b) - h = (b - r) / d + 2.0f - else - h = (r - g) / d + 4.0f - - h /= 6.0f - } - - val hsl = floatArrayOf(h, s, l) - return hsl - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/Game.kt b/src/main/kotlin/com/persesgames/game/Game.kt deleted file mode 100644 index 9eb3cb5..0000000 --- a/src/main/kotlin/com/persesgames/game/Game.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.persesgames.game - -import com.persesgames.texture.Textures -import org.khronos.webgl.WebGLRenderingContext -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import org.w3c.dom.HTMLElement -import kotlin.browser.document -import kotlin.browser.window -import kotlin.js.Date - -/** - * Created by rnentjes on 19-4-16. - */ - -enum class DrawMode { - LINEAR, - NEAREST -} - -class HTMLElements { - var container: HTMLElement - var webgl: WebGLRenderingContext - var canvas2d: CanvasRenderingContext2D - - init { - container = document.createElement("div") as HTMLElement - - val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement - val canvas = document.createElement("canvas") as HTMLCanvasElement - - container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") - webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) - canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) - - document.body!!.appendChild(container) - container.appendChild(webGlCanvas) - container.appendChild(canvas) - - webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext - canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D - } -} - -object Game { - var started = false - val view: View = View() - val html: HTMLElements by lazy { HTMLElements() } - var currentScreen: Screen = DefaultScreen() - var start = Date().getTime() - var currentTime = start - var currentDelta = 0f - var pause: Boolean = false - - var clearRed = 0f - var clearGreen = 0f - var clearBlue = 0f - var clearAlpha = 0f - - var fps = 0 - var fpsCount = 0 - var fpsCountTime = 0f - - fun gl() = html.webgl - - fun resize() { - val canvas = gl().canvas - - // Check if the canvas is not the same size. - val windowWidth = window.innerWidth.toInt() - val windowHeight = window.innerHeight.toInt() - - if (view.lastWindowWidth != windowWidth || - view.lastWindowHeight != windowHeight) { - view.lastWindowWidth = windowWidth - view.lastWindowHeight = windowHeight - view.windowWidth = windowWidth - view.windowHeight = windowHeight - - view.updateView() - - val textCanvas = html.canvas2d.canvas - - // Make the canvas the same size - canvas.width = view.width.toInt() - canvas.height = view.height.toInt() - - textCanvas.width = view.width.toInt() - textCanvas.height = view.height.toInt() - - gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) - - val left = (windowWidth - view.windowWidth) / 2 - val top = (windowHeight - view.windowHeight) / 2 - - canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) - } - } - - fun start(startScreen: Screen) { - if (started) { - throw IllegalStateException("You can only start a game once!") - } - - setScreen(startScreen) - - // start game loop - started = true - gameLoop() - } - - fun setScreen(screen: Screen) { - currentScreen.closeResources() - - currentScreen = screen - - currentScreen.loadResources() - } - - fun setClearColor(r: Float, g: Float, b: Float, a: Float) { - clearRed = r - clearGreen = g - clearBlue = b - clearAlpha = a - } - - fun gameLoop() { - if (!Textures.ready()) { - Game.gl().clearColor(1f, 1f, 1f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } else { - resize() - - if (!pause) { - html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); - - Game.gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - - Game.gl().enable(WebGLRenderingContext.BLEND) - Game.gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); - - val time = Date().getTime() - currentDelta = ((time - currentTime) / 1000f).toFloat() - currentTime = time - - val timeInSeconds = (currentTime - start) / 1000f - - fpsCountTime += currentDelta - fpsCount++ - while (fpsCountTime > 1f) { - fps = fpsCount - fpsCountTime -= 1f - fpsCount = 0 - } - - currentScreen.update(timeInSeconds.toFloat(), currentDelta) - currentScreen.render() - } - } - - window.requestAnimationFrame { - gameLoop() - } - } - -} diff --git a/src/main/kotlin/com/persesgames/game/Screen.kt b/src/main/kotlin/com/persesgames/game/Screen.kt deleted file mode 100644 index 6fb558e..0000000 --- a/src/main/kotlin/com/persesgames/game/Screen.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.persesgames.game - -import org.khronos.webgl.WebGLRenderingContext - -/** - * Created by rnentjes on 19-4-16. - */ - -abstract class Screen { - - open fun loadResources() { - - } - - open fun closeResources() { - - } - - abstract fun update(time: Float, delta: Float) - - abstract fun render() - -} - - -class DefaultScreen: Screen() { - override fun update(time: Float, delta: Float) { - } - - override fun render() { - // show loading message? - Game.gl().clearColor(1f, 1f, 0f, 1f) - Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/game/View.kt b/src/main/kotlin/com/persesgames/game/View.kt deleted file mode 100644 index d806855..0000000 --- a/src/main/kotlin/com/persesgames/game/View.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.persesgames.game - -import com.persesgames.math.Matrix4 - -enum class ViewType { - PROJECTION, - WIDTH, - HEIGHT, - ABSOLUTE -} - -class View( - var lastWindowWidth: Int = 2000, - var lastWindowHeight: Int = 1000, - var windowWidth: Int = 2000, - var windowHeight: Int = 1000, - var width: Float = 1024f, - var height: Float = 1024f, - var angle: Float = 60f, - var near: Float = -0.1f, - var far: Float = -100f, - var minAspectRatio: Float = 1f, - var maxAspectRatio: Float = 1f, - var leftOffset: Int = 0, - var bottomOffset: Int = 0, - var viewType: ViewType = ViewType.WIDTH, - var drawMode: DrawMode = DrawMode.LINEAR) { - var vMatrix = Matrix4() - var aspectRatio = 1f - - init { - updateView() - } - - fun requestFullscreen() { - //println("Requesting fullscreen") - js(""" - if (document.webkitFullscreenElement) { - document.webkitCancelFullScreen(); - } else { - document.documentElement.webkitRequestFullScreen(); - } - """) - //if (document.fullscreenEnabled) { - // println("fullscreen Enabled") - //Game.html.container.requestFullscreen() - //document.documentElement?.requestFullscreen() - //} - } - - fun updateView() { - aspectRatio = windowWidth / windowHeight.toFloat() - - if (aspectRatio < minAspectRatio) { - aspectRatio = minAspectRatio - - windowHeight = (windowWidth / aspectRatio).toInt() - } - - if (aspectRatio > maxAspectRatio) { - aspectRatio = maxAspectRatio - - windowWidth = (windowHeight * aspectRatio).toInt() - } - - when (viewType) { - ViewType.ABSOLUTE -> { - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.WIDTH -> { - height = width / aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.HEIGHT -> { - width = height * aspectRatio - - vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) - } - ViewType.PROJECTION -> { - vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - //println("width: $width, height: $height") - } - - fun screenToGameCoordX(screenX: Float): Float { - var result = screenX - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.HEIGHT -> { - result = (screenX / windowWidth * width) - width / 2 - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun screenToGameCoordY(screenY: Float): Float { - var result = screenY - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.HEIGHT -> { - result = -((screenY / windowHeight * height) - height / 2) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordX(gameX: Float): Float { - var result = gameX - val normalizedX = gameX + (width / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = (windowWidth / width * normalizedX) - } - ViewType.HEIGHT -> { - result = (windowWidth / width * normalizedX) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun gameToScreenCoordY(gameY: Float): Float { - var result = gameY - val normalizedY = gameY + (height / 2) - - when (viewType) { - ViewType.ABSOLUTE -> { - // nop - } - ViewType.WIDTH -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.HEIGHT -> { - result = windowHeight - (windowHeight / height * normalizedY) - } - ViewType.PROJECTION -> { - // uhm - } - else -> { - throw IllegalStateException("ViewType $viewType not implemented!") - } - } - - return result - } - - fun setToWidth(width: Float) { - this.width = width - this.viewType = ViewType.WIDTH - - updateView() - } - - fun setToHeight(height: Float) { - this.height = height - this.viewType = ViewType.HEIGHT - - updateView() - } - - fun setProjection(angle: Float) { - this.angle = angle - this.viewType = ViewType.PROJECTION - - updateView() - } - - fun setNear(near: Float) { - this.near = near - - updateView() - } - - fun setFar(far: Float) { - this.far = far - - updateView() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/persesgames/input/Keys.kt b/src/main/kotlin/com/persesgames/input/Keys.kt deleted file mode 100644 index 7a6911d..0000000 --- a/src/main/kotlin/com/persesgames/input/Keys.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.persesgames.input - -import com.persesgames.game.Game -import org.w3c.dom.events.Event -import org.w3c.dom.events.KeyboardEvent -import org.w3c.dom.events.MouseEvent -import kotlin.browser.document -import kotlin.js.Date - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:18 - */ - -enum class KeyCode(val keyCode: Int) { - LEFT(37), - UP(38), - DOWN(40), - RIGHT(39), - SPACE(32), - MINUS(109), - PLUS(107), -} - -interface InputProcessor { - - fun keyPressed(charCode: Int) - - fun keyDown(keyCode: Int) - - fun keyUp(keyCode: Int) - - fun pointerClick(pointer: Int, x: Float, y: Float) - -} - -open class EmptyInputProcessor : InputProcessor { - - override fun pointerClick(pointer: Int, x: Float, y: Float) { } - - override fun keyDown(keyCode: Int) { } - - override fun keyPressed(charCode: Int) { } - - override fun keyUp(keyCode: Int) { } -} - -object Keys { - - private val keys: MutableMap = HashMap() - private var inputProcesser: InputProcessor = EmptyInputProcessor() - - init { - val body = document.body - if (body != null) { - body.onkeydown = { - Keys.keyDown(it) - } - - body.onkeyup = { - Keys.keyUp(it) - } - - body.onkeypress = { - Keys.keyPress(it) - } - - body.onclick = { - Keys.mouseClick(it) - } - - body.onmousedown = { - Keys.mouseMove(it) - } - - body.onmouseup = { - Keys.mouseMove(it) - } - - body.onmousemove = { - Keys.mouseMove(it) - } - } else { - console.log("Can't register key events, document.body is null!?") - } - } - - fun setInputProcessor(processor: InputProcessor) { - this.inputProcesser = processor - } - - private fun keyDown(key: Event) { - if (key is KeyboardEvent) { - keys.put(key.keyCode, Date().getTime()) - - inputProcesser.keyDown(key.keyCode) - } - } - - private fun keyUp(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyUp(key.keyCode) - - keys.remove(key.keyCode) - } - } - - private fun keyPress(key: Event) { - if (key is KeyboardEvent) { - inputProcesser.keyPressed(key.charCode) - } - } - - private fun mouseClick(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - inputProcesser.pointerClick(event.button.toInt(), vx, vy) - } - } - - private fun mouseMove(event: Event) { - if (event is MouseEvent) { - val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) - val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) - - - } - } - - fun isDown(keyCode: Int) = keys.containsKey(keyCode) - - fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) - - fun wasPressed(keyCode: Int, delta: Double): Boolean { - val time = keys[keyCode] - - return (time != null && time > (Date().getTime() - delta)) - } - -} diff --git a/src/main/kotlin/com/persesgames/map/Map.kt b/src/main/kotlin/com/persesgames/map/Map.kt deleted file mode 100644 index b5d7bc8..0000000 --- a/src/main/kotlin/com/persesgames/map/Map.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.persesgames.map - -/** - * Created by rnentjes on 22-7-16. - */ - -open class Map { - -} diff --git a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt b/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt deleted file mode 100644 index 9101fe5..0000000 --- a/src/main/kotlin/com/persesgames/map/tiled/TiledMap.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.persesgames.map.tiled - -import com.persesgames.net.getUrlAsString -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures -import kotlin.browser.window -import kotlin.js.Math - -/** - * Created by rnentjes on 22-7-16. - */ - -class MapData { - var version: Int = 1 - var properties: MutableMap = HashMap() - var layers: Array? = null - var tilesets: Array? = null - - var height: Int = 0 - var width: Int = 0 - - var nextobjectid: Int = 0 - var orientation: String = "orthogonal" - var renderorder: String = "right-down" - var tileheight: Int = 0 - var tilewidth: Int = 0 -} - -class MapLayer { - var properties: MutableMap = HashMap() - - var data: Array? = null - var encoding: String = "" - var x: Int = 0 - var y: Int = 0 - var width: Int = 0 - var height: Int = 0 - var name: String = "" - var opacity: Float = 1f - var type: String = "" - var visible: Boolean = true - var draworder: String = "" -} - -class MapTileset { - var properties: MutableMap = HashMap() - - var firstgid: Int = 0 - var image: String = "" - var imageheight: Int = 0 - var imagewidth: Int = 0 - var margin: Int = 0 - var name: String = "" - var spacing: Int = 0 - var tilecount: Int = 0 - var tileheight: Int = 0 - var tilewidth: Int = 0 - var tileproperties: MutableMap> = HashMap() -} - -class TilesetIndex( - val texture: Texture?, - val tcLeft: Float, - val tcTop: Float, - val tcRight: Float, - val tcBottom: Float, - val scale: Float - ) { - constructor() : this(null, 0f, 0f, 0f, 0f, 0f) - - fun render(x: Float, y: Float) { - texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) - } -} - -class TiledMap(dir: String = "", url: String) { - val properties: Map = HashMap() - val data: MapData - val tileset: Array - val tiles: Array - var first = true - //var tilesetIndex: Array = Array(0, { TilesetIndex() }) - - init { - var tileDir = dir - if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { - tileDir = tileDir + "/" - } - - data = JSON.parse(getUrlAsString(tileDir + url)) - //println("map data is loaded") - val tilesets = data.tilesets - if (tilesets != null) { - tileset = Array(tilesets.size, { "" }) - var maxGid = 0 - for (index in 0..tilesets.size - 1) { - tileset[index] = tilesets[index].name - Textures.load(tilesets[index].name, tileDir + tilesets[index].image) - maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) - } - - tiles = Array(maxGid, { TilesetIndex() }) - } else { - tileset = Array(0, { "" }) - tiles = Array(0, { TilesetIndex() }) - } - - cacheTiles() - } - - fun cacheTiles() { - if (!Textures.ready()) { - window.setTimeout({ cacheTiles() }, 10) - } else { - val tilesets = data.tilesets - var tcLeft = 0f - var tcTop = 0f - var tcRight = 0f - var tcBottom = 0f - - if (tilesets != null) { - - - for (tileset in tilesets) { - val tilesHor = tileset.imagewidth / tileset.tilewidth - val tilesVer = tileset.imageheight / tileset.tileheight - val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) - - for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { - val texture = Textures.get(tileset.name) - - val gid = index - tileset.firstgid - - val xi = gid % tilesHor - var yi = gid / tilesHor - yi = tilesVer - yi - 1 - val tw = 1f / tilesHor.toFloat() - val th = 1f / tilesVer.toFloat() - - val pixelW = 0.1f / tileset.tilewidth - val pixelH = 0.1f / tileset.tileheight - - tcLeft = xi * tw - tcRight = tcLeft + tw - - // switch up/down because of texture coord 0,0 in left bottom corner - tcBottom = yi * th - tcTop = tcBottom + th - - tcLeft += pixelW - tcRight -= pixelW - - tcBottom += pixelH - tcTop -= pixelH - - tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) - } - } - } - } - } - - fun drawTile(tile: Int, x: Float, y: Float) { - tiles[tile].render(x, y) - } - - fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { - var x = 0f - var y = 0f - val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") - val layer = layers[layerIndex] - - val layerData = layer.data - if (layerData != null) { - for (index in layerData.indices) { - // todo: determine if in view - // todo: determine tilewidth - //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { - drawTile(layerData[index], xo + x * 128f, yo + y * 128f) - - when (data.renderorder) { - "right-down" -> { - x++ - if (x >= layer.width) { - x = 0f - y-- - } - } - else -> { - throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") - } - } - //} - } - } - - val tilesets = data.tilesets - if (tilesets != null) { - for (tileset in tilesets) { - if (Textures.has(tileset.name)) { - val tx = Textures.get(tileset.name) - - tx.render() - } - } - } - - - first = false - } -} diff --git a/src/main/kotlin/com/persesgames/math/Matrix4.kt b/src/main/kotlin/com/persesgames/math/Matrix4.kt deleted file mode 100644 index d17b5a4..0000000 --- a/src/main/kotlin/com/persesgames/math/Matrix4.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.persesgames.math - -import org.khronos.webgl.Float32Array -import kotlin.js.Math - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:43 - */ -class Matrix4 { - - internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - internal var temp = FloatArray(16) - - private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) - - fun get(): FloatArray { - return matrix - } - - fun getFloat32Array() = Float32Array(get().toTypedArray()) - - fun set(values: FloatArray) { - if (values.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - matrix = values - } - - fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { - val r = (angle / 180f * Math.PI).toFloat() - val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() - - matrix[0] = f / imageAspectRatio - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - - matrix[4] = 0.0f - matrix[5] = f - matrix[6] = 0.0f - matrix[7] = 0.0f - - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = -(far + near) / (far - near) - matrix[11] = -1.0f - - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = -(2.0f * far * near) / (far - near) - matrix[15] = 0.0f - } - - fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { - matrix[0] = 2f / (right - left) - matrix[1] = 0f - matrix[2] = 0f - matrix[3] = 0f - - matrix[4] = 0f - matrix[5] = 2f / (top - bottom) - matrix[6] = 0f - matrix[7] = 0f - - matrix[8] = 0f - matrix[9] = 0f - matrix[10] = -2f / (far - near) - matrix[11] = 0f - - matrix[12] = - (right + left) / (right - left) - matrix[13] = - (top + bottom) / (top - bottom) - matrix[14] = - (far + near) / (far - near) - matrix[15] = 1f - } - - fun setToIdentity() { - matrix[0] = 1.0f - matrix[1] = 0.0f - matrix[2] = 0.0f - matrix[3] = 0.0f - matrix[4] = 0.0f - matrix[5] = 1.0f - matrix[6] = 0.0f - matrix[7] = 0.0f - matrix[8] = 0.0f - matrix[9] = 0.0f - matrix[10] = 1.0f - matrix[11] = 0.0f - matrix[12] = 0.0f - matrix[13] = 0.0f - matrix[14] = 0.0f - matrix[15] = 1.0f - } - - fun mul(other: Matrix4) { - mul(other.get()) - } - - protected fun mul(other: FloatArray) { - if (other.size != 16) { - throw IllegalArgumentException("Matrix size should be 16!") - } - - temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] - temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] - temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] - temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] - temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] - temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] - temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] - temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] - temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] - temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] - temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] - temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] - temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] - temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] - temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] - temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] - - matrix[0] = temp[0] - matrix[1] = temp[1] - matrix[2] = temp[2] - matrix[3] = temp[3] - matrix[4] = temp[4] - matrix[5] = temp[5] - matrix[6] = temp[6] - matrix[7] = temp[7] - matrix[8] = temp[8] - matrix[9] = temp[9] - matrix[10] = temp[10] - matrix[11] = temp[11] - matrix[12] = temp[12] - matrix[13] = temp[13] - matrix[14] = temp[14] - matrix[15] = temp[15] - } - - fun translate(x: Float, y: Float, z: Float) { - translateMatrix[12] = x - translateMatrix[13] = y - translateMatrix[14] = z - - mul(translateMatrix) - } - - fun scale(x: Float, y: Float, z: Float) { - scaleMatrix[0] = x - scaleMatrix[5] = y - scaleMatrix[10] = z - - mul(scaleMatrix) - } - - fun rotateX(angle: Float) { - rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() - rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() - rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() - rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateXMatrix) - } - - fun rotateY(angle: Float) { - rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() - rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() - rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateYMatrix) - } - - fun rotateZ(angle: Float) { - rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() - rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() - rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() - rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() - - mul(rotateZMatrix) - } -} diff --git a/src/main/kotlin/com/persesgames/net/NetUtils.kt b/src/main/kotlin/com/persesgames/net/NetUtils.kt deleted file mode 100644 index b59cbb3..0000000 --- a/src/main/kotlin/com/persesgames/net/NetUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.persesgames.net - -import org.w3c.xhr.XMLHttpRequest - -/** - * User: rnentjes - * Date: 30-7-16 - * Time: 16:39 - */ - -fun getUrlAsString(url: String): String { - val req = XMLHttpRequest() - - req.open("GET", url, false) - req.send(null) - - return req.responseText -} diff --git a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt deleted file mode 100644 index c2ee93b..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgram.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.* - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:15 - */ - -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 = 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 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/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt b/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt deleted file mode 100644 index 246373e..0000000 --- a/src/main/kotlin/com/persesgames/shader/ShaderProgramMesh.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.persesgames.shader - -import org.khronos.webgl.Float32Array -import org.khronos.webgl.WebGLBuffer -import org.khronos.webgl.WebGLRenderingContext - -/** - * User: rnentjes - * Date: 14-5-16 - * Time: 11:57 - */ - -class VertextAttributeInfo(val locationName: String, val numElements: Int) { - var location = 0 - var offset = 0 -} - -class ShaderProgramMesh( - val shaderProgram: ShaderProgram -) { - val webgl = shaderProgram.webgl - val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) - var currentIndex: Int = 0 - val attribBuffer: WebGLBuffer - var counter = 0 - - init { - - attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") - webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); - } - - fun queue(vararg vertices: Float) { - queueArray(vertices as Array) - } - - fun queueArray(vertices: Array) { - data.set(vertices, currentIndex) - currentIndex += vertices.size - - if (bufferFull()) { - //println("Skipped draw call, to many values!") - currentIndex = 0 - } - } - - fun remaining() = data.length - currentIndex - - fun bufferFull() = currentIndex == data.length - - fun render(userdata: T) { - counter++ - if (currentIndex > 0) { - if (currentIndex % shaderProgram.verticesBlockSize != 0) { - throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") - } - - shaderProgram.begin(attribBuffer, userdata) - - webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) - webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) - currentIndex = 0 - - shaderProgram.end() - } - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Music.kt b/src/main/kotlin/com/persesgames/sound/Music.kt deleted file mode 100644 index 72c6f4d..0000000 --- a/src/main/kotlin/com/persesgames/sound/Music.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 13:02 - */ - -object Music { - val playing: MutableSet = HashSet() - - fun load(url: String): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - - return audio - } - - fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { - val audio = document.createElement("audio") as HTMLAudioElement - - audio.src = url - audio.volume = volume - audio.play() - - audio.onended = { - if (looping) { - audio.currentTime = 0.0 - audio.play() - } else { - //println("REMOVING: $audio") - audio.remove() - playing.remove(audio) - } - } - - return audio - } - - fun stopAll() { - - } -} diff --git a/src/main/kotlin/com/persesgames/sound/Sounds.kt b/src/main/kotlin/com/persesgames/sound/Sounds.kt deleted file mode 100644 index a0a4df6..0000000 --- a/src/main/kotlin/com/persesgames/sound/Sounds.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.persesgames.sound - -import org.w3c.dom.HTMLAudioElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 18-5-16 - * Time: 12:34 - */ - -class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { - var channels: Array - var nextChannel: Int = 0 - - init { - //println("CREATING: $name") - channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) - - for (audio in channels) { - audio.src = url - audio.pause() - audio.load() - audio.volume = volume - } - } - - fun play() { - //println("PLAYING: $name - $nextChannel") - channels[nextChannel].currentTime = 0.0 - channels[nextChannel].play() - - nextChannel = (nextChannel + 1) % channels.size - } - - fun pause() { - for (audio in channels) { - audio.pause() - } - } -} - -object Sounds { - val sounds: MutableMap = HashMap() - - fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { - sounds.put(name, Sound(name, url, volume, channels)) - } - - fun play(name: String, volume: Float = 0.75f) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.play() - } - - fun pause(name: String) { - val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") - - sound.pause() - } -} diff --git a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt b/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt deleted file mode 100644 index 8f72f31..0000000 --- a/src/main/kotlin/com/persesgames/sprite/SpriteBatch.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.persesgames.sprite - -import com.persesgames.texture.Texture -import com.persesgames.texture.Textures - -/** - * User: rnentjes - * Date: 20-4-16 - * Time: 13:48 - */ - -class Sprite(val textureName: String) { - val texture: Texture by lazy { Textures.get(textureName) } -} - -class SpriteBatch { - - fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { - sprite.texture.queueDraw(x, y, scale, rotation) - } - - fun render() { - Textures.render() - } - -} diff --git a/src/main/kotlin/com/persesgames/text/Texts.kt b/src/main/kotlin/com/persesgames/text/Texts.kt deleted file mode 100644 index b0e2a17..0000000 --- a/src/main/kotlin/com/persesgames/text/Texts.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.persesgames.text - -import com.persesgames.game.Game - -/** - * Created by rnentjes on 16-5-16. - */ - -object Texts { - - fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - var yy = y - if (yy < 0) { - yy += Game.view.height - } - Game.html.canvas2d.fillStyle = fillStyle - Game.html.canvas2d.font = font - Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) - } - - fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { - drawText( - Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), - Game.view.gameToScreenCoordY(Game.view.height / 2f - top), - message, - font, - fillStyle - ) - } -} diff --git a/src/main/kotlin/com/persesgames/texture/Sprites.kt b/src/main/kotlin/com/persesgames/texture/Sprites.kt deleted file mode 100644 index 1b0fd0a..0000000 --- a/src/main/kotlin/com/persesgames/texture/Sprites.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.persesgames.texture - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 15:44 - */ diff --git a/src/main/kotlin/com/persesgames/texture/Textures.kt b/src/main/kotlin/com/persesgames/texture/Textures.kt deleted file mode 100644 index 6187459..0000000 --- a/src/main/kotlin/com/persesgames/texture/Textures.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.persesgames.texture - -import com.persesgames.game.DrawMode -import com.persesgames.game.Game -import com.persesgames.map.tiled.MapTileset -import com.persesgames.math.Matrix4 -import com.persesgames.shader.ShaderProgram -import com.persesgames.shader.ShaderProgramMesh -import com.persesgames.shader.VertextAttributeInfo -import org.khronos.webgl.ArrayBufferView -import org.khronos.webgl.WebGLRenderingContext -import org.khronos.webgl.WebGLTexture -import org.w3c.dom.HTMLImageElement -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 17-4-16 - * Time: 14:52 - */ -//language=GLSL -private val vertexShaderSource = """ - attribute vec2 a_position; - attribute vec2 a_boundingBox; - attribute vec2 a_texCoord; - attribute float a_scale; - attribute float a_rotation; - - uniform mat4 u_projectionView; - - varying vec2 v_textCoord; - - mat4 scale(float scale) { - return mat4( - vec4(scale, 0.0, 0.0, 0.0), - vec4(0.0, scale, 0.0, 0.0), - vec4(0.0, 0.0, scale, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - mat4 rotateZ(float angle) { - return mat4( - vec4(cos(angle), sin(angle), 0.0, 0.0), - vec4(-sin(angle), cos(angle), 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - } - - void main(void) { - v_textCoord = a_texCoord; - - vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); - - gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); - } -""" - -private val fragmentShaderSource = """ - precision mediump float; - - uniform sampler2D u_sampler; - - varying vec2 v_textCoord; - - void main(void) { - gl_FragColor = texture2D(u_sampler, v_textCoord); - } -""" - -class TextureData( - val vMatrix: Matrix4, - val texture: WebGLTexture -) - -class Texture( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val width: Int, - val height: Int -) { - val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) - val left = -width / 2f - val right = width / 2f - val bottom = -height / 2f - val top = height / 2f - - fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) - shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) - shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) - shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) - shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) - - if (shaderProgramMesh.remaining() < 36) { - render() - } - } - - fun render() { - Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) - Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) - - shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) - } -} - -/* -{ - "frame": {"x":921,"y":1,"w":182,"h":103}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, - "sourceSize": {"w":190,"h":110}, - "pivot": {"x":0.5,"y":0.5} -}, -*/ - -class Rect(val x: Int, val y: Int, val w: Int, val h: Int) -class Size(val w: Int, val h: Int) -class Pivot(val x: Double, val y: Double) - -class SpriteSheetData( - val frame: Rect, - val rotated: Boolean, - val trimmed: Boolean, - val spriteSourceSize: Rect, - val sourceSize: Size, - val pivot: Pivot - ) - -class SpriteSheet( - val glTexture: WebGLTexture, - val shaderProgram: ShaderProgram, - val data: Map -) { - -} - -object Textures { - var textures = HashMap() - var startedLoading = 0 - var loaded = 0 - val shaderProgram: ShaderProgram - - init { - val setter = { program: ShaderProgram, data: TextureData -> - program.setUniform1i("u_sampler", 0) - program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) - } - - val vainfo = arrayOf( - VertextAttributeInfo("a_position", 2), - VertextAttributeInfo("a_boundingBox", 2), - VertextAttributeInfo("a_texCoord", 2), - VertextAttributeInfo("a_scale", 1), - VertextAttributeInfo("a_rotation", 1) - ) - - shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) - } - - fun loadSpriteSheet(name: String) { - //val data = Request(name).json() - - //println(data) - } - - fun load(name: String, filename: String) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - val image = document.createElement("img") as HTMLImageElement - image.onload = { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - //println("loaded texture $loaded/$startedLoading ${ready()}") - } - image.src = filename - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun create(name: String, image: HTMLImageElement) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, image) - val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - - fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - startedLoading++ - - val webGlTexture = gl.createTexture() - if (webGlTexture != null) { - textureLoaded(webGlTexture, width, height, image) - val texture = Texture(webGlTexture, shaderProgram, width, height) - - textures.put(name, texture) - - loaded++ - } else { - throw IllegalStateException("Couldn't create webgl texture!") - } - } - - fun load(mapTileSet: MapTileset) { - - } - - private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { - val gl = Game.gl() - - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) - gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int - gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) - setTextureParameters(); - gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) - } - - private fun setTextureParameters() { - val gl = Game.gl() - - if (Game.view.drawMode == DrawMode.NEAREST) { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) - } else { - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) - } - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) - gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) - } - - fun ready() = loaded == startedLoading - - fun has(name: String) = textures[name] != null - fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") - - fun clear() { - // delete and unbind all textures... - } - - fun render() { - for ((key, value) in textures) { - value.render() - } - } - -} diff --git a/src/main/kotlin/games/perses/color/Color.kt b/src/main/kotlin/games/perses/color/Color.kt new file mode 100644 index 0000000..7f235b0 --- /dev/null +++ b/src/main/kotlin/games/perses/color/Color.kt @@ -0,0 +1,116 @@ +package games.perses.color + +/** + * User: rnentjes + * Date: 8-2-17 + * Time: 12:28 + * + * Taken from (java version): http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + */ + +object Color { + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + + * @param h The hue + * * + * @param s The saturation + * * + * @param l The lightness + * * + * @return int array, the RGB representation + */ + fun hslToRgb(h: Float, s: Float, l: Float): IntArray { + val r: Float + val g: Float + val b: Float + + if (s == 0f) { + b = l + g = b + r = g // achromatic + } else { + val q = if (l < 0.5f) l * (1 + s) else l + s - l * s + val p = 2 * l - q + r = hueToRgb(p, q, h + 1f / 3f) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1f / 3f) + } + val rgb = intArrayOf((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt()) + + return rgb + } + + /** Helper method that converts hue to rgb */ + fun hueToRgb(p: Float, q: Float, t: Float): Float { + var lt = t + if (lt < 0f) { + lt += 1f + } + if (lt > 1f) { + lt -= 1f + } + if (lt < 1f / 6f) { + return p + (q - p) * 6f * lt + } + if (lt < 1f / 2f) { + return q + } + if (lt < 2f / 3f) { + return p + (q - p) * (2f / 3f - lt) * 6f + } + return p + } + + /** + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes pR, pG, and bpBare contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + + * @param pR The red color value + * * + * @param pG The green color value + * * + * @param pB The blue color value + * * + * @return float array, the HSL representation + */ + fun rgbToHsl(pR: Int, pG: Int, pB: Int): FloatArray { + val r = pR / 255f + val g = pG / 255f + val b = pB / 255f + + val max = if (r > g && r > b) r else if (g > b) g else b + val min = if (r < g && r < b) r else if (g < b) g else b + + var h: Float + val s: Float + val l: Float + l = (max + min) / 2.0f + + if (max == min) { + s = 0.0f + h = s + } else { + val d = max - min + s = if (l > 0.5f) d / (2.0f - max - min) else d / (max + min) + + if (r > g && r > b) + h = (g - b) / d + (if (g < b) 6.0f else 0.0f) + else if (g > b) + h = (b - r) / d + 2.0f + else + h = (r - g) / d + 4.0f + + h /= 6.0f + } + + val hsl = floatArrayOf(h, s, l) + return hsl + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/Game.kt b/src/main/kotlin/games/perses/game/Game.kt new file mode 100644 index 0000000..0bb309d --- /dev/null +++ b/src/main/kotlin/games/perses/game/Game.kt @@ -0,0 +1,168 @@ +package games.perses.game + +import games.perses.texture.Textures +import org.khronos.webgl.WebGLRenderingContext +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.HTMLElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.js.Date + +/** + * Created by rnentjes on 19-4-16. + */ + +enum class DrawMode { + LINEAR, + NEAREST +} + +class HTMLElements { + var container: HTMLElement + var webgl: WebGLRenderingContext + var canvas2d: CanvasRenderingContext2D + + init { + container = document.createElement("div") as HTMLElement + + val webGlCanvas = document.createElement("canvas") as HTMLCanvasElement + val canvas = document.createElement("canvas") as HTMLCanvasElement + + container.setAttribute("style", "position: absolute; left: 0px; top: 0px;") + webGlCanvas.setAttribute("style", "position: absolute; left: 0px; top: 0px;" ) + canvas.setAttribute("style", "position: absolute; left: 0px; top: 0px; z-index: 10; width: 1000px; height: 500px;" ) + + document.body!!.appendChild(container) + container.appendChild(webGlCanvas) + container.appendChild(canvas) + + webgl = webGlCanvas.getContext("webgl") as WebGLRenderingContext + canvas2d = canvas.getContext("2d") as CanvasRenderingContext2D + } +} + +object Game { + var started = false + val view: View = View() + val html: HTMLElements by lazy { HTMLElements() } + var currentScreen: Screen = DefaultScreen() + var start = Date().getTime() + var currentTime = start + var currentDelta = 0f + var pause: Boolean = false + + var clearRed = 0f + var clearGreen = 0f + var clearBlue = 0f + var clearAlpha = 0f + + var fps = 0 + var fpsCount = 0 + var fpsCountTime = 0f + + fun gl() = html.webgl + + fun resize() { + val canvas = gl().canvas + + // Check if the canvas is not the same size. + val windowWidth = window.innerWidth.toInt() + val windowHeight = window.innerHeight.toInt() + + if (view.lastWindowWidth != windowWidth || + view.lastWindowHeight != windowHeight) { + view.lastWindowWidth = windowWidth + view.lastWindowHeight = windowHeight + view.windowWidth = windowWidth + view.windowHeight = windowHeight + + view.updateView() + + val textCanvas = html.canvas2d.canvas + + // Make the canvas the same size + canvas.width = view.width.toInt() + canvas.height = view.height.toInt() + + textCanvas.width = view.width.toInt() + textCanvas.height = view.height.toInt() + + gl().viewport(0, 0, view.width.toInt(), view.height.toInt()) + + val left = (windowWidth - view.windowWidth) / 2 + val top = (windowHeight - view.windowHeight) / 2 + + canvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 5; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + textCanvas.setAttribute("style", "position: absolute; left: ${left}px; top: ${top}px; z-index: 10; width: ${view.windowWidth}px; height: ${view.windowHeight}px;" ) + } + } + + fun start(startScreen: Screen) { + if (started) { + throw IllegalStateException("You can only start a game once!") + } + + setScreen(startScreen) + + // start game loop + started = true + gameLoop() + } + + fun setScreen(screen: Screen) { + currentScreen.closeResources() + + currentScreen = screen + + currentScreen.loadResources() + } + + fun setClearColor(r: Float, g: Float, b: Float, a: Float) { + clearRed = r + clearGreen = g + clearBlue = b + clearAlpha = a + } + + fun gameLoop() { + if (!Textures.ready()) { + gl().clearColor(1f, 1f, 1f, 1f) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } else { + resize() + + if (!pause) { + html.canvas2d.clearRect(0.0, 0.0, view.width.toDouble(), view.height.toDouble()); + + gl().clearColor(clearRed, clearGreen, clearBlue, clearAlpha) + gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + + gl().enable(WebGLRenderingContext.BLEND) + gl().blendFunc(WebGLRenderingContext.SRC_ALPHA, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA) //ONE_MINUS_DST_ALPHA); + + val time = Date().getTime() + currentDelta = ((time - currentTime) / 1000f).toFloat() + currentTime = time + + val timeInSeconds = (currentTime - start) / 1000f + + fpsCountTime += currentDelta + fpsCount++ + while (fpsCountTime > 1f) { + fps = fpsCount + fpsCountTime -= 1f + fpsCount = 0 + } + + currentScreen.update(timeInSeconds.toFloat(), currentDelta) + currentScreen.render() + } + } + + window.requestAnimationFrame { + gameLoop() + } + } + +} diff --git a/src/main/kotlin/games/perses/game/Screen.kt b/src/main/kotlin/games/perses/game/Screen.kt new file mode 100644 index 0000000..5d00bd4 --- /dev/null +++ b/src/main/kotlin/games/perses/game/Screen.kt @@ -0,0 +1,35 @@ +package games.perses.game + +import org.khronos.webgl.WebGLRenderingContext + +/** + * Created by rnentjes on 19-4-16. + */ + +abstract class Screen { + + open fun loadResources() { + + } + + open fun closeResources() { + + } + + abstract fun update(time: Float, delta: Float) + + abstract fun render() + +} + + +class DefaultScreen: Screen() { + override fun update(time: Float, delta: Float) { + } + + override fun render() { + // show loading message? + Game.gl().clearColor(1f, 1f, 0f, 1f) + Game.gl().clear(WebGLRenderingContext.COLOR_BUFFER_BIT) + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/game/View.kt b/src/main/kotlin/games/perses/game/View.kt new file mode 100644 index 0000000..b51342b --- /dev/null +++ b/src/main/kotlin/games/perses/game/View.kt @@ -0,0 +1,221 @@ +package games.perses.game + +import games.perses.math.Matrix4 + +enum class ViewType { + PROJECTION, + WIDTH, + HEIGHT, + ABSOLUTE +} + +class View( + var lastWindowWidth: Int = 2000, + var lastWindowHeight: Int = 1000, + var windowWidth: Int = 2000, + var windowHeight: Int = 1000, + var width: Float = 1024f, + var height: Float = 1024f, + var angle: Float = 60f, + var near: Float = -0.1f, + var far: Float = -100f, + var minAspectRatio: Float = 1f, + var maxAspectRatio: Float = 1f, + var leftOffset: Int = 0, + var bottomOffset: Int = 0, + var viewType: ViewType = ViewType.WIDTH, + var drawMode: DrawMode = DrawMode.LINEAR) { + var vMatrix = Matrix4() + var aspectRatio = 1f + + init { + updateView() + } + + fun requestFullscreen() { + //println("Requesting fullscreen") + js(""" + if (document.webkitFullscreenElement) { + document.webkitCancelFullScreen(); + } else { + document.documentElement.webkitRequestFullScreen(); + } + """) + //if (document.fullscreenEnabled) { + // println("fullscreen Enabled") + //Game.html.container.requestFullscreen() + //document.documentElement?.requestFullscreen() + //} + } + + fun updateView() { + aspectRatio = windowWidth / windowHeight.toFloat() + + if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio + + windowHeight = (windowWidth / aspectRatio).toInt() + } + + if (aspectRatio > maxAspectRatio) { + aspectRatio = maxAspectRatio + + windowWidth = (windowHeight * aspectRatio).toInt() + } + + when (viewType) { + ViewType.ABSOLUTE -> { + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.WIDTH -> { + height = width / aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.HEIGHT -> { + width = height * aspectRatio + + vMatrix.setOrthographicProjection(0f, width, 0f, height, near, far) + } + ViewType.PROJECTION -> { + vMatrix.setPerspectiveProjection(angle, aspectRatio, near, far) + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + //println("width: $width, height: $height") + } + + fun screenToGameCoordX(screenX: Float): Float { + var result = screenX + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.HEIGHT -> { + result = (screenX / windowWidth * width) - width / 2 + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun screenToGameCoordY(screenY: Float): Float { + var result = screenY + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.HEIGHT -> { + result = -((screenY / windowHeight * height) - height / 2) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordX(gameX: Float): Float { + var result = gameX + val normalizedX = gameX + (width / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = (windowWidth / width * normalizedX) + } + ViewType.HEIGHT -> { + result = (windowWidth / width * normalizedX) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun gameToScreenCoordY(gameY: Float): Float { + var result = gameY + val normalizedY = gameY + (height / 2) + + when (viewType) { + ViewType.ABSOLUTE -> { + // nop + } + ViewType.WIDTH -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.HEIGHT -> { + result = windowHeight - (windowHeight / height * normalizedY) + } + ViewType.PROJECTION -> { + // uhm + } + else -> { + throw IllegalStateException("ViewType $viewType not implemented!") + } + } + + return result + } + + fun setToWidth(width: Float) { + this.width = width + this.viewType = ViewType.WIDTH + + updateView() + } + + fun setToHeight(height: Float) { + this.height = height + this.viewType = ViewType.HEIGHT + + updateView() + } + + fun setProjection(angle: Float) { + this.angle = angle + this.viewType = ViewType.PROJECTION + + updateView() + } + + fun setNear(near: Float) { + this.near = near + + updateView() + } + + fun setFar(far: Float) { + this.far = far + + updateView() + } +} \ No newline at end of file diff --git a/src/main/kotlin/games/perses/input/Keys.kt b/src/main/kotlin/games/perses/input/Keys.kt new file mode 100644 index 0000000..a3ec0d3 --- /dev/null +++ b/src/main/kotlin/games/perses/input/Keys.kt @@ -0,0 +1,143 @@ +package games.perses.input + +import games.perses.game.Game +import org.w3c.dom.events.Event +import org.w3c.dom.events.KeyboardEvent +import org.w3c.dom.events.MouseEvent +import kotlin.browser.document +import kotlin.js.Date + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:18 + */ + +enum class KeyCode(val keyCode: Int) { + LEFT(37), + UP(38), + DOWN(40), + RIGHT(39), + SPACE(32), + MINUS(109), + PLUS(107), +} + +interface InputProcessor { + + fun keyPressed(charCode: Int) + + fun keyDown(keyCode: Int) + + fun keyUp(keyCode: Int) + + fun pointerClick(pointer: Int, x: Float, y: Float) + +} + +open class EmptyInputProcessor : InputProcessor { + + override fun pointerClick(pointer: Int, x: Float, y: Float) { } + + override fun keyDown(keyCode: Int) { } + + override fun keyPressed(charCode: Int) { } + + override fun keyUp(keyCode: Int) { } +} + +object Keys { + + private val keys: MutableMap = HashMap() + private var inputProcesser: InputProcessor = EmptyInputProcessor() + + init { + val body = document.body + if (body != null) { + body.onkeydown = { + keyDown(it) + } + + body.onkeyup = { + keyUp(it) + } + + body.onkeypress = { + keyPress(it) + } + + body.onclick = { + mouseClick(it) + } + + body.onmousedown = { + mouseMove(it) + } + + body.onmouseup = { + mouseMove(it) + } + + body.onmousemove = { + mouseMove(it) + } + } else { + console.log("Can't register key events, document.body is null!?") + } + } + + fun setInputProcessor(processor: InputProcessor) { + this.inputProcesser = processor + } + + private fun keyDown(key: Event) { + if (key is KeyboardEvent) { + keys.put(key.keyCode, Date().getTime()) + + inputProcesser.keyDown(key.keyCode) + } + } + + private fun keyUp(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyUp(key.keyCode) + + keys.remove(key.keyCode) + } + } + + private fun keyPress(key: Event) { + if (key is KeyboardEvent) { + inputProcesser.keyPressed(key.charCode) + } + } + + private fun mouseClick(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + inputProcesser.pointerClick(event.button.toInt(), vx, vy) + } + } + + private fun mouseMove(event: Event) { + if (event is MouseEvent) { + val vx: Float = Game.view.screenToGameCoordX(event.clientX.toFloat()) + val vy: Float = Game.view.screenToGameCoordY(event.clientY.toFloat()) + + + } + } + + fun isDown(keyCode: Int) = keys.containsKey(keyCode) + + fun isDown(keyCode: KeyCode) = keys.containsKey(keyCode.keyCode) + + fun wasPressed(keyCode: Int, delta: Double): Boolean { + val time = keys[keyCode] + + return (time != null && time > (Date().getTime() - delta)) + } + +} diff --git a/src/main/kotlin/games/perses/map/Map.kt b/src/main/kotlin/games/perses/map/Map.kt new file mode 100644 index 0000000..7c6a8e3 --- /dev/null +++ b/src/main/kotlin/games/perses/map/Map.kt @@ -0,0 +1,9 @@ +package games.perses.map + +/** + * Created by rnentjes on 22-7-16. + */ + +open class Map { + +} diff --git a/src/main/kotlin/games/perses/map/tiled/TiledMap.kt b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt new file mode 100644 index 0000000..f1c7808 --- /dev/null +++ b/src/main/kotlin/games/perses/map/tiled/TiledMap.kt @@ -0,0 +1,211 @@ +package games.perses.map.tiled + +import games.perses.net.getUrlAsString +import games.perses.texture.Texture +import games.perses.texture.Textures +import kotlin.browser.window +import kotlin.js.Math + +/** + * Created by rnentjes on 22-7-16. + */ + +class MapData { + var version: Int = 1 + var properties: MutableMap = HashMap() + var layers: Array? = null + var tilesets: Array? = null + + var height: Int = 0 + var width: Int = 0 + + var nextobjectid: Int = 0 + var orientation: String = "orthogonal" + var renderorder: String = "right-down" + var tileheight: Int = 0 + var tilewidth: Int = 0 +} + +class MapLayer { + var properties: MutableMap = HashMap() + + var data: Array? = null + var encoding: String = "" + var x: Int = 0 + var y: Int = 0 + var width: Int = 0 + var height: Int = 0 + var name: String = "" + var opacity: Float = 1f + var type: String = "" + var visible: Boolean = true + var draworder: String = "" +} + +class MapTileset { + var properties: MutableMap = HashMap() + + var firstgid: Int = 0 + var image: String = "" + var imageheight: Int = 0 + var imagewidth: Int = 0 + var margin: Int = 0 + var name: String = "" + var spacing: Int = 0 + var tilecount: Int = 0 + var tileheight: Int = 0 + var tilewidth: Int = 0 + var tileproperties: MutableMap> = HashMap() +} + +class TilesetIndex( + val texture: Texture?, + val tcLeft: Float, + val tcTop: Float, + val tcRight: Float, + val tcBottom: Float, + val scale: Float + ) { + constructor() : this(null, 0f, 0f, 0f, 0f, 0f) + + fun render(x: Float, y: Float) { + texture?.queueTileDraw(x, y, tcLeft, tcTop, tcRight, tcBottom, scale) + } +} + +class TiledMap(dir: String = "", url: String) { + val properties: Map = HashMap() + val data: MapData + val tileset: Array + val tiles: Array + var first = true + //var tilesetIndex: Array = Array(0, { TilesetIndex() }) + + init { + var tileDir = dir + if (!tileDir.isEmpty() && !tileDir.endsWith("/")) { + tileDir = tileDir + "/" + } + + data = JSON.parse(getUrlAsString(tileDir + url)) + //println("map data is loaded") + val tilesets = data.tilesets + if (tilesets != null) { + tileset = Array(tilesets.size, { "" }) + var maxGid = 0 + for (index in 0..tilesets.size - 1) { + tileset[index] = tilesets[index].name + Textures.load(tilesets[index].name, tileDir + tilesets[index].image) + maxGid = Math.max(maxGid, tilesets[index].firstgid + tilesets[index].tilecount) + } + + tiles = Array(maxGid, { TilesetIndex() }) + } else { + tileset = Array(0, { "" }) + tiles = Array(0, { TilesetIndex() }) + } + + cacheTiles() + } + + fun cacheTiles() { + if (!Textures.ready()) { + window.setTimeout({ cacheTiles() }, 10) + } else { + val tilesets = data.tilesets + var tcLeft = 0f + var tcTop = 0f + var tcRight = 0f + var tcBottom = 0f + + if (tilesets != null) { + + + for (tileset in tilesets) { + val tilesHor = tileset.imagewidth / tileset.tilewidth + val tilesVer = tileset.imageheight / tileset.tileheight + val scale = (tileset.tilewidth / tileset.imagewidth.toFloat()) + + for (index in tileset.firstgid..tileset.firstgid + tileset.tilecount) { + val texture = Textures.get(tileset.name) + + val gid = index - tileset.firstgid + + val xi = gid % tilesHor + var yi = gid / tilesHor + yi = tilesVer - yi - 1 + val tw = 1f / tilesHor.toFloat() + val th = 1f / tilesVer.toFloat() + + val pixelW = 0.1f / tileset.tilewidth + val pixelH = 0.1f / tileset.tileheight + + tcLeft = xi * tw + tcRight = tcLeft + tw + + // switch up/down because of texture coord 0,0 in left bottom corner + tcBottom = yi * th + tcTop = tcBottom + th + + tcLeft += pixelW + tcRight -= pixelW + + tcBottom += pixelH + tcTop -= pixelH + + tiles[index] = TilesetIndex(texture, tcLeft, tcTop, tcRight, tcBottom, scale) + } + } + } + } + } + + fun drawTile(tile: Int, x: Float, y: Float) { + tiles[tile].render(x, y) + } + + fun drawLayer(layerIndex: Int, xo: Float, yo: Float) { + var x = 0f + var y = 0f + val layers = data.layers ?: throw IllegalArgumentException("MapData has no layers ($data)") + val layer = layers[layerIndex] + + val layerData = layer.data + if (layerData != null) { + for (index in layerData.indices) { + // todo: determine if in view + // todo: determine tilewidth + //if (xo+x*128f < Game.view.width && yo + y * 128 < Game.view.height) { + drawTile(layerData[index], xo + x * 128f, yo + y * 128f) + + when (data.renderorder) { + "right-down" -> { + x++ + if (x >= layer.width) { + x = 0f + y-- + } + } + else -> { + throw IllegalStateException("Renderorder ${data.renderorder} not supported in $this") + } + } + //} + } + } + + val tilesets = data.tilesets + if (tilesets != null) { + for (tileset in tilesets) { + if (Textures.has(tileset.name)) { + val tx = Textures.get(tileset.name) + + tx.render() + } + } + } + + + first = false + } +} diff --git a/src/main/kotlin/games/perses/math/Matrix4.kt b/src/main/kotlin/games/perses/math/Matrix4.kt new file mode 100644 index 0000000..7da03cd --- /dev/null +++ b/src/main/kotlin/games/perses/math/Matrix4.kt @@ -0,0 +1,193 @@ +package games.perses.math + +import org.khronos.webgl.Float32Array +import kotlin.js.Math + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:43 + */ +class Matrix4 { + + internal var matrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + internal var temp = FloatArray(16) + + private val translateMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val scaleMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateXMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateYMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + private val rotateZMatrix = floatArrayOf(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f) + + fun get(): FloatArray { + return matrix + } + + fun getFloat32Array() = Float32Array(get().toTypedArray()) + + fun set(values: FloatArray) { + if (values.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + matrix = values + } + + fun setPerspectiveProjection(angle: Float, imageAspectRatio: Float, near: Float, far: Float) { + val r = (angle / 180f * Math.PI).toFloat() + val f = (1.0f / Math.tan((r / 2.0f).toDouble())).toFloat() + + matrix[0] = f / imageAspectRatio + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + + matrix[4] = 0.0f + matrix[5] = f + matrix[6] = 0.0f + matrix[7] = 0.0f + + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = -(far + near) / (far - near) + matrix[11] = -1.0f + + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = -(2.0f * far * near) / (far - near) + matrix[15] = 0.0f + } + + fun setOrthographicProjection(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) { + matrix[0] = 2f / (right - left) + matrix[1] = 0f + matrix[2] = 0f + matrix[3] = 0f + + matrix[4] = 0f + matrix[5] = 2f / (top - bottom) + matrix[6] = 0f + matrix[7] = 0f + + matrix[8] = 0f + matrix[9] = 0f + matrix[10] = -2f / (far - near) + matrix[11] = 0f + + matrix[12] = - (right + left) / (right - left) + matrix[13] = - (top + bottom) / (top - bottom) + matrix[14] = - (far + near) / (far - near) + matrix[15] = 1f + } + + fun setToIdentity() { + matrix[0] = 1.0f + matrix[1] = 0.0f + matrix[2] = 0.0f + matrix[3] = 0.0f + matrix[4] = 0.0f + matrix[5] = 1.0f + matrix[6] = 0.0f + matrix[7] = 0.0f + matrix[8] = 0.0f + matrix[9] = 0.0f + matrix[10] = 1.0f + matrix[11] = 0.0f + matrix[12] = 0.0f + matrix[13] = 0.0f + matrix[14] = 0.0f + matrix[15] = 1.0f + } + + fun mul(other: Matrix4) { + mul(other.get()) + } + + protected fun mul(other: FloatArray) { + if (other.size != 16) { + throw IllegalArgumentException("Matrix size should be 16!") + } + + temp[0] = matrix[0] * other[0] + matrix[1] * other[4] + matrix[2] * other[8] + matrix[3] * other[12] + temp[1] = matrix[0] * other[1] + matrix[1] * other[5] + matrix[2] * other[9] + matrix[3] * other[13] + temp[2] = matrix[0] * other[2] + matrix[1] * other[6] + matrix[2] * other[10] + matrix[3] * other[14] + temp[3] = matrix[0] * other[3] + matrix[1] * other[7] + matrix[2] * other[11] + matrix[3] * other[15] + temp[4] = matrix[4] * other[0] + matrix[5] * other[4] + matrix[6] * other[8] + matrix[7] * other[12] + temp[5] = matrix[4] * other[1] + matrix[5] * other[5] + matrix[6] * other[9] + matrix[7] * other[13] + temp[6] = matrix[4] * other[2] + matrix[5] * other[6] + matrix[6] * other[10] + matrix[7] * other[14] + temp[7] = matrix[4] * other[3] + matrix[5] * other[7] + matrix[6] * other[11] + matrix[7] * other[15] + temp[8] = matrix[8] * other[0] + matrix[9] * other[4] + matrix[10] * other[8] + matrix[11] * other[12] + temp[9] = matrix[8] * other[1] + matrix[9] * other[5] + matrix[10] * other[9] + matrix[11] * other[13] + temp[10] = matrix[8] * other[2] + matrix[9] * other[6] + matrix[10] * other[10] + matrix[11] * other[14] + temp[11] = matrix[8] * other[3] + matrix[9] * other[7] + matrix[10] * other[11] + matrix[11] * other[15] + temp[12] = matrix[12] * other[0] + matrix[13] * other[4] + matrix[14] * other[8] + matrix[15] * other[12] + temp[13] = matrix[12] * other[1] + matrix[13] * other[5] + matrix[14] * other[9] + matrix[15] * other[13] + temp[14] = matrix[12] * other[2] + matrix[13] * other[6] + matrix[14] * other[10] + matrix[15] * other[14] + temp[15] = matrix[12] * other[3] + matrix[13] * other[7] + matrix[14] * other[11] + matrix[15] * other[15] + + matrix[0] = temp[0] + matrix[1] = temp[1] + matrix[2] = temp[2] + matrix[3] = temp[3] + matrix[4] = temp[4] + matrix[5] = temp[5] + matrix[6] = temp[6] + matrix[7] = temp[7] + matrix[8] = temp[8] + matrix[9] = temp[9] + matrix[10] = temp[10] + matrix[11] = temp[11] + matrix[12] = temp[12] + matrix[13] = temp[13] + matrix[14] = temp[14] + matrix[15] = temp[15] + } + + fun translate(x: Float, y: Float, z: Float) { + translateMatrix[12] = x + translateMatrix[13] = y + translateMatrix[14] = z + + mul(translateMatrix) + } + + fun scale(x: Float, y: Float, z: Float) { + scaleMatrix[0] = x + scaleMatrix[5] = y + scaleMatrix[10] = z + + mul(scaleMatrix) + } + + fun rotateX(angle: Float) { + rotateXMatrix[5] = Math.cos(angle.toDouble()).toFloat() + rotateXMatrix[6] = (-Math.sin(angle.toDouble())).toFloat() + rotateXMatrix[9] = Math.sin(angle.toDouble()).toFloat() + rotateXMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateXMatrix) + } + + fun rotateY(angle: Float) { + rotateYMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateYMatrix[2] = Math.sin(angle.toDouble()).toFloat() + rotateYMatrix[8] = (-Math.sin(angle.toDouble())).toFloat() + rotateYMatrix[10] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateYMatrix) + } + + fun rotateZ(angle: Float) { + rotateZMatrix[0] = Math.cos(angle.toDouble()).toFloat() + rotateZMatrix[1] = Math.sin(angle.toDouble()).toFloat() + rotateZMatrix[4] = (-Math.sin(angle.toDouble())).toFloat() + rotateZMatrix[5] = Math.cos(angle.toDouble()).toFloat() + + mul(rotateZMatrix) + } +} diff --git a/src/main/kotlin/games/perses/net/NetUtils.kt b/src/main/kotlin/games/perses/net/NetUtils.kt new file mode 100644 index 0000000..5b5b820 --- /dev/null +++ b/src/main/kotlin/games/perses/net/NetUtils.kt @@ -0,0 +1,18 @@ +package games.perses.net + +import org.w3c.xhr.XMLHttpRequest + +/** + * User: rnentjes + * Date: 30-7-16 + * Time: 16:39 + */ + +fun getUrlAsString(url: String): String { + val req = XMLHttpRequest() + + req.open("GET", url, false) + req.send(null) + + return req.responseText +} diff --git a/src/main/kotlin/games/perses/shader/ShaderProgram.kt b/src/main/kotlin/games/perses/shader/ShaderProgram.kt new file mode 100644 index 0000000..a227158 --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgram.kt @@ -0,0 +1,109 @@ +package games.perses.shader + +import org.khronos.webgl.* + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:15 + */ + +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 = 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 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/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt new file mode 100644 index 0000000..36356bd --- /dev/null +++ b/src/main/kotlin/games/perses/shader/ShaderProgramMesh.kt @@ -0,0 +1,67 @@ +package games.perses.shader + +import org.khronos.webgl.Float32Array +import org.khronos.webgl.WebGLBuffer +import org.khronos.webgl.WebGLRenderingContext + +/** + * User: rnentjes + * Date: 14-5-16 + * Time: 11:57 + */ + +class VertextAttributeInfo(val locationName: String, val numElements: Int) { + var location = 0 + var offset = 0 +} + +class ShaderProgramMesh( + val shaderProgram: ShaderProgram +) { + val webgl = shaderProgram.webgl + val data: Float32Array = Float32Array(20000 - (20000 % shaderProgram.drawLength)) + var currentIndex: Int = 0 + val attribBuffer: WebGLBuffer + var counter = 0 + + init { + + attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!") + webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer); + } + + fun queue(vararg vertices: Float) { + queueArray(vertices as Array) + } + + fun queueArray(vertices: Array) { + data.set(vertices, currentIndex) + currentIndex += vertices.size + + if (bufferFull()) { + //println("Skipped draw call, to many values!") + currentIndex = 0 + } + } + + fun remaining() = data.length - currentIndex + + fun bufferFull() = currentIndex == data.length + + fun render(userdata: T) { + counter++ + if (currentIndex > 0) { + if (currentIndex % shaderProgram.verticesBlockSize != 0) { + throw IllegalStateException("Number of vertices not a multiple of the attribute block size!") + } + + shaderProgram.begin(attribBuffer, userdata) + + webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, data, WebGLRenderingContext.DYNAMIC_DRAW) + webgl.drawArrays(shaderProgram.drawType, 0, (currentIndex / shaderProgram.verticesBlockSize).toInt()) + currentIndex = 0 + + shaderProgram.end() + } + } +} diff --git a/src/main/kotlin/games/perses/sound/Music.kt b/src/main/kotlin/games/perses/sound/Music.kt new file mode 100644 index 0000000..90bac23 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Music.kt @@ -0,0 +1,47 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 13:02 + */ + +object Music { + val playing: MutableSet = HashSet() + + fun load(url: String): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + + return audio + } + + fun play(url: String, volume: Double = 0.75, looping: Boolean = false): HTMLAudioElement { + val audio = document.createElement("audio") as HTMLAudioElement + + audio.src = url + audio.volume = volume + audio.play() + + audio.onended = { + if (looping) { + audio.currentTime = 0.0 + audio.play() + } else { + //println("REMOVING: $audio") + audio.remove() + playing.remove(audio) + } + } + + return audio + } + + fun stopAll() { + + } +} diff --git a/src/main/kotlin/games/perses/sound/Sounds.kt b/src/main/kotlin/games/perses/sound/Sounds.kt new file mode 100644 index 0000000..806fd54 --- /dev/null +++ b/src/main/kotlin/games/perses/sound/Sounds.kt @@ -0,0 +1,61 @@ +package games.perses.sound + +import org.w3c.dom.HTMLAudioElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 18-5-16 + * Time: 12:34 + */ + +class Sound(val name:String, val url: String, val volume: Double = 0.75, val numberOfChannels: Int) { + var channels: Array + var nextChannel: Int = 0 + + init { + //println("CREATING: $name") + channels = Array(numberOfChannels, { document.createElement("audio") as HTMLAudioElement }) + + for (audio in channels) { + audio.src = url + audio.pause() + audio.load() + audio.volume = volume + } + } + + fun play() { + //println("PLAYING: $name - $nextChannel") + channels[nextChannel].currentTime = 0.0 + channels[nextChannel].play() + + nextChannel = (nextChannel + 1) % channels.size + } + + fun pause() { + for (audio in channels) { + audio.pause() + } + } +} + +object Sounds { + val sounds: MutableMap = HashMap() + + fun load(name: String, url: String, volume: Double = 0.75, channels: Int = 1 ) { + sounds.put(name, Sound(name, url, volume, channels)) + } + + fun play(name: String, volume: Float = 0.75f) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.play() + } + + fun pause(name: String) { + val sound: Sound = sounds[name] ?: throw IllegalArgumentException("Sound '$name' not found, load it first!") + + sound.pause() + } +} diff --git a/src/main/kotlin/games/perses/sprite/SpriteBatch.kt b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt new file mode 100644 index 0000000..3de1163 --- /dev/null +++ b/src/main/kotlin/games/perses/sprite/SpriteBatch.kt @@ -0,0 +1,26 @@ +package games.perses.sprite + +import games.perses.texture.Texture +import games.perses.texture.Textures + +/** + * User: rnentjes + * Date: 20-4-16 + * Time: 13:48 + */ + +class Sprite(val textureName: String) { + val texture: Texture by lazy { Textures.get(textureName) } +} + +class SpriteBatch { + + fun draw(sprite: Sprite, x: Float, y: Float, scale: Float = 1f, rotation: Float = 0f) { + sprite.texture.queueDraw(x, y, scale, rotation) + } + + fun render() { + Textures.render() + } + +} diff --git a/src/main/kotlin/games/perses/text/Texts.kt b/src/main/kotlin/games/perses/text/Texts.kt new file mode 100644 index 0000000..4caa6f7 --- /dev/null +++ b/src/main/kotlin/games/perses/text/Texts.kt @@ -0,0 +1,30 @@ +package games.perses.text + +import games.perses.game.Game + +/** + * Created by rnentjes on 16-5-16. + */ + +object Texts { + + fun drawText(x: Float, y: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { + var yy = y + if (yy < 0) { + yy += Game.view.height + } + Game.html.canvas2d.fillStyle = fillStyle + Game.html.canvas2d.font = font + Game.html.canvas2d.fillText(message, x.toDouble(), yy.toDouble()) + } + + fun drawLeftTop(left: Float, top: Float, message: String, font: String = "bold 24pt Arial", fillStyle: String = "white") { + drawText( + Game.view.gameToScreenCoordX(-Game.view.width / 2f + left), + Game.view.gameToScreenCoordY(Game.view.height / 2f - top), + message, + font, + fillStyle + ) + } +} diff --git a/src/main/kotlin/games/perses/texture/Sprites.kt b/src/main/kotlin/games/perses/texture/Sprites.kt new file mode 100644 index 0000000..4061eea --- /dev/null +++ b/src/main/kotlin/games/perses/texture/Sprites.kt @@ -0,0 +1,7 @@ +package games.perses.texture + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 15:44 + */ diff --git a/src/main/kotlin/games/perses/texture/Textures.kt b/src/main/kotlin/games/perses/texture/Textures.kt new file mode 100644 index 0000000..2ae5bda --- /dev/null +++ b/src/main/kotlin/games/perses/texture/Textures.kt @@ -0,0 +1,297 @@ +package games.perses.texture + +import games.perses.game.DrawMode +import games.perses.game.Game +import games.perses.map.tiled.MapTileset +import games.perses.math.Matrix4 +import games.perses.shader.ShaderProgram +import games.perses.shader.ShaderProgramMesh +import games.perses.shader.VertextAttributeInfo +import org.khronos.webgl.ArrayBufferView +import org.khronos.webgl.WebGLRenderingContext +import org.khronos.webgl.WebGLTexture +import org.w3c.dom.HTMLImageElement +import kotlin.browser.document + +/** + * User: rnentjes + * Date: 17-4-16 + * Time: 14:52 + */ +//language=GLSL +private val vertexShaderSource = """ + attribute vec2 a_position; + attribute vec2 a_boundingBox; + attribute vec2 a_texCoord; + attribute float a_scale; + attribute float a_rotation; + + uniform mat4 u_projectionView; + + varying vec2 v_textCoord; + + mat4 scale(float scale) { + return mat4( + vec4(scale, 0.0, 0.0, 0.0), + vec4(0.0, scale, 0.0, 0.0), + vec4(0.0, 0.0, scale, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ); + } + + mat4 rotateZ(float angle) { + return mat4( + vec4(cos(angle), sin(angle), 0.0, 0.0), + vec4(-sin(angle), cos(angle), 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ); + } + + void main(void) { + v_textCoord = a_texCoord; + + vec4 scaledBox = vec4(a_boundingBox, 1.0, 1.0) * scale(a_scale) * rotateZ(a_rotation); + + gl_Position = u_projectionView * vec4(a_position + scaledBox.xy, 1.0, 1.0); + } +""" + +private val fragmentShaderSource = """ + precision mediump float; + + uniform sampler2D u_sampler; + + varying vec2 v_textCoord; + + void main(void) { + gl_FragColor = texture2D(u_sampler, v_textCoord); + } +""" + +class TextureData( + val vMatrix: Matrix4, + val texture: WebGLTexture +) + +class Texture( + val glTexture: WebGLTexture, + val shaderProgram: ShaderProgram, + val width: Int, + val height: Int +) { + val shaderProgramMesh: ShaderProgramMesh = ShaderProgramMesh(shaderProgram) + val left = -width / 2f + val right = width / 2f + val bottom = -height / 2f + val top = height / 2f + + fun queueDraw(x: Float, y: Float, scale: Float, rotation: Float) { + shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) + shaderProgramMesh.queue( x, y, left, top, 0f, 1f, scale, rotation) + shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) + shaderProgramMesh.queue( x, y, right, top, 1f, 1f, scale, rotation) + shaderProgramMesh.queue( x, y, right, bottom, 1f, 0f, scale, rotation) + shaderProgramMesh.queue( x, y, left, bottom, 0f, 0f, scale, rotation) + + if (shaderProgramMesh.remaining() < 36) { + render() + } + } + + fun queueTileDraw(x: Float, y: Float, tcLeft: Float, tcTop: Float, tcRight: Float, tcBottom: Float, scale: Float) { + shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) + shaderProgramMesh.queue( x, y, left, top, tcLeft, tcTop, scale, 0f) + shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) + shaderProgramMesh.queue( x, y, right, top, tcRight, tcTop, scale, 0f) + shaderProgramMesh.queue( x, y, right, bottom, tcRight, tcBottom, scale, 0f) + shaderProgramMesh.queue( x, y, left, bottom, tcLeft, tcBottom, scale, 0f) + + if (shaderProgramMesh.remaining() < 36) { + render() + } + } + + fun render() { + Game.gl().activeTexture(WebGLRenderingContext.TEXTURE0) + Game.gl().bindTexture(WebGLRenderingContext.TEXTURE_2D, glTexture) + + shaderProgramMesh.render(TextureData(Game.view.vMatrix, glTexture)) + } +} + +/* +{ + "frame": {"x":921,"y":1,"w":182,"h":103}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":4,"w":182,"h":103}, + "sourceSize": {"w":190,"h":110}, + "pivot": {"x":0.5,"y":0.5} +}, +*/ + +class Rect(val x: Int, val y: Int, val w: Int, val h: Int) +class Size(val w: Int, val h: Int) +class Pivot(val x: Double, val y: Double) + +class SpriteSheetData( + val frame: Rect, + val rotated: Boolean, + val trimmed: Boolean, + val spriteSourceSize: Rect, + val sourceSize: Size, + val pivot: Pivot + ) + +class SpriteSheet( + val glTexture: WebGLTexture, + val shaderProgram: ShaderProgram, + val data: Map +) { + +} + +object Textures { + var textures = HashMap() + var startedLoading = 0 + var loaded = 0 + val shaderProgram: ShaderProgram + + init { + val setter = { program: ShaderProgram, data: TextureData -> + program.setUniform1i("u_sampler", 0) + program.setUniformMatrix4fv("u_projectionView", Game.view.vMatrix.getFloat32Array()) + } + + val vainfo = arrayOf( + VertextAttributeInfo("a_position", 2), + VertextAttributeInfo("a_boundingBox", 2), + VertextAttributeInfo("a_texCoord", 2), + VertextAttributeInfo("a_scale", 1), + VertextAttributeInfo("a_rotation", 1) + ) + + shaderProgram = ShaderProgram(Game.gl(), WebGLRenderingContext.Companion.TRIANGLES, vertexShaderSource, fragmentShaderSource, vainfo, setter) + } + + fun loadSpriteSheet(name: String) { + //val data = Request(name).json() + + //println(data) + } + + fun load(name: String, filename: String) { + val gl = Game.gl() + + startedLoading++ + + val webGlTexture = gl.createTexture() + if (webGlTexture != null) { + val image = document.createElement("img") as HTMLImageElement + image.onload = { + textureLoaded(webGlTexture, image) + val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) + + textures.put(name, texture) + + loaded++ + //println("loaded texture $loaded/$startedLoading ${ready()}") + } + image.src = filename + } else { + throw IllegalStateException("Couldn't create webgl texture!") + } + } + + fun create(name: String, image: HTMLImageElement) { + val gl = Game.gl() + + startedLoading++ + + val webGlTexture = gl.createTexture() + if (webGlTexture != null) { + textureLoaded(webGlTexture, image) + val texture = Texture(webGlTexture, shaderProgram, image.width, image.height) + + textures.put(name, texture) + + loaded++ + } else { + throw IllegalStateException("Couldn't create webgl texture!") + } + } + + + fun create(name: String, width: Int, height: Int, image: ArrayBufferView) { + val gl = Game.gl() + + startedLoading++ + + val webGlTexture = gl.createTexture() + if (webGlTexture != null) { + textureLoaded(webGlTexture, width, height, image) + val texture = Texture(webGlTexture, shaderProgram, width, height) + + textures.put(name, texture) + + loaded++ + } else { + throw IllegalStateException("Couldn't create webgl texture!") + } + } + + fun load(mapTileSet: MapTileset) { + + } + + private fun textureLoaded(texture: WebGLTexture, image: HTMLImageElement) { + val gl = Game.gl() + + gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) + gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int + gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) + setTextureParameters(); + gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) + } + + private fun textureLoaded(texture: WebGLTexture, width: Int, height: Int, image: ArrayBufferView) { + val gl = Game.gl() + + gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture) + gl.pixelStorei(WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL, 1) // second argument must be an int + gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image) + setTextureParameters(); + gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, null) + } + + private fun setTextureParameters() { + val gl = Game.gl() + + if (Game.view.drawMode == DrawMode.NEAREST) { + gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST) + gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST) + } else { + gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.LINEAR) + gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.LINEAR) + } + gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_T, WebGLRenderingContext.CLAMP_TO_EDGE) + gl.texParameteri(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_WRAP_S, WebGLRenderingContext.CLAMP_TO_EDGE) + } + + fun ready() = loaded == startedLoading + + fun has(name: String) = textures[name] != null + fun get(name: String) = textures[name] ?: throw IllegalArgumentException("Texture with name $name is not loaded!") + + fun clear() { + // delete and unbind all textures... + } + + fun render() { + for ((key, value) in textures) { + value.render() + } + } + +}