From 4338887efb81828349ed41a4590ff1ad6f8b55f0 Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Tue, 26 May 2020 23:19:46 +0100 Subject: [PATCH 01/21] Updated UI library --- .../engine/context/loader/FontLoader.kt | 3 +- .../engine/context/loader/GLLoader.kt | 7 + .../mechanica/engine/shader/script/Shader.kt | 32 +- .../shader/script/ShaderDeclarations.kt | 0 .../engine/shader/script/ShaderScript.kt | 8 +- .../shader/vars/uniforms/vars/UniformVar.kt | 13 +- .../kotlin/com/mechanica/engine/text/Font.kt | 4 +- .../engine/text/FontAtlasConfiguration.kt | 16 + .../mechanica/engine/memory/BitmapBuffer.kt | 110 +++++ .../engine/unit/vector/DynamicIntVector.kt | 20 + .../mechanica/engine/unit/vector/IntVector.kt | 6 + .../src/main/java/GLFWUtil.java | 49 +++ desktop-application/src/main/java/IOUtil.java | 71 +++ .../src/main/java/TruetypeOversample.java | 413 ++++++++++++++++++ .../engine/context/loader/LwjglFontLoader.kt | 12 +- .../engine/context/loader/LwjglLoader.kt | 18 + .../mechanica/engine/memory/MemoryTools.kt | 6 + .../engine/memory/RectangleFloatBuffers.kt | 12 + .../engine/memory/RectangleIntBuffers.kt | 11 + .../engine/memory/buffer/BufferTools.kt | 7 + .../engine/shader/script/ShaderImpl.kt | 14 - .../engine/shader/script/ShaderLoader.kt | 3 - .../com/mechanica/engine/text/FontAtlas.kt | 172 ++++++-- .../com/mechanica/engine/text/FontMetrics.kt | 31 ++ .../com/mechanica/engine/text/FontUtils.kt | 49 +++ .../com/mechanica/engine/text/LwjglFont.kt | 117 ----- .../text/LwjglFontAtlasConfiguration.kt | 57 +++ .../engine/text/LwjglStandardFont.kt | 63 +++ .../src/main/kotlin/temp/TextToPng.kt | 200 +++++++++ .../engine/drawer/shader/DrawerRenderer.kt | 2 +- .../engine/drawer/shader/DrawerShader.kt | 15 +- .../com/mechanica/engine/resources/Res.kt | 2 +- .../engine/samples/drawer/DrawerDemo.kt | 2 - .../engine/samples/text/FontRenderer.kt | 36 +- .../mechanica/engine/samples/text/SDFTest.kt | 7 + .../engine/samples/text/TextEditor.kt | 4 +- 36 files changed, 1369 insertions(+), 223 deletions(-) rename {desktop-application => application-interface}/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt (55%) rename {desktop-application => application-interface}/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt (100%) rename {desktop-application => application-interface}/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt (78%) create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/text/FontAtlasConfiguration.kt create mode 100644 common/src/main/kotlin/com/mechanica/engine/memory/BitmapBuffer.kt create mode 100644 common/src/main/kotlin/com/mechanica/engine/unit/vector/DynamicIntVector.kt create mode 100644 common/src/main/kotlin/com/mechanica/engine/unit/vector/IntVector.kt create mode 100644 desktop-application/src/main/java/GLFWUtil.java create mode 100644 desktop-application/src/main/java/IOUtil.java create mode 100644 desktop-application/src/main/java/TruetypeOversample.java create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleFloatBuffers.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleIntBuffers.kt delete mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderImpl.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/text/FontMetrics.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/text/FontUtils.kt delete mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFont.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFontAtlasConfiguration.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt create mode 100644 desktop-application/src/main/kotlin/temp/TextToPng.kt create mode 100644 samples/src/main/kotlin/com/mechanica/engine/samples/text/SDFTest.kt diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/FontLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/FontLoader.kt index bfdd2f76..a9380c8e 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/FontLoader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/FontLoader.kt @@ -2,9 +2,10 @@ package com.mechanica.engine.context.loader import com.mechanica.engine.text.Font import com.mechanica.engine.resources.Resource +import com.mechanica.engine.text.FontAtlasConfiguration interface FontLoader { val defaultFont: Font - fun font(res: Resource): Font + fun font(res: Resource, initializer: FontAtlasConfiguration.() -> Unit): Font } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt index a4089731..c9df7ac6 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt @@ -3,6 +3,8 @@ package com.mechanica.engine.context.loader import com.mechanica.engine.context.GLInitializer import com.mechanica.engine.shader.qualifiers.AttributeQualifier import com.mechanica.engine.shader.qualifiers.Qualifier +import com.mechanica.engine.shader.script.Shader +import com.mechanica.engine.shader.script.ShaderScript import com.mechanica.engine.vertices.ElementArrayType interface GLLoader { @@ -15,6 +17,11 @@ interface GLLoader { fun createAttributeLoader(qualifier: AttributeQualifier): AttributeLoader fun createUniformLoader(qualifier: Qualifier): UniformLoader fun createElementArray(): ElementArrayType + fun defaultShader( + vertex: ShaderScript, + fragment: ShaderScript, + tessellation: ShaderScript?, + geometry: ShaderScript?): Shader companion object : GLLoader by GLInitializer.loader } \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt similarity index 55% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt index e8099174..42dcfa02 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt @@ -1,29 +1,36 @@ package com.mechanica.engine.shader.script +import com.mechanica.engine.context.loader.GLLoader import com.mechanica.engine.models.Bindable import com.mechanica.engine.models.Model -import org.lwjgl.opengl.GL20 - -abstract class Shader { - abstract val vertex: ShaderScript - abstract val fragment: ShaderScript - abstract val tessellation: ShaderScript? - abstract val geometry: ShaderScript? +abstract class Shader( + val vertex: ShaderScript, + val fragment: ShaderScript, + val tessellation: ShaderScript?, + val geometry: ShaderScript?) { abstract val id: Int + private var locationsFound = false + protected fun load() { - GL20.glUseProgram(id) + loadProgram(id) loadUniforms() } + protected abstract fun loadProgram(id: Int) + private fun loadUniforms() { + if (!locationsFound) findLocations() + vertex.loadVariables() fragment.loadVariables() tessellation?.loadVariables() geometry?.loadVariables() } + abstract fun loadUniformLocation(name: String): Int + open fun render(inputs: Array, draw: () -> Unit) { load() @@ -40,13 +47,20 @@ abstract class Shader { model.draw() } + private fun findLocations() { + vertex.loadUniformLocations(this) + geometry?.loadUniformLocations(this) + fragment.loadUniformLocations(this) + locationsFound = true + } + companion object { operator fun invoke( vertex: ShaderScript, fragment: ShaderScript, tessellation: ShaderScript? = null, geometry: ShaderScript? = null): Shader { - return ShaderImpl(vertex, fragment, tessellation, geometry) + return GLLoader.defaultShader(vertex, fragment, tessellation, geometry) } } } \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt similarity index 100% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt similarity index 78% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt index bcfd8658..becafeb6 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt @@ -2,14 +2,11 @@ package com.mechanica.engine.shader.script import com.mechanica.engine.models.Bindable import com.mechanica.engine.shader.vars.uniforms.vars.UniformVar -import org.lwjgl.opengl.GL20 abstract class ShaderScript : ShaderDeclarations("autoVal") { val script: String get() = generateScript() - private var programId: Int = 0 - abstract val main: String private val inputs: Array = Array(20) { null } @@ -22,11 +19,10 @@ abstract class ShaderScript : ShaderDeclarations("autoVal") { return sb.toString() } - internal fun loadProgram(programId: Int) { - this.programId = programId + fun loadUniformLocations(shader: Shader) { for (v in iterator) { if (v is UniformVar<*>) { - v.location = GL20.glGetUniformLocation(programId, v.locationName) + v.location = shader.loadUniformLocation(v.locationName) } } } diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVar.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVar.kt index 55378946..9893e5ce 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVar.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVar.kt @@ -13,14 +13,19 @@ abstract class UniformVar : ShaderVariable { var location = 0 - private val regex = Regex("[^\\w\\d]") val locationName: String - get() { + get() = getLocationName(name) + + override fun toString() = name + + companion object { + private val regex = Regex("[^\\w\\d]") + + fun getLocationName(name: String): String { val index = regex.find(name)?.range?.first return if (index != null) { name.substring(0 until index) } else name } - - override fun toString() = name + } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/text/Font.kt b/application-interface/src/main/kotlin/com/mechanica/engine/text/Font.kt index 73c71125..f868fe4c 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/text/Font.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/text/Font.kt @@ -17,8 +17,8 @@ abstract class Font { abstract fun addCharacterDataToArrays(cursor: CharacterCursor, positions: Array, texCoords: Array) companion object { - fun create(resource: Resource): Font { - return GLLoader.fontLoader.font(resource) + fun create(resource: Resource, configureAtlas: FontAtlasConfiguration.() -> Unit = { }): Font { + return GLLoader.fontLoader.font(resource, configureAtlas) } } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/text/FontAtlasConfiguration.kt b/application-interface/src/main/kotlin/com/mechanica/engine/text/FontAtlasConfiguration.kt new file mode 100644 index 00000000..04f2dc64 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/text/FontAtlasConfiguration.kt @@ -0,0 +1,16 @@ +package com.mechanica.engine.text + +interface FontAtlasConfiguration { + var width: Int + var height: Int + var characterSize: Float + var padding: Float + var charRange: CharRange + fun configureSDF(block: SDFConfiguration.() -> Unit) + + class SDFConfiguration { + var start = -20.0 + var end = 20.0 + } +} + diff --git a/common/src/main/kotlin/com/mechanica/engine/memory/BitmapBuffer.kt b/common/src/main/kotlin/com/mechanica/engine/memory/BitmapBuffer.kt new file mode 100644 index 00000000..6f853029 --- /dev/null +++ b/common/src/main/kotlin/com/mechanica/engine/memory/BitmapBuffer.kt @@ -0,0 +1,110 @@ +package com.mechanica.engine.memory + +import com.mechanica.engine.unit.vector.DynamicIntVector +import java.nio.ByteBuffer + +class BitmapBuffer(val buffer: ByteBuffer, val width: Int, val height: Int) { + + val bufferPosition = object : DynamicIntVector { + private var _x = 0 + override var x: Int + get() = _x + set(value) { + set(value, _y) + } + + private var _y = 0 + override var y: Int + get() = _y + set(value) { + set(_x, value) + } + + override fun set(x: Int, y: Int) { + _x = x + _y = y + val row = y*width + buffer.position(row+x) + } + } + + val stBufferPosition = STVector() + + fun advanceBy(advance: Int) { + buffer.position(buffer.position() + advance) + } + + fun insert(other: ByteBuffer, otherWidth: Int, s: Float, t: Float) { + stBufferPosition.set(s, t) + insert(other, otherWidth) + } + + fun insert(other: ByteBuffer, otherWidth: Int, x: Int, y: Int) { + bufferPosition.set(x, y) + insert(other, otherWidth) + } + + fun insert(other: BitmapBuffer, s: Float, t: Float) { + stBufferPosition.set(s, t) + other.zero() + + insert(other.buffer, other.width) + } + + fun insert(other: BitmapBuffer, x: Int, y: Int) { + bufferPosition.set(x, y) + other.zero() + + insert(other.buffer, other.width) + } + + fun zero() { + buffer.position(0) + } + + private fun insert(other: ByteBuffer, otherWidth: Int) { + var j = 0 + val limit = other.limit() + while (other.position() < limit - otherWidth) { + other.limit((j + 1)*otherWidth - 1) + other.position(j++*otherWidth) + buffer.put(other) + advanceBy(width - otherWidth + 1) + } + } + private fun insertOld(other: ByteBuffer, otherWidth: Int) { + var j = 0 + while (other.hasRemaining()) { + var i = 0 + while (i++ < otherWidth) { + buffer.put(other.get()) + } + advanceBy(width - otherWidth) + j++ + } + } + + inner class STVector { + private var _s = 0f + var s: Float + get() = _s + set(value) { + set(value, _t) + } + + private var _t = 0f + var t: Float + get() = _t + set(value) { + set(_s, value) + } + + fun set(s: Float, t: Float) { + _s = s + _t = t + val x = (s*width).toInt() + val y = (t*height).toInt() + bufferPosition.set(x, y) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/vector/DynamicIntVector.kt b/common/src/main/kotlin/com/mechanica/engine/unit/vector/DynamicIntVector.kt new file mode 100644 index 00000000..3eb4c4a8 --- /dev/null +++ b/common/src/main/kotlin/com/mechanica/engine/unit/vector/DynamicIntVector.kt @@ -0,0 +1,20 @@ +package com.mechanica.engine.unit.vector + +interface DynamicIntVector : IntVector { + override var x: Int + override var y: Int + + fun set(x: Int, y: Int) { + this.x = x + this.y = y + } + + fun set(other: IntVector) = set(other.x, other.y) + + companion object { + fun create(x: Int = 0, y: Int = 0) = object : DynamicIntVector { + override var x = x + override var y = y + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/vector/IntVector.kt b/common/src/main/kotlin/com/mechanica/engine/unit/vector/IntVector.kt new file mode 100644 index 00000000..52c34953 --- /dev/null +++ b/common/src/main/kotlin/com/mechanica/engine/unit/vector/IntVector.kt @@ -0,0 +1,6 @@ +package com.mechanica.engine.unit.vector + +interface IntVector { + val x: Int + val y: Int +} \ No newline at end of file diff --git a/desktop-application/src/main/java/GLFWUtil.java b/desktop-application/src/main/java/GLFWUtil.java new file mode 100644 index 00000000..7e89c365 --- /dev/null +++ b/desktop-application/src/main/java/GLFWUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ + +import org.lwjgl.glfw.*; +import org.lwjgl.system.*; + +import java.nio.*; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.system.MemoryStack.*; + +/** GLFW demo utilities. */ +public final class GLFWUtil { + + private GLFWUtil() { + } + + /** + * Invokes the specified callbacks using the current window and framebuffer sizes of the specified GLFW window. + * + * @param window the GLFW window + * @param windowSizeCB the window size callback, may be null + * @param framebufferSizeCB the framebuffer size callback, may be null + */ + public static void glfwInvoke( + long window, + GLFWWindowSizeCallbackI windowSizeCB, + GLFWFramebufferSizeCallbackI framebufferSizeCB + ) { + try (MemoryStack stack = stackPush()) { + IntBuffer w = stack.mallocInt(1); + IntBuffer h = stack.mallocInt(1); + + if (windowSizeCB != null) { + glfwGetWindowSize(window, w, h); + windowSizeCB.invoke(window, w.get(0), h.get(0)); + } + + if (framebufferSizeCB != null) { + glfwGetFramebufferSize(window, w, h); + framebufferSizeCB.invoke(window, w.get(0), h.get(0)); + } + } + + } + +} \ No newline at end of file diff --git a/desktop-application/src/main/java/IOUtil.java b/desktop-application/src/main/java/IOUtil.java new file mode 100644 index 00000000..b6327b97 --- /dev/null +++ b/desktop-application/src/main/java/IOUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ + +import org.lwjgl.*; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.file.*; + +import static org.lwjgl.BufferUtils.*; + +public final class IOUtil { + + private IOUtil() { + } + + private static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) { + ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity); + buffer.flip(); + newBuffer.put(buffer); + return newBuffer; + } + + /** + * Reads the specified resource and returns the raw data as a ByteBuffer. + * + * @param resource the resource to read + * @param bufferSize the initial buffer size + * + * @return the resource data + * + * @throws IOException if an IO error occurs + */ + public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException { + ByteBuffer buffer; + + Path path = Paths.get(resource); + if (Files.isReadable(path)) { + try (SeekableByteChannel fc = Files.newByteChannel(path)) { + buffer = BufferUtils.createByteBuffer((int)fc.size() + 1); + while (fc.read(buffer) != -1) { + ; + } + } + } else { + try ( + InputStream source = IOUtil.class.getClassLoader().getResourceAsStream(resource); + ReadableByteChannel rbc = Channels.newChannel(source) + ) { + buffer = createByteBuffer(bufferSize); + + while (true) { + int bytes = rbc.read(buffer); + if (bytes == -1) { + break; + } + if (buffer.remaining() == 0) { + buffer = resizeBuffer(buffer, buffer.capacity() * 3 / 2); // 50% + } + } + } + } + + buffer.flip(); + return buffer; + } + +} \ No newline at end of file diff --git a/desktop-application/src/main/java/TruetypeOversample.java b/desktop-application/src/main/java/TruetypeOversample.java new file mode 100644 index 00000000..f842c14b --- /dev/null +++ b/desktop-application/src/main/java/TruetypeOversample.java @@ -0,0 +1,413 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ + +import org.lwjgl.*; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; +import org.lwjgl.stb.*; +import org.lwjgl.system.*; + +import java.io.*; +import java.nio.*; + +import static org.lwjgl.glfw.Callbacks.*; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.stb.STBTruetype.*; +import static org.lwjgl.system.MemoryUtil.*; + +/** + * STB Truetype oversampling demo. + * + *

This is a Java port of https://github + * .com/nothings/stb/blob/master/tests/oversample/main.c.

+ */ +public final class TruetypeOversample { + + private static final int BITMAP_W = 512; + private static final int BITMAP_H = 512; + + private static final float[] scale = { + 24.0f, + 14.0f + }; + + private static final int[] sf = { + 0, 1, 2, + 0, 1, 2 + }; + + // ---- + + private final STBTTAlignedQuad q = STBTTAlignedQuad.malloc(); + private final FloatBuffer xb = memAllocFloat(1); + private final FloatBuffer yb = memAllocFloat(1); + + private long window; + + private Callback debugProc; + + // ---- + + private int ww = 1024; + private int wh = 768; + + private int fbw = ww; + private int fbh = wh; + + private int font_tex; + + private STBTTPackedchar.Buffer chardata; + + private int font = 3; + + private boolean black_on_white; + private boolean integer_align; + private boolean translating; + private boolean rotating; + + private boolean supportsSRGB; + private boolean srgb; + + private float rotate_t, translate_t; + + private boolean show_tex; + + private TruetypeOversample() { + } + + public static void main(String[] args) { + new TruetypeOversample().run("STB Truetype Oversample Demo"); + } + + private void load_fonts() { + font_tex = glGenTextures(); + chardata = STBTTPackedchar.malloc(6 * 128); + + try (STBTTPackContext pc = STBTTPackContext.malloc()) { + ByteBuffer ttf = IOUtil.ioResourceToByteBuffer("res/fonts/Roboto-Regular.ttf", 512 * 1024); + + ByteBuffer bitmap = BufferUtils.createByteBuffer(BITMAP_W * BITMAP_H); + + stbtt_PackBegin(pc, bitmap, BITMAP_W, BITMAP_H, 0, 1, NULL); + for (int i = 0; i < 2; i++) { + int p = (i * 3 + 0) * 128 + 32; + chardata.limit(p + 95); + chardata.position(p); + stbtt_PackSetOversampling(pc, 1, 1); + stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, chardata); + + p = (i * 3 + 1) * 128 + 32; + chardata.limit(p + 95); + chardata.position(p); + stbtt_PackSetOversampling(pc, 2, 2); + stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, chardata); + + p = (i * 3 + 2) * 128 + 32; + chardata.limit(p + 95); + chardata.position(p); + stbtt_PackSetOversampling(pc, 3, 1); + stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, chardata); + } + chardata.clear(); + stbtt_PackEnd(pc); + + glBindTexture(GL_TEXTURE_2D, font_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void draw_init() { + glDisable(GL_CULL_FACE); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + glViewport(0, 0, fbw, fbh); + if (black_on_white) { + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); + } else { + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + } + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, ww, wh, 0.0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } + + private static void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) { + glTexCoord2f(s0, t0); + glVertex2f(x0, y0); + glTexCoord2f(s1, t0); + glVertex2f(x1, y0); + glTexCoord2f(s1, t1); + glVertex2f(x1, y1); + glTexCoord2f(s0, t1); + glVertex2f(x0, y1); + } + + private void print(float x, float y, int font, String text) { + xb.put(0, x); + yb.put(0, y); + + chardata.position(font * 128); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font_tex); + + glBegin(GL_QUADS); + for (int i = 0; i < text.length(); i++) { + stbtt_GetPackedQuad(chardata, BITMAP_W, BITMAP_H, text.charAt(i), xb, yb, q, font == 0 && integer_align); + drawBoxTC( + q.x0(), q.y0(), q.x1(), q.y1(), + q.s0(), q.t0(), q.s1(), q.t1() + ); + } + glEnd(); + } + + private void draw_world() { + int sfont = sf[font]; + + float x = 20; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (black_on_white) { + glColor3f(0.0f, 0.0f, 0.0f); + } else { + glColor3f(1.0f, 1.0f, 1.0f); + } + + print(80, 30, sfont, "Controls:"); + print(100, 60, sfont, "S: toggle font size"); + print(100, 85, sfont, "O: toggle oversampling"); + print(100, 110, sfont, "T: toggle translation"); + print(100, 135, sfont, "R: toggle rotation"); + print(100, 160, sfont, "P: toggle pixel-snap (only non-oversampled)"); + if (supportsSRGB) { + print(100, 185, sfont, "G: toggle srgb gamma-correction"); + } + if (black_on_white) { + print(100, 210, sfont, "B: toggle to white-on-black"); + } else { + print(100, 210, sfont, "B: toggle to black-on-white"); + } + print(100, 235, sfont, "V: view font texture"); + + print(80, 300, sfont, "Current font:"); + + if (!show_tex) { + if (font < 3) { + print(100, 350, sfont, "Font height: 24 pixels"); + } else { + print(100, 350, sfont, "Font height: 14 pixels"); + } + } + + if (font % 3 == 1) { + print(100, 325, sfont, "2x2 oversampled text at 1:1"); + } else if (font % 3 == 2) { + print(100, 325, sfont, "3x1 oversampled text at 1:1"); + } else if (integer_align) { + print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates"); + } else { + print(100, 325, sfont, "1:1 text, one texel = one pixel"); + } + + if (show_tex) { + glBegin(GL_QUADS); + drawBoxTC(200, 400, 200 + BITMAP_W, 300 + BITMAP_H, 0, 0, 1, 1); + glEnd(); + } else { + glMatrixMode(GL_MODELVIEW); + glTranslatef(200, 350, 0); + + if (translating) { + x += translate_t * 8 % 30; + } + + if (rotating) { + glTranslatef(100, 150, 0); + glRotatef(rotate_t * 2, 0, 0, 1); + glTranslatef(-100, -150, 0); + } + print(x, 100, font, "This is a test"); + print(x, 130, font, "Now is the time for all good men to come to the aid of their country."); + print(x, 160, font, "The quick brown fox jumps over the lazy dog."); + print(x, 190, font, "0123456789"); + } + } + + private void draw() { + draw_init(); + draw_world(); + glfwSwapBuffers(window); + } + + private void loopmode(float dt) { + if (dt > 0.25f) { + dt = 0.25f; + } + if (dt < 0.01f) { + dt = 0.01f; + } + + rotate_t += dt; + translate_t += dt; + + draw(); + } + + private void windowSizeChanged(long window, int width, int height) { + this.ww = width; + this.wh = height; + } + + private void framebufferSizeChanged(long window, int width, int height) { + this.fbw = width; + this.fbh = height; + } + + private void createWindow(String title) { + GLFWErrorCallback.createPrint().set(); + if (!glfwInit()) { + throw new IllegalStateException("Unable to initialize GLFW"); + } + + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + + this.window = glfwCreateWindow(ww, wh, title, NULL, NULL); + if (window == NULL) { + throw new RuntimeException("Failed to create the GLFW window"); + } + + glfwSetWindowSizeCallback(window, this::windowSizeChanged); + glfwSetFramebufferSizeCallback(window, this::framebufferSizeChanged); + + glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> { + if (action == GLFW_RELEASE) { + return; + } + + switch (key) { + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, true); + break; + case GLFW_KEY_O: + font = (font + 1) % 3 + (font / 3) * 3; + break; + case GLFW_KEY_S: + font = (font + 3) % 6; + break; + case GLFW_KEY_T: + translating = !translating; + translate_t = 0.0f; + break; + case GLFW_KEY_R: + rotating = !rotating; + rotate_t = 0.0f; + break; + case GLFW_KEY_P: + integer_align = !integer_align; + break; + case GLFW_KEY_G: + if (!supportsSRGB) { + break; + } + + srgb = !srgb; + if (srgb) { + glEnable(GL30.GL_FRAMEBUFFER_SRGB); + } else { + glDisable(GL30.GL_FRAMEBUFFER_SRGB); + } + break; + case GLFW_KEY_V: + show_tex = !show_tex; + break; + case GLFW_KEY_B: + black_on_white = !black_on_white; + break; + } + }); + + // Center window + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + + glfwSetWindowPos( + window, + (vidmode.width() - ww) / 2, + (vidmode.height() - wh) / 2 + ); + + // Create context + glfwMakeContextCurrent(window); + GL.createCapabilities(); + debugProc = GLUtil.setupDebugMessageCallback(); + + glfwSwapInterval(1); + glfwShowWindow(window); + + GLFWUtil.glfwInvoke(window, this::windowSizeChanged, this::framebufferSizeChanged); + + // Detect sRGB support + GLCapabilities caps = GL.getCapabilities(); + supportsSRGB = caps.OpenGL30 || caps.GL_ARB_framebuffer_sRGB || caps.GL_EXT_framebuffer_sRGB; + } + + private void run(String title) { + try { + createWindow(title); + load_fonts(); + + long time = System.nanoTime(); + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + long t = System.nanoTime(); + float dt = (float)((t - time) / 1000000000.0); + time = t; + + loopmode(dt); + } + } finally { + try { + destroy(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void destroy() { + GL.setCapabilities(null); + + chardata.free(); + + if (debugProc != null) { + debugProc.free(); + } + + glfwFreeCallbacks(window); + glfwTerminate(); + glfwSetErrorCallback(null).free(); + + memFree(yb); + memFree(xb); + + q.free(); + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglFontLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglFontLoader.kt index 059d9898..1a6fca54 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglFontLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglFontLoader.kt @@ -1,12 +1,16 @@ package com.mechanica.engine.context.loader -import com.mechanica.engine.text.LwjglFont +import com.mechanica.engine.text.LwjglStandardFont import com.mechanica.engine.text.Font -import com.mechanica.engine.context.loader.FontLoader import com.mechanica.engine.resources.Resource +import com.mechanica.engine.text.FontAtlasConfiguration class LwjglFontLoader : FontLoader { - override val defaultFont: Font by lazy { LwjglFont(Resource("res/fonts/Roboto-Regular.ttf")) } + override val defaultFont: Font by lazy { LwjglStandardFont(Resource("res/fonts/Roboto-Regular.ttf")) { + width = 1024 + height = 1024 + characterSize = 200f + } } - override fun font(res: Resource) = LwjglFont(res) + override fun font(res: Resource, initializer: FontAtlasConfiguration.() -> Unit) = LwjglStandardFont(res, initializer) } diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt index 25cb78c5..db45f39f 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt @@ -2,7 +2,11 @@ package com.mechanica.engine.context.loader import com.mechanica.engine.shader.qualifiers.AttributeQualifier import com.mechanica.engine.shader.qualifiers.Qualifier +import com.mechanica.engine.shader.script.Shader +import com.mechanica.engine.shader.script.ShaderLoader +import com.mechanica.engine.shader.script.ShaderScript import com.mechanica.engine.shader.vbo.LwjglElementArrayType +import org.lwjgl.opengl.GL20 class LwjglLoader : GLLoader { override val constants = LwjglConstants() @@ -15,4 +19,18 @@ class LwjglLoader : GLLoader { override fun createUniformLoader(qualifier: Qualifier) = LwjglUniformLoader(qualifier) override fun createElementArray() = LwjglElementArrayType() + + override fun defaultShader(vertex: ShaderScript, fragment: ShaderScript, tessellation: ShaderScript?, geometry: ShaderScript?): Shader { + return object : Shader(vertex, fragment, tessellation, geometry) { + private val loader: ShaderLoader by lazy { ShaderLoader(vertex, fragment, tessellation, geometry) } + + override val id: Int get() = loader.id + + override fun loadProgram(id: Int) { + GL20.glUseProgram(id) + } + + override fun loadUniformLocation(name: String) = GL20.glGetUniformLocation(id, name) + } + } } \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/memory/MemoryTools.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/MemoryTools.kt index 4887fd09..ad4e56ae 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/memory/MemoryTools.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/MemoryTools.kt @@ -4,4 +4,10 @@ import org.lwjgl.system.MemoryStack inline fun useMemoryStack(use: MemoryStack.() -> Unit) { MemoryStack.stackPush().use { use(it) } +} + + +fun Byte.toIntValue(): Int = when { + (this.toInt() < 0) -> 255 + this.toInt() + 1 + else -> this.toInt() } \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleFloatBuffers.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleFloatBuffers.kt new file mode 100644 index 00000000..b578af94 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleFloatBuffers.kt @@ -0,0 +1,12 @@ +package com.mechanica.engine.memory + +import org.lwjgl.system.MemoryStack +import java.nio.FloatBuffer +import java.nio.IntBuffer + +class RectangleFloatBuffers(stack: MemoryStack) { + val x: FloatBuffer = stack.floats(0f) + val y: FloatBuffer = stack.floats(0f) + val width: FloatBuffer = stack.floats(0f) + val height: FloatBuffer = stack.floats(0f) +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleIntBuffers.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleIntBuffers.kt new file mode 100644 index 00000000..598e2947 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/RectangleIntBuffers.kt @@ -0,0 +1,11 @@ +package com.mechanica.engine.memory + +import org.lwjgl.system.MemoryStack +import java.nio.IntBuffer + +class RectangleIntBuffers(stack: MemoryStack) { + val x: IntBuffer = stack.ints(0) + val y: IntBuffer = stack.ints(0) + val width: IntBuffer = stack.ints(0) + val height: IntBuffer = stack.ints(0) +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/memory/buffer/BufferTools.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/buffer/BufferTools.kt index f915831a..df88fc5c 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/memory/buffer/BufferTools.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/memory/buffer/BufferTools.kt @@ -1,7 +1,9 @@ package com.mechanica.engine.memory.buffer +import com.mechanica.engine.memory.BitmapBuffer import com.mechanica.engine.memory.useMemoryStack import org.lwjgl.BufferUtils +import org.lwjgl.stb.STBImageWrite import java.nio.FloatBuffer import java.nio.IntBuffer import java.nio.ShortBuffer @@ -78,4 +80,9 @@ fun FloatBuffer.contentsToString(): String { sb.append(this.get()).append(", ") } return sb.toString() +} + + +fun BitmapBuffer.write(name: String) { + STBImageWrite.stbi_write_png(name, width, height, 1, buffer, width) } \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderImpl.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderImpl.kt deleted file mode 100644 index 8ac411f7..00000000 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.mechanica.engine.shader.script - -internal class ShaderImpl( - override val vertex: ShaderScript, - override val fragment: ShaderScript, - override val tessellation: ShaderScript?, - override val geometry: ShaderScript?): Shader() { - - private val loader: ShaderLoader by lazy { ShaderLoader(vertex, fragment, tessellation, geometry) } - - override val id: Int - get() = loader.id - -} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderLoader.kt index 92835d8c..909fd110 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/script/ShaderLoader.kt @@ -60,9 +60,6 @@ class ShaderLoader( GL20.glValidateProgram(id) checkProgram(id, GL20.GL_VALIDATE_STATUS) { "Shader validating failed" } - vertex.loadProgram(id) - geometry?.loadProgram(id) - fragment.loadProgram(id) } private fun loadShader(script: String, type: Int): Int { diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt index 07a28caf..d88e114e 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt @@ -1,83 +1,167 @@ package com.mechanica.engine.text +import com.mechanica.engine.memory.BitmapBuffer +import com.mechanica.engine.memory.buffer.write +import com.mechanica.engine.memory.useMemoryStack import com.mechanica.engine.models.Image +import com.mechanica.engine.unit.vector.vec +import com.mechanica.engine.util.extensions.constrain import com.mechanica.engine.utils.loadImage import org.lwjgl.BufferUtils import org.lwjgl.opengl.GL11 -import org.lwjgl.stb.STBTTBakedChar -import org.lwjgl.stb.STBTTFontinfo +import org.lwjgl.stb.STBTTAlignedQuad +import org.lwjgl.stb.STBTTPackContext +import org.lwjgl.stb.STBTTPackedchar import org.lwjgl.stb.STBTruetype -import org.lwjgl.system.MemoryStack import java.nio.ByteBuffer import java.nio.IntBuffer +import kotlin.math.* -class FontAtlas(data: LwjglFont.FontData, val width: Int = 1024, val height: Int = 1024) : Image { +class FontAtlas(private val data: LwjglStandardFont.FontData, private val config: LwjglFontAtlasConfiguration) : Image { private val image: Image + val width: Int = config.width + val height: Int = config.height + override val id: Int get() = image.id val scale: Double + private val distanceFieldStart: Double + private val distanceFieldEnd: Double + + private val distanceFieldPadding: Double + get() = max(distanceFieldStart, distanceFieldEnd) + val padding: Float + get() = config.padding + if (config.distanceFieldsWasSet) distanceFieldPadding.toFloat() else 0f + + private val charRange = config.charRange.first.toInt()..config.charRange.last.toInt() + private val unknownIndex = 128 - charRange.first + init { val bitmap = BufferUtils.createByteBuffer(width * height) - bakeFont(bitmap, data.ttf, data.cdata) - scale = getBakedQuadScale(data.info, data.cdata) + + val correction = distanceFieldCorrection() + distanceFieldStart = config.sdfConfiguration.start + correction + distanceFieldEnd = config.sdfConfiguration.end + correction + + packFont(bitmap, data.ttf, data.cdata) + + if (config.distanceFieldsWasSet) { + insertCharacters(bitmap) + } + + scale = getScale(data) image = loadImage(bitmap, width, height, 1, GL11.GL_ALPHA) } + private fun insertCharacters(bitmap: ByteBuffer) { + val bitmapBuffer = BitmapBuffer(bitmap, width, height) + for (char in charRange){ + val glyph = createSDFGlyph(char.toChar()) + + val charAsInt = if (char in charRange) char - charRange.first else unknownIndex + val paddingVector = vec(padding/width.toFloat(), padding/height.toFloat()) + + if (glyph != null) { + useMemoryStack { + val q = STBTTAlignedQuad.mallocStack(this) + STBTruetype.stbtt_GetPackedQuad(data.cdata, width, height, charAsInt, floats(0f), floats(0f), q, false) + bitmapBuffer.insert(glyph, q.s0() - paddingVector.x.toFloat(), q.t0() - paddingVector.y.toFloat()) + } + } + } + bitmap.position(0) + bitmapBuffer.write("font.png") + } + + private fun getScale(data: LwjglStandardFont.FontData): Double { + val c = config.charRange.first + val x = data.cdata[0].xadvance() + return getAtlasScale(data.info, c, if (x >= 1f) x else 1f) + } + override fun bind() { image.bind() } - private fun bakeFont(bitmap: ByteBuffer, ttf: ByteBuffer, cdata: STBTTBakedChar.Buffer): Float { - var check: Int - var attempts = 0 - var maxFontHeight = 100f - var maxRow = 0 - var checkedFontHeight = maxFontHeight - - while (true) { - check = STBTruetype.stbtt_BakeFontBitmap(ttf, checkedFontHeight, bitmap, width, height, 32, cdata) - if (check == 0) { maxFontHeight = checkedFontHeight; break } - - if (attempts < 5) { - if (check > maxRow) { - maxRow = check - maxFontHeight = checkedFontHeight - } + private fun packFont(bitmap: ByteBuffer, ttf: ByteBuffer, cdata: STBTTPackedchar.Buffer) { + val characterHeight = config.characterSize - var error = 0f - if (check < 0) { - error = -0.3f*(1f + (check.toFloat())/ LwjglFont.charRange.count().toFloat()) - } else if (check > 0) { - error = 0.8f*(1f - check.toFloat()/ height.toFloat()) - } + STBTTPackContext.malloc().use { pc -> + STBTruetype.stbtt_PackBegin(pc, bitmap, width, height, 0, padding.toInt()*2) - checkedFontHeight *= (1f + error) - attempts++ - continue - } else { - break - } + val success = STBTruetype.stbtt_PackFontRange(pc, ttf, 0, characterHeight, charRange.first, cdata) + + if (!success) throw IllegalStateException("Error packing font into font atlas. The provided character size, ${config.characterSize}," + + " is likely too big for the provided atlas size, ${width}x$height") + + STBTruetype.stbtt_PackEnd(pc) } - STBTruetype.stbtt_BakeFontBitmap(ttf, maxFontHeight, bitmap, width, height, LwjglFont.charRange.first, cdata) - return maxFontHeight + + config.characterSize = characterHeight + } + + private fun checkPadding() { + val area = calculateAllowedCharacterArea() + val side = sqrt(area.toDouble()) + val paddingArea = padding*4*(side - padding) + val characterArea = area - paddingArea + + if (characterArea <= 0) throw IllegalStateException("Error packing font into font atlas. The provided padding, $padding," + + " is too much for the provided atlas size, ${width}x$height") + + } + + private fun calculateAllowedCharacterArea(): Int { + val bitmapArea = width*height + return bitmapArea/charRange.count() } + private fun distanceFieldCorrection(): Double { + val start = config.sdfConfiguration.start + val end = config.sdfConfiguration.end - private fun getBakedQuadScale(fontInfo: STBTTFontinfo, bakedFontData: STBTTBakedChar.Buffer): Double { - MemoryStack.stackPush().use { stack -> - val xAdvanceBuffer: IntBuffer = stack.mallocInt(1) - val lsbBuffer: IntBuffer = stack.mallocInt(1) - STBTruetype.stbtt_GetCodepointHMetrics(fontInfo, LwjglFont.charRange.first, xAdvanceBuffer, lsbBuffer) + return if (start*end > 0) { + val sign = sign(start) + -sign*min(abs(start), abs(end)) + } else 0.0 + } + + private fun createSDFGlyph(char: Char): BitmapBuffer? { + var start = distanceFieldStart + var end = distanceFieldEnd + + if (start*end > 0) { + val sign = sign(start) + val min = sign*min(abs(start), abs(end)) + start -= min + end -= min + } + + val edgeValue = getOnEdgeValue(start, end) - val xFromBakedData = bakedFontData[0].xadvance() - val xFromFontInfo = xAdvanceBuffer[0] + val pixelDistScale = 255f/(end - start).toFloat() - return xFromFontInfo.toDouble()/xFromBakedData + useMemoryStack { + val width: IntBuffer = ints(0) + val height: IntBuffer = ints(0) + val scale = STBTruetype.stbtt_ScaleForPixelHeight(data.info, config.characterSize) + + val buffer = STBTruetype.stbtt_GetCodepointSDF(data.info, scale, char.toInt(), distanceFieldPadding.toInt(), edgeValue.toByte(), pixelDistScale, + width, height, ints(0), ints(0)) + + if (buffer != null) return BitmapBuffer(buffer, width[0], height[0]) } + + return null + } + + private fun getOnEdgeValue(start: Double, end: Double): Int { + val startRatio = start/(end - start) + return ((1.0 + startRatio)*255).constrain(0.0, 255.0).toInt() } } \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontMetrics.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontMetrics.kt new file mode 100644 index 00000000..95b6cdc8 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontMetrics.kt @@ -0,0 +1,31 @@ +package com.mechanica.engine.text + +import org.lwjgl.stb.STBTTFontinfo +import org.lwjgl.stb.STBTruetype +import org.lwjgl.system.MemoryStack + +class FontMetrics(info: STBTTFontinfo, scale: Float = STBTruetype.stbtt_ScaleForPixelHeight(info, 1f)) { + val ascent: Float + val descent: Float + val lineGap: Float + + init { + var ascent = 0 + var descent = 0 + var lineGap = 0 + + MemoryStack.stackPush().use { stack -> + val ascentBuffer = stack.mallocInt(1) + val descentBuffer = stack.mallocInt(1) + val lineGapBuffer = stack.mallocInt(1) + STBTruetype.stbtt_GetFontVMetrics(info, ascentBuffer, descentBuffer, lineGapBuffer) + ascent = ascentBuffer[0] + descent = descentBuffer[0] + lineGap = lineGapBuffer[0] + } + + this.ascent = ascent*scale + this.descent = descent*scale + this.lineGap = lineGap*scale + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontUtils.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontUtils.kt new file mode 100644 index 00000000..2790a396 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontUtils.kt @@ -0,0 +1,49 @@ +package com.mechanica.engine.text + +import com.mechanica.engine.unit.vector.DynamicVector +import org.lwjgl.stb.STBTTAlignedQuad +import org.lwjgl.stb.STBTTFontinfo +import org.lwjgl.stb.STBTruetype +import org.lwjgl.system.MemoryStack +import java.nio.IntBuffer +import kotlin.math.max + +fun STBTTAlignedQuad.copyToArrays(cursor: CharacterCursor, scale: Double, atlas: FontAtlas, p: Array, tc: Array) { + val i = max(cursor.nonWhiteSpaceIndex - 1, 0) *4 + + val padding = atlas.padding*1.01 + val texPaddingX = padding/atlas.width.toDouble() + val texPaddingY = padding/atlas.height.toDouble() + + tc[i + 0].x = s0().toDouble() - texPaddingX + tc[i + 0].y = t1().toDouble() + texPaddingY + tc[i + 1].x = s1().toDouble() + texPaddingX + tc[i + 1].y = t1().toDouble() + texPaddingY + tc[i + 2].x = s0().toDouble() - texPaddingX + tc[i + 2].y = t0().toDouble() - texPaddingY + tc[i + 3].x = s1().toDouble() + texPaddingX + tc[i + 3].y = t0().toDouble() - texPaddingY + + val yAdvance = cursor.yAdvance + p[i + 0].x = (x0() - padding)*scale + p[i + 0].y = (-y1() - padding)*scale + yAdvance + p[i + 1].x = (x1() + padding)*scale + p[i + 1].y = (-y1() - padding)*scale + yAdvance + p[i + 2].x = (x0() - padding)*scale + p[i + 2].y = (-y0() + padding)*scale + yAdvance + p[i + 3].x = (x1() + padding)*scale + p[i + 3].y = (-y0() + padding)*scale + yAdvance +} + + +fun getAtlasScale(fontInfo: STBTTFontinfo, testChar: Char, testCharXAdvance: Float): Double { + MemoryStack.stackPush().use { stack -> + val xAdvanceBuffer: IntBuffer = stack.mallocInt(1) + val lsbBuffer: IntBuffer = stack.mallocInt(1) + STBTruetype.stbtt_GetCodepointHMetrics(fontInfo, testChar.toInt(), xAdvanceBuffer, lsbBuffer) + + val xFromFontInfo = xAdvanceBuffer[0] + + return xFromFontInfo.toDouble()/ testCharXAdvance + } +} diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFont.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFont.kt deleted file mode 100644 index 91f32a69..00000000 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFont.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.mechanica.engine.text - -import com.mechanica.engine.resources.Resource -import com.mechanica.engine.unit.vector.DynamicVector -import org.lwjgl.stb.STBTTAlignedQuad -import org.lwjgl.stb.STBTTBakedChar -import org.lwjgl.stb.STBTTFontinfo -import org.lwjgl.stb.STBTruetype -import org.lwjgl.stb.STBTruetype.stbtt_GetCodepointKernAdvance -import org.lwjgl.system.MemoryStack -import java.nio.ByteBuffer -import kotlin.math.max - -class LwjglFont(resource: Resource): Font() { - - override val ascent: Float - get() = metrics.ascent - override val descent: Float - get() = metrics.descent - override val lineGap: Float - get() = metrics.lineGap - - private val data = FontData(resource) - override val atlas = FontAtlas(data) - - private val metrics = FontMetrics() - - override fun addCharacterDataToArrays(cursor: CharacterCursor, positions: Array, texCoords: Array) { - val c = cursor.currentChar - val atlasScale = atlas.scale - val dataScale = data.scale - - val kern = stbtt_GetCodepointKernAdvance(data.info, cursor.previousChar.toInt(), c.toInt()).toFloat()/atlasScale.toFloat() - val charAsInt = if (c.toInt() in charRange) c.toInt() - charRange.first else unknownIndex - - MemoryStack.stackPush().use { stack -> - val x = stack.floats(cursor.xAdvance/(dataScale*atlasScale).toFloat() + kern) - val y = stack.floats(cursor.yAdvance) - val q = STBTTAlignedQuad.mallocStack(stack) - - STBTruetype.stbtt_GetBakedQuad(data.cdata, atlas.width, atlas.height, charAsInt, x, y, q, true) - cursor.xAdvance = (x[0]*dataScale*atlasScale).toFloat() - - if (c != ' ') copyToFloats(q, cursor, positions, texCoords) - } - } - - private fun copyToFloats(q: STBTTAlignedQuad, cursor: CharacterCursor, p: Array, tc: Array) { - val i = max(cursor.nonWhiteSpaceIndex - 1, 0)*4 - val atlasScale = atlas.scale - val dataScale = data.scale - - tc[i + 0].x = q.s0().toDouble() - tc[i + 0].y = q.t1().toDouble() - tc[i + 1].x = q.s1().toDouble() - tc[i + 1].y = q.t1().toDouble() - tc[i + 2].x = q.s0().toDouble() - tc[i + 2].y = q.t0().toDouble() - tc[i + 3].x = q.s1().toDouble() - tc[i + 3].y = q.t0().toDouble() - - val yAdvance = cursor.yAdvance - p[i + 0].x = (q.x0())*dataScale*atlasScale - p[i + 0].y = (-q.y1())*dataScale*atlasScale + yAdvance - p[i + 1].x = (q.x1())*dataScale*atlasScale - p[i + 1].y = (-q.y1())*dataScale*atlasScale + yAdvance - p[i + 2].x = (q.x0())*dataScale*atlasScale - p[i + 2].y = (-q.y0())*dataScale*atlasScale + yAdvance - p[i + 3].x = (q.x1())*dataScale*atlasScale - p[i + 3].y = (-q.y0())*dataScale*atlasScale + yAdvance - } - - inner class FontData(resource: Resource) { - val ttf: ByteBuffer = resource.buffer - val info: STBTTFontinfo = STBTTFontinfo.create() - val cdata: STBTTBakedChar.Buffer - val scale: Float - - init { - check(STBTruetype.stbtt_InitFont(info, ttf)) { "Failed to initialize font information." } - cdata = STBTTBakedChar.create(charRange.count()) - scale = STBTruetype.stbtt_ScaleForPixelHeight(info, 1f) - } - } - - private inner class FontMetrics { - val ascent: Float - val descent: Float - val lineGap: Float - - init { - var ascent = 0 - var descent = 0 - var lineGap = 0 - - MemoryStack.stackPush().use { stack -> - val ascentBuffer = stack.mallocInt(1) - val descentBuffer = stack.mallocInt(1) - val lineGapBuffer = stack.mallocInt(1) - STBTruetype.stbtt_GetFontVMetrics(data.info, ascentBuffer, descentBuffer, lineGapBuffer) - ascent = ascentBuffer[0] - descent = descentBuffer[0] - lineGap = lineGapBuffer[0] - } - - this.ascent = ascent*data.scale - this.descent = descent*data.scale - this.lineGap = lineGap*data.scale - } - } - companion object { - - val charRange = 32..128 - private val unknownIndex = 128 - charRange.first - - } -} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFontAtlasConfiguration.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFontAtlasConfiguration.kt new file mode 100644 index 00000000..e4a1490a --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglFontAtlasConfiguration.kt @@ -0,0 +1,57 @@ +package com.mechanica.engine.text + + +class LwjglFontAtlasConfiguration(initializer: FontAtlasConfiguration.() -> Unit) : FontAtlasConfiguration { + val anyWasSet: Boolean + get() = dimensionsWereSet || paddingWasSet || characterSizeWasSet || distanceFieldsWasSet + + var dimensionsWereSet = false + private set + override var width = 1024 + set(value) { + field = value + dimensionsWereSet = true + } + override var height = 1024 + set(value) { + field = value + dimensionsWereSet = true + } + + var characterSizeWasSet = false + private set + override var characterSize = 100f + set(value) { + field = value + characterSizeWasSet = true + } + + var paddingWasSet = false + private set + override var padding = 2f + set(value) { + field = value + paddingWasSet = true + } + + override var charRange = 32.toChar()..128.toChar() + + var distanceFieldsWasSet = false + val sdfConfiguration = FontAtlasConfiguration.SDFConfiguration() + override fun configureSDF(block: FontAtlasConfiguration.SDFConfiguration.() -> Unit) { + distanceFieldsWasSet = true + block(sdfConfiguration) + } + + init { + initializer(this) + } + + fun reset() { + paddingWasSet = false + characterSizeWasSet = false + distanceFieldsWasSet = false + dimensionsWereSet = false + } + +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt new file mode 100644 index 00000000..f062b322 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt @@ -0,0 +1,63 @@ +package com.mechanica.engine.text + +import com.mechanica.engine.resources.Resource +import com.mechanica.engine.unit.vector.DynamicVector +import org.lwjgl.stb.* +import org.lwjgl.stb.STBTruetype.stbtt_GetCodepointKernAdvance +import org.lwjgl.system.MemoryStack +import java.nio.ByteBuffer + +class LwjglStandardFont(resource: Resource, initializer: FontAtlasConfiguration.() -> Unit): Font() { + + override val ascent: Float + get() = metrics.ascent + override val descent: Float + get() = metrics.descent + override val lineGap: Float + get() = metrics.lineGap + + private val config = LwjglFontAtlasConfiguration(initializer) + private val charRange = config.charRange.first.toInt()..config.charRange.last.toInt() + private val unknownIndex = 128 - charRange.first + + private val data = FontData(resource) + + override val atlas = FontAtlas(data, config) + + private val metrics = FontMetrics(data.info, data.scale) + + + override fun addCharacterDataToArrays(cursor: CharacterCursor, positions: Array, texCoords: Array) { + val c = cursor.currentChar + val atlasScale = atlas.scale + val dataScale = data.scale + + val kern = stbtt_GetCodepointKernAdvance(data.info, cursor.previousChar.toInt(), c.toInt()).toFloat()/atlasScale.toFloat() + val charAsInt = if (c.toInt() in charRange) c.toInt() - charRange.first else unknownIndex + + MemoryStack.stackPush().use { stack -> + val x = stack.floats(cursor.xAdvance/(dataScale*atlasScale).toFloat() + kern) + val y = stack.floats(cursor.yAdvance) + val q = STBTTAlignedQuad.mallocStack(stack) + + STBTruetype.stbtt_GetPackedQuad(data.cdata, atlas.width, atlas.height, charAsInt, x, y, q, true) + cursor.xAdvance = (x[0]*dataScale*atlasScale).toFloat() + + if (c != ' ') q.copyToArrays(cursor, dataScale*atlasScale, atlas, positions, texCoords) + } + } + + inner class FontData(resource: Resource) { + val ttf: ByteBuffer = resource.buffer + val info: STBTTFontinfo = STBTTFontinfo.create() + val cdata: STBTTPackedchar.Buffer + val scale: Float + + init { + check(STBTruetype.stbtt_InitFont(info, ttf)) { "Failed to initialize font information." } + cdata = STBTTPackedchar.create(charRange.count()) + scale = STBTruetype.stbtt_ScaleForPixelHeight(info, 1f) + } + } + +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/temp/TextToPng.kt b/desktop-application/src/main/kotlin/temp/TextToPng.kt new file mode 100644 index 00000000..7a21a75e --- /dev/null +++ b/desktop-application/src/main/kotlin/temp/TextToPng.kt @@ -0,0 +1,200 @@ +package temp + +import TruetypeOversample +import com.mechanica.engine.context.GLContext +import com.mechanica.engine.context.GLInitializer +import com.mechanica.engine.context.loader.LwjglLoader +import com.mechanica.engine.display.Window +import com.mechanica.engine.memory.useMemoryStack +import com.mechanica.engine.resources.Resource +import org.lwjgl.BufferUtils +import org.lwjgl.stb.* +import org.lwjgl.stb.STBTruetype.* +import org.lwjgl.system.MemoryStack +import java.nio.ByteBuffer + + +fun main() { + GLContext.initialize(Window.Companion.create("test", 100, 100)) + GLInitializer.initialize(LwjglLoader()) + + val demo = TextToPngDemo("Roboto-Regular.ttf") + + val text = "The quick brown fox jumped over the lazy dog" + demo.packCharacters() +// STBTruetype.stbtt_PackBegin(ctx) + +} + +private class TextToPngDemo(fontName: String) { + + val info: STBTTFontinfo = STBTTFontinfo.create() + + val ascent: Int + val descent: Int + val lineGap: Int + + val fontHeight = 24 + + val width = 512 + val height = 512 + + val bitmap: ByteBuffer + val ttf: ByteBuffer = Resource("res/fonts/$fontName").buffer + + init { + check(stbtt_InitFont(info, ttf)) { "Failed to initialize font information." } + + var ascent = 0 + var descent = 0 + var lineGap = 0 + + MemoryStack.stackPush().use { stack -> + val pAscent = stack.mallocInt(1) + val pDescent = stack.mallocInt(1) + val pLineGap = stack.mallocInt(1) + stbtt_GetFontVMetrics(info, pAscent, pDescent, pLineGap) + ascent = pAscent[0] + descent = pDescent[0] + lineGap = pLineGap[0] + } + this.ascent = ascent + this.descent = descent + this.lineGap = lineGap + + bitmap = BufferUtils.createByteBuffer(width * height) + + } + + fun packCharacters() { + val cdata = STBTTPackedchar.malloc(128) + + STBTTPackContext.malloc().use { pc -> + stbtt_PackBegin(pc, bitmap, width, height, 0, 20) + stbtt_PackFontRange(pc, ttf, 0, 32f, 32, cdata) + stbtt_PackEnd(pc) + } + STBImageWrite.stbi_write_png("packed.png", width, height, 1, bitmap, width) + + useMemoryStack { + val xb = floats(0f) + val yb = floats(0f) + val q = STBTTAlignedQuad.malloc() + stbtt_GetPackedQuad(cdata, width, height, ')'.toInt() - 32, xb, yb, q, false) + println("p0: ${q.x0()}, ${q.y0()}") + println("p1: ${q.x1()}, ${q.y1()}") + println("t0: ${q.s0()}, ${q.t0()}") + println("t1: ${q.s1()}, ${q.t1()}") + q.free() + } + cdata.free() + } + + // Calling these functions in sequence is roughly equivalent to calling + // stbtt_PackFontRanges(). If you more control over the packing of multiple + // fonts, or if you want to pack custom data into a font texture, take a look + // at the source to of stbtt_PackFontRanges() and create a custom version + // using these functions, e.g. call GatherRects multiple times, + // building up a single array of rects, then call PackRects once, + // then call RenderIntoRects repeatedly. This may result in a + // better packing than calling PackFontRanges multiple times + // (or it may not). + fun packCharactersIndividually() { + val capacity = 95 + val cdata = STBTTPackedchar.calloc(capacity) +// cdata.limit(32 + 95) +// cdata.position(32) +// stbtt_PackFontRange(pc, ttf, 0, 24f, 32, cdata) + val range = STBTTPackRange.calloc(1) + range.first_unicode_codepoint_in_range(32) + + range.num_chars(cdata.remaining()) + range.chardata_for_range(cdata) + range.font_size(24f) + + STBTTPackContext.malloc().use { pc -> + stbtt_PackBegin(pc, bitmap, width, height, 0, 1) + + val rects = STBRPRect.malloc(capacity) + + stbtt_PackFontRangesGatherRects(pc, info, range, rects) + + stbtt_PackFontRangesPackRects(pc, rects) + + stbtt_PackFontRangesRenderIntoRects(pc, info, range, rects) + + stbtt_PackEnd(pc) + rects.free() + } + cdata.free() + range.free() + STBImageWrite.stbi_write_png("packed.png", width, height, 1, bitmap, width) + } + + fun saveAllCharacters() { + + var x = 0 + for (i in 'A'.toInt()..'z'.toInt()){ + x = addCharacterToBitmap(i.toChar(), bitmap, x) + } + + bitmap.position(0) + STBImageWrite.stbi_write_png("out.png", width, height, 1, bitmap, width) + } + + fun saveText(text: String) { + + var x = 0 + for (c in text){ + x = addCharacterToBitmap(c, bitmap, x) + } + + bitmap.position(0) + STBImageWrite.stbi_write_png("out.png", width, height, 1, bitmap, width) + } + + private fun addCharacterToBitmap(c: Char, bitmap: ByteBuffer, x: Int): Int { + + val scale = stbtt_ScaleForPixelHeight(info, fontHeight.toFloat()) + + val ascent = (ascent*scale).toInt() + val descent = (descent*scale).toInt() + val lineGap = (lineGap*scale).toInt() + + + var xAdvance = 0 + MemoryStack.stackPush().use { stack -> + val character = c.toInt() + + val axBuffer = stack.mallocInt(1) + val lsbBuffer = stack.mallocInt(1) + STBTruetype.stbtt_GetCodepointHMetrics(info, character, axBuffer, lsbBuffer) + + val ax = axBuffer[0] + val lsb = lsbBuffer[0] + + val x1 = stack.mallocInt(1); + val x2 = stack.mallocInt(1) + val y1 = stack.mallocInt(1); + val y2 = stack.mallocInt(1) + + STBTruetype.stbtt_GetCodepointBitmapBox(info, character, scale, scale, x1, y1, x2, y2) + + val y: Int = ascent + y1[0] + + val byteOffset: Int = x + (lsb * scale + y * width.toLong()).toInt() + + STBTruetype.stbtt_MakeCodepointBitmap(info, bitmap.position(byteOffset), x2[0] - x1[0], y2[0] - y1[0], width, scale, scale, character) + + xAdvance = x + (ax*scale).toInt() + + /* add kerning */ +// if (c < text.length - 1) { +// val kern = STBTruetype.stbtt_GetCodepointKernAdvance(info, character, text[c + 1].toInt()) +// x += (kern * scale).toInt() +// } + } + + return xAdvance + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt index 009c90a4..96607eef 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt @@ -41,7 +41,7 @@ class DrawerRenderer { vec4 inColor; if ($blend > 0.0 || $alphaBlend > 0.0) { vec4 texColor = texture(samp, tc); - inColor = vec4(mix($color.rgb, texColor.rgb, $blend), mix($color.a, texColor.a, $alphaBlend)); + inColor = vec4(mix($color.rgb, texColor.rgb, $blend), $color.a*texColor.a); } else { inColor = $color; } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt index 92d193b3..3eb7ca03 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt @@ -8,12 +8,13 @@ import com.mechanica.engine.shader.script.ShaderLoader import com.mechanica.engine.matrix.Matrices import com.mechanica.engine.models.Model import org.joml.Matrix4f +import org.lwjgl.opengl.GL20 class DrawerShader( - override val vertex: DrawerScript, - override val fragment: DrawerScript, - override val tessellation: DrawerScript? = null, - override val geometry: DrawerScript? = null): Shader() { + vertex: DrawerScript, + fragment: DrawerScript, + tessellation: DrawerScript? = null, + geometry: DrawerScript? = null): Shader(vertex, fragment, tessellation, geometry) { private val matrixLoaders = ArrayList() @@ -74,6 +75,12 @@ class DrawerShader( } } + override fun loadProgram(id: Int) { + GL20.glUseProgram(id) + } + + override fun loadUniformLocation(name: String) = GL20.glGetUniformLocation(id, name) + private class MatrixLoader(script: DrawerScript) { val matrixType = script.uniform.float("matrixType") diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt b/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt index d07acebf..a964762d 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt @@ -23,7 +23,7 @@ object Res { val svg = SpecificResource("res/svg/", "svg") val image = SpecificResource("res/images/", "png") - val font = SpecificResource("res/fonts/", "png") + val font = SpecificResource("res/fonts/", "ttf") val animations = SpecificResourceDirectory("/res/animations/") fun external(path: String, createIfAbsent: Boolean = false): ExternalResource { diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt index 89cf0e24..960c7679 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt @@ -20,7 +20,6 @@ fun main() { val draw = Drawer.create() val image = loadImage(Res.image["testImage"]) - val stand = loadImage(Res.image["stand"]) val points = arrayOf( vec(0, 0), @@ -53,7 +52,6 @@ fun main() { draw.transformed.scale(3, 3).translate(4, -2).cyan.polygon(polygonModel) draw.color.strokeColor(hex(0xD0F045FF), strokeWidth = 0.05).path(points) draw.cyan.ellipse(-3, -3, 2, 1) - draw.image(stand) } } diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt index f1582b98..fc466374 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt @@ -13,6 +13,7 @@ import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.vec import com.mechanica.engine.util.extensions.constrain import com.mechanica.engine.debug.ScreenLog +import com.mechanica.engine.input.Mouse import org.joml.Matrix4f import kotlin.math.ceil import kotlin.math.max @@ -41,6 +42,7 @@ class FontRenderer { private val fragment = object : DrawerScript() { + val mouse = uniform.float() //language=GLSL override val main: String = """ sample in vec2 tc; @@ -49,7 +51,23 @@ class FontRenderer { void main(void) { vec4 texColor = texture(samp, tc); - out_Color = vec4($color.rgb, texColor.a*$color.a); + float alpha = texColor.a; + float thickness = 0.52; + vec3 neon = vec3(1.0, 0.43, 0.78); + + vec4 inside = vec4(mix(neon, vec3(1.0), 0.95), 1.0); + vec4 outside = vec4(mix(vec3(1.0, 0.43, 0.78), vec3(1.0), alpha*thickness), texColor.a/0.55 - 0.5); + float border = 0.03; + if (alpha < 1.0 && alpha > thickness) { + out_Color = inside; + } else if (alpha < thickness && alpha > thickness-border) { + float blend = (alpha - thickness + border)/border; + out_Color = mix(outside, inside, blend); + } else { + float blend = texColor.a*thickness; + out_Color = outside; + } +// out_Color = vec4($color.rgb, alpha*$color.a); } """ @@ -63,16 +81,15 @@ class FontRenderer { field = value } - private val fontMap = HashMap() - - var font: Font = Font.create(Res.font["Roboto-Regular.ttf"]).also { fontMap[it] = TextModel(text, it) } - set(value) { - fontMap[value] = TextModel(text, value) - field = value + private val font: Font = Font.create(Res.font["Roboto-Regular.ttf"]) { + characterSize = 100f + configureSDF { + start = -20.0 + end = 20.0 } + } - val model: TextModel - get() = fontMap[font] ?: TextModel(text, font).also { fontMap[font] = it } + val model: TextModel = TextModel(text, font) private val characterOutput = CharacterOutputImpl() @@ -90,6 +107,7 @@ class FontRenderer { } fun render(transformation: Matrix4f ) { + fragment.mouse.value = (Mouse.world.x/Game.view.width).toFloat() + 0.5f if (transformation == this.transformation) { transformation.translate(position.x.toFloat(), position.y.toFloat(), 0f) transformation.scale(fontSize.toFloat(), fontSize.toFloat(), 1f) diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/text/SDFTest.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/text/SDFTest.kt new file mode 100644 index 00000000..b8c8f827 --- /dev/null +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/text/SDFTest.kt @@ -0,0 +1,7 @@ +package com.mechanica.engine.samples.text + +import com.mechanica.engine.text.LwjglStandardFont + +fun main() { + +} \ No newline at end of file diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt index 033afe05..3d1d9702 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt @@ -1,6 +1,5 @@ package com.mechanica.engine.samples.text -import com.mechanica.engine.color.hex import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.input.Keyboard @@ -21,6 +20,7 @@ fun main() { constructionDraws = true } } +// val font = LwjglSDFFont(Res.font["freebooterscript.ttf"]) Game.run() } @@ -96,7 +96,7 @@ private class StartText : State() { } override fun render(draw: Drawer) { - draw.color(hex(0xC0C0C0FF)).background() + draw.black.background() val pos = renderer.from(cursor).getPosition() draw.blue.rectangle(pos.x, pos.y - 0.1*renderer.fontSize, 0.05, 0.75*renderer.fontSize) From 4e91af9341188a2a8c7724d849e706fe73bed05c Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Fri, 29 May 2020 22:55:18 +0100 Subject: [PATCH 02/21] Updated Gradle and set the JVM target to version 12 --- .../engine/resources/ResourceDirectory.kt | 1 - build.gradle.kts | 23 +- common/build.gradle.kts | 5 - .../src/main/java/GLFWUtil.java | 49 --- desktop-application/src/main/java/IOUtil.java | 71 --- .../src/main/java/TruetypeOversample.java | 413 ------------------ .../com/mechanica/engine/text/FontAtlas.kt | 1 - .../src/main/kotlin/temp/TextToPng.kt | 14 +- 8 files changed, 15 insertions(+), 562 deletions(-) delete mode 100644 desktop-application/src/main/java/GLFWUtil.java delete mode 100644 desktop-application/src/main/java/IOUtil.java delete mode 100644 desktop-application/src/main/java/TruetypeOversample.java diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt b/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt index 2ef84494..ce199609 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt @@ -4,7 +4,6 @@ import java.net.URI import java.nio.file.* import java.util.stream.Stream - class ResourceDirectory(directory: String): Iterable { private val resources: Array val fileCount: Int diff --git a/build.gradle.kts b/build.gradle.kts index e4270188..1c6b8b01 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,7 @@ buildscript { } plugins { - id("org.jetbrains.kotlin.jvm") version "1.3.61" + kotlin("jvm") version "1.3.61" `java-library` maven } @@ -69,6 +69,16 @@ allprojects { apply(plugin = "org.jetbrains.kotlin.jvm") dependencies(commonDependencies) + val compileKotlin: KotlinCompile by tasks + compileKotlin.kotlinOptions { + freeCompilerArgs = listOf("-XXLanguage:+InlineClasses") + jvmTarget = "12" + } + + java { + sourceCompatibility = JavaVersion.VERSION_12 + targetCompatibility = JavaVersion.VERSION_12 + } } project(":desktop-application") { @@ -88,14 +98,3 @@ project(":samples") { apply(plugin = "org.jetbrains.kotlin.jvm") dependencies(coreLwjgl) } - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -val compileKotlin: KotlinCompile by tasks -compileKotlin.kotlinOptions { - freeCompilerArgs = listOf("-XXLanguage:+InlineClasses") - jvmTarget = "1.8" -} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 891ebe85..b556fdbe 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -2,9 +2,4 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") -} - -val compileKotlin: KotlinCompile by tasks -compileKotlin.kotlinOptions { - freeCompilerArgs = listOf("-XXLanguage:+InlineClasses") } \ No newline at end of file diff --git a/desktop-application/src/main/java/GLFWUtil.java b/desktop-application/src/main/java/GLFWUtil.java deleted file mode 100644 index 7e89c365..00000000 --- a/desktop-application/src/main/java/GLFWUtil.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright LWJGL. All rights reserved. - * License terms: https://www.lwjgl.org/license - */ - -import org.lwjgl.glfw.*; -import org.lwjgl.system.*; - -import java.nio.*; - -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.system.MemoryStack.*; - -/** GLFW demo utilities. */ -public final class GLFWUtil { - - private GLFWUtil() { - } - - /** - * Invokes the specified callbacks using the current window and framebuffer sizes of the specified GLFW window. - * - * @param window the GLFW window - * @param windowSizeCB the window size callback, may be null - * @param framebufferSizeCB the framebuffer size callback, may be null - */ - public static void glfwInvoke( - long window, - GLFWWindowSizeCallbackI windowSizeCB, - GLFWFramebufferSizeCallbackI framebufferSizeCB - ) { - try (MemoryStack stack = stackPush()) { - IntBuffer w = stack.mallocInt(1); - IntBuffer h = stack.mallocInt(1); - - if (windowSizeCB != null) { - glfwGetWindowSize(window, w, h); - windowSizeCB.invoke(window, w.get(0), h.get(0)); - } - - if (framebufferSizeCB != null) { - glfwGetFramebufferSize(window, w, h); - framebufferSizeCB.invoke(window, w.get(0), h.get(0)); - } - } - - } - -} \ No newline at end of file diff --git a/desktop-application/src/main/java/IOUtil.java b/desktop-application/src/main/java/IOUtil.java deleted file mode 100644 index b6327b97..00000000 --- a/desktop-application/src/main/java/IOUtil.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright LWJGL. All rights reserved. - * License terms: https://www.lwjgl.org/license - */ - -import org.lwjgl.*; - -import java.io.*; -import java.nio.*; -import java.nio.channels.*; -import java.nio.file.*; - -import static org.lwjgl.BufferUtils.*; - -public final class IOUtil { - - private IOUtil() { - } - - private static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) { - ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity); - buffer.flip(); - newBuffer.put(buffer); - return newBuffer; - } - - /** - * Reads the specified resource and returns the raw data as a ByteBuffer. - * - * @param resource the resource to read - * @param bufferSize the initial buffer size - * - * @return the resource data - * - * @throws IOException if an IO error occurs - */ - public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException { - ByteBuffer buffer; - - Path path = Paths.get(resource); - if (Files.isReadable(path)) { - try (SeekableByteChannel fc = Files.newByteChannel(path)) { - buffer = BufferUtils.createByteBuffer((int)fc.size() + 1); - while (fc.read(buffer) != -1) { - ; - } - } - } else { - try ( - InputStream source = IOUtil.class.getClassLoader().getResourceAsStream(resource); - ReadableByteChannel rbc = Channels.newChannel(source) - ) { - buffer = createByteBuffer(bufferSize); - - while (true) { - int bytes = rbc.read(buffer); - if (bytes == -1) { - break; - } - if (buffer.remaining() == 0) { - buffer = resizeBuffer(buffer, buffer.capacity() * 3 / 2); // 50% - } - } - } - } - - buffer.flip(); - return buffer; - } - -} \ No newline at end of file diff --git a/desktop-application/src/main/java/TruetypeOversample.java b/desktop-application/src/main/java/TruetypeOversample.java deleted file mode 100644 index f842c14b..00000000 --- a/desktop-application/src/main/java/TruetypeOversample.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright LWJGL. All rights reserved. - * License terms: https://www.lwjgl.org/license - */ - -import org.lwjgl.*; -import org.lwjgl.glfw.*; -import org.lwjgl.opengl.*; -import org.lwjgl.stb.*; -import org.lwjgl.system.*; - -import java.io.*; -import java.nio.*; - -import static org.lwjgl.glfw.Callbacks.*; -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.stb.STBTruetype.*; -import static org.lwjgl.system.MemoryUtil.*; - -/** - * STB Truetype oversampling demo. - * - *

This is a Java port of https://github - * .com/nothings/stb/blob/master/tests/oversample/main.c.

- */ -public final class TruetypeOversample { - - private static final int BITMAP_W = 512; - private static final int BITMAP_H = 512; - - private static final float[] scale = { - 24.0f, - 14.0f - }; - - private static final int[] sf = { - 0, 1, 2, - 0, 1, 2 - }; - - // ---- - - private final STBTTAlignedQuad q = STBTTAlignedQuad.malloc(); - private final FloatBuffer xb = memAllocFloat(1); - private final FloatBuffer yb = memAllocFloat(1); - - private long window; - - private Callback debugProc; - - // ---- - - private int ww = 1024; - private int wh = 768; - - private int fbw = ww; - private int fbh = wh; - - private int font_tex; - - private STBTTPackedchar.Buffer chardata; - - private int font = 3; - - private boolean black_on_white; - private boolean integer_align; - private boolean translating; - private boolean rotating; - - private boolean supportsSRGB; - private boolean srgb; - - private float rotate_t, translate_t; - - private boolean show_tex; - - private TruetypeOversample() { - } - - public static void main(String[] args) { - new TruetypeOversample().run("STB Truetype Oversample Demo"); - } - - private void load_fonts() { - font_tex = glGenTextures(); - chardata = STBTTPackedchar.malloc(6 * 128); - - try (STBTTPackContext pc = STBTTPackContext.malloc()) { - ByteBuffer ttf = IOUtil.ioResourceToByteBuffer("res/fonts/Roboto-Regular.ttf", 512 * 1024); - - ByteBuffer bitmap = BufferUtils.createByteBuffer(BITMAP_W * BITMAP_H); - - stbtt_PackBegin(pc, bitmap, BITMAP_W, BITMAP_H, 0, 1, NULL); - for (int i = 0; i < 2; i++) { - int p = (i * 3 + 0) * 128 + 32; - chardata.limit(p + 95); - chardata.position(p); - stbtt_PackSetOversampling(pc, 1, 1); - stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, chardata); - - p = (i * 3 + 1) * 128 + 32; - chardata.limit(p + 95); - chardata.position(p); - stbtt_PackSetOversampling(pc, 2, 2); - stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, chardata); - - p = (i * 3 + 2) * 128 + 32; - chardata.limit(p + 95); - chardata.position(p); - stbtt_PackSetOversampling(pc, 3, 1); - stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, chardata); - } - chardata.clear(); - stbtt_PackEnd(pc); - - glBindTexture(GL_TEXTURE_2D, font_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void draw_init() { - glDisable(GL_CULL_FACE); - glDisable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - - glViewport(0, 0, fbw, fbh); - if (black_on_white) { - glClearColor(1.0f, 1.0f, 1.0f, 0.0f); - } else { - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - } - glClear(GL_COLOR_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, ww, wh, 0.0, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - } - - private static void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) { - glTexCoord2f(s0, t0); - glVertex2f(x0, y0); - glTexCoord2f(s1, t0); - glVertex2f(x1, y0); - glTexCoord2f(s1, t1); - glVertex2f(x1, y1); - glTexCoord2f(s0, t1); - glVertex2f(x0, y1); - } - - private void print(float x, float y, int font, String text) { - xb.put(0, x); - yb.put(0, y); - - chardata.position(font * 128); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, font_tex); - - glBegin(GL_QUADS); - for (int i = 0; i < text.length(); i++) { - stbtt_GetPackedQuad(chardata, BITMAP_W, BITMAP_H, text.charAt(i), xb, yb, q, font == 0 && integer_align); - drawBoxTC( - q.x0(), q.y0(), q.x1(), q.y1(), - q.s0(), q.t0(), q.s1(), q.t1() - ); - } - glEnd(); - } - - private void draw_world() { - int sfont = sf[font]; - - float x = 20; - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - if (black_on_white) { - glColor3f(0.0f, 0.0f, 0.0f); - } else { - glColor3f(1.0f, 1.0f, 1.0f); - } - - print(80, 30, sfont, "Controls:"); - print(100, 60, sfont, "S: toggle font size"); - print(100, 85, sfont, "O: toggle oversampling"); - print(100, 110, sfont, "T: toggle translation"); - print(100, 135, sfont, "R: toggle rotation"); - print(100, 160, sfont, "P: toggle pixel-snap (only non-oversampled)"); - if (supportsSRGB) { - print(100, 185, sfont, "G: toggle srgb gamma-correction"); - } - if (black_on_white) { - print(100, 210, sfont, "B: toggle to white-on-black"); - } else { - print(100, 210, sfont, "B: toggle to black-on-white"); - } - print(100, 235, sfont, "V: view font texture"); - - print(80, 300, sfont, "Current font:"); - - if (!show_tex) { - if (font < 3) { - print(100, 350, sfont, "Font height: 24 pixels"); - } else { - print(100, 350, sfont, "Font height: 14 pixels"); - } - } - - if (font % 3 == 1) { - print(100, 325, sfont, "2x2 oversampled text at 1:1"); - } else if (font % 3 == 2) { - print(100, 325, sfont, "3x1 oversampled text at 1:1"); - } else if (integer_align) { - print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates"); - } else { - print(100, 325, sfont, "1:1 text, one texel = one pixel"); - } - - if (show_tex) { - glBegin(GL_QUADS); - drawBoxTC(200, 400, 200 + BITMAP_W, 300 + BITMAP_H, 0, 0, 1, 1); - glEnd(); - } else { - glMatrixMode(GL_MODELVIEW); - glTranslatef(200, 350, 0); - - if (translating) { - x += translate_t * 8 % 30; - } - - if (rotating) { - glTranslatef(100, 150, 0); - glRotatef(rotate_t * 2, 0, 0, 1); - glTranslatef(-100, -150, 0); - } - print(x, 100, font, "This is a test"); - print(x, 130, font, "Now is the time for all good men to come to the aid of their country."); - print(x, 160, font, "The quick brown fox jumps over the lazy dog."); - print(x, 190, font, "0123456789"); - } - } - - private void draw() { - draw_init(); - draw_world(); - glfwSwapBuffers(window); - } - - private void loopmode(float dt) { - if (dt > 0.25f) { - dt = 0.25f; - } - if (dt < 0.01f) { - dt = 0.01f; - } - - rotate_t += dt; - translate_t += dt; - - draw(); - } - - private void windowSizeChanged(long window, int width, int height) { - this.ww = width; - this.wh = height; - } - - private void framebufferSizeChanged(long window, int width, int height) { - this.fbw = width; - this.fbh = height; - } - - private void createWindow(String title) { - GLFWErrorCallback.createPrint().set(); - if (!glfwInit()) { - throw new IllegalStateException("Unable to initialize GLFW"); - } - - glfwDefaultWindowHints(); - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - - this.window = glfwCreateWindow(ww, wh, title, NULL, NULL); - if (window == NULL) { - throw new RuntimeException("Failed to create the GLFW window"); - } - - glfwSetWindowSizeCallback(window, this::windowSizeChanged); - glfwSetFramebufferSizeCallback(window, this::framebufferSizeChanged); - - glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> { - if (action == GLFW_RELEASE) { - return; - } - - switch (key) { - case GLFW_KEY_ESCAPE: - glfwSetWindowShouldClose(window, true); - break; - case GLFW_KEY_O: - font = (font + 1) % 3 + (font / 3) * 3; - break; - case GLFW_KEY_S: - font = (font + 3) % 6; - break; - case GLFW_KEY_T: - translating = !translating; - translate_t = 0.0f; - break; - case GLFW_KEY_R: - rotating = !rotating; - rotate_t = 0.0f; - break; - case GLFW_KEY_P: - integer_align = !integer_align; - break; - case GLFW_KEY_G: - if (!supportsSRGB) { - break; - } - - srgb = !srgb; - if (srgb) { - glEnable(GL30.GL_FRAMEBUFFER_SRGB); - } else { - glDisable(GL30.GL_FRAMEBUFFER_SRGB); - } - break; - case GLFW_KEY_V: - show_tex = !show_tex; - break; - case GLFW_KEY_B: - black_on_white = !black_on_white; - break; - } - }); - - // Center window - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - - glfwSetWindowPos( - window, - (vidmode.width() - ww) / 2, - (vidmode.height() - wh) / 2 - ); - - // Create context - glfwMakeContextCurrent(window); - GL.createCapabilities(); - debugProc = GLUtil.setupDebugMessageCallback(); - - glfwSwapInterval(1); - glfwShowWindow(window); - - GLFWUtil.glfwInvoke(window, this::windowSizeChanged, this::framebufferSizeChanged); - - // Detect sRGB support - GLCapabilities caps = GL.getCapabilities(); - supportsSRGB = caps.OpenGL30 || caps.GL_ARB_framebuffer_sRGB || caps.GL_EXT_framebuffer_sRGB; - } - - private void run(String title) { - try { - createWindow(title); - load_fonts(); - - long time = System.nanoTime(); - while (!glfwWindowShouldClose(window)) { - glfwPollEvents(); - - long t = System.nanoTime(); - float dt = (float)((t - time) / 1000000000.0); - time = t; - - loopmode(dt); - } - } finally { - try { - destroy(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - private void destroy() { - GL.setCapabilities(null); - - chardata.free(); - - if (debugProc != null) { - debugProc.free(); - } - - glfwFreeCallbacks(window); - glfwTerminate(); - glfwSetErrorCallback(null).free(); - - memFree(yb); - memFree(xb); - - q.free(); - } -} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt index d88e114e..166ab253 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/text/FontAtlas.kt @@ -74,7 +74,6 @@ class FontAtlas(private val data: LwjglStandardFont.FontData, private val config } } bitmap.position(0) - bitmapBuffer.write("font.png") } private fun getScale(data: LwjglStandardFont.FontData): Double { diff --git a/desktop-application/src/main/kotlin/temp/TextToPng.kt b/desktop-application/src/main/kotlin/temp/TextToPng.kt index 7a21a75e..80f2c2ae 100644 --- a/desktop-application/src/main/kotlin/temp/TextToPng.kt +++ b/desktop-application/src/main/kotlin/temp/TextToPng.kt @@ -1,6 +1,5 @@ package temp -import TruetypeOversample import com.mechanica.engine.context.GLContext import com.mechanica.engine.context.GLInitializer import com.mechanica.engine.context.loader.LwjglLoader @@ -15,15 +14,12 @@ import java.nio.ByteBuffer fun main() { - GLContext.initialize(Window.Companion.create("test", 100, 100)) + GLContext.initialize(Window.create("test", 100, 100)) GLInitializer.initialize(LwjglLoader()) val demo = TextToPngDemo("Roboto-Regular.ttf") - val text = "The quick brown fox jumped over the lazy dog" demo.packCharacters() -// STBTruetype.stbtt_PackBegin(ctx) - } private class TextToPngDemo(fontName: String) { @@ -158,8 +154,6 @@ private class TextToPngDemo(fontName: String) { val scale = stbtt_ScaleForPixelHeight(info, fontHeight.toFloat()) val ascent = (ascent*scale).toInt() - val descent = (descent*scale).toInt() - val lineGap = (lineGap*scale).toInt() var xAdvance = 0 @@ -168,7 +162,7 @@ private class TextToPngDemo(fontName: String) { val axBuffer = stack.mallocInt(1) val lsbBuffer = stack.mallocInt(1) - STBTruetype.stbtt_GetCodepointHMetrics(info, character, axBuffer, lsbBuffer) + stbtt_GetCodepointHMetrics(info, character, axBuffer, lsbBuffer) val ax = axBuffer[0] val lsb = lsbBuffer[0] @@ -178,13 +172,13 @@ private class TextToPngDemo(fontName: String) { val y1 = stack.mallocInt(1); val y2 = stack.mallocInt(1) - STBTruetype.stbtt_GetCodepointBitmapBox(info, character, scale, scale, x1, y1, x2, y2) + stbtt_GetCodepointBitmapBox(info, character, scale, scale, x1, y1, x2, y2) val y: Int = ascent + y1[0] val byteOffset: Int = x + (lsb * scale + y * width.toLong()).toInt() - STBTruetype.stbtt_MakeCodepointBitmap(info, bitmap.position(byteOffset), x2[0] - x1[0], y2[0] - y1[0], width, scale, scale, character) + stbtt_MakeCodepointBitmap(info, bitmap.position(byteOffset), x2[0] - x1[0], y2[0] - y1[0], width, scale, scale, character) xAdvance = x + (ax*scale).toInt() From 2043cbb189a9ebd86ab34c4eeedbcd203a4b3b2b Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Wed, 3 Jun 2020 15:12:07 +0100 Subject: [PATCH 03/21] Added the Scene and Process Tree logic --- .../src/main/kotlin/temp/TextToPng.kt | 194 ------------------ .../engine/drawer/shader/DrawerRenderer.kt | 2 +- .../engine/drawer/shader/DrawerShader.kt | 2 +- .../kotlin/com/mechanica/engine/game/Game.kt | 58 +++--- .../game/configuration/ConfigurationData.kt | 6 +- .../game/configuration/GameConfiguration.kt | 8 +- .../configuration/GameConfigurationImpl.kt | 11 +- .../engine/game/configuration/GameSetup.kt | 8 +- .../NullableConfigurationData.kt | 8 +- .../mechanica/engine/scenes/SceneManager.kt | 125 +++++++++++ .../engine/scenes/processes/Process.kt | 48 +++++ .../engine/scenes/processes/ProcessNode.kt | 8 + .../engine/scenes/processes/Updateable.kt | 5 + .../engine/scenes/scenes/Drawable.kt | 7 + .../engine/scenes/scenes/GUIScene.kt | 12 ++ .../engine/scenes/scenes/LoadScene.kt | 37 ++++ .../engine/scenes/scenes/MainScene.kt | 9 + .../mechanica/engine/scenes/scenes/Scene.kt | 62 ++++++ .../engine/scenes/scenes/SceneNode.kt | 11 + .../mechanica/engine/state/StateManager.kt | 4 +- .../mechanica/engine/samples/color/Main.kt | 4 +- .../engine/samples/display/DisplayDemo.kt | 4 +- .../engine/samples/geometry/BezierCurve.kt | 4 +- .../engine/samples/geometry/DrawingExample.kt | 4 +- .../engine/samples/geometry/LinesExample.kt | 4 +- .../mechanica/engine/samples/polygon/Main.kt | 4 +- .../mechanica/engine/samples/renderer/Main.kt | 4 +- .../engine/samples/text/TextEditor.kt | 14 +- 28 files changed, 399 insertions(+), 268 deletions(-) delete mode 100644 desktop-application/src/main/kotlin/temp/TextToPng.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Updateable.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Drawable.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt diff --git a/desktop-application/src/main/kotlin/temp/TextToPng.kt b/desktop-application/src/main/kotlin/temp/TextToPng.kt deleted file mode 100644 index 80f2c2ae..00000000 --- a/desktop-application/src/main/kotlin/temp/TextToPng.kt +++ /dev/null @@ -1,194 +0,0 @@ -package temp - -import com.mechanica.engine.context.GLContext -import com.mechanica.engine.context.GLInitializer -import com.mechanica.engine.context.loader.LwjglLoader -import com.mechanica.engine.display.Window -import com.mechanica.engine.memory.useMemoryStack -import com.mechanica.engine.resources.Resource -import org.lwjgl.BufferUtils -import org.lwjgl.stb.* -import org.lwjgl.stb.STBTruetype.* -import org.lwjgl.system.MemoryStack -import java.nio.ByteBuffer - - -fun main() { - GLContext.initialize(Window.create("test", 100, 100)) - GLInitializer.initialize(LwjglLoader()) - - val demo = TextToPngDemo("Roboto-Regular.ttf") - - demo.packCharacters() -} - -private class TextToPngDemo(fontName: String) { - - val info: STBTTFontinfo = STBTTFontinfo.create() - - val ascent: Int - val descent: Int - val lineGap: Int - - val fontHeight = 24 - - val width = 512 - val height = 512 - - val bitmap: ByteBuffer - val ttf: ByteBuffer = Resource("res/fonts/$fontName").buffer - - init { - check(stbtt_InitFont(info, ttf)) { "Failed to initialize font information." } - - var ascent = 0 - var descent = 0 - var lineGap = 0 - - MemoryStack.stackPush().use { stack -> - val pAscent = stack.mallocInt(1) - val pDescent = stack.mallocInt(1) - val pLineGap = stack.mallocInt(1) - stbtt_GetFontVMetrics(info, pAscent, pDescent, pLineGap) - ascent = pAscent[0] - descent = pDescent[0] - lineGap = pLineGap[0] - } - this.ascent = ascent - this.descent = descent - this.lineGap = lineGap - - bitmap = BufferUtils.createByteBuffer(width * height) - - } - - fun packCharacters() { - val cdata = STBTTPackedchar.malloc(128) - - STBTTPackContext.malloc().use { pc -> - stbtt_PackBegin(pc, bitmap, width, height, 0, 20) - stbtt_PackFontRange(pc, ttf, 0, 32f, 32, cdata) - stbtt_PackEnd(pc) - } - STBImageWrite.stbi_write_png("packed.png", width, height, 1, bitmap, width) - - useMemoryStack { - val xb = floats(0f) - val yb = floats(0f) - val q = STBTTAlignedQuad.malloc() - stbtt_GetPackedQuad(cdata, width, height, ')'.toInt() - 32, xb, yb, q, false) - println("p0: ${q.x0()}, ${q.y0()}") - println("p1: ${q.x1()}, ${q.y1()}") - println("t0: ${q.s0()}, ${q.t0()}") - println("t1: ${q.s1()}, ${q.t1()}") - q.free() - } - cdata.free() - } - - // Calling these functions in sequence is roughly equivalent to calling - // stbtt_PackFontRanges(). If you more control over the packing of multiple - // fonts, or if you want to pack custom data into a font texture, take a look - // at the source to of stbtt_PackFontRanges() and create a custom version - // using these functions, e.g. call GatherRects multiple times, - // building up a single array of rects, then call PackRects once, - // then call RenderIntoRects repeatedly. This may result in a - // better packing than calling PackFontRanges multiple times - // (or it may not). - fun packCharactersIndividually() { - val capacity = 95 - val cdata = STBTTPackedchar.calloc(capacity) -// cdata.limit(32 + 95) -// cdata.position(32) -// stbtt_PackFontRange(pc, ttf, 0, 24f, 32, cdata) - val range = STBTTPackRange.calloc(1) - range.first_unicode_codepoint_in_range(32) - - range.num_chars(cdata.remaining()) - range.chardata_for_range(cdata) - range.font_size(24f) - - STBTTPackContext.malloc().use { pc -> - stbtt_PackBegin(pc, bitmap, width, height, 0, 1) - - val rects = STBRPRect.malloc(capacity) - - stbtt_PackFontRangesGatherRects(pc, info, range, rects) - - stbtt_PackFontRangesPackRects(pc, rects) - - stbtt_PackFontRangesRenderIntoRects(pc, info, range, rects) - - stbtt_PackEnd(pc) - rects.free() - } - cdata.free() - range.free() - STBImageWrite.stbi_write_png("packed.png", width, height, 1, bitmap, width) - } - - fun saveAllCharacters() { - - var x = 0 - for (i in 'A'.toInt()..'z'.toInt()){ - x = addCharacterToBitmap(i.toChar(), bitmap, x) - } - - bitmap.position(0) - STBImageWrite.stbi_write_png("out.png", width, height, 1, bitmap, width) - } - - fun saveText(text: String) { - - var x = 0 - for (c in text){ - x = addCharacterToBitmap(c, bitmap, x) - } - - bitmap.position(0) - STBImageWrite.stbi_write_png("out.png", width, height, 1, bitmap, width) - } - - private fun addCharacterToBitmap(c: Char, bitmap: ByteBuffer, x: Int): Int { - - val scale = stbtt_ScaleForPixelHeight(info, fontHeight.toFloat()) - - val ascent = (ascent*scale).toInt() - - - var xAdvance = 0 - MemoryStack.stackPush().use { stack -> - val character = c.toInt() - - val axBuffer = stack.mallocInt(1) - val lsbBuffer = stack.mallocInt(1) - stbtt_GetCodepointHMetrics(info, character, axBuffer, lsbBuffer) - - val ax = axBuffer[0] - val lsb = lsbBuffer[0] - - val x1 = stack.mallocInt(1); - val x2 = stack.mallocInt(1) - val y1 = stack.mallocInt(1); - val y2 = stack.mallocInt(1) - - stbtt_GetCodepointBitmapBox(info, character, scale, scale, x1, y1, x2, y2) - - val y: Int = ascent + y1[0] - - val byteOffset: Int = x + (lsb * scale + y * width.toLong()).toInt() - - stbtt_MakeCodepointBitmap(info, bitmap.position(byteOffset), x2[0] - x1[0], y2[0] - y1[0], width, scale, scale, character) - - xAdvance = x + (ax*scale).toInt() - - /* add kerning */ -// if (c < text.length - 1) { -// val kern = STBTruetype.stbtt_GetCodepointKernAdvance(info, character, text[c + 1].toInt()) -// x += (kern * scale).toInt() -// } - } - - return xAdvance - } -} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt index 96607eef..c6613d0c 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt @@ -46,7 +46,7 @@ class DrawerRenderer { inColor = $color; } - if ($colorPassthrough == 0f) { + if ($colorPassthrough == 0.0) { vec2 st = pos - vec2(0.5); float height = abs($size.y); diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt index 3eb7ca03..6725e673 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt @@ -99,7 +99,7 @@ class DrawerShader( //language=GLSL script.addOther(""" vec4 matrices(vec4 position) { - if(matrixType == 0f) { + if(matrixType == 0.0) { return pvMatrix*transformation*position; } return projection*view*transformation*position; diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt index 6fd8ed55..d1b715f0 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt @@ -9,19 +9,18 @@ import com.mechanica.engine.persistence.loadData import com.mechanica.engine.persistence.saveData import com.mechanica.engine.game.configuration.GameConfiguration import com.mechanica.engine.game.configuration.GameConfigurationImpl -import com.mechanica.engine.game.configuration.GameSetup import com.mechanica.engine.game.view.GameMatrices import com.mechanica.engine.game.view.GameView import com.mechanica.engine.game.view.View import com.mechanica.engine.context.GLInitializer import com.mechanica.engine.context.loader.LwjglLoader import com.mechanica.engine.matrix.Matrices -import com.mechanica.engine.state.State -import com.mechanica.engine.state.StateManager +import com.mechanica.engine.scenes.SceneManager +import com.mechanica.engine.scenes.processes.Process +import com.mechanica.engine.scenes.scenes.* import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.vec import com.mechanica.engine.util.Timer -import org.lwjgl.glfw.GLFW object Game { private val configuration = GameConfigurationImpl() @@ -39,11 +38,21 @@ object Game { private val gameMatrices: GameMatrices get() = matrices as GameMatrices - private val stateManager = StateManager() + private val sceneManager = SceneManager() + val scene: Scene + get() = sceneManager.currentScene ?: throw UninitializedPropertyAccessException("The top level scene has not yet been initialized") private var hasStarted = false private var hasFinished = false + fun addProcess(process: Process) { + sceneManager.addProcess(process) + } + + fun addScene(scene: Scene) { + sceneManager.addScene(scene) + } + fun configure(setup: GameConfiguration.() -> Unit) { setup(configuration) if (configuration.initalize) start() @@ -59,7 +68,7 @@ object Game { Timer loadPersistenceData() - setStartingState(data) + sceneManager.setStartingScene(data) hasStarted = true } @@ -71,17 +80,25 @@ object Game { } } - fun run(update: () -> Unit = { }) { + fun run() { start() + loop() + } + + fun run(update: (Double) -> Unit) { + start() + sceneManager.updateVar = update + loop() + } + + private fun loop() { try { while (!hasFinished) { GLContext.startFrame() gameMatrices.updateMatrices() - stateManager.updateState() - - update() + sceneManager.updateScenes() if (!window.update()) { return @@ -106,25 +123,8 @@ object Game { GLFWContext.terminate() } - fun setCurrentState(setter: () -> State?) { - stateManager.setCurrentState(setter) - } - - private fun setStartingState(data: GameSetup) { - val state = data.startingState - val loadState = data.loadState?.invoke() - - if (loadState != null) { - loadState.onFinish = { - setCurrentState { state?.invoke() } - } - setCurrentState { - loadState.preLoad() - loadState - } - } else { - setCurrentState { state?.invoke() } - } + fun setMainScene(setter: () -> MainScene?) { + sceneManager.setMainScene(setter) } private fun refreshView(window: Window) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt index 9d0b6bc7..bfc2f8f1 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt @@ -6,6 +6,8 @@ import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.view.View import com.mechanica.engine.input.ControlsMap +import com.mechanica.engine.scenes.scenes.MainScene +import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f import com.mechanica.engine.state.LoadState import com.mechanica.engine.state.State @@ -22,8 +24,8 @@ interface ConfigurationData { val saveData: Array? val controlsMap: ControlsMap? val fullscreen: Boolean? - val startingState: (() -> State)? - val loadState: (() -> LoadState)? + val startingScene: (() -> MainScene)? + val loadState: (() -> LoadScene)? val windowConfiguration: (Window.() -> Unit)? val debugConfiguration: (GameDebugConfiguration.() -> Unit)? val projectionMatrixConfiguration: (Matrix4f.(View) -> Unit)? diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt index 749c11bd..688e3e4b 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt @@ -4,9 +4,9 @@ import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.game.view.View import com.mechanica.engine.input.ControlsMap +import com.mechanica.engine.scenes.scenes.MainScene +import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f -import com.mechanica.engine.state.LoadState -import com.mechanica.engine.state.State interface GameConfiguration { var initalize: Boolean @@ -15,8 +15,8 @@ interface GameConfiguration { fun setViewport(width: Double = 0.0, height: Double = 0.0) fun setViewLocation(x: Double, y: Double) - fun setStartingState(state: () -> State) - fun setLoader(loader: () -> LoadState) + fun setStartingState(scene: () -> MainScene) + fun setLoader(loader: () -> LoadScene) fun setControlMapping(controlsMap: ControlsMap) fun setSaveData(vararg savedata: Any) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt index c5336ee9..b864539d 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt @@ -3,12 +3,11 @@ package com.mechanica.engine.game.configuration import com.mechanica.engine.context.GLFWContext import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration -import com.mechanica.engine.game.Game import com.mechanica.engine.game.view.View import com.mechanica.engine.input.ControlsMap +import com.mechanica.engine.scenes.scenes.MainScene +import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f -import com.mechanica.engine.state.LoadState -import com.mechanica.engine.state.State internal class GameConfigurationImpl : GameConfiguration { private val _data = NullableConfigurationData() @@ -40,11 +39,11 @@ internal class GameConfigurationImpl : GameConfiguration { _data.viewY = y } - override fun setStartingState(state: () -> State) { - _data.startingState = state + override fun setStartingState(scene: () -> MainScene) { + _data.startingScene = scene } - override fun setLoader(loader: () -> LoadState) { + override fun setLoader(loader: () -> LoadScene) { _data.loadState = loader } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt index c05d77a2..797c9b46 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt @@ -9,10 +9,10 @@ import com.mechanica.engine.game.view.View import com.mechanica.engine.input.ControlsMap import com.mechanica.engine.input.KeyboardHandler import com.mechanica.engine.input.MouseHandler +import com.mechanica.engine.scenes.scenes.MainScene +import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f import org.lwjgl.glfw.GLFW -import com.mechanica.engine.state.LoadState -import com.mechanica.engine.state.State class GameSetup(data: NullableConfigurationData) : ConfigurationData { override val monitor = Monitor.getPrimaryMonitor() @@ -27,8 +27,8 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { override val saveData: Array = data.saveData ?: emptyArray() override val controlsMap: ControlsMap = data.controlsMap ?: object : ControlsMap() { } override val fullscreen: Boolean = data.fullscreen ?: false - override val startingState: (() -> State)? = data.startingState - override val loadState: (() -> LoadState)? = data.loadState + override val startingScene: (() -> MainScene)? = data.startingScene + override val loadState: (() -> LoadScene)? = data.loadState override val windowConfiguration: (Window.() -> Unit) = data.windowConfiguration ?: { } override val debugConfiguration: (GameDebugConfiguration.() -> Unit) = data.debugConfiguration ?: { } override val projectionMatrixConfiguration: (Matrix4f.(View) -> Unit) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt index cf476cb2..706dd925 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt @@ -5,9 +5,9 @@ import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.game.view.View import com.mechanica.engine.input.ControlsMap +import com.mechanica.engine.scenes.scenes.MainScene +import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f -import com.mechanica.engine.state.LoadState -import com.mechanica.engine.state.State class NullableConfigurationData : ConfigurationData { override val monitor: Monitor? = null @@ -28,8 +28,8 @@ class NullableConfigurationData : ConfigurationData { override var fullscreen: Boolean? = null - override var startingState: (() -> State)? = null - override var loadState: (() -> LoadState)? = null + override var startingScene: (() -> MainScene)? = null + override var loadState: (() -> LoadScene)? = null override var windowConfiguration: (Window.() -> Unit)? = null override var debugConfiguration: (GameDebugConfiguration.() -> Unit)? = null diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt new file mode 100644 index 00000000..51d03bc3 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt @@ -0,0 +1,125 @@ +package com.mechanica.engine.scenes + +import com.mechanica.engine.debug.DebugDrawer +import com.mechanica.engine.debug.ScreenLog +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.Game +import com.mechanica.engine.game.configuration.GameSetup +import com.mechanica.engine.game.view.View +import com.mechanica.engine.scenes.processes.Process +import com.mechanica.engine.scenes.scenes.MainScene +import com.mechanica.engine.scenes.scenes.Scene +import com.mechanica.engine.util.Timer + +internal class SceneManager : Scene() { + override val view: View + get() = Game.view + + var updateVar: ((Double) -> Unit)? = null + set(value) { + field = value + if (value != null) { + updateAndRenderVar = null + } + } + var updateAndRenderVar: ((Double, Drawer) -> Unit)? = null + set(value) { + field = value + if (value != null) { + updateVar = null + } + } + + private var sceneSetter: () -> MainScene? = { null } + var currentScene: MainScene? = null + get() { + val first = if (childScenes.isNotEmpty()) childScenes.first() else null + return if (first is MainScene) first else null + } + private set(value) { + val children = childScenes as ArrayList + if (value != null) { + when { + children.isEmpty() -> super.addScene(value) + children[0] === field -> children[0] = value + else -> children.add(0, value) + } + } + field = value + } + + private var scheduleSceneChange = true + + private var drawer: Drawer? = null + + private var startOfLoop = Timer.now + private var updateDuration = 0.1 + + fun setStartingScene(data: GameSetup) { + val state = data.startingScene + val loadState = data.loadState?.invoke() + + if (loadState != null) { + loadState.onFinish = { + setMainScene { state?.invoke() } + } + setMainScene { + loadState.preLoad() + loadState + } + } else { + setMainScene { state?.invoke() } + } + } + + fun setMainScene(setter: () -> MainScene?) { + sceneSetter = setter + scheduleSceneChange = true + } + + fun updateScenes(): Double { + updateDuration = Timer.now - startOfLoop + startOfLoop = Timer.now + + checkStateChange() + + internalUpdate(updateDuration) + + render() + + return updateDuration + } + + override fun update(delta: Double) { + updateVar?.invoke(delta) + } + + private fun render() { + if (currentScene != null || childScenes.isNotEmpty()) { + internalRender(getDrawer()) + } + + if (Game.debug.screenLog) + ScreenLog.render(getDrawer()) + if (Game.debug.constructionDraws) + DebugDrawer.render(getDrawer()) + } + + override fun render(draw: Drawer) { + updateAndRenderVar?.invoke(updateDuration, draw) + } + + private fun checkStateChange() { + if (scheduleSceneChange) { + currentScene = sceneSetter() + scheduleSceneChange = false + } + } + + private fun getDrawer(): Drawer { + val drawer = this.drawer ?: Drawer.create() + this.drawer = drawer + return drawer + } + +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt new file mode 100644 index 00000000..27310a16 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt @@ -0,0 +1,48 @@ +package com.mechanica.engine.scenes.processes + +abstract class Process : ProcessNode { + + protected val childProcesses: List = ArrayList() + + override fun addProcess(process: P): P { + (childProcesses as ArrayList).add(process) + return process + } + + override fun removeProcess(process: Process): Boolean { + process.destructor() + return (childProcesses as ArrayList).remove(process) + } + + override fun replaceProcess(old: P, new: P): P { + val index = childProcesses.indexOf(old) + if (index != -1) { + childProcesses[index].destructor() + (childProcesses as ArrayList)[index] = new + return new + } + return old + } + + inline fun forEachProcess(operation: (Process)-> Unit) { + for (i in `access$childProcesses`.indices) { + operation(`access$childProcesses`[i]) + } + } + + internal open fun internalUpdate(delta: Double) { + this.update(delta) + for (i in childProcesses.indices) { + childProcesses[i].internalUpdate(delta) + } + } + + override fun destructor() { } + + protected fun finalize() { destructor() } + + @Suppress("PropertyName") + @PublishedApi + internal val `access$childProcesses`: List + get() = childProcesses +} diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt new file mode 100644 index 00000000..1fc53472 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt @@ -0,0 +1,8 @@ +package com.mechanica.engine.scenes.processes + +interface ProcessNode : Updateable { + fun addProcess(process: P): P + fun removeProcess(process: Process): Boolean + fun replaceProcess(old: P, new: P): P + fun destructor() +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Updateable.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Updateable.kt new file mode 100644 index 00000000..9d12dd6d --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Updateable.kt @@ -0,0 +1,5 @@ +package com.mechanica.engine.scenes.processes + +interface Updateable { + fun update(delta: Double) +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Drawable.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Drawable.kt new file mode 100644 index 00000000..ebb9a066 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Drawable.kt @@ -0,0 +1,7 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.drawer.Drawer + +interface Drawable { + fun render(draw: Drawer) +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt new file mode 100644 index 00000000..3234506b --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt @@ -0,0 +1,12 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.Game +import com.mechanica.engine.game.view.View + +abstract class GUIScene : Scene() { + override val view: View + get() = Game.ui + override val Drawer.inScene: Drawer + get() = drawInScene(this, view).ui +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt new file mode 100644 index 00000000..ed7d6d51 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt @@ -0,0 +1,37 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.drawer.Drawer + +abstract class LoadScene : MainScene() { + private val waitTime = 0.2f + private val minLoops = 2 + + private var currentLoops = 0 + private var currentWait = 0.0 + private var startLoading = false + private var finishedLoading = false + + override fun update(delta: Double) { + currentWait += delta + currentLoops++ + startLoading = currentWait > waitTime && currentLoops > minLoops + if (finishedLoading) onFinish() + + } + + override fun render(draw: Drawer) { + renderLoadScreen(draw) + if (startLoading && !finishedLoading) { + load() + finishedLoading = true + } + + } + + abstract fun preLoad() + abstract fun renderLoadScreen(draw: Drawer) + + abstract fun load() + + internal var onFinish: () -> Unit = { } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt new file mode 100644 index 00000000..6d4f7985 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt @@ -0,0 +1,9 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.game.Game +import com.mechanica.engine.game.view.GameView + +abstract class MainScene : Scene() { + override val view: GameView + get() = Game.view +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt new file mode 100644 index 00000000..65480641 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt @@ -0,0 +1,62 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.view.View +import com.mechanica.engine.scenes.processes.Process + +abstract class Scene : Process(), SceneNode { + + protected val childScenes: List = ArrayList() + + protected open val Drawer.inScene: Drawer + get() = drawInScene(this, view) + + override fun addScene(scene: S): S { + (childScenes as ArrayList).add(scene) + return scene + } + + override fun removeScene(scene: Scene): Boolean { + scene.destructor() + return (childScenes as ArrayList).remove(scene) + } + + override fun replaceScene(old: S, new: S): S { + val index = childScenes.indexOf(old) + if (index != -1) { + childScenes[index].destructor() + (childScenes as ArrayList)[index] = new + return new + } + return old + } + + inline fun forEachScene(operation: (Scene)-> Unit) { + for (i in `access$childScenes`.indices) { + operation(`access$childScenes`[i]) + } + } + + override fun internalUpdate(delta: Double) { + super.internalUpdate(delta) + for (i in childScenes.indices) { + childScenes[i].internalUpdate(delta) + } + } + + internal fun internalRender(draw: Drawer) { + this.render(draw) + for (i in childScenes.indices) { + childScenes[i].internalRender(draw) + } + } + + @Suppress("PropertyName") + @PublishedApi + internal val `access$childScenes`: List + get() = childScenes + + companion object { + internal fun drawInScene(draw: Drawer, view: View): Drawer = draw.transformed.translate(view.x, view.y).layout.origin() + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt new file mode 100644 index 00000000..15b464bb --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt @@ -0,0 +1,11 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.game.view.View +import com.mechanica.engine.scenes.processes.ProcessNode + +interface SceneNode : ProcessNode, Drawable { + val view: View + fun addScene(scene: S): S + fun removeScene(scene: Scene): Boolean + fun replaceScene(old: S, new: S): S +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/state/StateManager.kt b/mechanica/src/main/kotlin/com/mechanica/engine/state/StateManager.kt index 372139c9..f154bbf7 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/state/StateManager.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/state/StateManager.kt @@ -17,7 +17,7 @@ internal class StateManager { private var startOfLoop = Timer.now private var updateDuration = 0.1 - fun updateState() { + fun updateState(): Double { updateDuration = Timer.now - startOfLoop startOfLoop = Timer.now @@ -33,6 +33,8 @@ internal class StateManager { ScreenLog.render(drawer) if (Game.debug.constructionDraws) DebugDrawer.render(drawer) + + return updateDuration } fun setCurrentState(setter: () -> State?) { diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt index e8e8936d..4ee929ca 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt @@ -3,7 +3,7 @@ package com.mechanica.engine.samples.color import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.input.Mouse -import com.mechanica.engine.state.State +import com.mechanica.engine.scenes.scenes.MainScene fun main() { Game.configure { @@ -16,7 +16,7 @@ fun main() { } -private class StartMain : State() { +private class StartMain : MainScene() { override fun update(delta: Double) { } diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/display/DisplayDemo.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/display/DisplayDemo.kt index 7e47092f..c06e38cb 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/display/DisplayDemo.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/display/DisplayDemo.kt @@ -2,7 +2,7 @@ package com.mechanica.engine.samples.display import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game -import com.mechanica.engine.state.State +import com.mechanica.engine.scenes.scenes.MainScene fun main() { Game.configure { @@ -13,7 +13,7 @@ fun main() { Game.run() } -class DisplayTestState : State() { +class DisplayTestState : MainScene() { override fun update(delta: Double) { } override fun render(draw: Drawer) { diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt index 93008c9d..bfa44b75 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt @@ -5,7 +5,7 @@ import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.input.Keyboard import com.mechanica.engine.input.Mouse -import com.mechanica.engine.state.State +import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.* fun main() { @@ -17,7 +17,7 @@ fun main() { Game.run() } -class BezierCurve : State() { +class BezierCurve : MainScene() { private val maxVertices = 1000 private val minLineLength = 0.1 diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt index 851bfeb0..cc289015 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt @@ -6,7 +6,7 @@ import com.mechanica.engine.drawer.shader.PathRenderer import com.mechanica.engine.game.Game import com.mechanica.engine.input.Keyboard import com.mechanica.engine.input.Mouse -import com.mechanica.engine.state.State +import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.distanceTo import com.mechanica.engine.unit.vector.vec @@ -21,7 +21,7 @@ fun main() { Game.run() } -class DrawingExample : State() { +class DrawingExample : MainScene() { private val renderer = PathRenderer() private val transformation = Matrix4f() diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt index 87272d70..5986deed 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt @@ -9,10 +9,10 @@ import com.mechanica.engine.geometry.lines.LineSegment import com.mechanica.engine.geometry.lines.LineSegmentImpl import com.mechanica.engine.input.Keyboard import com.mechanica.engine.input.Mouse -import com.mechanica.engine.state.State import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.vec import com.mechanica.engine.debug.ScreenLog +import com.mechanica.engine.scenes.scenes.MainScene fun main() { Game.configure { @@ -23,7 +23,7 @@ fun main() { Game.run() } -class LinesDemo : State() { +class LinesDemo : MainScene() { private val stroke = 0.05f private val renderer = PathRenderer() diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt index af8937f4..42e27bb5 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt @@ -9,7 +9,7 @@ import com.mechanica.engine.geometry.lines.LineSegmentImpl import com.mechanica.engine.input.Keyboard import com.mechanica.engine.input.Mouse import com.mechanica.engine.models.PolygonModel -import com.mechanica.engine.state.State +import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.vec fun main() { @@ -34,7 +34,7 @@ fun main() { } } -private class StartMain : State() { +private class StartMain : MainScene() { val pathRenderer = PathRenderer() val points = arrayOf( diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt index a8bce753..5f2ce54a 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt @@ -8,7 +8,7 @@ import com.mechanica.engine.input.Keyboard import com.mechanica.engine.input.Mouse import com.mechanica.engine.models.PolygonModel import com.mechanica.engine.resources.Res -import com.mechanica.engine.state.State +import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.angle.degrees import com.mechanica.engine.unit.vector.vec import org.joml.Matrix4f @@ -24,7 +24,7 @@ fun main() { Game.run() } -private class StartMain : State() { +private class StartMain : MainScene() { private val transformation = Matrix4f() val image: Image diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt index 3d1d9702..9722eb55 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt @@ -4,8 +4,7 @@ import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.input.Keyboard import com.mechanica.engine.input.Mouse -import com.mechanica.engine.models.Model -import com.mechanica.engine.state.State +import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.vec import com.mechanica.engine.util.extensions.constrain import org.joml.Matrix4f @@ -26,10 +25,9 @@ fun main() { } -private class StartText : State() { +private class StartText : MainScene() { val renderer = FontRenderer() - val model = Model() val transformation = Matrix4f() val startPosition = vec(-Game.view.width.toFloat()/2f, Game.view.height.toFloat()/2f - renderer.fontSize) @@ -45,15 +43,15 @@ private class StartText : State() { override fun update(delta: Double) { fun setViewPosition() { - Game.view.x = startPosition.x + Game.view.width/2.0 - Game.view.y = startPosition.y + 1.0 - Game.view.height/2.0 + view.x = startPosition.x + Game.view.width/2.0 + view.y = startPosition.y + 1.0 - Game.view.height/2.0 } if (Mouse.scrollDown.hasBeenPressed) { - Game.view.height *= 1.0 + Mouse.scrollDown.distance/10.0 + view.height *= 1.0 + Mouse.scrollDown.distance/10.0 setViewPosition() } if (Mouse.scrollUp.hasBeenPressed) { - Game.view.height /= 1.0 + Mouse.scrollUp.distance/10.0 + view.height /= 1.0 + Mouse.scrollUp.distance/10.0 setViewPosition() } From 64e0d18736704b45e1be962ef118bee84ecdbf1c Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Thu, 4 Jun 2020 21:44:26 +0100 Subject: [PATCH 04/21] Updated the input logic --- .../mechanica/engine/context/GLInitializer.kt | 4 +- .../context/callbacks/EventCallbacks.kt | 20 ++ .../context/callbacks/KeyboardHandler.kt | 18 ++ .../engine/context/callbacks/MouseHandler.kt | 57 ++++++ .../com/mechanica/engine/input/KeyInput.kt | 23 +++ .../com/mechanica/engine/input/TextInput.kt | 41 +++++ .../engine/util/NullTerminatedChars.kt | 77 ++++++++ .../com/mechanica/engine/context/GLContext.kt | 13 ++ .../com/mechanica/engine/display/Window.kt | 3 + .../mechanica/engine/input/GLFWKeyHandler.kt | 15 ++ .../engine/input/GLFWMouseButtonHandler.kt | 15 ++ .../engine/input/GLFWMouseCursorHandler.kt | 10 + .../engine/input/GLFWScrollHandler.kt | 10 + .../engine/input/GLFWTextInputHandler.kt | 11 ++ .../kotlin/com/mechanica/engine/game/Game.kt | 3 +- .../engine/game/configuration/GameSetup.kt | 14 -- .../engine/game/view/GameMatrices.kt | 3 - .../com/mechanica/engine/input/ControlsMap.kt | 4 +- .../kotlin/com/mechanica/engine/input/Key.kt | 48 ++--- .../com/mechanica/engine/input/Keyboard.kt | 172 ------------------ .../mechanica/engine/input/KeyboardHandler.kt | 32 ---- .../kotlin/com/mechanica/engine/input/Keys.kt | 3 +- .../com/mechanica/engine/input/Mouse.kt | 95 ---------- .../mechanica/engine/input/MouseHandler.kt | 49 ----- .../com/mechanica/engine/input/PressedKeys.kt | 68 ------- .../engine/input/keyboard/Keyboard.kt | 106 +++++++++++ .../engine/input/keyboard/KeyboardImpl.kt | 106 +++++++++++ .../com/mechanica/engine/input/mouse/Mouse.kt | 33 ++++ .../mechanica/engine/input/mouse/MouseImpl.kt | 61 +++++++ .../com/mechanica/engine/resources/Res.kt | 1 + .../mechanica/engine/scenes/SceneManager.kt | 4 +- .../engine/scenes/processes/Process.kt | 16 +- .../engine/scenes/processes/ProcessNode.kt | 7 +- .../mechanica/engine/scenes/scenes/Scene.kt | 22 +-- .../engine/scenes/scenes/SceneNode.kt | 8 +- .../mechanica/engine/samples/color/Main.kt | 2 +- .../engine/samples/drawer/DrawerDemo.kt | 2 +- .../engine/samples/geometry/BezierCurve.kt | 4 +- .../engine/samples/geometry/DrawingExample.kt | 4 +- .../engine/samples/geometry/LinesExample.kt | 4 +- .../mechanica/engine/samples/polygon/Main.kt | 4 +- .../engine/samples/polygon/PolygonRenderer.kt | 2 +- .../mechanica/engine/samples/renderer/Main.kt | 4 +- .../samples/shaders/FragmentRenderer.kt | 2 +- .../samples/shaders/FragmentShaderDemo.kt | 2 +- .../com/mechanica/engine/samples/temp/Test.kt | 4 +- .../engine/samples/text/FontRenderer.kt | 2 +- .../engine/samples/text/TextEditor.kt | 62 +++---- 48 files changed, 724 insertions(+), 546 deletions(-) create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/EventCallbacks.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/KeyboardHandler.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/MouseHandler.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/input/TextInput.kt create mode 100644 common/src/main/kotlin/com/mechanica/engine/util/NullTerminatedChars.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyHandler.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseButtonHandler.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseCursorHandler.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWScrollHandler.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWTextInputHandler.kt delete mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/Keyboard.kt delete mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/KeyboardHandler.kt delete mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/Mouse.kt delete mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/MouseHandler.kt delete mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/PressedKeys.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/Keyboard.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt index c67dd1ed..90a4f063 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt @@ -1,5 +1,6 @@ package com.mechanica.engine.context +import com.mechanica.engine.context.callbacks.EventCallbacks import com.mechanica.engine.context.loader.GLLoader object GLInitializer { @@ -7,9 +8,10 @@ object GLInitializer { internal val loader: GLLoader get() = _loader ?: throw UninitializedPropertyAccessException("The OpenGL context has not been initialized") - fun initialize(loader: GLLoader) { + fun initialize(loader: GLLoader): EventCallbacks { if (_loader == null) { _loader = loader + return EventCallbacks.create() } else throw IllegalStateException("The OpenGl context has already been initialized") } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/EventCallbacks.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/EventCallbacks.kt new file mode 100644 index 00000000..03528e88 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/EventCallbacks.kt @@ -0,0 +1,20 @@ +package com.mechanica.engine.context.callbacks + +import com.mechanica.engine.input.TextInput + +interface EventCallbacks { + val keyboardHandler: KeyboardHandler + val mouseHandler: MouseHandler + + companion object { + fun prepare() { + TextInput.prepare() + MouseHandler.prepare() + } + + internal fun create(): EventCallbacks = object : EventCallbacks { + override val keyboardHandler = KeyboardHandler.create() + override val mouseHandler = MouseHandler.create() + } + } +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/KeyboardHandler.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/KeyboardHandler.kt new file mode 100644 index 00000000..2717c0c2 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/KeyboardHandler.kt @@ -0,0 +1,18 @@ +package com.mechanica.engine.context.callbacks + +import com.mechanica.engine.input.KeyInput +import com.mechanica.engine.input.TextInput + +interface KeyboardHandler { + fun keyPressed(key: Int) + fun keyReleased(key: Int) + fun textInput(codepoint: Int) + + companion object { + internal fun create(): KeyboardHandler = object : KeyboardHandler { + override fun keyPressed(key: Int) = KeyInput.addPressed(key) + override fun keyReleased(key: Int) = KeyInput.removePressed(key) + override fun textInput(codepoint: Int) = TextInput.addCodePoint(codepoint) + } + } +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/MouseHandler.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/MouseHandler.kt new file mode 100644 index 00000000..59cd127d --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/callbacks/MouseHandler.kt @@ -0,0 +1,57 @@ +package com.mechanica.engine.context.callbacks + +import com.mechanica.engine.input.KeyInput + +interface MouseHandler { + fun buttonPressed(key: Int) + fun buttonReleased(key: Int) + fun cursorMoved(x: Double, y: Double) + fun scroll(x: Double, y: Double) + + companion object { + var cursorX: Double = 0.0 + private set + var cursorY: Double = 0.0 + private set + + var scrollX: Double = 0.0 + private set + var scrollY: Double = 0.0 + private set + + internal fun prepare() { + KeyInput.removePressed(1000) + KeyInput.removePressed(1001) + KeyInput.removePressed(1002) + scrollX = 0.0 + scrollY = 0.0 + } + + internal fun create() = object : MouseHandler { + override fun buttonPressed(key: Int) { + KeyInput.addPressed(key) + } + + override fun buttonReleased(key: Int) { + KeyInput.removePressed(key) + } + + override fun cursorMoved(x: Double, y: Double) { + cursorX = x + cursorY = y + } + + override fun scroll(x: Double, y: Double) { + if (y > 0.0) { + KeyInput.addPressed(1000) + } else if (y < 0.0) { + KeyInput.addPressed(1001) + } + KeyInput.addPressed(1002) + scrollX = x + scrollY = y + } + + } + } +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt new file mode 100644 index 00000000..11f1e734 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt @@ -0,0 +1,23 @@ +package com.mechanica.engine.input + +object KeyInput { + + var pressedKeyCount: Int = 0 + private set + val any: Boolean + get() = pressedKeyCount > 0 + private val pressedKeys = BooleanArray(1500) + + fun isPressed(keyId: Int) = pressedKeys[keyId] + + internal fun addPressed(key: Int) { + pressedKeyCount++ + pressedKeys[key] = true + } + + internal fun removePressed(key: Int) { + if (pressedKeyCount > 0) pressedKeyCount-- + pressedKeys[key] = false + } + +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/input/TextInput.kt b/application-interface/src/main/kotlin/com/mechanica/engine/input/TextInput.kt new file mode 100644 index 00000000..c51e40d9 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/input/TextInput.kt @@ -0,0 +1,41 @@ +package com.mechanica.engine.input + +import com.mechanica.engine.context.callbacks.KeyboardHandler +import com.mechanica.engine.util.NullTerminatedChars + +object TextInput { + + @PublishedApi + internal val codepoints = NullTerminatedChars(10) + var textInputCount: Int = 0 + private set + val hasTextInput: Boolean + get() = textInputCount != 0 + + internal fun prepare() { + codepoints.clear() + textInputCount = 0 + } + + fun getCodepoints(sb: StringBuilder, offset: Int = -1): Int { + return codepoints.get(sb, offset) + } + + fun getCodepoints(charArray: CharArray) { + codepoints.get(charArray) + } + + fun getCodepoints(chars: NullTerminatedChars) { + codepoints.get(chars) + } + + internal fun addCodePoint(cp: Int) { + codepoints.addCodePoint(cp) + textInputCount++ + } + + inline fun forEachChar(operation: (Char) -> Unit) { + codepoints.forEach(operation) + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/util/NullTerminatedChars.kt b/common/src/main/kotlin/com/mechanica/engine/util/NullTerminatedChars.kt new file mode 100644 index 00000000..b071375f --- /dev/null +++ b/common/src/main/kotlin/com/mechanica/engine/util/NullTerminatedChars.kt @@ -0,0 +1,77 @@ +package com.mechanica.engine.util + +import kotlin.math.min + +class NullTerminatedChars(capacity: Int = 50) { + val chars = CharArray(capacity) { Character.MIN_VALUE } + var size: Int = 0 + + fun add(char: Char) { + var index = 0 + + while (index < chars.size) { + if (chars[index] == Character.MIN_VALUE) { + chars[index] = char + break + } + index++ + } + size = index+1 + } + + fun addCodePoint(cp: Int) { + when { + Character.isBmpCodePoint(cp) -> { + add(cp.toChar()) + } + Character.isValidCodePoint(cp) -> { + add(Character.highSurrogate(cp)) + add(Character.lowSurrogate(cp)) + } + } + } + + fun get(other: NullTerminatedChars) { + get(other.chars) + } + + fun get(other: CharArray) { + val endIndex = min(other.size, this.chars.size) + this.chars.copyInto(other, endIndex = endIndex) + if (endIndex < other.size) { + other.fill(Character.MIN_VALUE, endIndex, other.lastIndex) + } + } + + fun get(sb: StringBuilder, offset: Int = -1): Int { + var count = 0 + forEach { + if (offset < 0) { + sb.append(it) + } else { + sb.insert(offset+count, it) + } + count++ + } + return count + } + + fun clear() { + size = 0 + set { Character.MIN_VALUE } + } + + inline fun forEach(operation: (Char) -> Unit) { + set { operation(it); it } + } + + inline fun set(operation: (Char) -> Char) { + var i = 0 + var currentChar = chars.first() + + while (currentChar != Character.MIN_VALUE && i < chars.size - 1) { + chars[i] = operation(currentChar) + currentChar = chars[++i] + } + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt index 86533d47..625037c0 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt @@ -1,6 +1,8 @@ package com.mechanica.engine.context +import com.mechanica.engine.context.callbacks.EventCallbacks import com.mechanica.engine.display.Window +import com.mechanica.engine.input.* import com.mechanica.engine.utils.enableAlphaBlending import org.lwjgl.glfw.GLFW import org.lwjgl.opengl.* @@ -52,6 +54,17 @@ object GLContext : Version { GL.createCapabilities() } + + fun setCallbacks(window: Window, callbacks: EventCallbacks) { + GLFW.glfwSetKeyCallback(window.id, GLFWKeyHandler(callbacks.keyboardHandler)) + GLFW.glfwSetCharCallback(window.id, GLFWTextInputHandler(callbacks.keyboardHandler)) + + GLFW.glfwSetMouseButtonCallback(window.id, GLFWMouseButtonHandler(callbacks.mouseHandler)) + GLFW.glfwSetCursorPosCallback(window.id, GLFWMouseCursorHandler(callbacks.mouseHandler)) + GLFW.glfwSetScrollCallback(window.id, GLFWScrollHandler(callbacks.mouseHandler)) + + } + private fun parseVersionString(): GLVersionStringParser { val versionString: String = GL11.glGetString(GL11.GL_VERSION) ?: throw IllegalStateException("Unable to get the version of OpenGL") diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/display/Window.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/display/Window.kt index 40f89506..b0d16cbb 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/display/Window.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/display/Window.kt @@ -1,6 +1,8 @@ package com.mechanica.engine.display import com.mechanica.engine.context.GLFWContext +import com.mechanica.engine.context.callbacks.EventCallbacks +import com.mechanica.engine.context.callbacks.KeyboardHandler import com.mechanica.engine.utils.ImageData import org.lwjgl.glfw.Callbacks import org.lwjgl.glfw.GLFW.* @@ -159,6 +161,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon fun update(): Boolean { swapBuffers() + EventCallbacks.prepare() pollEvents() val shouldClose = this.shouldClose if (finished) { diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyHandler.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyHandler.kt new file mode 100644 index 00000000..312dd561 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyHandler.kt @@ -0,0 +1,15 @@ +package com.mechanica.engine.input + +import com.mechanica.engine.context.callbacks.KeyboardHandler +import org.lwjgl.glfw.GLFW +import org.lwjgl.glfw.GLFWKeyCallback + +class GLFWKeyHandler(private val handler: KeyboardHandler) : GLFWKeyCallback() { + override fun invoke(window: Long, key: Int, scancode: Int, action: Int, mods: Int) { + if (action == GLFW.GLFW_PRESS) { + handler.keyPressed(key) + } else if (action == GLFW.GLFW_RELEASE) { + handler.keyReleased(key) + } + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseButtonHandler.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseButtonHandler.kt new file mode 100644 index 00000000..84369d26 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseButtonHandler.kt @@ -0,0 +1,15 @@ +package com.mechanica.engine.input + +import com.mechanica.engine.context.callbacks.MouseHandler +import org.lwjgl.glfw.GLFW +import org.lwjgl.glfw.GLFWMouseButtonCallback + +class GLFWMouseButtonHandler(private val handler: MouseHandler) : GLFWMouseButtonCallback() { + override fun invoke(window: Long, button: Int, action: Int, mods: Int) { + if (action == GLFW.GLFW_PRESS) { + handler.buttonPressed(button) + } else if (action == GLFW.GLFW_RELEASE) { + handler.buttonReleased(button) + } + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseCursorHandler.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseCursorHandler.kt new file mode 100644 index 00000000..d3f5e3ed --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWMouseCursorHandler.kt @@ -0,0 +1,10 @@ +package com.mechanica.engine.input + +import com.mechanica.engine.context.callbacks.MouseHandler +import org.lwjgl.glfw.GLFWCursorPosCallback + +class GLFWMouseCursorHandler(private val handler: MouseHandler) : GLFWCursorPosCallback() { + override fun invoke(window: Long, x: Double, y: Double) { + handler.cursorMoved(x, y) + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWScrollHandler.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWScrollHandler.kt new file mode 100644 index 00000000..96ec4bd5 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWScrollHandler.kt @@ -0,0 +1,10 @@ +package com.mechanica.engine.input + +import com.mechanica.engine.context.callbacks.MouseHandler +import org.lwjgl.glfw.GLFWScrollCallback + +class GLFWScrollHandler(private val handler: MouseHandler): GLFWScrollCallback() { + override fun invoke(window: Long, xoffset: Double, yoffset: Double) { + handler.scroll(xoffset, yoffset) + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWTextInputHandler.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWTextInputHandler.kt new file mode 100644 index 00000000..4d917bf0 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWTextInputHandler.kt @@ -0,0 +1,11 @@ +package com.mechanica.engine.input + +import com.mechanica.engine.context.callbacks.KeyboardHandler +import org.lwjgl.glfw.GLFWCharCallback + + +class GLFWTextInputHandler(private val handler: KeyboardHandler) : GLFWCharCallback() { + override fun invoke(window: Long, codepoint: Int) { + handler.textInput(codepoint) + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt index d1b715f0..69df6e4e 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt @@ -63,7 +63,8 @@ object Game { try { if (!hasStarted) { GLContext.initialize(window) - GLInitializer.initialize(LwjglLoader()) + val callbacks = GLInitializer.initialize(LwjglLoader()) + GLContext.setCallbacks(window, callbacks) window.addRefreshCallback { refreshView(it) } Timer diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt index 797c9b46..ed352f47 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt @@ -7,8 +7,6 @@ import com.mechanica.engine.game.view.GameMatrices import com.mechanica.engine.game.view.ResolutionConverter import com.mechanica.engine.game.view.View import com.mechanica.engine.input.ControlsMap -import com.mechanica.engine.input.KeyboardHandler -import com.mechanica.engine.input.MouseHandler import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f @@ -52,7 +50,6 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { ratio = resolutionHeight.toDouble()/resolutionWidth.toDouble() window = setWindow(resolutionWasSet) - setCallbacks(window) resolutionConverter = ResolutionConverter(resolutionWidth, resolutionHeight, data.viewWidth, data.viewHeight) @@ -80,17 +77,6 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { window.position.set((screenWidth - window.width)/2, (screenHeight - window.height)/2) } - private fun setCallbacks(window: Window) { - // Setup a key callback. It will be called every time a key is pressed, repeated or released. - GLFW.glfwSetKeyCallback(window.id, KeyboardHandler.KeyHandler()) - GLFW.glfwSetCharCallback(window.id, KeyboardHandler.TextHandler()) - - GLFW.glfwSetMouseButtonCallback(window.id, MouseHandler.ButtonHandler()) - GLFW.glfwSetCursorPosCallback(window.id, MouseHandler.CursorHandler()) - GLFW.glfwSetScrollCallback(window.id, MouseHandler.ScrollHandler()) - - } - private fun setResolution(resolutionWasSet: Boolean, data: NullableConfigurationData): Pair { val defaultResolutionWidth = (monitor.currentVideoMode.width()*0.75).toInt() val defaultResolutionHeight = (monitor.currentVideoMode.height()*0.75).toInt() diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameMatrices.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameMatrices.kt index 3b6918e6..e6faa3be 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameMatrices.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameMatrices.kt @@ -3,7 +3,6 @@ package com.mechanica.engine.game.view import com.mechanica.engine.display.Monitor import com.mechanica.engine.game.Game import com.mechanica.engine.game.configuration.GameSetup -import com.mechanica.engine.input.Mouse import com.mechanica.engine.matrix.Matrices import com.mechanica.engine.matrix.Matrices.Companion.calculatePixelSize import com.mechanica.engine.matrix.Matrices.Companion.getYScale @@ -31,8 +30,6 @@ internal class GameMatrices(data: GameSetup, viewPort: View) : Matrices { fun updateView(viewData: GameView) { updateView(viewData.x, viewData.y, viewData.height) - - Mouse.refreshCursor() } private fun updateView(x: Double, y: Double, height: Double) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt index cd1522f1..685b34ba 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt @@ -8,11 +8,11 @@ abstract class ControlsMap { if (keys != null) { return keys } - return arrayListOf(Key(map, keyId)) + return arrayListOf(Key(keyId)) } protected fun mapToKeys(vararg keys: Keys): Key { - return Key(map, *keys) + return Key(*keys) } companion object { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt index 6d724d2a..efd24de7 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt @@ -2,15 +2,13 @@ package com.mechanica.engine.input import org.lwjgl.glfw.GLFW -open class Key (map: HashMap>, private vararg val key: Int) { - private val label: String - get() = getLabel(key) - - internal var isDown: Boolean = false - set(value) { - if (value) hasBeenReleased = true +open class Key (private val condition: () -> Boolean) { + val isDown: Boolean + get() { + val isPressed = condition() + if (isPressed) hasBeenReleased = true else hasBeenPressed = true - field = value + return isPressed } private var privateHasBeenDown = false @@ -20,6 +18,7 @@ open class Key (map: HashMap>, private vararg val key: Int) } get() { if (isDown) { + privateHasBeenDown = true return false } if (privateHasBeenDown) { @@ -36,6 +35,7 @@ open class Key (map: HashMap>, private vararg val key: Int) } get() { if (!isDown) { + privateHasBeenUp = true return false } if (privateHasBeenUp) { @@ -45,33 +45,25 @@ open class Key (map: HashMap>, private vararg val key: Int) return privateHasBeenUp } - operator fun invoke(): Boolean { return isDown } - init { - key.forEach { - if (map.containsKey(it)) { - map[it]?.add(this) - } else { - map[it] = arrayListOf(this) - } - } - } + constructor(vararg key: Int) : this(keysToBoolean(*key)) - constructor(map: HashMap>, vararg keys: Keys) : this( - map, + constructor(vararg keys: Keys) : this( *(keys.map { it.id }).toIntArray() ) - private fun getLabel(keys: IntArray): String { - return if (keys.isNotEmpty()) { - GLFW.glfwGetKeyName(keys[0], 0) ?: "N/A" - } else "N/A" - } - - override fun toString(): String { - return label.toUpperCase() + companion object { + fun keysToBoolean(vararg key: Int): () -> Boolean { + return { + var isPressed = false + for (i in key.indices) { + isPressed = KeyInput.isPressed(key[i]) || isPressed + } + isPressed + } + } } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/Keyboard.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/Keyboard.kt deleted file mode 100644 index 70c3aac2..00000000 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/Keyboard.kt +++ /dev/null @@ -1,172 +0,0 @@ -@file:Suppress("unused") // There will be many variables here that go unused most of the time -package com.mechanica.engine.input - -import com.mechanica.engine.util.codepointToString -import java.util.* -import kotlin.collections.HashMap - - -object Keyboard { - private val map = HashMap>() - - val pressed: Iterable = PressedKeys(map) - - val textInput = TextInput() - - val any = KeyboardKey() - - val A = KeyboardKey(Keys.A) - val B = KeyboardKey(Keys.B) - val C = KeyboardKey(Keys.C) - val D = KeyboardKey(Keys.D) - val E = KeyboardKey(Keys.E) - val F = KeyboardKey(Keys.F) - val G = KeyboardKey(Keys.G) - val H = KeyboardKey(Keys.H) - val I = KeyboardKey(Keys.I) - val J = KeyboardKey(Keys.J) - val K = KeyboardKey(Keys.K) - val L = KeyboardKey(Keys.L) - val M = KeyboardKey(Keys.M) - val N = KeyboardKey(Keys.N) - val O = KeyboardKey(Keys.O) - val P = KeyboardKey(Keys.P) - val Q = KeyboardKey(Keys.Q) - val R = KeyboardKey(Keys.R) - val S = KeyboardKey(Keys.S) - val T = KeyboardKey(Keys.T) - val U = KeyboardKey(Keys.U) - val V = KeyboardKey(Keys.V) - val W = KeyboardKey(Keys.W) - val X = KeyboardKey(Keys.X) - val Y = KeyboardKey(Keys.Y) - val Z = KeyboardKey(Keys.Z) - - val space = KeyboardKey(Keys.SPACE) - val shift = KeyboardKey(Keys.LSHIFT) - val tab = KeyboardKey(Keys.TAB) - val lAlt = KeyboardKey(Keys.LALT) - val esc = KeyboardKey(Keys.ESC) - val comma = KeyboardKey(Keys.COMMA) - val period = KeyboardKey(Keys.PERIOD) - - val apostrophe = KeyboardKey(Keys.APOSTROPHE) - val minus = KeyboardKey(Keys.MINUS) - val slash = KeyboardKey(Keys.SLASH) - val N0 = KeyboardKey(Keys.N0) - val N1 = KeyboardKey(Keys.N1) - val N2 = KeyboardKey(Keys.N2) - val N3 = KeyboardKey(Keys.N3) - val N4 = KeyboardKey(Keys.N4) - val N5 = KeyboardKey(Keys.N5) - val N6 = KeyboardKey(Keys.N6) - val N7 = KeyboardKey(Keys.N7) - val N8 = KeyboardKey(Keys.N8) - val N9 = KeyboardKey(Keys.N9) - val semicolon = KeyboardKey(Keys.SEMICOLON) - val equal = KeyboardKey(Keys.EQUAL) - - val leftBracket = KeyboardKey(Keys.LEFT_BRACKET) - val backslash = KeyboardKey(Keys.BACKSLASH) - val rightBracket = KeyboardKey(Keys.RIGHT_BRACKET) - val graveAccent = KeyboardKey(Keys.GRAVE_ACCENT) - val world1 = KeyboardKey(Keys.WORLD_1) - val world2 = KeyboardKey(Keys.WORLD_2) - val enter = KeyboardKey(Keys.ENTER) - val backspace = KeyboardKey(Keys.BACKSPACE) - val insert = KeyboardKey(Keys.INSERT) - val delete = KeyboardKey(Keys.DELETE) - val right = KeyboardKey(Keys.RIGHT) - val left = KeyboardKey(Keys.LEFT) - val down = KeyboardKey(Keys.DOWN) - val up = KeyboardKey(Keys.UP) - val pageUp = KeyboardKey(Keys.PAGE_UP) - val pageDown = KeyboardKey(Keys.PAGE_DOWN) - val home = KeyboardKey(Keys.HOME) - val end = KeyboardKey(Keys.END) - val capsLock = KeyboardKey(Keys.CAPS_LOCK) - val scrollLock = KeyboardKey(Keys.SCROLL_LOCK) - val numLock = KeyboardKey(Keys.NUM_LOCK) - val printScreen = KeyboardKey(Keys.PRINT_SCREEN) - val pause = KeyboardKey(Keys.PAUSE) - - val F1 = KeyboardKey(Keys.F1) - val F2 = KeyboardKey(Keys.F2) - val F3 = KeyboardKey(Keys.F3) - val F4 = KeyboardKey(Keys.F4) - val F5 = KeyboardKey(Keys.F5) - val F6 = KeyboardKey(Keys.F6) - val F7 = KeyboardKey(Keys.F7) - val F8 = KeyboardKey(Keys.F8) - val F9 = KeyboardKey(Keys.F9) - val F10 = KeyboardKey(Keys.F10) - val F11 = KeyboardKey(Keys.F11) - val F12 = KeyboardKey(Keys.F12) - - val lShift = KeyboardKey(Keys.LSHIFT) - val lCtrl = KeyboardKey(Keys.LCTRL) - val ctrl = KeyboardKey(Keys.LCTRL, Keys.RCTRL) - val leftSuper = KeyboardKey(Keys.LEFT_SUPER) - val rShift = KeyboardKey(Keys.RSHIFT) - val rCtrl = KeyboardKey(Keys.RCTRL) - val rAlt = KeyboardKey(Keys.RALT) - val rightSuper = KeyboardKey(Keys.RIGHT_SUPER) - val menu = KeyboardKey(Keys.MENU) - - operator fun get(keyId: Int): ArrayList { - val keys = map[keyId] - if (keys != null) { - return keys - } - return arrayListOf(Key(map, keyId)) - } - - internal fun addPressed(key: Int) { - val pressed = (pressed as PressedKeys) - val pressedCount = pressed.size - get(key).forEach { it.isDown = true } - pressed.add(key) - any.isDown = pressed.size > pressedCount - } - - internal fun removePressed(key: Int) { - val pressed = (pressed as PressedKeys) - val pressedCount = pressed.size - get(key).forEach { it.isDown = false } - pressed.remove(key) - any.isDown = pressed.size >= pressedCount - } - - class KeyboardKey(vararg keys: Keys): Key(map, *keys) - - class TextInput { - - private val emptyString = "" - - var hasBeenInput: String = emptyString - get() { - val value = field - field = emptyString - return value - } - private set - - - private val inputCharacterHistory = Array(1024) { emptyString } - - var inputText = emptyString - private set(value) { - field = value - hasBeenInput = value - } - - internal fun setInputTextChar(codepoint: Int) { - var characters = inputCharacterHistory[codepoint] - if (characters == emptyString) { - characters = codepointToString(codepoint) - inputCharacterHistory[codepoint] = characters - } - inputText = characters - } - } -} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/KeyboardHandler.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/KeyboardHandler.kt deleted file mode 100644 index cb18f2cb..00000000 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/KeyboardHandler.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.mechanica.engine.input - -import com.mechanica.engine.game.Game -import org.lwjgl.glfw.GLFW.GLFW_PRESS -import org.lwjgl.glfw.GLFW.GLFW_RELEASE -import org.lwjgl.glfw.GLFWCharCallback -import org.lwjgl.glfw.GLFWKeyCallback - -/** - * Created by domin on 29/10/2017. - */ -class KeyboardHandler { - class KeyHandler : GLFWKeyCallback() { - override fun invoke(window: Long, key: Int, scancode: Int, action: Int, mods: Int) { - if (action == GLFW_PRESS) { - Keyboard.addPressed(key) - Game.controls[key].forEach { it.isDown = true } - } else if (action == GLFW_RELEASE) { - Keyboard.removePressed(key) - Game.controls[key].forEach { it.isDown = false } - } - } - - } - - class TextHandler : GLFWCharCallback() { - override fun invoke(window: Long, codepoint: Int) { - Keyboard.textInput.setInputTextChar(codepoint) - } - - } -} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt index 1b4ac69e..6ea97817 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt @@ -135,7 +135,8 @@ enum class Keys(val id: Int) { MENU(GLFW_KEY_MENU), SCROLL_UP(1000), - SCROLL_DOWN(1000); + SCROLL_DOWN(1001), + SCROLL(1002); companion object { fun uniqueId(): Int { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/Mouse.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/Mouse.kt deleted file mode 100644 index 43e3652b..00000000 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/Mouse.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.mechanica.engine.input - -import com.mechanica.engine.unit.vector.DynamicVector -import com.mechanica.engine.unit.vector.Vector -import com.mechanica.engine.game.Game - -object Mouse { - private val map = HashMap>() - - val MB1 = MouseButton(Keys.M1) - val MB2 = MouseButton(Keys.M2) - val MMB = MouseButton(Keys.M3) - val M4 = MouseButton(Keys.M4) - val M5 = MouseButton(Keys.M5) - val M6 = MouseButton(Keys.M6) - val M7 = MouseButton(Keys.M7) - val M8 = MouseButton(Keys.M8) - - val scrollUp = ScrollWheel(Keys.SCROLL_UP) - val scrollDown = ScrollWheel(Keys.SCROLL_DOWN) - - operator fun get(keyId: Int): ArrayList { - val keys = map[keyId] - if (keys != null) { - return keys - } - return arrayListOf(Key(map, keyId)) - } - - private val worldRatio: Vector = object : Vector { - override val x: Double - get() = Game.window.width/ Game.view.width - override val y: Double - get() = Game.window.height/ Game.view.height - } - - private val uiRatio: Vector = object : Vector { - override val x: Double - get() = Game.window.width/ Game.ui.width - override val y: Double - get() = Game.window.height/ Game.ui.height - - } - - val pixel: Vector = object : DynamicVector { - override var x: Double = 0.0 - set(value) { - field = value - setX(value) - } - override var y: Double = 0.0 - set(value) { - field = value - setY(value) - } - } - - val world: Vector = DynamicVector.create() - val ui: Vector = DynamicVector.create() - - fun refreshCursor() { - setX(pixel.x) - setY(pixel.y) - } - - private fun setX(value: Double) { - val uiX = value/ uiRatio.x - Game.ui.width/2.0 - val worldX = value/ worldRatio.x - Game.view.width/2.0 + Game.view.x - - setUi(x = uiX) - setWorld(x = worldX) - } - - private fun setY(value: Double) { - val uiY = Game.ui.height/2.0 - value/ uiRatio.y - val worldY = Game.view.height/2.0 - value/ worldRatio.y + Game.view.y - - setUi(y = uiY) - setWorld(y = worldY) - } - - private fun setWorld(x: Double = world.x, y: Double = world.y) { - (world as DynamicVector).set(x, y) - } - - private fun setUi(x: Double = ui.x, y: Double = ui.y) { - (ui as DynamicVector).set(x, y) - } - - class MouseButton(vararg keys: Keys): Key(map, *keys) - class ScrollWheel(vararg keys: Keys): Key(map, *keys) { - var distance = 0.0 - internal set - } -} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/MouseHandler.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/MouseHandler.kt deleted file mode 100644 index a415aeba..00000000 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/MouseHandler.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.mechanica.engine.input - -import com.mechanica.engine.unit.vector.DynamicVector -import com.mechanica.engine.game.Game -import org.lwjgl.glfw.GLFW -import org.lwjgl.glfw.GLFWCursorPosCallback -import org.lwjgl.glfw.GLFWMouseButtonCallback -import org.lwjgl.glfw.GLFWScrollCallback - -class MouseHandler { - class ButtonHandler : GLFWMouseButtonCallback() { - override fun invoke(window: Long, button: Int, action: Int, mods: Int) { - if (action == GLFW.GLFW_RELEASE) { - Keyboard[button].forEach { it.isDown = false } - Mouse[button].forEach { it.isDown = false } - Game.controls[button].forEach { it.isDown = false } - } else if (action == GLFW.GLFW_PRESS) { - Keyboard[button].forEach { it.isDown = true } - Mouse[button].forEach { it.isDown = true } - Game.controls[button].forEach { it.isDown = true } - } - } - - } - - class CursorHandler : GLFWCursorPosCallback() { - override fun invoke(window: Long, x: Double, y: Double) { - (Mouse.pixel as DynamicVector).set(x, y) - } - - } - - class ScrollHandler: GLFWScrollCallback() { - override fun invoke(window: Long, xoffset: Double, yoffset: Double) { - - if (yoffset > 0.0) { - Mouse.scrollUp.isDown = false - Mouse.scrollUp.isDown = true - Mouse.scrollUp.distance = yoffset - } else if (yoffset < 0.0) { - Mouse.scrollDown.isDown = false - Mouse.scrollDown.isDown = true - Mouse.scrollDown.distance = -yoffset - } - } - - } - -} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/PressedKeys.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/PressedKeys.kt deleted file mode 100644 index 200cfa35..00000000 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/PressedKeys.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.mechanica.engine.input - -internal class PressedKeys(private val map: HashMap>) : Iterable, Iterator { - private val emptyElement = -10 - private var pressedKeys = IntArray(100) { emptyElement } - private var current = 0 - private var currentIndex = -1 - var size = 0; private set - - override fun hasNext(): Boolean { - val hasNext = current < size - if (!hasNext) { - current = 0 - currentIndex = -1 - } - return hasNext - } - - override fun next(): Key { - current++ - var nextIndex = currentIndex + 1 - while (pressedKeys[nextIndex] == -10) { - nextIndex++ - } - currentIndex = nextIndex - - val keyId = pressedKeys[currentIndex] - val keys = map[keyId] - if (keys != null && keys.size > 0) { - return keys.first() - } - return Key(map, keyId) - } - - override fun iterator() = this - - fun add(key: Int) { - if (!pressedKeys.contains(key)) { - var firstAvailable = 0 - while (pressedKeys[firstAvailable] != -10) { - firstAvailable++ - } - sizeCheck() - pressedKeys[firstAvailable] = key - size++ - } - } - - fun remove(key: Int) { - val index = pressedKeys.indexOf(key) - if (index != -1) { - pressedKeys[index] = emptyElement - if (size > 0) { - size-- - } - } - } - - private fun sizeCheck() { - if (size + 1 >= pressedKeys.size) { - pressedKeys = IntArray(pressedKeys.size*2) { - if (pressedKeys.size < it) { - pressedKeys[it] - } else emptyElement - } - } - } -} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/Keyboard.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/Keyboard.kt new file mode 100644 index 00000000..640a2ab2 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/Keyboard.kt @@ -0,0 +1,106 @@ +@file:Suppress("unused") // There will be many variables here that go unused most of the time +package com.mechanica.engine.input.keyboard + +import com.mechanica.engine.input.Key +import java.util.ArrayList + + +interface Keyboard { + val any: Key + val A: Key + val B: Key + val C: Key + val D: Key + val E: Key + val F: Key + val G: Key + val H: Key + val I: Key + val J: Key + val K: Key + val L: Key + val M: Key + val N: Key + val O: Key + val P: Key + val Q: Key + val R: Key + val S: Key + val T: Key + val U: Key + val V: Key + val W: Key + val X: Key + val Y: Key + val Z: Key + val space: Key + val shift: Key + val tab: Key + val lAlt: Key + val esc: Key + val comma: Key + val period: Key + val apostrophe: Key + val minus: Key + val slash: Key + val N0: Key + val N1: Key + val N2: Key + val N3: Key + val N4: Key + val N5: Key + val N6: Key + val N7: Key + val N8: Key + val N9: Key + val semicolon: Key + val equal: Key + val leftBracket: Key + val backslash: Key + val rightBracket: Key + val graveAccent: Key + val world1: Key + val world2: Key + val enter: Key + val backspace: Key + val insert: Key + val delete: Key + val right: Key + val left: Key + val down: Key + val up: Key + val pageUp: Key + val pageDown: Key + val home: Key + val end: Key + val capsLock: Key + val scrollLock: Key + val numLock: Key + val printScreen: Key + val pause: Key + val F1: Key + val F2: Key + val F3: Key + val F4: Key + val F5: Key + val F6: Key + val F7: Key + val F8: Key + val F9: Key + val F10: Key + val F11: Key + val F12: Key + val lShift: Key + val lCtrl: Key + val ctrl: Key + val leftSuper: Key + val rShift: Key + val rCtrl: Key + val rAlt: Key + val rightSuper: Key + val menu: Key + + companion object : Keyboard by KeyboardImpl() { + fun create(): Keyboard = KeyboardImpl() + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt new file mode 100644 index 00000000..ad2d50d8 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt @@ -0,0 +1,106 @@ +package com.mechanica.engine.input.keyboard + +import com.mechanica.engine.input.Key +import com.mechanica.engine.input.KeyInput +import com.mechanica.engine.input.Keys + +internal class KeyboardImpl : Keyboard { + override val any: Key by lazy { Key { KeyInput.any } } + override val A: Key by lazy { Key(Keys.A) } + override val B: Key by lazy { Key(Keys.B) } + override val C: Key by lazy { Key(Keys.C) } + override val D: Key by lazy { Key(Keys.D) } + override val E: Key by lazy { Key(Keys.E) } + override val F: Key by lazy { Key(Keys.F) } + override val G: Key by lazy { Key(Keys.G) } + override val H: Key by lazy { Key(Keys.H) } + override val I: Key by lazy { Key(Keys.I) } + override val J: Key by lazy { Key(Keys.J) } + override val K: Key by lazy { Key(Keys.K) } + override val L: Key by lazy { Key(Keys.L) } + override val M: Key by lazy { Key(Keys.M) } + override val N: Key by lazy { Key(Keys.N) } + override val O: Key by lazy { Key(Keys.O) } + override val P: Key by lazy { Key(Keys.P) } + override val Q: Key by lazy { Key(Keys.Q) } + override val R: Key by lazy { Key(Keys.R) } + override val S: Key by lazy { Key(Keys.S) } + override val T: Key by lazy { Key(Keys.T) } + override val U: Key by lazy { Key(Keys.U) } + override val V: Key by lazy { Key(Keys.V) } + override val W: Key by lazy { Key(Keys.W) } + override val X: Key by lazy { Key(Keys.X) } + override val Y: Key by lazy { Key(Keys.Y) } + override val Z: Key by lazy { Key(Keys.Z) } + + override val space: Key by lazy { Key(Keys.SPACE) } + override val shift: Key by lazy { Key(Keys.LSHIFT) } + override val tab: Key by lazy { Key(Keys.TAB) } + override val lAlt: Key by lazy { Key(Keys.LALT) } + override val esc: Key by lazy { Key(Keys.ESC) } + override val comma: Key by lazy { Key(Keys.COMMA) } + override val period: Key by lazy { Key(Keys.PERIOD) } + override val apostrophe: Key by lazy { Key(Keys.APOSTROPHE) } + override val minus: Key by lazy { Key(Keys.MINUS) } + override val slash: Key by lazy { Key(Keys.SLASH) } + + override val N0: Key by lazy { Key(Keys.N0) } + override val N1: Key by lazy { Key(Keys.N1) } + override val N2: Key by lazy { Key(Keys.N2) } + override val N3: Key by lazy { Key(Keys.N3) } + override val N4: Key by lazy { Key(Keys.N4) } + override val N5: Key by lazy { Key(Keys.N5) } + override val N6: Key by lazy { Key(Keys.N6) } + override val N7: Key by lazy { Key(Keys.N7) } + override val N8: Key by lazy { Key(Keys.N8) } + override val N9: Key by lazy { Key(Keys.N9) } + + override val semicolon: Key by lazy { Key(Keys.SEMICOLON) } + override val equal: Key by lazy { Key(Keys.EQUAL) } + override val leftBracket: Key by lazy { Key(Keys.LEFT_BRACKET) } + override val backslash: Key by lazy { Key(Keys.BACKSLASH) } + override val rightBracket: Key by lazy { Key(Keys.RIGHT_BRACKET) } + override val graveAccent: Key by lazy { Key(Keys.GRAVE_ACCENT) } + override val world1: Key by lazy { Key(Keys.WORLD_1) } + override val world2: Key by lazy { Key(Keys.WORLD_2) } + override val enter: Key by lazy { Key(Keys.ENTER) } + override val backspace: Key by lazy { Key(Keys.BACKSPACE) } + override val insert: Key by lazy { Key(Keys.INSERT) } + override val delete: Key by lazy { Key(Keys.DELETE) } + override val right: Key by lazy { Key(Keys.RIGHT) } + override val left: Key by lazy { Key(Keys.LEFT) } + override val down: Key by lazy { Key(Keys.DOWN) } + override val up: Key by lazy { Key(Keys.UP) } + override val pageUp: Key by lazy { Key(Keys.PAGE_UP) } + override val pageDown: Key by lazy { Key(Keys.PAGE_DOWN) } + override val home: Key by lazy { Key(Keys.HOME) } + override val end: Key by lazy { Key(Keys.END) } + override val capsLock: Key by lazy { Key(Keys.CAPS_LOCK) } + override val scrollLock: Key by lazy { Key(Keys.SCROLL_LOCK) } + override val numLock: Key by lazy { Key(Keys.NUM_LOCK) } + override val printScreen: Key by lazy { Key(Keys.PRINT_SCREEN) } + override val pause: Key by lazy { Key(Keys.PAUSE) } + + override val F1: Key by lazy { Key(Keys.F1) } + override val F2: Key by lazy { Key(Keys.F2) } + override val F3: Key by lazy { Key(Keys.F3) } + override val F4: Key by lazy { Key(Keys.F4) } + override val F5: Key by lazy { Key(Keys.F5) } + override val F6: Key by lazy { Key(Keys.F6) } + override val F7: Key by lazy { Key(Keys.F7) } + override val F8: Key by lazy { Key(Keys.F8) } + override val F9: Key by lazy { Key(Keys.F9) } + override val F10: Key by lazy { Key(Keys.F10) } + override val F11: Key by lazy { Key(Keys.F11) } + override val F12: Key by lazy { Key(Keys.F12) } + + override val lShift: Key by lazy { Key(Keys.LSHIFT) } + override val lCtrl: Key by lazy { Key(Keys.LCTRL) } + override val ctrl: Key by lazy { Key(Keys.LCTRL, Keys.RCTRL) } + override val leftSuper: Key by lazy { Key(Keys.LEFT_SUPER) } + override val rShift: Key by lazy { Key(Keys.RSHIFT) } + override val rCtrl: Key by lazy { Key(Keys.RCTRL) } + override val rAlt: Key by lazy { Key(Keys.RALT) } + override val rightSuper: Key by lazy { Key(Keys.RIGHT_SUPER) } + override val menu: Key by lazy { Key(Keys.MENU) } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt new file mode 100644 index 00000000..ec6a25ee --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt @@ -0,0 +1,33 @@ +package com.mechanica.engine.input.mouse + +import com.mechanica.engine.context.callbacks.MouseHandler +import com.mechanica.engine.input.Key +import com.mechanica.engine.input.Keys +import com.mechanica.engine.unit.vector.Vector + + +interface Mouse { + val MB1: Key + val MB2: Key + val MMB: Key + val M4: Key + val M5: Key + val M6: Key + val M7: Key + val M8: Key + val scroll: ScrollWheel + val scrollDown: ScrollWheel + val scrollUp: ScrollWheel + val pixel: Vector + val world: Vector + val ui: Vector + + class ScrollWheel(vararg keys: Keys): Key(*keys) { + val distance: Double + get() = MouseHandler.scrollY + } + + companion object : Mouse by MouseImpl() { + fun create(): Mouse = MouseImpl() + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt new file mode 100644 index 00000000..4609b789 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt @@ -0,0 +1,61 @@ +package com.mechanica.engine.input.mouse + +import com.mechanica.engine.context.callbacks.MouseHandler +import com.mechanica.engine.unit.vector.Vector +import com.mechanica.engine.game.Game +import com.mechanica.engine.input.Key +import com.mechanica.engine.input.Keys + +internal class MouseImpl : Mouse { + override val MB1 = Key(Keys.M1) + override val MB2 = Key(Keys.M2) + override val MMB = Key(Keys.M3) + override val M4 = Key(Keys.M4) + override val M5 = Key(Keys.M5) + override val M6 = Key(Keys.M6) + override val M7 = Key(Keys.M7) + override val M8 = Key(Keys.M8) + + override val scroll = Mouse.ScrollWheel(Keys.SCROLL) + override val scrollDown = Mouse.ScrollWheel(Keys.SCROLL_DOWN) + override val scrollUp = Mouse.ScrollWheel(Keys.SCROLL_UP) + + private val worldRatio: Vector = object : Vector { + override val x: Double + get() = Game.window.width/ Game.view.width + override val y: Double + get() = Game.window.height/ Game.view.height + } + + private val uiRatio: Vector = object : Vector { + override val x: Double + get() = Game.window.width/ Game.ui.width + override val y: Double + get() = Game.window.height/ Game.ui.height + + } + + override val pixel: Vector = object : Vector { + override val x: Double + get() = MouseHandler.cursorX + override val y: Double + get() = MouseHandler.cursorY + + } + + override val world: Vector = object : Vector { + override val x: Double + get() = pixel.x/ worldRatio.x - Game.view.width/2.0 + Game.view.x + override val y: Double + get() = Game.view.height/2.0 - pixel.y/ worldRatio.y + Game.view.y + } + + override val ui: Vector = object : Vector { + override val x: Double + get() = pixel.x/ uiRatio.x - Game.ui.width/2.0 + override val y: Double + get() = Game.ui.height/2.0 - pixel.y/ uiRatio.y + + } + +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt b/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt index a964762d..03a06e2c 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt @@ -24,6 +24,7 @@ object Res { val svg = SpecificResource("res/svg/", "svg") val image = SpecificResource("res/images/", "png") val font = SpecificResource("res/fonts/", "ttf") + val audio = SpecificResource("res/audio/", "ogg") val animations = SpecificResourceDirectory("/res/animations/") fun external(path: String, createIfAbsent: Boolean = false): ExternalResource { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt index 51d03bc3..89becc74 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt @@ -83,7 +83,7 @@ internal class SceneManager : Scene() { checkStateChange() - internalUpdate(updateDuration) + updateNodes(updateDuration) render() @@ -96,7 +96,7 @@ internal class SceneManager : Scene() { private fun render() { if (currentScene != null || childScenes.isNotEmpty()) { - internalRender(getDrawer()) + renderNodes(getDrawer()) } if (Game.debug.screenLog) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt index 27310a16..1ef2452a 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt @@ -2,19 +2,19 @@ package com.mechanica.engine.scenes.processes abstract class Process : ProcessNode { - protected val childProcesses: List = ArrayList() + protected val childProcesses: List = ArrayList() - override fun addProcess(process: P): P { + override fun addProcess(process: P): P { (childProcesses as ArrayList).add(process) return process } - override fun removeProcess(process: Process): Boolean { + override fun removeProcess(process: ProcessNode): Boolean { process.destructor() return (childProcesses as ArrayList).remove(process) } - override fun replaceProcess(old: P, new: P): P { + override fun replaceProcess(old: P, new: P): P { val index = childProcesses.indexOf(old) if (index != -1) { childProcesses[index].destructor() @@ -24,16 +24,16 @@ abstract class Process : ProcessNode { return old } - inline fun forEachProcess(operation: (Process)-> Unit) { + inline fun forEachProcess(operation: (ProcessNode)-> Unit) { for (i in `access$childProcesses`.indices) { operation(`access$childProcesses`[i]) } } - internal open fun internalUpdate(delta: Double) { + override fun updateNodes(delta: Double) { this.update(delta) for (i in childProcesses.indices) { - childProcesses[i].internalUpdate(delta) + childProcesses[i].updateNodes(delta) } } @@ -43,6 +43,6 @@ abstract class Process : ProcessNode { @Suppress("PropertyName") @PublishedApi - internal val `access$childProcesses`: List + internal val `access$childProcesses`: List get() = childProcesses } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt index 1fc53472..94fe97e3 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt @@ -1,8 +1,9 @@ package com.mechanica.engine.scenes.processes interface ProcessNode : Updateable { - fun addProcess(process: P): P - fun removeProcess(process: Process): Boolean - fun replaceProcess(old: P, new: P): P + fun addProcess(process: P): P + fun removeProcess(process: ProcessNode): Boolean + fun replaceProcess(old: P, new: P): P + fun updateNodes(delta: Double) fun destructor() } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt index 65480641..59eb2bff 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt @@ -6,22 +6,22 @@ import com.mechanica.engine.scenes.processes.Process abstract class Scene : Process(), SceneNode { - protected val childScenes: List = ArrayList() + protected val childScenes: List = ArrayList() protected open val Drawer.inScene: Drawer get() = drawInScene(this, view) - override fun addScene(scene: S): S { + override fun addScene(scene: S): S { (childScenes as ArrayList).add(scene) return scene } - override fun removeScene(scene: Scene): Boolean { + override fun removeScene(scene: SceneNode): Boolean { scene.destructor() return (childScenes as ArrayList).remove(scene) } - override fun replaceScene(old: S, new: S): S { + override fun replaceScene(old: S, new: S): S { val index = childScenes.indexOf(old) if (index != -1) { childScenes[index].destructor() @@ -31,29 +31,29 @@ abstract class Scene : Process(), SceneNode { return old } - inline fun forEachScene(operation: (Scene)-> Unit) { + inline fun forEachScene(operation: (SceneNode)-> Unit) { for (i in `access$childScenes`.indices) { operation(`access$childScenes`[i]) } } - override fun internalUpdate(delta: Double) { - super.internalUpdate(delta) + override fun updateNodes(delta: Double) { + super.updateNodes(delta) for (i in childScenes.indices) { - childScenes[i].internalUpdate(delta) + childScenes[i].updateNodes(delta) } } - internal fun internalRender(draw: Drawer) { + override fun renderNodes(draw: Drawer) { this.render(draw) for (i in childScenes.indices) { - childScenes[i].internalRender(draw) + childScenes[i].renderNodes(draw) } } @Suppress("PropertyName") @PublishedApi - internal val `access$childScenes`: List + internal val `access$childScenes`: List get() = childScenes companion object { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt index 15b464bb..2a58a3e3 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/SceneNode.kt @@ -1,11 +1,13 @@ package com.mechanica.engine.scenes.scenes +import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.view.View import com.mechanica.engine.scenes.processes.ProcessNode interface SceneNode : ProcessNode, Drawable { val view: View - fun addScene(scene: S): S - fun removeScene(scene: Scene): Boolean - fun replaceScene(old: S, new: S): S + fun addScene(scene: S): S + fun removeScene(scene: SceneNode): Boolean + fun replaceScene(old: S, new: S): S + fun renderNodes(draw: Drawer) } \ No newline at end of file diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt index 4ee929ca..e4cb90d4 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/color/Main.kt @@ -2,7 +2,7 @@ package com.mechanica.engine.samples.color import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.scenes.scenes.MainScene fun main() { diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt index 960c7679..75bf51d1 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/drawer/DrawerDemo.kt @@ -4,7 +4,7 @@ import com.mechanica.engine.color.hex import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.utils.loadImage -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.models.PolygonModel import com.mechanica.engine.resources.Res import com.mechanica.engine.unit.angle.degrees diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt index bfa44b75..c50003b4 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/BezierCurve.kt @@ -3,8 +3,8 @@ package com.mechanica.engine.samples.geometry import com.mechanica.engine.color.rgba import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game -import com.mechanica.engine.input.Keyboard -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.* diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt index cc289015..3c7ccfb7 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/DrawingExample.kt @@ -4,8 +4,8 @@ import com.mechanica.engine.color.rgba import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.drawer.shader.PathRenderer import com.mechanica.engine.game.Game -import com.mechanica.engine.input.Keyboard -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.distanceTo diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt index 5986deed..b0354dbf 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/geometry/LinesExample.kt @@ -7,8 +7,8 @@ import com.mechanica.engine.drawer.shader.PathRenderer import com.mechanica.engine.game.Game import com.mechanica.engine.geometry.lines.LineSegment import com.mechanica.engine.geometry.lines.LineSegmentImpl -import com.mechanica.engine.input.Keyboard -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.vec import com.mechanica.engine.debug.ScreenLog diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt index 42e27bb5..053927e1 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/Main.kt @@ -6,8 +6,8 @@ import com.mechanica.engine.drawer.shader.PathRenderer import com.mechanica.engine.game.Game import com.mechanica.engine.geometry.lines.LineSegment import com.mechanica.engine.geometry.lines.LineSegmentImpl -import com.mechanica.engine.input.Keyboard -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.models.PolygonModel import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.vec diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/PolygonRenderer.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/PolygonRenderer.kt index 2d0ba1af..b48956e9 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/PolygonRenderer.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/polygon/PolygonRenderer.kt @@ -5,7 +5,7 @@ import com.mechanica.engine.color.hex import com.mechanica.engine.color.toColor import com.mechanica.engine.drawer.shader.DrawerScript import com.mechanica.engine.drawer.shader.DrawerShader -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.models.Model import org.joml.Matrix4f diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt index 5f2ce54a..fb5ae1bf 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt @@ -4,8 +4,8 @@ import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.models.Image import com.mechanica.engine.utils.loadImage -import com.mechanica.engine.input.Keyboard -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.models.PolygonModel import com.mechanica.engine.resources.Res import com.mechanica.engine.scenes.scenes.MainScene diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt index 4b2fdea4..dfb2f695 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt @@ -4,7 +4,7 @@ import com.mechanica.engine.drawer.shader.DrawerScript import com.mechanica.engine.drawer.shader.DrawerShader import com.mechanica.engine.game.Game import com.mechanica.engine.shader.qualifiers.Attribute -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.models.Model import org.joml.Matrix4f diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentShaderDemo.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentShaderDemo.kt index 04a551cf..970154b3 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentShaderDemo.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentShaderDemo.kt @@ -1,7 +1,7 @@ package com.mechanica.engine.samples.shaders import com.mechanica.engine.game.Game -import com.mechanica.engine.input.Keyboard +import com.mechanica.engine.input.keyboard.Keyboard fun main() { Game.configure { diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt index 9bb5ddb6..dc135c82 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt @@ -9,8 +9,8 @@ import com.mechanica.engine.shader.qualifiers.Attribute import com.mechanica.engine.shader.script.ShaderScript import com.mechanica.engine.shader.script.Shader import com.mechanica.engine.utils.loadImage -import com.mechanica.engine.input.Keyboard -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse import com.mechanica.engine.models.TextModel import com.mechanica.engine.resources.Res import com.mechanica.engine.unit.angle.degrees diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt index fc466374..6961ac2c 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt @@ -13,7 +13,7 @@ import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.vec import com.mechanica.engine.util.extensions.constrain import com.mechanica.engine.debug.ScreenLog -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.mouse.Mouse import org.joml.Matrix4f import kotlin.math.ceil import kotlin.math.max diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt index 9722eb55..7b24a066 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/text/TextEditor.kt @@ -2,11 +2,11 @@ package com.mechanica.engine.samples.text import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game -import com.mechanica.engine.input.Keyboard -import com.mechanica.engine.input.Mouse +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse +import com.mechanica.engine.input.TextInput import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.vector.vec -import com.mechanica.engine.util.extensions.constrain import org.joml.Matrix4f import kotlin.math.max import kotlin.math.min @@ -34,6 +34,8 @@ private class StartText : MainScene() { var cursor = 0 + private val sb = StringBuilder() + init { renderer.text = "" @@ -46,17 +48,14 @@ private class StartText : MainScene() { view.x = startPosition.x + Game.view.width/2.0 view.y = startPosition.y + 1.0 - Game.view.height/2.0 } - if (Mouse.scrollDown.hasBeenPressed) { - view.height *= 1.0 + Mouse.scrollDown.distance/10.0 - setViewPosition() - } - if (Mouse.scrollUp.hasBeenPressed) { - view.height /= 1.0 + Mouse.scrollUp.distance/10.0 + + if (Mouse.scroll.hasBeenPressed) { + view.height /= 1.0 + Mouse.scroll.distance/10.0 setViewPosition() } - if (Keyboard.textInput.hasBeenInput.isNotEmpty()) { - addLetter(cursor, Keyboard.textInput.inputText) + if (TextInput.hasTextInput) { + addLetterFromKeyboard() } if (Keyboard.backspace.hasBeenPressed) { @@ -65,11 +64,13 @@ private class StartText : MainScene() { cursor-- } } + if (Keyboard.delete.hasBeenPressed) { - removeLetter(cursor+1) + removeLetter(cursor + 1) } if (Keyboard.enter.hasBeenPressed) { - addLetter(cursor, "\n") + addLetter('\n', cursor) + cursor++ } if (Keyboard.left.hasBeenPressed) { @@ -101,33 +102,24 @@ private class StartText : MainScene() { renderer.render(transformation) } - fun addLetter(index: Int, str: String) { - val fullText = renderer.text - val safeIndex = index.constrain(0, fullText.length) - - val before = if (index <= 0) "" - else fullText.substring(0 until safeIndex) - - val after = if (index >= fullText.length) "" - else fullText.substring(safeIndex until fullText.length) + fun updateText() { + renderer.text = sb.toString() + } - renderer.text = before + str + after - cursor++ + fun addLetterFromKeyboard() { + val changed = TextInput.getCodepoints(sb, cursor) + cursor += changed + updateText() + } + fun addLetter(char: Char, index: Int) { + sb.insert(index, char) + updateText() } fun removeLetter(index: Int) { - val fullText = renderer.text - val safeIndex = index.constrain(0, fullText.length) - - val before = if (index <= 0) "" - else fullText.substring(0 until max(0,safeIndex-1)) - - val after = if (index >= fullText.length) "" - else fullText.substring(safeIndex until fullText.length) - - renderer.text = before + after + sb.delete(index-1, index) + updateText() } - } From 1da0d38164b575948eb04cbe815bf2b5c1c4b458 Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Sat, 6 Jun 2020 02:28:23 +0100 Subject: [PATCH 05/21] Started adding the audio and OpenAL logic --- .../com/mechanica/engine/audio/AudioFile.kt | 10 +++ .../com/mechanica/engine/audio/AudioObject.kt | 9 ++ .../com/mechanica/engine/audio/Listener.kt | 24 +++++ .../com/mechanica/engine/audio/Sound.kt | 7 ++ .../com/mechanica/engine/audio/SoundSource.kt | 21 +++++ .../engine/context/loader/AudioLoader.kt | 11 +++ .../engine/context/loader/BufferLoader.kt | 1 + .../engine/context/loader/GLLoader.kt | 1 + .../com/mechanica/engine/input/KeyInput.kt | 8 +- .../engine/resources/AudioResource.kt | 18 ++++ .../engine/resources/GenericResource.kt | 12 +++ .../mechanica/engine/resources/Resource.kt | 11 ++- .../mechanica/engine/audio/ALAudioObject.kt | 39 +++++++++ .../com/mechanica/engine/audio/ALListener.kt | 82 ++++++++++++++++++ .../com/mechanica/engine/audio/ALSound.kt | 31 +++++++ .../com/mechanica/engine/audio/ALSource.kt | 63 ++++++++++++++ .../com/mechanica/engine/audio/AudioUtils.kt | 23 +++++ .../mechanica/engine/audio/OggAudioFile.kt | 55 ++++++++++++ .../com/mechanica/engine/context/ALContext.kt | 35 ++++++++ .../engine/context/loader/LwjglAudioLoader.kt | 11 +++ .../context/loader/LwjglBufferLoader.kt | 2 +- .../engine/context/loader/LwjglLoader.kt | 1 + .../com/mechanica/engine/utils/GLUtils.kt | 2 +- .../kotlin/com/mechanica/engine/game/Game.kt | 3 + .../com/mechanica/engine/resources/Res.kt | 22 +++-- .../resources/SpecificGenericResource.kt | 18 ++++ .../engine/samples/audio/AudioTest.kt | 37 ++++++++ .../src/main/resources/res/audio/radar3.ogg | Bin 0 -> 11307 bytes .../src/main/resources/res/audio/switch3.ogg | Bin 0 -> 12227 bytes 29 files changed, 535 insertions(+), 22 deletions(-) create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioFile.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioObject.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/audio/Listener.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/audio/Sound.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/audio/SoundSource.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/context/loader/AudioLoader.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/resources/AudioResource.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/resources/GenericResource.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALAudioObject.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALListener.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSound.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/audio/AudioUtils.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/audio/OggAudioFile.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/context/ALContext.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglAudioLoader.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/resources/SpecificGenericResource.kt create mode 100644 samples/src/main/kotlin/com/mechanica/engine/samples/audio/AudioTest.kt create mode 100644 samples/src/main/resources/res/audio/radar3.ogg create mode 100644 samples/src/main/resources/res/audio/switch3.ogg diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioFile.kt b/application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioFile.kt new file mode 100644 index 00000000..1ab8b1b3 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioFile.kt @@ -0,0 +1,10 @@ +package com.mechanica.engine.audio + +import java.nio.ShortBuffer + +interface AudioFile { + val channels: Int + val sampleRate: Int + val buffer: ShortBuffer + val format: Int +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioObject.kt b/application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioObject.kt new file mode 100644 index 00000000..d5ba48c2 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/audio/AudioObject.kt @@ -0,0 +1,9 @@ +package com.mechanica.engine.audio + +import org.joml.Vector3f + +interface AudioObject { + var gain: Float + var position: Vector3f + var velocity: Vector3f +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/audio/Listener.kt b/application-interface/src/main/kotlin/com/mechanica/engine/audio/Listener.kt new file mode 100644 index 00000000..21dbe662 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/audio/Listener.kt @@ -0,0 +1,24 @@ +package com.mechanica.engine.audio + +import com.mechanica.engine.context.loader.GLLoader +import org.joml.Vector3f + +interface Listener : AudioObject { + var distanceModel: DistanceModel + val orientation: Orientaion + + interface Orientaion { + var at: Vector3f + var up: Vector3f + } + + companion object : Listener by GLLoader.audioLoader.listener() + + enum class DistanceModel { + INVERSE, + LINEAR, + EXPONENTIAL; + + var clamped: Boolean = false + } +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/audio/Sound.kt b/application-interface/src/main/kotlin/com/mechanica/engine/audio/Sound.kt new file mode 100644 index 00000000..f44acd54 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/audio/Sound.kt @@ -0,0 +1,7 @@ +package com.mechanica.engine.audio + +interface Sound : AudioFile { + val id: Int + val frequency: Int + fun destroy() +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/audio/SoundSource.kt b/application-interface/src/main/kotlin/com/mechanica/engine/audio/SoundSource.kt new file mode 100644 index 00000000..9ed375a3 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/audio/SoundSource.kt @@ -0,0 +1,21 @@ +package com.mechanica.engine.audio + +import com.mechanica.engine.context.loader.GLLoader +import com.mechanica.engine.resources.AudioResource + +interface SoundSource : AudioObject { + val id: Int + val sound: Sound + var pitch: Float + var rolloff: Float + var maxDistance: Float + var referenceDistance: Float + fun play() + fun pause() + fun stop() + fun destroy() + + companion object { + fun create(res: AudioResource) = GLLoader.audioLoader.source(res.sound) + } +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/AudioLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/AudioLoader.kt new file mode 100644 index 00000000..57ee71c6 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/AudioLoader.kt @@ -0,0 +1,11 @@ +package com.mechanica.engine.context.loader + +import com.mechanica.engine.audio.Listener +import com.mechanica.engine.audio.Sound +import com.mechanica.engine.audio.SoundSource + +interface AudioLoader { + fun sound(file: String): Sound + fun source(sound: Sound): SoundSource + fun listener(): Listener +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/BufferLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/BufferLoader.kt index b2deb82a..8b10824b 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/BufferLoader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/BufferLoader.kt @@ -1,5 +1,6 @@ package com.mechanica.engine.context.loader +import com.mechanica.engine.audio.AudioFile import java.nio.ByteBuffer import java.nio.FloatBuffer import java.nio.IntBuffer diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt index c9df7ac6..d7d207e8 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt @@ -13,6 +13,7 @@ interface GLLoader { val bufferLoader: BufferLoader val fontLoader: FontLoader val graphicsLoader: GraphicsLoader + val audioLoader: AudioLoader fun createAttributeLoader(qualifier: AttributeQualifier): AttributeLoader fun createUniformLoader(qualifier: Qualifier): UniformLoader diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt index 11f1e734..506b1acc 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyInput.kt @@ -12,12 +12,16 @@ object KeyInput { internal fun addPressed(key: Int) { pressedKeyCount++ - pressedKeys[key] = true + if (key >= 0) { + pressedKeys[key] = true + } } internal fun removePressed(key: Int) { if (pressedKeyCount > 0) pressedKeyCount-- - pressedKeys[key] = false + if (key >= 0) { + pressedKeys[key] = false + } } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/resources/AudioResource.kt b/application-interface/src/main/kotlin/com/mechanica/engine/resources/AudioResource.kt new file mode 100644 index 00000000..05e14bdf --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/resources/AudioResource.kt @@ -0,0 +1,18 @@ +package com.mechanica.engine.resources + +import com.mechanica.engine.audio.AudioFile +import com.mechanica.engine.audio.Sound +import com.mechanica.engine.audio.SoundSource +import com.mechanica.engine.context.loader.GLLoader +import java.nio.ShortBuffer + +class AudioResource(res: Resource, val sound: Sound) : GenericResource by res, AudioFile by sound { + + constructor(res: Resource) : this(res, GLLoader.audioLoader.sound(res.path)) + constructor(file: String) : this(Resource(file)) + + override val buffer: ShortBuffer + get() = sound.buffer + + fun createSource(): SoundSource = GLLoader.audioLoader.source(sound) +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/resources/GenericResource.kt b/application-interface/src/main/kotlin/com/mechanica/engine/resources/GenericResource.kt new file mode 100644 index 00000000..69e6cb53 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/resources/GenericResource.kt @@ -0,0 +1,12 @@ +package com.mechanica.engine.resources + +import java.io.InputStream +import java.nio.Buffer + +interface GenericResource { + val path: String + val stream: InputStream + val lines: List + val contents: String + val buffer: Buffer +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt b/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt index bf89fb6a..3c1603ae 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt @@ -6,20 +6,19 @@ import java.io.InputStream import java.io.InputStreamReader import java.net.URI import java.net.URL +import java.nio.Buffer import java.nio.ByteBuffer import kotlin.streams.toList -interface Resource { - val path: String - val stream: InputStream - val lines: List +interface Resource : GenericResource { + override val lines: List get() { val reader = BufferedReader(InputStreamReader(stream)) val lines = reader.lines().toList() reader.close() return lines } - val contents: String + override val contents: String get() { val sb = StringBuilder() lines.forEach { @@ -27,7 +26,7 @@ interface Resource { } return sb.toString() } - val buffer: ByteBuffer + override val buffer: ByteBuffer get() { val bytes = stream.readAllBytes() val buffer = GLLoader.bufferLoader.byteBuffer(bytes.size) diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALAudioObject.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALAudioObject.kt new file mode 100644 index 00000000..9f66548a --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALAudioObject.kt @@ -0,0 +1,39 @@ +package com.mechanica.engine.audio + +import com.mechanica.engine.memory.useMemoryStack +import org.joml.Vector3f +import org.lwjgl.openal.AL10 + +abstract class ALAudioObject : AudioObject { + private val _position: Vector3f by lazy { createVector3fSetterInterept(AL10.AL_POSITION, this::set3f) } + override var position: Vector3f + get() { + getVector3f(AL10.AL_POSITION, _position) + return _position + } + set(value) { + _position.set(value) + } + + private val _velocity: Vector3f by lazy { createVector3fSetterInterept(AL10.AL_VELOCITY, this::set3f) } + override var velocity: Vector3f + get() { + getVector3f(AL10.AL_VELOCITY, _velocity) + return _velocity + } + set(value) { + _velocity.set(value) + } + + protected abstract fun getVector3f(property: Int, vec: Vector3f) + + protected abstract fun set3f(property: Int, x: Float, y: Float, z: Float) + + + protected fun createVector3fSetterInterept(property: Int, setter: (Int, Float, Float, Float) -> Unit) = object : Vector3f() { + override fun set(x: Float, y: Float, z: Float): Vector3f { + setter(property, x, y, z) + return super.set(x, y, z) + } + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALListener.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALListener.kt new file mode 100644 index 00000000..fe3f25cb --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALListener.kt @@ -0,0 +1,82 @@ +package com.mechanica.engine.audio + +import com.mechanica.engine.memory.useMemoryStack +import org.joml.Vector3f +import org.lwjgl.openal.AL10.* +import java.nio.FloatBuffer + +class ALListener : ALAudioObject(), Listener { + override var gain: Float + get() = alGetListenerf(AL_GAIN) + set(value) { alListenerf(AL_GAIN, value )} + + override val orientation = ALOrientation() + + override var distanceModel: Listener.DistanceModel = Listener.DistanceModel.INVERSE.also { alDistanceModel(distanceModelEnumToALNum(it)) } + set(value) { alDistanceModel(distanceModelEnumToALNum(value)) } + + override fun getVector3f(property: Int, vec: Vector3f) { + useMemoryStack { + val floats = floats(0f, 0f, 0f) + alGetListenerfv(property, floats) + vec.set(floats[0], floats[1], floats[2]) + } + } + + override fun set3f(property: Int, x: Float, y: Float, z: Float) { + alListener3f(property, x, y, z) + } + + inner class ALOrientation : Listener.Orientaion { + private val _at: Vector3f = createVector3fSetterInterept(AL_ORIENTATION, this::setAt) + override var at: Vector3f + get() { + get() + return _at + } + set(value) { + _at.set(value) + } + + private val _up: Vector3f = createVector3fSetterInterept(AL_ORIENTATION, this::setUp) + override var up: Vector3f + get() { + get() + return _up + } + set(value) { + _up.set(value) + } + + private fun get() { + useMemoryStack { + val floats = mallocFloat(6) + alGetListenerfv(AL_ORIENTATION, floats) + getValues(floats) + } + } + + private fun setUp(property: Int = AL_ORIENTATION, x: Float, y: Float, z: Float) = set(property, _at.x, _at.y, _at.z, x, y, z) + private fun setAt(property: Int = AL_ORIENTATION, x: Float, y: Float, z: Float) = set(property, x, y, z, _up.x, _up.y, _up.z) + + private fun set(property: Int, atX: Float, atY: Float, atZ: Float, upX: Float, upY: Float, upZ: Float) { + useMemoryStack { + val floats = mallocFloat(6) + floats.put(atX).put(atY).put(atZ) + .put(upX).put(upY).put(upZ) + alListenerfv(property, floats) + } + } + + private fun getValues(buffer: FloatBuffer) { + buffer.getVector3f(at) + buffer.getVector3f(up) + buffer.position(0) + } + + private fun FloatBuffer.getVector3f(vec: Vector3f) { + vec.set(get(), get(), get()) + } + } + +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSound.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSound.kt new file mode 100644 index 00000000..957ee8c1 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSound.kt @@ -0,0 +1,31 @@ +package com.mechanica.engine.audio + +import org.lwjgl.openal.AL10.* + +class ALSound(file: AudioFile) : Sound, AudioFile by file { + override val id: Int = generateBufferId() + override val frequency: Int + get() = alGetBufferi(id, AL_FREQUENCY) + + constructor(file: String) : this(OggAudioFile(file)) + + init { + alBufferData(id, format, buffer, sampleRate) + } + + override fun destroy() { + alDeleteBuffers(id) + } + + companion object { + fun generateBufferId(): Int { + alGetError() + val id = alGenBuffers() + val error = alGetError() + if (error != AL_NO_ERROR) { + throw IllegalStateException("Error generating OpenAL buffer, ERROR_CODE: $error") + } + return id + } + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt new file mode 100644 index 00000000..2c4d4142 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt @@ -0,0 +1,63 @@ +package com.mechanica.engine.audio + +import com.mechanica.engine.memory.useMemoryStack +import org.joml.Vector3f +import org.lwjgl.openal.AL10.* +import kotlin.reflect.KProperty + +class ALSource(override val sound: Sound) : ALAudioObject(), SoundSource { + override val id: Int = alGenSources() + + override var gain: Float by ALFloatProperty(AL_GAIN) + override var pitch: Float by ALFloatProperty(AL_PITCH) + override var rolloff: Float by ALFloatProperty(AL_ROLLOFF_FACTOR) + override var maxDistance: Float by ALFloatProperty(AL_MAX_DISTANCE) + override var referenceDistance: Float by ALFloatProperty(AL_REFERENCE_DISTANCE) + + init { + alSourcei(id, AL_BUFFER, sound.id) + } + + override fun play() { + alSourcePlay(id) + } + + override fun pause() { + alSourcePause(id) + } + + override fun stop() { + alSourceStop(id) + } + + override fun destroy() { + alDeleteSources(id) + } + + + override fun getVector3f(property: Int, vec: Vector3f) { + useMemoryStack { + val floats = floats(0f, 0f, 0f) + alGetSourcefv(id, property, floats) + vec.set(floats[0], floats[1], floats[2]) + } + } + + override fun set3f(property: Int, x: Float, y: Float, z: Float) { + alSource3f(id, property, x, y, z) + } + + + private inner class ALFloatProperty(private val property: Int) { + operator fun getValue(thisRef: SoundSource, property: KProperty<*>): Float { + return getFloat(this.property) + } + + operator fun setValue(thisRef: SoundSource, property: KProperty<*>, value: Float) { + setFloat(this.property, value) + } + + private fun getFloat(property: Int) = alGetSourcef(id, property) + private fun setFloat(property: Int, float: Float) = alSourcef(id, property, float) + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/AudioUtils.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/AudioUtils.kt new file mode 100644 index 00000000..73f75388 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/AudioUtils.kt @@ -0,0 +1,23 @@ +package com.mechanica.engine.audio + +import org.lwjgl.openal.AL11.* + +fun distanceModelEnumToALNum(dm: Listener.DistanceModel): Int { + return when(dm) { + Listener.DistanceModel.INVERSE -> if (dm.clamped) AL_INVERSE_DISTANCE_CLAMPED else AL_INVERSE_DISTANCE + Listener.DistanceModel.LINEAR -> if (dm.clamped) AL_LINEAR_DISTANCE_CLAMPED else AL_LINEAR_DISTANCE + Listener.DistanceModel.EXPONENTIAL -> if (dm.clamped) AL_EXPONENT_DISTANCE_CLAMPED else AL_EXPONENT_DISTANCE + } +} + +fun distanceModelALNumToEnum(dm: Int): Listener.DistanceModel { + return when(dm) { + AL_INVERSE_DISTANCE -> Listener.DistanceModel.INVERSE.also { it.clamped = false } + AL_INVERSE_DISTANCE_CLAMPED -> Listener.DistanceModel.INVERSE.also { it.clamped = true } + AL_EXPONENT_DISTANCE -> Listener.DistanceModel.EXPONENTIAL.also { it.clamped = false } + AL_EXPONENT_DISTANCE_CLAMPED -> Listener.DistanceModel.EXPONENTIAL.also { it.clamped = true } + AL_LINEAR_DISTANCE -> Listener.DistanceModel.LINEAR.also { it.clamped = false } + AL_LINEAR_DISTANCE_CLAMPED -> Listener.DistanceModel.LINEAR.also { it.clamped = true } + else -> Listener.DistanceModel.INVERSE + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/OggAudioFile.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/OggAudioFile.kt new file mode 100644 index 00000000..eabb75b9 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/OggAudioFile.kt @@ -0,0 +1,55 @@ +package com.mechanica.engine.audio + +import com.mechanica.engine.memory.useMemoryStack +import org.lwjgl.BufferUtils +import org.lwjgl.openal.AL10 +import org.lwjgl.stb.STBVorbis +import org.lwjgl.system.libc.LibCStdlib +import java.nio.ShortBuffer + +class OggAudioFile(fileName: String) : AudioFile { + override val channels: Int + override val sampleRate: Int + override val buffer: ShortBuffer + override val format: Int + get() = when (channels) { + 1 -> AL10.AL_FORMAT_MONO16 + 2 -> AL10.AL_FORMAT_STEREO16 + else -> -1 + } + + init { + var channels = -1 + var sampleRate = -1 + var ogg: ShortBuffer? = null + useMemoryStack { + val channelBuffer = ints(0) + val sampleRateBuffer = ints(0) + ogg = STBVorbis.stb_vorbis_decode_filename(fileName.removePrefix("/"), channelBuffer, sampleRateBuffer) + channels = channelBuffer[0] + sampleRate = sampleRateBuffer[0] + } + + this.buffer = convertToGCBuffer(ogg) ?: throw IllegalStateException("Unable to load audio file: $fileName") + + this.channels = channels + this.sampleRate = sampleRate + } + + /** + * Convert from a ShortBuffer generated by the STBVorbis call to a ShortBuffer that can be garbage collected + */ + private fun convertToGCBuffer(ogg: ShortBuffer?): ShortBuffer? { + return if (ogg != null) { + val buffer = BufferUtils.createShortBuffer(ogg.remaining()) + buffer.put(ogg).position(0) + freeVorbis(ogg) + buffer + } else null + } + + private fun freeVorbis(buffer: ShortBuffer) { + buffer.position(0) + LibCStdlib.free(buffer) + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/ALContext.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/ALContext.kt new file mode 100644 index 00000000..1fbd2308 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/ALContext.kt @@ -0,0 +1,35 @@ +package com.mechanica.engine.context + +import org.lwjgl.openal.AL +import org.lwjgl.openal.ALC +import org.lwjgl.openal.ALC10.* +import java.nio.ByteBuffer +import java.nio.IntBuffer + +object ALContext { + val device by lazy { getDefaultDevice() } + val context by lazy { createContext() } + + fun initialize() { + val capabilities = ALC.createCapabilities(device) + + alcMakeContextCurrent(context) + + AL.createCapabilities(capabilities) + } + + fun destroy() { + alcDestroyContext(context) + alcCloseDevice(device) + } + + private fun getDefaultDevice(): Long { + val specifier: ByteBuffer? = null + return alcOpenDevice(specifier) + } + + private fun createContext(): Long { + val attrs: IntBuffer? = null + return alcCreateContext(device, attrs) + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglAudioLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglAudioLoader.kt new file mode 100644 index 00000000..b8584091 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglAudioLoader.kt @@ -0,0 +1,11 @@ +package com.mechanica.engine.context.loader + +import com.mechanica.engine.audio.* + +class LwjglAudioLoader : AudioLoader { + override fun sound(file: String) = ALSound(file) + + override fun source(sound: Sound) = ALSource(sound) + + override fun listener() = ALListener() +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglBufferLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglBufferLoader.kt index 90b81eee..d399e01e 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglBufferLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglBufferLoader.kt @@ -1,6 +1,6 @@ package com.mechanica.engine.context.loader -import com.mechanica.engine.context.loader.BufferLoader +import com.mechanica.engine.audio.OggAudioFile import org.lwjgl.BufferUtils import org.lwjgl.system.MemoryUtil import java.nio.ByteBuffer diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt index db45f39f..57c240b8 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt @@ -13,6 +13,7 @@ class LwjglLoader : GLLoader { override val bufferLoader = LwjglBufferLoader() override val fontLoader = LwjglFontLoader() override val graphicsLoader = LwjglGraphicsLoader() + override val audioLoader = LwjglAudioLoader() override fun createAttributeLoader(qualifier: AttributeQualifier) = LwjglAttributeLoader(qualifier) diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/utils/GLUtils.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/utils/GLUtils.kt index b19dcc39..052edcce 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/utils/GLUtils.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/utils/GLUtils.kt @@ -59,7 +59,7 @@ class ImageData(resource: Resource) { val heightBuffer = BufferUtils.createIntBuffer(1) val componentsBuffer = BufferUtils.createIntBuffer(1) - data = STBImage.stbi_load_from_memory(resource.buffer, widthBuffer, heightBuffer, componentsBuffer, 4) + data = STBImage.stbi_load_from_memory(resource.buffer as ByteBuffer, widthBuffer, heightBuffer, componentsBuffer, 4) width = widthBuffer.get() height = heightBuffer.get() } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt index 69df6e4e..4974bdf6 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt @@ -1,6 +1,7 @@ package com.mechanica.engine.game import com.mechanica.engine.config.BackendDebugConfiguration +import com.mechanica.engine.context.ALContext import com.mechanica.engine.context.GLFWContext import com.mechanica.engine.display.Monitor import com.mechanica.engine.display.Window @@ -65,6 +66,7 @@ object Game { GLContext.initialize(window) val callbacks = GLInitializer.initialize(LwjglLoader()) GLContext.setCallbacks(window, callbacks) + ALContext.initialize() window.addRefreshCallback { refreshView(it) } Timer @@ -122,6 +124,7 @@ object Game { hasFinished = true GLContext.free() GLFWContext.terminate() + ALContext.destroy() } fun setMainScene(setter: () -> MainScene?) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt b/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt index 03a06e2c..99981d73 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/resources/Res.kt @@ -1,18 +1,12 @@ package com.mechanica.engine.resources object Res { - class SpecificResource(private val prefix: String, private val extension: String) { - operator fun get(file: String): Resource { - val fileFixed = fixExtension(file) - return Resource("$prefix$fileFixed") - } + class SpecificResource(prefix: String, extension: String): SpecificGenericResource(prefix, extension) { + override fun returnResource(fixedFile: String) = Resource(fixedFile) + } - private fun fixExtension(file: String): String { - val lastSlash = file.lastIndexOf("/") - val dotIndex = file.lastIndexOf(".") - val hasExtension = (dotIndex > lastSlash) || (lastSlash == -1 && dotIndex != -1) - return if (hasExtension) file else "$file.$extension" - } + class SpecificAudioResource(prefix: String, extension: String) : SpecificGenericResource(prefix, extension) { + override fun returnResource(fixedFile: String) = AudioResource(fixedFile) } class SpecificResourceDirectory(private val prefix: String) { @@ -24,7 +18,7 @@ object Res { val svg = SpecificResource("res/svg/", "svg") val image = SpecificResource("res/images/", "png") val font = SpecificResource("res/fonts/", "ttf") - val audio = SpecificResource("res/audio/", "ogg") + val audio = SpecificAudioResource("res/audio/", "ogg") val animations = SpecificResourceDirectory("/res/animations/") fun external(path: String, createIfAbsent: Boolean = false): ExternalResource { @@ -33,6 +27,10 @@ object Res { } operator fun get(file: String): Resource { + return invoke(file) + } + + operator fun invoke(file: String): Resource { return Resource("res/$file") } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/resources/SpecificGenericResource.kt b/mechanica/src/main/kotlin/com/mechanica/engine/resources/SpecificGenericResource.kt new file mode 100644 index 00000000..742e5274 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/resources/SpecificGenericResource.kt @@ -0,0 +1,18 @@ +package com.mechanica.engine.resources + +abstract class SpecificGenericResource(private val prefix: String, private val extension: String) { + operator fun get(file: String): R = invoke(file) + operator fun invoke(file: String): R { + val fileFixed = fixExtension(file) + return returnResource("$prefix$fileFixed") + } + + protected abstract fun returnResource(fixedFile: String): R + + private fun fixExtension(file: String): String { + val lastSlash = file.lastIndexOf("/") + val dotIndex = file.lastIndexOf(".") + val hasExtension = (dotIndex > lastSlash) || (lastSlash == -1 && dotIndex != -1) + return if (hasExtension) file else "$file.$extension" + } +} \ No newline at end of file diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/audio/AudioTest.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/audio/AudioTest.kt new file mode 100644 index 00000000..bcf12901 --- /dev/null +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/audio/AudioTest.kt @@ -0,0 +1,37 @@ +package com.mechanica.engine.samples.audio + +import com.mechanica.engine.audio.Listener +import com.mechanica.engine.audio.SoundSource +import com.mechanica.engine.game.Game +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse +import com.mechanica.engine.resources.Res +import org.joml.Vector3f + +fun main() { + Game.configure { + setViewport(height = 10.0) + } + + val source = Res.audio("radar3").createSource() + + Game.run { + if (Keyboard.space.hasBeenPressed) { + source.play() + } + if (Keyboard.esc.hasBeenPressed) { + source.stop() + } + if (Keyboard.P.hasBeenPressed) { + source.pause() + } + if (Keyboard.up.hasBeenPressed) { + source.pitch += 0.1f + } + if (Keyboard.down.hasBeenPressed) { + source.pitch -= 0.1f + } + + Listener.position.set(Mouse.world.x.toFloat()/5f, 0f, Mouse.world.y.toFloat()/2f) + } +} \ No newline at end of file diff --git a/samples/src/main/resources/res/audio/radar3.ogg b/samples/src/main/resources/res/audio/radar3.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6d2766c3d320de589624b76eeb858112a72f4646 GIT binary patch literal 11307 zcmbt)c|26__xK%KmXeSqD*I0Mouq_eEVCGcA)0hr zvW65XQorl{{(QgR*Y~gA@AdlK*K_Zj=RW5-_dMsE=bUrT9a9$<1V9D+=LmE87dX#x zxg2r{;_v0-;O2XB3nF0g??^lL-(M2M^kn9LA15;*V9$`q({)<2##60 zdArJ(`nd3Ux;a?T!PhM75MOIl}{)7^Q_FrTU-Rl+*fF1xs_!zmnLxz)30KftO zx{q#ln^mOIWQIku=y#b#Cw(__U+23EweQW^WA|XurJuh))N=6gKA)n~c>tgRbhv1l z9Twymbm@z^-ZGfyiPP)S+p95T_*_zF&^_RbV3Aq<=(F8&aEbm5h86&&AWP350g^en zFp8J6!(+vXSq^=2J-oi}<=*jypH^QhOErK_zPgES}H07YW4 zOJWIvh8g8{x40(^_>KuLj~yc+~Msp=LN6esu=!}@H+t1 zX}cM_ellti*xVS3=f9KysvSJqcX@|)c}>58q5uIQ(@{kj<^QpP zf=&O&Ucz`O%qc=FVT>s*EWRwM&g>wx$(6>Q8l4vwo4hLINSDk6<;;!+g{B9vfEK_N z&UO7g|7u>E34A#(b=1&}FCs%7nqNNZ9wS$ZbT?{HW>JUoH#~;NG}O%?bE9 zBo>3lP5;17HwGE9qfp3ykO`>CaXf`Um_wkZm9Xdx*kl84G98ai$1P4GQU7!)1l(*o zcFqEad7(GCPe5fv&Zpt#Gx691+?++!2mx=Gj+;}#VSeJQIsDO+LH4YAsC^{LAGBL6 zg@DHru(K0Lg#QU;1ObNukpr-pNhMS|4v~SM^}}K;aMP1m%mjV}WFrl?hz3WobIJ)I zpZ0#(6UqlD%tR1oGSYqmZwGR90Gr;Aq@)w<4}u)C2v}9^$^9TyW29p`?$czXo#kyT zXYddq2-|>jw8YJF`Om9FO&`QNj^iD(@N;V$>h?b)7heW_dNR{EJdU>yB+NeHxw@zv zTuLOh-fklA=GK1Mti8?jX6vYL_Z4}ks@C&2$&;zAdb98Ok@ja_teqte!)0O35d|`Y zHF9pp-L9OM%uMZoocM^Zq_XpGcaitt8b-&Nf!RCRfdt zxc8CWNVU}@(sm!Y`lwcFw)P2$?9oJ$$|Xt6)=F(QNzHbWo5=WFlH?{?ih>*W51M_k zPTHJlm3ZB{-B(-vy0NOi34he;l2hwRB6@<;`fI<;G)nik%H|L~a>?%f#Erhj&88-q z{#tz82?rZZjg>jIQvJ1C{k5L6Al+Jz{>GiATF?GQ=`XdC?^{9q?Y>6or)2lpTCXEd z>^KSxe$>oh2op&Q1$QwOU=9Ewa*yTcip~4fR{0d6YOB1U8=&VeOHn(;ACV%z%9oY` zU*U~_!x{NO$b%n*+^fM!RHwj@z@sS4aSF5+VIYYSxaG|Y%6u{%v}n*~K#kyMxi~<} z0r$`>M*{4hJJypUpty0N!NeYzd(M@3xYmO_S zG{tkaY3Hc>c^79?XDzZ59lZF2h6-qB;{`$(JuNRTmbpqjJMpDDbO3NG34T+&{4pFV zUe1Mp%87t?+93d6dxX51>$_Nh4|IgygKDdJzW@93zvJQmM8b`a|074P}qCmxk{~h?R?tk$9 zKfAO*mZmD8BRr-020DsaboDOgmxP3T0015F6|Ph>T?zhl7G1G)M;3Ka;j%F+2Jw1U ziM1j^f-Y3L!2&KJjAI2ej|f(%TY1_Tw3jb}RacxpA`N6VBI6o^c$yg;D%>y(7Z(m^ z(RC9pvw(Yury=2V9}S zpJZiJ-C$uQUhbCxKCNNG%DuWg&5EJCeiZ5fKJsJ~3HOk0$T)#AtR&0R(m<3GyvnjM zIAeL51(dO}92}^uXVEnhPPedf0|~$-ia|C&lnE|lQ-xBA z$tFR7Q}hgQK*v6`*sqvL_YohK2BYb{*cC5&TebK))4$0FJ@5bkcu(uxxZ!(-`7Fyh zRyOwY93Y|pKB)eadfxi?&PnQdlEJ=l7yc-B%iRKLVQFk;W@3)8v$U`|@ z%mx*XAGjSh6%9JqpjYp#d^jx&b;+yEDvd<#H*b~q=b{Sl$qs4=W0!0$DnxD14}Kny zMfbc}vsDUv^AUP(d-KuD7Vfb4!spt+vv71~8gO0S2sOLXXV_e@kt4ZUoR$1$?E9rW zPOc$|ISbRZE6Hilzo7{cR!#ZD&NrWi_1sy3&^L`{uc)Xs=$E>bquE+b*wYPU z3`;Hyy9|6;d@3yt0rUzg#dB@U12ccvZLSzTWbU6H^Ob&_&*9~=euKGn_N-oV!6zP(uZ(Acse+TQJ_4T6x*SUhvN*xSeF(?- z*%?!kI4{d8$)^)N=LtgTw}1Q!zdvE_@+~LhVuV1fk=f!6Hxl>nnwnd8ubQ~)&9oAGF0S-hH~|4g2S5=dK)B zrQ-Wd_S+vT2FpPBSL94o04 zt}7`e7e@}uMikZ$Io@F@V`{$s>CRd!qyVIK2WVU%l$TM3z!e(~rBAWXRDR~#nVE3w zbGjzKYy5Mogh#=S*3;y`jq7dF()W^RvgwC|CWO%=w9=rAE# zQvBKQ04kvQ&khZsfYJ&+rIVH{=iJIm*qs?VcR%%hyemx%Em0hO6nC+49T5TcH%-k@UXajLSC z7Gx12-x}AuY4M&o%)s=Em+XFRh)3NusWS_I)$jv@vhwJV1}%cMY}wVTwT&!kJ1V-e zPnQC&))4Ubp2%Pa%p^_6uX0=7crkAC&H3~Gnmqr<<>%hY&sA2nM7%5q(RN+gy zvK%EeB`kU#-sL)}*`0Xx)2oRKcn?lAJUS4{^J^`XAd|^MW#D?Z)%dgN9b|+6jZ8Wd zmZMj0|89pb{RJ|(;P3{$u79g!b#d{9p>Wxwy&a_&{m42p^#k+I zi`SNnpOl$a-ulFSe&*t zi0N(abAiVaH95N<2xHbiP1K%Bxlxw5O*`x{H<~TswL*xPf&%+~xGU$v)51#bjtpZ? zfC!qwS)d(b2L<0k`^IMux}{cL3>;Z6!4S%W)2_SEn3*;#h6oZSlBEW5N$894y zpNFJ1zx0G}C+||NNArYQ;`$-s{hYE>Nil&vBe4+Jp)u=U*sl`;gE7g8Y!_nSmh2!E zaH>EfU?cuPswGm(AXt+6fx8gzbpm&A|I>vZ4!y;(AF**Cb85w$cAc8szP>II#_EXB zN_?HzR74S0qO>>3e;R zcQL2EtM)4F5;^y-)41}MWW28fug3b?UY2XlFrhyL$-+pE&WuR~^}b)hin%M0)r+dY zj(%|)%zZf*pE&-bWMy+X;en*fCGl%L-o}bazE)S(^w;smgR6vBXQyvIsOWEA50x2H zcj=cpW>2!=j|>o3%z%)>Uk_rVBI-K~Q7L=WmMmALNzy}`hf)|zL1L2TIb+5>aZ?!0C3nNYWU%O+O172hP+HOJG%clCP~|F> zz{1mcy(S@u8HqHm)LY~GJ9WR|@beeTT&aN%diSPhYrhK3-$U&COS-;Gp2u){3VUf<{PgD;OUUf+R{i0~C)Q;U61cO5sFIZL9^VJ5uAvOaKZ(~F z?EjtHIhYm@T&+4d^0B`(Dqkmx{FbMu@MPx-qk6^ZUri<%+k3>26>eM6(qHJNjII&K ztDX^(eR*|xif>O#$CVl9qk)w8LLd?HEIhTT3<$CJA%9B?H6PE&OU0AR)=ad%kn4j@ zRxiu7Y)_b&SDY^#A1#l!yp!E!TmSq*`oub<>szgprc!~SM=oDQ&?57B=EbH^wec#(6w0;Jh$W4aopQXb)s63{3`&sf3i;0AXv2A*nBi zcH#pHJ=VE%59>W|PV7ZZE^7zYco8~T=iWT&HhSwhHwMe#yySHOtw1pCI$9uGu-{Z? zI7^N>8^!~KO#Y=?l};k54|s1K)&+f={WkZ?*X2{^%136ofTzF8(^U%-tZzO}gR2(V zhb54im%b7wf?gQws9fw_Z*;wU_PZOG$(hFR$x*gAnoaMCd4Y?zkX?mzjp-DT3#)~< zd(y~%zIYmZ>{(Em`>?f!{t9n2sT8iwIP(;#tipD^JyRFH!kzfLuwx3PQ5Mi;Y zcIOWC+a^B0#D3Ty^s;u74mG?!u)XLw6I-t&n}&WXs33VE4P^n1jCiip(K( z+8Oi~qFLw1pZ+e$E&tPIy?Kn6Zf*5b?`wfZA5a=?ghzJ-z8sB=ip**cYROR2)}_-R zNOlGO{@-aZzm(Wty;P~MC*HAstUcwXf81}@DMgzDD##Rai%IMnwOA#I3K$FOwz)3W z%Ml+@d40->Tgf&QodYhq^plM0uvw(*@`JTGT{Xw}%FxFfo6hEwHQtBje`#28=9dCQ zv_fZT&Hy>!UO#aklb50DfLJzZ|42Sd(QSOp;L%dvxs4{c)A&A1*WwNKi!4UF`8T>? zT|y-)}G1Gs)PL$kyp}6--})F z`@8n8tkz5?i&9Wl*AR?VY}4HLK$V@KT)X;y zwyR84x<8eK6B{S?<*VH{7V|~+1y}{R%1o%Ns)r13D^$psNp*1jJeQ|8=2ykQ?*AoT{S;am+Bt`m(@kAG{P6;#E@jTT!cAa#+4M_AT@80G?+=-Z zM7`7*Aq{D^#Zv#U-F;%5$^BG-9FtkD5>E|gRl>t1?6O9Pd>03UEnCW7!gZ#NmfI5Lp=_WBOl0kF7eeK1KXGlqt;kz#P}Z#P}_VqsKD->Zv5D3n>{K*$e!}AuCTsO5Qa43Z`~!D>jQ6@J1J? zvgAXOfr7ZpW1>v$B6;aosDWD25Z4xuffnVt>&C9&#=v&u`=6wZE@b3fWLKlOW_ zufbJb`*AzcyXT2e>-EWVjJ}3(shD?&g^XT0L)DEGWTpsfM%?9vQ-HPiM4LNc1mz&x zzBuEMa`#2F&E_(07B^zte6f3cOf^tK-C0F+Md+$pPT8L-d%sI5u|l_A@>kxLE~@vR zx}KxO{5Pk3R@^k}%mF=sIQ5^RMCbn$C8$BLMy10_&d|aV3$--2GDo3pP$(l4GbG9y z1IO4{qb!WAEYWBS49XIXG{)EBpj47&a2J`|teMre;c^w3XbuII! z-r{8rQlwdnAx7vDPsfl+)PDSv!o4((JGQ> zX#rJ9G~9AnAj^$(J3a8eY2WHQ%CFkX>ks^@KbD9otBth2|X`Une}2I0M^)e-7hm~IR0HYzBupESq(zj#dwuQHwTB%{@gXK#&w4h z2A(^LzR7wQa?LL^4*|6xw^!kicFaTQSGufnj(jf^`s&=dhCdG42mZcy!{5wh__*I3 zaGm(sWgD~dA{N_JZvK5nSE4-bSXak!FSfJchwnXfe+i0IZhD>h0<@DLEZ3gW*%EH} zfK9yslqmvC@+Z@TfNSQcf#h(K|5j#DmZwi0bhTp@573`m2AgkA`}^V^hr<)WdF{65S1yrb)? z4ZXx0ALZ^_^Q>}A;q^k=)2a`iskxgvl_O^(bQ9O4AN9%5L9JXD=4H*7qJy~Zw4qsR(r=YZD* zwVRxZY#R%AL|?Ifc8@fmrS(eoDtx)b?&n=2Hd~*Z$2erA5b5zHH=k;be@#3gRRm;J z;OhIVq7KYM>ZdD3NR&$yuJZ4d0qOcUFZG{fxhCD`B>$^H$KwgYl|%!@3^cFZQ~2I4Xy1ht zLd?p@v??)XRlgd(euRoMRqkjSGMBg5qJd4Io+M?|PoMH&?b6Md2-l1acJhfkp8~oj zJXmac`p^h4}UM?mc1a z*l)kM#y^cvWR1PG0f11y2_Z=d z;*dWoHB6$d)Oi|?WWU`keF=MSh#2e&;Si+Z%@>@N^(zpL=Fv)-eWd}H5nmz{)AHag zC>$dEvOz{`k?KS+ExQi@dFe9ZKoJ6k!2t%1-`Q^m=D$LX|6S~vDS6c?^S*I>Ijgn{{X7WNs(>_l5w?SJmsxzhebmN&mPzVDQkF0VqnZs{1Wpgp`QR zywmp6b^h>!_Kf75)_S1h-?Y((tJln58hqi-v*7#K+Q^lO4a;Jzdn?`zy=AQ7t9vuB zX)=%=IN3&yCnEd{0b+gjF%Kamc$>(9^i$8oV2e2!OjW`Pzy1R0Y3%K((#LHtRh}4H zBsOoBE%j!+ePwH3Qa`-c`;ZS>PoE( zoXsJttgFa#>Mzbe<}z>UtX)s+4(fUwtp*`RpS5pWIep@rY^i`E#dMDr{iq1ygdzM& zm;bMomt%fx&lssa(Cqqxi+j{dEd?$6rnGV9g;wnmFKk>L+S_ES9UuN#MV6vzir zdN3ijx~z+H=?5h-|9a(2VOoG)n)v5Foo*iL_QW2sKz)b(%d+qX1@ljh8~6B`jvRHp zG%u+b6yEriRHq^rPJc~V-CxMhtYOM#Y3l709{SWCB`oFFI-~&1?jLekJO^Lpr3Hbk zY*L*qH_P~Mi|*s?gRKy-7X3?s-u%ve{S{4 zBa1lLy*MVz^Lo+HYU@4P3|b>as=>LLLl?+pnl(If58jDL1jrO_7^`k|Wp|ghVcv;`-uiQ6fI21S zETSz1$n~D+VTCxcfZqiaQc4W4+Pz90A3|dSHa%>bZzQABmtK#uLBC7N6;C<1icm_% zA;ISiEUp+`t!`tHvHbYNTAshdwh!`%z4`uS^ ziKKZdSdM95n!EXgDkt$D13v3LkqN#=>7lAjxn;jAo>O;<9sc2)rw5VF9msxn`sLaU zgd}w?ywHr|*v_i=G)MtTALsT<@rIe!z=Je_T@H)Y99Ztnylv1 z>vgQpi(k1J#~tTAi51n+-n#f%Eh@gSXoRO-r1_6EM>{nXF<#@w=}0Ve^S=|k73zBK zlq7=7>oeW*(xcC10s2-$E@#oJx7j&ch)VBMig;t857&(@?mL?krC{b4qL1RU8e;}C z%{W{yT?kIJGXVYR{tIIup+kuV?D3%uz;EM25;J112N2wQpW;;-IWy@egP8L03~JRtY5h ztaRT9RvQ!XfPa6NMUNU!kk@oVbyY6kHu`kfIy){A(Y{{!+&A-UrgnSS+n_FL~u5{Ji`=wHT;Mv}U;c-dYme-la<^{q0`?{`T>ULD@ z`BGMplho?!KM}hHSjb9W-WcD5La^GmC##5m(^7H2F1yjcNbmy)(OFdC;r0a-ZhysK@0fB#twP*V=N-^lH4t3Z`TEFdJ`EZz zuoEH5_?(YUSZiAKX=EP*^F2_sL$tR}fu80%K&2qPG4mojV@C5Y(qmsLe6shl(wW`r zNAZ^mrHAkE7mu(tw;SlZJOIRxZ_^THoU~ylcn~pWBoaAx; z^Gs50suuG>-e5yCL6z3y07-gBO!BdRm=bb_Y?uWQRi zJ?RT9ej8>~H>9w$<7WNjJ^R2o(BnmYFVXJnsgR3#`_WV(p{-6n_=Bf%Hg>T}Q?gXp zB0CJH#5KKpza+BtP?#`$(4zPuB}ByM?KFS%>-cLN6pd=x)sMygt*nRu!dRZ1e!<5; z%U)l`w~wl0#~?#A&fy1gi(r-0nvR+Qte*c0*vT7BX_y`<`}V-4b9dY|DElb**r0fR zL6Jv^NsY>(zwNyG$Oyms9nW_RhTImquRms=v2D0ALglA7xs{htKf-xPZT0ZQ$IIS; zs>{QEA0hCi9N(hfl!M8s^eT0F%k24Wm-d0g934LDMu1V(&@Yiv_~Zk}w?4{@yGqPd zv4e5nerZF8>%=R^M%7{`-|K!%zR8U@*0c>hK;6)RJoxeA-A~mO%5u}M6qSKrrRv|O z{V(}AmRjbO-5bS+hQ!J6$_`%- zQOmm~+o53U z`%#$+zz=CBtsQ>HPvU|1OxxGz9g{|if=(-@oQm;d6}y#eNx8K(kU)FJIWpjRQ3%!D zW0`XXA*0Kr;~isx24U$T5v_oU%&mqe$^_!+E10YAY`pM~V%(F;^D2WE!h~Hg&&zHp zDVu#(TLg*bLqbj$20^Iw_pQdiyQa1p{%w3^ z=Z4v4k6&%>Aq2Q>49*P|pLuoL=ZT<=8_SGwPxYH9bFIhoUg!lkh{Ymj2-Waq?cPtr z)y^Dm5{E-#I?Ke3{}=2TgVZKeO1eWjHr?GNsept?gLF4YcQ=S4-MOU&1f;v`F7W^U z^Ph8{=iKMHvu6+UzVptkd3$ED<;~2L07T$lg~I4>2W8|tF9JD&i=Bg^h2tFxQMCA9 zBs+hJ>k;JdX8ya}%|rkpEM$UeT+!SAR_u}fF2n|o3(5094D6JhRUuj|ub7!%v9Yk} zKdt9U;~hhN}0^hu%E|;o;vk7=%Ss5dc&W!z=^o@E(p( z0RUV8(5Hf8N1IAQb7C^-ToYm>?s^@sf)ZkaI!KH{nLGb!AZ*5j0Pq0trb7$+vMOmg z$Yn-C8|IL~Wg*9z2SEu|{KbX#{g}qcHn%$0%61S+jf$o55M&<^l%Vh{s%D)=4dp;oGUWAcJjd6uh@0JgQ<|a6ROZ{aF0(gUL0&gM-Br6E|D#!*#sN@eA z74g|eSjCi-WmUl9tfS^)0(WtSyLf0M_-WO9Xw>^@{q)m0^)n#x|5rWpSUA0__pCz@ zfQ;u{am&yw%EwttTv6xZ@tsH@Xi=2Vs4tSark14^$+hN1wKjc~jD3}u_avZl zAKdK*$goJt{lBWIMx4q2y9%0j(*uH_EW7Ny5jKT5b$I{*kWlXcjW^?=fVF^xH}t2y$)9cAI5DDEWkzn72q z_5w0Ok!l)e>5a-7VQ?=NZ*W{u0?Q0&~&m@1DX!lJ2y_*89=+k{ZI1| z1odX{lcY1%2jUTiY)C+nOA$@(6pkI8!Q2haCjT5JE>-YJ0Y*xjBE-g2@b8F(W%@#h+_d(;Q2=B&>33CN&vy zVqj9xoWC9gDivZJA%skN?S4!zJk&@28^aeGulkDiYFM;+QHQ^cbN>j(>z5-XpI zvWl9EjYholOr4+3Pv-@=#{&Gb9}&Y zpevHBCz49Kl1g@zao~VW{*Y}1UrdorR*6t&gwT16$VBP2&KSGNn2O1Si^){2iB_G5 zM(w{G=C9i9D8#s&&D763@W zPzdcv@>Pful9SpJv*?7NlpwD_eMzE!hO8$DcPlE;8)K6G7VH^k_QyRmG|D3o?ho0| zaSRoD*;x-0ru&8phP?-XA^;#uQc6-u9N%BGnH%P6U2 z7XAxNqD{I7)94E6!7y34-e^2iYtZ*EFn6QTda9OU26THWc_rB-(7kvP5)5G?pN2<}ZHEw2!qYmsDuC#K|QDfJ(qieG`- z-?@pon+_%#{!H%w2BUN|{27q|U|41>E+}gYPYiC7REhzgkWs1)%3{F(7f7fw5m%7Q zq@<#;+E|IHL8PKmn^EvcO|ax-0($7Jy(42m*jN0t(;_ z;P(oGB1G_}1Au@f^*B&$T@{S7__AR}8cC+XRFY%7Lu{EEX=X{oY>LV%=1HLB?lK>M zP6qz59*$8b%eXTFK?%US-JVezBtH}dT`785J#H;1iw;Z+h4kQxL-!6grmPZ|W06K1 zoMoO=SXemB_7@GB6S(jlX<=dG3s8TcX@NK$;-ppAJIam5QVd>s0$C*$9k}z|dE&nn zFfNmEf-#Roh?Z1^$lwbaF}e3Y2Ib8h$lQ^jh%OAJW|I;^qmDrrwkXJsk(4N@nPh<$ zfB}m-r#eovFC%+W(<;#yY)Sy290>*dVr1U)MGgQyp|DV{H8OT_XlIIJ*yAq*MuS{$ zVqv{pYmn?1`i>MwY?u`3T^y#%0qIg4V_^3QU8*1k#F6DFOMze*jGh{tMhgJ>Z3qCQ zO+0b{P22|^O9KtaMg&O6x++1z$&9cuNsh895{Q9!~&kSN(Q*rjKOo4CI%pA{uTgu$_<=-qq8d7Ky@Jku3)wombyEY;Zo#~81*v; zM8NJ2Y0AOOon%JX;MT!28wv%uz*@>D+`IJ#g-}x+!@#}z4s&%SIN}Pgr5N>dp=LUU zGOwn9hZ4j)1Aij+nwA8O{*FgnFl3OlCpd<3A*cGHQ>;Opu!ZRhlu@}*G97WHBn#eS zb3-Y>{6*gZf@qmBF`5i7{9#gbIoac|NM>I!v4PwMGw1q7z@!RlR706FeU(9EtzqbA zkOZ_HLL+NN>exFg0{3bKtsych2Bn}aU64a!#n?%*>~6`_scr=Fl&)n7&DF*LUb(&n zyrs(R{bRGMzK{I6mqP=1lD(5d0%$LHh9sFoa%Wr+-@Ef*j11gP z%y|m1jRZ&RyX<#Imoxs)3=jm>0gCGHfNu=!pB_jD9*F;$5rYS^;s@fWeetAFsk38X z=yx-qIleGq@ZbzuO-_u06f`piWFE91VY+*e`Yzv7-{pL2a1{IS zUc@&9EHZ+A{>2Xf7-;=>`vJ+=Lly5I5lr@X-TSF`&D~D^F7$8I{~w6ZHb;!@&5!@z z{0qE3yz&QC<}k>W1NsnXUojF;(7xoPpnP}wNcD|zyH^wxdQ2ihM*#|Fv%+7F8#SkT z+zN-nH%8O4;FO#tpDrf~w#!$e0JDU4&bv!fSB+{~wXDrc(?dI_-FCU^qF^#iM1Vft zBH&%G-@buLD`xZ&6agY&2s)1lrm)0c9{+u_`L8<;GXmfZ#svUDwVa`<+VV?%L3Hz< zPi?eLKTM6gbCmmkiw-|$A2+1_nMpYN>!JQ#P5@W{;D=%{d#5ggA&GC4B_ozls8CRY zNhjS3)#@bpq-b@e?ig6VMliLL3{)JNiznO}lMPxv)M-?6kgrZz3IohC5TH%axBmg+ zLj`>>3}NHqWdq*d5k$XRrK&D<~~^`JIrg>BXdIx@Il|qvM64e@fDCt1jOkX}H7>&6I3)ZL);#M5Mp7 zg&ss7UUR#DDR>o6?hcuE>^tFQ+ghm2^!VH*EDKx^U0rtd{CJ3|U;X+j-Ht+sq~l>F z@Zr@i8F4~IiJxhn$0py-DgD|ZVF+AZad!wa==0d;f|iOVr66foYDY7j6ReVd|RAdOA#rnIJLc6@T*U1;V+_o8N&)Y!shuy zh3v=V%3qPb&iiDQSazxtakdZg?gPoUVWbs9yi>TeD4&h%A_Db?BeM&;q(%3JikXwE zo5o?8H%UGSTj3>{`IF)|&j$JqN{aDY9;f!+;y?cQ(&VW$q~w*Zvfvn4F$y>soP zShJM*bXhZ6ul=J5HTv;`c2eOFanIzPPv+DZlQbQ>2W>9-hWt%zk83?T#F(rgw~hO5 z`zMj?b~E{;Q@=w`lNQDn_Z|m^wl{tJ$j*~*pCE%|MFHP^+?Zs2t?&(VPOqrJ+Q&?w zgI-sj+%lV9lDK1XW?VtRF1yD*Zj32azn{}S#5|$#exmi zo3s+OBz*ktf;stdXDkzw8cl0gjr;%uPW$h-(Azk)kSF`hL`+0q3cK!t_^R!OmWf!d`wAmYrToVk!#$NQ$N3XQ-&riLFn5^nik>A^J@8~4!_gCE@)zLO zHC27h*Y?Nh!>6z?3qV1KE&HML)LT*@<u_CO`6*xTE9UFwX{EilOX%H1RCs1u zd12%|K_7s5gzATR=4F;)r}gXlLtI4tJ-USrUpt3JUNat4$u~trLSJ+5+$wp%;0hBsb!5z z#A)kQy7^ki?_D$fJv#nQ?)06_t*2f4oy$_&JuvqoT`9|0bwqEgEsIAcMiou`kMRAf zjkIu1`(+OHDVQPKr?w%1fx%PGDiaDSNau;K@uTTGLgOyBw~w{Z{RF6~R~#C&VF_WX zu17nBUQ*b7)PBNt7HV3%Eu1dzZaAGc4jL~t8M1V>w8F2O)2fhACR=2aSsXq0_I?L$ zZ1nc3CTceIb>hf0!<&Z68)F2@x$}x5uSVRijz^@UBVHLlJ^6zj%$)cuk`P5G?guqx zbd)p`ocrr8eEoy-W}n&ffTuh|s21CLsry45(%2QVvdaq_*hCtwee*|}eaB4YyL`%w z9}{WgJRIj4j9vI1td?!{d`=Xe?y#eXyS5|SO+x8FLOUU}aVBLw^&wF^p3Z%wx`m?r zBT(BW_*lQX^PAyGe&Le$`63dLYpBk6pBwjM&0~_TjW6U+6_cO+`8Y>j&sUf0o`FH`p0F5X4D}jHt|9w@buh^QTv7C`6THZRQv1#a!{3usHqliM z>t2&Chp=n~C#>|Qmh6XI7j2r4S$X;YY&s!Ntn3z9t#UP{*=;%GJ(*EqH@P%6ue(hg z@Jx1HJmpJo&LQ#78`#+o%PlKfsp{-8a9HY&@zvoOknX5ZQ+3|B9-3SNR-uPQ)yHExQ&Y7p9y`Pg&4qZNsunp-iZRfSI>oBykJdH8t|VP4zso6b$;OX@We z_omE(Qy-kt?Rm6O*WDu8R5rd5l{v02_@56q*sUjS&TQq{84(7)hohvL6<2BD0NO~| z+rbzgA$Z@zx=Bi_y=U7)M+tn^PE8hEe=;22cFn8HatvemF}RJ5NVi|Pa%SIXFK}Q_ zRISOkcHvigp7Zi+!trSxor~2>7WFuU>UM(5-tzP>rsEecRt~L!dgKr5 zE|!AZ%_IE;JDO#_1kW)imRfGT2>Ys6Va{0Lu__X$un~KHSt@q zs(fADr?5}PiZ8`9&Qwk=POdeBwt1N7HB59jrVVUztB+&&kYI0_V$HZuzQKzG9$kX6jux-EMM91$Wd6`6bi*ou(Q|vJlTcjhz8v}etmLEV4eT423(S=w@$OpBq1|I!g1ew}*EtW;y97y1=>MRZRUu^!oJzZYF16`A`p z-fYJF4$OiqHXphg2cVCydR_Gh5Ez{+eEppA^@6eS(FKnv|0SNnWtkE4L#Q!b=3W&M z)C|hS`k2PHmycn;AdX zmtzBeK%ap6>o40=cC)AEU)`|E8eVF}hj#zEsMH%4qQyf_f-AuVf!ysjY8cW@YpNSE zqP1m+OoJ%*eE4-?U0n0c(Sq0cQrY$UltR|59tndMS-p}Mto>EqUP#Bh&hj2tWJy3^ zJms_~ha{Qw8wdbgrq5J<@GQ`9fNu=Uekkvq3bFV^d3av+Zb8X3@;aSJvv^1IC;@Y} zbSGuPD^c5BUQTQ0AauiZ>b#U_+&cAyvP}6v^!D_4AbTK~B}m*g+1|yHk;UTV(QV3G zratSb4<2^TC@m-Yay~Qd`^7aMxLR%c$cR2XlxD1^chV$E?wfBNM7p6?{O6+0lTIIT}4RnAJc{~X0K9F zIl-^;JN$Bm`rA(GDB&e9LLBm)DmaDErFg5dd*cwO*1wi)#4v}h3@?BA%A>&D5dSTC zt360*xzU&%$;uK#V??A3B{ip%HL_ZH%~IwfHVREOG?Ql?+rf#xSIGk{x{&!;HakK# zOa$?5Haxg6zi2!}XP+D=^k#m!;RXs&=AlV*{w+(OC6 z8pF8NI%wXYbUjrWL*vWqTYIlO!=tjzDMZ7<9f2YXd!ko(T2z21arMhyIKgv?ADA`3 z(!8ER14Ct^wUAq2zs0sKqk)I%%6meAW8F{#emkopgnkA!k-hS~+;xxQrys`E`32Lf zj9MI@unBPw&^JmhNy#$;KpMKii@D&q=J%#5Iim~bUeDc&?E)I-n8iYR)6KLvtj<;j zzE$=2oxAo;-+0d2C?IBhMnbo5fSyb?>Q|I|tn(y#b8nhB`;v7?H<{PqtdrebZ#h$w zVhs?H)_c6;?SMxY)$x}Ho%Pz33V6RbcHpibWVg*WQl&T9s@{HXZEMO`8@$<3@}e@c zPc>$-?wfcw|ehr=`Ef||wp~{wA znSl4GbM^Mmm)D~+Ul3I6wjMh!YYfcGC8hTnaJP#g{vzO?r7P*ArZ-uOd#Wa;j-M_8 zTRshMG9&cfUtD}-G8?X;zgr7=F0Y_dnVjY>qjzBEp=$)d7f$0#u0NhVUTN8RBw72= zTem9D4P9{Vp{WG03bfKHx1w1ne-h7*cCq;09~&bXL-=TX2WfS!nb<#&amOHqx*xul zmRb_7X!l6u`2YuC0@b>pUZ<5lk>{+b;nKk<; zN+M&|Nxkf-Y5H^({z1LK?-bDx&*f{KlF`zV!sc5IwKlfb0zGN!FS~K(s9wK}*T+0T zw^*-vTD(sqJjGj{raO_e_?SppF^;oevn)=juB0oVWj2zK>EME3hq<98E;-eYmF2qQ zv)i`Gsl}Ok`gLiS4fOzf=YmGJ7HcDow4KY7r6ULTEw{@2_4(hEi@D{E#-7JTVO$8f z3XI&k3uWgQtE-%w@qzIU5U*Sh+TJD-F@b!KnPnoA5!`Kuz9j`ll$vzzBo{5|UXCU6 zjCY9$f|`wgGLE=MJ}Hw%10kB(rIfNo(ODv8AJ&j}Y>R4<0IJ@>IbyzL=i(EUYO{6g zml#ETB$m1HQ#Ppi;qsS`54f8=Wa1G>1PHGJYOo~|5C3AVO3o5(I zk97gyS(nCdy3dtbUWGC?h?gmh@zi@q$`v)S#u#lh;m-u3*_$YqEDGV^c(e4QtlxoMOdV$y-IL}N zyH9j>uX0vaOdQYbHV%6QjY}wP z_C=9nnd6_eT~nE*fqb7Ht;ME?hh+VZM{TL5S_a4Va;1Ozd260Do;g>Ym_dJf-F6MX zJ#DY&R$<5u3}{0nq|CI~D-VQzrABq5Gnvx2St@U{qo-#sKC3V4+L|2L*lpE@z>vKk z-jaPABWC`!Yt1*2NA0ES-k(scEak2IoH}OnN$C%xUpN_81qbw*h^WB*OO*!#0{`_= z1$;vdVEi(^#Jg<;KP|VFw_k3PZ?kT@Zi`!AizXvk(A+$kiy$VKfaBVWUc97;zt|OQ zNf=OlU z!^XRJLCx;JSK3J*Z;2IOEOos+OP$>?e-2+gxRSpS;R*fZ)Qji6Sw+R9awGLJC#9lF zE_>EQA~r;C%Fkr75Y{Sx#JWhrsKe5=m(FdjbarSvqxT!PC2KLuVb?f)dgGOs1uMYG z^SLwLy3kKaT6FYHfI%P0>bfTefr7i$D#^lllPp#$oP7|9(Gf#PW(?P!g@2{uT|qH2 zx1*nGSu$MNy%wRr=IY$D%6Ty(Lq8pKN}^<{!6 zatEFK zj6v}@s<%blyw>z(g|>L22I+@a?;HUA=?CZ6gbb@y$q06!j2bRiR5t?*o~i)-A1N9D zr}fhd+Yurn8EpBa0Yo5HApN$5orwc!=JUaJ>7G0A<;`RPFV5-EZC%Q)Q-QYgVoymG zkPo!JHopBaDu4aOeQ%mZ*N?8LXsPj0>o%$BU48?wISwQHW+ z+%LxlhJVF=3BGKs;_%+6IQVh1Za4XL(5q&0BQJb)ouz0n7jbyzdTYliQTHn@AXql% zf$>(Mj`DDJg12JKbhPCwz?RP>fdI$$pQ)jrD3jB{9D_5cUtJ8+mhCIFs@#kqDM)|uN6&!YQ)i~Q<+dPOg&0=OB zEstpp;l>r&8hJySV37TVy~v}`xkDP+8;{Qa&^kIj&&5>c>h^1ZuUJPc8-ieMDcdlh zE(j^Zu@i(skp3W$@YRLTSUE%8fVY6BN7hdEz#F0t3ZVIz&~?;gutl25nb4|%a8t+D zy0|V*Z&A+Dh`|I&*X=WPLBG{uK_KIxJ)rooLkxkiReb9fI+}O;TyJUfO?s;v!l2Vn}C+;!(4!=}B#VZ~4?^=3Z9@b3jO3l3YayvQMEwCrJ zbPV@Os;Z4S=C=5)a6}OO%}6uZ{GFApTw0N}Pi3lDb7aZL`-Ae?ue)2bEwg#P1ND9N z$=4jufE_%5pLq5Lht}#igzoCXex$te{q$q|CN5f!^+3z3lamoC{!|>}m7Tu0JUKRx zV=XK<>NaegXFK1pIX=9tYBQX_A~w%`Bd(^c{WBE&Vbt2d01Y(6$F8p@Y+Pg;Z_4gq z?2mJ#d~EZDS4)Xa@smn>W&kJ*m~xuoXBN+X$#C6wRQX#JP@s8r(j;)O%hQkkuz*@> z_G;WFqb2cc|J+lHirxNEoJe4}WtVlxj+^4S)X)LfCan|KntQWY#@gVD)OXd@Ybn9* zY>F=}ECw@`Papuq{Le+F{ZXQ#a#BA8phWnSdHlzw@`joqz8T&Q%%lJ6TiMc}Nekd` z(rT@1{ez|Hzt3!v^m&cf0a3)_t(_Q88lh&fZIm5`K6{Xk_(rAT;~J(qFGhqpqU(m# zIX$V_>mP2(65GE@ zdS}yeQNBKF|CIICYKA{VMm4UL9%okydo8Hj@gkZH>gF^i@$n;RlXlJAM9sm%RQfX7 z)`dQkI)2Wc<Hb?1RgW ziE`BUi*_D^-AyV<)KvXUQY8t^p^A%?!&;|->f{Q_bA_Jw*UHdTIbhs%; zN}S#s6M~ALA2}7j!jvrx04-IQPt8xKWONVTp&9OM6JZdFsxy}%t$*_|A)3kyA@K-a z4;UGmk6S}`ZRw=TvwW!NA}DesDDYItbEP7H=d<-6g9KOh2N0Uu{-&MqnkjUV)UIS4 z;Pv~oM^l%)UoK5#g%kzTUcBZoZR2c1QOe7DQEXZYo3D`Igb+6&>L*Wm$rBy6#j_|? zAW#`cP4bB^_q?mJCVR*)cKW{VTZ5oe;XXd%`({?k6DAA|^(3`?C0k-|PSXgBZKc|? zQrP1@>*=y%-X?9a5`irb-8a2c%@yW{Z_B%`PF9neNA`VxnA%O_ZB17%1vBx}G|twO zp|gg)tD?(g(|a~#&+E?M%g^-)2AbPICCpU+B6i*(##mwLK z$FZ|M)7SjOpp^C{&92P@v0hv?Sm%36AQ;v9lV`(}c)fNP@dE;o&pm&r*eyt2i0huo z3LC^tcJ|omOQJkAMVaBUYn4I)HgB11+;c)JEo4v%gdQ@x6iw%17 z9`-%udhhglv#b-XZ>QiZ?!#(l!YoR@ zALo4avbw0G9I4`2m9@R4=vFq-T{|1B9DIlZw9;Jd#z$gf^1cmd_;q_V!D5f#xhRAb z^Hl%SlP7dCJl=;0s(`{;y&QEcXCo^ZCZ?b42w1>T>$n)hyn?4#hf)O392au44i@(Sf3yndpRivpcjkk4v55%+ku0P( z75~%$Y4Tgf$P4C%`FOsW(ZLzT&4Y6t9@=1C>)cI~)W)wxt*WOkYD*@=@uX{S7(M1* zTpl#Ka6C%x8ls8aoD6TOT}Z#!p0Zj)N`Voyq3+8bZ2@lLX%>S_eApZw-m<38PWr%~O5Lj=lSGDVRU1@}D5O8$7X zD6*RSkyX}|?J^aPl>UYG)Ya5;YiG)5>ltt4F^`4gO1bNA@;V|RIoz(j`%J{$>Qrmb|+8y z-X~91vV60M8t0W-8IsZ3`n4Hn+42l{C(Vv@W&QXM@^b((WadK?3;!@Q_o*Kb3s3B3 zS>}79F@V4uKy`Y)LSGK>t6h0rEF1eE0FLdqc>>}@or0I5+rH&m==#$@ZUr33q#4wv zmb|-Mdr9B?9Os{VU31QpT_Dnwm+7t>Y7JZR&uuAOO4 z?baH8x}LJ@!?@kwJ2=M=MYImaySJA4nMA(o4LY#jDTbrBxt)K>Nw)21qVaIaZiU0= z8ZNC%ldM~u1hB3l@q>%ESfNi7U`NY|j&cXj`o0_<-JH%yC4_3JEqt*4kuvwTVNJN^ zk3eZ%BxCO_J6!zf*U3rS!Z$Tc&FhhpdaLWWXDv1CA&h%GJiE6dyVk4x8O?io*E;$; zLuMb&7v&u?qddFkX~p61SZoGH-S~;E@c9a(!w(`EJ#b?BIylNJsi{(iRyLa?ui`6J z>5~b|EhDy2(g?$c2)jnl%58;VB+LgB7wsDwyxf`-pPN>71yecRU$hM_bxg|KY%wNJ f!1wLb(GlP0Y3#qaBr7ITpGxXen=)KMF9-e)=@q#8 literal 0 HcmV?d00001 From f6d14d160d1395e1aaf27cc63888335e50f7e454 Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Sun, 7 Jun 2020 01:04:48 +0100 Subject: [PATCH 06/21] Updated the Input logic and made some minor changes to the Drawer logic --- .../engine/context/loader/GLLoader.kt | 1 + .../engine/context/loader/InputLoader.kt | 7 + .../com/mechanica/engine/input/KeyID.kt | 6 + .../com/mechanica/engine/input/KeyIDs.kt | 141 +++++++++++++ .../com/mechanica/engine/audio/ALSource.kt | 14 +- .../engine/audio/ALSourceProperty.kt | 22 ++ .../engine/context/loader/LwjglInputLoader.kt | 8 + .../engine/context/loader/LwjglLoader.kt | 1 + .../com/mechanica/engine/input/GLFWKeyIDs.kt | 148 ++++++++++++++ .../engine/animation/FrameAnimation.kt | 4 +- .../com/mechanica/engine/drawer/DrawData.kt | 29 +-- .../transformation/TransformationDrawer.kt | 2 + .../TransformationDrawerImpl.kt | 5 + .../drawer/superclass/circle/CircleDrawer.kt | 14 +- .../superclass/circle/CircleDrawerImpl.kt | 10 +- .../drawer/superclass/image/ImageDrawer.kt | 5 +- .../superclass/image/ImageDrawerImpl.kt | 27 +-- .../superclass/rectangle/RectangleDrawer.kt | 4 +- .../rectangle/RectangleDrawerImpl.kt | 19 +- .../kotlin/com/mechanica/engine/game/Game.kt | 8 +- .../game/configuration/ConfigurationData.kt | 2 - .../game/configuration/GameConfiguration.kt | 2 - .../configuration/GameConfigurationImpl.kt | 5 - .../engine/game/configuration/GameSetup.kt | 2 - .../NullableConfigurationData.kt | 2 - .../engine/game/view/DefaultDynamicView.kt | 55 +++++ .../mechanica/engine/game/view/DynamicView.kt | 21 ++ .../mechanica/engine/game/view/GameView.kt | 49 +++-- .../mechanica/engine/game/view/StaticView.kt | 34 ++++ .../com/mechanica/engine/game/view/View.kt | 10 + .../com/mechanica/engine/input/ControlsMap.kt | 27 --- .../kotlin/com/mechanica/engine/input/Key.kt | 20 +- .../kotlin/com/mechanica/engine/input/Keys.kt | 3 +- .../engine/input/keyboard/KeyboardImpl.kt | 188 +++++++++--------- .../com/mechanica/engine/input/mouse/Mouse.kt | 3 +- .../mechanica/engine/input/mouse/MouseImpl.kt | 26 +-- .../mechanica/engine/scenes/SceneManager.kt | 48 ++--- .../engine/scenes/processes/InputProcess.kt | 9 + .../engine/scenes/processes/Process.kt | 42 +++- .../engine/scenes/processes/ProcessNode.kt | 3 + .../engine/scenes/scenes/DynamicScene.kt | 74 +++++++ .../engine/scenes/scenes/GUIScene.kt | 6 +- .../engine/scenes/scenes/MainScene.kt | 5 +- .../mechanica/engine/scenes/scenes/Scene.kt | 59 ++++-- .../engine/scenes/scenes/StaticScene.kt | 22 ++ 45 files changed, 877 insertions(+), 315 deletions(-) create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/context/loader/InputLoader.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/input/KeyID.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/input/KeyIDs.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSourceProperty.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglInputLoader.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyIDs.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/game/view/DynamicView.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt delete mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt index d7d207e8..480ddfe0 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/GLLoader.kt @@ -14,6 +14,7 @@ interface GLLoader { val fontLoader: FontLoader val graphicsLoader: GraphicsLoader val audioLoader: AudioLoader + val inputLoader: InputLoader fun createAttributeLoader(qualifier: AttributeQualifier): AttributeLoader fun createUniformLoader(qualifier: Qualifier): UniformLoader diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/InputLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/InputLoader.kt new file mode 100644 index 00000000..04e44b1d --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/InputLoader.kt @@ -0,0 +1,7 @@ +package com.mechanica.engine.context.loader + +import com.mechanica.engine.input.KeyIDs + +interface InputLoader { + fun keyIds(): KeyIDs +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyID.kt b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyID.kt new file mode 100644 index 00000000..fd7fb00e --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyID.kt @@ -0,0 +1,6 @@ +package com.mechanica.engine.input + +interface KeyID { + val id: Int + val label: String +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyIDs.kt b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyIDs.kt new file mode 100644 index 00000000..8e05518b --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/input/KeyIDs.kt @@ -0,0 +1,141 @@ +package com.mechanica.engine.input + +import com.mechanica.engine.context.loader.GLLoader + +interface KeyIDs { + val UNKNOWN: KeyID + val M1: KeyID + val M2: KeyID + val M3: KeyID + val M4: KeyID + val M5: KeyID + val M6: KeyID + val M7: KeyID + val M8: KeyID + val SPACE: KeyID + val APOSTROPHE: KeyID + val COMMA: KeyID + val MINUS: KeyID + val PERIOD: KeyID + val SLASH: KeyID + val N0: KeyID + val N1: KeyID + val N2: KeyID + val N3: KeyID + val N4: KeyID + val N5: KeyID + val N6: KeyID + val N7: KeyID + val N8: KeyID + val N9: KeyID + val SEMICOLON: KeyID + val EQUAL: KeyID + val A: KeyID + val B: KeyID + val C: KeyID + val D: KeyID + val E: KeyID + val F: KeyID + val G: KeyID + val H: KeyID + val I: KeyID + val J: KeyID + val K: KeyID + val L: KeyID + val M: KeyID + val N: KeyID + val O: KeyID + val P: KeyID + val Q: KeyID + val R: KeyID + val S: KeyID + val T: KeyID + val U: KeyID + val V: KeyID + val W: KeyID + val X: KeyID + val Y: KeyID + val Z: KeyID + val LEFT_BRACKET: KeyID + val BACKSLASH: KeyID + val RIGHT_BRACKET: KeyID + val GRAVE_ACCENT: KeyID + val WORLD_1: KeyID + val WORLD_2: KeyID + val ESC: KeyID + val ENTER: KeyID + val TAB: KeyID + val BACKSPACE: KeyID + val INSERT: KeyID + val DELETE: KeyID + val RIGHT: KeyID + val LEFT: KeyID + val DOWN: KeyID + val UP: KeyID + val PAGE_UP: KeyID + val PAGE_DOWN: KeyID + val HOME: KeyID + val END: KeyID + val CAPS_LOCK: KeyID + val SCROLL_LOCK: KeyID + val NUM_LOCK: KeyID + val PRINT_SCREEN: KeyID + val PAUSE: KeyID + val F1: KeyID + val F2: KeyID + val F3: KeyID + val F4: KeyID + val F5: KeyID + val F6: KeyID + val F7: KeyID + val F8: KeyID + val F9: KeyID + val F10: KeyID + val F11: KeyID + val F12: KeyID + val F13: KeyID + val F14: KeyID + val F15: KeyID + val F16: KeyID + val F17: KeyID + val F18: KeyID + val F19: KeyID + val F20: KeyID + val F21: KeyID + val F22: KeyID + val F23: KeyID + val F24: KeyID + val F25: KeyID + val KP_0: KeyID + val KP_1: KeyID + val KP_2: KeyID + val KP_3: KeyID + val KP_4: KeyID + val KP_5: KeyID + val KP_6: KeyID + val KP_7: KeyID + val KP_8: KeyID + val KP_9: KeyID + val KP_DECIMAL: KeyID + val KP_DIVIDE: KeyID + val KP_MULTIPLY: KeyID + val KP_SUBTRACT: KeyID + val KP_ADD: KeyID + val KP_ENTER: KeyID + val KP_EQUAL: KeyID + val LSHIFT: KeyID + val LCTRL: KeyID + val LALT: KeyID + val LEFT_SUPER: KeyID + val RSHIFT: KeyID + val RCTRL: KeyID + val RALT: KeyID + val RIGHT_SUPER: KeyID + val MENU: KeyID + + val SCROLL_UP: KeyID + val SCROLL_DOWN: KeyID + val SCROLL: KeyID + + companion object : KeyIDs by GLLoader.inputLoader.keyIds() +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt index 2c4d4142..ed349345 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSource.kt @@ -5,14 +5,16 @@ import org.joml.Vector3f import org.lwjgl.openal.AL10.* import kotlin.reflect.KProperty -class ALSource(override val sound: Sound) : ALAudioObject(), SoundSource { +class ALSource(override var sound: Sound) : ALAudioObject(), SoundSource { override val id: Int = alGenSources() - override var gain: Float by ALFloatProperty(AL_GAIN) - override var pitch: Float by ALFloatProperty(AL_PITCH) - override var rolloff: Float by ALFloatProperty(AL_ROLLOFF_FACTOR) - override var maxDistance: Float by ALFloatProperty(AL_MAX_DISTANCE) - override var referenceDistance: Float by ALFloatProperty(AL_REFERENCE_DISTANCE) + private val properties = ALSourceProperty(id) + + override var gain: Float by properties.createProperty(AL_GAIN) + override var pitch: Float by properties.createProperty(AL_PITCH) + override var rolloff: Float by properties.createProperty(AL_ROLLOFF_FACTOR) + override var maxDistance: Float by properties.createProperty(AL_MAX_DISTANCE) + override var referenceDistance: Float by properties.createProperty(AL_REFERENCE_DISTANCE) init { alSourcei(id, AL_BUFFER, sound.id) diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSourceProperty.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSourceProperty.kt new file mode 100644 index 00000000..c87c2d47 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/audio/ALSourceProperty.kt @@ -0,0 +1,22 @@ +package com.mechanica.engine.audio + +import org.lwjgl.openal.AL10 +import kotlin.reflect.KProperty + +class ALSourceProperty(private val sourceId: Int) { + + fun createProperty(property: Int) = DelegatedProperty(property) + + private fun getFloat(property: Int) = AL10.alGetSourcef(sourceId, property) + private fun setFloat(property: Int, float: Float) = AL10.alSourcef(sourceId, property, float) + + inner class DelegatedProperty(private val property: Int) { + operator fun getValue(thisRef: SoundSource, property: KProperty<*>): Float { + return getFloat(this.property) + } + + operator fun setValue(thisRef: SoundSource, property: KProperty<*>, value: Float) { + setFloat(this.property, value) + } + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglInputLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglInputLoader.kt new file mode 100644 index 00000000..3d0cf566 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglInputLoader.kt @@ -0,0 +1,8 @@ +package com.mechanica.engine.context.loader + +import com.mechanica.engine.input.GLFWKeyIDs +import com.mechanica.engine.input.KeyIDs + +class LwjglInputLoader : InputLoader { + override fun keyIds() = GLFWKeyIDs() +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt index 57c240b8..834f75b3 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt @@ -14,6 +14,7 @@ class LwjglLoader : GLLoader { override val fontLoader = LwjglFontLoader() override val graphicsLoader = LwjglGraphicsLoader() override val audioLoader = LwjglAudioLoader() + override val inputLoader = LwjglInputLoader() override fun createAttributeLoader(qualifier: AttributeQualifier) = LwjglAttributeLoader(qualifier) diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyIDs.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyIDs.kt new file mode 100644 index 00000000..a827eb10 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/input/GLFWKeyIDs.kt @@ -0,0 +1,148 @@ +package com.mechanica.engine.input + +import org.lwjgl.glfw.GLFW.* + +class GLFWKeyIDs : KeyIDs { + override val UNKNOWN = GLFWKey(GLFW_KEY_UNKNOWN) + override val M1 = GLFWKey(GLFW_MOUSE_BUTTON_1) + override val M2 = GLFWKey(GLFW_MOUSE_BUTTON_2) + override val M3 = GLFWKey(GLFW_MOUSE_BUTTON_3) + override val M4 = GLFWKey(GLFW_MOUSE_BUTTON_4) + override val M5 = GLFWKey(GLFW_MOUSE_BUTTON_5) + override val M6 = GLFWKey(GLFW_MOUSE_BUTTON_6) + override val M7 = GLFWKey(GLFW_MOUSE_BUTTON_7) + override val M8 = GLFWKey(GLFW_MOUSE_BUTTON_8) + override val SPACE = GLFWKey(GLFW_KEY_SPACE) + override val APOSTROPHE = GLFWKey(GLFW_KEY_APOSTROPHE) + override val COMMA = GLFWKey(GLFW_KEY_COMMA) + override val MINUS = GLFWKey(GLFW_KEY_MINUS) + override val PERIOD = GLFWKey(GLFW_KEY_PERIOD) + override val SLASH = GLFWKey(GLFW_KEY_SLASH) + override val N0 = GLFWKey(GLFW_KEY_0) + override val N1 = GLFWKey(GLFW_KEY_1) + override val N2 = GLFWKey(GLFW_KEY_2) + override val N3 = GLFWKey(GLFW_KEY_3) + override val N4 = GLFWKey(GLFW_KEY_4) + override val N5 = GLFWKey(GLFW_KEY_5) + override val N6 = GLFWKey(GLFW_KEY_6) + override val N7 = GLFWKey(GLFW_KEY_7) + override val N8 = GLFWKey(GLFW_KEY_8) + override val N9 = GLFWKey(GLFW_KEY_9) + override val SEMICOLON = GLFWKey(GLFW_KEY_SEMICOLON) + override val EQUAL = GLFWKey(GLFW_KEY_EQUAL) + override val A = GLFWKey(GLFW_KEY_A) + override val B = GLFWKey(GLFW_KEY_B) + override val C = GLFWKey(GLFW_KEY_C) + override val D = GLFWKey(GLFW_KEY_D) + override val E = GLFWKey(GLFW_KEY_E) + override val F = GLFWKey(GLFW_KEY_F) + override val G = GLFWKey(GLFW_KEY_G) + override val H = GLFWKey(GLFW_KEY_H) + override val I = GLFWKey(GLFW_KEY_I) + override val J = GLFWKey(GLFW_KEY_J) + override val K = GLFWKey(GLFW_KEY_K) + override val L = GLFWKey(GLFW_KEY_L) + override val M = GLFWKey(GLFW_KEY_M) + override val N = GLFWKey(GLFW_KEY_N) + override val O = GLFWKey(GLFW_KEY_O) + override val P = GLFWKey(GLFW_KEY_P) + override val Q = GLFWKey(GLFW_KEY_Q) + override val R = GLFWKey(GLFW_KEY_R) + override val S = GLFWKey(GLFW_KEY_S) + override val T = GLFWKey(GLFW_KEY_T) + override val U = GLFWKey(GLFW_KEY_U) + override val V = GLFWKey(GLFW_KEY_V) + override val W = GLFWKey(GLFW_KEY_W) + override val X = GLFWKey(GLFW_KEY_X) + override val Y = GLFWKey(GLFW_KEY_Y) + override val Z = GLFWKey(GLFW_KEY_Z) + override val LEFT_BRACKET = GLFWKey(GLFW_KEY_LEFT_BRACKET) + override val BACKSLASH = GLFWKey(GLFW_KEY_BACKSLASH) + override val RIGHT_BRACKET = GLFWKey(GLFW_KEY_RIGHT_BRACKET) + override val GRAVE_ACCENT = GLFWKey(GLFW_KEY_GRAVE_ACCENT) + override val WORLD_1 = GLFWKey(GLFW_KEY_WORLD_1) + override val WORLD_2 = GLFWKey(GLFW_KEY_WORLD_2) + override val ESC = GLFWKey(GLFW_KEY_ESCAPE) + override val ENTER = GLFWKey(GLFW_KEY_ENTER) + override val TAB = GLFWKey(GLFW_KEY_TAB) + override val BACKSPACE = GLFWKey(GLFW_KEY_BACKSPACE) + override val INSERT = GLFWKey(GLFW_KEY_INSERT) + override val DELETE = GLFWKey(GLFW_KEY_DELETE) + override val RIGHT = GLFWKey(GLFW_KEY_RIGHT) + override val LEFT = GLFWKey(GLFW_KEY_LEFT) + override val DOWN = GLFWKey(GLFW_KEY_DOWN) + override val UP = GLFWKey(GLFW_KEY_UP) + override val PAGE_UP = GLFWKey(GLFW_KEY_PAGE_UP) + override val PAGE_DOWN = GLFWKey(GLFW_KEY_PAGE_DOWN) + override val HOME = GLFWKey(GLFW_KEY_HOME) + override val END = GLFWKey(GLFW_KEY_END) + override val CAPS_LOCK = GLFWKey(GLFW_KEY_CAPS_LOCK) + override val SCROLL_LOCK = GLFWKey(GLFW_KEY_SCROLL_LOCK) + override val NUM_LOCK = GLFWKey(GLFW_KEY_NUM_LOCK) + override val PRINT_SCREEN = GLFWKey(GLFW_KEY_PRINT_SCREEN) + override val PAUSE = GLFWKey(GLFW_KEY_PAUSE) + override val F1 = GLFWKey(GLFW_KEY_F1) + override val F2 = GLFWKey(GLFW_KEY_F2) + override val F3 = GLFWKey(GLFW_KEY_F3) + override val F4 = GLFWKey(GLFW_KEY_F4) + override val F5 = GLFWKey(GLFW_KEY_F5) + override val F6 = GLFWKey(GLFW_KEY_F6) + override val F7 = GLFWKey(GLFW_KEY_F7) + override val F8 = GLFWKey(GLFW_KEY_F8) + override val F9 = GLFWKey(GLFW_KEY_F9) + override val F10 = GLFWKey(GLFW_KEY_F10) + override val F11 = GLFWKey(GLFW_KEY_F11) + override val F12 = GLFWKey(GLFW_KEY_F12) + override val F13 = GLFWKey(GLFW_KEY_F13) + override val F14 = GLFWKey(GLFW_KEY_F14) + override val F15 = GLFWKey(GLFW_KEY_F15) + override val F16 = GLFWKey(GLFW_KEY_F16) + override val F17 = GLFWKey(GLFW_KEY_F17) + override val F18 = GLFWKey(GLFW_KEY_F18) + override val F19 = GLFWKey(GLFW_KEY_F19) + override val F20 = GLFWKey(GLFW_KEY_F20) + override val F21 = GLFWKey(GLFW_KEY_F21) + override val F22 = GLFWKey(GLFW_KEY_F22) + override val F23 = GLFWKey(GLFW_KEY_F23) + override val F24 = GLFWKey(GLFW_KEY_F24) + override val F25 = GLFWKey(GLFW_KEY_F25) + override val KP_0 = GLFWKey(GLFW_KEY_KP_0) + override val KP_1 = GLFWKey(GLFW_KEY_KP_1) + override val KP_2 = GLFWKey(GLFW_KEY_KP_2) + override val KP_3 = GLFWKey(GLFW_KEY_KP_3) + override val KP_4 = GLFWKey(GLFW_KEY_KP_4) + override val KP_5 = GLFWKey(GLFW_KEY_KP_5) + override val KP_6 = GLFWKey(GLFW_KEY_KP_6) + override val KP_7 = GLFWKey(GLFW_KEY_KP_7) + override val KP_8 = GLFWKey(GLFW_KEY_KP_8) + override val KP_9 = GLFWKey(GLFW_KEY_KP_9) + override val KP_DECIMAL = GLFWKey(GLFW_KEY_KP_DECIMAL) + override val KP_DIVIDE = GLFWKey(GLFW_KEY_KP_DIVIDE) + override val KP_MULTIPLY = GLFWKey(GLFW_KEY_KP_MULTIPLY) + override val KP_SUBTRACT = GLFWKey(GLFW_KEY_KP_SUBTRACT) + override val KP_ADD = GLFWKey(GLFW_KEY_KP_ADD) + override val KP_ENTER = GLFWKey(GLFW_KEY_KP_ENTER) + override val KP_EQUAL = GLFWKey(GLFW_KEY_KP_EQUAL) + override val LSHIFT = GLFWKey(GLFW_KEY_LEFT_SHIFT) + override val LCTRL = GLFWKey(GLFW_KEY_LEFT_CONTROL) + override val LALT = GLFWKey(GLFW_KEY_LEFT_ALT) + override val LEFT_SUPER = GLFWKey(GLFW_KEY_LEFT_SUPER) + override val RSHIFT = GLFWKey(GLFW_KEY_RIGHT_SHIFT) + override val RCTRL = GLFWKey(GLFW_KEY_RIGHT_CONTROL) + override val RALT = GLFWKey(GLFW_KEY_RIGHT_ALT) + override val RIGHT_SUPER = GLFWKey(GLFW_KEY_RIGHT_SUPER) + override val MENU = GLFWKey(GLFW_KEY_MENU) + override val SCROLL_UP = GLFWKey(1000, "scroll up") + override val SCROLL_DOWN = GLFWKey(1001, "scroll down") + override val SCROLL = GLFWKey(1002, "scroll") + + class GLFWKey(override val id: Int, label: String? = null) : KeyID { + private var _label: String? = label + + override val label: String + get() { + val l = _label + return l ?: glfwGetKeyName(id, 0)?.also { _label = it } ?: "?" + } + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt b/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt index cfff200b..0e5aed8b 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt @@ -11,6 +11,8 @@ class FrameAnimation(private val frames: List, frameRate : Double) { private var progress = 0.0 set(value) { field = value%duration } + var reversed = false + var fraction: Double get() = progress/duration set(value) { @@ -31,7 +33,7 @@ class FrameAnimation(private val frames: List, frameRate : Double) { field = abs(value) } - fun play(delta: Double, reversed: Boolean = false) { + fun update(delta: Double) { if (!reversed) { progress += scale*delta } else { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt index f7135226..d9cb1d35 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt @@ -20,14 +20,13 @@ class DrawData { viewMatrixWasSet = true field = value } - var projectionMatrix: Matrix4f = Game.matrices.projection + private var projectionMatrix: Matrix4f = Game.matrices.projection private val renderer = DrawerRenderer() private val transformation = Matrix4f().identity() private val translation: Vector3f = Vector3f() private val pivot: Vector3f = Vector3f() - private val unpivot: Vector3f = Vector3f() private val scale: Vector3f = Vector3f(1f, 1f, 1f) private val zAxis = Vector3f(0f, 0f, 1f) private var rx = 0f @@ -72,27 +71,22 @@ class DrawData { val cornerSize: DynamicVector get() = renderer.size -// private val fontMap = HashMap() -// -// private var font: Font = Font.create(Res.font["Roboto-Regular.ttf"]).also { fontMap[it] = TextModel("", it) } - - val textModel: TextModel = TextModel("Test") - //get() = fontMap[font] ?: TextModel("", font).also { fontMap[font] = it } + val textModel: TextModel = TextModel("") fun setTranslate(x: Float, y: Float) { - translation.set(x, y, translateZ) + translation.set(translation.x + x, translation.y + y, translateZ) } fun setRotate(angle: Float) { - rz = angle + rz += angle } fun setScale(x: Float, y: Float) { - scale.set(x, y, 0f) + scale.set(scale.x*x, scale.y*y, 0f) } fun setDepth(z: Float) { - translation.set(translateX, translateY, -z) + translation.set(translateX, translateY, translation.z-z) } fun getTransformationMatrix(matrix: Matrix4f): Matrix4f { @@ -109,10 +103,6 @@ class DrawData { matrix.scale(scale) pivot.set(0f, 0f, 0f) - unpivot.set(0f, 0f, 0f) - rx = 0f - ry = 0f - rz = 0f return matrix } @@ -129,10 +119,9 @@ class DrawData { } fun rewind() { - setRotate(0f) - setTranslate(0f, 0f) - setDepth(0f) - setScale(1f, 1f) + rz = 0f + translation.set(0f, 0f, 0f) + scale.set(1f, 1f, 1f) radius = 0f noReset = false diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt index 136f06c6..e7d0af0b 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt @@ -15,4 +15,6 @@ interface TransformationDrawer : Drawer { fun scale(scale: LightweightVector) = scale(scale.x, scale.y) fun scale(scale: Vector) = scale(scale.x, scale.y) fun scale(scale: Double) = scale(scale, scale) + + operator fun invoke(x: Number = 0.0, y: Number = 0.0, scaleX: Number = 1.0, scaleY: Number = 1.0): TransformationDrawer } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt index 2ecc97d7..0bc1b1e5 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt @@ -14,4 +14,9 @@ class TransformationDrawerImpl(drawer: Drawer, private val data: DrawData): Tran return this } + override fun invoke(x: Number, y: Number, scaleX: Number, scaleY: Number): TransformationDrawer { + translate(x, y) + scale(scaleX, scaleY) + return this + } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawer.kt index 717482f5..ce9b9f09 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawer.kt @@ -4,17 +4,11 @@ import com.mechanica.engine.unit.vector.LightweightVector import com.mechanica.engine.unit.vector.Vector interface CircleDrawer { - fun circle() + fun circle(x: Number = 0, y: Number = 0, radius: Number = 0.5f) - fun circle(x: Number, y: Number) - fun circle(x: Number, y: Number, radius: Number) + fun circle(position: Vector, radius: Number = 0.5f) = circle(position.x, position.y, radius) + fun circle(position: LightweightVector, radius: Number = 0.5f) = circle(position.x, position.y, radius) - fun circle(position: Vector) = circle(position.x, position.y) - fun circle(position: LightweightVector) = circle(position.x, position.y) - - fun circle(position: Vector, radius: Number) = circle(position.x, position.y, radius) - fun circle(position: LightweightVector, radius: Number) = circle(position.x, position.y, radius) - - fun ellipse(x: Number, y: Number, width: Number, height: Number) + fun ellipse(x: Number = 0, y: Number = 0, width: Number = 1, height: Number = 1) fun ellipse(xy: LightweightVector, wh: LightweightVector) = ellipse(xy.x, xy.y, wh.x, wh.y) } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt index 86054ef7..86d7dd97 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt @@ -24,7 +24,7 @@ class CircleDrawerImpl( model = Model(position, disableTexCoords, draw = GLLoader.graphicsLoader::drawArrays) } - override fun circle() { + private fun drawCircle() { val radius = if (data.radius > 0.0) data.radius else { data.radius = 0.5f @@ -44,14 +44,10 @@ class CircleDrawerImpl( data.draw(model) } - override fun circle(x: Number, y: Number) { - data.setTranslate(x.toFloat(), y.toFloat()) - circle() - } - override fun circle(x: Number, y: Number, radius: Number) { data.radius = radius.toFloat() - circle(x, y) + data.setTranslate(x.toFloat(), y.toFloat()) + drawCircle() } override fun ellipse(x: Number, y: Number, width: Number, height: Number) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawer.kt index 7be9f498..1b8610c1 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawer.kt @@ -4,10 +4,7 @@ import com.mechanica.engine.models.Image import com.mechanica.engine.unit.vector.LightweightVector interface ImageDrawer { - fun image(image: Image) - - fun image(image: Image, x: Number, y: Number) - fun image(image: Image, x: Number, y: Number, width: Number, height: Number) + fun image(image: Image, x: Number = 0, y: Number = 0, width: Number = 1, height: Number = 1) fun image(image: Image, xy: LightweightVector, wh: LightweightVector) = image(image, xy.x, xy.y, wh.x, wh.y) } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt index ad4e201f..1ef36783 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt @@ -17,7 +17,17 @@ class ImageDrawerImpl( model = ImageModel(Image(0), position, textureCoords) } - override fun image(image: Image) { + override fun image(image: Image, x: Number, y: Number, width: Number, height: Number) { + data.setTranslate(x.toFloat(), y.toFloat()) + data.setScale(width.toFloat(), height.toFloat()) + + data.cornerSize.x = width.toDouble() + data.cornerSize.y = height.toDouble() + + drawImage(image) + } + + private fun drawImage(image: Image) { model.image = image data.cornerSize.x = data.scaleX.toDouble() @@ -30,19 +40,4 @@ class ImageDrawerImpl( data.alphaBlend = 0f } - override fun image(image: Image, x: Number, y: Number) { - data.setTranslate(x.toFloat(), y.toFloat()) - image(image) - } - - override fun image(image: Image, x: Number, y: Number, width: Number, height: Number) { - data.setTranslate(x.toFloat(), y.toFloat()) - data.setScale(width.toFloat(), height.toFloat()) - - data.cornerSize.x = width.toDouble() - data.cornerSize.y = height.toDouble() - - image(image) - } - } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawer.kt index 4f9f9668..b26f811d 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawer.kt @@ -3,8 +3,6 @@ package com.mechanica.engine.drawer.superclass.rectangle import com.mechanica.engine.unit.vector.LightweightVector interface RectangleDrawer { - fun rectangle() - fun rectangle(x: Number, y: Number) - fun rectangle(x: Number, y: Number, width: Number, height: Number) + fun rectangle(x: Number = 0, y: Number = 0, width: Number = 1, height: Number = 1) fun rectangle(xy: LightweightVector, wh: LightweightVector) = rectangle(xy.x, xy.y, wh.x, wh.y) } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt index 94877519..13e03f91 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt @@ -24,21 +24,16 @@ internal class RectangleDrawerImpl( } - override fun rectangle() { - data.cornerSize.x = data.scaleX.toDouble() - data.cornerSize.y = data.scaleY.toDouble() - data.draw(model) - } - - override fun rectangle(x: Number, y: Number) { - data.setTranslate(x.toFloat(), y.toFloat()) - rectangle() - } - override fun rectangle(x: Number, y: Number, width: Number, height: Number) { data.setTranslate(x.toFloat(), y.toFloat()) data.setScale(width.toFloat(), height.toFloat()) - rectangle() + drawRectangle() + } + + private fun drawRectangle() { + data.cornerSize.x = data.scaleX.toDouble() + data.cornerSize.y = data.scaleY.toDouble() + data.draw(model) } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt index 4974bdf6..71c8604d 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt @@ -31,8 +31,6 @@ object Game { val ui: View by lazy { UIView() } val window: Window by lazy { data.window } - internal val controls by lazy { data.controlsMap } - val debug by lazy { data.debugConfig } val matrices: Matrices by lazy { GameMatrices(data, view) } @@ -163,7 +161,9 @@ object Game { override val height: Double override val x: Double = 0.0 override val y: Double = 0.0 - override val center: Vector = vec(0.0, 0.0) + override val xy: Vector = vec(0.0, 0.0) + override val wh: Vector + override val center: Vector override val ratio: Double get() = view.ratio*(scale.y/scale.x) @@ -172,6 +172,8 @@ object Game { scale = vec(contentScale.xScale, contentScale.yScale) width = view.width/scale.x height = view.height/scale.y + wh = vec(width, height) + center = vec(width/2.0, height/2.0) } } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt index bfc2f8f1..3fb39e39 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt @@ -5,7 +5,6 @@ import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.view.View -import com.mechanica.engine.input.ControlsMap import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f @@ -22,7 +21,6 @@ interface ConfigurationData { val viewX: Double? val viewY: Double? val saveData: Array? - val controlsMap: ControlsMap? val fullscreen: Boolean? val startingScene: (() -> MainScene)? val loadState: (() -> LoadScene)? diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt index 688e3e4b..54f2fe3b 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt @@ -3,7 +3,6 @@ package com.mechanica.engine.game.configuration import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.game.view.View -import com.mechanica.engine.input.ControlsMap import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f @@ -18,7 +17,6 @@ interface GameConfiguration { fun setStartingState(scene: () -> MainScene) fun setLoader(loader: () -> LoadScene) - fun setControlMapping(controlsMap: ControlsMap) fun setSaveData(vararg savedata: Any) fun setMultisampling(samples: Int) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt index b864539d..f9b9717e 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt @@ -4,7 +4,6 @@ import com.mechanica.engine.context.GLFWContext import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.game.view.View -import com.mechanica.engine.input.ControlsMap import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f @@ -47,10 +46,6 @@ internal class GameConfigurationImpl : GameConfiguration { _data.loadState = loader } - override fun setControlMapping(controlsMap: ControlsMap) { - _data.controlsMap = controlsMap - } - override fun setSaveData(vararg savedata: Any) { _data.saveData = arrayOf(*savedata) } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt index ed352f47..b5647826 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt @@ -6,7 +6,6 @@ import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.game.view.GameMatrices import com.mechanica.engine.game.view.ResolutionConverter import com.mechanica.engine.game.view.View -import com.mechanica.engine.input.ControlsMap import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f @@ -23,7 +22,6 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { override val viewX: Double = data.viewX ?: 0.0 override val viewY: Double = data.viewX ?: 0.0 override val saveData: Array = data.saveData ?: emptyArray() - override val controlsMap: ControlsMap = data.controlsMap ?: object : ControlsMap() { } override val fullscreen: Boolean = data.fullscreen ?: false override val startingScene: (() -> MainScene)? = data.startingScene override val loadState: (() -> LoadScene)? = data.loadState diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt index 706dd925..0a0d8a12 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt @@ -4,7 +4,6 @@ import com.mechanica.engine.display.Monitor import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration import com.mechanica.engine.game.view.View -import com.mechanica.engine.input.ControlsMap import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.scenes.scenes.LoadScene import org.joml.Matrix4f @@ -24,7 +23,6 @@ class NullableConfigurationData : ConfigurationData { override var viewY: Double? = null override var saveData: Array? = null - override var controlsMap: ControlsMap? = null override var fullscreen: Boolean? = null diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt new file mode 100644 index 00000000..6827d5ac --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt @@ -0,0 +1,55 @@ +package com.mechanica.engine.game.view + +import com.mechanica.engine.unit.vector.DynamicVector + +class DefaultDynamicView( + override var x: Double = 0.0, + override var y: Double = 0.0, + override var width: Double = 1.0, + override var height: Double = 1.0) : DynamicView { + + @Suppress("SetterBackingFieldAssignment") + override var xy: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@DefaultDynamicView.x + set(value) {this@DefaultDynamicView.x = value} + override var y: Double + get() = this@DefaultDynamicView.y + set(value) { this@DefaultDynamicView.y = value} + } + set(value) { + x = value.x + y = value.y + } + + @Suppress("SetterBackingFieldAssignment") + override var wh: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@DefaultDynamicView.width + set(value) {this@DefaultDynamicView.width = value } + override var y: Double + get() = this@DefaultDynamicView.height + set(value) { this@DefaultDynamicView.height = value} + } + set(value) { + x = value.x + y = value.y + } + + @Suppress("SetterBackingFieldAssignment") + override var center: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@DefaultDynamicView.x + width/2.0 + set(value) {this@DefaultDynamicView.x = value - width/2.0} + override var y: Double + get() = this@DefaultDynamicView.y + height/2.0 + set(value) { this@DefaultDynamicView.y = value - height/2.0} + } + set(value) { + x = value.x + y = value.y + } + + override val ratio: Double + get() = height/width +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DynamicView.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DynamicView.kt new file mode 100644 index 00000000..363e5db7 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DynamicView.kt @@ -0,0 +1,21 @@ +package com.mechanica.engine.game.view + +import com.mechanica.engine.unit.vector.DynamicVector + +interface DynamicView : View { + override var x: Double + override var y: Double + override var width: Double + override var height: Double + + override var xy: DynamicVector + override var wh: DynamicVector + override var center: DynamicVector + + companion object { + fun create(x: Double = 0.0, + y: Double = 0.0, + width: Double = 1.0, + height: Double = 1.0): DynamicView = DefaultDynamicView(x, y, width, height) + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt index c303a29a..60069956 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt @@ -6,7 +6,7 @@ import com.mechanica.engine.game.configuration.GameSetup import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.vec -class GameView(data: GameSetup): View { +class GameView(data: GameSetup): DynamicView { private var _width: Double = data.viewWidth override var width: Double get() = _width @@ -44,18 +44,46 @@ class GameView(data: GameSetup): View { gameMatrices.updateView(this) } - var xy: Vector = DynamicVector.create() + @Suppress("SetterBackingFieldAssignment") + override var xy: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@GameView.x + set(value) {this@GameView.x = value} + override var y: Double + get() = this@GameView.y + set(value) { this@GameView.y = value} + } set(value) { - (field as DynamicVector).set(value) x = value.x y = value.y } - var wh: Vector = DynamicVector.create() + @Suppress("SetterBackingFieldAssignment") + override var center: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@GameView.x + width/2.0 + set(value) {this@GameView.x = value - width/2.0} + override var y: Double + get() = this@GameView.y + height/2.0 + set(value) { this@GameView.y = value - height/2.0} + } set(value) { - (field as DynamicVector).set(value) - width = value.x - height = value.y + x = value.x + y = value.y + } + + @Suppress("SetterBackingFieldAssignment") + override var wh: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@GameView.width + set(value) {this@GameView.width = value } + override var y: Double + get() = this@GameView.height + set(value) { this@GameView.height = value} + } + set(value) { + x = value.x + y = value.y } override var ratio: Double = height/width @@ -67,13 +95,6 @@ class GameView(data: GameSetup): View { } var lockRatio = true - override val center: DynamicVector = DynamicVector.create(x + width/2.0, y + height/2.0) - get() { - field.x = x + width/2.0 - field.y = y + height/2.0 - return field - } - private val gameMatrices: GameMatrices get() = Game.matrices as GameMatrices } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt new file mode 100644 index 00000000..597edda0 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt @@ -0,0 +1,34 @@ +package com.mechanica.engine.game.view + +import com.mechanica.engine.unit.vector.Vector + +class StaticView( + override val x: Double = 0.0, + override val y: Double = 0.0, + override val width: Double = 1.0, + override val height: Double = 1.0) : View { + + override val xy: Vector = object : Vector { + override val x: Double + get() = this@StaticView.x + override val y: Double + get() = this@StaticView.y + } + + override val wh: Vector = object : Vector { + override val x: Double + get() = this@StaticView.width + override val y: Double + get() = this@StaticView.height + } + + override val center: Vector = object : Vector { + override val x: Double + get() = this@StaticView.x + width/2.0 + override val y: Double + get() = this@StaticView.y + height/2.0 + } + + override val ratio: Double + get() = height/width +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/View.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/View.kt index 8ba8a6f0..267303f4 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/View.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/View.kt @@ -7,6 +7,8 @@ interface View { val height: Double val x: Double val y: Double + val xy: Vector + val wh: Vector val bottom: Double get() = y - height/2.0 @@ -20,4 +22,12 @@ interface View { val center: Vector val ratio: Double + get() = width/height + + companion object { + fun create(x: Double = 0.0, + y: Double = 0.0, + width: Double = 1.0, + height: Double = 1.0): View = StaticView(x, y, width, height) + } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt deleted file mode 100644 index 685b34ba..00000000 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/ControlsMap.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mechanica.engine.input - -abstract class ControlsMap { - private val map = HashMap>() - - operator fun get(keyId: Int): ArrayList { - val keys = map[keyId] - if (keys != null) { - return keys - } - return arrayListOf(Key(keyId)) - } - - protected fun mapToKeys(vararg keys: Keys): Key { - return Key(*keys) - } - - companion object { - fun getKeyEnum(keyId: Int): Keys? { - for (value in Keys.values()) { - if (keyId == value.id) - return value - } - return null - } - } -} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt index efd24de7..1d55b847 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/Key.kt @@ -1,8 +1,8 @@ package com.mechanica.engine.input -import org.lwjgl.glfw.GLFW +open class Key (label: String, private val condition: () -> Boolean) { + private val label = label.toUpperCase() -open class Key (private val condition: () -> Boolean) { val isDown: Boolean get() { val isPressed = condition() @@ -49,18 +49,22 @@ open class Key (private val condition: () -> Boolean) { return isDown } - constructor(vararg key: Int) : this(keysToBoolean(*key)) - - constructor(vararg keys: Keys) : this( - *(keys.map { it.id }).toIntArray() + constructor(vararg keys: KeyID) : this( + keys[0].label, + keysToBoolean(*keys) ) + override fun toString(): String { + return label + } + companion object { - fun keysToBoolean(vararg key: Int): () -> Boolean { + + fun keysToBoolean(vararg key: KeyID): () -> Boolean { return { var isPressed = false for (i in key.indices) { - isPressed = KeyInput.isPressed(key[i]) || isPressed + isPressed = KeyInput.isPressed(key[i].id) || isPressed } isPressed } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt index 6ea97817..0a51df43 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/Keys.kt @@ -3,7 +3,7 @@ package com.mechanica.engine.input import org.lwjgl.glfw.GLFW.* -enum class Keys(val id: Int) { +enum class Keys(var id: Int) { UNKNOWN(GLFW_KEY_UNKNOWN), M1(GLFW_MOUSE_BUTTON_1), M2(GLFW_MOUSE_BUTTON_2), @@ -138,6 +138,7 @@ enum class Keys(val id: Int) { SCROLL_DOWN(1001), SCROLL(1002); + val label: String get() = glfwGetKeyName(id, 0) ?: "?" companion object { fun uniqueId(): Int { val keys = values().map { it.id }.toTypedArray() diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt index ad2d50d8..129ccad2 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/keyboard/KeyboardImpl.kt @@ -2,105 +2,105 @@ package com.mechanica.engine.input.keyboard import com.mechanica.engine.input.Key import com.mechanica.engine.input.KeyInput -import com.mechanica.engine.input.Keys +import com.mechanica.engine.input.KeyIDs internal class KeyboardImpl : Keyboard { - override val any: Key by lazy { Key { KeyInput.any } } - override val A: Key by lazy { Key(Keys.A) } - override val B: Key by lazy { Key(Keys.B) } - override val C: Key by lazy { Key(Keys.C) } - override val D: Key by lazy { Key(Keys.D) } - override val E: Key by lazy { Key(Keys.E) } - override val F: Key by lazy { Key(Keys.F) } - override val G: Key by lazy { Key(Keys.G) } - override val H: Key by lazy { Key(Keys.H) } - override val I: Key by lazy { Key(Keys.I) } - override val J: Key by lazy { Key(Keys.J) } - override val K: Key by lazy { Key(Keys.K) } - override val L: Key by lazy { Key(Keys.L) } - override val M: Key by lazy { Key(Keys.M) } - override val N: Key by lazy { Key(Keys.N) } - override val O: Key by lazy { Key(Keys.O) } - override val P: Key by lazy { Key(Keys.P) } - override val Q: Key by lazy { Key(Keys.Q) } - override val R: Key by lazy { Key(Keys.R) } - override val S: Key by lazy { Key(Keys.S) } - override val T: Key by lazy { Key(Keys.T) } - override val U: Key by lazy { Key(Keys.U) } - override val V: Key by lazy { Key(Keys.V) } - override val W: Key by lazy { Key(Keys.W) } - override val X: Key by lazy { Key(Keys.X) } - override val Y: Key by lazy { Key(Keys.Y) } - override val Z: Key by lazy { Key(Keys.Z) } + override val any: Key by lazy { Key("any") { KeyInput.any } } + override val A: Key by lazy { Key(KeyIDs.A) } + override val B: Key by lazy { Key(KeyIDs.B) } + override val C: Key by lazy { Key(KeyIDs.C) } + override val D: Key by lazy { Key(KeyIDs.D) } + override val E: Key by lazy { Key(KeyIDs.E) } + override val F: Key by lazy { Key(KeyIDs.F) } + override val G: Key by lazy { Key(KeyIDs.G) } + override val H: Key by lazy { Key(KeyIDs.H) } + override val I: Key by lazy { Key(KeyIDs.I) } + override val J: Key by lazy { Key(KeyIDs.J) } + override val K: Key by lazy { Key(KeyIDs.K) } + override val L: Key by lazy { Key(KeyIDs.L) } + override val M: Key by lazy { Key(KeyIDs.M) } + override val N: Key by lazy { Key(KeyIDs.N) } + override val O: Key by lazy { Key(KeyIDs.O) } + override val P: Key by lazy { Key(KeyIDs.P) } + override val Q: Key by lazy { Key(KeyIDs.Q) } + override val R: Key by lazy { Key(KeyIDs.R) } + override val S: Key by lazy { Key(KeyIDs.S) } + override val T: Key by lazy { Key(KeyIDs.T) } + override val U: Key by lazy { Key(KeyIDs.U) } + override val V: Key by lazy { Key(KeyIDs.V) } + override val W: Key by lazy { Key(KeyIDs.W) } + override val X: Key by lazy { Key(KeyIDs.X) } + override val Y: Key by lazy { Key(KeyIDs.Y) } + override val Z: Key by lazy { Key(KeyIDs.Z) } - override val space: Key by lazy { Key(Keys.SPACE) } - override val shift: Key by lazy { Key(Keys.LSHIFT) } - override val tab: Key by lazy { Key(Keys.TAB) } - override val lAlt: Key by lazy { Key(Keys.LALT) } - override val esc: Key by lazy { Key(Keys.ESC) } - override val comma: Key by lazy { Key(Keys.COMMA) } - override val period: Key by lazy { Key(Keys.PERIOD) } - override val apostrophe: Key by lazy { Key(Keys.APOSTROPHE) } - override val minus: Key by lazy { Key(Keys.MINUS) } - override val slash: Key by lazy { Key(Keys.SLASH) } + override val space: Key by lazy { Key(KeyIDs.SPACE) } + override val shift: Key by lazy { Key(KeyIDs.LSHIFT) } + override val tab: Key by lazy { Key(KeyIDs.TAB) } + override val lAlt: Key by lazy { Key(KeyIDs.LALT) } + override val esc: Key by lazy { Key(KeyIDs.ESC) } + override val comma: Key by lazy { Key(KeyIDs.COMMA) } + override val period: Key by lazy { Key(KeyIDs.PERIOD) } + override val apostrophe: Key by lazy { Key(KeyIDs.APOSTROPHE) } + override val minus: Key by lazy { Key(KeyIDs.MINUS) } + override val slash: Key by lazy { Key(KeyIDs.SLASH) } - override val N0: Key by lazy { Key(Keys.N0) } - override val N1: Key by lazy { Key(Keys.N1) } - override val N2: Key by lazy { Key(Keys.N2) } - override val N3: Key by lazy { Key(Keys.N3) } - override val N4: Key by lazy { Key(Keys.N4) } - override val N5: Key by lazy { Key(Keys.N5) } - override val N6: Key by lazy { Key(Keys.N6) } - override val N7: Key by lazy { Key(Keys.N7) } - override val N8: Key by lazy { Key(Keys.N8) } - override val N9: Key by lazy { Key(Keys.N9) } + override val N0: Key by lazy { Key(KeyIDs.N0) } + override val N1: Key by lazy { Key(KeyIDs.N1) } + override val N2: Key by lazy { Key(KeyIDs.N2) } + override val N3: Key by lazy { Key(KeyIDs.N3) } + override val N4: Key by lazy { Key(KeyIDs.N4) } + override val N5: Key by lazy { Key(KeyIDs.N5) } + override val N6: Key by lazy { Key(KeyIDs.N6) } + override val N7: Key by lazy { Key(KeyIDs.N7) } + override val N8: Key by lazy { Key(KeyIDs.N8) } + override val N9: Key by lazy { Key(KeyIDs.N9) } - override val semicolon: Key by lazy { Key(Keys.SEMICOLON) } - override val equal: Key by lazy { Key(Keys.EQUAL) } - override val leftBracket: Key by lazy { Key(Keys.LEFT_BRACKET) } - override val backslash: Key by lazy { Key(Keys.BACKSLASH) } - override val rightBracket: Key by lazy { Key(Keys.RIGHT_BRACKET) } - override val graveAccent: Key by lazy { Key(Keys.GRAVE_ACCENT) } - override val world1: Key by lazy { Key(Keys.WORLD_1) } - override val world2: Key by lazy { Key(Keys.WORLD_2) } - override val enter: Key by lazy { Key(Keys.ENTER) } - override val backspace: Key by lazy { Key(Keys.BACKSPACE) } - override val insert: Key by lazy { Key(Keys.INSERT) } - override val delete: Key by lazy { Key(Keys.DELETE) } - override val right: Key by lazy { Key(Keys.RIGHT) } - override val left: Key by lazy { Key(Keys.LEFT) } - override val down: Key by lazy { Key(Keys.DOWN) } - override val up: Key by lazy { Key(Keys.UP) } - override val pageUp: Key by lazy { Key(Keys.PAGE_UP) } - override val pageDown: Key by lazy { Key(Keys.PAGE_DOWN) } - override val home: Key by lazy { Key(Keys.HOME) } - override val end: Key by lazy { Key(Keys.END) } - override val capsLock: Key by lazy { Key(Keys.CAPS_LOCK) } - override val scrollLock: Key by lazy { Key(Keys.SCROLL_LOCK) } - override val numLock: Key by lazy { Key(Keys.NUM_LOCK) } - override val printScreen: Key by lazy { Key(Keys.PRINT_SCREEN) } - override val pause: Key by lazy { Key(Keys.PAUSE) } + override val semicolon: Key by lazy { Key(KeyIDs.SEMICOLON) } + override val equal: Key by lazy { Key(KeyIDs.EQUAL) } + override val leftBracket: Key by lazy { Key(KeyIDs.LEFT_BRACKET) } + override val backslash: Key by lazy { Key(KeyIDs.BACKSLASH) } + override val rightBracket: Key by lazy { Key(KeyIDs.RIGHT_BRACKET) } + override val graveAccent: Key by lazy { Key(KeyIDs.GRAVE_ACCENT) } + override val world1: Key by lazy { Key(KeyIDs.WORLD_1) } + override val world2: Key by lazy { Key(KeyIDs.WORLD_2) } + override val enter: Key by lazy { Key(KeyIDs.ENTER) } + override val backspace: Key by lazy { Key(KeyIDs.BACKSPACE) } + override val insert: Key by lazy { Key(KeyIDs.INSERT) } + override val delete: Key by lazy { Key(KeyIDs.DELETE) } + override val right: Key by lazy { Key(KeyIDs.RIGHT) } + override val left: Key by lazy { Key(KeyIDs.LEFT) } + override val down: Key by lazy { Key(KeyIDs.DOWN) } + override val up: Key by lazy { Key(KeyIDs.UP) } + override val pageUp: Key by lazy { Key(KeyIDs.PAGE_UP) } + override val pageDown: Key by lazy { Key(KeyIDs.PAGE_DOWN) } + override val home: Key by lazy { Key(KeyIDs.HOME) } + override val end: Key by lazy { Key(KeyIDs.END) } + override val capsLock: Key by lazy { Key(KeyIDs.CAPS_LOCK) } + override val scrollLock: Key by lazy { Key(KeyIDs.SCROLL_LOCK) } + override val numLock: Key by lazy { Key(KeyIDs.NUM_LOCK) } + override val printScreen: Key by lazy { Key(KeyIDs.PRINT_SCREEN) } + override val pause: Key by lazy { Key(KeyIDs.PAUSE) } - override val F1: Key by lazy { Key(Keys.F1) } - override val F2: Key by lazy { Key(Keys.F2) } - override val F3: Key by lazy { Key(Keys.F3) } - override val F4: Key by lazy { Key(Keys.F4) } - override val F5: Key by lazy { Key(Keys.F5) } - override val F6: Key by lazy { Key(Keys.F6) } - override val F7: Key by lazy { Key(Keys.F7) } - override val F8: Key by lazy { Key(Keys.F8) } - override val F9: Key by lazy { Key(Keys.F9) } - override val F10: Key by lazy { Key(Keys.F10) } - override val F11: Key by lazy { Key(Keys.F11) } - override val F12: Key by lazy { Key(Keys.F12) } + override val F1: Key by lazy { Key(KeyIDs.F1) } + override val F2: Key by lazy { Key(KeyIDs.F2) } + override val F3: Key by lazy { Key(KeyIDs.F3) } + override val F4: Key by lazy { Key(KeyIDs.F4) } + override val F5: Key by lazy { Key(KeyIDs.F5) } + override val F6: Key by lazy { Key(KeyIDs.F6) } + override val F7: Key by lazy { Key(KeyIDs.F7) } + override val F8: Key by lazy { Key(KeyIDs.F8) } + override val F9: Key by lazy { Key(KeyIDs.F9) } + override val F10: Key by lazy { Key(KeyIDs.F10) } + override val F11: Key by lazy { Key(KeyIDs.F11) } + override val F12: Key by lazy { Key(KeyIDs.F12) } - override val lShift: Key by lazy { Key(Keys.LSHIFT) } - override val lCtrl: Key by lazy { Key(Keys.LCTRL) } - override val ctrl: Key by lazy { Key(Keys.LCTRL, Keys.RCTRL) } - override val leftSuper: Key by lazy { Key(Keys.LEFT_SUPER) } - override val rShift: Key by lazy { Key(Keys.RSHIFT) } - override val rCtrl: Key by lazy { Key(Keys.RCTRL) } - override val rAlt: Key by lazy { Key(Keys.RALT) } - override val rightSuper: Key by lazy { Key(Keys.RIGHT_SUPER) } - override val menu: Key by lazy { Key(Keys.MENU) } + override val lShift: Key by lazy { Key(KeyIDs.LSHIFT) } + override val lCtrl: Key by lazy { Key(KeyIDs.LCTRL) } + override val ctrl: Key by lazy { Key(KeyIDs.LCTRL, KeyIDs.RCTRL) } + override val leftSuper: Key by lazy { Key(KeyIDs.LEFT_SUPER) } + override val rShift: Key by lazy { Key(KeyIDs.RSHIFT) } + override val rCtrl: Key by lazy { Key(KeyIDs.RCTRL) } + override val rAlt: Key by lazy { Key(KeyIDs.RALT) } + override val rightSuper: Key by lazy { Key(KeyIDs.RIGHT_SUPER) } + override val menu: Key by lazy { Key(KeyIDs.MENU) } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt index ec6a25ee..2dc17546 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt @@ -2,6 +2,7 @@ package com.mechanica.engine.input.mouse import com.mechanica.engine.context.callbacks.MouseHandler import com.mechanica.engine.input.Key +import com.mechanica.engine.input.KeyID import com.mechanica.engine.input.Keys import com.mechanica.engine.unit.vector.Vector @@ -22,7 +23,7 @@ interface Mouse { val world: Vector val ui: Vector - class ScrollWheel(vararg keys: Keys): Key(*keys) { + class ScrollWheel(vararg keys: KeyID): Key(*keys) { val distance: Double get() = MouseHandler.scrollY } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt index 4609b789..b5ba6d6a 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt @@ -4,21 +4,21 @@ import com.mechanica.engine.context.callbacks.MouseHandler import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.game.Game import com.mechanica.engine.input.Key -import com.mechanica.engine.input.Keys +import com.mechanica.engine.input.KeyIDs internal class MouseImpl : Mouse { - override val MB1 = Key(Keys.M1) - override val MB2 = Key(Keys.M2) - override val MMB = Key(Keys.M3) - override val M4 = Key(Keys.M4) - override val M5 = Key(Keys.M5) - override val M6 = Key(Keys.M6) - override val M7 = Key(Keys.M7) - override val M8 = Key(Keys.M8) - - override val scroll = Mouse.ScrollWheel(Keys.SCROLL) - override val scrollDown = Mouse.ScrollWheel(Keys.SCROLL_DOWN) - override val scrollUp = Mouse.ScrollWheel(Keys.SCROLL_UP) + override val MB1 = Key(KeyIDs.M1) + override val MB2 = Key(KeyIDs.M2) + override val MMB = Key(KeyIDs.M3) + override val M4 = Key(KeyIDs.M4) + override val M5 = Key(KeyIDs.M5) + override val M6 = Key(KeyIDs.M6) + override val M7 = Key(KeyIDs.M7) + override val M8 = Key(KeyIDs.M8) + + override val scroll = Mouse.ScrollWheel(KeyIDs.SCROLL) + override val scrollDown = Mouse.ScrollWheel(KeyIDs.SCROLL_DOWN) + override val scrollUp = Mouse.ScrollWheel(KeyIDs.SCROLL_UP) private val worldRatio: Vector = object : Vector { override val x: Double diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt index 89becc74..d60ab44f 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt @@ -16,36 +16,30 @@ internal class SceneManager : Scene() { get() = Game.view var updateVar: ((Double) -> Unit)? = null - set(value) { - field = value - if (value != null) { - updateAndRenderVar = null - } - } - var updateAndRenderVar: ((Double, Drawer) -> Unit)? = null - set(value) { - field = value - if (value != null) { - updateVar = null - } - } private var sceneSetter: () -> MainScene? = { null } var currentScene: MainScene? = null - get() { - val first = if (childScenes.isNotEmpty()) childScenes.first() else null - return if (first is MainScene) first else null - } private set(value) { - val children = childScenes as ArrayList - if (value != null) { - when { - children.isEmpty() -> super.addScene(value) - children[0] === field -> children[0] = value - else -> children.add(0, value) - } + val scene = currentScene + + if (value == null) { + if (scene != null) removeScene(scene) + field = null + return } - field = value + + if (scene == null) { + addScene(value) + field = value + return + } + + val new = replaceScene(scene, value) + if (new === scene) { + addScene(value) + } + + field = new } private var scheduleSceneChange = true @@ -105,9 +99,7 @@ internal class SceneManager : Scene() { DebugDrawer.render(getDrawer()) } - override fun render(draw: Drawer) { - updateAndRenderVar?.invoke(updateDuration, draw) - } + override fun render(draw: Drawer) { } private fun checkStateChange() { if (scheduleSceneChange) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt new file mode 100644 index 00000000..50328347 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt @@ -0,0 +1,9 @@ +package com.mechanica.engine.scenes.processes + +import com.mechanica.engine.input.keyboard.Keyboard +import com.mechanica.engine.input.mouse.Mouse + +abstract class InputProcess(priority: Int = -1) : Process(priority) { + val keyboard = Keyboard.create() + val mouse = Mouse.create() +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt index 1ef2452a..1f420d84 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt @@ -1,24 +1,31 @@ package com.mechanica.engine.scenes.processes -abstract class Process : ProcessNode { +import kotlin.math.max + +abstract class Process(override val priority: Int = 0) : ProcessNode { protected val childProcesses: List = ArrayList() - override fun addProcess(process: P): P { - (childProcesses as ArrayList).add(process) + final override fun addProcess(process: P): P { + val processes = (childProcesses as ArrayList) + + processes.add(process) + processes.sortBy { it.priority } return process } - override fun removeProcess(process: ProcessNode): Boolean { - process.destructor() + final override fun removeProcess(process: ProcessNode): Boolean { + process.destructNodes() return (childProcesses as ArrayList).remove(process) } - override fun replaceProcess(old: P, new: P): P { - val index = childProcesses.indexOf(old) + final override fun replaceProcess(old: P, new: P): P { + val processes = (childProcesses as ArrayList) + val index = processes.indexOf(old) if (index != -1) { - childProcesses[index].destructor() - (childProcesses as ArrayList)[index] = new + removeProcess(processes[index]) + processes.add(index, new) + processes.sortBy { it.priority } return new } return old @@ -31,10 +38,25 @@ abstract class Process : ProcessNode { } override fun updateNodes(delta: Double) { + val index = updateNodesFor(delta) { it.priority < 0 } this.update(delta) + updateNodesFor(delta, index) { it.priority >= 0 } + } + + private inline fun updateNodesFor(delta: Double, from: Int = 0, condition: (ProcessNode) -> Boolean): Int { + var i = from + do { + val process = childProcesses.getOrNull(i++) ?: break + process.updateNodes(delta) + } while (condition(process)) + return i + } + + override fun destructNodes() { for (i in childProcesses.indices) { - childProcesses[i].updateNodes(delta) + childProcesses[i].destructNodes() } + destructor() } override fun destructor() { } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt index 94fe97e3..551e775f 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt @@ -1,9 +1,12 @@ package com.mechanica.engine.scenes.processes interface ProcessNode : Updateable { + val priority: Int + get() = 0 fun addProcess(process: P): P fun removeProcess(process: ProcessNode): Boolean fun replaceProcess(old: P, new: P): P fun updateNodes(delta: Double) fun destructor() + fun destructNodes() } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt new file mode 100644 index 00000000..3d68ff66 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt @@ -0,0 +1,74 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.view.DynamicView +import com.mechanica.engine.game.view.View +import com.mechanica.engine.unit.vector.DynamicVector +import com.mechanica.engine.unit.vector.Vector + +abstract class DynamicScene( + x: Double = 0.0, + y: Double = 0.0, + width: Double = 1.0, + height: Double = 1.0, + priority: Int = 0) : Scene(priority) { + + constructor( + view: View, + priority: Int = 0) : this(view.x, view.y, view.width, view.height, priority) + + open val position = DynamicVector.create(x + width/2.0, y + height/2.0) + + override val view: DynamicView = DynamicSceneView(width, height) + + override fun drawInScene(draw: Drawer, view: View): Drawer { + return draw.centered.transformed.translate(position).scale(view.wh) + } + + inner class DynamicSceneView( + override var width: Double, + override var height: Double) : DynamicView { + + override var x: Double + get() = center.x - width/2.0 + set(value) { center.x = value + width/2.0} + override var y: Double + get() = center.y - height/2.0 + set(value) {center.y = value + height/2.0} + + @Suppress("SetterBackingFieldAssignment") + override var center: DynamicVector = object : DynamicVector by position {} + set(value) { + position.x = value.x + position.y = value.y + } + + @Suppress("SetterBackingFieldAssignment") + override var xy: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@DynamicSceneView.width + set(value) {this@DynamicSceneView.width = value } + override var y: Double + get() = this@DynamicSceneView.height + set(value) { this@DynamicSceneView.height = value} + } + set(value) { + x = value.x + y = value.y + } + + @Suppress("SetterBackingFieldAssignment") + override var wh: DynamicVector = object : DynamicVector { + override var x: Double + get() = this@DynamicSceneView.width + set(value) {this@DynamicSceneView.width = value } + override var y: Double + get() = this@DynamicSceneView.height + set(value) { this@DynamicSceneView.height = value} + } + set(value) { + width = value.x + height = value.y + } + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt index 3234506b..d919ec77 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt @@ -7,6 +7,8 @@ import com.mechanica.engine.game.view.View abstract class GUIScene : Scene() { override val view: View get() = Game.ui - override val Drawer.inScene: Drawer - get() = drawInScene(this, view).ui + + override fun drawInScene(draw: Drawer, view: View): Drawer { + return super.drawInScene(draw, view).ui + } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt index 6d4f7985..1562d1d2 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt @@ -3,7 +3,10 @@ package com.mechanica.engine.scenes.scenes import com.mechanica.engine.game.Game import com.mechanica.engine.game.view.GameView -abstract class MainScene : Scene() { +abstract class MainScene(priority: Int = 0) : Scene(priority) { override val view: GameView get() = Game.view + + fun setNewMainScene(setter: () -> MainScene?) = Game.setMainScene(setter) + } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt index 59eb2bff..b68c0746 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt @@ -4,28 +4,38 @@ import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.view.View import com.mechanica.engine.scenes.processes.Process -abstract class Scene : Process(), SceneNode { +abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { protected val childScenes: List = ArrayList() - protected open val Drawer.inScene: Drawer + protected val Drawer.inScene: Drawer get() = drawInScene(this, view) - override fun addScene(scene: S): S { - (childScenes as ArrayList).add(scene) + protected open fun drawInScene(draw: Drawer, view: View): Drawer = draw.transformed.translate(view.x, view.y) + + final override fun addScene(scene: S): S { + addProcess(scene) + val scenes = (childScenes as ArrayList) + + scenes.add(scene) + scenes.sortBy { it.priority } return scene } - override fun removeScene(scene: SceneNode): Boolean { - scene.destructor() + final override fun removeScene(scene: SceneNode): Boolean { + removeProcess(scene) return (childScenes as ArrayList).remove(scene) } - override fun replaceScene(old: S, new: S): S { - val index = childScenes.indexOf(old) + final override fun replaceScene(old: S, new: S): S { + replaceProcess(old, new) + + val scenes = (childScenes as ArrayList) + val index = scenes.indexOf(old) if (index != -1) { - childScenes[index].destructor() - (childScenes as ArrayList)[index] = new + scenes.removeAt(index) + scenes.add(index, new) + scenes.sortBy { it.priority } return new } return old @@ -37,18 +47,28 @@ abstract class Scene : Process(), SceneNode { } } - override fun updateNodes(delta: Double) { - super.updateNodes(delta) - for (i in childScenes.indices) { - childScenes[i].updateNodes(delta) - } - } - override fun renderNodes(draw: Drawer) { + val index = renderNodesFor(draw) { it.priority < 0} this.render(draw) + renderNodesFor(draw, index) { it.priority >= 0} + } + + private inline fun renderNodesFor(draw: Drawer, from: Int = 0, condition: (SceneNode) -> Boolean): Int { + var i = from + do { + val scene = childScenes.getOrNull(i++) ?: break + scene.renderNodes(draw) + } while (condition(scene)) + return i + } + + override fun update(delta: Double) { } + + override fun destructNodes() { for (i in childScenes.indices) { - childScenes[i].renderNodes(draw) + childScenes[i].destructNodes() } + super.destructNodes() } @Suppress("PropertyName") @@ -56,7 +76,4 @@ abstract class Scene : Process(), SceneNode { internal val `access$childScenes`: List get() = childScenes - companion object { - internal fun drawInScene(draw: Drawer, view: View): Drawer = draw.transformed.translate(view.x, view.y).layout.origin() - } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt new file mode 100644 index 00000000..d51b1f4f --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt @@ -0,0 +1,22 @@ +package com.mechanica.engine.scenes.scenes + +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.view.DynamicView +import com.mechanica.engine.game.view.View +import com.mechanica.engine.unit.vector.Vector +import com.mechanica.engine.unit.vector.vec + +abstract class StaticScene(final override val view: View, priority: Int = 0) : Scene(priority) { + constructor( + x: Double = 0.0, + y: Double = 0.0, + width: Double = 1.0, + height: Double = 1.0, + priority: Int = 0) : this(View.create(x, y, width, height), priority) + + val position: Vector by lazy { vec(view.xy) } + + override fun drawInScene(draw: Drawer, view: View): Drawer { + return draw.centered.transformed.translate(position).scale(view.wh) + } +} \ No newline at end of file From fdb70bf1e33c687a17a731e46f6417fa1c6c88ae Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Tue, 9 Jun 2020 01:03:51 +0100 Subject: [PATCH 07/21] Added ExclusiveActivationMap.kt for processes that need to be run exclusively Added monitor and window to the application-interface module --- .../mechanica/engine/context/GLInitializer.kt | 11 +++ .../engine/context/loader/DisplayLoader.kt | 17 ++++ .../com/mechanica/engine/display/Monitor.kt | 27 ++++++ .../com/mechanica/engine/display/Window.kt | 65 +++++++++++++ .../engine/util/extensions/Iterables.kt | 18 ++++ .../com/mechanica/engine/context/GLContext.kt | 4 + .../mechanica/engine/context/GLFWContext.kt | 3 +- .../context/loader/LwjglDisplayLoader.kt | 27 ++++++ .../engine/context/loader/LwjglLoader.kt | 1 + .../display/{Monitor.kt => GLFWMonitor.kt} | 32 ++++--- .../display/{Window.kt => GLFWWindow.kt} | 96 +++++++++---------- .../com/mechanica/engine/drawer/DrawData.kt | 13 +++ .../drawer/superclass/path/PathDrawerImpl.kt | 6 +- .../game/configuration/ConfigurationData.kt | 1 - .../game/configuration/GameConfiguration.kt | 1 - .../configuration/GameConfigurationImpl.kt | 4 - .../engine/game/configuration/GameSetup.kt | 14 +-- .../NullableConfigurationData.kt | 1 - .../mechanica/engine/scenes/SceneManager.kt | 13 +-- .../exclusiveScenes/ExclusiveActivationMap.kt | 48 ++++++++++ .../engine/scenes/processes/Process.kt | 58 ++++++++++- .../engine/scenes/processes/ProcessNode.kt | 2 + .../engine/scenes/scenes/DynamicScene.kt | 6 +- .../engine/scenes/scenes/LoadScene.kt | 8 +- .../mechanica/engine/scenes/scenes/Scene.kt | 30 +++++- 25 files changed, 404 insertions(+), 102 deletions(-) create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/context/loader/DisplayLoader.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/display/Monitor.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/display/Window.kt create mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglDisplayLoader.kt rename desktop-application/src/main/kotlin/com/mechanica/engine/display/{Monitor.kt => GLFWMonitor.kt} (79%) rename desktop-application/src/main/kotlin/com/mechanica/engine/display/{Window.kt => GLFWWindow.kt} (74%) create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt index 90a4f063..5de13231 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/GLInitializer.kt @@ -1,6 +1,7 @@ package com.mechanica.engine.context import com.mechanica.engine.context.callbacks.EventCallbacks +import com.mechanica.engine.context.loader.DisplayLoader import com.mechanica.engine.context.loader.GLLoader object GLInitializer { @@ -8,6 +9,16 @@ object GLInitializer { internal val loader: GLLoader get() = _loader ?: throw UninitializedPropertyAccessException("The OpenGL context has not been initialized") + private var _displayLoader: DisplayLoader? = null + internal val displayLoader: DisplayLoader + get() = _displayLoader ?: throw UninitializedPropertyAccessException("The OpenGL context has not been initialized") + + fun initializeDisplay(loader: DisplayLoader) { + if (_loader == null) { + _displayLoader = loader + } else throw IllegalStateException("The Display context has already been initialized") + } + fun initialize(loader: GLLoader): EventCallbacks { if (_loader == null) { _loader = loader diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/DisplayLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/DisplayLoader.kt new file mode 100644 index 00000000..37ddd905 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/DisplayLoader.kt @@ -0,0 +1,17 @@ +package com.mechanica.engine.context.loader + +import com.mechanica.engine.context.GLInitializer +import com.mechanica.engine.display.Monitor +import com.mechanica.engine.display.Window + +interface DisplayLoader { + fun createWindow(title: String, width: Int, height: Int): Window + fun createWindow(title: String, monitor: Monitor): Window + fun createWindow(title: String, width: Int, height: Int, monitor: Monitor): Window + + val allMonitors: Array + + fun getPrimaryMonitor(): Monitor + + companion object : DisplayLoader by GLInitializer.displayLoader +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/display/Monitor.kt b/application-interface/src/main/kotlin/com/mechanica/engine/display/Monitor.kt new file mode 100644 index 00000000..6f9114b9 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/display/Monitor.kt @@ -0,0 +1,27 @@ +package com.mechanica.engine.display + +import com.mechanica.engine.context.loader.DisplayLoader +import com.mechanica.engine.context.loader.GLLoader + + +interface Monitor { + val id: Long + val name: String + val size: Size + val contentScale: ContentScale + + val width: Int + val height: Int + + data class Size(val width_mm: Int, val height_mm: Int) + data class ContentScale(val xScale: Float, val yScale: Float) + + companion object { + val allMonitors: Array + get() = DisplayLoader.allMonitors + + fun getPrimaryMonitor(): Monitor { + return DisplayLoader.getPrimaryMonitor() + } + } +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/display/Window.kt b/application-interface/src/main/kotlin/com/mechanica/engine/display/Window.kt new file mode 100644 index 00000000..a69701e0 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/display/Window.kt @@ -0,0 +1,65 @@ +package com.mechanica.engine.display + +import com.mechanica.engine.context.loader.DisplayLoader +import com.mechanica.engine.context.loader.GLLoader +import com.mechanica.engine.resources.Resource +import java.nio.ByteBuffer + +interface Window { + val title: String + val id: Long + val width: Int + val height: Int + val aspectRatio: Double + var isFocused: Boolean + var isIconified: Boolean + var isMaximized: Boolean + val isHovered: Boolean + var isVisible: Boolean + var isResizable: Boolean + var isDecorated: Boolean + var isFloating: Boolean + var vSync: Boolean + var opacity: Float + val position: Position + val resolution: Dimension + val size: Dimension + val isResizing: Boolean + var shouldClose: Boolean + + fun addRefreshCallback(callback: (Window) -> Unit) + fun requestAttention() + fun setIcon(resource: Resource) + fun setIcon(width: Int, height: Int, imageBuffer: ByteBuffer) + fun setFullscreen(monitor: Monitor) + fun setFullscreen(monitor: Monitor, width: Int, height: Int, refreshRate: Int = 60) + fun exitFullscreen() + fun destroy() + fun update(): Boolean + + interface Dimension { + val width: Int + val height: Int + } + + interface Position { + var x: Int + var y: Int + + fun set(x: Int, y: Int) + } + + companion object { + fun create(title: String, width: Int, height: Int): Window { + return DisplayLoader.createWindow(title, width, height) + } + + fun create(title: String, monitor: Monitor): Window { + return DisplayLoader.createWindow(title, monitor) + } + + fun create(title: String, width: Int, height: Int, monitor: Monitor): Window { + return DisplayLoader.createWindow(title, width, height, monitor) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/util/extensions/Iterables.kt b/common/src/main/kotlin/com/mechanica/engine/util/extensions/Iterables.kt index 1a1071f6..4cfa5e58 100644 --- a/common/src/main/kotlin/com/mechanica/engine/util/extensions/Iterables.kt +++ b/common/src/main/kotlin/com/mechanica/engine/util/extensions/Iterables.kt @@ -14,4 +14,22 @@ fun Iterable.containsPoint(point: Vector): Boolean { prev = vec } return result +} + +/** + * A version of for each that should be more efficient for lists that + * are fast at getting element with random access + * + * @param action action that should be performed on each element + */ +inline fun List.fori(action: (E) -> Unit) { + for (i in this.indices) { + action(this[i]) + } +} + +inline fun List.foriIndexed(action: (E, Int) -> Unit) { + for (i in this.indices) { + action(this[i], i) + } } \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt index 625037c0..a13dd4ed 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLContext.kt @@ -22,6 +22,9 @@ object GLContext : Version { override val version: Double get() = parsedVersionString?.version ?: throw IllegalStateException(errorMessage) + var initialized = false + private set + fun isExtensionSupported(extension: String): Boolean { return extensionMap.containsKey(extension) } @@ -47,6 +50,7 @@ object GLContext : Version { GL11.glEnable(GL11.GL_STENCIL_TEST) enableAlphaBlending() + initialized = true } private fun initContext(windowId: Long) { diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLFWContext.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLFWContext.kt index 59a72dc3..329d3e3a 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLFWContext.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/GLFWContext.kt @@ -8,7 +8,8 @@ object GLFWContext : Version { override val minorVersion: Int override val version: Double - private var initialized = false + var initialized = false + private set private var _samples = 4 val samples: Int diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglDisplayLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglDisplayLoader.kt new file mode 100644 index 00000000..2dc116f4 --- /dev/null +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglDisplayLoader.kt @@ -0,0 +1,27 @@ +package com.mechanica.engine.context.loader + +import com.mechanica.engine.display.GLFWMonitor +import com.mechanica.engine.display.GLFWWindow +import com.mechanica.engine.display.Monitor +import com.mechanica.engine.display.Window + +class LwjglDisplayLoader : DisplayLoader { + override fun createWindow(title: String, width: Int, height: Int): Window { + return GLFWWindow.create(title, width, height) + } + + override fun createWindow(title: String, monitor: Monitor): Window { + return GLFWWindow.create(title, monitor) + } + + override fun createWindow(title: String, width: Int, height: Int, monitor: Monitor): Window { + return GLFWWindow.create(title, width, height, monitor) + } + + override val allMonitors: Array + get() = GLFWMonitor.allMonitors + + override fun getPrimaryMonitor(): Monitor { + return GLFWMonitor.getPrimaryMonitor() + } +} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt index 834f75b3..6d06b614 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt @@ -1,5 +1,6 @@ package com.mechanica.engine.context.loader +import com.mechanica.engine.display.GLFWWindow import com.mechanica.engine.shader.qualifiers.AttributeQualifier import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.script.Shader diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/display/Monitor.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/display/GLFWMonitor.kt similarity index 79% rename from desktop-application/src/main/kotlin/com/mechanica/engine/display/Monitor.kt rename to desktop-application/src/main/kotlin/com/mechanica/engine/display/GLFWMonitor.kt index dd0a3365..71270e5f 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/display/Monitor.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/display/GLFWMonitor.kt @@ -7,9 +7,14 @@ import org.lwjgl.glfw.GLFWMonitorCallback import org.lwjgl.glfw.GLFWVidMode import org.lwjgl.system.MemoryStack -class Monitor private constructor(val id: Long) { +class GLFWMonitor private constructor(override val id: Long) : Monitor { - val name by lazy { glfwGetMonitorName(id) ?: "N/A" } + override val name by lazy { glfwGetMonitorName(id) ?: "N/A" } + + override val width: Int + get() = currentVideoMode.width() + override val height: Int + get() = currentVideoMode.height() var monitorUserPointer: Long get() = glfwGetMonitorUserPointer(id) @@ -19,6 +24,7 @@ class Monitor private constructor(val id: Long) { val currentVideoMode: GLFWVidMode get() = glfwGetVideoMode(id) ?: throw IllegalStateException("Error retrieving the current Vid Mode") + val supportedVideoModes: Array get() { val vidmodes = glfwGetVideoModes(id) @@ -29,7 +35,7 @@ class Monitor private constructor(val id: Long) { } } - val size: Size + override val size: Monitor.Size get() { var width = -1 var height = -1 @@ -40,10 +46,10 @@ class Monitor private constructor(val id: Long) { width = widthBuffer[0] height = heightBuffer[0] } - return Size(width, height) + return Monitor.Size(width, height) } - val contentScale: ContentScale + override val contentScale: Monitor.ContentScale get() { var xScale = 1f var yScale = 1f @@ -54,7 +60,7 @@ class Monitor private constructor(val id: Long) { xScale = xBuffer[0] yScale = yBuffer[0] } - return ContentScale(xScale, yScale) + return Monitor.ContentScale(xScale, yScale) } var gammaRamp: GLFWGammaRamp @@ -71,10 +77,6 @@ class Monitor private constructor(val id: Long) { field = value } - data class Size(val width_mm: Int, val height_mm: Int) - - data class ContentScale(val xScale: Float, val yScale: Float) - companion object { var allMonitors = createMonitorsArray() private set @@ -83,22 +85,22 @@ class Monitor private constructor(val id: Long) { return field } - fun getPrimaryMonitor(): Monitor { + fun getPrimaryMonitor(): GLFWMonitor { GLFWContext.initialize() - return Monitor(glfwGetPrimaryMonitor()) + return GLFWMonitor(glfwGetPrimaryMonitor()) } - private fun createMonitorsArray(): Array { + private fun createMonitorsArray(): Array { GLFWContext.initialize() val pointers = glfwGetMonitors() return if (pointers != null) { - Array(pointers.limit()) {Monitor(pointers[it])} + Array(pointers.limit()) {GLFWMonitor(pointers[it])} } else { emptyArray() } } - private fun checkMonitors(monitors: Array): Array { + private fun checkMonitors(monitors: Array): Array { val pointers = glfwGetMonitors() if (pointers != null) { val pointerSize = pointers.limit() diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/display/Window.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/display/GLFWWindow.kt similarity index 74% rename from desktop-application/src/main/kotlin/com/mechanica/engine/display/Window.kt rename to desktop-application/src/main/kotlin/com/mechanica/engine/display/GLFWWindow.kt index b0d16cbb..ee10dc95 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/display/Window.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/display/GLFWWindow.kt @@ -1,8 +1,8 @@ package com.mechanica.engine.display +import com.mechanica.engine.context.GLContext import com.mechanica.engine.context.GLFWContext import com.mechanica.engine.context.callbacks.EventCallbacks -import com.mechanica.engine.context.callbacks.KeyboardHandler import com.mechanica.engine.utils.ImageData import org.lwjgl.glfw.Callbacks import org.lwjgl.glfw.GLFW.* @@ -12,60 +12,62 @@ import com.mechanica.engine.resources.Resource import java.nio.ByteBuffer -class Window private constructor(width: Int, height: Int, val title: String, monitor: Monitor?) { - val id: Long= glfwCreateWindow(width, height, title, monitor?.id ?: MemoryUtil.NULL, MemoryUtil.NULL) +class GLFWWindow private constructor(width: Int, height: Int, override val title: String, monitor: Monitor?) : Window { + override val id: Long= glfwCreateWindow(width, height, title, monitor?.id ?: MemoryUtil.NULL, MemoryUtil.NULL) var hasInitialized = false private set - val width: Int + override val width: Int get() = resolution.width - val height: Int + override val height: Int get() = resolution.height - val aspectRatio: Double + override val aspectRatio: Double get() = width.toDouble()/height.toDouble() - var isFocused: Boolean + override var isFocused: Boolean get() = glfwGetWindowAttrib(id, GLFW_FOCUSED) == GLFW_TRUE set(value) { if (value) glfwFocusWindow(id) } - var isIconified: Boolean + override var isIconified: Boolean get() = glfwGetWindowAttrib(id, GLFW_ICONIFIED ) == GLFW_TRUE set(value) { if (value) glfwIconifyWindow(id) else glfwRestoreWindow(id) } - var isMaximized: Boolean + override var isMaximized: Boolean get() = glfwGetWindowAttrib(id, GLFW_MAXIMIZED ) == GLFW_TRUE set(value) { if (value) glfwMaximizeWindow(id) else glfwRestoreWindow(id) } - val isHovered: Boolean + override val isHovered: Boolean get() = glfwGetWindowAttrib(id, GLFW_HOVERED ) == GLFW_TRUE - var isVisible: Boolean + override var isVisible: Boolean get() = glfwGetWindowAttrib(id, GLFW_VISIBLE ) == GLFW_TRUE set(value) { if (value) glfwShowWindow(id) else glfwHideWindow(id) } - var isResizable: Boolean + override var isResizable: Boolean get() = glfwGetWindowAttrib(id, GLFW_RESIZABLE ) == 1 set(value) { glfwSetWindowAttrib(id, GLFW_RESIZABLE, if (value) GLFW_TRUE else GLFW_FALSE ) } - var isDecorated: Boolean + override var isDecorated: Boolean get() = glfwGetWindowAttrib(id, GLFW_DECORATED ) == 1 set(value) { glfwSetWindowAttrib(id, GLFW_DECORATED, if (value) GLFW_TRUE else GLFW_FALSE ) } - var isFloating: Boolean + override var isFloating: Boolean get() = glfwGetWindowAttrib(id, GLFW_FLOATING ) == 1 set(value) { glfwSetWindowAttrib(id, GLFW_FLOATING, if (value) GLFW_TRUE else GLFW_FALSE ) } - var shouldClose: Boolean + override var shouldClose: Boolean get() = glfwWindowShouldClose(id) set(value) { glfwSetWindowShouldClose(id, value) } - var vSync: Boolean = true + override var vSync: Boolean = true set(value) { - glfwSwapInterval(if (value) 1 else 0) + if (GLContext.initialized) { + glfwSwapInterval(if (value) 1 else 0) + } field = value } private var finished = false - var opacity: Float + override var opacity: Float get() = glfwGetWindowOpacity(id) set(value) = glfwSetWindowOpacity(id, value) @@ -73,7 +75,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon get() { val monitor = field val monitorId = glfwGetWindowMonitor(id) - val foundMonitor = Monitor.allMonitors.firstOrNull { it.id == monitorId } + val foundMonitor = GLFWMonitor.allMonitors.firstOrNull { it.id == monitorId } return if (monitorId == MemoryUtil.NULL){ field = null null @@ -90,17 +92,17 @@ class Window private constructor(width: Int, height: Int, val title: String, mon field = value } - val position: Position = Position() + override val position: PositionImpl = PositionImpl() - val resolution: Dimension by lazy { + override val resolution: Window.Dimension by lazy { setResolution(DimensionImpl(0, 0, false)) } - val size: Dimension by lazy { + override val size: Window.Dimension by lazy { setWindowSize(DimensionImpl(0, 0, false)) } - val isResizing: Boolean + override val isResizing: Boolean get() { val resolution = (resolution as DimensionImpl) val size = (size as DimensionImpl) @@ -132,7 +134,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon } - fun addRefreshCallback(callback: (Window) -> Unit) { + override fun addRefreshCallback(callback: (Window) -> Unit) { callbackList.add(callback) } @@ -159,7 +161,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon } } - fun update(): Boolean { + override fun update(): Boolean { swapBuffers() EventCallbacks.prepare() pollEvents() @@ -170,7 +172,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon return !shouldClose } - fun destroy() { + override fun destroy() { if (hasInitialized) { Callbacks.glfwFreeCallbacks(id) glfwDestroyWindow(id) @@ -186,11 +188,11 @@ class Window private constructor(width: Int, height: Int, val title: String, mon glfwPollEvents() } - fun requestAttention() { + override fun requestAttention() { glfwRequestWindowAttention(id) } - private fun setWindowSize(out: DimensionImpl): Dimension { + private fun setWindowSize(out: DimensionImpl): Window.Dimension { val widthArray = IntArray(1) val heightArray = IntArray(1) glfwGetWindowSize(id, widthArray, heightArray) @@ -199,7 +201,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon return out } - private fun setResolution(out: DimensionImpl): Dimension { + private fun setResolution(out: DimensionImpl): Window.Dimension { val widthArray = IntArray(1) val heightArray = IntArray(1) glfwGetFramebufferSize(id, widthArray, heightArray) @@ -208,7 +210,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon return out } - fun setIcon(resource: Resource) { + override fun setIcon(resource: Resource) { val image = ImageData(resource) if (image.data != null) { @@ -218,7 +220,7 @@ class Window private constructor(width: Int, height: Int, val title: String, mon image.free() } - fun setIcon(width: Int, height: Int, imageBuffer: ByteBuffer) { + override fun setIcon(width: Int, height: Int, imageBuffer: ByteBuffer) { val image: GLFWImage = GLFWImage.malloc() val imagebf: GLFWImage.Buffer = GLFWImage.malloc(1) @@ -228,32 +230,32 @@ class Window private constructor(width: Int, height: Int, val title: String, mon glfwSetWindowIcon(id, imagebf) } - fun setFullscreen(monitor: Monitor) { - val vidMode = monitor.currentVideoMode + override fun setFullscreen(monitor: Monitor) { + val vidMode = if (monitor is GLFWMonitor) monitor.currentVideoMode else throw IllegalStateException("Unable to put window into fullscreen mode") glfwSetWindowMonitor(id, monitor.id, 0, 0, vidMode.width(), vidMode.height(), vidMode.refreshRate()) } - fun setFullscreen(monitor: Monitor, width: Int, height: Int, refreshRate: Int = 60) { + override fun setFullscreen(monitor: Monitor, width: Int, height: Int, refreshRate: Int) { glfwSetWindowMonitor(id, monitor.id, 0, 0, width, height, refreshRate) } - fun exitFullscreen() { + override fun exitFullscreen() { if (this.monitor != null) { - val vidMode = Monitor.getPrimaryMonitor().currentVideoMode + val vidMode = GLFWMonitor.getPrimaryMonitor().currentVideoMode glfwSetWindowMonitor(id, MemoryUtil.NULL, 0, 0, vidMode.width(), vidMode.height(), 0) } } - inner class Position { + inner class PositionImpl : Window.Position { val xArray = IntArray(1) val yArray = IntArray(1) - var x: Int + override var x: Int set(value) = set(value, y) get() { updatePosition() return xArray[0] } - var y: Int + override var y: Int set(value) = set(x, value) get() { updatePosition() @@ -264,29 +266,25 @@ class Window private constructor(width: Int, height: Int, val title: String, mon glfwGetWindowPos(id, xArray, yArray) } - fun set(x: Int, y: Int) { + override fun set(x: Int, y: Int) { glfwSetWindowPos(id, x, y) } } - interface Dimension { - val width: Int - val height: Int - } private data class DimensionImpl( override var width: Int, override var height: Int, - var isChanging: Boolean) : Dimension + var isChanging: Boolean) : Window.Dimension companion object { fun create(title: String, width: Int, height: Int): Window { GLFWContext.initialize() - return Window(width, height, title, null) + return GLFWWindow(width, height, title, null) } fun create(title: String, monitor: Monitor): Window { GLFWContext.initialize() - val vidMode = monitor.currentVideoMode + val vidMode = if (monitor is GLFWMonitor) monitor.currentVideoMode else throw IllegalStateException("Unable to create window") val width = vidMode.width() val height = vidMode.height() glfwWindowHint(GLFW_RED_BITS, vidMode.redBits()) @@ -294,12 +292,12 @@ class Window private constructor(width: Int, height: Int, val title: String, mon glfwWindowHint(GLFW_BLUE_BITS, vidMode.blueBits()) glfwWindowHint(GLFW_REFRESH_RATE, vidMode.refreshRate()) - return Window(width, height, title, monitor) + return GLFWWindow(width, height, title, monitor) } fun create(title: String, width: Int, height: Int, monitor: Monitor): Window { GLFWContext.initialize() - return Window(width, height, title, monitor) + return GLFWWindow(width, height, title, monitor) } } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt index d9cb1d35..83914809 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt @@ -46,9 +46,19 @@ class DrawData { renderer.strokeWidth = value } + var strokeColorWasSet = false val strokeColor = DynamicColor(0.0, 0.0, 0.0, 1.0) + get() { + strokeColorWasSet = true + return field + } + var fillColorWasSet = false val fillColor = DynamicColor(0.0, 0.0, 0.0, 1.0) + get() { + fillColorWasSet = true + return field + } val modelOrigin = OriginVector() @@ -130,6 +140,9 @@ class DrawData { viewMatrixWasSet = false projectionMatrix = Game.matrices.projection + strokeColorWasSet = false + fillColorWasSet = false + transformation.identity() renderer.rewind() modelOrigin.reset() diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/path/PathDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/path/PathDrawerImpl.kt index effb0a54..2537d733 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/path/PathDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/path/PathDrawerImpl.kt @@ -45,7 +45,11 @@ class PathDrawerImpl(private val data: DrawData): PathDrawer { private fun draw() { data.getTransformationMatrix(transformation) - renderer.color = data.strokeColor + if (!data.strokeColorWasSet && data.fillColorWasSet) { + renderer.color = data.fillColor + } else { + renderer.color = data.strokeColor + } renderer.stroke = data.strokeWidth.toFloat() renderer.render(transformation) } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt index 3fb39e39..a836d5f0 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt @@ -23,7 +23,6 @@ interface ConfigurationData { val saveData: Array? val fullscreen: Boolean? val startingScene: (() -> MainScene)? - val loadState: (() -> LoadScene)? val windowConfiguration: (Window.() -> Unit)? val debugConfiguration: (GameDebugConfiguration.() -> Unit)? val projectionMatrixConfiguration: (Matrix4f.(View) -> Unit)? diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt index 54f2fe3b..680e8d62 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt @@ -15,7 +15,6 @@ interface GameConfiguration { fun setViewLocation(x: Double, y: Double) fun setStartingState(scene: () -> MainScene) - fun setLoader(loader: () -> LoadScene) fun setSaveData(vararg savedata: Any) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt index f9b9717e..26247e04 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt @@ -42,10 +42,6 @@ internal class GameConfigurationImpl : GameConfiguration { _data.startingScene = scene } - override fun setLoader(loader: () -> LoadScene) { - _data.loadState = loader - } - override fun setSaveData(vararg savedata: Any) { _data.saveData = arrayOf(*savedata) } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt index b5647826..5eb41b82 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt @@ -1,5 +1,7 @@ package com.mechanica.engine.game.configuration +import com.mechanica.engine.context.GLInitializer +import com.mechanica.engine.context.loader.LwjglDisplayLoader import com.mechanica.engine.display.Monitor import com.mechanica.engine.display.Window import com.mechanica.engine.debug.GameDebugConfiguration @@ -12,7 +14,7 @@ import org.joml.Matrix4f import org.lwjgl.glfw.GLFW class GameSetup(data: NullableConfigurationData) : ConfigurationData { - override val monitor = Monitor.getPrimaryMonitor() + override val monitor by lazy { Monitor.getPrimaryMonitor() } override val title: String = data.title ?: "Mechanica" override val resolutionWidth: Int @@ -24,7 +26,6 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { override val saveData: Array = data.saveData ?: emptyArray() override val fullscreen: Boolean = data.fullscreen ?: false override val startingScene: (() -> MainScene)? = data.startingScene - override val loadState: (() -> LoadScene)? = data.loadState override val windowConfiguration: (Window.() -> Unit) = data.windowConfiguration ?: { } override val debugConfiguration: (GameDebugConfiguration.() -> Unit) = data.debugConfiguration ?: { } override val projectionMatrixConfiguration: (Matrix4f.(View) -> Unit) @@ -38,6 +39,7 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { val resolutionConverter: ResolutionConverter init { + GLInitializer.initializeDisplay(LwjglDisplayLoader()) val resolutionWasSet = data.resolutionWidth != null && data.resolutionHeight != null @@ -70,14 +72,14 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { private fun centerWindow(window: Window) { val monitor = Monitor.getPrimaryMonitor() - val screenWidth = monitor.currentVideoMode.width() - val screenHeight = monitor.currentVideoMode.height() + val screenWidth = monitor.width + val screenHeight = monitor.height window.position.set((screenWidth - window.width)/2, (screenHeight - window.height)/2) } private fun setResolution(resolutionWasSet: Boolean, data: NullableConfigurationData): Pair { - val defaultResolutionWidth = (monitor.currentVideoMode.width()*0.75).toInt() - val defaultResolutionHeight = (monitor.currentVideoMode.height()*0.75).toInt() + val defaultResolutionWidth = (monitor.width*0.75).toInt() + val defaultResolutionHeight = (monitor.height*0.75).toInt() return Pair( if (resolutionWasSet) data.resolutionWidth ?: defaultResolutionWidth diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt index 0a0d8a12..3591abeb 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt @@ -27,7 +27,6 @@ class NullableConfigurationData : ConfigurationData { override var fullscreen: Boolean? = null override var startingScene: (() -> MainScene)? = null - override var loadState: (() -> LoadScene)? = null override var windowConfiguration: (Window.() -> Unit)? = null override var debugConfiguration: (GameDebugConfiguration.() -> Unit)? = null diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt index d60ab44f..b5cfa227 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt @@ -51,19 +51,8 @@ internal class SceneManager : Scene() { fun setStartingScene(data: GameSetup) { val state = data.startingScene - val loadState = data.loadState?.invoke() - if (loadState != null) { - loadState.onFinish = { - setMainScene { state?.invoke() } - } - setMainScene { - loadState.preLoad() - loadState - } - } else { - setMainScene { state?.invoke() } - } + setMainScene { state?.invoke() } } fun setMainScene(setter: () -> MainScene?) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt new file mode 100644 index 00000000..b526218f --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt @@ -0,0 +1,48 @@ +package com.mechanica.engine.scenes.exclusiveScenes + +import com.mechanica.engine.scenes.processes.ProcessNode +import com.mechanica.engine.scenes.processes.Process +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +open class ExclusiveActivationMap

(vararg processes: P) : ReadOnlyProperty { + private val processes: ArrayList

= ArrayList() + private var activeIndex = -1 + val active: P + get() = processes[activeIndex] + + init { + for (i in processes.indices) { + add(processes[i]) + } + } + + override operator fun getValue(thisRef: ProcessNode, property: KProperty<*>): P = active + + fun add(process: P) = addProcess(process) + + open fun addProcess(process: R): R { + processes.add(process) + process.addActivationCallback() + return process + } + + private fun P.addActivationCallback() { + addActivationChangedListener { + if (it) { + setExclusiveActivation(this) + } else if (this@ExclusiveActivationMap.active === this) { + activeIndex = -1 + } + } + } + + private fun setExclusiveActivation(process: P) { + activeIndex = processes.indexOf(process) + for (i in processes.indices) { + if (processes[i] != process) { + processes[i].active = false + } + } + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt index 1f420d84..3d2914bc 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt @@ -1,16 +1,39 @@ package com.mechanica.engine.scenes.processes -import kotlin.math.max +import com.mechanica.engine.scenes.exclusiveScenes.ExclusiveActivationMap abstract class Process(override val priority: Int = 0) : ProcessNode { - protected val childProcesses: List = ArrayList() + private val activationListeners = ArrayList<(Boolean) -> Unit>() + final override var active: Boolean = true + set(value) { + val hasChanged = field != value + field = value + if (hasChanged) { + runActivationCallbacks(value) + } + } + + private val childProcesses: List = ArrayList() + + fun addActivationChangedListener(listener: (Boolean) -> Unit) { + activationListeners.add(listener) + runActivationCallbacks(active) + } + + private fun runActivationCallbacks(activate: Boolean) { + for (i in activationListeners.indices) { + activationListeners[i].invoke(activate) + } + } final override fun addProcess(process: P): P { val processes = (childProcesses as ArrayList) - processes.add(process) - processes.sortBy { it.priority } + if (!processes.contains(process)) { + processes.add(process) + processes.sortBy { it.priority } + } return process } @@ -47,7 +70,9 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { var i = from do { val process = childProcesses.getOrNull(i++) ?: break - process.updateNodes(delta) + if (process.active) { + process.updateNodes(delta) + } } while (condition(process)) return i } @@ -59,6 +84,29 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { destructor() } + /** + * Sets the [processes] in which only one process can be active at once. This exclusivity is automatically enforced + * + * Usage: + * ``` + * val currentlyActiveProcess by exclusivelyActiveProcesses(Process1(), Process2(), Process3()) + * ``` + * + * @param processes the set of processes that will be added as children to this process and only one can be + * set to active at a time + * @return An ExclusiveProcessMap which can have more processes added at a later stage + */ + protected fun exclusivelyActiveProcesses(vararg processes: P): ExclusiveActivationMap

{ + return ExclusiveProcessMap(*processes) + } + + private inner class ExclusiveProcessMap(vararg processes: P) : ExclusiveActivationMap

(*processes) { + override fun addProcess(process: R): R { + this@Process.addProcess(process) + return super.addProcess(process) + } + } + override fun destructor() { } protected fun finalize() { destructor() } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt index 551e775f..19853d76 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt @@ -3,6 +3,8 @@ package com.mechanica.engine.scenes.processes interface ProcessNode : Updateable { val priority: Int get() = 0 + val active: Boolean + get() = true fun addProcess(process: P): P fun removeProcess(process: ProcessNode): Boolean fun replaceProcess(old: P, new: P): P diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt index 3d68ff66..74137094 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt @@ -19,7 +19,7 @@ abstract class DynamicScene( open val position = DynamicVector.create(x + width/2.0, y + height/2.0) - override val view: DynamicView = DynamicSceneView(width, height) + override val view: DynamicView by lazy { DynamicSceneView(width, height) } override fun drawInScene(draw: Drawer, view: View): Drawer { return draw.centered.transformed.translate(position).scale(view.wh) @@ -30,7 +30,9 @@ abstract class DynamicScene( override var height: Double) : DynamicView { override var x: Double - get() = center.x - width/2.0 + get() { + return center.x - width/2.0 + } set(value) { center.x = value + width/2.0} override var y: Double get() = center.y - height/2.0 diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt index ed7d6d51..e6231735 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt @@ -11,7 +11,13 @@ abstract class LoadScene : MainScene() { private var startLoading = false private var finishedLoading = false + private var hasPreLoaded = false + override fun update(delta: Double) { + if (!hasPreLoaded) { + preLoad() + hasPreLoaded = true + } currentWait += delta currentLoops++ startLoading = currentWait > waitTime && currentLoops > minLoops @@ -33,5 +39,5 @@ abstract class LoadScene : MainScene() { abstract fun load() - internal var onFinish: () -> Unit = { } + abstract fun onFinish() } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt index b68c0746..35ae688d 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt @@ -2,6 +2,7 @@ package com.mechanica.engine.scenes.scenes import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.view.View +import com.mechanica.engine.scenes.exclusiveScenes.ExclusiveActivationMap import com.mechanica.engine.scenes.processes.Process abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { @@ -17,8 +18,10 @@ abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { addProcess(scene) val scenes = (childScenes as ArrayList) - scenes.add(scene) - scenes.sortBy { it.priority } + if (!scenes.contains(scene)) { + scenes.add(scene) + scenes.sortBy { it.priority } + } return scene } @@ -57,7 +60,9 @@ abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { var i = from do { val scene = childScenes.getOrNull(i++) ?: break - scene.renderNodes(draw) + if (scene.active) { + scene.renderNodes(draw) + } } while (condition(scene)) return i } @@ -71,6 +76,25 @@ abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { super.destructNodes() } + /** + * This does the same as [exclusivelyActiveProcesses][com.mechanica.engine.scenes.processes.Process.exclusivelyActiveProcesses] but it adds + * scenes to the parent instead of processes + * + * @param scenes the set of scenes that will be added as children to this scene and only one can be + * set to active at a time + * @return An ExclusiveProcessMap which can have more processes added at a later stage + */ + protected fun exclusivelyActiveScenes(vararg scenes: P): ExclusiveActivationMap

{ + return ExclusiveSceneMap(*scenes) + } + + private inner class ExclusiveSceneMap(vararg scenes: P) : ExclusiveActivationMap

(*scenes) { + override fun addProcess(process: R): R { + this@Scene.addScene(process) + return super.addProcess(process) + } + } + @Suppress("PropertyName") @PublishedApi internal val `access$childScenes`: List From 4ac81da4541329cd80d4b86a76a60b4d1c123764 Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Tue, 9 Jun 2020 14:31:42 +0100 Subject: [PATCH 08/21] Fixed a bug related to drawing text --- .../com/mechanica/engine/models/TextModel.kt | 28 +++++++++---------- .../kotlin/com/mechanica/engine/text/Text.kt | 2 +- .../engine/shader/vbo/LwjglIndexArray.kt | 5 +++- .../com/mechanica/engine/drawer/DrawData.kt | 3 +- .../drawer/superclass/text/TextDrawerImpl.kt | 8 +++--- .../engine/scenes/scenes/LoadScene.kt | 2 +- .../com/mechanica/engine/samples/temp/Test.kt | 2 +- .../engine/samples/text/FontRenderer.kt | 2 +- 8 files changed, 28 insertions(+), 24 deletions(-) diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt b/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt index a935ab12..ca272aa4 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt @@ -8,13 +8,14 @@ import com.mechanica.engine.utils.createIndicesArrayForQuads import com.mechanica.engine.vertices.AttributeArray import com.mechanica.engine.vertices.IndexArray import com.mechanica.engine.vertices.FloatBufferMaker +import kotlin.math.max class TextModel(text: Text, positionBufferMaker: FloatBufferMaker = Attribute(0).vec3(), texCoordsBufferMaker: FloatBufferMaker = Attribute(1).vec2()) : Model( positionBufferMaker.createBuffer(text.positions), texCoordsBufferMaker.createBuffer(text.texCoords), - IndexArray.create(*createIndicesArrayForQuads(20)), + IndexArray.create(*createIndicesArrayForQuads(max(text.positions.size/2, 20))), Image.invoke(text.font.atlas.id), draw = { model -> GLLoader.graphicsLoader.drawElements(model) @@ -26,11 +27,11 @@ class TextModel(text: Text, private var textHolder: Text = text - var text: String = textHolder.text + var string: String + get() = textHolder.string set(value) { - field = value - textHolder.text = value - updateTextHolder() + textHolder.string = value + updateTextHolder(textHolder) } val lineCount: Int @@ -44,20 +45,19 @@ class TextModel(text: Text, } fun setText(text: Text) { - if (text.text != this.text || textHolder != text) { +// if (text.string != this.string || textHolder != text) { this.textHolder = text - this.text = textHolder.text - updateTextHolder() - } + updateTextHolder(text) +// } } - private fun updateTextHolder() { - positionAttribute.set(textHolder.positions, 0, textHolder.vertexCount) - texCoordsAttribute.set(textHolder.texCoords, 0, textHolder.vertexCount) - vertexCount = textHolder.vertexCount + private fun updateTextHolder(text: Text) { + positionAttribute.set(text.positions, 0, text.vertexCount) + texCoordsAttribute.set(text.texCoords, 0, text.vertexCount) + vertexCount = text.vertexCount if (vertexCount > indexArray.vertexCount) { - indexArray.set(createIndicesArrayForQuads(vertexCount*2/6)) + indexArray.set(createIndicesArrayForQuads(vertexCount)) } } diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/text/Text.kt b/application-interface/src/main/kotlin/com/mechanica/engine/text/Text.kt index f782aeb0..3cb4fa03 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/text/Text.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/text/Text.kt @@ -8,7 +8,7 @@ import kotlin.math.max class Text(text: String, val font: Font = GLLoader.fontLoader.defaultFont) { - var text = text + var string = text set(value) { field = value update(value, font) diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vbo/LwjglIndexArray.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vbo/LwjglIndexArray.kt index a32e21d6..247c5946 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vbo/LwjglIndexArray.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vbo/LwjglIndexArray.kt @@ -18,5 +18,8 @@ class LwjglIndexArray(array: ShortArray) : LwjglVertexBuffer(array.s override fun subData(offset: Long, array: ShortArray) = glBufferSubData(bufferTarget, offset, array) - + override fun set(array: ShortArray, from: Int, length: Int) { + vertexCount = array.size + super.set(array, from, length) + } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt index 83914809..2c3a42a3 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt @@ -81,7 +81,8 @@ class DrawData { val cornerSize: DynamicVector get() = renderer.size - val textModel: TextModel = TextModel("") + val stringHolderModel: TextModel = TextModel("") + val textHolderModel: TextModel = TextModel("") fun setTranslate(x: Float, y: Float) { translation.set(translation.x + x, translation.y + y, translateZ) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt index 9a92eae4..22fcd004 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt @@ -11,14 +11,14 @@ class TextDrawerImpl( private val data: DrawData) : TextDrawer { override fun text(text: String) { - val model = data.textModel - model.text = text + val model = data.stringHolderModel + model.string = text drawText(model) } override fun text(text: Text) { - val model = data.textModel - data.textModel.setText(text) + val model = data.textHolderModel + model.setText(text) drawText(model) } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt index e6231735..4cf1e618 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/LoadScene.kt @@ -15,8 +15,8 @@ abstract class LoadScene : MainScene() { override fun update(delta: Double) { if (!hasPreLoaded) { - preLoad() hasPreLoaded = true + preLoad() } currentWait += delta currentLoops++ diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt index dc135c82..3cce38cf 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt @@ -115,7 +115,7 @@ fun main() { draw.image(image, -1, 0) shader.render(textModel) if (Keyboard.enter.hasBeenPressed) { - textModel.text = "Enter Pressed" + textModel.string = "Enter Pressed" } draw.text(text) } diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt index 6961ac2c..77f0571e 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/text/FontRenderer.kt @@ -77,7 +77,7 @@ class FontRenderer { var text: String = "" set(value) { - model.text = value + model.string = value field = value } From 6015d5768400d5060674e3acdf9b66c19d93f14c Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Thu, 11 Jun 2020 02:36:09 +0100 Subject: [PATCH 09/21] Added a way to pause the frame and other minor improvements --- .../engine/debug/DebugConfiguration.kt | 2 + .../engine/util/extensions/Arrays.kt | 19 +++++++ .../config/BackendDebugConfiguration.kt | 8 +++ .../engine/debug/GameDebugConfiguration.kt | 10 ++++ .../kotlin/com/mechanica/engine/game/Game.kt | 2 +- .../mechanica/engine/scenes/SceneManager.kt | 50 ++++++++++++++++--- .../exclusiveScenes/ExclusiveActivationMap.kt | 10 ++-- .../engine/scenes/processes/Process.kt | 47 ++++++++++++++--- 8 files changed, 128 insertions(+), 20 deletions(-) diff --git a/common/src/main/kotlin/com/mechanica/engine/debug/DebugConfiguration.kt b/common/src/main/kotlin/com/mechanica/engine/debug/DebugConfiguration.kt index b40bb568..4475d5e9 100644 --- a/common/src/main/kotlin/com/mechanica/engine/debug/DebugConfiguration.kt +++ b/common/src/main/kotlin/com/mechanica/engine/debug/DebugConfiguration.kt @@ -6,4 +6,6 @@ interface DebugConfiguration { val constructionDraws: Boolean val printWarnings: Boolean val lwjglDebug: Boolean + fun pauseUpdates(pause: Boolean) + fun frameAdvance() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/util/extensions/Arrays.kt b/common/src/main/kotlin/com/mechanica/engine/util/extensions/Arrays.kt index 98530a1f..a5e67492 100644 --- a/common/src/main/kotlin/com/mechanica/engine/util/extensions/Arrays.kt +++ b/common/src/main/kotlin/com/mechanica/engine/util/extensions/Arrays.kt @@ -118,6 +118,25 @@ fun FloatArray.fill(iterator: Iterator) { } } + +/** + * A version of for each that should be more efficient for lists that + * are fast at getting element with random access + * + * @param action action that should be performed on each element + */ +inline fun Array.fori(action: (E) -> Unit) { + for (i in this.indices) { + action(this[i]) + } +} + +inline fun Array.foriIndexed(action: (E, Int) -> Unit) { + for (i in this.indices) { + action(this[i], i) + } +} + fun List<*>.indexLooped(index: Int): Int { return when { index < 0 -> this.size + index diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/config/BackendDebugConfiguration.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/config/BackendDebugConfiguration.kt index 2326c6db..e25130a8 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/config/BackendDebugConfiguration.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/config/BackendDebugConfiguration.kt @@ -19,4 +19,12 @@ object BackendDebugConfiguration : DebugConfiguration { get() = configuration?.printWarnings ?: false override val lwjglDebug: Boolean get() = configuration?.lwjglDebug ?: false + + override fun pauseUpdates(pause: Boolean) { + configuration?.pauseUpdates(pause) + } + + override fun frameAdvance() { + configuration?.frameAdvance() + } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/debug/GameDebugConfiguration.kt b/mechanica/src/main/kotlin/com/mechanica/engine/debug/GameDebugConfiguration.kt index fed749ab..10b4faa7 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/debug/GameDebugConfiguration.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/debug/GameDebugConfiguration.kt @@ -1,9 +1,19 @@ package com.mechanica.engine.debug +import com.mechanica.engine.game.Game + class GameDebugConfiguration : DebugConfiguration { override var failEarly = false override var screenLog = false override var constructionDraws = false override var printWarnings = false override var lwjglDebug = false + + override fun pauseUpdates(pause: Boolean) { + Game.sceneManager.pauseExecution(pause) + } + + override fun frameAdvance() { + Game.sceneManager.frameAdvance() + } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt index 71c8604d..d3b08a44 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt @@ -37,7 +37,7 @@ object Game { private val gameMatrices: GameMatrices get() = matrices as GameMatrices - private val sceneManager = SceneManager() + internal val sceneManager = SceneManager() val scene: Scene get() = sceneManager.currentScene ?: throw UninitializedPropertyAccessException("The top level scene has not yet been initialized") diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt index b5cfa227..83e958b1 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneManager.kt @@ -6,7 +6,6 @@ import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.game.configuration.GameSetup import com.mechanica.engine.game.view.View -import com.mechanica.engine.scenes.processes.Process import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.scenes.scenes.Scene import com.mechanica.engine.util.Timer @@ -49,6 +48,26 @@ internal class SceneManager : Scene() { private var startOfLoop = Timer.now private var updateDuration = 0.1 + private var pause = false + private var hasPaused = false + private var pausedUpdate = 0.17 + + private var frameAdvance = false + + fun pauseExecution(pause: Boolean) { + this.pause = pause + hasPaused = false + + if (!pause) { + updateDuration = 0.017 + startOfLoop = Timer.now + } + } + + fun frameAdvance() { + frameAdvance = true + } + fun setStartingScene(data: GameSetup) { val state = data.startingScene @@ -61,18 +80,27 @@ internal class SceneManager : Scene() { } fun updateScenes(): Double { - updateDuration = Timer.now - startOfLoop - startOfLoop = Timer.now - checkStateChange() - - updateNodes(updateDuration) + if (!pause || frameAdvance) { + updateScene() + frameAdvance = false + } else { + updatePaused() + } render() + checkStateChange() return updateDuration } + private fun updateScene() { + updateDuration = Timer.now - startOfLoop + startOfLoop = Timer.now + + updateNodes(updateDuration) + } + override fun update(delta: Double) { updateVar?.invoke(delta) } @@ -103,4 +131,14 @@ internal class SceneManager : Scene() { return drawer } + + private fun updatePaused() { + if (!hasPaused) { + hasPaused = true + updateDuration = Timer.now - startOfLoop + } + startOfLoop = Timer.now + } + + } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt index b526218f..7a0433d5 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/exclusiveScenes/ExclusiveActivationMap.kt @@ -5,11 +5,11 @@ import com.mechanica.engine.scenes.processes.Process import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty -open class ExclusiveActivationMap

(vararg processes: P) : ReadOnlyProperty { +open class ExclusiveActivationMap

(vararg processes: P) : ReadOnlyProperty { private val processes: ArrayList

= ArrayList() private var activeIndex = -1 - val active: P - get() = processes[activeIndex] + val active: P? + get() = if (activeIndex != -1) processes[activeIndex] else null init { for (i in processes.indices) { @@ -17,7 +17,7 @@ open class ExclusiveActivationMap

(vararg processes: P) : ReadOnlyPr } } - override operator fun getValue(thisRef: ProcessNode, property: KProperty<*>): P = active + override operator fun getValue(thisRef: ProcessNode, property: KProperty<*>): P? = active fun add(process: P) = addProcess(process) @@ -28,7 +28,7 @@ open class ExclusiveActivationMap

(vararg processes: P) : ReadOnlyPr } private fun P.addActivationCallback() { - addActivationChangedListener { + addActivationChangedListener(0) { if (it) { setExclusiveActivation(this) } else if (this@ExclusiveActivationMap.active === this) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt index 3d2914bc..442b9df7 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt @@ -4,11 +4,12 @@ import com.mechanica.engine.scenes.exclusiveScenes.ExclusiveActivationMap abstract class Process(override val priority: Int = 0) : ProcessNode { - private val activationListeners = ArrayList<(Boolean) -> Unit>() - final override var active: Boolean = true + private val activationListeners = ArrayList() + private var _active = true + final override var active: Boolean + get() = _active set(value) { - val hasChanged = field != value - field = value + val hasChanged = _active != value if (hasChanged) { runActivationCallbacks(value) } @@ -16,14 +17,33 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { private val childProcesses: List = ArrayList() - fun addActivationChangedListener(listener: (Boolean) -> Unit) { - activationListeners.add(listener) - runActivationCallbacks(active) + private var callbacksInitialized = false + + init { + addActivationChangedListener(0) { _active = it } + } + + /** + * Adds a callback for when the value of [active] is changed. The [priority] value dictates the order in which + * the callbacks are executed, the higher the priority value, the earlier the callback will execute. + * The value can be negative, in which case the callback will be called after the value for [active] + * has been set + * + * @param priority an integer value which represents the order that the callbacks are executed, the higher + * the value, the earlier the execution, the value for [active] is changed at a priority of zero + * + * @param listener the callback which will execute when the value of [active] has been changed, the lambda takes + * a boolean expression which is the new value for [active] + */ + fun addActivationChangedListener(priority: Int = 0, listener: (Boolean) -> Unit) { + activationListeners.add(ActivationChangedEvents(priority, listener)) + activationListeners.sortByDescending { it.priority } } private fun runActivationCallbacks(activate: Boolean) { + callbacksInitialized = true for (i in activationListeners.indices) { - activationListeners[i].invoke(activate) + activationListeners[i].listener.invoke(activate) } } @@ -61,17 +81,26 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { } override fun updateNodes(delta: Double) { + if (!callbacksInitialized) runActivationCallbacks(active) + val index = updateNodesFor(delta) { it.priority < 0 } this.update(delta) updateNodesFor(delta, index) { it.priority >= 0 } } + /** + * A function to be overridden which will run while [active] is set to false + */ + open fun whileInactive(delta: Double) { } + private inline fun updateNodesFor(delta: Double, from: Int = 0, condition: (ProcessNode) -> Boolean): Int { var i = from do { val process = childProcesses.getOrNull(i++) ?: break if (process.active) { process.updateNodes(delta) + } else if (process is Process) { + process.whileInactive(delta) } } while (condition(process)) return i @@ -115,4 +144,6 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { @PublishedApi internal val `access$childProcesses`: List get() = childProcesses + + private class ActivationChangedEvents(val priority: Int, val listener: (Boolean) -> Unit) } From 1a23f994f257bec0b2484f2044ee83c20426fe1a Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Fri, 12 Jun 2020 02:37:29 +0100 Subject: [PATCH 10/21] Minor changes --- .../engine/context/loader/UniformLoader.kt | 2 +- .../com/mechanica/engine/models/TextModel.kt | 16 ++- .../engine/shader/qualifiers/Uniform.kt | 8 +- .../engine/shader/script/ScriptVariables.kt | 2 +- .../mechanica/engine/shader/script/Shader.kt | 19 ++- .../shader/script/ShaderDeclarations.kt | 2 +- .../engine/shader/script/ShaderScript.kt | 2 +- .../shader/{vars => }/uniforms/UniformVars.kt | 4 +- .../{vars => }/uniforms/vars/UniformFloat.kt | 7 +- .../uniforms/vars/UniformMatrix4f.kt | 7 +- .../{vars => }/uniforms/vars/UniformVar.kt | 9 +- .../uniforms/vars/UniformVector2f.kt | 7 +- .../uniforms/vars/UniformVector3f.kt | 7 +- .../uniforms/vars/UniformVector4f.kt | 7 +- .../com/mechanica/engine/color/ColorTools.kt | 2 +- .../com/mechanica/engine/unit/angle/Angle.kt | 3 +- .../mechanica/engine/unit/angle/AngleTools.kt | 22 ++-- .../com/mechanica/engine/unit/angle/Degree.kt | 10 +- .../com/mechanica/engine/unit/angle/Radian.kt | 10 +- .../engine/unit/vector/VectorTools.kt | 5 +- .../engine/context/loader/LwjglLoader.kt | 7 +- .../context/loader/LwjglUniformLoader.kt | 2 +- .../shader/{vars => }/uniforms/LwjglFloat.kt | 4 +- .../{vars => }/uniforms/LwjglMatrix4f.kt | 4 +- .../{vars => }/uniforms/LwjglVector2f.kt | 4 +- .../{vars => }/uniforms/LwjglVector3f.kt | 4 +- .../{vars => }/uniforms/LwjglVector4f.kt | 4 +- .../com/mechanica/engine/drawer/DrawData.kt | 50 ++++++-- .../com/mechanica/engine/drawer/Drawer.kt | 5 +- .../com/mechanica/engine/drawer/DrawerImpl.kt | 11 +- .../drawer/shader/AbstractDrawerShader.kt | 115 ++++++++++++++++++ .../engine/drawer/shader/DrawerShader.kt | 114 +---------------- .../subclasses/rotation/RotatedDrawerImpl.kt | 4 +- .../transformation/TransformationDrawer.kt | 12 ++ .../TransformationDrawerImpl.kt | 12 ++ .../engine/scenes/processes/InputProcess.kt | 2 +- .../engine/scenes/processes/Process.kt | 10 +- .../engine/scenes/processes/ProcessNode.kt | 8 +- .../engine/scenes/scenes/DynamicScene.kt | 3 +- .../engine/scenes/scenes/MainScene.kt | 2 +- .../mechanica/engine/scenes/scenes/Scene.kt | 10 +- .../engine/scenes/scenes/StaticScene.kt | 3 +- .../mechanica/engine/samples/renderer/Main.kt | 17 ++- 43 files changed, 356 insertions(+), 202 deletions(-) rename application-interface/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/UniformVars.kt (93%) rename application-interface/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/vars/UniformFloat.kt (57%) rename application-interface/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/vars/UniformMatrix4f.kt (72%) rename application-interface/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/vars/UniformVar.kt (71%) rename application-interface/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/vars/UniformVector2f.kt (70%) rename application-interface/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/vars/UniformVector3f.kt (73%) rename application-interface/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/vars/UniformVector4f.kt (84%) rename desktop-application/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/LwjglFloat.kt (72%) rename desktop-application/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/LwjglMatrix4f.kt (80%) rename desktop-application/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/LwjglVector2f.kt (74%) rename desktop-application/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/LwjglVector3f.kt (74%) rename desktop-application/src/main/kotlin/com/mechanica/engine/shader/{vars => }/uniforms/LwjglVector4f.kt (76%) create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/UniformLoader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/UniformLoader.kt index b197b05c..dcf7d5bf 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/UniformLoader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/context/loader/UniformLoader.kt @@ -1,7 +1,7 @@ package com.mechanica.engine.context.loader import com.mechanica.engine.shader.qualifiers.Qualifier -import com.mechanica.engine.shader.vars.uniforms.vars.* +import com.mechanica.engine.shader.uniforms.vars.* import org.joml.Matrix4f interface UniformLoader { diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt b/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt index ca272aa4..8d9e9ed2 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt @@ -25,7 +25,17 @@ class TextModel(text: Text, private val texCoordsAttribute = inputs[1] as AttributeArray private val indexArray = inputs[2] as IndexArray - private var textHolder: Text = text + private var atlas + get() = inputs[3] as Image + set(value) { + inputs[3] = value + } + + var textHolder: Text = text + set(value) { + atlas = value.font.atlas + field = value + } var string: String get() = textHolder.string @@ -45,10 +55,10 @@ class TextModel(text: Text, } fun setText(text: Text) { -// if (text.string != this.string || textHolder != text) { + if (text.string != this.string || textHolder != text) { this.textHolder = text updateTextHolder(text) -// } + } } private fun updateTextHolder(text: Text) { diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Uniform.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Uniform.kt index d2712a4a..36bd9a4c 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Uniform.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Uniform.kt @@ -3,11 +3,12 @@ package com.mechanica.engine.shader.qualifiers import com.mechanica.engine.context.loader.GLLoader import com.mechanica.engine.context.loader.UniformLoader import com.mechanica.engine.shader.script.ScriptVariables +import com.mechanica.engine.shader.uniforms.UniformVars +import com.mechanica.engine.shader.uniforms.vars.* import com.mechanica.engine.shader.vars.ShaderType import com.mechanica.engine.shader.vars.ShaderVariable -import com.mechanica.engine.shader.vars.uniforms.* -import com.mechanica.engine.shader.vars.uniforms.vars.* import org.joml.Matrix4f +import kotlin.reflect.KProperty class Uniform(private val variables: ScriptVariables) : Qualifier, UniformVars { override val qualifierName: String = "uniform" @@ -21,6 +22,9 @@ class Uniform(private val variables: ScriptVariables) : Qualifier, UniformVars { override val qualifier = this@Uniform override val type = ShaderType.get(type) override fun loadUniform() { load(value) } + override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { + this.value = value + } } return addVariable(v) } diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ScriptVariables.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ScriptVariables.kt index afd251f5..2454290e 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ScriptVariables.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ScriptVariables.kt @@ -1,7 +1,7 @@ package com.mechanica.engine.shader.script import com.mechanica.engine.shader.vars.ShaderVariable -import com.mechanica.engine.shader.vars.uniforms.vars.UniformVar +import com.mechanica.engine.shader.uniforms.vars.UniformVar class ScriptVariables(private val placeHolder: String): Iterable { private val variables = ArrayList() diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt index 42dcfa02..355441bb 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/Shader.kt @@ -4,13 +4,14 @@ import com.mechanica.engine.context.loader.GLLoader import com.mechanica.engine.models.Bindable import com.mechanica.engine.models.Model -abstract class Shader( - val vertex: ShaderScript, - val fragment: ShaderScript, - val tessellation: ShaderScript?, - val geometry: ShaderScript?) { +abstract class Shader { abstract val id: Int + abstract val vertex: ShaderScript + abstract val fragment: ShaderScript + open val tessellation: ShaderScript? = null + open val geometry: ShaderScript? = null + private var locationsFound = false protected fun load() { @@ -60,6 +61,14 @@ abstract class Shader( fragment: ShaderScript, tessellation: ShaderScript? = null, geometry: ShaderScript? = null): Shader { + return create(vertex, fragment, tessellation, geometry) + } + + fun create( + vertex: ShaderScript, + fragment: ShaderScript, + tessellation: ShaderScript? = null, + geometry: ShaderScript? = null): Shader { return GLLoader.defaultShader(vertex, fragment, tessellation, geometry) } } diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt index bdb11bd3..c9474986 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderDeclarations.kt @@ -1,7 +1,7 @@ package com.mechanica.engine.shader.script import com.mechanica.engine.shader.qualifiers.Attribute -import com.mechanica.engine.shader.vars.uniforms.UniformVars +import com.mechanica.engine.shader.uniforms.UniformVars import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.qualifiers.Uniform import com.mechanica.engine.shader.vars.ShaderVariable diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt index becafeb6..75bf6b3e 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/script/ShaderScript.kt @@ -1,7 +1,7 @@ package com.mechanica.engine.shader.script import com.mechanica.engine.models.Bindable -import com.mechanica.engine.shader.vars.uniforms.vars.UniformVar +import com.mechanica.engine.shader.uniforms.vars.UniformVar abstract class ShaderScript : ShaderDeclarations("autoVal") { val script: String diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/UniformVars.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/UniformVars.kt similarity index 93% rename from application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/UniformVars.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/UniformVars.kt index 96e0197d..468f47a7 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/UniformVars.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/UniformVars.kt @@ -1,7 +1,7 @@ -package com.mechanica.engine.shader.vars.uniforms +package com.mechanica.engine.shader.uniforms import com.mechanica.engine.color.Color -import com.mechanica.engine.shader.vars.uniforms.vars.* +import com.mechanica.engine.shader.uniforms.vars.* import com.mechanica.engine.unit.vector.Vector import org.joml.Matrix4f import org.joml.Vector3f diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformFloat.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformFloat.kt similarity index 57% rename from application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformFloat.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformFloat.kt index 5fa2c417..bfce2497 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformFloat.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformFloat.kt @@ -1,7 +1,9 @@ -package com.mechanica.engine.shader.vars.uniforms.vars +package com.mechanica.engine.shader.uniforms.vars import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.vars.ShaderType +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty abstract class UniformFloat( override var value: Float, @@ -10,4 +12,7 @@ abstract class UniformFloat( ) : UniformVar() { override val type = ShaderType.float() + override fun setValue(thisRef: Any, property: KProperty<*>, value: Float) { + this.value = value + } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformMatrix4f.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformMatrix4f.kt similarity index 72% rename from application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformMatrix4f.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformMatrix4f.kt index 7c134fd2..1fa1a0a1 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformMatrix4f.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformMatrix4f.kt @@ -1,8 +1,9 @@ -package com.mechanica.engine.shader.vars.uniforms.vars +package com.mechanica.engine.shader.uniforms.vars import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.vars.ShaderType import org.joml.Matrix4f +import kotlin.reflect.KProperty abstract class UniformMatrix4f( var matrix: Matrix4f, @@ -19,4 +20,8 @@ abstract class UniformMatrix4f( fun set(matrix: Matrix4f) { this.matrix.set(matrix) } + + override fun setValue(thisRef: Any, property: KProperty<*>, value: Matrix4f) { + set(value) + } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVar.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVar.kt similarity index 71% rename from application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVar.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVar.kt index 9893e5ce..209e0dce 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVar.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVar.kt @@ -1,10 +1,13 @@ -package com.mechanica.engine.shader.vars.uniforms.vars +package com.mechanica.engine.shader.uniforms.vars import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.vars.ShaderType import com.mechanica.engine.shader.vars.ShaderVariable +import kotlin.properties.ReadOnlyProperty +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty -abstract class UniformVar : ShaderVariable { +abstract class UniformVar : ShaderVariable, ReadWriteProperty { abstract val value: T abstract override val name: String abstract override val qualifier: Qualifier @@ -18,6 +21,8 @@ abstract class UniformVar : ShaderVariable { override fun toString() = name + override fun getValue(thisRef: Any, property: KProperty<*>) = value + companion object { private val regex = Regex("[^\\w\\d]") diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector2f.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector2f.kt similarity index 70% rename from application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector2f.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector2f.kt index 52e619ec..29d46e35 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector2f.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector2f.kt @@ -1,8 +1,9 @@ -package com.mechanica.engine.shader.vars.uniforms.vars +package com.mechanica.engine.shader.uniforms.vars import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.vars.ShaderType import com.mechanica.engine.unit.vector.DynamicVector +import kotlin.reflect.KProperty abstract class UniformVector2f( x: Number, y: Number, @@ -11,4 +12,8 @@ abstract class UniformVector2f( ) : UniformVar(), DynamicVector by DynamicVector.create(x.toDouble(), y.toDouble()) { override val value: DynamicVector by lazy { this } override val type = ShaderType.vec2() + + override fun setValue(thisRef: Any, property: KProperty<*>, value: DynamicVector) { + this.value.set(value) + } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector3f.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector3f.kt similarity index 73% rename from application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector3f.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector3f.kt index 9ed2985c..88355450 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector3f.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector3f.kt @@ -1,8 +1,9 @@ -package com.mechanica.engine.shader.vars.uniforms.vars +package com.mechanica.engine.shader.uniforms.vars import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.vars.ShaderType import org.joml.Vector3f +import kotlin.reflect.KProperty abstract class UniformVector3f( x: Number, y: Number, z: Number, @@ -19,4 +20,8 @@ abstract class UniformVector3f( value.y = y.toFloat() value.z = z.toFloat() } + + override fun setValue(thisRef: Any, property: KProperty<*>, value: Vector3f) { + this.value.set(value) + } } \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector4f.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector4f.kt similarity index 84% rename from application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector4f.kt rename to application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector4f.kt index 5a99ad2e..84674ba4 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/vars/UniformVector4f.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/uniforms/vars/UniformVector4f.kt @@ -1,10 +1,11 @@ -package com.mechanica.engine.shader.vars.uniforms.vars +package com.mechanica.engine.shader.uniforms.vars import com.mechanica.engine.color.Color import com.mechanica.engine.color.LightweightColor import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.vars.ShaderType import org.joml.Vector4f +import kotlin.reflect.KProperty abstract class UniformVector4f ( @@ -37,4 +38,8 @@ abstract class UniformVector4f ( value.z = z.toFloat() value.w = w.toFloat() } + + override fun setValue(thisRef: Any, property: KProperty<*>, value: Vector4f) { + this.value.set(value) + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt b/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt index 72c894c3..b7fd63f8 100644 --- a/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt +++ b/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt @@ -189,7 +189,7 @@ fun hsl(hue: Angle, saturation: Double, lightness: Double, alpha: Double = 1.0): return (l - a* max(min(min(k-3.0, 9.0-k), 1.0),-1.0)) } - val h = (hue.toDegrees().asDouble() + 360.0)%360.0 + val h = (hue.toDegrees().toDouble() + 360.0)%360.0 val s = saturation val l = lightness diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/angle/Angle.kt b/common/src/main/kotlin/com/mechanica/engine/unit/angle/Angle.kt index a8c8a292..d6f81781 100644 --- a/common/src/main/kotlin/com/mechanica/engine/unit/angle/Angle.kt +++ b/common/src/main/kotlin/com/mechanica/engine/unit/angle/Angle.kt @@ -1,7 +1,8 @@ package com.mechanica.engine.unit.angle interface Angle { - fun asDouble(): Double + fun toDouble(): Double + fun toFloat(): Float fun toDegrees(): Degree fun toRadians(): Radian } \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt b/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt index 51282eed..c49a4072 100644 --- a/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt +++ b/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt @@ -6,40 +6,40 @@ inline val Number.degrees: Degree inline val Number.radians: Radian get() = Radian(this.toDouble()) -operator fun Angle.minus(other: Angle): Angle = (this.toRadians().asDouble() - other.toRadians().asDouble()).radians +operator fun Angle.minus(other: Angle): Angle = (this.toRadians().toDouble() - other.toRadians().toDouble()).radians -operator fun Degree.unaryMinus(): Degree = Degree(-(this.asDouble())) +operator fun Degree.unaryMinus(): Degree = Degree(-(this.toDouble())) -operator fun Radian.unaryMinus(): Radian = Radian(-(this.asDouble())) +operator fun Radian.unaryMinus(): Radian = Radian(-(this.toDouble())) operator fun Degree.plus(other: Degree): Degree { - return (this.asDouble() + other.asDouble()).degrees + return (this.toDouble() + other.toDouble()).degrees } operator fun Radian.plus(other: Radian): Radian { - return (this.asDouble() + other.asDouble()).radians + return (this.toDouble() + other.toDouble()).radians } operator fun Degree.minus(other: Degree): Degree { - return (this.asDouble() - other.asDouble()).degrees + return (this.toDouble() - other.toDouble()).degrees } operator fun Radian.minus(other: Radian): Radian { - return (this.asDouble() - other.asDouble()).radians + return (this.toDouble() - other.toDouble()).radians } operator fun Degree.div(other: Degree): Double { - return (this.asDouble() / other.asDouble()) + return (this.toDouble() / other.toDouble()) } operator fun Radian.div(other: Radian): Double { - return (this.asDouble() / other.asDouble()) + return (this.toDouble() / other.toDouble()) } operator fun Degree.times(other: Number): Degree { - return (this.asDouble() * other.toDouble()).degrees + return (this.toDouble() * other.toDouble()).degrees } operator fun Radian.times(other: Number): Radian { - return (this.asDouble() * other.toDouble()).radians + return (this.toDouble() * other.toDouble()).radians } diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/angle/Degree.kt b/common/src/main/kotlin/com/mechanica/engine/unit/angle/Degree.kt index 8b12a903..fcf47c0b 100644 --- a/common/src/main/kotlin/com/mechanica/engine/unit/angle/Degree.kt +++ b/common/src/main/kotlin/com/mechanica/engine/unit/angle/Degree.kt @@ -3,13 +3,15 @@ package com.mechanica.engine.unit.angle import kotlin.math.PI inline class Degree(private val degrees: Double) : Angle { - override fun toDegrees() = asDouble().degrees + override fun toDegrees() = toDouble().degrees - override fun toRadians() = (this.asDouble()*PI /180.0).radians + override fun toRadians() = (this.toDouble()*PI /180.0).radians - override fun asDouble() = degrees + override fun toDouble() = degrees + + override fun toFloat() = degrees.toFloat() override fun toString(): String { - return asDouble().toString() + return toDouble().toString() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/angle/Radian.kt b/common/src/main/kotlin/com/mechanica/engine/unit/angle/Radian.kt index 7131f7a4..fe0320b0 100644 --- a/common/src/main/kotlin/com/mechanica/engine/unit/angle/Radian.kt +++ b/common/src/main/kotlin/com/mechanica/engine/unit/angle/Radian.kt @@ -3,13 +3,15 @@ package com.mechanica.engine.unit.angle import kotlin.math.PI inline class Radian(private val radians: Double) : Angle { - override fun toDegrees() = (this.asDouble()*180.0/ PI).degrees + override fun toDegrees() = (this.toDouble()*180.0/ PI).degrees - override fun toRadians() = asDouble().radians + override fun toRadians() = toDouble().radians - override fun asDouble() = radians + override fun toDouble() = radians + + override fun toFloat() = radians.toFloat() override fun toString(): String { - return asDouble().toString() + return toDouble().toString() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/vector/VectorTools.kt b/common/src/main/kotlin/com/mechanica/engine/unit/vector/VectorTools.kt index 652d76d6..5ce3e100 100644 --- a/common/src/main/kotlin/com/mechanica/engine/unit/vector/VectorTools.kt +++ b/common/src/main/kotlin/com/mechanica/engine/unit/vector/VectorTools.kt @@ -2,7 +2,6 @@ package com.mechanica.engine.unit.vector import com.mechanica.engine.unit.angle.Angle -import com.mechanica.engine.unit.angle.radians import kotlin.math.* @@ -19,8 +18,8 @@ fun vec(x: Number, y: Number): LightweightVector { } fun vec(r: Number, theta: Angle): LightweightVector { - val x = r.toDouble()*cos(theta.toRadians().asDouble()) - val y = r.toDouble()*sin(theta.toRadians().asDouble()) + val x = r.toDouble()*cos(theta.toRadians().toDouble()) + val y = r.toDouble()*sin(theta.toRadians().toDouble()) return vec(x, y) } diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt index 6d06b614..3b1a3939 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglLoader.kt @@ -24,7 +24,12 @@ class LwjglLoader : GLLoader { override fun createElementArray() = LwjglElementArrayType() override fun defaultShader(vertex: ShaderScript, fragment: ShaderScript, tessellation: ShaderScript?, geometry: ShaderScript?): Shader { - return object : Shader(vertex, fragment, tessellation, geometry) { + return object : Shader() { + override val vertex = vertex + override val fragment = fragment + override val tessellation = tessellation + override val geometry = geometry + private val loader: ShaderLoader by lazy { ShaderLoader(vertex, fragment, tessellation, geometry) } override val id: Int get() = loader.id diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt index 5736d15e..10122c2d 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt @@ -2,7 +2,7 @@ package com.mechanica.engine.context.loader import com.mechanica.engine.context.loader.UniformLoader import com.mechanica.engine.shader.qualifiers.Qualifier -import com.mechanica.engine.shader.vars.uniforms.* +import com.mechanica.engine.shader.uniforms.* import org.joml.Matrix4f class LwjglUniformLoader(override val qualifier: Qualifier): UniformLoader { diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglFloat.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglFloat.kt similarity index 72% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglFloat.kt rename to desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglFloat.kt index e10f4c46..75a26110 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglFloat.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglFloat.kt @@ -1,7 +1,7 @@ -package com.mechanica.engine.shader.vars.uniforms +package com.mechanica.engine.shader.uniforms import com.mechanica.engine.shader.qualifiers.Qualifier -import com.mechanica.engine.shader.vars.uniforms.vars.UniformFloat +import com.mechanica.engine.shader.uniforms.vars.UniformFloat import org.lwjgl.opengl.GL20.glUniform1f class LwjglFloat( diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglMatrix4f.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglMatrix4f.kt similarity index 80% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglMatrix4f.kt rename to desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglMatrix4f.kt index 58cb422a..a9920796 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglMatrix4f.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglMatrix4f.kt @@ -1,7 +1,7 @@ -package com.mechanica.engine.shader.vars.uniforms +package com.mechanica.engine.shader.uniforms import com.mechanica.engine.shader.qualifiers.Qualifier -import com.mechanica.engine.shader.vars.uniforms.vars.UniformMatrix4f +import com.mechanica.engine.shader.uniforms.vars.UniformMatrix4f import org.joml.Matrix4f import org.lwjgl.BufferUtils import org.lwjgl.opengl.GL20.glUniformMatrix4fv diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector2f.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector2f.kt similarity index 74% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector2f.kt rename to desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector2f.kt index 9c24284c..45e154f0 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector2f.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector2f.kt @@ -1,7 +1,7 @@ -package com.mechanica.engine.shader.vars.uniforms +package com.mechanica.engine.shader.uniforms import com.mechanica.engine.shader.qualifiers.Qualifier -import com.mechanica.engine.shader.vars.uniforms.vars.UniformVector2f +import com.mechanica.engine.shader.uniforms.vars.UniformVector2f import org.lwjgl.opengl.GL20.glUniform2f class LwjglVector2f( diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector3f.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector3f.kt similarity index 74% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector3f.kt rename to desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector3f.kt index 842d30be..b876a3cd 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector3f.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector3f.kt @@ -1,7 +1,7 @@ -package com.mechanica.engine.shader.vars.uniforms +package com.mechanica.engine.shader.uniforms import com.mechanica.engine.shader.qualifiers.Qualifier -import com.mechanica.engine.shader.vars.uniforms.vars.UniformVector3f +import com.mechanica.engine.shader.uniforms.vars.UniformVector3f import org.lwjgl.opengl.GL20.glUniform3f class LwjglVector3f( diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector4f.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector4f.kt similarity index 76% rename from desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector4f.kt rename to desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector4f.kt index 1e2b50a1..9c77da62 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/shader/vars/uniforms/LwjglVector4f.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/shader/uniforms/LwjglVector4f.kt @@ -1,7 +1,7 @@ -package com.mechanica.engine.shader.vars.uniforms +package com.mechanica.engine.shader.uniforms import com.mechanica.engine.shader.qualifiers.Qualifier -import com.mechanica.engine.shader.vars.uniforms.vars.UniformVector4f +import com.mechanica.engine.shader.uniforms.vars.UniformVector4f import org.lwjgl.opengl.GL20.glUniform4f class LwjglVector4f ( diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt index 2c3a42a3..7a3eb0dc 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt @@ -5,6 +5,7 @@ import com.mechanica.engine.drawer.shader.DrawerRenderer import com.mechanica.engine.game.Game import com.mechanica.engine.models.Model import com.mechanica.engine.models.TextModel +import com.mechanica.engine.text.Text import com.mechanica.engine.unit.vector.DynamicVector import com.mechanica.engine.unit.vector.LightweightVector import com.mechanica.engine.unit.vector.Vector @@ -23,7 +24,8 @@ class DrawData { private var projectionMatrix: Matrix4f = Game.matrices.projection private val renderer = DrawerRenderer() - private val transformation = Matrix4f().identity() + private val defaultTransformation = Matrix4f().identity() + var transformation: Matrix4f? = null private val translation: Vector3f = Vector3f() private val pivot: Vector3f = Vector3f() @@ -33,6 +35,9 @@ class DrawData { private var ry = 0f private var rz = 0f + private var skewX = 0f + private var skewY = 0f + val scaleX get() = scale.x val scaleY get() = scale.y @@ -96,21 +101,24 @@ class DrawData { scale.set(scale.x*x, scale.y*y, 0f) } + fun setSkew(x: Float, y: Float) { + skewX = x + skewY = y + } + fun setDepth(z: Float) { translation.set(translateX, translateY, translation.z-z) } fun getTransformationMatrix(matrix: Matrix4f): Matrix4f { + addSkewToMatrix(matrix) matrix.translate(translation) if (rz != 0f) matrix.rotate(rz, zAxis) - if (modelOrigin.x != 0.0 || modelOrigin.y != 0.0) { - val pivotX = modelOrigin.x.toFloat()*scale.x - val pivotY = modelOrigin.y.toFloat()*scale.y - matrix.translate(-pivotX, -pivotY, 0f) - } + addModelOriginToMatrix(matrix) + matrix.scale(scale) pivot.set(0f, 0f, 0f) @@ -121,18 +129,41 @@ class DrawData { renderer.color = fillColor renderer.radius = radius - getTransformationMatrix(transformation) - renderer.render(model, transformation, viewMatrix, projectionMatrix) + getTransformationMatrix(defaultTransformation) + transformation?.let { defaultTransformation.mul(it) } + + renderer.render(model, defaultTransformation, viewMatrix, projectionMatrix) if (!noReset) { rewind() } } + private fun addModelOriginToMatrix(matrix: Matrix4f) { + if (modelOrigin.x != 0.0 || modelOrigin.y != 0.0) { + val pivotX = modelOrigin.x.toFloat()*scale.x + val pivotY = modelOrigin.y.toFloat()*scale.y + matrix.translate(-pivotX, -pivotY, 0f) + } + } + + private fun addSkewToMatrix(matrix: Matrix4f) { + if (skewX != 0f) { + matrix.rotationZ(skewX) + matrix.m01(matrix.m10() + matrix.m01()) + } + if (skewY != 0f) { + matrix.rotationZ(skewY) + matrix.m10(matrix.m10() + matrix.m01()) + } + } + fun rewind() { rz = 0f translation.set(0f, 0f, 0f) scale.set(1f, 1f, 1f) + skewX = 0f + skewY = 0f radius = 0f noReset = false @@ -144,7 +175,8 @@ class DrawData { strokeColorWasSet = false fillColorWasSet = false - transformation.identity() + defaultTransformation.identity() + transformation = null renderer.rewind() modelOrigin.reset() } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt index 9378cbad..6acfad95 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt @@ -1,6 +1,7 @@ package com.mechanica.engine.drawer import com.mechanica.engine.color.hex +import com.mechanica.engine.drawer.shader.DrawerShader import com.mechanica.engine.drawer.subclasses.color.ColorDrawer import com.mechanica.engine.drawer.subclasses.rotation.RotatedDrawer import com.mechanica.engine.drawer.subclasses.layout.LayoutDrawer @@ -90,7 +91,9 @@ interface Drawer : RectangleDrawer, CircleDrawer, ImageDrawer, TextDrawer, PathD fun polygon(polygon: PolygonModel) - fun model(model: Model) + fun model(model: Model, blend: Float = 0f, alphaBlend: Float = 0f, colorPassthrough: Boolean = true) + + fun shader(shader: DrawerShader, model: Model?, blend: Float = 0f, alphaBlend: Float = 0f, colorPassthrough: Boolean = true) fun radius(r: Number): Drawer diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt index 1ca5f397..ebfbee88 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt @@ -1,5 +1,6 @@ package com.mechanica.engine.drawer +import com.mechanica.engine.drawer.shader.DrawerShader import com.mechanica.engine.drawer.subclasses.color.ColorDrawer import com.mechanica.engine.drawer.subclasses.color.ColorDrawerImpl import com.mechanica.engine.drawer.subclasses.rotation.RotatedDrawer @@ -87,8 +88,14 @@ class DrawerImpl(private val data: DrawData) : data.draw(polygon) } - override fun model(model: Model) { - data.colorPassthrough = true + override fun model(model: Model, blend: Float, alphaBlend: Float, colorPassthrough: Boolean) { + data.blend = blend + data.alphaBlend = alphaBlend + data.colorPassthrough = colorPassthrough data.draw(model) } + + override fun shader(shader: DrawerShader, model: Model?, blend: Float, alphaBlend: Float, colorPassthrough: Boolean) { + TODO("not implemented") + } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt new file mode 100644 index 00000000..8d4a10bc --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt @@ -0,0 +1,115 @@ +package com.mechanica.engine.drawer.shader + +import com.mechanica.engine.game.Game +import com.mechanica.engine.game.view.GameMatrices +import com.mechanica.engine.matrix.Matrices +import com.mechanica.engine.models.Model +import com.mechanica.engine.shader.script.Shader +import com.mechanica.engine.shader.script.ShaderLoader +import com.mechanica.engine.shader.script.ShaderScript +import org.joml.Matrix4f +import org.lwjgl.opengl.GL20 + +abstract class AbstractDrawerShader : Shader() { + + abstract override val vertex: DrawerScript + abstract override val fragment: DrawerScript + override val tessellation: DrawerScript? = null + override val geometry: DrawerScript? = null + + private val matrixLoaders by lazy { + val loaders = ArrayList() + loaders.add(MatrixLoader(vertex)) + + val geometry = this.geometry + if (geometry != null) { + loaders.add(MatrixLoader(geometry)) + } + + loaders + } + + private val pixelSize by lazy { fragment.uniform.float("pixelSize") } + + private val loader: ShaderLoader by lazy { ShaderLoader(vertex, fragment, tessellation, geometry) } + + override val id: Int + get() = loader.id + + fun render(model: Model, transformation: Matrix4f, projection: Matrix4f? = null, view: Matrix4f? = null) { + + loadMatrixUniforms(transformation, + projection ?: Game.matrices.projection, + view ?: Game.matrices.view + ) + load() + + model.bind() + model.draw() + } + + private fun loadMatrixUniforms(transformation: Matrix4f, projection: Matrix4f, view: Matrix4f) { + setGameMatrices(projection, view) + + for (loader in matrixLoaders) { + loader.transformation.set(transformation) + loader.projection.set(projection) + loader.view.set(view) + } + } + + private fun setGameMatrices(projection: Matrix4f, view: Matrix4f) { + + for (loader in matrixLoaders) { + loader.matrixType.value = 1f + if (projection === Game.matrices.projection) { + if (view === Game.matrices.view) { + loader.matrixType.value = 0f + loader.pvMatrix.set((Game.matrices as GameMatrices).pvMatrix) + this.pixelSize.value = (Game.matrices as GameMatrices).pixelScale + } else if (view === Game.matrices.uiView) { + loader.matrixType.value = 0f + loader.pvMatrix.set((Game.matrices as GameMatrices).pvUiMatrix) + this.pixelSize.value = (Game.matrices as GameMatrices).pixelUIScale + } + } + if (loader.matrixType.value == 1f) { + this.pixelSize.value = Matrices.calculatePixelSize(projection, view, Game.window.height) + } + } + } + + override fun loadProgram(id: Int) { + GL20.glUseProgram(id) + } + + override fun loadUniformLocation(name: String) = GL20.glGetUniformLocation(id, name) + + private class MatrixLoader(script: DrawerScript) { + val matrixType = script.uniform.float("matrixType") + + val projection = script.uniform.mat4("projection") + val transformation = script.uniform.mat4("transformation") + val view = script.uniform.mat4("view") + + val pvMatrix = script.uniform.mat4("pvMatrix") + + init { + loadMatrixMethod(script) + } + + fun loadMatrixMethod(script: ShaderScript) { + if (!script.script.contains("vec4 matrices(vec4 position)")) { + //language=GLSL + script.addOther(""" + vec4 matrices(vec4 position) { + if(matrixType == 0.0) { + return pvMatrix*transformation*position; + } + return projection*view*transformation*position; + } + """) + } + } + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt index 6725e673..26a72591 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerShader.kt @@ -1,111 +1,7 @@ package com.mechanica.engine.drawer.shader -import com.mechanica.engine.game.Game -import com.mechanica.engine.game.view.GameMatrices -import com.mechanica.engine.shader.script.ShaderScript -import com.mechanica.engine.shader.script.Shader -import com.mechanica.engine.shader.script.ShaderLoader -import com.mechanica.engine.matrix.Matrices -import com.mechanica.engine.models.Model -import org.joml.Matrix4f -import org.lwjgl.opengl.GL20 - -class DrawerShader( - vertex: DrawerScript, - fragment: DrawerScript, - tessellation: DrawerScript? = null, - geometry: DrawerScript? = null): Shader(vertex, fragment, tessellation, geometry) { - - private val matrixLoaders = ArrayList() - - private val pixelSize = fragment.uniform.float("pixelSize") - - private val loader: ShaderLoader by lazy { ShaderLoader(vertex, fragment, tessellation, geometry) } - - override val id: Int - get() = loader.id - - init { - matrixLoaders.add(MatrixLoader(vertex)) - if (geometry != null) { - matrixLoaders.add(MatrixLoader(geometry)) - } - } - - fun render(model: Model, transformation: Matrix4f, projection: Matrix4f? = null, view: Matrix4f? = null) { - - loadMatrixUniforms(transformation, - projection ?: Game.matrices.projection, - view ?: Game.matrices.view - ) - load() - - model.bind() - model.draw() - } - - private fun loadMatrixUniforms(transformation: Matrix4f, projection: Matrix4f, view: Matrix4f) { - setGameMatrices(projection, view) - - for (loader in matrixLoaders) { - loader.transformation.set(transformation) - loader.projection.set(projection) - loader.view.set(view) - } - } - - private fun setGameMatrices(projection: Matrix4f, view: Matrix4f) { - - for (loader in matrixLoaders) { - loader.matrixType.value = 1f - if (projection === Game.matrices.projection) { - if (view === Game.matrices.view) { - loader.matrixType.value = 0f - loader.pvMatrix.set((Game.matrices as GameMatrices).pvMatrix) - this.pixelSize.value = (Game.matrices as GameMatrices).pixelScale - } else if (view === Game.matrices.uiView) { - loader.matrixType.value = 0f - loader.pvMatrix.set((Game.matrices as GameMatrices).pvUiMatrix) - this.pixelSize.value = (Game.matrices as GameMatrices).pixelUIScale - } - } - if (loader.matrixType.value == 1f) { - this.pixelSize.value = Matrices.calculatePixelSize(projection, view, Game.window.height) - } - } - } - - override fun loadProgram(id: Int) { - GL20.glUseProgram(id) - } - - override fun loadUniformLocation(name: String) = GL20.glGetUniformLocation(id, name) - - private class MatrixLoader(script: DrawerScript) { - val matrixType = script.uniform.float("matrixType") - - val projection = script.uniform.mat4("projection") - val transformation = script.uniform.mat4("transformation") - val view = script.uniform.mat4("view") - - val pvMatrix = script.uniform.mat4("pvMatrix") - - init { - loadMatrixMethod(script) - } - - fun loadMatrixMethod(script: ShaderScript) { - if (!script.script.contains("vec4 matrices(vec4 position)")) { - //language=GLSL - script.addOther(""" - vec4 matrices(vec4 position) { - if(matrixType == 0.0) { - return pvMatrix*transformation*position; - } - return projection*view*transformation*position; - } - """) - } - } - } -} \ No newline at end of file +open class DrawerShader( + override val vertex: DrawerScript, + override val fragment: DrawerScript, + override val tessellation: DrawerScript? = null, + override val geometry: DrawerScript? = null): AbstractDrawerShader() \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/rotation/RotatedDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/rotation/RotatedDrawerImpl.kt index 0e9446ae..ea0c0732 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/rotation/RotatedDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/rotation/RotatedDrawerImpl.kt @@ -9,9 +9,9 @@ import com.mechanica.engine.unit.vector.Vector internal class RotatedDrawerImpl(drawer: Drawer, private val data: DrawData): RotatedDrawer, Drawer by drawer { - override fun invoke(angle: Degree) = rotate(angle.toRadians().asDouble()) + override fun invoke(angle: Degree) = rotate(angle.toRadians().toDouble()) - override fun invoke(angle: Radian) = rotate(angle.asDouble()) + override fun invoke(angle: Radian) = rotate(angle.toDouble()) override fun about(pivotX: Number, pivotY: Number): Drawer { data.modelOrigin.set(pivotX.toDouble(), pivotY.toDouble()) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt index e7d0af0b..d722d191 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawer.kt @@ -1,8 +1,13 @@ package com.mechanica.engine.drawer.subclasses.transformation import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.unit.angle.Degree +import com.mechanica.engine.unit.angle.Radian +import com.mechanica.engine.unit.angle.degrees +import com.mechanica.engine.unit.angle.radians import com.mechanica.engine.unit.vector.LightweightVector import com.mechanica.engine.unit.vector.Vector +import org.joml.Matrix4f interface TransformationDrawer : Drawer { fun translate(x: Number, y: Number): TransformationDrawer @@ -16,5 +21,12 @@ interface TransformationDrawer : Drawer { fun scale(scale: Vector) = scale(scale.x, scale.y) fun scale(scale: Double) = scale(scale, scale) + fun skew(horizontal: Degree = 0.degrees, vertical: Degree = 0.degrees): TransformationDrawer = skew(horizontal.toRadians(), vertical.toRadians()) + fun skew(horizontal: Radian = 0.radians, vertical: Radian = 0.radians): TransformationDrawer + + fun matrix(matrix: Matrix4f): TransformationDrawer + operator fun invoke(x: Number = 0.0, y: Number = 0.0, scaleX: Number = 1.0, scaleY: Number = 1.0): TransformationDrawer + + operator fun invoke(matrix: Matrix4f) = matrix(matrix) } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt index 0bc1b1e5..18f1fc1b 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/transformation/TransformationDrawerImpl.kt @@ -2,6 +2,8 @@ package com.mechanica.engine.drawer.subclasses.transformation import com.mechanica.engine.drawer.DrawData import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.unit.angle.Radian +import org.joml.Matrix4f class TransformationDrawerImpl(drawer: Drawer, private val data: DrawData): TransformationDrawer, Drawer by drawer { override fun translate(x: Number, y: Number): TransformationDrawer { @@ -14,6 +16,16 @@ class TransformationDrawerImpl(drawer: Drawer, private val data: DrawData): Tran return this } + override fun skew(horizontal: Radian, vertical: Radian): TransformationDrawer { + data.setSkew(horizontal.toDouble().toFloat(), vertical.toDouble().toFloat()) + return this + } + + override fun matrix(matrix: Matrix4f): TransformationDrawer { + data.transformation = matrix + return this + } + override fun invoke(x: Number, y: Number, scaleX: Number, scaleY: Number): TransformationDrawer { translate(x, y) scale(scaleX, scaleY) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt index 50328347..8587834b 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/InputProcess.kt @@ -3,7 +3,7 @@ package com.mechanica.engine.scenes.processes import com.mechanica.engine.input.keyboard.Keyboard import com.mechanica.engine.input.mouse.Mouse -abstract class InputProcess(priority: Int = -1) : Process(priority) { +abstract class InputProcess(order: Int = -1) : Process(order) { val keyboard = Keyboard.create() val mouse = Mouse.create() } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt index 442b9df7..679266cb 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt @@ -2,7 +2,7 @@ package com.mechanica.engine.scenes.processes import com.mechanica.engine.scenes.exclusiveScenes.ExclusiveActivationMap -abstract class Process(override val priority: Int = 0) : ProcessNode { +abstract class Process(override val order: Int = 0) : ProcessNode { private val activationListeners = ArrayList() private var _active = true @@ -52,7 +52,7 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { if (!processes.contains(process)) { processes.add(process) - processes.sortBy { it.priority } + processes.sortBy { it.order } } return process } @@ -68,7 +68,7 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { if (index != -1) { removeProcess(processes[index]) processes.add(index, new) - processes.sortBy { it.priority } + processes.sortBy { it.order } return new } return old @@ -83,9 +83,9 @@ abstract class Process(override val priority: Int = 0) : ProcessNode { override fun updateNodes(delta: Double) { if (!callbacksInitialized) runActivationCallbacks(active) - val index = updateNodesFor(delta) { it.priority < 0 } + val index = updateNodesFor(delta) { it.order < 0 } this.update(delta) - updateNodesFor(delta, index) { it.priority >= 0 } + updateNodesFor(delta, index) { it.order >= 0 } } /** diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt index 19853d76..10447a6c 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt @@ -1,7 +1,13 @@ package com.mechanica.engine.scenes.processes interface ProcessNode : Updateable { - val priority: Int + /** + * The [order] value dictates the order in which the processes are updated and/or rendered, + * the lower the order value, the earlier the process will update. + * The value can be negative, in which case the process will be updated before the parent process + * has been updated. If it is positive, it will happen after. + */ + val order: Int get() = 0 val active: Boolean get() = true diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt index 74137094..d5fef485 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt @@ -4,14 +4,13 @@ import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.view.DynamicView import com.mechanica.engine.game.view.View import com.mechanica.engine.unit.vector.DynamicVector -import com.mechanica.engine.unit.vector.Vector abstract class DynamicScene( x: Double = 0.0, y: Double = 0.0, width: Double = 1.0, height: Double = 1.0, - priority: Int = 0) : Scene(priority) { + order: Int = 0) : Scene(order) { constructor( view: View, diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt index 1562d1d2..6a659bf6 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/MainScene.kt @@ -3,7 +3,7 @@ package com.mechanica.engine.scenes.scenes import com.mechanica.engine.game.Game import com.mechanica.engine.game.view.GameView -abstract class MainScene(priority: Int = 0) : Scene(priority) { +abstract class MainScene(order: Int = 0) : Scene(order) { override val view: GameView get() = Game.view diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt index 35ae688d..1706e312 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt @@ -5,7 +5,7 @@ import com.mechanica.engine.game.view.View import com.mechanica.engine.scenes.exclusiveScenes.ExclusiveActivationMap import com.mechanica.engine.scenes.processes.Process -abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { +abstract class Scene(order: Int = 0) : Process(order), SceneNode { protected val childScenes: List = ArrayList() @@ -20,7 +20,7 @@ abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { if (!scenes.contains(scene)) { scenes.add(scene) - scenes.sortBy { it.priority } + scenes.sortBy { it.order } } return scene } @@ -38,7 +38,7 @@ abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { if (index != -1) { scenes.removeAt(index) scenes.add(index, new) - scenes.sortBy { it.priority } + scenes.sortBy { it.order } return new } return old @@ -51,9 +51,9 @@ abstract class Scene(priority: Int = 0) : Process(priority), SceneNode { } override fun renderNodes(draw: Drawer) { - val index = renderNodesFor(draw) { it.priority < 0} + val index = renderNodesFor(draw) { it.order < 0} this.render(draw) - renderNodesFor(draw, index) { it.priority >= 0} + renderNodesFor(draw, index) { it.order >= 0 } } private inline fun renderNodesFor(draw: Drawer, from: Int = 0, condition: (SceneNode) -> Boolean): Int { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt index d51b1f4f..cc4447d4 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/StaticScene.kt @@ -1,12 +1,11 @@ package com.mechanica.engine.scenes.scenes import com.mechanica.engine.drawer.Drawer -import com.mechanica.engine.game.view.DynamicView import com.mechanica.engine.game.view.View import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.unit.vector.vec -abstract class StaticScene(final override val view: View, priority: Int = 0) : Scene(priority) { +abstract class StaticScene(final override val view: View, order: Int = 0) : Scene(order) { constructor( x: Double = 0.0, y: Double = 0.0, diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt index fb5ae1bf..9950fdae 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/renderer/Main.kt @@ -2,18 +2,19 @@ package com.mechanica.engine.samples.renderer import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game -import com.mechanica.engine.models.Image -import com.mechanica.engine.utils.loadImage import com.mechanica.engine.input.keyboard.Keyboard import com.mechanica.engine.input.mouse.Mouse +import com.mechanica.engine.models.Image import com.mechanica.engine.models.PolygonModel import com.mechanica.engine.resources.Res import com.mechanica.engine.scenes.scenes.MainScene import com.mechanica.engine.unit.angle.degrees import com.mechanica.engine.unit.vector.vec +import com.mechanica.engine.utils.loadImage import org.joml.Matrix4f import org.lwjgl.opengl.GL11.GL_STENCIL_TEST import org.lwjgl.opengl.GL11.glEnable +import kotlin.math.tan fun main() { @@ -34,6 +35,15 @@ private class StartMain : MainScene() { init { transformation.scale(1f, 1f,1f) + println(transformation) + println() + + transformation.rotationZ((-Math.PI/6f).toFloat()) + transformation.m01(transformation.m10() + transformation.m01()) + +// transformation.rotationZ((-Math.PI/6f).toFloat()) +// transformation.m10(transformation.m10() + transformation.m01()) + image = loadImage(Res.image["testImage"]) @@ -80,7 +90,8 @@ private class StartMain : MainScene() { draw.stroke(0.1).transformed.scale(cursorFraction*3.0).blue.polygon(polygon) draw.centered.rotated(blend).about(0, 1).image(image, 0, 0, 1, 1) - draw.image(image, -0.5, -0.5, 1, 1) + draw.transformed(transformation).image(image, -0.5, -0.5, 1, 1) + draw.rotated(30.degrees).image(image, 0.5, -0.5, 1, 1) draw.stroke(0.1).red.circle(0, 3, 1.0) draw.stroke(0.1).green.line(vec(4, 3), Mouse.world) } From 7eb4ae2eaa739429dfe2d9c23cbad1d38a14eac9 Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Tue, 16 Jun 2020 02:54:35 +0100 Subject: [PATCH 11/21] Added logic for animating values Fixed a bug with the View classes Updated how the persisted save data works --- .../com/mechanica/engine/models/TextModel.kt | 6 +- .../engine/persistence/PersistenceMap.kt | 70 +++++++++++ .../engine/persistence/PersistenceUtil.kt | 21 ++++ .../engine/persistence/PersistentVariable.kt | 64 ++++++++++ .../mechanica/engine/resources/Resource.kt | 3 +- .../engine/shader/qualifiers/Attribute.kt | 17 ++- build.gradle.kts | 6 +- .../com/mechanica/engine/util/JsonUtil.kt | 65 ++++++++++ .../engine/util/extensions/Numbers.kt | 26 +++- .../context/loader/LwjglUniformLoader.kt | 1 - .../engine/persistence/PersistenceUtil.kt | 112 ------------------ .../engine/animation/AnimationController.kt | 35 ++++++ .../engine/animation/AnimationFormula.kt | 59 +++++++++ .../engine/animation/AnimationFormulas.kt | 79 ++++++++++++ .../engine/animation/AnimationSequence.kt | 67 +++++++++++ .../engine/animation/FrameAnimation.kt | 74 ++++++++---- .../com/mechanica/engine/drawer/DrawData.kt | 6 +- .../com/mechanica/engine/drawer/Drawer.kt | 3 +- .../com/mechanica/engine/drawer/DrawerImpl.kt | 18 ++- .../drawer/shader/AbstractDrawerShader.kt | 4 +- .../engine/drawer/shader/DrawerRenderer.kt | 4 +- .../engine/drawer/shader/PathRenderer.kt | 2 +- .../drawer/subclasses/color/ColorDrawer.kt | 10 +- .../superclass/circle/CircleDrawerImpl.kt | 2 +- .../superclass/image/ImageDrawerImpl.kt | 4 +- .../rectangle/RectangleDrawerImpl.kt | 2 +- .../drawer/superclass/text/TextDrawer.kt | 4 +- .../kotlin/com/mechanica/engine/game/Game.kt | 14 +-- .../game/configuration/ConfigurationData.kt | 1 - .../game/configuration/GameConfiguration.kt | 2 - .../configuration/GameConfigurationImpl.kt | 4 - .../engine/game/configuration/GameSetup.kt | 1 - .../NullableConfigurationData.kt | 4 +- .../engine/game/view/DefaultDynamicView.kt | 6 +- .../mechanica/engine/game/view/GameView.kt | 8 +- .../mechanica/engine/game/view/StaticView.kt | 4 +- .../com/mechanica/engine/scenes/SceneUtil.kt | 67 +++++++++++ .../engine/scenes/processes/Process.kt | 94 ++++++++++----- .../engine/scenes/processes/ProcessNode.kt | 6 +- .../engine/scenes/scenes/DynamicScene.kt | 8 +- .../engine/scenes/scenes/GUIScene.kt | 2 +- .../mechanica/engine/scenes/scenes/Scene.kt | 19 --- samples/build.gradle.kts | 1 + .../engine/samples/data/SaveDataExample.kt | 39 ++++++ .../samples/shaders/FragmentRenderer.kt | 2 +- .../com/mechanica/engine/samples/temp/Test.kt | 4 +- 46 files changed, 794 insertions(+), 256 deletions(-) create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt create mode 100644 application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt create mode 100644 common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt delete mode 100644 desktop-application/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationController.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormula.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormulas.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationSequence.kt create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneUtil.kt create mode 100644 samples/src/main/kotlin/com/mechanica/engine/samples/data/SaveDataExample.kt diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt b/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt index 8d9e9ed2..afc6bd85 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/models/TextModel.kt @@ -55,10 +55,8 @@ class TextModel(text: Text, } fun setText(text: Text) { - if (text.string != this.string || textHolder != text) { - this.textHolder = text - updateTextHolder(text) - } + this.textHolder = text + updateTextHolder(text) } private fun updateTextHolder(text: Text) { diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt new file mode 100644 index 00000000..b1a41c7e --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt @@ -0,0 +1,70 @@ +package com.mechanica.engine.persistence + +import com.mechanica.engine.resources.ExternalResource +import com.mechanica.engine.util.StringMapSerializer +import kotlinx.serialization.UnstableDefault +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import java.io.FileNotFoundException + +class PersistenceMap private constructor(private val path: String, private val map: HashMap) : Map by map { + constructor(path: String) : this(path, HashMap()) + + private val serializer = StringMapSerializer() + + fun store() { + val json = Json(JsonConfiguration.Stable) + val string = json.stringify(serializer, map) + + val file = ExternalResource(path, true) + file.write(string) + } + + @OptIn(UnstableDefault::class) + fun populate() { + val string = try { + val file = ExternalResource(path, false) + file.contents + } catch (ex: FileNotFoundException) { + "{}" + } + + + val map = Json.parse(serializer, string) + map.forEach { + this.map[it.key] = it.value + } + } + + fun put(name: String, value: Any) { + map[name] = value + } + + fun getDouble(name: String, default: Double = 0.0): Double { + return map[name] as? Double ?: default + } + + fun getFloat(name: String, default: Float = 0f): Float { + return map[name] as? Float ?: default + } + + fun getBoolean(name: String, default: Boolean = false): Boolean { + return map[name] as? Boolean ?: default + } + + fun getInt(name: String, default: Int = 0): Int { + return map[name] as? Int ?: default + } + + fun getLong(name: String, default: Long = 0): Long { + return map[name] as? Long ?: default + } + + fun getString(name: String, default: String = ""): String { + return map[name] as? String ?: default + } + + override fun toString() = map.toString() + +} + diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt new file mode 100644 index 00000000..99201f86 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt @@ -0,0 +1,21 @@ +package com.mechanica.engine.persistence + +private val map = PersistenceMap("res/data/persistence.txt") + +fun persist(default: Boolean, instance: String? = null) = PersistentVariable(map, default, instance) + +fun persist(default: Int, instance: String? = null) = PersistentVariable(map, default, instance) +fun persist(default: Long, instance: String? = null) = PersistentVariable(map, default, instance) + +fun persist(default: Float, instance: String? = null) = PersistentVariable(map, default, instance) +fun persist(default: Double, instance: String? = null) = PersistentVariable(map, default, instance) + +fun persist(default: String, instance: String? = null) = PersistentVariable(map, default, instance) + +fun storeData() { + map.store() +} + +fun populateData() { + map.populate() +} diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt new file mode 100644 index 00000000..04b821b7 --- /dev/null +++ b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt @@ -0,0 +1,64 @@ +package com.mechanica.engine.persistence + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +class PersistentVariable( + private val map: PersistenceMap, + private val defaultValue: T, + private val instance: String? = null) : ReadWriteProperty { + + private var hasRetrieved = false + private var value: T = defaultValue + + private var fullPropertyName: String? = null + + override fun getValue(thisRef: Any, property: KProperty<*>): T { + if (!hasRetrieved) { + val name = fullPropertyName(thisRef, property) + value = if (instance == null) { + map.retrieveSingleValue(name) + } else { + map.retrieveInstancedValue(instance, name) + } + hasRetrieved = true + } + return value + } + + private fun Map<*, *>.retrieveSingleValue(name: String): T { + @Suppress("UNCHECKED_CAST") + return this[name] as? T ?: defaultValue + } + + private fun Map<*, *>.retrieveInstancedValue(instance: String, name: String): T { + val subMap = subMap(instance) ?: return defaultValue + + return subMap.retrieveSingleValue(name) + } + + private fun Map<*, *>.subMap(instance: String) : HashMap? { + @Suppress("UNCHECKED_CAST") + return this[instance] as? HashMap + } + + override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { + this.value = value + val name = fullPropertyName(thisRef, property) + if (instance == null) { + map.put(name, value) + } else { + map.subMap(instance)?.put(name, value) + } + } + + private fun fullPropertyName(ref: Any, property: KProperty<*>): String { + val fullName = this.fullPropertyName + return if (fullName != null) fullName + else { + val newName = ref::class.java.canonicalName + "." + property.name + fullPropertyName = newName + newName + } + } +} \ No newline at end of file diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt b/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt index 3c1603ae..872e79dc 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/resources/Resource.kt @@ -2,6 +2,7 @@ package com.mechanica.engine.resources import com.mechanica.engine.context.loader.GLLoader import java.io.BufferedReader +import java.io.FileNotFoundException import java.io.InputStream import java.io.InputStreamReader import java.net.URI @@ -39,7 +40,7 @@ interface Resource : GenericResource { operator fun invoke(file: String): Resource { val fileForURL = file.replace("\\", "/") - val url = getResourceURL(fileForURL) ?: throw IllegalStateException("Resource not found at $fileForURL") + val url = getResourceURL(fileForURL) ?: throw FileNotFoundException("Resource not found at $fileForURL") return ResourceImpl(url) } diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Attribute.kt b/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Attribute.kt index 93d273c3..381efb7e 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Attribute.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/shader/qualifiers/Attribute.kt @@ -13,7 +13,7 @@ import com.mechanica.engine.vertices.FloatBufferMaker import org.joml.Vector3f import org.joml.Vector4f -class Attribute( +class Attribute internal constructor( override val location: Int, private val variables: ScriptVariables? = null) : AttributeQualifier, AttributeVars { @@ -81,8 +81,19 @@ class Attribute( } companion object { - operator fun invoke(location: Int): AttributeVars { - return Attribute(location) + private val attributeLocations = Array(20) { null} + + fun location(location: Int): AttributeVars { + return if (location > attributeLocations.lastIndex) { + Attribute(location) + } else { + val nullable = attributeLocations[location] + if (nullable == null) { + val attribute = Attribute(location) + attributeLocations[location] = attribute + attribute + } else nullable + } } } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 1c6b8b01..27f9b75e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile val lwjglVersion = "3.2.3" val lwjglNatives = "natives-windows" -val kotlinVersion = "1.3.50" +val kotlinVersion = "1.3.70" buildscript { repositories { @@ -18,7 +18,8 @@ buildscript { } plugins { - kotlin("jvm") version "1.3.61" + kotlin("jvm") version "1.3.70" + kotlin("plugin.serialization") version "1.3.70" `java-library` maven } @@ -28,6 +29,7 @@ val commonDependencies: DependencyHandlerScope.() -> Unit = { // Align versions of all Kotlin components implementation(platform("org.jetbrains.kotlin:kotlin-bom")) + implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") diff --git a/common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt b/common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt new file mode 100644 index 00000000..acb9f3d5 --- /dev/null +++ b/common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt @@ -0,0 +1,65 @@ +package com.mechanica.engine.util + +import kotlinx.serialization.* +import kotlinx.serialization.json.* + + +fun Map<*, *>.toJson(): JsonObject { + return json { + for (entry in this@toJson) { + addSupportedType(entry.key.toString(), entry.value) + } + } +} + +fun JsonObject.toStringValueMap(): Map { + return this.mapNotNull { + val value = it.value.getSupportedType() + + if (value != null) { + it.key to value + } else null + + }.toMap() +} + +fun JsonObjectBuilder.addSupportedType(key: String, value: V) { + when (value) { + is Number -> key to value + is String -> key to value + is Boolean -> key to value + is JsonElement -> key to value + is Map<*, *> -> key to value.toJson() + } +} + +fun JsonElement.getSupportedType(): Any? { + return try { + booleanOrNull ?: intOrNull ?: longOrNull ?: floatOrNull ?: doubleOrNull ?: contentOrNull + } catch (ex: JsonException) { + if (this is JsonObject) { + return this.toStringValueMap() + } else null + } +} + +fun Decoder.toJsonObject(): JsonObject { + val input = this as? JsonInput ?: throw SerializationException("Expected Json Input") + return input.decodeJson() as? JsonObject ?: throw SerializationException("Expected JsonArray") +} + + +class StringMapSerializer : DeserializationStrategy>, SerializationStrategy> { + + override val descriptor = SerialDescriptor("PersistenceMap") + + override fun deserialize(decoder: Decoder) = decoder.toJsonObject().toStringValueMap() + + override fun serialize(encoder: Encoder, value: Map) { + JsonElementSerializer.serialize(encoder, value.toJson()) + } + + override fun patch(decoder: Decoder, old: Map): Map = + throw UpdateNotSupportedException("Update not supported") + +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/util/extensions/Numbers.kt b/common/src/main/kotlin/com/mechanica/engine/util/extensions/Numbers.kt index 3a05f536..8436af64 100644 --- a/common/src/main/kotlin/com/mechanica/engine/util/extensions/Numbers.kt +++ b/common/src/main/kotlin/com/mechanica/engine/util/extensions/Numbers.kt @@ -3,6 +3,7 @@ package com.mechanica.engine.util.extensions import kotlin.math.max import kotlin.math.min +import kotlin.math.sign fun Number.isHigher(other: Number): Boolean { return this.toDouble() > other.toDouble() @@ -55,8 +56,29 @@ fun Number.isLower(other1: Number, other2: Number, other3: Number, vararg others } fun Double.constrain(lower: Double, higher: Double): Double { - val restrainLower = max(this, lower) - return min(restrainLower, higher) + val sign = sign(higher - lower) + if (sign == 0.0) return lower + + val restrainLower = max(sign*this, sign*lower) + return sign*min(restrainLower, sign*higher) +} + +fun Double.constrainLooped(lower: Double, higher: Double): Double { + val diff = (higher - lower) + val sign = sign(diff) + + if (sign == 0.0) return lower + + return when { + sign*this < sign*lower -> higher + sign * (sign * this - lower) % diff + sign*this > sign*higher -> lower + sign * (sign * this - higher) % diff + else -> this + } +} + +fun main() { + val num = (7.0).constrain(5.0, 5.0) + println(num) } fun Int.constrain(lower: Int, higher: Int): Int { diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt index 10122c2d..0b0c7669 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/context/loader/LwjglUniformLoader.kt @@ -1,6 +1,5 @@ package com.mechanica.engine.context.loader -import com.mechanica.engine.context.loader.UniformLoader import com.mechanica.engine.shader.qualifiers.Qualifier import com.mechanica.engine.shader.uniforms.* import org.joml.Matrix4f diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt deleted file mode 100644 index 928224a9..00000000 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.mechanica.engine.persistence - -import com.mechanica.engine.resources.ExternalResource -import java.io.File -import java.lang.reflect.Method -import java.util.* -import kotlin.collections.HashMap -import kotlin.collections.forEach -import kotlin.collections.set - -fun saveData(dataObj: Any) { - val getters = ArrayList() - for (method in dataObj.methods) { - if (method.name.startsWith("get") && method.name != "getClass") { - getters.add(method) - } - } - - val contentBuilder = StringBuilder() - - for (method in getters) { - contentBuilder - .append(method.camelCaseName).append(",") - .append(method.returnType.simpleName).append(",") - .append(method.invoke(dataObj)).append("\n") - } - val content = contentBuilder.toString() - - val file = resource(dataObj.dataFile, true) - file.write(content) - -} - -fun loadData(dataObj: Any) { - data class Variable(val name: String, val type: String, val value: String, val setter: Method?, val getter: Method?) - fun Variable.set() { - if (this.setter != null) { - when (this.type) { - "int" -> setter.invoke(dataObj, value.toIntOrNull()?: getter?.invoke(dataObj)?: 0) - "double" -> setter.invoke(dataObj, value.toDoubleOrNull()?: getter?.invoke(dataObj)?: 0.0) - "float" -> setter.invoke(dataObj, value.toFloatOrNull()?: getter?.invoke(dataObj)?: 0f) - "long" -> setter.invoke(dataObj, value.toLongOrNull()?: getter?.invoke(dataObj)?: 0L) - "boolean" -> setter.invoke(dataObj, value.toBoolean()) - "String" -> setter.invoke(dataObj, value) - } - } - } - - val setters = getSetters(dataObj) - val getters = getGetters(dataObj) - - val dataFile = resource(dataObj.dataFile, true) - - - dataFile.lines.forEach { - val params = it.split(",") - val name = params[0] - val v = Variable(name, params[1], params[2], setters[name], getters[name]) - v.set() - } - -} - -private fun getGetters(dataObj: Any): HashMap { - val getters = HashMap() - for (method in dataObj.methods) { - if (method.name.startsWith("get") && method.name != "getClass") { - getters[method.camelCaseName] = method - } - } - - return getters -} - - -private fun getSetters(dataObj: Any): HashMap { - val setters = HashMap() - for (method in dataObj.methods) { - if (method.name.startsWith("set")) { - setters[method.camelCaseName] = method - } - } - - return setters -} - -private fun resource(path: String, createIfAbsent: Boolean = false): ExternalResource { - val prefix = "res/" - return ExternalResource("$prefix$path", createIfAbsent) -} - -private val Method.camelCaseName: String - get() = this.name - .replace("get", "") - .replace("set", "") - .replace(0..1) { it.toLowerCase() + this } - -private fun String.replace(range: IntRange, function: String.(String) -> String): String { - val rangeString = this.substring(range) - val removed = this.removeRange(range) - return removed.function(rangeString) -} - -private const val directory: String = "data" - -private val Any.methods get() = this::class.java.methods - -private val Any.dataFile get() = directory + sep + this.name + ".txt" - -private val Any.name get() = this::class.java.name - -private val sep get() = File.separator diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationController.kt b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationController.kt new file mode 100644 index 00000000..8bdedc31 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationController.kt @@ -0,0 +1,35 @@ +package com.mechanica.engine.animation + +import com.mechanica.engine.scenes.processes.Updateable + +interface AnimationController : Updateable { + + val startTime: Double + val endTime: Double + + val time: Double + + var looped: Boolean + var paused: Boolean + + val isPlaying: Boolean + get() = !paused + + fun goTo(time: Double) + + fun restart() { + goTo(startTime) + update(0.0) + } + + fun pause() { + paused = true + } + + fun play() { + paused = false + if (time == endTime) { + restart() + } + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormula.kt b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormula.kt new file mode 100644 index 00000000..a701d307 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormula.kt @@ -0,0 +1,59 @@ +package com.mechanica.engine.animation + +import com.mechanica.engine.util.extensions.constrain +import com.mechanica.engine.util.extensions.constrainLooped + +class AnimationFormula( + startTime: Double, endTime: Double, + private var formula: AnimationFormulas.(Double) -> Double): AnimationController { + + private val formulas = AnimationFormulas(this) + + override var startTime: Double = startTime + private set + override var endTime: Double = endTime + private set + + override var looped = false + override var paused = false + + var value = formula(formulas, 0.0) + private set + + override var time = startTime + private set(value) { + field = if (looped) { + value.constrainLooped(startTime, endTime) + } else { + value.constrain(startTime, endTime) + } + } + private val relativeTime: Double + get() = time - startTime + + constructor(length: Double, formula: AnimationFormulas.(Double) -> Double) : this(0.0, length, formula) + + override fun goTo(time: Double) { + this.time = time + } + + fun start(start: Double) { + this.startTime = start + } + + fun length(length: Double) { + endTime = startTime + length + } + + fun end(end: Double) { + this.endTime = end + } + + override fun update(delta: Double) { + if (!paused) { + time += delta + value = formula(formulas, relativeTime) + } + } + +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormulas.kt b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormulas.kt new file mode 100644 index 00000000..39c17161 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationFormulas.kt @@ -0,0 +1,79 @@ +package com.mechanica.engine.animation + +class AnimationFormulas(private val formula: AnimationFormula) { + val startTime: Double + get() = formula.startTime + val endTime: Double + get() = formula.endTime + val length: Double + get() = endTime - startTime + + fun progress(time: Double): Double { + return time / length + } + + fun absoluteTime(time: Double): Double = time + startTime + + fun linear(time: Double, start: Double, end: Double, timeSpan: Double = length): Double { + val slope = (end - start)/timeSpan + return lineEquation(time, slope, start) + } + + fun lineEquation(time: Double, slope: Double, yIntercept: Double): Double { + return slope*time + yIntercept + } + + fun sin(time: Double, frequency: Double = 1.0, amplitude: Double = 1.0, phase: Double = 0.0): Double { + return amplitude* kotlin.math.sin(frequency * time + phase) + } + + fun quadratic(time: Double, a: Double = 1.0, b: Double = 1.0, c: Double = 1.0): Double { + return time*time*a + time*b + c + } + + fun quadraticDescending(time: Double, start: Double, end: Double, timeSpan: Double = length): Double { + return quadratic(time, -(start-end)/(timeSpan*timeSpan), 0.0, start) + } + + fun quadraticAscending(time: Double, start: Double, end: Double, timeSpan: Double = length): Double { + val diff = (start-end) + val a = diff/(timeSpan*timeSpan) + val b = -2*diff/timeSpan + val c = start + return quadratic(time, a, b, c) + } + + fun quadraticBump(time: Double, base: Double, bumpSize: Double, timeSpan: Double = length): Double { + val constant = 4*bumpSize + + val a = -constant/(timeSpan*timeSpan) + val b = constant/timeSpan + val c = base + + return quadratic(time, a, b, c) + } + + companion object { + fun linear(start: Double, end: Double): AnimationFormulas.(Double) -> Double = { linear(it, start, end) } + + fun sin(frequency: Double = 1.0, amplitude: Double = 1.0, phase: Double = 0.0): AnimationFormulas.(Double) -> Double { + return { sin(it, frequency, amplitude, phase) } + } + + fun quadratic(a: Double = 1.0, b: Double = 1.0, c: Double = 1.0): AnimationFormulas.(Double) -> Double { + return { quadratic(it, a, b, c) } + } + + fun quadraticDescending(start: Double, end: Double): AnimationFormulas.(Double) -> Double { + return { quadraticDescending(it, start, end) } + } + + fun quadraticAscending(start: Double, end: Double): AnimationFormulas.(Double) -> Double { + return { quadraticAscending(it, start, end) } + } + + fun quadraticBump(base: Double, bumpSize: Double): AnimationFormulas.(Double) -> Double { + return { quadraticBump(it, base, bumpSize) } + } + } +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationSequence.kt b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationSequence.kt new file mode 100644 index 00000000..e72d91e3 --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/animation/AnimationSequence.kt @@ -0,0 +1,67 @@ +package com.mechanica.engine.animation + +import com.mechanica.engine.util.extensions.fori + +class AnimationSequence(private vararg val animations: AnimationController): AnimationController { + override val startTime: Double + override val endTime: Double + + override var looped: Boolean = false + + override var time: Double = 0.0 + private set + + override var paused = false + set(value) { + field = value + } + + init { + var startTime = 0.0 + var endTime = 0.0 + for (a in animations) { + if (a.startTime < startTime) { + startTime = a.startTime + } + if (a.endTime > endTime) { + endTime = a.endTime + } + } + this.startTime = startTime + this.endTime = endTime + + time = startTime + } + + override fun pause() { + super.pause() + animations.fori { + it.pause() + } + } + + override fun play() { + super.play() + animations.fori { + it.paused = false + } + } + + override fun goTo(time: Double) { + this.time = time + animations.fori { + it.goTo(time) + } + } + + override fun update(delta: Double) { + if (!paused) { + animations.fori { + it.goTo(time) + it.update(delta) + } + time += delta + } + } + +} \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt b/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt index 0e5aed8b..3a261957 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt @@ -2,45 +2,75 @@ package com.mechanica.engine.animation import com.mechanica.engine.models.Image import com.mechanica.engine.resources.ResourceDirectory +import com.mechanica.engine.util.extensions.constrain +import com.mechanica.engine.util.extensions.constrainLooped import com.mechanica.engine.utils.loadImage -import kotlin.math.abs import kotlin.math.floor +import kotlin.math.sign -class FrameAnimation(private val frames: List, frameRate : Double) { - val duration = frames.size.toDouble()/frameRate - private var progress = 0.0 - set(value) { field = value%duration } +class FrameAnimation(private val frames: List, frameRate : Double, + override var startTime: Double = 0.0, + override var endTime: Double = frames.size.toDouble()/frameRate): AnimationController { + val duration: Double + get() = endTime - startTime + private val direction = sign(duration) + private val originalDuration = duration - var reversed = false + override var paused = false - var fraction: Double - get() = progress/duration - set(value) { - progress = value*duration + override var time: Double = startTime + private set(value) { + field = if (looped) { + value.constrainLooped(startTime, endTime) + } else { + value.constrain(startTime, endTime) + } } + private val relativeTime: Double + get() = time - startTime + + private val fraction: Double + get() = relativeTime/duration - var currentFrame: Image + val currentFrame: Image get() { - val index = floor(fraction*frames.size).toInt() + val index = floor(fraction*(frames.size-1)).toInt() return frames[index] } - set(value) { - fraction = value.id.toDouble()/frames.size.toDouble() - } - var scale: Double = 1.0 + var scale: Double + get() = duration/originalDuration set(value) { - field = abs(value) + val scaleValue = direction/value + endTime = startTime + scaleValue*originalDuration + time = startTime + scaleValue*fraction*originalDuration } - fun update(delta: Double) { - if (!reversed) { - progress += scale*delta - } else { - progress -= scale*delta + override var looped: Boolean = true + + override fun goTo(time: Double) { + this.time = time + } + + override fun restart() { + time = startTime + } + + override fun pause() { + paused = true + } + + override fun play() { + paused = false + if (time == endTime) { + time = startTime } } + override fun update(delta: Double) { + time += direction*delta + } + companion object { fun loadAnimation(directory: ResourceDirectory, frameRate: Double = 24.0) = FrameAnimation(directory.map { loadImage(it) }, frameRate) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt index 7a3eb0dc..18838bc9 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt @@ -1,7 +1,9 @@ package com.mechanica.engine.drawer import com.mechanica.engine.color.DynamicColor +import com.mechanica.engine.drawer.shader.AbstractDrawerShader import com.mechanica.engine.drawer.shader.DrawerRenderer +import com.mechanica.engine.drawer.shader.DrawerShader import com.mechanica.engine.game.Game import com.mechanica.engine.models.Model import com.mechanica.engine.models.TextModel @@ -125,14 +127,14 @@ class DrawData { return matrix } - fun draw(model: Model) { + fun draw(model: Model, shader: AbstractDrawerShader = renderer.shader) { renderer.color = fillColor renderer.radius = radius getTransformationMatrix(defaultTransformation) transformation?.let { defaultTransformation.mul(it) } - renderer.render(model, defaultTransformation, viewMatrix, projectionMatrix) + shader.render(model, defaultTransformation, projectionMatrix, viewMatrix) if (!noReset) { rewind() diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt index 6acfad95..31717ab4 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/Drawer.kt @@ -1,6 +1,7 @@ package com.mechanica.engine.drawer import com.mechanica.engine.color.hex +import com.mechanica.engine.drawer.shader.AbstractDrawerShader import com.mechanica.engine.drawer.shader.DrawerShader import com.mechanica.engine.drawer.subclasses.color.ColorDrawer import com.mechanica.engine.drawer.subclasses.rotation.RotatedDrawer @@ -93,7 +94,7 @@ interface Drawer : RectangleDrawer, CircleDrawer, ImageDrawer, TextDrawer, PathD fun model(model: Model, blend: Float = 0f, alphaBlend: Float = 0f, colorPassthrough: Boolean = true) - fun shader(shader: DrawerShader, model: Model?, blend: Float = 0f, alphaBlend: Float = 0f, colorPassthrough: Boolean = true) + fun shader(shader: AbstractDrawerShader, model: Model? = null) fun radius(r: Number): Drawer diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt index ebfbee88..4598565d 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawerImpl.kt @@ -1,5 +1,7 @@ package com.mechanica.engine.drawer +import com.mechanica.engine.context.loader.GLLoader +import com.mechanica.engine.drawer.shader.AbstractDrawerShader import com.mechanica.engine.drawer.shader.DrawerShader import com.mechanica.engine.drawer.subclasses.color.ColorDrawer import com.mechanica.engine.drawer.subclasses.color.ColorDrawerImpl @@ -22,9 +24,12 @@ import com.mechanica.engine.drawer.superclass.rectangle.RectangleDrawerImpl import com.mechanica.engine.drawer.superclass.text.TextDrawer import com.mechanica.engine.drawer.superclass.text.TextDrawerImpl import com.mechanica.engine.game.Game +import com.mechanica.engine.models.Bindable import com.mechanica.engine.models.Model import com.mechanica.engine.models.PolygonModel +import com.mechanica.engine.shader.qualifiers.Attribute import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL20 class DrawerImpl(private val data: DrawData) : RectangleDrawer by RectangleDrawerImpl(data), @@ -35,6 +40,15 @@ class DrawerImpl(private val data: DrawData) : Drawer { + private val model: Model + + init { + val position = Attribute.location(0).vec3().createUnitQuad() + val texCoords = Attribute.location(1).vec2().createInvertedUnitQuad() + + model = Model(position, texCoords, draw = GLLoader.graphicsLoader::drawArrays) + } + private val colorDrawer = ColorDrawerImpl(this, data) override val color: ColorDrawer get() = colorDrawer @@ -95,7 +109,7 @@ class DrawerImpl(private val data: DrawData) : data.draw(model) } - override fun shader(shader: DrawerShader, model: Model?, blend: Float, alphaBlend: Float, colorPassthrough: Boolean) { - TODO("not implemented") + override fun shader(shader: AbstractDrawerShader, model: Model?) { + data.draw(model ?: this.model, shader) } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt index 8d4a10bc..600a605f 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/AbstractDrawerShader.kt @@ -42,10 +42,8 @@ abstract class AbstractDrawerShader : Shader() { projection ?: Game.matrices.projection, view ?: Game.matrices.view ) - load() - model.bind() - model.draw() + render(model) } private fun loadMatrixUniforms(transformation: Matrix4f, projection: Matrix4f, view: Matrix4f) { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt index c6613d0c..17384a35 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/DrawerRenderer.kt @@ -74,7 +74,7 @@ class DrawerRenderer { } - private val shader: DrawerShader by lazy { DrawerShader(vertex, fragment) } + val shader: DrawerShader by lazy { DrawerShader(vertex, fragment) } var color: Color get() = fragment.color.value.toColor() @@ -115,7 +115,7 @@ class DrawerRenderer { var strokeWidth = 0.0 - fun render(model: Model, transformation: Matrix4f, view: Matrix4f, projection: Matrix4f) { + fun render(model: Model, transformation: Matrix4f, projection: Matrix4f, view: Matrix4f) { shader.render(model, transformation, projection, view) } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/PathRenderer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/PathRenderer.kt index c01a2a11..0e682cf9 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/PathRenderer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/shader/PathRenderer.kt @@ -15,7 +15,7 @@ import org.joml.Matrix4f import org.lwjgl.opengl.GL11.* import org.lwjgl.opengl.GL20 -class PathRenderer(positionBufferMaker: FloatBufferMaker = Attribute(0).vec3()) { +class PathRenderer(positionBufferMaker: FloatBufferMaker = Attribute.location(0).vec3()) { private val vertex = object : DrawerScript() { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt index cdfd387c..0044baf3 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt @@ -16,27 +16,27 @@ interface ColorDrawer : Drawer, Color { operator fun invoke(hex: Long): ColorDrawer = invoke(hex(hex)) - fun alpha(alpha: Double): Drawer { + fun alpha(alpha: Double): ColorDrawer { this.invoke(com.mechanica.engine.color.rgba(r, g, b, alpha)) return this } - fun rgba(r: Double = this.r, g: Double = this.g, b: Double = this.b, a: Double = this.a): Drawer { + fun rgba(r: Double = this.r, g: Double = this.g, b: Double = this.b, a: Double = this.a): ColorDrawer { this.invoke(com.mechanica.engine.color.rgba(r, g, b, a)) return this } - fun hue(hue: Angle): Drawer { + fun hue(hue: Angle): ColorDrawer { this.invoke(hsl(hue, saturation, lightness)) return this } - fun lightness(lightness: Double): Drawer { + fun lightness(lightness: Double): ColorDrawer { this.invoke(hsl(hue, saturation, lightness)) return this } - fun saturation(saturation: Double): Drawer { + fun saturation(saturation: Double): ColorDrawer { this.invoke(hsl(hue, saturation, lightness)) return this } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt index 86d7dd97..c77aef52 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/circle/CircleDrawerImpl.kt @@ -14,7 +14,7 @@ class CircleDrawerImpl( private val model: Model init { - val position = Attribute(0).vec3().createUnitQuad() + val position = Attribute.location(0).vec3().createUnitQuad() val disableTexCoords = object : Bindable { override fun bind() { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt index 1ef36783..0d34c8dc 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/image/ImageDrawerImpl.kt @@ -11,8 +11,8 @@ class ImageDrawerImpl( private val model: ImageModel init { - val position = Attribute(0).vec3().createUnitQuad() - val textureCoords = Attribute(1).vec2().createInvertedUnitQuad() + val position = Attribute.location(0).vec3().createUnitQuad() + val textureCoords = Attribute.location(1).vec2().createInvertedUnitQuad() model = ImageModel(Image(0), position, textureCoords) } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt index 13e03f91..77915d34 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/rectangle/RectangleDrawerImpl.kt @@ -13,7 +13,7 @@ internal class RectangleDrawerImpl( private val model: Model init { - val position = Attribute(0).vec3().createUnitQuad() + val position = Attribute.location(0).vec3().createUnitQuad() val disableTexCoords = object : Bindable { override fun bind() { diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawer.kt index 19e855dc..8bcc42fe 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawer.kt @@ -7,13 +7,13 @@ import com.mechanica.engine.unit.vector.Vector interface TextDrawer { fun text(text: String) - fun text(text: String, size: Number, x: Number, y: Number) + fun text(text: String, size: Number = 1.0, x: Number = 0.0, y: Number = 0.0) fun text(text: String, size: Number, position: Vector) = text(text, size, position.x, position.y) fun text(text: String, size: Number, position: LightweightVector) = text(text, size, position.x, position.y) fun text(text: Text) - fun text(text: Text, size: Number, x: Number, y: Number) + fun text(text: Text, size: Number = 1.0, x: Number = 0.0, y: Number = 0.0) fun text(text: Text, size: Number, position: Vector) = text(text, size, position.x, position.y) fun text(text: Text, size: Number, position: LightweightVector) = text(text, size, position.x, position.y) } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt index d3b08a44..c8dfc8ba 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/Game.kt @@ -6,8 +6,6 @@ import com.mechanica.engine.context.GLFWContext import com.mechanica.engine.display.Monitor import com.mechanica.engine.display.Window import com.mechanica.engine.context.GLContext -import com.mechanica.engine.persistence.loadData -import com.mechanica.engine.persistence.saveData import com.mechanica.engine.game.configuration.GameConfiguration import com.mechanica.engine.game.configuration.GameConfigurationImpl import com.mechanica.engine.game.view.GameMatrices @@ -16,6 +14,8 @@ import com.mechanica.engine.game.view.View import com.mechanica.engine.context.GLInitializer import com.mechanica.engine.context.loader.LwjglLoader import com.mechanica.engine.matrix.Matrices +import com.mechanica.engine.persistence.populateData +import com.mechanica.engine.persistence.storeData import com.mechanica.engine.scenes.SceneManager import com.mechanica.engine.scenes.processes.Process import com.mechanica.engine.scenes.scenes.* @@ -144,15 +144,11 @@ object Game { } private fun loadPersistenceData() { - for (saveData in data.saveData) { - loadData(saveData) - } + populateData() } private fun savePersistenceData() { - for (saveData in data.saveData) { - saveData(saveData) - } + storeData() } private class UIView : View { @@ -173,7 +169,7 @@ object Game { width = view.width/scale.x height = view.height/scale.y wh = vec(width, height) - center = vec(width/2.0, height/2.0) + center = vec(0.0, 0.0) } } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt index a836d5f0..9a67e597 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/ConfigurationData.kt @@ -20,7 +20,6 @@ interface ConfigurationData { val viewHeight: Double? val viewX: Double? val viewY: Double? - val saveData: Array? val fullscreen: Boolean? val startingScene: (() -> MainScene)? val windowConfiguration: (Window.() -> Unit)? diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt index 680e8d62..07611bb1 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfiguration.kt @@ -16,8 +16,6 @@ interface GameConfiguration { fun setStartingState(scene: () -> MainScene) - fun setSaveData(vararg savedata: Any) - fun setMultisampling(samples: Int) fun configureWindow(configuration: (Window.() -> Unit)) diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt index 26247e04..3217bffd 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameConfigurationImpl.kt @@ -42,10 +42,6 @@ internal class GameConfigurationImpl : GameConfiguration { _data.startingScene = scene } - override fun setSaveData(vararg savedata: Any) { - _data.saveData = arrayOf(*savedata) - } - override fun configureWindow(configuration: Window.() -> Unit) { _data.windowConfiguration = configuration } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt index 5eb41b82..72550740 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/GameSetup.kt @@ -23,7 +23,6 @@ class GameSetup(data: NullableConfigurationData) : ConfigurationData { override val viewHeight: Double override val viewX: Double = data.viewX ?: 0.0 override val viewY: Double = data.viewX ?: 0.0 - override val saveData: Array = data.saveData ?: emptyArray() override val fullscreen: Boolean = data.fullscreen ?: false override val startingScene: (() -> MainScene)? = data.startingScene override val windowConfiguration: (Window.() -> Unit) = data.windowConfiguration ?: { } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt index 3591abeb..672be1f0 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/configuration/NullableConfigurationData.kt @@ -21,9 +21,7 @@ class NullableConfigurationData : ConfigurationData { override var viewX: Double? = null override var viewY: Double? = null - - override var saveData: Array? = null - + override var fullscreen: Boolean? = null override var startingScene: (() -> MainScene)? = null diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt index 6827d5ac..685e2794 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/DefaultDynamicView.kt @@ -39,11 +39,11 @@ class DefaultDynamicView( @Suppress("SetterBackingFieldAssignment") override var center: DynamicVector = object : DynamicVector { override var x: Double - get() = this@DefaultDynamicView.x + width/2.0 - set(value) {this@DefaultDynamicView.x = value - width/2.0} + get() = this@DefaultDynamicView.x + set(value) {this@DefaultDynamicView.x = value } override var y: Double get() = this@DefaultDynamicView.y + height/2.0 - set(value) { this@DefaultDynamicView.y = value - height/2.0} + set(value) { this@DefaultDynamicView.y = value } } set(value) { x = value.x diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt index 60069956..fcd4b5f6 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/GameView.kt @@ -61,11 +61,11 @@ class GameView(data: GameSetup): DynamicView { @Suppress("SetterBackingFieldAssignment") override var center: DynamicVector = object : DynamicVector { override var x: Double - get() = this@GameView.x + width/2.0 - set(value) {this@GameView.x = value - width/2.0} + get() = this@GameView.x + set(value) {this@GameView.x = value} override var y: Double - get() = this@GameView.y + height/2.0 - set(value) { this@GameView.y = value - height/2.0} + get() = this@GameView.y + set(value) { this@GameView.y = value} } set(value) { x = value.x diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt index 597edda0..27919223 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/game/view/StaticView.kt @@ -24,9 +24,9 @@ class StaticView( override val center: Vector = object : Vector { override val x: Double - get() = this@StaticView.x + width/2.0 + get() = this@StaticView.x override val y: Double - get() = this@StaticView.y + height/2.0 + get() = this@StaticView.y } override val ratio: Double diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneUtil.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneUtil.kt new file mode 100644 index 00000000..8ca32c2a --- /dev/null +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/SceneUtil.kt @@ -0,0 +1,67 @@ +package com.mechanica.engine.scenes + +import com.mechanica.engine.animation.AnimationController +import com.mechanica.engine.animation.AnimationFormula +import com.mechanica.engine.animation.AnimationFormulas +import com.mechanica.engine.animation.AnimationSequence +import com.mechanica.engine.scenes.exclusiveScenes.ExclusiveActivationMap +import com.mechanica.engine.scenes.processes.Process +import com.mechanica.engine.scenes.processes.ProcessNode +import com.mechanica.engine.scenes.scenes.Scene +import com.mechanica.engine.scenes.scenes.SceneNode + + +fun ProcessNode.addAnimation(start: Double, end: Double, formula: AnimationFormulas.(Double) -> Double): AnimationFormula { + return addProcess(AnimationFormula(start, end, formula)) +} + +fun ProcessNode.addAnimation(length: Double, formula: AnimationFormulas.(Double) -> Double): AnimationFormula { + return addProcess(AnimationFormula(length, formula)) +} + +fun ProcessNode.addAnimationSequence(vararg animations: AnimationController): AnimationSequence { + return addProcess(AnimationSequence(*animations)) +} + + +/** + * Sets the [processes] in which only one process can be active at once. This exclusivity is automatically enforced + * + * Usage: + * ``` + * val currentlyActiveProcess by exclusivelyActiveProcesses(Process1(), Process2(), Process3()) + * ``` + * + * @param processes the set of processes that will be added as children to this process and only one can be + * set to active at a time + * @return An ExclusiveProcessMap which can have more processes added at a later stage + */ +fun ProcessNode.exclusivelyActiveProcesses(vararg processes: P): ExclusiveActivationMap

{ + val parent = this + return object : ExclusiveActivationMap

(*processes) { + override fun addProcess(process: R): R { + parent.addProcess(process) + return super.addProcess(process) + } + } +} + +/** + * This does the same as [exclusivelyActiveProcesses][com.mechanica.engine.scenes.exclusivelyActiveProcesses] but it adds + * scenes to the parent instead of processes + * + * @param scenes the set of scenes that will be added as children to this scene and only one can be + * set to active at a time + * @return An ExclusiveProcessMap which can have more processes added at a later stage + */ +fun SceneNode.exclusivelyActiveScenes(vararg scenes: P): ExclusiveActivationMap

{ + val parent = this + return object : ExclusiveActivationMap

(*scenes) { + override fun addProcess(process: R): R { + parent.addScene(process) + return super.addProcess(process) + } + } +} + + diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt index 679266cb..b21d9a04 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/Process.kt @@ -1,6 +1,9 @@ package com.mechanica.engine.scenes.processes +import com.mechanica.engine.animation.AnimationFormula +import com.mechanica.engine.animation.AnimationFormulas import com.mechanica.engine.scenes.exclusiveScenes.ExclusiveActivationMap +import com.mechanica.engine.util.extensions.fori abstract class Process(override val order: Int = 0) : ProcessNode { @@ -16,6 +19,7 @@ abstract class Process(override val order: Int = 0) : ProcessNode { } private val childProcesses: List = ArrayList() + private val leafProcesses: List = ArrayList() private var callbacksInitialized = false @@ -47,31 +51,80 @@ abstract class Process(override val order: Int = 0) : ProcessNode { } } - final override fun addProcess(process: P): P { + final override fun addProcess(process: P): P { + if (process is ProcessNode) { + addProcessNode(process) + } else { + addUpdateable(process) + } + return process + } + + private fun addProcessNode(process: ProcessNode) { val processes = (childProcesses as ArrayList) if (!processes.contains(process)) { processes.add(process) processes.sortBy { it.order } } - return process } - final override fun removeProcess(process: ProcessNode): Boolean { + private fun addUpdateable(updateable: Updateable) { + val processes = (leafProcesses as ArrayList) + + if (!processes.contains(updateable)) { + processes.add(updateable) + } + } + + final override fun removeProcess(process: Updateable): Boolean { + return if (process is ProcessNode) { + removeProcessNode(process) + } else { + removeUpdateable(process) + } + } + + private fun removeProcessNode(process: ProcessNode): Boolean { process.destructNodes() return (childProcesses as ArrayList).remove(process) } - final override fun replaceProcess(old: P, new: P): P { + private fun removeUpdateable(updateable: Updateable): Boolean { + return (leafProcesses as ArrayList).remove(updateable) + } + + final override fun replaceProcess(old: P, new: P): P { + val success = if (old is ProcessNode && new is ProcessNode) { + replaceProcessNode(old, new) + } else { + replaceUpdateable(old, new) + } + + return if (success) new else old + } + + private fun replaceProcessNode(old: ProcessNode, new: ProcessNode): Boolean { val processes = (childProcesses as ArrayList) val index = processes.indexOf(old) if (index != -1) { removeProcess(processes[index]) processes.add(index, new) processes.sortBy { it.order } - return new + return true } - return old + return false + } + + private fun replaceUpdateable(old: Updateable, new: Updateable): Boolean { + val processes = (leafProcesses as ArrayList) + val index = processes.indexOf(old) + if (index != -1) { + removeProcess(processes[index]) + processes.add(index, new) + return true + } + return false } inline fun forEachProcess(operation: (ProcessNode)-> Unit) { @@ -84,6 +137,7 @@ abstract class Process(override val order: Int = 0) : ProcessNode { if (!callbacksInitialized) runActivationCallbacks(active) val index = updateNodesFor(delta) { it.order < 0 } + updateLeaves(delta) this.update(delta) updateNodesFor(delta, index) { it.order >= 0 } } @@ -106,6 +160,12 @@ abstract class Process(override val order: Int = 0) : ProcessNode { return i } + private fun updateLeaves(delta: Double) { + leafProcesses.fori { + it.update(delta) + } + } + override fun destructNodes() { for (i in childProcesses.indices) { childProcesses[i].destructNodes() @@ -113,28 +173,6 @@ abstract class Process(override val order: Int = 0) : ProcessNode { destructor() } - /** - * Sets the [processes] in which only one process can be active at once. This exclusivity is automatically enforced - * - * Usage: - * ``` - * val currentlyActiveProcess by exclusivelyActiveProcesses(Process1(), Process2(), Process3()) - * ``` - * - * @param processes the set of processes that will be added as children to this process and only one can be - * set to active at a time - * @return An ExclusiveProcessMap which can have more processes added at a later stage - */ - protected fun exclusivelyActiveProcesses(vararg processes: P): ExclusiveActivationMap

{ - return ExclusiveProcessMap(*processes) - } - - private inner class ExclusiveProcessMap(vararg processes: P) : ExclusiveActivationMap

(*processes) { - override fun addProcess(process: R): R { - this@Process.addProcess(process) - return super.addProcess(process) - } - } override fun destructor() { } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt index 10447a6c..3edef02a 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/processes/ProcessNode.kt @@ -11,9 +11,9 @@ interface ProcessNode : Updateable { get() = 0 val active: Boolean get() = true - fun addProcess(process: P): P - fun removeProcess(process: ProcessNode): Boolean - fun replaceProcess(old: P, new: P): P + fun addProcess(process: P): P + fun removeProcess(process: Updateable): Boolean + fun replaceProcess(old: P, new: P): P fun updateNodes(delta: Double) fun destructor() fun destructNodes() diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt index d5fef485..1959fc83 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/DynamicScene.kt @@ -30,12 +30,12 @@ abstract class DynamicScene( override var x: Double get() { - return center.x - width/2.0 + return center.x } - set(value) { center.x = value + width/2.0} + set(value) { center.x = value} override var y: Double - get() = center.y - height/2.0 - set(value) {center.y = value + height/2.0} + get() = center.y + set(value) {center.y = value} @Suppress("SetterBackingFieldAssignment") override var center: DynamicVector = object : DynamicVector by position {} diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt index d919ec77..e64a657c 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/GUIScene.kt @@ -4,7 +4,7 @@ import com.mechanica.engine.drawer.Drawer import com.mechanica.engine.game.Game import com.mechanica.engine.game.view.View -abstract class GUIScene : Scene() { +abstract class GUIScene(order: Int = 1) : Scene(order) { override val view: View get() = Game.ui diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt index 1706e312..deb34a66 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/Scene.kt @@ -76,25 +76,6 @@ abstract class Scene(order: Int = 0) : Process(order), SceneNode { super.destructNodes() } - /** - * This does the same as [exclusivelyActiveProcesses][com.mechanica.engine.scenes.processes.Process.exclusivelyActiveProcesses] but it adds - * scenes to the parent instead of processes - * - * @param scenes the set of scenes that will be added as children to this scene and only one can be - * set to active at a time - * @return An ExclusiveProcessMap which can have more processes added at a later stage - */ - protected fun exclusivelyActiveScenes(vararg scenes: P): ExclusiveActivationMap

{ - return ExclusiveSceneMap(*scenes) - } - - private inner class ExclusiveSceneMap(vararg scenes: P) : ExclusiveActivationMap

(*scenes) { - override fun addProcess(process: R): R { - this@Scene.addScene(process) - return super.addProcess(process) - } - } - @Suppress("PropertyName") @PublishedApi internal val `access$childScenes`: List diff --git a/samples/build.gradle.kts b/samples/build.gradle.kts index dd548318..4464ecf0 100644 --- a/samples/build.gradle.kts +++ b/samples/build.gradle.kts @@ -1,6 +1,7 @@ plugins { kotlin("jvm") + kotlin("plugin.serialization") application } diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/data/SaveDataExample.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/data/SaveDataExample.kt new file mode 100644 index 00000000..2ffbe813 --- /dev/null +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/data/SaveDataExample.kt @@ -0,0 +1,39 @@ +package com.mechanica.engine.samples.data + +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.Game +import com.mechanica.engine.input.Key +import com.mechanica.engine.input.KeyIDs +import com.mechanica.engine.persistence.persist +import com.mechanica.engine.scenes.scenes.MainScene +import com.mechanica.engine.text.Text + +fun main() { + Game.configure { + setViewport(height = 10.0) + setStartingState { SaveDataExample() } + } + + Game.run() +} + +class SaveDataExample : MainScene() { + private var count by persist(0) + + private val upKey = Key(KeyIDs.W, KeyIDs.UP, KeyIDs.SCROLL_UP) + private val downKey = Key(KeyIDs.S, KeyIDs.DOWN, KeyIDs.SCROLL_DOWN) + + private val info by lazy { Text("Press $upKey or $downKey to change count\nThe value persists after re-running") } + + override fun render(draw: Drawer) { + if (upKey.hasBeenPressed) { + count++ + } + if (downKey.hasBeenPressed) { + count-- + } + + draw.centered.darkGrey.text("count: $count", y = 1.0) + draw.centered.darkGrey.text(info, 0.4, 0.0, -1.0) + } +} diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt index dfb2f695..82ba16ce 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/shaders/FragmentRenderer.kt @@ -10,7 +10,7 @@ import org.joml.Matrix4f class FragmentRenderer { - private val vbo = Attribute(0).vec3().createUnitQuad() + private val vbo = Attribute.location(0).vec3().createUnitQuad() private val model = Model(vbo) private val vertex = object : DrawerScript() { diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt index 3cce38cf..8346fa8b 100644 --- a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/Test.kt @@ -27,7 +27,7 @@ fun main() { setMultisampling(0) } - Attribute(0).vec3().createBuffer(emptyArray()) + Attribute.location(0).vec3().createBuffer(emptyArray()) val vertex = object : ShaderScript() { val position = attribute(0).vec4() @@ -77,7 +77,7 @@ fun main() { vec(0.85, 0.95) ) - val square = Attribute(0).vec2().createBuffer(squareArray) + val square = Attribute.location(0).vec2().createBuffer(squareArray) val colorArray = arrayOf( hex(0xFF00FFFF), From 2f455b7a83840eed07f35b8e800595b61821cb11 Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Tue, 16 Jun 2020 14:42:32 +0100 Subject: [PATCH 12/21] Fixed issues with persistence --- .../com/mechanica/engine/persistence/PersistenceMap.kt | 2 -- .../mechanica/engine/persistence/PersistenceUtil.kt | 2 +- .../mechanica/engine/persistence/PersistentVariable.kt | 10 +++++----- .../com/mechanica/engine/resources/ExternalResource.kt | 2 +- .../main/kotlin/com/mechanica/engine/util/JsonUtil.kt | 10 +++++++++- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt index b1a41c7e..6c8fe36c 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceMap.kt @@ -15,7 +15,6 @@ class PersistenceMap private constructor(private val path: String, private val m fun store() { val json = Json(JsonConfiguration.Stable) val string = json.stringify(serializer, map) - val file = ExternalResource(path, true) file.write(string) } @@ -29,7 +28,6 @@ class PersistenceMap private constructor(private val path: String, private val m "{}" } - val map = Json.parse(serializer, string) map.forEach { this.map[it.key] = it.value diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt index 99201f86..181f3403 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistenceUtil.kt @@ -1,6 +1,6 @@ package com.mechanica.engine.persistence -private val map = PersistenceMap("res/data/persistence.txt") +private val map = PersistenceMap("res/data/persistence.json") fun persist(default: Boolean, instance: String? = null) = PersistentVariable(map, default, instance) diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt index 04b821b7..4945b21f 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/persistence/PersistentVariable.kt @@ -32,14 +32,14 @@ class PersistentVariable( } private fun Map<*, *>.retrieveInstancedValue(instance: String, name: String): T { - val subMap = subMap(instance) ?: return defaultValue + val subMap = subMap(name) ?: return defaultValue - return subMap.retrieveSingleValue(name) + return subMap.retrieveSingleValue(instance) } - private fun Map<*, *>.subMap(instance: String) : HashMap? { + private fun Map<*, *>.subMap(name: String) : HashMap? { @Suppress("UNCHECKED_CAST") - return this[instance] as? HashMap + return this[name] as? HashMap } override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { @@ -48,7 +48,7 @@ class PersistentVariable( if (instance == null) { map.put(name, value) } else { - map.subMap(instance)?.put(name, value) + map.subMap(name)?.put(instance, value) ?: map.put(name, HashMap().also { it[instance] = value }) } } diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/resources/ExternalResource.kt b/application-interface/src/main/kotlin/com/mechanica/engine/resources/ExternalResource.kt index 1f52d9e9..f488830d 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/resources/ExternalResource.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/resources/ExternalResource.kt @@ -68,7 +68,7 @@ class ExternalResource(filePath: String, createIfAbsent: Boolean = false) : Reso return if (createIfAbsent) { try { Resource(filePath) - } catch (ex: java.lang.IllegalStateException) { + } catch (ex: FileNotFoundException) { val newFile = File("${getResourceLocation()}/$filePath") newFile.parentFile.mkdirs() newFile.createNewFile() diff --git a/common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt b/common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt index acb9f3d5..1c68c20a 100644 --- a/common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt +++ b/common/src/main/kotlin/com/mechanica/engine/util/JsonUtil.kt @@ -20,7 +20,15 @@ fun JsonObject.toStringValueMap(): Map { it.key to value } else null - }.toMap() + }.toHashMap() +} + +fun List>.toHashMap(): HashMap { + val map = HashMap() + for (p in this) { + map[p.first] = p.second + } + return map } fun JsonObjectBuilder.addSupportedType(key: String, value: V) { From 9c859362aa47ce2e7430793c9421e25b4d2a3851 Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Sun, 28 Jun 2020 18:12:01 +0100 Subject: [PATCH 13/21] Corrected the skew transformation and fixed ResourceDirectory.kt --- .../engine/resources/ResourceDirectory.kt | 53 +++++++++++++++---- .../com/mechanica/engine/color/ColorTools.kt | 16 ++++-- .../mechanica/engine/unit/angle/AngleTools.kt | 7 +++ .../engine/animation/FrameAnimation.kt | 2 +- .../com/mechanica/engine/drawer/DrawData.kt | 27 ++++++---- .../drawer/subclasses/color/ColorDrawer.kt | 6 +-- .../drawer/superclass/text/TextDrawerImpl.kt | 3 +- .../com/mechanica/engine/input/mouse/Mouse.kt | 2 +- .../mechanica/engine/input/mouse/MouseImpl.kt | 9 +++- .../mechanica/engine/samples/temp/ColorHSL.kt | 19 +++++++ .../mechanica/engine/samples/temp/SkewTest.kt | 29 ++++++++++ 11 files changed, 140 insertions(+), 33 deletions(-) create mode 100644 samples/src/main/kotlin/com/mechanica/engine/samples/temp/ColorHSL.kt create mode 100644 samples/src/main/kotlin/com/mechanica/engine/samples/temp/SkewTest.kt diff --git a/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt b/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt index ce199609..43a2a7cf 100644 --- a/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt +++ b/application-interface/src/main/kotlin/com/mechanica/engine/resources/ResourceDirectory.kt @@ -1,40 +1,73 @@ package com.mechanica.engine.resources +import java.io.FileNotFoundException import java.net.URI import java.nio.file.* import java.util.stream.Stream -class ResourceDirectory(directory: String): Iterable { - private val resources: Array +class ResourceDirectory(directory: String, recursive: Boolean = false) { + val resources: Array + val folders: Array + val fileCount: Int get() = resources.size + val folderCount: Int + get() = folders.size + + val name: String + init { - val list = ArrayList() + val resourceList = ArrayList() + val directoryList = ArrayList() - val uri: URI = this::class.java.getResource(directory).toURI() + val pathString = directory.removeSuffix("\\").removeSuffix("/") + "\\" + + val uri: URI = this::class.java.getResource(pathString)?.toURI() ?: throw FileNotFoundException("No file or directory found at $directory") var fileSystem: FileSystem? = null val path = if (uri.scheme == "jar") { fileSystem = FileSystems.newFileSystem(uri, emptyMap()) - fileSystem.getPath(directory) + fileSystem.getPath(pathString) } else { Paths.get(uri) } + name = path.fileName.toString() + val walk: Stream = Files.walk(path, 1) for (e in walk) { - if (!Files.isDirectory(e)) - list.add(Resource(e.toUri())) + if (!Files.isDirectory(e)) { + resourceList.add(Resource(e.toUri())) + } else if (Files.isDirectory(e)) { + val relative = path.relativize(e).toString() + if (relative.isNotEmpty() && relative != "." && relative != "..") { + directoryList.add(ResourceDirectory(pathString + relative, recursive)) + } + } } fileSystem?.close() - list.sortBy { it.path } - resources = list.toTypedArray() + resourceList.sortBy { it.path } + resources = resourceList.toTypedArray() + + folders = directoryList.toTypedArray() } - override fun iterator() = resources.iterator() + fun getFile(index: Int) = resources[index] + fun getFolder(index: Int) = folders[index] + inline fun forEachFile(operation: (Resource) -> Unit) { + for (i in 0 until fileCount) { + operation(getFile(i)) + } + } + + inline fun forEachFolder(operation: (ResourceDirectory) -> Unit) { + for (i in 0 until folderCount) { + operation(getFolder(i)) + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt b/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt index b7fd63f8..b1e670bd 100644 --- a/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt +++ b/common/src/main/kotlin/com/mechanica/engine/color/ColorTools.kt @@ -2,9 +2,9 @@ package com.mechanica.engine.color -import com.mechanica.engine.unit.angle.degrees -import com.mechanica.engine.unit.angle.Angle import com.mechanica.engine.unit.angle.Degree +import com.mechanica.engine.unit.angle.Radian +import com.mechanica.engine.unit.angle.degrees import org.joml.Vector4f import kotlin.math.abs import kotlin.math.max @@ -182,14 +182,22 @@ fun rgb2Lightness(r: Double, g: Double, b: Double): Double { return (max + min)/2.0 } -fun hsl(hue: Angle, saturation: Double, lightness: Double, alpha: Double = 1.0): LightweightColor { +fun hsl(hue: Degree, saturation: Double, lightness: Double, alpha: Double = 1.0): LightweightColor { + return hsl(hue.toDouble(), saturation, lightness, alpha) +} + +fun hsl(hue: Radian, saturation: Double, lightness: Double, alpha: Double = 1.0): LightweightColor { + return hsl(hue.toDegrees().toDouble(), saturation, lightness, alpha) +} + +fun hsl(hue: Double, saturation: Double, lightness: Double, alpha: Double = 1.0): LightweightColor { fun f(n: Int, h: Double, s: Double, l: Double): Double { val a = s* min(l, 1.0-l) val k = (n + h/30.0)%12 return (l - a* max(min(min(k-3.0, 9.0-k), 1.0),-1.0)) } - val h = (hue.toDegrees().toDouble() + 360.0)%360.0 + val h = (hue + 360.0)%360.0 val s = saturation val l = lightness diff --git a/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt b/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt index c49a4072..abd694c4 100644 --- a/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt +++ b/common/src/main/kotlin/com/mechanica/engine/unit/angle/AngleTools.kt @@ -1,5 +1,7 @@ package com.mechanica.engine.unit.angle +import kotlin.math.PI + inline val Number.degrees: Degree get() = Degree(this.toDouble()) @@ -43,3 +45,8 @@ operator fun Degree.times(other: Number): Degree { operator fun Radian.times(other: Number): Radian { return (this.toDouble() * other.toDouble()).radians } + +fun minimumDifference(angle1: Radian, angle2: Radian): Radian { + val diff = angle2 - angle1 + return ((diff.toDouble() + PI) % (2.0* PI) - PI).radians +} diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt b/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt index 3a261957..97d43daf 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/animation/FrameAnimation.kt @@ -73,6 +73,6 @@ class FrameAnimation(private val frames: List, frameRate : Double, companion object { fun loadAnimation(directory: ResourceDirectory, frameRate: Double = 24.0) - = FrameAnimation(directory.map { loadImage(it) }, frameRate) + = FrameAnimation(directory.resources.map { loadImage(it) }, frameRate) } } \ No newline at end of file diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt index 18838bc9..fcecc844 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/DrawData.kt @@ -3,16 +3,15 @@ package com.mechanica.engine.drawer import com.mechanica.engine.color.DynamicColor import com.mechanica.engine.drawer.shader.AbstractDrawerShader import com.mechanica.engine.drawer.shader.DrawerRenderer -import com.mechanica.engine.drawer.shader.DrawerShader import com.mechanica.engine.game.Game import com.mechanica.engine.models.Model import com.mechanica.engine.models.TextModel -import com.mechanica.engine.text.Text import com.mechanica.engine.unit.vector.DynamicVector import com.mechanica.engine.unit.vector.LightweightVector import com.mechanica.engine.unit.vector.Vector import org.joml.Matrix4f import org.joml.Vector3f +import kotlin.math.tan class DrawData { @@ -27,6 +26,7 @@ class DrawData { private val renderer = DrawerRenderer() private val defaultTransformation = Matrix4f().identity() + private val spareTransformation = Matrix4f().identity() var transformation: Matrix4f? = null private val translation: Vector3f = Vector3f() @@ -113,15 +113,14 @@ class DrawData { } fun getTransformationMatrix(matrix: Matrix4f): Matrix4f { - addSkewToMatrix(matrix) matrix.translate(translation) if (rz != 0f) matrix.rotate(rz, zAxis) + addSkewToMatrix(matrix) addModelOriginToMatrix(matrix) - matrix.scale(scale) pivot.set(0f, 0f, 0f) return matrix @@ -150,13 +149,19 @@ class DrawData { } private fun addSkewToMatrix(matrix: Matrix4f) { - if (skewX != 0f) { - matrix.rotationZ(skewX) - matrix.m01(matrix.m10() + matrix.m01()) - } - if (skewY != 0f) { - matrix.rotationZ(skewY) - matrix.m10(matrix.m10() + matrix.m01()) +// if (skewX != 0f) { +// matrix.rotationZ(skewX) +// matrix.m01(matrix.m10() + matrix.m01()) +// } +// if (skewY != 0f) { +// matrix.rotationZ(skewY) +// matrix.m10(matrix.m10() + matrix.m01()) +// } + if (skewX != 0f || skewY != 0f) { + spareTransformation.m10(tan(-skewX)) + spareTransformation.m01(tan(skewY)) + + matrix.mul(spareTransformation) } } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt index 0044baf3..94dc1643 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/subclasses/color/ColorDrawer.kt @@ -2,10 +2,10 @@ package com.mechanica.engine.drawer.subclasses.color import com.mechanica.engine.color.Color import com.mechanica.engine.color.LightweightColor -import com.mechanica.engine.color.hsl import com.mechanica.engine.color.hex +import com.mechanica.engine.color.hsl import com.mechanica.engine.drawer.Drawer -import com.mechanica.engine.unit.angle.Angle +import com.mechanica.engine.unit.angle.Degree interface ColorDrawer : Drawer, Color { fun get(): Color @@ -26,7 +26,7 @@ interface ColorDrawer : Drawer, Color { return this } - fun hue(hue: Angle): ColorDrawer { + fun hue(hue: Degree): ColorDrawer { this.invoke(hsl(hue, saturation, lightness)) return this } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt index 22fcd004..c7229faf 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/drawer/superclass/text/TextDrawerImpl.kt @@ -2,8 +2,8 @@ package com.mechanica.engine.drawer.superclass.text import com.mechanica.engine.drawer.DrawData import com.mechanica.engine.game.Game -import com.mechanica.engine.text.Text import com.mechanica.engine.models.TextModel +import com.mechanica.engine.text.Text import com.mechanica.engine.unit.vector.LightweightVector import com.mechanica.engine.unit.vector.vec @@ -41,7 +41,6 @@ class TextDrawerImpl( origin.set(oX*width, oY*height - bottomRight.y) data.draw(model) - origin.set(oX, oY) } diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt index 2dc17546..3f18ab89 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/Mouse.kt @@ -3,7 +3,6 @@ package com.mechanica.engine.input.mouse import com.mechanica.engine.context.callbacks.MouseHandler import com.mechanica.engine.input.Key import com.mechanica.engine.input.KeyID -import com.mechanica.engine.input.Keys import com.mechanica.engine.unit.vector.Vector @@ -22,6 +21,7 @@ interface Mouse { val pixel: Vector val world: Vector val ui: Vector + val normalized: Vector class ScrollWheel(vararg keys: KeyID): Key(*keys) { val distance: Double diff --git a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt index b5ba6d6a..45fa1b3e 100644 --- a/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt +++ b/mechanica/src/main/kotlin/com/mechanica/engine/input/mouse/MouseImpl.kt @@ -1,10 +1,10 @@ package com.mechanica.engine.input.mouse import com.mechanica.engine.context.callbacks.MouseHandler -import com.mechanica.engine.unit.vector.Vector import com.mechanica.engine.game.Game import com.mechanica.engine.input.Key import com.mechanica.engine.input.KeyIDs +import com.mechanica.engine.unit.vector.Vector internal class MouseImpl : Mouse { override val MB1 = Key(KeyIDs.M1) @@ -58,4 +58,11 @@ internal class MouseImpl : Mouse { } + override val normalized: Vector = object : Vector { + override val x: Double + get() = pixel.x/ Game.window.width + override val y: Double + get() = 1.0 - pixel.y/ Game.window.height + + } } \ No newline at end of file diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/ColorHSL.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/ColorHSL.kt new file mode 100644 index 00000000..25fa04a6 --- /dev/null +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/ColorHSL.kt @@ -0,0 +1,19 @@ +package com.mechanica.engine.samples.temp + +import com.mechanica.engine.color.rgba +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.Game +import com.mechanica.engine.input.mouse.Mouse + +fun main() { + Game.configure { + setViewport(height = 10.0) + } + val draw = Drawer.create() + + Game.run { + val color = rgba(0.5, 0.5, 0.5, 1.0) + draw.centered.cyan.rectangle(x = 2.5, width = 5.0, height = 5.0) + draw.centered.color(color).lightness(Mouse.normalized.x).saturation(Mouse.normalized.y).rectangle(width = 5.0, height = 5.0) + } +} \ No newline at end of file diff --git a/samples/src/main/kotlin/com/mechanica/engine/samples/temp/SkewTest.kt b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/SkewTest.kt new file mode 100644 index 00000000..3e1d0d09 --- /dev/null +++ b/samples/src/main/kotlin/com/mechanica/engine/samples/temp/SkewTest.kt @@ -0,0 +1,29 @@ +package com.mechanica.engine.samples.temp + +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.game.Game +import com.mechanica.engine.input.mouse.Mouse +import com.mechanica.engine.unit.angle.degrees +import com.mechanica.engine.unit.angle.radians + +fun main() { + Game.configure { + setViewport(height = 10.0) + } + + val draw = Drawer.create() + + var skewY = Mouse.ui.theta + var skewX = 0.radians + + Game.run { + if (Mouse.MB1()) { + skewX = Mouse.ui.theta + } else { + skewY = Mouse.ui.theta + } + draw.lightGrey.centered.rotated(10.degrees).rectangle(2, 2, width = 2.0, height = 2.0) + draw.darkGrey.transformed.skew(skewX, skewY).rotated(10.degrees).rectangle(2,2, 1, 1) + + } +} \ No newline at end of file From 75a483abd714d2f7d3c8ce87ce8b1329a868d48c Mon Sep 17 00:00:00 2001 From: Dominic Dolan Date: Thu, 2 Jul 2020 15:08:08 +0100 Subject: [PATCH 14/21] Added a module for UI which is linked to DukeUI --- .../util/{GameBoolean.kt => EventBoolean.kt} | 6 +- .../engine/util/GenericGameBoolean.kt | 7 -- .../engine/text/LwjglStandardFont.kt | 6 +- mechanica-ui/build.gradle.kts | 13 +++ .../com/mechanica/engine/ui/Elements.kt | 110 ++++++++++++++++++ .../kotlin/com/mechanica/engine/ui/Events.kt | 44 +++++++ .../com/mechanica/engine/ui/GUIScene.kt | 18 +++ .../com/mechanica/engine/ui/MechanicaUI.kt | 26 +++++ .../kotlin/com/mechanica/engine/ui/Sample.kt | 55 +++++++++ .../kotlin/com/mechanica/engine/ui/Style.kt | 11 ++ .../kotlin/com/mechanica/engine/ui/UIUtils.kt | 24 ++++ .../kotlin/com/mechanica/engine/game/Game.kt | 41 ++----- .../game/configuration/ConfigurationData.kt | 5 +- .../game/configuration/GameConfiguration.kt | 3 +- .../configuration/GameConfigurationImpl.kt | 3 +- .../engine/game/configuration/GameSetup.kt | 4 +- .../NullableConfigurationData.kt | 3 +- .../engine/game/view/GameMatrices.kt | 2 +- .../game/view/{GameView.kt => GameViews.kt} | 53 ++++++--- .../mechanica/engine/game/view/WorldView.kt | 2 + .../engine/scenes/scenes/LoadScene.kt | 2 +- .../engine/scenes/scenes/MainScene.kt | 4 +- .../scenes/{GUIScene.kt => MainUIScene.kt} | 5 +- .../mechanica/engine/scenes/scenes/Scene.kt | 3 +- .../engine/scenes/scenes/WorldScene.kt | 9 ++ .../mechanica/engine/samples/color/Main.kt | 4 +- .../engine/samples/data/SaveDataExample.kt | 4 +- .../engine/samples/display/DisplayDemo.kt | 4 +- .../engine/samples/geometry/BezierCurve.kt | 4 +- .../engine/samples/geometry/DrawingExample.kt | 4 +- .../engine/samples/geometry/LinesExample.kt | 6 +- .../mechanica/engine/samples/polygon/Main.kt | 4 +- .../mechanica/engine/samples/renderer/Main.kt | 5 +- .../engine/samples/text/TextEditor.kt | 6 +- settings.gradle.kts | 4 +- 35 files changed, 404 insertions(+), 100 deletions(-) rename common/src/main/kotlin/com/mechanica/engine/util/{GameBoolean.kt => EventBoolean.kt} (89%) delete mode 100644 common/src/main/kotlin/com/mechanica/engine/util/GenericGameBoolean.kt create mode 100644 mechanica-ui/build.gradle.kts create mode 100644 mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/Elements.kt create mode 100644 mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/Events.kt create mode 100644 mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/GUIScene.kt create mode 100644 mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/MechanicaUI.kt create mode 100644 mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/Sample.kt create mode 100644 mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/Style.kt create mode 100644 mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/UIUtils.kt rename mechanica/src/main/kotlin/com/mechanica/engine/game/view/{GameView.kt => GameViews.kt} (62%) create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/game/view/WorldView.kt rename mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/{GUIScene.kt => MainUIScene.kt} (69%) create mode 100644 mechanica/src/main/kotlin/com/mechanica/engine/scenes/scenes/WorldScene.kt diff --git a/common/src/main/kotlin/com/mechanica/engine/util/GameBoolean.kt b/common/src/main/kotlin/com/mechanica/engine/util/EventBoolean.kt similarity index 89% rename from common/src/main/kotlin/com/mechanica/engine/util/GameBoolean.kt rename to common/src/main/kotlin/com/mechanica/engine/util/EventBoolean.kt index e44220b4..750ba889 100644 --- a/common/src/main/kotlin/com/mechanica/engine/util/GameBoolean.kt +++ b/common/src/main/kotlin/com/mechanica/engine/util/EventBoolean.kt @@ -1,11 +1,11 @@ package com.mechanica.engine.util -abstract class GameBoolean { - fun update(delta: Double) { +open class EventBoolean(private val condition: () -> Boolean) { + fun update() { isTrue = condition() } - abstract fun condition() : Boolean + fun condition() : Boolean = condition.invoke() var isTrue: Boolean = false private set(value) { diff --git a/common/src/main/kotlin/com/mechanica/engine/util/GenericGameBoolean.kt b/common/src/main/kotlin/com/mechanica/engine/util/GenericGameBoolean.kt deleted file mode 100644 index 5922fea3..00000000 --- a/common/src/main/kotlin/com/mechanica/engine/util/GenericGameBoolean.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.mechanica.engine.util - -class GenericGameBoolean(private val condition: () -> Boolean) : GameBoolean() { - override fun condition(): Boolean { - return condition.invoke() - } -} \ No newline at end of file diff --git a/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt b/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt index f062b322..7a09f80b 100644 --- a/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt +++ b/desktop-application/src/main/kotlin/com/mechanica/engine/text/LwjglStandardFont.kt @@ -2,7 +2,10 @@ package com.mechanica.engine.text import com.mechanica.engine.resources.Resource import com.mechanica.engine.unit.vector.DynamicVector -import org.lwjgl.stb.* +import org.lwjgl.stb.STBTTAlignedQuad +import org.lwjgl.stb.STBTTFontinfo +import org.lwjgl.stb.STBTTPackedchar +import org.lwjgl.stb.STBTruetype import org.lwjgl.stb.STBTruetype.stbtt_GetCodepointKernAdvance import org.lwjgl.system.MemoryStack import java.nio.ByteBuffer @@ -26,7 +29,6 @@ class LwjglStandardFont(resource: Resource, initializer: FontAtlasConfiguration. private val metrics = FontMetrics(data.info, data.scale) - override fun addCharacterDataToArrays(cursor: CharacterCursor, positions: Array, texCoords: Array) { val c = cursor.currentChar val atlasScale = atlas.scale diff --git a/mechanica-ui/build.gradle.kts b/mechanica-ui/build.gradle.kts new file mode 100644 index 00000000..6a8e19d7 --- /dev/null +++ b/mechanica-ui/build.gradle.kts @@ -0,0 +1,13 @@ + +plugins { + kotlin("jvm") + `java-library` +} + +dependencies { + implementation(project(":application-interface")) + implementation(project(":mechanica")) + + api("com.dubulduke.ui:DukeUI:0.1") + +} \ No newline at end of file diff --git a/mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/Elements.kt b/mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/Elements.kt new file mode 100644 index 00000000..b57e86aa --- /dev/null +++ b/mechanica-ui/src/main/kotlin/com/mechanica/engine/ui/Elements.kt @@ -0,0 +1,110 @@ +package com.mechanica.engine.ui + +import com.dubulduke.ui.UIContext +import com.dubulduke.ui.element.Element +import com.dubulduke.ui.render.ElementRenderer +import com.dubulduke.ui.render.RenderDescription +import com.mechanica.engine.drawer.Drawer +import com.mechanica.engine.text.Font +import com.mechanica.engine.text.Text + +class DrawerElement(context: UIContext, renderer: ElementRenderer) + : Element(context, renderer) { + + fun String.unaryPlus() { + text(this) + } + +} + +abstract class ElementDrawer : ElementRenderer { + override fun draw(description: RenderDescription