From b18a422c543cc2faa542afe3b5a1d05c87965795 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sat, 9 Aug 2025 16:52:38 -0500 Subject: [PATCH 01/16] Start whiteboxing the UI system pausing work on it for a bit because I need to decouple texture and mesh caches I think a bit to make it not rely on the scene so much --- .../engine/client/ui/Style.java | 70 +++++++++++++++++++ .../engine/graph/UIRenderNode.java | 59 ++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java new file mode 100644 index 0000000..5f04451 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java @@ -0,0 +1,70 @@ +package com.terminalvelocitycabbage.engine.client.ui; + +import com.terminalvelocitycabbage.engine.registry.Identifier; +import com.terminalvelocitycabbage.engine.util.Transformation; + +public class Style { + + Identifier textureIdentifier; + Transformation transformation; + + public Style(Identifier textureIdentifier, Transformation transformation) { + this.textureIdentifier = textureIdentifier; + this.transformation = transformation; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Identifier textureIdentifier; + private final Transformation transformation; + + public Builder() { + this.textureIdentifier = null; + this.transformation = new Transformation(); + } + + public Builder setTexture(Identifier textureIdentifier) { + this.textureIdentifier = textureIdentifier; + return this; + } + + public Builder setPosition(int x, int y) { + transformation.setPosition(x, y, 0); + return this; + } + + public Builder setZIndex(int zIndex) { + transformation.translate(0, 0, zIndex); + return this; + } + + public Builder setScale(float scale) { + transformation.setScale(scale); + return this; + } + + public Style build() { + this.transformation.translate(0, 0, -50); //zIndex makes more sense where a higher number is on top so we will make the default -50 and add to it + return new Style(textureIdentifier, transformation); + } + } + + public Identifier getTextureIdentifier() { + return textureIdentifier; + } + + public Transformation getTransformation() { + return transformation; + } + + @Override + public String toString() { + return "Style{" + + "textureIdentifier=" + textureIdentifier + + ", transformation=" + transformation + + '}'; + } +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java new file mode 100644 index 0000000..0eb5157 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -0,0 +1,59 @@ +package com.terminalvelocitycabbage.engine.graph; + +import com.terminalvelocitycabbage.engine.client.renderer.Projection; +import com.terminalvelocitycabbage.engine.client.renderer.elements.VertexAttribute; +import com.terminalvelocitycabbage.engine.client.renderer.elements.VertexFormat; +import com.terminalvelocitycabbage.engine.client.renderer.model.Mesh; +import com.terminalvelocitycabbage.engine.client.renderer.shader.ShaderProgramConfig; +import com.terminalvelocitycabbage.engine.client.scene.Scene; +import com.terminalvelocitycabbage.engine.client.ui.Style; +import com.terminalvelocitycabbage.engine.client.window.WindowProperties; +import com.terminalvelocitycabbage.engine.util.HeterogeneousMap; +import com.terminalvelocitycabbage.templates.meshes.SquareDataMesh; + +public abstract class UIRenderNode extends RenderNode { + + static final Projection PROJECTION = new Projection(Projection.Type.ORTHOGONAL, -100, 100); + + public static final VertexFormat UI_ELEMENT_MESH_FORMAT = VertexFormat.builder() + .addElement(VertexAttribute.XYZ_POSITION) + .addElement(VertexAttribute.RGB_COLOR) + .addElement(VertexAttribute.UV) + .build(); + + static final Mesh BOX_MESH = new Mesh(UI_ELEMENT_MESH_FORMAT, new SquareDataMesh()); + + public UIRenderNode(ShaderProgramConfig shaderProgramConfig) { + super(shaderProgramConfig); + } + + public abstract void drawUIElements(Scene scene); + + @Override + public void execute(Scene scene, WindowProperties properties, HeterogeneousMap renderConfig, long deltaTime) { + + //Set the shader up for rendering + var shaderProgram = getShaderProgram(); + if (properties.isResized()) PROJECTION.updateProjectionMatrix(properties.getWidth(), properties.getHeight()); + shaderProgram.bind(); + shaderProgram.getUniform("textureSampler").setUniform(0); + shaderProgram.getUniform("projectionMatrix").setUniform(PROJECTION.getProjectionMatrix()); + + drawUIElements(scene); + + //Reset + shaderProgram.unbind(); + } + + public void drawBox(Scene scene, Style style) { + + var texture = scene.getTextureCache().getTexture(style.getTextureIdentifier()); + texture.bind(); + + shaderProgram.getUniform("modelMatrix").setUniform(style.getTransformation().getTransformationMatrix()); + + BOX_MESH.render(); + + } + +} From bb8ff766893e2b5b8aa8df5e6c4ad9028fd9d8b1 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sat, 9 Aug 2025 18:40:31 -0500 Subject: [PATCH 02/16] Make UI work with atlases --- .../com/terminalvelocitycabbage/engine/graph/UIRenderNode.java | 3 ++- .../templates/events/ConfigureTexturesEvent.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 0eb5157..01ff3bd 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -1,5 +1,6 @@ package com.terminalvelocitycabbage.engine.graph; +import com.terminalvelocitycabbage.engine.client.ClientBase; import com.terminalvelocitycabbage.engine.client.renderer.Projection; import com.terminalvelocitycabbage.engine.client.renderer.elements.VertexAttribute; import com.terminalvelocitycabbage.engine.client.renderer.elements.VertexFormat; @@ -47,7 +48,7 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r public void drawBox(Scene scene, Style style) { - var texture = scene.getTextureCache().getTexture(style.getTextureIdentifier()); + var texture = ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()); texture.bind(); shaderProgram.getUniform("modelMatrix").setUniform(style.getTransformation().getTransformationMatrix()); diff --git a/src/main/java/com/terminalvelocitycabbage/templates/events/ConfigureTexturesEvent.java b/src/main/java/com/terminalvelocitycabbage/templates/events/ConfigureTexturesEvent.java index 48eda77..ac3ed2f 100644 --- a/src/main/java/com/terminalvelocitycabbage/templates/events/ConfigureTexturesEvent.java +++ b/src/main/java/com/terminalvelocitycabbage/templates/events/ConfigureTexturesEvent.java @@ -1,6 +1,7 @@ package com.terminalvelocitycabbage.templates.events; import com.terminalvelocitycabbage.engine.TerminalVelocityEngine; +import com.terminalvelocitycabbage.engine.debug.Log; import com.terminalvelocitycabbage.engine.event.Event; import com.terminalvelocitycabbage.engine.filesystem.GameFileSystem; import com.terminalvelocitycabbage.engine.filesystem.resources.Resource; @@ -36,6 +37,7 @@ public void addTexture(Identifier textureIdentifier, Identifier... atlasIdentifi singleTextures.putIfAbsent(textureIdentifier, fileSystem.getResource(ResourceCategory.TEXTURE, textureIdentifier)); } else { for (Identifier atlasIdentifier : atlasIdentifiers) { + if (!texturesToCompileToAtlas.containsKey(atlasIdentifier)) Log.crash("Tried to add texture to non-existent atlas, make sure that the atlas was registered beforehand"); texturesToCompileToAtlas.get(atlasIdentifier).putIfAbsent(textureIdentifier, fileSystem.getResource(ResourceCategory.TEXTURE, textureIdentifier)); } } From 9be52f2c9e3f7ad15df66eff148fbaf583620f92 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sat, 9 Aug 2025 19:11:32 -0500 Subject: [PATCH 03/16] A few bug fixes in TVE (probably needed in main but oh well) --- .../engine/client/ui/Style.java | 7 ++++++- .../engine/client/window/WindowManager.java | 14 -------------- .../engine/client/window/WindowThread.java | 8 ++++++++ .../engine/graph/UIRenderNode.java | 12 ++++++++++-- .../engine/util/Transformation.java | 14 ++++++++++---- .../ecs/components/TransformationComponent.java | 2 +- .../templates/meshes/SquareDataMesh.java | 8 ++++---- 7 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java index 5f04451..7a2be3f 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java @@ -41,11 +41,16 @@ public Builder setZIndex(int zIndex) { return this; } - public Builder setScale(float scale) { + public Builder scale(float scale) { transformation.setScale(scale); return this; } + public Builder size(int width, int height) { + transformation.setScale(width, height, 1f); + return this; + } + public Style build() { this.transformation.translate(0, 0, -50); //zIndex makes more sense where a higher number is on top so we will make the default -50 and add to it return new Style(textureIdentifier, transformation); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowManager.java b/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowManager.java index fba6572..963c74e 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowManager.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowManager.java @@ -9,14 +9,12 @@ import org.lwjgl.system.Platform; import java.nio.FloatBuffer; -import java.nio.IntBuffer; import java.util.*; import java.util.stream.Collectors; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.system.MemoryUtil.NULL; -import static org.lwjgl.system.MemoryUtil.memAddress; public class WindowManager { @@ -133,11 +131,6 @@ public long createNewWindow(WindowProperties properties) { //Add this window to the list of active window threads threads.put(windowID, new WindowThread(windowID, this, properties)); - //Set framebuffer size callback - glfwSetFramebufferSizeCallback(windowID, (long window, int w, int h) -> { - properties.resize(w, h); - }); - //Set focus state callback glfwSetWindowFocusCallback(windowID, (long window, boolean isActive) -> { properties.setFocused(isActive); @@ -168,13 +161,6 @@ public long createNewWindow(WindowProperties properties) { videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); glfwSetWindowPos(windowID, (videoMode.width() - properties.getWidth()) / 2, (videoMode.height() - properties.getHeight()) / 2); - //Update the window size variables based on post-init dimensions before showing the window - try (MemoryStack frame = MemoryStack.stackPush()) { - IntBuffer framebufferSize = frame.mallocInt(2); - nglfwGetFramebufferSize(windowID, memAddress(framebufferSize), memAddress(framebufferSize) + 4); - properties.resize(framebufferSize.get(0), framebufferSize.get(1)); - } - //Set the window title glfwSetWindowTitle(windowID, properties.getTitle()); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowThread.java b/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowThread.java index 8f0796c..e7b42f5 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowThread.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowThread.java @@ -55,7 +55,15 @@ public void run() { while (!quit) { deltaTime = rendererClock.getDeltaTime(); rendererClock.now(); + + //Make sure window Properties are correct with the current window size + int[] width = new int[1]; + int[] height = new int[1]; + glfwGetFramebufferSize(windowHandle, width, height); + properties.resize(width[0], height[0]); if (properties.isResized()) glViewport(0, 0, properties.getWidth(), properties.getHeight()); + + //Clear the last frame and render a new one glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ClientBase.getInstance().getRenderGraphRegistry().get(properties.getActiveScene().getRenderGraph()).render(getProperties(), deltaTime); properties.endFrame(); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 01ff3bd..52c9704 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -12,9 +12,11 @@ import com.terminalvelocitycabbage.engine.util.HeterogeneousMap; import com.terminalvelocitycabbage.templates.meshes.SquareDataMesh; +import static org.lwjgl.opengl.GL11.*; + public abstract class UIRenderNode extends RenderNode { - static final Projection PROJECTION = new Projection(Projection.Type.ORTHOGONAL, -100, 100); + static final Projection PROJECTION = new Projection(Projection.Type.ORTHOGONAL, 0, 100); public static final VertexFormat UI_ELEMENT_MESH_FORMAT = VertexFormat.builder() .addElement(VertexAttribute.XYZ_POSITION) @@ -40,7 +42,13 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r shaderProgram.getUniform("textureSampler").setUniform(0); shaderProgram.getUniform("projectionMatrix").setUniform(PROJECTION.getProjectionMatrix()); - drawUIElements(scene); + if (glIsEnabled(GL_DEPTH_TEST)) { + glDisable(GL_DEPTH_TEST); + drawUIElements(scene); + glEnable(GL_DEPTH_TEST); + } else { + drawUIElements(scene); + } //Reset shaderProgram.unbind(); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java b/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java index 0d56cce..5279c2a 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java @@ -12,7 +12,7 @@ public class Transformation { Vector3f position; Quaternionf rotation; - float scale; + Vector3f scale; boolean dirty; @@ -20,7 +20,7 @@ public Transformation() { transformationMatrix = new Matrix4f(); position = new Vector3f(); rotation = new Quaternionf(); - scale = 1f; + scale = new Vector3f(1f); dirty = true; } @@ -62,12 +62,18 @@ public Transformation setRotation(float x, float y, float z, float angle) { return this; } - public float getScale() { + public Vector3f getScale() { return scale; } public Transformation setScale(float scale) { - this.scale = scale; + this.scale.set(scale); + dirty = true; + return this; + } + + public Transformation setScale(float x, float y, float z) { + this.scale.set(x, y, z); dirty = true; return this; } diff --git a/src/main/java/com/terminalvelocitycabbage/templates/ecs/components/TransformationComponent.java b/src/main/java/com/terminalvelocitycabbage/templates/ecs/components/TransformationComponent.java index 7f544ef..3e85487 100644 --- a/src/main/java/com/terminalvelocitycabbage/templates/ecs/components/TransformationComponent.java +++ b/src/main/java/com/terminalvelocitycabbage/templates/ecs/components/TransformationComponent.java @@ -44,7 +44,7 @@ public TransformationComponent setRotation(float x, float y, float z, float angl return this; } - public float getScale() { + public Vector3f getScale() { return transformation.getScale(); } diff --git a/src/main/java/com/terminalvelocitycabbage/templates/meshes/SquareDataMesh.java b/src/main/java/com/terminalvelocitycabbage/templates/meshes/SquareDataMesh.java index 1a004c9..c578237 100644 --- a/src/main/java/com/terminalvelocitycabbage/templates/meshes/SquareDataMesh.java +++ b/src/main/java/com/terminalvelocitycabbage/templates/meshes/SquareDataMesh.java @@ -12,19 +12,19 @@ public class SquareDataMesh extends DataMesh { public Vertex[] getVertices(VertexFormat format) { return new Vertex[] { new Vertex(format) - .setXYZPosition(-1f, 1f, 0f) + .setXYZPosition(-0.5f, 0.5f, 0f) .setRGBColor(0.5f, 0.0f, 0.0f) .setUV(0, 0), new Vertex(format) - .setXYZPosition(-1f, -1f, 0f) + .setXYZPosition(-0.5f, -0.5f, 0f) .setRGBColor(0.0f, 0.5f, 0.0f) .setUV(0, 1), new Vertex(format) - .setXYZPosition(1f, -1f, 0f) + .setXYZPosition(0.5f, -0.5f, 0f) .setRGBColor(0.0f, 0.0f, 0.5f) .setUV(1, 1), new Vertex(format) - .setXYZPosition(1f, 1f, 0f) + .setXYZPosition(0.5f, 0.5f, 0f) .setRGBColor(0.0f, 0.5f, 0.5f) .setUV(1, 0) }; From ccd30758b499ca39a3c986f3b17602a9cfd7cd25 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sat, 9 Aug 2025 19:20:46 -0500 Subject: [PATCH 04/16] Fix a couple more bugs, now size style works --- .../engine/client/renderer/Projection.java | 2 +- .../engine/client/window/WindowProperties.java | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/Projection.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/Projection.java index 41035f5..9ba3037 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/Projection.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/Projection.java @@ -53,7 +53,7 @@ public Matrix4f getProjectionMatrix() { public void updateProjectionMatrix(float width, float height) { switch (type) { case PERSPECTIVE -> projectionMatrix.setPerspective(fieldOfView, width / height, nearPlane, farPlane); - case ORTHOGONAL -> projectionMatrix.setOrtho(-(width / 2), width / 2, -(width / 2), width / 2, nearPlane, farPlane); //TODO verify + case ORTHOGONAL -> projectionMatrix.setOrtho(-(width / 2), width / 2, -(height / 2), height / 2, nearPlane, farPlane); } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowProperties.java b/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowProperties.java index 98e512a..89ba510 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowProperties.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/window/WindowProperties.java @@ -11,6 +11,8 @@ public class WindowProperties { private int width; private int height; + private int lastWidth; + private int lastHeight; private String title; private boolean focused; private boolean mousedOver; @@ -102,9 +104,13 @@ protected void setMousedOver(boolean mousedOver) { * @param height the height of this window */ public void resize(int width, int height) { - this.resized = true; - this.width = width; - this.height = height; + if (width != lastWidth || height != lastHeight) { + this.resized = true; + this.lastWidth = this.width; + this.lastHeight = this.height; + this.width = width; + this.height = height; + } } /** From f5175f89d4faff473e4219a1bd8026a20538a6ca Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 14:09:20 -0500 Subject: [PATCH 05/16] Lots of refactors to get container context passing around --- .../engine/client/ui/Element.java | 3 + .../engine/client/ui/Layout.java | 110 ++++++++++++++++++ .../engine/client/ui/Style.java | 35 +----- .../engine/client/ui/UIContext.java | 60 ++++++++++ .../engine/graph/UIRenderNode.java | 100 +++++++++++++++- .../engine/util/Transformation.java | 12 ++ 6 files changed, 281 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java new file mode 100644 index 0000000..897ab4f --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java @@ -0,0 +1,3 @@ +package com.terminalvelocitycabbage.engine.client.ui; + +public record Element(Element parent, Layout layout, Style style) { } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java new file mode 100644 index 0000000..c121527 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java @@ -0,0 +1,110 @@ +package com.terminalvelocitycabbage.engine.client.ui; + +import org.joml.Matrix4f; + +public class Layout { + + Dimension width; + Dimension height; + Anchor anchor; + PlacementDirection placementDirection; + + public Layout(Dimension width, Dimension height, Anchor anchor, PlacementDirection placementDirection) { + this.width = width; + this.height = height; + this.anchor = anchor; + this.placementDirection = placementDirection; + } + + public Layout(Dimension width, Dimension height) { + this(width, height, Anchor.INHERIT, PlacementDirection.CENTER_CENTER); //TODO down right + } + + public Layout(int width, int height) { + this(new Dimension(width, Unit.PIXELS), new Dimension(height, Unit.PIXELS)); + } + + public enum Anchor { + TOP_LEFT, + TOP_CENTER, + TOP_RIGHT, + CENTER_LEFT, + CENTER_CENTER, + CENTER_RIGHT, + BOTTOM_LEFT, + BOTTOM_CENTER, + BOTTOM_RIGHT, + INHERIT + } + + public enum PlacementDirection { + DOWN_RIGHT, + DOWN_CENTER, + DOWN_LEFT, + RIGHT_CENTER, + CENTER_CENTER, + LEFT_CENTER, + UP_RIGHT, + UP_CENTER, + UP_LEFT; + + boolean transformedHorizontally() { + return this != UP_CENTER && this != DOWN_CENTER && this != CENTER_CENTER; + } + + boolean transformedVertically() { + return this != LEFT_CENTER && this != RIGHT_CENTER && this != CENTER_CENTER; + } + } + + public enum Unit { + PIXELS, + PERCENTAGE; + } + + public record Dimension(Integer value, Unit unit) { + + float toPixelDimension(UIContext context, boolean width) { + if (unit == Unit.PIXELS) return value; + if (width) { + return context.getCurrentContainer().layout().getWidth().value() * ((float) value / 100); + } else { + return context.getCurrentContainer().layout().getHeight().value() * ((float) value / 100); + } + } + } + + public Matrix4f getTransformationMatrix(UIContext context) { + + Matrix4f transformationMatrix = new Matrix4f(); + + //If the placement direction is not center, we want to move by half each dimension in the placement directions + //This is so that once we scale it'll already be in the right place + if (placementDirection.transformedHorizontally()) { + transformationMatrix.translate((float) width.value / 2, 0, 0); + } + if (placementDirection.transformedVertically()) { + transformationMatrix.translate(0, (float) height.value / 2, 0); + } + //Scale the object by its sizes + transformationMatrix.scale(width.toPixelDimension(context, true), height.toPixelDimension(context, false), 1); + //Move the element to its proper location + return transformationMatrix; + } + + public PlacementDirection getPlacementDirection() { + return placementDirection; + } + + public Anchor getAnchor() { + return anchor; + } + + public Dimension getHeight() { + return height; + } + + public Dimension getWidth() { + return width; + } +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java index 7a2be3f..766f7db 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java @@ -1,16 +1,13 @@ package com.terminalvelocitycabbage.engine.client.ui; import com.terminalvelocitycabbage.engine.registry.Identifier; -import com.terminalvelocitycabbage.engine.util.Transformation; public class Style { Identifier textureIdentifier; - Transformation transformation; - public Style(Identifier textureIdentifier, Transformation transformation) { + public Style(Identifier textureIdentifier) { this.textureIdentifier = textureIdentifier; - this.transformation = transformation; } public static Builder builder() { @@ -19,11 +16,9 @@ public static Builder builder() { public static class Builder { private Identifier textureIdentifier; - private final Transformation transformation; public Builder() { this.textureIdentifier = null; - this.transformation = new Transformation(); } public Builder setTexture(Identifier textureIdentifier) { @@ -31,29 +26,8 @@ public Builder setTexture(Identifier textureIdentifier) { return this; } - public Builder setPosition(int x, int y) { - transformation.setPosition(x, y, 0); - return this; - } - - public Builder setZIndex(int zIndex) { - transformation.translate(0, 0, zIndex); - return this; - } - - public Builder scale(float scale) { - transformation.setScale(scale); - return this; - } - - public Builder size(int width, int height) { - transformation.setScale(width, height, 1f); - return this; - } - public Style build() { - this.transformation.translate(0, 0, -50); //zIndex makes more sense where a higher number is on top so we will make the default -50 and add to it - return new Style(textureIdentifier, transformation); + return new Style(textureIdentifier); } } @@ -61,15 +35,10 @@ public Identifier getTextureIdentifier() { return textureIdentifier; } - public Transformation getTransformation() { - return transformation; - } - @Override public String toString() { return "Style{" + "textureIdentifier=" + textureIdentifier + - ", transformation=" + transformation + '}'; } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java new file mode 100644 index 0000000..e972363 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java @@ -0,0 +1,60 @@ +package com.terminalvelocitycabbage.engine.client.ui; + +import com.terminalvelocitycabbage.engine.client.window.WindowProperties; + +public class UIContext { + + Element previousContainer; + Element currentContainer; + Element previousElement; + Element currentElement; + + WindowProperties windowProperties; + + public UIContext(WindowProperties windowProperties) { + previousContainer = null; + currentContainer = null; + previousElement = null; + this.windowProperties = windowProperties; + } + + public Element getPreviousContainer() { + return previousContainer; + } + + public void setPreviousContainer(Element previousContainer) { + this.previousContainer = previousContainer; + } + + public Element getCurrentContainer() { + return currentContainer; + } + + public void setCurrentContainer(Element currentContainer) { + this.currentContainer = currentContainer; + } + + public Element getPreviousElement() { + return previousElement; + } + + public void setPreviousElement(Element previousSibling) { + this.previousElement = previousSibling; + } + + public void setCurrentElement(Element currentSibling) { + this.currentElement = currentSibling; + } + + public Element getCurrentElement() { + return currentElement; + } + + public int getWindowWidth() { + return windowProperties.getWidth(); + } + + public int getWindowHeight() { + return windowProperties.getHeight(); + } +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 52c9704..f3ded4a 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -7,8 +7,13 @@ import com.terminalvelocitycabbage.engine.client.renderer.model.Mesh; import com.terminalvelocitycabbage.engine.client.renderer.shader.ShaderProgramConfig; import com.terminalvelocitycabbage.engine.client.scene.Scene; +import com.terminalvelocitycabbage.engine.client.ui.Element; +import com.terminalvelocitycabbage.engine.client.ui.Layout; import com.terminalvelocitycabbage.engine.client.ui.Style; +import com.terminalvelocitycabbage.engine.client.ui.UIContext; import com.terminalvelocitycabbage.engine.client.window.WindowProperties; +import com.terminalvelocitycabbage.engine.registry.Identifier; +import com.terminalvelocitycabbage.engine.registry.Registry; import com.terminalvelocitycabbage.engine.util.HeterogeneousMap; import com.terminalvelocitycabbage.templates.meshes.SquareDataMesh; @@ -17,19 +22,28 @@ public abstract class UIRenderNode extends RenderNode { static final Projection PROJECTION = new Projection(Projection.Type.ORTHOGONAL, 0, 100); + //MeshCache meshCache; + Registry elementRegistry; public static final VertexFormat UI_ELEMENT_MESH_FORMAT = VertexFormat.builder() .addElement(VertexAttribute.XYZ_POSITION) .addElement(VertexAttribute.RGB_COLOR) .addElement(VertexAttribute.UV) .build(); + static final Mesh QUAD_MESH = new Mesh(UI_ELEMENT_MESH_FORMAT, new SquareDataMesh()); - static final Mesh BOX_MESH = new Mesh(UI_ELEMENT_MESH_FORMAT, new SquareDataMesh()); + UIContext context; public UIRenderNode(ShaderProgramConfig shaderProgramConfig) { super(shaderProgramConfig); + this.elementRegistry = new Registry<>(); + registerUIElements(new ElementRegistry(elementRegistry)); + //TODO generate mesh cache from registered elements and their styles + //this.meshCache = new MeshCache(); } + public abstract void registerUIElements(ElementRegistry elementRegistry); + public abstract void drawUIElements(Scene scene); @Override @@ -42,6 +56,19 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r shaderProgram.getUniform("textureSampler").setUniform(0); shaderProgram.getUniform("projectionMatrix").setUniform(PROJECTION.getProjectionMatrix()); + context = new UIContext(properties); + var rootElement = new Element( + null, + new Layout( + new Layout.Dimension(properties.getWidth(), Layout.Unit.PIXELS), + new Layout.Dimension(properties.getHeight(), Layout.Unit.PIXELS), + Layout.Anchor.CENTER_CENTER, Layout.PlacementDirection.CENTER_CENTER), + Style.builder().build()); + context.setPreviousContainer(null); + context.setPreviousElement(null); + context.setCurrentContainer(rootElement); + context.setCurrentElement(rootElement); + if (glIsEnabled(GL_DEPTH_TEST)) { glDisable(GL_DEPTH_TEST); drawUIElements(scene); @@ -50,19 +77,80 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r drawUIElements(scene); } + if (rootElement.style().getTextureIdentifier() != null) { + ClientBase.getInstance().getTextureCache().getTexture(rootElement.style().getTextureIdentifier()).bind(); + shaderProgram.getUniform("modelMatrix").setUniform(rootElement.layout().getTransformationMatrix(context)); + QUAD_MESH.render(); + } + //Reset shaderProgram.unbind(); } - public void drawBox(Scene scene, Style style) { + public void startContainer(Identifier elementIdentifier) { - var texture = ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()); - texture.bind(); + var thisElement = elementRegistry.get(elementIdentifier); + + context.setPreviousElement(null); + context.setCurrentElement(null); + context.setPreviousContainer(context.getCurrentContainer()); + context.setCurrentContainer(thisElement); + + + } - shaderProgram.getUniform("modelMatrix").setUniform(style.getTransformation().getTransformationMatrix()); + public void endContainer() { - BOX_MESH.render(); + var thisElement = context.getCurrentContainer(); + var thisLayout = thisElement.layout(); + var style = thisElement.style(); + + if (style.getTextureIdentifier() != null) { + ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()).bind(); + } + + shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix(context)); + + QUAD_MESH.render(); + + context.setPreviousElement(thisElement); + context.setCurrentElement(null); + context.setPreviousContainer(context.getPreviousContainer().parent()); + context.setCurrentContainer(context.getPreviousContainer()); + + } + public void drawBox(Identifier elementIdentifier) { + + var thisElement = elementRegistry.get(elementIdentifier); + var layout = thisElement.layout(); + var style = thisElement.style(); + + if (style.getTextureIdentifier() != null) { + var texture = ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()); + texture.bind(); + } + + shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(context)); + + QUAD_MESH.render(); + + context.setPreviousElement(context.getCurrentElement()); + context.setCurrentElement(thisElement); + + } + + public static class ElementRegistry extends Registry { + + Registry elementRegistry; + + public ElementRegistry(Registry elementRegistry) { + this.elementRegistry = elementRegistry; + } + + public Identifier registerElement(Identifier elementID, Layout layout, Style style) { + return elementRegistry.register(elementID, new Element(null, layout, style)).getIdentifier(); + } } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java b/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java index 5279c2a..ab19ca2 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/util/Transformation.java @@ -78,6 +78,18 @@ public Transformation setScale(float x, float y, float z) { return this; } + public Transformation addScale(float x, float y, float z) { + this.scale.add(x, y, z); + this.dirty = true; + return this; + } + + public Transformation addScale(Vector3f scale) { + this.scale.add(scale); + this.dirty = true; + return this; + } + public Matrix4f getTransformationMatrix() { if (dirty) updateTransformationMatrix(); return transformationMatrix; From baa7e94895490db34db4495cc670b60d2d331bc6 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 15:05:03 -0500 Subject: [PATCH 06/16] Some cleanup + really messy atlas work --- .../engine/client/renderer/RenderGraph.java | 3 + .../renderer/materials/TextureCache.java | 1 - .../engine/client/ui/Element.java | 4 +- .../engine/client/ui/Layout.java | 15 ++-- .../engine/client/ui/UIContext.java | 25 +++---- .../engine/graph/RenderNode.java | 8 +++ .../engine/graph/UIRenderNode.java | 71 ++++++++++--------- 7 files changed, 76 insertions(+), 51 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/RenderGraph.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/RenderGraph.java index 7a3a5ac..750f858 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/RenderGraph.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/RenderGraph.java @@ -55,6 +55,9 @@ public RenderGraph(RenderPath.Config renderPathBuilder) { public void init(GLCapabilities capabilities) { initialized = true; this.capabilities = capabilities; + graphNodes.forEach((identifier, togglePair) -> { + if (togglePair.getValue1() instanceof RenderNode renderNode) renderNode.init(this); + }); } /** diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java index 086f8ed..cd5b58e 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java @@ -29,7 +29,6 @@ public void generateAtlas(Identifier atlasIdentifier) { var textures = texturesToCompileToAtlas.get(atlasIdentifier); var atlas = new Atlas(textures); for (Identifier textureId : textures.keySet()) { - Log.info("Generating texture " + textureId + " for atlas " + atlasIdentifier); this.generatedTextures.put(textureId, atlas); } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java index 897ab4f..d11b436 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java @@ -1,3 +1,5 @@ package com.terminalvelocitycabbage.engine.client.ui; -public record Element(Element parent, Layout layout, Style style) { } +import com.terminalvelocitycabbage.engine.registry.Identifier; + +public record Element(Identifier parent, Layout layout, Style style) { } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java index c121527..73e6dd8 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java @@ -24,6 +24,11 @@ public Layout(int width, int height) { this(new Dimension(width, Unit.PIXELS), new Dimension(height, Unit.PIXELS)); } + public void setDimensions(int width, int height) { + this.width = new Dimension(width, Unit.PIXELS); + this.height = new Dimension(height, Unit.PIXELS); + } + public enum Anchor { TOP_LEFT, TOP_CENTER, @@ -64,17 +69,17 @@ public enum Unit { public record Dimension(Integer value, Unit unit) { - float toPixelDimension(UIContext context, boolean width) { + float toPixelDimension(Layout parentLayout, boolean width) { if (unit == Unit.PIXELS) return value; if (width) { - return context.getCurrentContainer().layout().getWidth().value() * ((float) value / 100); + return parentLayout.getWidth().value() * ((float) value / 100); } else { - return context.getCurrentContainer().layout().getHeight().value() * ((float) value / 100); + return parentLayout.getHeight().value() * ((float) value / 100); } } } - public Matrix4f getTransformationMatrix(UIContext context) { + public Matrix4f getTransformationMatrix(Layout currentContainerLayout) { Matrix4f transformationMatrix = new Matrix4f(); @@ -87,7 +92,7 @@ public Matrix4f getTransformationMatrix(UIContext context) { transformationMatrix.translate(0, (float) height.value / 2, 0); } //Scale the object by its sizes - transformationMatrix.scale(width.toPixelDimension(context, true), height.toPixelDimension(context, false), 1); + transformationMatrix.scale(width.toPixelDimension(currentContainerLayout, true), height.toPixelDimension(currentContainerLayout, false), 1); //Move the element to its proper location return transformationMatrix; } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java index e972363..7dd8335 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java @@ -1,13 +1,14 @@ package com.terminalvelocitycabbage.engine.client.ui; import com.terminalvelocitycabbage.engine.client.window.WindowProperties; +import com.terminalvelocitycabbage.engine.registry.Identifier; public class UIContext { - Element previousContainer; - Element currentContainer; - Element previousElement; - Element currentElement; + Identifier previousContainer; + Identifier currentContainer; + Identifier previousElement; + Identifier currentElement; WindowProperties windowProperties; @@ -18,35 +19,35 @@ public UIContext(WindowProperties windowProperties) { this.windowProperties = windowProperties; } - public Element getPreviousContainer() { + public Identifier getPreviousContainer() { return previousContainer; } - public void setPreviousContainer(Element previousContainer) { + public void setPreviousContainer(Identifier previousContainer) { this.previousContainer = previousContainer; } - public Element getCurrentContainer() { + public Identifier getCurrentContainer() { return currentContainer; } - public void setCurrentContainer(Element currentContainer) { + public void setCurrentContainer(Identifier currentContainer) { this.currentContainer = currentContainer; } - public Element getPreviousElement() { + public Identifier getPreviousElement() { return previousElement; } - public void setPreviousElement(Element previousSibling) { + public void setPreviousElement(Identifier previousSibling) { this.previousElement = previousSibling; } - public void setCurrentElement(Element currentSibling) { + public void setCurrentElement(Identifier currentSibling) { this.currentElement = currentSibling; } - public Element getCurrentElement() { + public Identifier getCurrentElement() { return currentElement; } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java index 35399ad..67d6022 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java @@ -62,6 +62,14 @@ public void recompileShaders() { recompileShaders = true; } + /** + * An optional init stage for rendergraph nodes + * @param renderGraph The rendergraph that this node belongs to + */ + public void init(RenderGraph renderGraph) { + + } + /** * @return The currently compiled shader program of this node */ diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index f3ded4a..6e006f0 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -2,9 +2,12 @@ import com.terminalvelocitycabbage.engine.client.ClientBase; import com.terminalvelocitycabbage.engine.client.renderer.Projection; +import com.terminalvelocitycabbage.engine.client.renderer.RenderGraph; import com.terminalvelocitycabbage.engine.client.renderer.elements.VertexAttribute; import com.terminalvelocitycabbage.engine.client.renderer.elements.VertexFormat; import com.terminalvelocitycabbage.engine.client.renderer.model.Mesh; +import com.terminalvelocitycabbage.engine.client.renderer.model.MeshCache; +import com.terminalvelocitycabbage.engine.client.renderer.model.Model; import com.terminalvelocitycabbage.engine.client.renderer.shader.ShaderProgramConfig; import com.terminalvelocitycabbage.engine.client.scene.Scene; import com.terminalvelocitycabbage.engine.client.ui.Element; @@ -21,8 +24,9 @@ public abstract class UIRenderNode extends RenderNode { + static final Identifier ROOT_ELEMENT_IDENTIFIER = ClientBase.getInstance().identifierOf("root"); static final Projection PROJECTION = new Projection(Projection.Type.ORTHOGONAL, 0, 100); - //MeshCache meshCache; + MeshCache meshCache; Registry elementRegistry; public static final VertexFormat UI_ELEMENT_MESH_FORMAT = VertexFormat.builder() @@ -36,10 +40,30 @@ public abstract class UIRenderNode extends RenderNode { public UIRenderNode(ShaderProgramConfig shaderProgramConfig) { super(shaderProgramConfig); + } + + @Override + public void init(RenderGraph renderGraph) { + super.init(renderGraph); this.elementRegistry = new Registry<>(); + var rootElement = new Element( + null, + new Layout( + new Layout.Dimension(0, Layout.Unit.PIXELS), + new Layout.Dimension(0, Layout.Unit.PIXELS), + Layout.Anchor.CENTER_CENTER, Layout.PlacementDirection.CENTER_CENTER), + Style.builder().build()); + elementRegistry.register(ROOT_ELEMENT_IDENTIFIER, rootElement); registerUIElements(new ElementRegistry(elementRegistry)); - //TODO generate mesh cache from registered elements and their styles - //this.meshCache = new MeshCache(); + var quadMesh = ClientBase.getInstance().identifierOf("quad"); + Registry meshRegistry = new Registry<>(); + meshRegistry.register(quadMesh, QUAD_MESH); + Registry modelRegistry = new Registry<>(); + elementRegistry.getRegistryContents().forEach((identifier, element) -> { + var texture = element.style().getTextureIdentifier(); + if (texture != null) modelRegistry.register(identifier, new Model(quadMesh, texture)); + }); + this.meshCache = new MeshCache(modelRegistry, meshRegistry, ClientBase.getInstance().getTextureCache()); } public abstract void registerUIElements(ElementRegistry elementRegistry); @@ -56,18 +80,12 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r shaderProgram.getUniform("textureSampler").setUniform(0); shaderProgram.getUniform("projectionMatrix").setUniform(PROJECTION.getProjectionMatrix()); + elementRegistry.get(ROOT_ELEMENT_IDENTIFIER).layout().setDimensions(properties.getWidth(), properties.getHeight()); context = new UIContext(properties); - var rootElement = new Element( - null, - new Layout( - new Layout.Dimension(properties.getWidth(), Layout.Unit.PIXELS), - new Layout.Dimension(properties.getHeight(), Layout.Unit.PIXELS), - Layout.Anchor.CENTER_CENTER, Layout.PlacementDirection.CENTER_CENTER), - Style.builder().build()); context.setPreviousContainer(null); context.setPreviousElement(null); - context.setCurrentContainer(rootElement); - context.setCurrentElement(rootElement); + context.setCurrentContainer(ROOT_ELEMENT_IDENTIFIER); + context.setCurrentElement(ROOT_ELEMENT_IDENTIFIER); if (glIsEnabled(GL_DEPTH_TEST)) { glDisable(GL_DEPTH_TEST); @@ -77,31 +95,20 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r drawUIElements(scene); } - if (rootElement.style().getTextureIdentifier() != null) { - ClientBase.getInstance().getTextureCache().getTexture(rootElement.style().getTextureIdentifier()).bind(); - shaderProgram.getUniform("modelMatrix").setUniform(rootElement.layout().getTransformationMatrix(context)); - QUAD_MESH.render(); - } - //Reset shaderProgram.unbind(); } public void startContainer(Identifier elementIdentifier) { - - var thisElement = elementRegistry.get(elementIdentifier); - context.setPreviousElement(null); context.setCurrentElement(null); context.setPreviousContainer(context.getCurrentContainer()); - context.setCurrentContainer(thisElement); - - + context.setCurrentContainer(elementIdentifier); } public void endContainer() { - var thisElement = context.getCurrentContainer(); + var thisElement = elementRegistry.get(context.getCurrentContainer()); var thisLayout = thisElement.layout(); var style = thisElement.style(); @@ -109,13 +116,13 @@ public void endContainer() { ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()).bind(); } - shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix(context)); + shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).layout())); - QUAD_MESH.render(); + meshCache.getMesh(context.getCurrentContainer()).render(); - context.setPreviousElement(thisElement); + context.setPreviousElement(context.getCurrentContainer()); context.setCurrentElement(null); - context.setPreviousContainer(context.getPreviousContainer().parent()); + context.setPreviousContainer(elementRegistry.get(context.getPreviousContainer()).parent()); context.setCurrentContainer(context.getPreviousContainer()); } @@ -131,12 +138,12 @@ public void drawBox(Identifier elementIdentifier) { texture.bind(); } - shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(context)); + shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).layout())); - QUAD_MESH.render(); + meshCache.getMesh(elementIdentifier).render(); context.setPreviousElement(context.getCurrentElement()); - context.setCurrentElement(thisElement); + context.setCurrentElement(elementIdentifier); } From ba4c6306ad742da84321f970d38fb62211dbcaf0 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 16:17:08 -0500 Subject: [PATCH 07/16] Fix element placements somewhat --- .../engine/client/ui/Layout.java | 91 +++++++++++-------- .../engine/graph/UIRenderNode.java | 40 +++++--- 2 files changed, 81 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java index 73e6dd8..6d4149e 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java @@ -17,7 +17,7 @@ public Layout(Dimension width, Dimension height, Anchor anchor, PlacementDirecti } public Layout(Dimension width, Dimension height) { - this(width, height, Anchor.INHERIT, PlacementDirection.CENTER_CENTER); //TODO down right + this(width, height, Anchor.INHERIT, PlacementDirection.CENTERED); //TODO down right } public Layout(int width, int height) { @@ -30,41 +30,49 @@ public void setDimensions(int width, int height) { } public enum Anchor { - TOP_LEFT, - TOP_CENTER, - TOP_RIGHT, - CENTER_LEFT, - CENTER_CENTER, - CENTER_RIGHT, - BOTTOM_LEFT, - BOTTOM_CENTER, - BOTTOM_RIGHT, - INHERIT + TOP_LEFT(1, -1), + TOP_CENTER(1, 0), + TOP_RIGHT(1, 1), + CENTER_LEFT(0, -1), + CENTER_CENTER(0, 0), + CENTER_RIGHT(0, 1), + BOTTOM_LEFT( -1, -1), + BOTTOM_CENTER( -1, 0), + BOTTOM_RIGHT( -1, 1), + INHERIT(0, 0); + + public int verticalMultiplier; + public int horizontalMultiplier; + + Anchor(int verticalMultiplier, int horizontalMultiplier) { + this.verticalMultiplier = verticalMultiplier; + this.horizontalMultiplier = horizontalMultiplier; + } } public enum PlacementDirection { - DOWN_RIGHT, - DOWN_CENTER, - DOWN_LEFT, - RIGHT_CENTER, - CENTER_CENTER, - LEFT_CENTER, - UP_RIGHT, - UP_CENTER, - UP_LEFT; - - boolean transformedHorizontally() { - return this != UP_CENTER && this != DOWN_CENTER && this != CENTER_CENTER; - } - - boolean transformedVertically() { - return this != LEFT_CENTER && this != RIGHT_CENTER && this != CENTER_CENTER; + DOWN_RIGHT(-0.5f, 0.5f), + DOWN(-0.5f, 0), + DOWN_LEFT(-0.5f, -0.5f), + RIGHT(0, 0.5f), + CENTERED(0, 0), + LEFT(0, -0.5f), + UP_RIGHT(0.5f, 0.5f), + UP(0.5f, 0), + UP_LEFT(0.5f, -0.5f); + + float xMultiplier; + float yMultiplier; + + PlacementDirection(float yOffset, float xOffset) { + this.xMultiplier = xOffset; + this.yMultiplier = yOffset; } } public enum Unit { PIXELS, - PERCENTAGE; + PERCENT; } public record Dimension(Integer value, Unit unit) { @@ -79,21 +87,30 @@ float toPixelDimension(Layout parentLayout, boolean width) { } } - public Matrix4f getTransformationMatrix(Layout currentContainerLayout) { + public Matrix4f getTransformationMatrix(Layout currentContainerLayout, Layout previousContainerLayout) { Matrix4f transformationMatrix = new Matrix4f(); + var pixelWidth = width.toPixelDimension(currentContainerLayout, true); + var pixelHeight = height.toPixelDimension(currentContainerLayout, false); + + var containerPixelWidth = currentContainerLayout.getWidth().toPixelDimension(previousContainerLayout, true); + var containerPixelHeight = currentContainerLayout.getHeight().toPixelDimension(previousContainerLayout, false); + + //Scale the object by its sizes + transformationMatrix.scale(pixelWidth, pixelHeight, 1); //If the placement direction is not center, we want to move by half each dimension in the placement directions //This is so that once we scale it'll already be in the right place - if (placementDirection.transformedHorizontally()) { - transformationMatrix.translate((float) width.value / 2, 0, 0); - } - if (placementDirection.transformedVertically()) { - transformationMatrix.translate(0, (float) height.value / 2, 0); - } - //Scale the object by its sizes - transformationMatrix.scale(width.toPixelDimension(currentContainerLayout, true), height.toPixelDimension(currentContainerLayout, false), 1); + transformationMatrix.translateLocal( + placementDirection.xMultiplier * pixelWidth, + placementDirection.yMultiplier * pixelHeight, + 0); + //Move the element to its proper location + transformationMatrix.translateLocal( + anchor.horizontalMultiplier * (containerPixelWidth / 2), + anchor.verticalMultiplier * (containerPixelHeight / 2), + 0); return transformationMatrix; } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 6e006f0..32dad15 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -15,6 +15,7 @@ import com.terminalvelocitycabbage.engine.client.ui.Style; import com.terminalvelocitycabbage.engine.client.ui.UIContext; import com.terminalvelocitycabbage.engine.client.window.WindowProperties; +import com.terminalvelocitycabbage.engine.debug.Log; import com.terminalvelocitycabbage.engine.registry.Identifier; import com.terminalvelocitycabbage.engine.registry.Registry; import com.terminalvelocitycabbage.engine.util.HeterogeneousMap; @@ -45,24 +46,26 @@ public UIRenderNode(ShaderProgramConfig shaderProgramConfig) { @Override public void init(RenderGraph renderGraph) { super.init(renderGraph); + + //Collect all elements used in this UI so that the textures and meshes can be cached this.elementRegistry = new Registry<>(); - var rootElement = new Element( - null, - new Layout( - new Layout.Dimension(0, Layout.Unit.PIXELS), - new Layout.Dimension(0, Layout.Unit.PIXELS), - Layout.Anchor.CENTER_CENTER, Layout.PlacementDirection.CENTER_CENTER), - Style.builder().build()); + var rootElement = new Element(null, new Layout(new Layout.Dimension(0, Layout.Unit.PIXELS), new Layout.Dimension(0, Layout.Unit.PIXELS), Layout.Anchor.CENTER_CENTER, Layout.PlacementDirection.CENTERED), Style.builder().build()); elementRegistry.register(ROOT_ELEMENT_IDENTIFIER, rootElement); registerUIElements(new ElementRegistry(elementRegistry)); - var quadMesh = ClientBase.getInstance().identifierOf("quad"); + + //Create a mesh cache so that textures can be sampled from the UI atlas Registry meshRegistry = new Registry<>(); + //All meshes that are provided by the UIRenderNode should be registered to the mesh registry here + var quadMesh = ClientBase.getInstance().identifierOf("quad"); meshRegistry.register(quadMesh, QUAD_MESH); + //Pair up meshes with textures here based on all textures used in element styles Registry modelRegistry = new Registry<>(); elementRegistry.getRegistryContents().forEach((identifier, element) -> { var texture = element.style().getTextureIdentifier(); + //TODO this will eventually depend on what type of texture it is using, eventually we will have textures with unique edge, corner, and middle conditions if (texture != null) modelRegistry.register(identifier, new Model(quadMesh, texture)); }); + //Generate the mesh cache from the paired meshes and textures (models) this.meshCache = new MeshCache(modelRegistry, meshRegistry, ClientBase.getInstance().getTextureCache()); } @@ -81,6 +84,7 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r shaderProgram.getUniform("projectionMatrix").setUniform(PROJECTION.getProjectionMatrix()); elementRegistry.get(ROOT_ELEMENT_IDENTIFIER).layout().setDimensions(properties.getWidth(), properties.getHeight()); + Log.info("set root dimensions to " + properties.getWidth() + " x " + properties.getHeight()); context = new UIContext(properties); context.setPreviousContainer(null); context.setPreviousElement(null); @@ -99,11 +103,14 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r shaderProgram.unbind(); } - public void startContainer(Identifier elementIdentifier) { + public boolean startContainer(Identifier elementIdentifier) { + context.setPreviousElement(null); context.setCurrentElement(null); context.setPreviousContainer(context.getCurrentContainer()); context.setCurrentContainer(elementIdentifier); + + return true; } public void endContainer() { @@ -116,18 +123,24 @@ public void endContainer() { ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()).bind(); } - shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).layout())); + var parent = elementRegistry.get(elementRegistry.get(context.getPreviousContainer()).parent()); + shaderProgram.getUniform("modelMatrix").setUniform( + thisLayout.getTransformationMatrix( + elementRegistry.get(context.getPreviousContainer()).layout(), + parent == null ? null : parent.layout() + ) + ); meshCache.getMesh(context.getCurrentContainer()).render(); context.setPreviousElement(context.getCurrentContainer()); context.setCurrentElement(null); - context.setPreviousContainer(elementRegistry.get(context.getPreviousContainer()).parent()); + context.setPreviousContainer(context.getPreviousContainer()); context.setCurrentContainer(context.getPreviousContainer()); } - public void drawBox(Identifier elementIdentifier) { + public boolean drawBox(Identifier elementIdentifier) { var thisElement = elementRegistry.get(elementIdentifier); var layout = thisElement.layout(); @@ -138,13 +151,14 @@ public void drawBox(Identifier elementIdentifier) { texture.bind(); } - shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).layout())); + shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).layout(), elementRegistry.get(context.getPreviousContainer()).layout())); meshCache.getMesh(elementIdentifier).render(); context.setPreviousElement(context.getCurrentElement()); context.setCurrentElement(elementIdentifier); + return true; } public static class ElementRegistry extends Registry { From 1d8b64bc47614403d14e9a51898c363af26b1def Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 17:34:38 -0500 Subject: [PATCH 08/16] A bit messy, but containers are working relative to one another --- .../engine/client/ui/ContainerLayout.java | 124 ++++++++++++++++++ .../engine/client/ui/Element.java | 29 +++- .../engine/client/ui/Layout.java | 106 +++++---------- .../engine/graph/UIRenderNode.java | 44 +++---- .../engine/registry/Registry.java | 2 +- 5 files changed, 205 insertions(+), 100 deletions(-) create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java new file mode 100644 index 0000000..fc9a208 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java @@ -0,0 +1,124 @@ +package com.terminalvelocitycabbage.engine.client.ui; + +import com.terminalvelocitycabbage.engine.debug.Log; +import org.joml.Matrix4f; +import org.joml.Vector3f; + +public class ContainerLayout extends Layout { + + Anchor anchor; + PlacementDirection placementDirection; + + Matrix4f transformationMatrix; + + public ContainerLayout(Dimension width, Dimension height, Anchor anchor, PlacementDirection placementDirection) { + super(width, height); + this.anchor = anchor; + this.placementDirection = placementDirection; + } + + public ContainerLayout(Dimension width, Dimension height) { + this(width, height, Anchor.CENTER_CENTER, PlacementDirection.CENTERED); + } + + public ContainerLayout(int width, int height) { + this(new Dimension(width, Unit.PIXELS), new Dimension(height, Unit.PIXELS)); + } + + @Override + public void setDimensions(int width, int height) { + super.setDimensions(width, height); + this.computedWidth = width; + this.computedHeight = height; + } + + public enum Anchor { + TOP_LEFT(1, -1), + TOP_CENTER(1, 0), + TOP_RIGHT(1, 1), + CENTER_LEFT(0, -1), + CENTER_CENTER(0, 0), + CENTER_RIGHT(0, 1), + BOTTOM_LEFT( -1, -1), + BOTTOM_CENTER( -1, 0), + BOTTOM_RIGHT( -1, 1), + INHERIT(0, 0); + + public int verticalMultiplier; + public int horizontalMultiplier; + + Anchor(int verticalMultiplier, int horizontalMultiplier) { + this.verticalMultiplier = verticalMultiplier; + this.horizontalMultiplier = horizontalMultiplier; + } + } + + public enum PlacementDirection { + DOWN_RIGHT(-0.5f, 0.5f), + DOWN(-0.5f, 0), + DOWN_LEFT(-0.5f, -0.5f), + RIGHT(0, 0.5f), + CENTERED(0, 0), + LEFT(0, -0.5f), + UP_RIGHT(0.5f, 0.5f), + UP(0.5f, 0), + UP_LEFT(0.5f, -0.5f); + + float xMultiplier; + float yMultiplier; + + PlacementDirection(float yOffset, float xOffset) { + this.xMultiplier = xOffset; + this.yMultiplier = yOffset; + } + } + + @Override + public Matrix4f getTransformationMatrix(Layout currentContainerLayout) { + + transformationMatrix = new Matrix4f(); + + ContainerLayout currentContainerLayoutCasted = (ContainerLayout) currentContainerLayout;; + + Log.info("drawing element with dimensions: " + width + " x " + height); + var pixelWidth = width.toPixelDimension(currentContainerLayout, true); + var pixelHeight = height.toPixelDimension(currentContainerLayout, false); + Log.info("drawing container with dimensions: " + pixelWidth + " x " + pixelHeight + " scale: " + transformationMatrix.getScale(new Vector3f())); + + var containerPixelWidth = currentContainerLayoutCasted.getComputedWidth(); + var containerPixelHeight = currentContainerLayoutCasted.getComputedHeight(); + + Log.info("container pixel dimensions: " + containerPixelWidth + " x " + containerPixelHeight); + + //Scale the object by its sizes + transformationMatrix.scale(pixelWidth, pixelHeight, 1); + //If the placement direction is not center, we want to move by half each dimension in the placement directions + //This is so that once we scale it'll already be in the right place + transformationMatrix.translateLocal( + placementDirection.xMultiplier * pixelWidth, + placementDirection.yMultiplier * pixelHeight, + 0); + + //Move the element to its proper location + var parentTransformationMatrix = currentContainerLayoutCasted.getStoredTransformationMatrix(); + transformationMatrix.translateLocal( + anchor.horizontalMultiplier * ((float) containerPixelWidth / 2), + anchor.verticalMultiplier * ((float) containerPixelHeight / 2), + 0); + //TODO precompute this somehow because it currently results in a one frame delay of proper layout generation + if (parentTransformationMatrix != null) transformationMatrix.translateLocal(parentTransformationMatrix.getTranslation(new Vector3f())); + return transformationMatrix; + } + + public PlacementDirection getPlacementDirection() { + return placementDirection; + } + + public Anchor getAnchor() { + return anchor; + } + + public Matrix4f getStoredTransformationMatrix() { + return transformationMatrix; + } +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java index d11b436..33b3595 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java @@ -2,4 +2,31 @@ import com.terminalvelocitycabbage.engine.registry.Identifier; -public record Element(Identifier parent, Layout layout, Style style) { } +public final class Element { + private Identifier parent; + private Layout layout; + private Style style; + + public Element(Identifier parent, Layout layout, Style style) { + this.parent = parent; + this.layout = layout; + this.style = style; + } + + public void setParent(Identifier parent) { + this.parent = parent; + } + + public Identifier getParent() { + return parent; + } + + public Layout getLayout() { + return layout; + } + + public Style getStyle() { + return style; + } + +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java index 6d4149e..1f876da 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java @@ -6,70 +6,30 @@ public class Layout { Dimension width; Dimension height; - Anchor anchor; - PlacementDirection placementDirection; - public Layout(Dimension width, Dimension height, Anchor anchor, PlacementDirection placementDirection) { - this.width = width; - this.height = height; - this.anchor = anchor; - this.placementDirection = placementDirection; - } + //In Pixels + int computedWidth; + int computedHeight; public Layout(Dimension width, Dimension height) { - this(width, height, Anchor.INHERIT, PlacementDirection.CENTERED); //TODO down right + this.width = width; + this.height = height; } public Layout(int width, int height) { this(new Dimension(width, Unit.PIXELS), new Dimension(height, Unit.PIXELS)); } + public void computeDimensions(Layout parentLayout) { + computedWidth = (int) (width.toPixelDimension(parentLayout, true)); + computedHeight = (int) (height.toPixelDimension(parentLayout, false)); + } + public void setDimensions(int width, int height) { this.width = new Dimension(width, Unit.PIXELS); this.height = new Dimension(height, Unit.PIXELS); } - public enum Anchor { - TOP_LEFT(1, -1), - TOP_CENTER(1, 0), - TOP_RIGHT(1, 1), - CENTER_LEFT(0, -1), - CENTER_CENTER(0, 0), - CENTER_RIGHT(0, 1), - BOTTOM_LEFT( -1, -1), - BOTTOM_CENTER( -1, 0), - BOTTOM_RIGHT( -1, 1), - INHERIT(0, 0); - - public int verticalMultiplier; - public int horizontalMultiplier; - - Anchor(int verticalMultiplier, int horizontalMultiplier) { - this.verticalMultiplier = verticalMultiplier; - this.horizontalMultiplier = horizontalMultiplier; - } - } - - public enum PlacementDirection { - DOWN_RIGHT(-0.5f, 0.5f), - DOWN(-0.5f, 0), - DOWN_LEFT(-0.5f, -0.5f), - RIGHT(0, 0.5f), - CENTERED(0, 0), - LEFT(0, -0.5f), - UP_RIGHT(0.5f, 0.5f), - UP(0.5f, 0), - UP_LEFT(0.5f, -0.5f); - - float xMultiplier; - float yMultiplier; - - PlacementDirection(float yOffset, float xOffset) { - this.xMultiplier = xOffset; - this.yMultiplier = yOffset; - } - } - public enum Unit { PIXELS, PERCENT; @@ -80,46 +40,32 @@ public record Dimension(Integer value, Unit unit) { float toPixelDimension(Layout parentLayout, boolean width) { if (unit == Unit.PIXELS) return value; if (width) { - return parentLayout.getWidth().value() * ((float) value / 100); + return parentLayout.getComputedWidth() * ((float) value / 100); } else { - return parentLayout.getHeight().value() * ((float) value / 100); + return parentLayout.getComputedHeight() * ((float) value / 100); } } + + @Override + public String toString() { + return "Dimension{" + + "value=" + value + + ", unit=" + unit + + '}'; + } } - public Matrix4f getTransformationMatrix(Layout currentContainerLayout, Layout previousContainerLayout) { + public Matrix4f getTransformationMatrix(Layout currentContainerLayout) { Matrix4f transformationMatrix = new Matrix4f(); var pixelWidth = width.toPixelDimension(currentContainerLayout, true); var pixelHeight = height.toPixelDimension(currentContainerLayout, false); - var containerPixelWidth = currentContainerLayout.getWidth().toPixelDimension(previousContainerLayout, true); - var containerPixelHeight = currentContainerLayout.getHeight().toPixelDimension(previousContainerLayout, false); - //Scale the object by its sizes transformationMatrix.scale(pixelWidth, pixelHeight, 1); - //If the placement direction is not center, we want to move by half each dimension in the placement directions - //This is so that once we scale it'll already be in the right place - transformationMatrix.translateLocal( - placementDirection.xMultiplier * pixelWidth, - placementDirection.yMultiplier * pixelHeight, - 0); - - //Move the element to its proper location - transformationMatrix.translateLocal( - anchor.horizontalMultiplier * (containerPixelWidth / 2), - anchor.verticalMultiplier * (containerPixelHeight / 2), - 0); - return transformationMatrix; - } - - public PlacementDirection getPlacementDirection() { - return placementDirection; - } - public Anchor getAnchor() { - return anchor; + return transformationMatrix; } public Dimension getHeight() { @@ -129,4 +75,12 @@ public Dimension getHeight() { public Dimension getWidth() { return width; } + + public int getComputedWidth() { + return computedWidth; + } + + public int getComputedHeight() { + return computedHeight; + } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 32dad15..9bb6dab 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -10,10 +10,7 @@ import com.terminalvelocitycabbage.engine.client.renderer.model.Model; import com.terminalvelocitycabbage.engine.client.renderer.shader.ShaderProgramConfig; import com.terminalvelocitycabbage.engine.client.scene.Scene; -import com.terminalvelocitycabbage.engine.client.ui.Element; -import com.terminalvelocitycabbage.engine.client.ui.Layout; -import com.terminalvelocitycabbage.engine.client.ui.Style; -import com.terminalvelocitycabbage.engine.client.ui.UIContext; +import com.terminalvelocitycabbage.engine.client.ui.*; import com.terminalvelocitycabbage.engine.client.window.WindowProperties; import com.terminalvelocitycabbage.engine.debug.Log; import com.terminalvelocitycabbage.engine.registry.Identifier; @@ -49,7 +46,7 @@ public void init(RenderGraph renderGraph) { //Collect all elements used in this UI so that the textures and meshes can be cached this.elementRegistry = new Registry<>(); - var rootElement = new Element(null, new Layout(new Layout.Dimension(0, Layout.Unit.PIXELS), new Layout.Dimension(0, Layout.Unit.PIXELS), Layout.Anchor.CENTER_CENTER, Layout.PlacementDirection.CENTERED), Style.builder().build()); + var rootElement = new Element(null, new ContainerLayout(new Layout.Dimension(0, Layout.Unit.PIXELS), new Layout.Dimension(0, Layout.Unit.PIXELS), ContainerLayout.Anchor.CENTER_CENTER, ContainerLayout.PlacementDirection.CENTERED), Style.builder().build()); elementRegistry.register(ROOT_ELEMENT_IDENTIFIER, rootElement); registerUIElements(new ElementRegistry(elementRegistry)); @@ -61,7 +58,7 @@ public void init(RenderGraph renderGraph) { //Pair up meshes with textures here based on all textures used in element styles Registry modelRegistry = new Registry<>(); elementRegistry.getRegistryContents().forEach((identifier, element) -> { - var texture = element.style().getTextureIdentifier(); + var texture = element.getStyle().getTextureIdentifier(); //TODO this will eventually depend on what type of texture it is using, eventually we will have textures with unique edge, corner, and middle conditions if (texture != null) modelRegistry.register(identifier, new Model(quadMesh, texture)); }); @@ -83,14 +80,14 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r shaderProgram.getUniform("textureSampler").setUniform(0); shaderProgram.getUniform("projectionMatrix").setUniform(PROJECTION.getProjectionMatrix()); - elementRegistry.get(ROOT_ELEMENT_IDENTIFIER).layout().setDimensions(properties.getWidth(), properties.getHeight()); - Log.info("set root dimensions to " + properties.getWidth() + " x " + properties.getHeight()); + elementRegistry.get(ROOT_ELEMENT_IDENTIFIER).getLayout().setDimensions(properties.getWidth(), properties.getHeight()); context = new UIContext(properties); context.setPreviousContainer(null); context.setPreviousElement(null); context.setCurrentContainer(ROOT_ELEMENT_IDENTIFIER); - context.setCurrentElement(ROOT_ELEMENT_IDENTIFIER); + context.setCurrentElement(null); + Log.info("Rendering UI -------------------------"); if (glIsEnabled(GL_DEPTH_TEST)) { glDisable(GL_DEPTH_TEST); drawUIElements(scene); @@ -105,9 +102,14 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r public boolean startContainer(Identifier elementIdentifier) { + Log.info(elementIdentifier + " started"); + context.setPreviousElement(null); context.setCurrentElement(null); context.setPreviousContainer(context.getCurrentContainer()); + var current = elementRegistry.get(elementIdentifier); + current.setParent(context.getCurrentContainer()); + current.getLayout().computeDimensions(elementRegistry.get(context.getCurrentContainer()).getLayout()); context.setCurrentContainer(elementIdentifier); return true; @@ -115,43 +117,41 @@ public boolean startContainer(Identifier elementIdentifier) { public void endContainer() { + Log.info(context.getCurrentContainer() + " ended"); var thisElement = elementRegistry.get(context.getCurrentContainer()); - var thisLayout = thisElement.layout(); - var style = thisElement.style(); + var thisLayout = (ContainerLayout) thisElement.getLayout(); + var style = thisElement.getStyle(); if (style.getTextureIdentifier() != null) { ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()).bind(); } - var parent = elementRegistry.get(elementRegistry.get(context.getPreviousContainer()).parent()); - shaderProgram.getUniform("modelMatrix").setUniform( - thisLayout.getTransformationMatrix( - elementRegistry.get(context.getPreviousContainer()).layout(), - parent == null ? null : parent.layout() - ) - ); + var previousParentContainer = elementRegistry.get(context.getPreviousContainer()).getParent(); + shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix(elementRegistry.get(context.getPreviousContainer()).getLayout())); meshCache.getMesh(context.getCurrentContainer()).render(); context.setPreviousElement(context.getCurrentContainer()); context.setCurrentElement(null); - context.setPreviousContainer(context.getPreviousContainer()); + Log.info("setting current container to " + context.getPreviousContainer()); + Log.info("setting previous container to " + previousParentContainer); context.setCurrentContainer(context.getPreviousContainer()); + context.setPreviousContainer(previousParentContainer == null ? context.getPreviousContainer() : previousParentContainer); } public boolean drawBox(Identifier elementIdentifier) { var thisElement = elementRegistry.get(elementIdentifier); - var layout = thisElement.layout(); - var style = thisElement.style(); + var layout = thisElement.getLayout(); + var style = thisElement.getStyle(); if (style.getTextureIdentifier() != null) { var texture = ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()); texture.bind(); } - shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).layout(), elementRegistry.get(context.getPreviousContainer()).layout())); + shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).getLayout())); meshCache.getMesh(elementIdentifier).render(); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/registry/Registry.java b/src/main/java/com/terminalvelocitycabbage/engine/registry/Registry.java index 3641ba7..d555777 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/registry/Registry.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/registry/Registry.java @@ -33,7 +33,7 @@ public Registry() { */ public RegistryPair register(Identifier identifier, T item) { if (registryContents.containsKey(identifier)) { - Log.warn("Tried to register item of same identifier " + identifier.toString() + " twice, the second addition has been ignored."); + Log.warn("Tried to register item of same identifier " + identifier.toString() + " twice, the second addition has been ignored. This will likely cause issues later on (probably crashes)."); return null; } registryContents.put(identifier, item); From 4f774afe17bb73b256deee305d496abbfc609b46 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 17:45:57 -0500 Subject: [PATCH 09/16] Remove some debug and get elements to align to containers kinda --- .../engine/client/ui/ContainerLayout.java | 15 ++++----------- .../engine/client/ui/Layout.java | 11 ++++++++++- .../engine/graph/UIRenderNode.java | 9 ++++----- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java index fc9a208..112c26d 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java @@ -1,6 +1,5 @@ package com.terminalvelocitycabbage.engine.client.ui; -import com.terminalvelocitycabbage.engine.debug.Log; import org.joml.Matrix4f; import org.joml.Vector3f; @@ -74,21 +73,15 @@ public enum PlacementDirection { } @Override - public Matrix4f getTransformationMatrix(Layout currentContainerLayout) { + public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout) { transformationMatrix = new Matrix4f(); - ContainerLayout currentContainerLayoutCasted = (ContainerLayout) currentContainerLayout;; - - Log.info("drawing element with dimensions: " + width + " x " + height); var pixelWidth = width.toPixelDimension(currentContainerLayout, true); var pixelHeight = height.toPixelDimension(currentContainerLayout, false); - Log.info("drawing container with dimensions: " + pixelWidth + " x " + pixelHeight + " scale: " + transformationMatrix.getScale(new Vector3f())); - - var containerPixelWidth = currentContainerLayoutCasted.getComputedWidth(); - var containerPixelHeight = currentContainerLayoutCasted.getComputedHeight(); - Log.info("container pixel dimensions: " + containerPixelWidth + " x " + containerPixelHeight); + var containerPixelWidth = currentContainerLayout.getComputedWidth(); + var containerPixelHeight = currentContainerLayout.getComputedHeight(); //Scale the object by its sizes transformationMatrix.scale(pixelWidth, pixelHeight, 1); @@ -100,7 +93,7 @@ public Matrix4f getTransformationMatrix(Layout currentContainerLayout) { 0); //Move the element to its proper location - var parentTransformationMatrix = currentContainerLayoutCasted.getStoredTransformationMatrix(); + var parentTransformationMatrix = currentContainerLayout.getStoredTransformationMatrix(); transformationMatrix.translateLocal( anchor.horizontalMultiplier * ((float) containerPixelWidth / 2), anchor.verticalMultiplier * ((float) containerPixelHeight / 2), diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java index 1f876da..b2e0a6a 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java @@ -1,6 +1,7 @@ package com.terminalvelocitycabbage.engine.client.ui; import org.joml.Matrix4f; +import org.joml.Vector3f; public class Layout { @@ -55,16 +56,24 @@ public String toString() { } } - public Matrix4f getTransformationMatrix(Layout currentContainerLayout) { + public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout) { Matrix4f transformationMatrix = new Matrix4f(); var pixelWidth = width.toPixelDimension(currentContainerLayout, true); var pixelHeight = height.toPixelDimension(currentContainerLayout, false); + var containerPixelWidth = currentContainerLayout.getComputedWidth(); + var containerPixelHeight = currentContainerLayout.getComputedHeight(); + //Scale the object by its sizes transformationMatrix.scale(pixelWidth, pixelHeight, 1); + //Move the element to its proper location + var parentTransformationMatrix = currentContainerLayout.getStoredTransformationMatrix(); + transformationMatrix.translateLocal((float) containerPixelWidth / 2, (float) containerPixelHeight / 2, 0); + if (parentTransformationMatrix != null) transformationMatrix.translateLocal(parentTransformationMatrix.getTranslation(new Vector3f())); + return transformationMatrix; } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 9bb6dab..ba15884 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -117,7 +117,6 @@ public boolean startContainer(Identifier elementIdentifier) { public void endContainer() { - Log.info(context.getCurrentContainer() + " ended"); var thisElement = elementRegistry.get(context.getCurrentContainer()); var thisLayout = (ContainerLayout) thisElement.getLayout(); var style = thisElement.getStyle(); @@ -127,14 +126,14 @@ public void endContainer() { } var previousParentContainer = elementRegistry.get(context.getPreviousContainer()).getParent(); - shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix(elementRegistry.get(context.getPreviousContainer()).getLayout())); + shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix((ContainerLayout) elementRegistry.get(context.getPreviousContainer()).getLayout())); meshCache.getMesh(context.getCurrentContainer()).render(); + Log.info(context.getCurrentContainer() + " ended"); + context.setPreviousElement(context.getCurrentContainer()); context.setCurrentElement(null); - Log.info("setting current container to " + context.getPreviousContainer()); - Log.info("setting previous container to " + previousParentContainer); context.setCurrentContainer(context.getPreviousContainer()); context.setPreviousContainer(previousParentContainer == null ? context.getPreviousContainer() : previousParentContainer); @@ -151,7 +150,7 @@ public boolean drawBox(Identifier elementIdentifier) { texture.bind(); } - shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix(elementRegistry.get(context.getCurrentContainer()).getLayout())); + shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix((ContainerLayout) elementRegistry.get(context.getCurrentContainer()).getLayout())); meshCache.getMesh(elementIdentifier).render(); From 4e07a6e83f2ea1d094dc62b7a8b8485c0e6be83a Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 18:28:17 -0500 Subject: [PATCH 10/16] Get elements rendering inside containers --- .../engine/client/ui/ContainerLayout.java | 2 +- .../engine/client/ui/Layout.java | 10 +++++-- .../engine/client/ui/UIContext.java | 9 ------- .../engine/graph/UIRenderNode.java | 26 ++++++++++++++----- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java index 112c26d..6956bd2 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java @@ -73,7 +73,7 @@ public enum PlacementDirection { } @Override - public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout) { + public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout, Layout previousElementLayout) { transformationMatrix = new Matrix4f(); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java index b2e0a6a..cc323dd 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java @@ -1,5 +1,6 @@ package com.terminalvelocitycabbage.engine.client.ui; +import com.terminalvelocitycabbage.engine.debug.Log; import org.joml.Matrix4f; import org.joml.Vector3f; @@ -56,7 +57,7 @@ public String toString() { } } - public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout) { + public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout, Layout previousElementLayout) { Matrix4f transformationMatrix = new Matrix4f(); @@ -71,7 +72,12 @@ public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout) //Move the element to its proper location var parentTransformationMatrix = currentContainerLayout.getStoredTransformationMatrix(); - transformationMatrix.translateLocal((float) containerPixelWidth / 2, (float) containerPixelHeight / 2, 0); + //TODO do container children positions here (don't move x and y, use container justify children to decide how to offset) + if (previousElementLayout != null) { + Log.info("Previous element stuff: " + previousElementLayout.getComputedWidth() + " x " + previousElementLayout.getComputedHeight()); + transformationMatrix.translateLocal(previousElementLayout.getComputedWidth(), previousElementLayout.getComputedHeight(), 0); + } + //Locate the elements based on container if (parentTransformationMatrix != null) transformationMatrix.translateLocal(parentTransformationMatrix.getTranslation(new Vector3f())); return transformationMatrix; diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java index 7dd8335..584f9b0 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/UIContext.java @@ -8,7 +8,6 @@ public class UIContext { Identifier previousContainer; Identifier currentContainer; Identifier previousElement; - Identifier currentElement; WindowProperties windowProperties; @@ -43,14 +42,6 @@ public void setPreviousElement(Identifier previousSibling) { this.previousElement = previousSibling; } - public void setCurrentElement(Identifier currentSibling) { - this.currentElement = currentSibling; - } - - public Identifier getCurrentElement() { - return currentElement; - } - public int getWindowWidth() { return windowProperties.getWidth(); } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index ba15884..9e35b03 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -40,6 +40,7 @@ public UIRenderNode(ShaderProgramConfig shaderProgramConfig) { super(shaderProgramConfig); } + //TODO this stuff needs to be done for all ui nodes on a graph, we don't really want to create a new cache for every UI node I don't think @Override public void init(RenderGraph renderGraph) { super.init(renderGraph); @@ -85,7 +86,6 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r context.setPreviousContainer(null); context.setPreviousElement(null); context.setCurrentContainer(ROOT_ELEMENT_IDENTIFIER); - context.setCurrentElement(null); Log.info("Rendering UI -------------------------"); if (glIsEnabled(GL_DEPTH_TEST)) { @@ -100,12 +100,12 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r shaderProgram.unbind(); } + //TODO add some warnings if a container is started but not ended as it creates some weird things if you don't public boolean startContainer(Identifier elementIdentifier) { Log.info(elementIdentifier + " started"); context.setPreviousElement(null); - context.setCurrentElement(null); context.setPreviousContainer(context.getCurrentContainer()); var current = elementRegistry.get(elementIdentifier); current.setParent(context.getCurrentContainer()); @@ -126,14 +126,18 @@ public void endContainer() { } var previousParentContainer = elementRegistry.get(context.getPreviousContainer()).getParent(); - shaderProgram.getUniform("modelMatrix").setUniform(thisLayout.getTransformationMatrix((ContainerLayout) elementRegistry.get(context.getPreviousContainer()).getLayout())); + shaderProgram.getUniform("modelMatrix").setUniform( + thisLayout.getTransformationMatrix( + (ContainerLayout) elementRegistry.get(context.getPreviousContainer()).getLayout(), + context.getPreviousElement() == null ? null : elementRegistry.get(context.getPreviousElement()).getLayout() + ) + ); meshCache.getMesh(context.getCurrentContainer()).render(); Log.info(context.getCurrentContainer() + " ended"); context.setPreviousElement(context.getCurrentContainer()); - context.setCurrentElement(null); context.setCurrentContainer(context.getPreviousContainer()); context.setPreviousContainer(previousParentContainer == null ? context.getPreviousContainer() : previousParentContainer); @@ -145,17 +149,25 @@ public boolean drawBox(Identifier elementIdentifier) { var layout = thisElement.getLayout(); var style = thisElement.getStyle(); + layout.computeDimensions(elementRegistry.get(context.getCurrentContainer()).getLayout()); + if (style.getTextureIdentifier() != null) { var texture = ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()); texture.bind(); } - shaderProgram.getUniform("modelMatrix").setUniform(layout.getTransformationMatrix((ContainerLayout) elementRegistry.get(context.getCurrentContainer()).getLayout())); + shaderProgram.getUniform("modelMatrix").setUniform( + layout.getTransformationMatrix( + (ContainerLayout) elementRegistry.get(context.getCurrentContainer()).getLayout(), + context.getPreviousElement() == null ? null : elementRegistry.get(context.getPreviousElement()).getLayout() + ) + ); meshCache.getMesh(elementIdentifier).render(); - context.setPreviousElement(context.getCurrentElement()); - context.setCurrentElement(elementIdentifier); + Log.info(elementIdentifier + " drawn"); + + context.setPreviousElement(elementIdentifier); return true; } From 4ca6474d396b3a4a825a55406ec0299ddea5cf56 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 18:30:42 -0500 Subject: [PATCH 11/16] Instead of disabling depth test instead just clear depth buffer before the UI renders --- .../engine/graph/UIRenderNode.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 9e35b03..4d1b56e 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -88,13 +88,8 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r context.setCurrentContainer(ROOT_ELEMENT_IDENTIFIER); Log.info("Rendering UI -------------------------"); - if (glIsEnabled(GL_DEPTH_TEST)) { - glDisable(GL_DEPTH_TEST); - drawUIElements(scene); - glEnable(GL_DEPTH_TEST); - } else { - drawUIElements(scene); - } + glClear(GL_DEPTH_BUFFER_BIT); + drawUIElements(scene); //Reset shaderProgram.unbind(); From 8ddf4b353efad8b2b07d3c02cd86d0fe25cd85cd Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sun, 10 Aug 2025 19:48:49 -0500 Subject: [PATCH 12/16] Add simple element wrapping --- .../engine/client/ui/ContainerLayout.java | 35 ++++++++++++++++--- .../engine/client/ui/Element.java | 5 +++ .../engine/client/ui/Layout.java | 31 ++++++++++++++-- .../engine/graph/UIRenderNode.java | 21 +++++++++-- 4 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java index 6956bd2..9c6fed5 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java @@ -7,17 +7,19 @@ public class ContainerLayout extends Layout { Anchor anchor; PlacementDirection placementDirection; + JustifyChildren justifyChildren; Matrix4f transformationMatrix; - public ContainerLayout(Dimension width, Dimension height, Anchor anchor, PlacementDirection placementDirection) { + public ContainerLayout(Dimension width, Dimension height, Anchor anchor, PlacementDirection placementDirection, JustifyChildren justifyChildren) { super(width, height); this.anchor = anchor; this.placementDirection = placementDirection; + this.justifyChildren = justifyChildren; } public ContainerLayout(Dimension width, Dimension height) { - this(width, height, Anchor.CENTER_CENTER, PlacementDirection.CENTERED); + this(width, height, Anchor.CENTER_CENTER, PlacementDirection.CENTERED, JustifyChildren.CENTER_CENTER); } public ContainerLayout(int width, int height) { @@ -40,8 +42,7 @@ public enum Anchor { CENTER_RIGHT(0, 1), BOTTOM_LEFT( -1, -1), BOTTOM_CENTER( -1, 0), - BOTTOM_RIGHT( -1, 1), - INHERIT(0, 0); + BOTTOM_RIGHT( -1, 1); public int verticalMultiplier; public int horizontalMultiplier; @@ -72,6 +73,28 @@ public enum PlacementDirection { } } + public enum JustifyChildren { + TOP_LEFT(0.5f, -0.5f, PlacementDirection.DOWN_RIGHT), + TOP_CENTER(0.5f, 0, PlacementDirection.DOWN), + TOP_RIGHT(0.5f, 0.5f, PlacementDirection.DOWN_LEFT), + CENTER_LEFT(0, -0.5f, PlacementDirection.RIGHT), + CENTER_CENTER(0, 0, PlacementDirection.CENTERED), + CENTER_RIGHT(0, 0.5f, PlacementDirection.LEFT), + BOTTOM_LEFT( -0.5f, -0.5f, PlacementDirection.UP_RIGHT), + BOTTOM_CENTER( -0.5f, 0, PlacementDirection.UP), + BOTTOM_RIGHT( -0.5f, 0.5f, PlacementDirection.UP_LEFT); + + public float verticalMultiplier; + public float horizontalMultiplier; + public PlacementDirection placementDirection; + + JustifyChildren(float verticalMultiplier, float horizontalMultiplier, PlacementDirection placementDirection) { + this.verticalMultiplier = verticalMultiplier; + this.horizontalMultiplier = horizontalMultiplier; + this.placementDirection = placementDirection; + } + } + @Override public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout, Layout previousElementLayout) { @@ -111,6 +134,10 @@ public Anchor getAnchor() { return anchor; } + public JustifyChildren getChildJustification() { + return justifyChildren; + } + public Matrix4f getStoredTransformationMatrix() { return transformationMatrix; } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java index 33b3595..456bb72 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java @@ -3,6 +3,7 @@ import com.terminalvelocitycabbage.engine.registry.Identifier; public final class Element { + private Identifier parent; private Layout layout; private Style style; @@ -29,4 +30,8 @@ public Style getStyle() { return style; } + public void reset() { + layout.reset(); + } + } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java index cc323dd..a0c86a7 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Layout.java @@ -13,6 +13,8 @@ public class Layout { int computedWidth; int computedHeight; + int nthChild = 0; + public Layout(Dimension width, Dimension height) { this.width = width; this.height = height; @@ -71,14 +73,33 @@ public Matrix4f getTransformationMatrix(ContainerLayout currentContainerLayout, transformationMatrix.scale(pixelWidth, pixelHeight, 1); //Move the element to its proper location + //Locate the elements based on container var parentTransformationMatrix = currentContainerLayout.getStoredTransformationMatrix(); + if (parentTransformationMatrix != null) transformationMatrix.translateLocal(parentTransformationMatrix.getTranslation(new Vector3f())); + //Justify element based on container + transformationMatrix.translateLocal( + containerPixelWidth * currentContainerLayout.getChildJustification().horizontalMultiplier, + containerPixelHeight * currentContainerLayout.getChildJustification().verticalMultiplier, + 0); + transformationMatrix.translateLocal( + pixelWidth * currentContainerLayout.getChildJustification().placementDirection.xMultiplier, + pixelHeight * currentContainerLayout.getChildJustification().placementDirection.yMultiplier, + 0 + ); + //Place relative to previous sibling //TODO do container children positions here (don't move x and y, use container justify children to decide how to offset) if (previousElementLayout != null) { Log.info("Previous element stuff: " + previousElementLayout.getComputedWidth() + " x " + previousElementLayout.getComputedHeight()); - transformationMatrix.translateLocal(previousElementLayout.getComputedWidth(), previousElementLayout.getComputedHeight(), 0); + transformationMatrix.translateLocal( + previousElementLayout.getComputedWidth() * currentContainerLayout.getChildJustification().placementDirection.xMultiplier * 2 * nthChild, + previousElementLayout.getComputedHeight() * currentContainerLayout.getChildJustification().placementDirection.yMultiplier * 2 * nthChild, + 0 + ); } - //Locate the elements based on container - if (parentTransformationMatrix != null) transformationMatrix.translateLocal(parentTransformationMatrix.getTranslation(new Vector3f())); + + nthChild++; + + Log.info("Nth Child incremented to: " + nthChild); return transformationMatrix; } @@ -98,4 +119,8 @@ public int getComputedWidth() { public int getComputedHeight() { return computedHeight; } + + public void reset() { + nthChild = 0; + } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 4d1b56e..f939815 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -20,6 +20,17 @@ import static org.lwjgl.opengl.GL11.*; +/* TODO +* It may be an issue of some reused elements being quasi connected. +* We need to replace the elementRegistry with some sort of element configuration registry that elements can be built +* from when they're used so that their actual identifiers in the render tree are not the same (when two identical +* element identifiers are used on a call they aren't modifying eachother). OR the other option is to make sure that +* in each of the drawX methods we don't modify any state for the current element before it is drawn to the screen +* since this is technically an immediate mode renderer it might be fine as is, it will depend mostly on how we handle +* saved states for more complex elements. I assume these will just be variables edited by actions not stored on any +* elements directly though so again, it might be fine. +*/ + public abstract class UIRenderNode extends RenderNode { static final Identifier ROOT_ELEMENT_IDENTIFIER = ClientBase.getInstance().identifierOf("root"); @@ -47,7 +58,7 @@ public void init(RenderGraph renderGraph) { //Collect all elements used in this UI so that the textures and meshes can be cached this.elementRegistry = new Registry<>(); - var rootElement = new Element(null, new ContainerLayout(new Layout.Dimension(0, Layout.Unit.PIXELS), new Layout.Dimension(0, Layout.Unit.PIXELS), ContainerLayout.Anchor.CENTER_CENTER, ContainerLayout.PlacementDirection.CENTERED), Style.builder().build()); + var rootElement = new Element(null, new ContainerLayout(new Layout.Dimension(0, Layout.Unit.PIXELS), new Layout.Dimension(0, Layout.Unit.PIXELS), ContainerLayout.Anchor.CENTER_CENTER, ContainerLayout.PlacementDirection.CENTERED, ContainerLayout.JustifyChildren.CENTER_CENTER), Style.builder().build()); elementRegistry.register(ROOT_ELEMENT_IDENTIFIER, rootElement); registerUIElements(new ElementRegistry(elementRegistry)); @@ -93,6 +104,8 @@ public void execute(Scene scene, WindowProperties properties, HeterogeneousMap r //Reset shaderProgram.unbind(); + + elementRegistry.getRegistryContents().values().forEach(Element::reset); } //TODO add some warnings if a container is started but not ended as it creates some weird things if you don't @@ -136,6 +149,8 @@ public void endContainer() { context.setCurrentContainer(context.getPreviousContainer()); context.setPreviousContainer(previousParentContainer == null ? context.getPreviousContainer() : previousParentContainer); + elementRegistry.getRegistryContents().values().forEach(Element::reset); + } public boolean drawBox(Identifier elementIdentifier) { @@ -144,8 +159,6 @@ public boolean drawBox(Identifier elementIdentifier) { var layout = thisElement.getLayout(); var style = thisElement.getStyle(); - layout.computeDimensions(elementRegistry.get(context.getCurrentContainer()).getLayout()); - if (style.getTextureIdentifier() != null) { var texture = ClientBase.getInstance().getTextureCache().getTexture(style.getTextureIdentifier()); texture.bind(); @@ -160,6 +173,8 @@ public boolean drawBox(Identifier elementIdentifier) { meshCache.getMesh(elementIdentifier).render(); + layout.computeDimensions(elementRegistry.get(context.getCurrentContainer()).getLayout()); + Log.info(elementIdentifier + " drawn"); context.setPreviousElement(elementIdentifier); From da582e5d0d2978eb3db2babae82d93bed957a1ed Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Fri, 15 Aug 2025 14:52:54 -0500 Subject: [PATCH 13/16] Adjust some redundant naming --- .../engine/client/ui/ContainerLayout.java | 12 ++++++------ .../engine/graph/UIRenderNode.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java index 9c6fed5..fa29f6a 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/ContainerLayout.java @@ -19,7 +19,7 @@ public ContainerLayout(Dimension width, Dimension height, Anchor anchor, Placeme } public ContainerLayout(Dimension width, Dimension height) { - this(width, height, Anchor.CENTER_CENTER, PlacementDirection.CENTERED, JustifyChildren.CENTER_CENTER); + this(width, height, Anchor.CENTER_CENTER, PlacementDirection.CENTERED, JustifyChildren.CENTER); } public ContainerLayout(int width, int height) { @@ -75,13 +75,13 @@ public enum PlacementDirection { public enum JustifyChildren { TOP_LEFT(0.5f, -0.5f, PlacementDirection.DOWN_RIGHT), - TOP_CENTER(0.5f, 0, PlacementDirection.DOWN), + TOP(0.5f, 0, PlacementDirection.DOWN), TOP_RIGHT(0.5f, 0.5f, PlacementDirection.DOWN_LEFT), - CENTER_LEFT(0, -0.5f, PlacementDirection.RIGHT), - CENTER_CENTER(0, 0, PlacementDirection.CENTERED), - CENTER_RIGHT(0, 0.5f, PlacementDirection.LEFT), + LEFT(0, -0.5f, PlacementDirection.RIGHT), + CENTER(0, 0, PlacementDirection.CENTERED), + RIGHT(0, 0.5f, PlacementDirection.LEFT), BOTTOM_LEFT( -0.5f, -0.5f, PlacementDirection.UP_RIGHT), - BOTTOM_CENTER( -0.5f, 0, PlacementDirection.UP), + BOTTOM( -0.5f, 0, PlacementDirection.UP), BOTTOM_RIGHT( -0.5f, 0.5f, PlacementDirection.UP_LEFT); public float verticalMultiplier; diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index f939815..213258f 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -58,7 +58,7 @@ public void init(RenderGraph renderGraph) { //Collect all elements used in this UI so that the textures and meshes can be cached this.elementRegistry = new Registry<>(); - var rootElement = new Element(null, new ContainerLayout(new Layout.Dimension(0, Layout.Unit.PIXELS), new Layout.Dimension(0, Layout.Unit.PIXELS), ContainerLayout.Anchor.CENTER_CENTER, ContainerLayout.PlacementDirection.CENTERED, ContainerLayout.JustifyChildren.CENTER_CENTER), Style.builder().build()); + var rootElement = new Element(null, new ContainerLayout(new Layout.Dimension(0, Layout.Unit.PIXELS), new Layout.Dimension(0, Layout.Unit.PIXELS), ContainerLayout.Anchor.CENTER_CENTER, ContainerLayout.PlacementDirection.CENTERED, ContainerLayout.JustifyChildren.CENTER), Style.builder().build()); elementRegistry.register(ROOT_ELEMENT_IDENTIFIER, rootElement); registerUIElements(new ElementRegistry(elementRegistry)); From 937d10f0553f94f8214d68f622698b8bbc1cd673 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sat, 16 Aug 2025 10:59:46 -0500 Subject: [PATCH 14/16] Start on working towards text rendering --- gradleScripts/dependenciesClient.gradle | 4 + .../engine/client/ClientBase.java | 10 +- .../engine/client/ui/Element.java | 8 ++ .../engine/client/ui/Font.java | 123 ++++++++++++++++++ .../engine/client/ui/Style.java | 18 ++- .../engine/graph/UIRenderNode.java | 13 ++ .../templates/events/GenerateFontsEvent.java | 33 +++++ 7 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java create mode 100644 src/main/java/com/terminalvelocitycabbage/templates/events/GenerateFontsEvent.java diff --git a/gradleScripts/dependenciesClient.gradle b/gradleScripts/dependenciesClient.gradle index 1ba29f0..9e56d48 100644 --- a/gradleScripts/dependenciesClient.gradle +++ b/gradleScripts/dependenciesClient.gradle @@ -15,10 +15,14 @@ dependencies { implementation "org.lwjgl:lwjgl-openal" implementation "org.lwjgl:lwjgl-opengl" implementation "org.lwjgl:lwjgl-stb" + implementation "org.lwjgl:lwjgl-freetype" + implementation "org.lwjgl:lwjgl-harfbuzz" runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-nanovg::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-freetype::$lwjglNatives" + runtimeOnly "org.lwjgl:lwjgl-harfbuzz::$lwjglNatives" } \ No newline at end of file diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java index 9323f12..92e9a23 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java @@ -7,6 +7,7 @@ import com.terminalvelocitycabbage.engine.client.renderer.materials.TextureCache; import com.terminalvelocitycabbage.engine.client.renderer.model.Mesh; import com.terminalvelocitycabbage.engine.client.renderer.model.Model; +import com.terminalvelocitycabbage.engine.client.ui.Font; import com.terminalvelocitycabbage.engine.client.window.InputCallbackListener; import com.terminalvelocitycabbage.engine.client.window.WindowManager; import com.terminalvelocitycabbage.engine.filesystem.resources.ResourceCategory; @@ -32,10 +33,11 @@ public abstract class ClientBase extends MainEntrypoint implements NetworkedSide private final WindowManager windowManager; private final Registry renderGraphRegistry; - //Scene stuff + //Rendering stuff protected final Registry meshRegistry; protected final Registry modelRegistry; protected TextureCache textureCache; + protected final Registry fontRegistry; //Networking stuff private final Client client; @@ -55,6 +57,7 @@ public ClientBase(String namespace, int ticksPerSecond) { inputCallbackListener = new InputCallbackListener(); meshRegistry = new Registry<>(); modelRegistry = new Registry<>(); + fontRegistry = new Registry<>(); client = new Client(); } @@ -95,6 +98,7 @@ public void init() { var configureTexturesEvent = new ConfigureTexturesEvent(fileSystem); eventDispatcher.dispatchEvent(configureTexturesEvent); textureCache = new TextureCache(configureTexturesEvent.getTexturesToCompileToAtlas(), configureTexturesEvent.getSingleTextures()); + eventDispatcher.dispatchEvent(new GenerateFontsEvent(fileSystem, fontRegistry)); eventDispatcher.dispatchEvent(new RendererRegistrationEvent(renderGraphRegistry)); eventDispatcher.dispatchEvent(new SceneRegistrationEvent(sceneRegistry)); eventDispatcher.dispatchEvent(new MeshRegistrationEvent(meshRegistry)); @@ -216,4 +220,8 @@ public Registry getModelRegistry() { public TextureCache getTextureCache() { return textureCache; } + + public Registry getFontRegistry() { + return fontRegistry; + } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java index 456bb72..47aedab 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Element.java @@ -34,4 +34,12 @@ public void reset() { layout.reset(); } + @Override + public String toString() { + return "Element{" + + "parent=" + parent + + ", layout=" + layout.toString() + + ", style=" + style.toString() + + '}'; + } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java new file mode 100644 index 0000000..be356b9 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java @@ -0,0 +1,123 @@ +package com.terminalvelocitycabbage.engine.client.ui; + +import com.terminalvelocitycabbage.engine.debug.Log; +import com.terminalvelocitycabbage.engine.filesystem.resources.Resource; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.util.freetype.FT_Face; +import org.lwjgl.util.freetype.FreeType; +import org.lwjgl.util.harfbuzz.HarfBuzz; +import org.lwjgl.util.harfbuzz.hb_glyph_info_t; +import org.lwjgl.util.harfbuzz.hb_glyph_position_t; + +public class Font { + + private final int fontSize; //In pixels + private final long freeType; + private final long facePointer; + private final FT_Face face; + private final long font; + + public Font(Resource resource, int fontSize) { + + this.fontSize = fontSize; + + //Init freetype for font loading + try (MemoryStack stack = MemoryStack.stackPush()) { + //Init freetype and store pointer to library for later + PointerBuffer ftPointer = stack.callocPointer(1); + FreeType.FT_Init_FreeType(ftPointer); + freeType = ftPointer.get(0); + + //Load font + PointerBuffer facePointerBuffer = stack.callocPointer(1); + FreeType.FT_New_Memory_Face(freeType, resource.asByteBuffer(true), 0, facePointerBuffer); + facePointer = facePointerBuffer.get(0); + + //Configure font + face = FT_Face.create(this.facePointer); + FreeType.FT_Set_Pixel_Sizes(face, 0, fontSize); + } + + //Init harfbuzz + font = HarfBuzz.hb_ft_font_create_referenced(facePointer); + } + + public ShapedText shapeText(String text) { + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = HarfBuzz.hb_buffer_create(); + HarfBuzz.hb_buffer_add_utf8(buffer, text, 0, text.length()); + HarfBuzz.hb_buffer_guess_segment_properties(buffer); + + HarfBuzz.hb_shape(font, buffer, null); + + int glyphCount = HarfBuzz.hb_buffer_get_length(buffer); + hb_glyph_info_t.Buffer infos = HarfBuzz.hb_buffer_get_glyph_infos(buffer); + hb_glyph_position_t.Buffer positions = HarfBuzz.hb_buffer_get_glyph_positions(buffer); + + ShapedText shaped = new ShapedText(glyphCount); + for (int i = 0; i < glyphCount; i++) { + int glyphId = infos.get(i).codepoint(); + float xAdvance = positions.get(i).x_advance() / 64f; + float yAdvance = positions.get(i).y_advance() / 64f; + float xOffset = positions.get(i).x_offset() / 64f; + float yOffset = positions.get(i).y_offset() / 64f; + shaped.setGlyph(i, glyphId, xAdvance, yAdvance, xOffset, yOffset); + } + + HarfBuzz.hb_buffer_destroy(buffer); + return shaped; + } + } + + public void drawText(ShapedText text, float startX, float startY) { + + float x = startX; + float y = startY; + + for (int i = 0; i < text.glyphCount; i++) { + GlyphData glyphData = text.glyphs[i]; + + //TODO Draw + Log.info("Drawing glyph " + glyphData.glyphId + " at " + x + glyphData.xOffset + ", " + y + glyphData.yOffset); + + x += glyphData.xAdvance; + y += glyphData.yAdvance; + } + + } + + public void cleanup() { + HarfBuzz.hb_font_destroy(font); + FreeType.FT_Done_Face(face); + FreeType.FT_Done_FreeType(freeType); + } + + public static class ShapedText { + final int glyphCount; + final GlyphData[] glyphs; + + ShapedText(int glyphCount) { + this.glyphCount = glyphCount; + this.glyphs = new GlyphData[glyphCount]; + } + + void setGlyph(int index, int glyphId, float xAdv, float yAdv, float xOff, float yOff) { + glyphs[index] = new GlyphData(glyphId, xAdv, yAdv, xOff, yOff); + } + } + + public static class GlyphData { + final int glyphId; + final float xAdvance, yAdvance; + final float xOffset, yOffset; + + GlyphData(int glyphId, float xAdvance, float yAdvance, float xOffset, float yOffset) { + this.glyphId = glyphId; + this.xAdvance = xAdvance; + this.yAdvance = yAdvance; + this.xOffset = xOffset; + this.yOffset = yOffset; + } + } +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java index 766f7db..4a1aba3 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Style.java @@ -5,9 +5,11 @@ public class Style { Identifier textureIdentifier; + Identifier fontIdentifier; - public Style(Identifier textureIdentifier) { + public Style(Identifier textureIdentifier, Identifier fontIdentifier) { this.textureIdentifier = textureIdentifier; + this.fontIdentifier = fontIdentifier; } public static Builder builder() { @@ -16,9 +18,11 @@ public static Builder builder() { public static class Builder { private Identifier textureIdentifier; + private Identifier fontIdentifier; public Builder() { this.textureIdentifier = null; + this.fontIdentifier = null; } public Builder setTexture(Identifier textureIdentifier) { @@ -26,8 +30,13 @@ public Builder setTexture(Identifier textureIdentifier) { return this; } + public Builder setFont(Identifier fontIdentifier) { + this.fontIdentifier = fontIdentifier; + return this; + } + public Style build() { - return new Style(textureIdentifier); + return new Style(textureIdentifier, fontIdentifier); } } @@ -35,10 +44,15 @@ public Identifier getTextureIdentifier() { return textureIdentifier; } + public Identifier getFontIdentifier() { + return fontIdentifier; + } + @Override public String toString() { return "Style{" + "textureIdentifier=" + textureIdentifier + + ", fontIdentifier=" + fontIdentifier + '}'; } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 213258f..20ce590 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -153,6 +153,19 @@ public void endContainer() { } + public boolean drawText(Identifier elementIdentifier, String text) { + + Element thisElement = elementRegistry.get(elementIdentifier); + Identifier fontIdentifier = thisElement.getStyle().getFontIdentifier(); + Font font = ClientBase.getInstance().getFontRegistry().get(fontIdentifier); + + Font.ShapedText shapedText = font.shapeText(text); + + font.drawText(shapedText, 0, 0); + + return true; + } + public boolean drawBox(Identifier elementIdentifier) { var thisElement = elementRegistry.get(elementIdentifier); diff --git a/src/main/java/com/terminalvelocitycabbage/templates/events/GenerateFontsEvent.java b/src/main/java/com/terminalvelocitycabbage/templates/events/GenerateFontsEvent.java new file mode 100644 index 0000000..b79c0fe --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/templates/events/GenerateFontsEvent.java @@ -0,0 +1,33 @@ +package com.terminalvelocitycabbage.templates.events; + +import com.terminalvelocitycabbage.engine.TerminalVelocityEngine; +import com.terminalvelocitycabbage.engine.client.ui.Font; +import com.terminalvelocitycabbage.engine.event.Event; +import com.terminalvelocitycabbage.engine.filesystem.GameFileSystem; +import com.terminalvelocitycabbage.engine.filesystem.resources.ResourceCategory; +import com.terminalvelocitycabbage.engine.registry.Identifier; +import com.terminalvelocitycabbage.engine.registry.Registry; +import org.lwjgl.system.Configuration; +import org.lwjgl.util.freetype.FreeType; + +public class GenerateFontsEvent extends Event { + + public static final Identifier EVENT = TerminalVelocityEngine.identifierOf("GenerateFontsEvent"); + + private GameFileSystem fileSystem; + private Registry fontRegistry; + + public GenerateFontsEvent(GameFileSystem fileSystem, Registry fontRegistry) { + super(EVENT); + this.fileSystem = fileSystem; + this.fontRegistry = fontRegistry; + Configuration.HARFBUZZ_LIBRARY_NAME.set(FreeType.getLibrary()); + } + + public Identifier generateFont(Identifier fontResourceIdentifier, int fontSize) { + var resource = fileSystem.getResource(ResourceCategory.FONT, fontResourceIdentifier); + var fontIdentifier = new Identifier(fontResourceIdentifier.getNamespace(), fontResourceIdentifier.getName() + "_" + fontSize); + fontRegistry.register(fontIdentifier, new Font(resource, fontSize)); + return fontIdentifier; + } +} From 29dcf5792f3d870ec1f16104587975f9dca3968f Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Mon, 18 Aug 2025 19:14:23 -0500 Subject: [PATCH 15/16] Commit some more progress, but it crashes now with no errors so not working --- getho_bold.ttf | Bin 0 -> 32856 bytes .../engine/client/ClientBase.java | 2 +- .../client/renderer/materials/FontAtlas.java | 73 ++++++++++++++++++ .../renderer/materials/TextureCache.java | 11 ++- .../engine/client/ui/Font.java | 24 ++++++ .../engine/graph/UIRenderNode.java | 2 + .../engine/util/BufferUtils.java | 42 ++++++++++ .../engine/util/MathUtils.java | 8 ++ 8 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 getho_bold.ttf create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/FontAtlas.java create mode 100644 src/main/java/com/terminalvelocitycabbage/engine/util/BufferUtils.java diff --git a/getho_bold.ttf b/getho_bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..49bc5de376e8676a417f284b3e3107bfd19b5aa8 GIT binary patch literal 32856 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

at>loUDDYh^`Ic%iIqfTk?_f2?TI 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

yLl4jr3BL1M*xR8Py*^? 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 Sh^QMPN5Y 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 literal 0 HcmV?d00001 diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java index 92e9a23..b2f6394 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ClientBase.java @@ -95,10 +95,10 @@ public void init() { eventDispatcher.dispatchEvent(new EntityComponentRegistrationEvent(manager)); eventDispatcher.dispatchEvent(new EntitySystemRegistrationEvent(manager)); eventDispatcher.dispatchEvent(new RoutineRegistrationEvent(routineRegistry)); + eventDispatcher.dispatchEvent(new GenerateFontsEvent(fileSystem, fontRegistry)); var configureTexturesEvent = new ConfigureTexturesEvent(fileSystem); eventDispatcher.dispatchEvent(configureTexturesEvent); textureCache = new TextureCache(configureTexturesEvent.getTexturesToCompileToAtlas(), configureTexturesEvent.getSingleTextures()); - eventDispatcher.dispatchEvent(new GenerateFontsEvent(fileSystem, fontRegistry)); eventDispatcher.dispatchEvent(new RendererRegistrationEvent(renderGraphRegistry)); eventDispatcher.dispatchEvent(new SceneRegistrationEvent(sceneRegistry)); eventDispatcher.dispatchEvent(new MeshRegistrationEvent(meshRegistry)); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/FontAtlas.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/FontAtlas.java new file mode 100644 index 0000000..8aca022 --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/FontAtlas.java @@ -0,0 +1,73 @@ +package com.terminalvelocitycabbage.engine.client.renderer.materials; + +import com.terminalvelocitycabbage.engine.client.ui.Font; +import com.terminalvelocitycabbage.engine.debug.Log; +import com.terminalvelocitycabbage.engine.util.ImageUtils; +import com.terminalvelocitycabbage.engine.util.MathUtils; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.util.freetype.FT_Bitmap; +import org.lwjgl.util.freetype.FreeType; + +import java.io.File; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +public class FontAtlas extends Texture { + + private final Map glyphMap = new HashMap<>(); + + public FontAtlas(Font font, int[] glyphIds) { + + //Determine the likely size of atlas (cross the bridge when we get to it if we're wrong) + var atlasSize = MathUtils.findNearestPowerOfTwo((int) Math.ceil(Math.sqrt(font.getFontSize() * font.getFontSize() * glyphIds.length))); + this.width = atlasSize; + this.height = atlasSize; + + //Create the buffer for this atlas + ByteBuffer atlasBuffer = ByteBuffer.allocateDirect(this.width * this.height); + + int x = 0, y = 0, rowHeight = 0; + try (MemoryStack stack = MemoryStack.stackPush()) { + for (int glyphId : glyphIds) { + // Load glyph into FreeType + FreeType.FT_Load_Glyph(font.getFace(), glyphId, FreeType.FT_LOAD_RENDER); + FT_Bitmap bitmap = font.getFace().glyph().bitmap(); + + if (x + bitmap.width() >= getWidth()) { + x = 0; + y += rowHeight; + rowHeight = 0; + } + + // Copy glyph bitmap into atlas + ByteBuffer bitmapBuffer = bitmap.buffer(bitmap.rows() * bitmap.pitch()); + for (int row = 0; row < bitmap.rows(); row++) { + for (int col = 0; col < bitmap.width(); col++) { + atlasBuffer.put((y + row) * getWidth() + (x + col), bitmapBuffer.get(row * bitmap.pitch() + col)); + } + } + + // Record glyph info + glyphMap.put(glyphId, new GlyphInfo(x, y, bitmap.width(), bitmap.rows(), font.getFace().glyph().advance().x() / 64f)); + + x += bitmap.width() + 1; + rowHeight = Math.max(rowHeight, bitmap.rows()); + } + } + + // Upload to OpenGL + Log.info("generateOpenGLTexture"); + generateOpenGLTexture(width, height, 4, atlasBuffer); + + Log.info("Saving font atlas to file"); + ImageUtils.saveRGBAtoPNG(atlasBuffer, width, height, new File("font_atlas.png")); + } + + public GlyphInfo getGlyphInfo(int glyphId) { + return glyphMap.get(glyphId); + } + + public record GlyphInfo(int x, int y, int width, int height, float xAdvance) { } + +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java index cd5b58e..8bb99ec 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/renderer/materials/TextureCache.java @@ -1,5 +1,6 @@ package com.terminalvelocitycabbage.engine.client.renderer.materials; +import com.terminalvelocitycabbage.engine.client.ClientBase; import com.terminalvelocitycabbage.engine.debug.Log; import com.terminalvelocitycabbage.engine.filesystem.resources.Resource; import com.terminalvelocitycabbage.engine.registry.Identifier; @@ -18,11 +19,14 @@ public class TextureCache { private final Map singleTextures; private final Map generatedTextures; + private final Map fontAtlasTextures; public TextureCache(Map> texturesToCompileToAtlas, Map singleTextures) { - this.generatedTextures = new HashMap<>(); this.texturesToCompileToAtlas = texturesToCompileToAtlas; + this.singleTextures = singleTextures; + this.generatedTextures = new HashMap<>(); + this.fontAtlasTextures = new HashMap<>(); } public void generateAtlas(Identifier atlasIdentifier) { @@ -33,6 +37,11 @@ public void generateAtlas(Identifier atlasIdentifier) { } } + public void generateFontAtlas(Identifier fontIdentifier, int[] glyphIds) { + Log.info("Generating font atlas for " + fontIdentifier); + this.fontAtlasTextures.put(fontIdentifier, new FontAtlas(ClientBase.getInstance().getFontRegistry().get(fontIdentifier), glyphIds)); + } + /** * @param texture The identifier for the texture you wish to retrieve from this cache * @return The requested texture or null (in the future this will return a default texture) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java index be356b9..625cac5 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/client/ui/Font.java @@ -10,6 +10,8 @@ import org.lwjgl.util.harfbuzz.hb_glyph_info_t; import org.lwjgl.util.harfbuzz.hb_glyph_position_t; +import java.util.stream.IntStream; + public class Font { private final int fontSize; //In pixels @@ -18,6 +20,8 @@ public class Font { private final FT_Face face; private final long font; + public static final int[] ISO_8859_1_GLYPH_IDS = IntStream.rangeClosed(32, 126).toArray(); + public Font(Resource resource, int fontSize) { this.fontSize = fontSize; @@ -120,4 +124,24 @@ public static class GlyphData { this.yOffset = yOffset; } } + + public int getFontSize() { + return fontSize; + } + + public long getFreeType() { + return freeType; + } + + public long getFacePointer() { + return facePointer; + } + + public FT_Face getFace() { + return face; + } + + public long getFont() { + return font; + } } diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java index 20ce590..7ea1aaf 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/UIRenderNode.java @@ -156,7 +156,9 @@ public void endContainer() { public boolean drawText(Identifier elementIdentifier, String text) { Element thisElement = elementRegistry.get(elementIdentifier); + Log.info("Drawing text " + text + " with element " + thisElement); Identifier fontIdentifier = thisElement.getStyle().getFontIdentifier(); + Log.info("Drawing text " + text + " with style " + thisElement.getStyle()); Font font = ClientBase.getInstance().getFontRegistry().get(fontIdentifier); Font.ShapedText shapedText = font.shapeText(text); diff --git a/src/main/java/com/terminalvelocitycabbage/engine/util/BufferUtils.java b/src/main/java/com/terminalvelocitycabbage/engine/util/BufferUtils.java new file mode 100644 index 0000000..f676a3e --- /dev/null +++ b/src/main/java/com/terminalvelocitycabbage/engine/util/BufferUtils.java @@ -0,0 +1,42 @@ +package com.terminalvelocitycabbage.engine.util; + +import org.joml.Vector2i; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +public class BufferUtils { + + /** + * Inserts the subimage into this atlas buffer + * @param atlas The atlas image buffer which this image is being added to + * @param subImage The image buffer to be inserted + * @param dest The location for the sub image buffer + */ + public static void insertSubImage(ByteBuffer atlas, int atlasW, int atlasH, ByteBuffer subImage, int subImageW, int subImageH, Vector2i dest) { + + long atlasBaseAddr = MemoryUtil.memAddress(atlas); + long subImageBaseAddr = MemoryUtil.memAddress(subImage); + + for (int row = 0; row < subImageH; row++) { + int destRow = dest.y() + row; + if (destRow >= atlasH || dest.x() + subImageW > atlasW) continue; + + long destOffset = ((long) destRow * atlasW + dest.x()) * 4; + long srcOffset = ((long) row * subImageW) * 4; + + MemoryUtil.memCopy( + subImageBaseAddr + srcOffset, + atlasBaseAddr + destOffset, + subImageW * 4L + ); + } + } + + public static Vector2i insertSubImage(ByteBuffer atlas, int atlasW, int atlasH, ByteBuffer subImage, int subImageW, int subImageH) { + Vector2i location = new Vector2i(); + insertSubImage(atlas, atlasW, atlasH, subImage, subImageW, subImageH, location); + return location; + } + +} diff --git a/src/main/java/com/terminalvelocitycabbage/engine/util/MathUtils.java b/src/main/java/com/terminalvelocitycabbage/engine/util/MathUtils.java index 89b02b2..cb0ad22 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/util/MathUtils.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/util/MathUtils.java @@ -63,4 +63,12 @@ public static Vector2i findMostSquareDimensions(int minArea) { return new Vector2i(bestWidth, bestHeight); } + public static int findNearestPowerOfTwo(int number) { + int powerOfTwo = 1; + while (powerOfTwo < number) { + powerOfTwo <<= 1; + } + return powerOfTwo; + } + } From e9b826b8e6807e49a9b68e45439bf1ead7ab1038 Mon Sep 17 00:00:00 2001 From: Brandon Davis Date: Sat, 23 Aug 2025 09:18:33 -0500 Subject: [PATCH 16/16] Patch up a couple merge bugs --- .../terminalvelocitycabbage/engine/graph/RenderNode.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java b/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java index 4ede70a..709a2c8 100644 --- a/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java +++ b/src/main/java/com/terminalvelocitycabbage/engine/graph/RenderNode.java @@ -64,14 +64,6 @@ public void recompileShaders() { recompileShaders = true; } - /** - * An optional init stage for rendergraph nodes - * @param renderGraph The rendergraph that this node belongs to - */ - public void init(RenderGraph renderGraph) { - - } - /** * @return The currently compiled shader program of this node */