From 5bb69c92eb71b159ce6329359c17a22f619fc15f Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Fri, 6 Mar 2026 11:11:24 +0100 Subject: [PATCH] Adding more implementations and fixes --- .../common/minecraft/IMinecraftClient.java | 12 ++ .../common/minecraft/MinecraftBinding.java | 9 ++ .../common/minecraft/MinecraftProvider.java | 9 +- .../minecraft/bindings/SaturnIdentifier.java | 14 ++ .../minecraft/bindings/SaturnQuaternionf.java | 9 ++ .../common/minecraft/render/IMatrixStack.java | 25 ++++ .../java/org/saturnclient/config/Config.java | 2 +- .../org/saturnclient/config/manager/Key.java | 97 +++++++++++++ .../java/org/saturnclient/ui/Element.java | 89 ++++++++++++ .../org/saturnclient/ui/ElementContext.java | 25 ++++ .../org/saturnclient/ui/ElementRenderer.java | 27 ++++ .../java/org/saturnclient/ui/RenderScope.java | 2 - src/main/java/org/saturnclient/ui/Utils.java | 63 +++++++++ .../org/saturnclient/ui/anim/Animation.java | 37 +++++ .../java/org/saturnclient/ui/anim/Curve.java | 22 +++ .../java/org/saturnclient/ui/anim/Fade.java | 27 ++++ .../org/saturnclient/ui/anim/FadeOut.java | 27 ++++ .../org/saturnclient/ui/anim/SlideFade.java | 39 ++++++ .../java/org/saturnclient/ui/anim/SlideX.java | 39 ++++++ .../java/org/saturnclient/ui/anim/SlideY.java | 39 ++++++ .../ui/elements/AnimationStagger.java | 66 +++++++++ .../org/saturnclient/ui/elements/Button.java | 35 +++++ .../ui/elements/ClickableTexture.java | 30 ++++ .../ui/elements/ImageTexture.java | 32 +++++ .../ui/elements/Notification.java | 58 ++++++++ .../org/saturnclient/ui/elements/Scroll.java | 130 ++++++++++++++++++ .../org/saturnclient/ui/elements/TabMenu.java | 63 +++++++++ .../org/saturnclient/ui/elements/Text.java | 22 +++ .../ui/elements/TextureButton.java | 37 +++++ .../org/saturnclient/ui/resources/Fonts.java | 45 ++++++ .../saturnclient/ui/resources/SvgTexture.java | 74 ++++++++++ 31 files changed, 1200 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/saturnclient/common/minecraft/IMinecraftClient.java create mode 100644 src/main/java/org/saturnclient/common/minecraft/bindings/SaturnQuaternionf.java create mode 100644 src/main/java/org/saturnclient/common/minecraft/render/IMatrixStack.java create mode 100644 src/main/java/org/saturnclient/ui/Element.java create mode 100644 src/main/java/org/saturnclient/ui/ElementContext.java create mode 100644 src/main/java/org/saturnclient/ui/ElementRenderer.java create mode 100644 src/main/java/org/saturnclient/ui/Utils.java create mode 100644 src/main/java/org/saturnclient/ui/anim/Animation.java create mode 100644 src/main/java/org/saturnclient/ui/anim/Curve.java create mode 100644 src/main/java/org/saturnclient/ui/anim/Fade.java create mode 100644 src/main/java/org/saturnclient/ui/anim/FadeOut.java create mode 100644 src/main/java/org/saturnclient/ui/anim/SlideFade.java create mode 100644 src/main/java/org/saturnclient/ui/anim/SlideX.java create mode 100644 src/main/java/org/saturnclient/ui/anim/SlideY.java create mode 100644 src/main/java/org/saturnclient/ui/elements/AnimationStagger.java create mode 100644 src/main/java/org/saturnclient/ui/elements/Button.java create mode 100644 src/main/java/org/saturnclient/ui/elements/ClickableTexture.java create mode 100644 src/main/java/org/saturnclient/ui/elements/ImageTexture.java create mode 100644 src/main/java/org/saturnclient/ui/elements/Notification.java create mode 100644 src/main/java/org/saturnclient/ui/elements/Scroll.java create mode 100644 src/main/java/org/saturnclient/ui/elements/TabMenu.java create mode 100644 src/main/java/org/saturnclient/ui/elements/Text.java create mode 100644 src/main/java/org/saturnclient/ui/elements/TextureButton.java create mode 100644 src/main/java/org/saturnclient/ui/resources/Fonts.java create mode 100644 src/main/java/org/saturnclient/ui/resources/SvgTexture.java diff --git a/src/main/java/org/saturnclient/common/minecraft/IMinecraftClient.java b/src/main/java/org/saturnclient/common/minecraft/IMinecraftClient.java new file mode 100644 index 0000000..b60d049 --- /dev/null +++ b/src/main/java/org/saturnclient/common/minecraft/IMinecraftClient.java @@ -0,0 +1,12 @@ +package org.saturnclient.common.minecraft; + +import java.io.File; +import java.io.InputStream; + +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; + +public interface IMinecraftClient { + public File getRunDirectory(); + + public InputStream getResource(SaturnIdentifier identifier); +} diff --git a/src/main/java/org/saturnclient/common/minecraft/MinecraftBinding.java b/src/main/java/org/saturnclient/common/minecraft/MinecraftBinding.java index 12f5798..7f96808 100644 --- a/src/main/java/org/saturnclient/common/minecraft/MinecraftBinding.java +++ b/src/main/java/org/saturnclient/common/minecraft/MinecraftBinding.java @@ -6,4 +6,13 @@ public class MinecraftBinding { public MinecraftBinding(Object binding) { this.inner = binding; } + + @SuppressWarnings("unchecked") + public T get() { + try { + return (T) inner; + } catch (ClassCastException e) { + return null; + } + } } diff --git a/src/main/java/org/saturnclient/common/minecraft/MinecraftProvider.java b/src/main/java/org/saturnclient/common/minecraft/MinecraftProvider.java index fa4bd7e..d2465f4 100644 --- a/src/main/java/org/saturnclient/common/minecraft/MinecraftProvider.java +++ b/src/main/java/org/saturnclient/common/minecraft/MinecraftProvider.java @@ -1,13 +1,18 @@ package org.saturnclient.common.minecraft; -import java.io.File; +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; +import java.awt.image.BufferedImage; public abstract class MinecraftProvider { public static MinecraftProvider PROVIDER; + public abstract IMinecraftClient getClient(); + public abstract Object createIdentifier(String namespace, String path); public abstract boolean isKeyPressed(int key); - public abstract File getRunDirectory(); + public abstract int getWidth(String text, int font); + + public abstract void registerBufferedImageTexture(SaturnIdentifier i, BufferedImage bi); } diff --git a/src/main/java/org/saturnclient/common/minecraft/bindings/SaturnIdentifier.java b/src/main/java/org/saturnclient/common/minecraft/bindings/SaturnIdentifier.java index 7bd08de..45971ef 100644 --- a/src/main/java/org/saturnclient/common/minecraft/bindings/SaturnIdentifier.java +++ b/src/main/java/org/saturnclient/common/minecraft/bindings/SaturnIdentifier.java @@ -7,4 +7,18 @@ public class SaturnIdentifier extends MinecraftBinding { public SaturnIdentifier(String namespace, String path) { super(MinecraftProvider.PROVIDER.createIdentifier(namespace, path)); } + + public static SaturnIdentifier of(String namespace, String path) { + return new SaturnIdentifier(namespace, path); + } + + public static SaturnIdentifier of(String fullPath) { + String[] p = fullPath.split(":", 2); + + return new SaturnIdentifier(p[0], p[1]); + } + + public static SaturnIdentifier ofVanilla(String path) { + return new SaturnIdentifier("minecraft", path); + } } diff --git a/src/main/java/org/saturnclient/common/minecraft/bindings/SaturnQuaternionf.java b/src/main/java/org/saturnclient/common/minecraft/bindings/SaturnQuaternionf.java new file mode 100644 index 0000000..deba303 --- /dev/null +++ b/src/main/java/org/saturnclient/common/minecraft/bindings/SaturnQuaternionf.java @@ -0,0 +1,9 @@ +package org.saturnclient.common.minecraft.bindings; + +import org.saturnclient.common.minecraft.MinecraftBinding; + +public class SaturnQuaternionf extends MinecraftBinding { + public SaturnQuaternionf(Object o) { + super(o); + } +} diff --git a/src/main/java/org/saturnclient/common/minecraft/render/IMatrixStack.java b/src/main/java/org/saturnclient/common/minecraft/render/IMatrixStack.java new file mode 100644 index 0000000..a588891 --- /dev/null +++ b/src/main/java/org/saturnclient/common/minecraft/render/IMatrixStack.java @@ -0,0 +1,25 @@ +package org.saturnclient.common.minecraft.render; + +import org.saturnclient.common.minecraft.bindings.SaturnQuaternionf; + +public interface IMatrixStack { + public void translate(double x, double y, double z); + + public void translate(float x, float y, float z); + + public void scale(float x, float y, float z); + + public void multiply(SaturnQuaternionf quaternion); + + public void multiply(SaturnQuaternionf quaternion, float originX, float originY, float originZ); + + public void push(); + + public void pop(); + + public Object peek(); + + public boolean isEmpty(); + + public void loadIdentity(); +} diff --git a/src/main/java/org/saturnclient/config/Config.java b/src/main/java/org/saturnclient/config/Config.java index 51e4391..d30852a 100644 --- a/src/main/java/org/saturnclient/config/Config.java +++ b/src/main/java/org/saturnclient/config/Config.java @@ -23,7 +23,7 @@ public class Config { } public static void init() { - File configFile = new File(MinecraftProvider.PROVIDER.getRunDirectory(), "saturn.json"); + File configFile = new File(MinecraftProvider.PROVIDER.getClient().getRunDirectory(), "saturn.json"); config = new ConfigManager(configFile, "Saturn Client"); config.property("Realistic logo", realisticLogo); diff --git a/src/main/java/org/saturnclient/config/manager/Key.java b/src/main/java/org/saturnclient/config/manager/Key.java index d6e7b81..ae413ef 100644 --- a/src/main/java/org/saturnclient/config/manager/Key.java +++ b/src/main/java/org/saturnclient/config/manager/Key.java @@ -52,4 +52,101 @@ public class Key { GLFW_KEY_GRAVE_ACCENT = 96, GLFW_KEY_WORLD_1 = 161, GLFW_KEY_WORLD_2 = 162; + + /** Function keys. */ + public static final int GLFW_KEY_ESCAPE = 256, + GLFW_KEY_ENTER = 257, + GLFW_KEY_TAB = 258, + GLFW_KEY_BACKSPACE = 259, + GLFW_KEY_INSERT = 260, + GLFW_KEY_DELETE = 261, + GLFW_KEY_RIGHT = 262, + GLFW_KEY_LEFT = 263, + GLFW_KEY_DOWN = 264, + GLFW_KEY_UP = 265, + GLFW_KEY_PAGE_UP = 266, + GLFW_KEY_PAGE_DOWN = 267, + GLFW_KEY_HOME = 268, + GLFW_KEY_END = 269, + GLFW_KEY_CAPS_LOCK = 280, + GLFW_KEY_SCROLL_LOCK = 281, + GLFW_KEY_NUM_LOCK = 282, + GLFW_KEY_PRINT_SCREEN = 283, + GLFW_KEY_PAUSE = 284, + GLFW_KEY_F1 = 290, + GLFW_KEY_F2 = 291, + GLFW_KEY_F3 = 292, + GLFW_KEY_F4 = 293, + GLFW_KEY_F5 = 294, + GLFW_KEY_F6 = 295, + GLFW_KEY_F7 = 296, + GLFW_KEY_F8 = 297, + GLFW_KEY_F9 = 298, + GLFW_KEY_F10 = 299, + GLFW_KEY_F11 = 300, + GLFW_KEY_F12 = 301, + GLFW_KEY_F13 = 302, + GLFW_KEY_F14 = 303, + GLFW_KEY_F15 = 304, + GLFW_KEY_F16 = 305, + GLFW_KEY_F17 = 306, + GLFW_KEY_F18 = 307, + GLFW_KEY_F19 = 308, + GLFW_KEY_F20 = 309, + GLFW_KEY_F21 = 310, + GLFW_KEY_F22 = 311, + GLFW_KEY_F23 = 312, + GLFW_KEY_F24 = 313, + GLFW_KEY_F25 = 314, + GLFW_KEY_KP_0 = 320, + GLFW_KEY_KP_1 = 321, + GLFW_KEY_KP_2 = 322, + GLFW_KEY_KP_3 = 323, + GLFW_KEY_KP_4 = 324, + GLFW_KEY_KP_5 = 325, + GLFW_KEY_KP_6 = 326, + GLFW_KEY_KP_7 = 327, + GLFW_KEY_KP_8 = 328, + GLFW_KEY_KP_9 = 329, + GLFW_KEY_KP_DECIMAL = 330, + GLFW_KEY_KP_DIVIDE = 331, + GLFW_KEY_KP_MULTIPLY = 332, + GLFW_KEY_KP_SUBTRACT = 333, + GLFW_KEY_KP_ADD = 334, + GLFW_KEY_KP_ENTER = 335, + GLFW_KEY_KP_EQUAL = 336, + GLFW_KEY_LEFT_SHIFT = 340, + GLFW_KEY_LEFT_CONTROL = 341, + GLFW_KEY_LEFT_ALT = 342, + GLFW_KEY_LEFT_SUPER = 343, + GLFW_KEY_RIGHT_SHIFT = 344, + GLFW_KEY_RIGHT_CONTROL = 345, + GLFW_KEY_RIGHT_ALT = 346, + GLFW_KEY_RIGHT_SUPER = 347, + GLFW_KEY_MENU = 348, + GLFW_KEY_LAST = GLFW_KEY_MENU; + + /** If this bit is set one or more Shift keys were held down. */ + public static final int GLFW_MOD_SHIFT = 0x1; + + /** If this bit is set one or more Control keys were held down. */ + public static final int GLFW_MOD_CONTROL = 0x2; + + /** If this bit is set one or more Alt keys were held down. */ + public static final int GLFW_MOD_ALT = 0x4; + + /** If this bit is set one or more Super keys were held down. */ + public static final int GLFW_MOD_SUPER = 0x8; + + /** + * If this bit is set the Caps Lock key is enabled and the + * {@link #GLFW_LOCK_KEY_MODS LOCK_KEY_MODS} input mode is set. + */ + public static final int GLFW_MOD_CAPS_LOCK = 0x10; + + /** + * If this bit is set the Num Lock key is enabled and the + * {@link #GLFW_LOCK_KEY_MODS LOCK_KEY_MODS} input mode is set. + */ + public static final int GLFW_MOD_NUM_LOCK = 0x20; } diff --git a/src/main/java/org/saturnclient/ui/Element.java b/src/main/java/org/saturnclient/ui/Element.java new file mode 100644 index 0000000..1b2f15b --- /dev/null +++ b/src/main/java/org/saturnclient/ui/Element.java @@ -0,0 +1,89 @@ +package org.saturnclient.ui; + +import org.saturnclient.ui.anim.Animation; + +public class Element { + public int x; + public int y; + public int width; + public int height; + public float opacity = 1.0f; + public boolean focused; + public Animation animation; + public float scale = 1.0f; + public Integer duration = null; // Element duration, will fade out if not null + + public void render(RenderScope renderScope, ElementContext ctx) { + } + + public void click(int mouseX, int mouseY) { + } + + public void scroll(int mouseX, int mouseY, double horizontalAmount, double verticalAmount) { + } + + public void keyPressed(int keyCode, int scanCode, int modifiers) { + } + + public void charTyped(char typedChar) { + } + + public void mouseClicked(double mouseX, double mouseY, int button) { + } + + public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + } + + public void mouseReleased(double mouseX, double mouseY, int button) { + } + + public void playAnimationFrame(long elapsed) { + if (this.animation != null && elapsed >= this.animation.delay) { + this.animation.tick(this.animation.curve + .apply(Math.min(1.0, (double) (elapsed - this.animation.delay) / this.animation.duration)), this); + } + } + + public Element dimensions(int width, int height) { + this.width = width; + this.height = height; + + return this; + } + + public Element position(int x, int y) { + this.x = x; + this.y = y; + + return this; + } + + public Element centerHorizontal(int w, int h, int offsetX, int offsetY) { + this.x = (w - (int) (width * scale)) / 2 + offsetX; + this.y = offsetY; + + return this; + } + + public Element center(int w, int h) { + this.centerOffset(w, h, 0, 0); + return this; + } + + public Element centerOffset(int w, int h, int offsetX, int offsetY) { + this.x = (w - (int) (width * scale)) / 2 + offsetX; + this.y = (h - (int) (height * scale)) / 2 + offsetY; + + return this; + } + + public Element animation(Animation animation) { + this.animation = animation; + return this; + } + + public Element scale(float scale) { + this.scale = scale; + return this; + } +} diff --git a/src/main/java/org/saturnclient/ui/ElementContext.java b/src/main/java/org/saturnclient/ui/ElementContext.java new file mode 100644 index 0000000..2077161 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/ElementContext.java @@ -0,0 +1,25 @@ +package org.saturnclient.ui; + +public class ElementContext { + public int mouseX, mouseY; + public int elementWidth, elementHeight; + public float elementScale; + public long elapsed; + + public ElementContext(long elapsed, int mouseX, int mouseY, Element e) { + this.mouseX = mouseX - e.x; + this.mouseY = mouseY - e.y; + this.elementWidth = e.width; + this.elementHeight = e.height; + this.elementScale = e.scale; + this.elapsed = elapsed; + } + + public boolean isHovering() { + return Utils.isHovering(mouseX, mouseY, elementWidth, elementHeight, elementScale); + } + + public boolean isHovering(int x, int y, int width, int height) { + return Utils.isHovering(mouseX - x, mouseY - y, width, height, elementScale); + } +} diff --git a/src/main/java/org/saturnclient/ui/ElementRenderer.java b/src/main/java/org/saturnclient/ui/ElementRenderer.java new file mode 100644 index 0000000..fb85aca --- /dev/null +++ b/src/main/java/org/saturnclient/ui/ElementRenderer.java @@ -0,0 +1,27 @@ +package org.saturnclient.ui; + +import java.util.List; + +public abstract class ElementRenderer { + public static ElementRenderer INSTANCE; + + public abstract void draw(List elements, Element element); + + public abstract void render(List elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY); + + public abstract void mouseClicked(List elements, double mouseX, double mouseY, int button); + + public abstract void mouseScrolled( + List elements, + double mouseX, + double mouseY, + double horizontalAmount, + double verticalAmount); + + public abstract void keyPressed(List elements, int keyCode, int scanCode, int modifiers); + + public abstract void mouseDragged(List elements, double mouseX, double mouseY, int button, double deltaX, + double deltaY); + + public abstract void mouseReleased(List elements, double mouseX, double mouseY, int button); +} diff --git a/src/main/java/org/saturnclient/ui/RenderScope.java b/src/main/java/org/saturnclient/ui/RenderScope.java index 2d76707..598a6ee 100644 --- a/src/main/java/org/saturnclient/ui/RenderScope.java +++ b/src/main/java/org/saturnclient/ui/RenderScope.java @@ -52,8 +52,6 @@ public interface RenderScope { public void drawItemWithoutEntity(SaturnItemStack stack, int x, int y, int seed); - public void drawItem(SaturnItemStack entity, SaturnItemStack stack, int x, int y, int seed); - public void drawSpriteStretched(SaturnSprite sprite, int x, int y, int width, int height); public void drawSpriteStretched(SaturnSprite sprite, int x, int y, int width, int height, int color); diff --git a/src/main/java/org/saturnclient/ui/Utils.java b/src/main/java/org/saturnclient/ui/Utils.java new file mode 100644 index 0000000..9f66b5e --- /dev/null +++ b/src/main/java/org/saturnclient/ui/Utils.java @@ -0,0 +1,63 @@ +package org.saturnclient.ui; + +import java.util.List; + +import org.saturnclient.config.manager.Key; + +public class Utils { + public static boolean isHovering(int mouseX, int mouseY, int elementWidth, int elementHeight, float elementScale) { + return mouseX >= 0 && + mouseX <= (elementWidth * elementScale) && + mouseY >= 0 && + mouseY <= (elementHeight * elementScale); + } + + public static char getCharFromKey(int keyCode, int modifiers) { + boolean shift = (modifiers & Key.GLFW_MOD_SHIFT) != 0; + + // A-Z + if (keyCode >= Key.GLFW_KEY_A && keyCode <= Key.GLFW_KEY_Z) { + return (char) (shift + ? 'A' + (keyCode - Key.GLFW_KEY_A) + : 'a' + (keyCode - Key.GLFW_KEY_A)); + } + + // 0-9 + if (keyCode >= Key.GLFW_KEY_0 && keyCode <= Key.GLFW_KEY_9) { + return shift + ? ")!@#$%^&*(".charAt(keyCode - Key.GLFW_KEY_0) + : (char) ('0' + (keyCode - Key.GLFW_KEY_0)); + } + + // Special characters + return switch (keyCode) { + case Key.GLFW_KEY_SPACE -> ' '; + case Key.GLFW_KEY_PERIOD -> shift ? '>' : '.'; + case Key.GLFW_KEY_COMMA -> shift ? '<' : ','; + case Key.GLFW_KEY_MINUS -> shift ? '_' : '-'; + case Key.GLFW_KEY_EQUAL -> shift ? '+' : '='; + case Key.GLFW_KEY_SEMICOLON -> shift ? ':' : ';'; + case Key.GLFW_KEY_APOSTROPHE -> shift ? '"' : '\''; + case Key.GLFW_KEY_SLASH -> shift ? '?' : '/'; + case Key.GLFW_KEY_BACKSLASH -> shift ? '|' : '\\'; + case Key.GLFW_KEY_LEFT_BRACKET -> shift ? '{' : '['; + case Key.GLFW_KEY_RIGHT_BRACKET -> shift ? '}' : ']'; + case Key.GLFW_KEY_GRAVE_ACCENT -> shift ? '~' : '`'; + default -> '\0'; + }; + } + + public static T getOrNull(List list, int index) { + return (index >= 0 && index < list.size()) ? list.get(index) : null; + } + + // public static void notify(NotificationKind kind, String title, String toast) + // { + // if (SaturnClient.client.currentScreen instanceof SaturnScreen) { + // ((SaturnScreen) SaturnClient.client.currentScreen) + // .draw(new Notification(SaturnClient.client.currentScreen.width, + // SaturnClient.client.currentScreen.height, kind, + // title, toast)); + // } + // } +} diff --git a/src/main/java/org/saturnclient/ui/anim/Animation.java b/src/main/java/org/saturnclient/ui/anim/Animation.java new file mode 100644 index 0000000..55982ac --- /dev/null +++ b/src/main/java/org/saturnclient/ui/anim/Animation.java @@ -0,0 +1,37 @@ +package org.saturnclient.ui.anim; + +import java.util.function.Function; + +import org.saturnclient.config.AnimationConfig; +import org.saturnclient.ui.Element; + +public abstract class Animation { + public int duration; + public int delay = 0; + public Function curve = Curve::easeInOutCubic; + + Animation(AnimationConfig config) { + this.duration = config.duration.value; + switch (config.curve.value) { + case 0: + this.curve = Curve::easeInOutCubic; + break; + case 1: + this.curve = Curve::easeOutCubic; + break; + case 2: + this.curve = Curve::easeInOutBack; + break; + default: + break; + } + } + + Animation(int duration) { + this.duration = duration; + } + + public abstract void tick(double progress, Element element); + + public abstract void init(Element element); +} diff --git a/src/main/java/org/saturnclient/ui/anim/Curve.java b/src/main/java/org/saturnclient/ui/anim/Curve.java new file mode 100644 index 0000000..e19d503 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/anim/Curve.java @@ -0,0 +1,22 @@ +package org.saturnclient.ui.anim; + +public class Curve { + public static double easeOutCubic(double x) { + return 1 - Math.pow(1 - x, 3); + } + + public static double easeInOutCubic(double x) { + return (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2); + } + + public static double easeInOutBack(double x) { + double c1 = 2.5; + double c2 = c1 * 1.525; + + if (x < 0.5) { + return (Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2; + } else { + return (Math.pow(2 * x - 2, 2) * ((c2 + 1) * (2 * x - 2) + c2) + 2) / 2; + } + } +} diff --git a/src/main/java/org/saturnclient/ui/anim/Fade.java b/src/main/java/org/saturnclient/ui/anim/Fade.java new file mode 100644 index 0000000..9a5ecef --- /dev/null +++ b/src/main/java/org/saturnclient/ui/anim/Fade.java @@ -0,0 +1,27 @@ +package org.saturnclient.ui.anim; + +import org.saturnclient.config.AnimationConfig; +import org.saturnclient.ui.Element; + +public class Fade extends Animation { + float targetOpacity; + + public Fade(int duration) { + super(duration); + } + + public Fade(AnimationConfig config) { + super(config); + } + + @Override + public void tick(double progress, Element element) { + element.opacity = Math.max(0.1f, targetOpacity * (float) progress); + } + + @Override + public void init(Element element) { + targetOpacity = element.opacity; + element.opacity = 0.1f; + } +} diff --git a/src/main/java/org/saturnclient/ui/anim/FadeOut.java b/src/main/java/org/saturnclient/ui/anim/FadeOut.java new file mode 100644 index 0000000..4172883 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/anim/FadeOut.java @@ -0,0 +1,27 @@ +package org.saturnclient.ui.anim; + +import org.saturnclient.config.AnimationConfig; +import org.saturnclient.ui.Element; + +public class FadeOut extends Animation { + float targetOpacity; + + public FadeOut(int duration) { + super(duration); + } + + public FadeOut(AnimationConfig config) { + super(config); + } + + @Override + public void tick(double progress, Element element) { + element.opacity = targetOpacity - (float) progress; + } + + @Override + public void init(Element element) { + targetOpacity = element.opacity; + // element.opacity = 1.0f; + } +} diff --git a/src/main/java/org/saturnclient/ui/anim/SlideFade.java b/src/main/java/org/saturnclient/ui/anim/SlideFade.java new file mode 100644 index 0000000..1318287 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/anim/SlideFade.java @@ -0,0 +1,39 @@ +package org.saturnclient.ui.anim; + +import org.saturnclient.config.AnimationConfig; +import org.saturnclient.ui.Element; + +public class SlideFade extends Animation { + public Fade fade; + public SlideY slideY; + + public SlideFade(int duration, int offset) { + super(duration); + this.fade = new Fade(duration); + this.slideY = new SlideY(duration, offset); + } + + public SlideFade(AnimationConfig config, int offset) { + super(config); + this.fade = new Fade(duration); + this.slideY = new SlideY(duration, offset); + } + + @Override + public void tick(double progress, Element element) { + fade.delay = this.delay; + fade.duration = this.duration; + + slideY.delay = this.delay; + slideY.duration = this.duration; + + fade.tick(progress, element); + slideY.tick(progress, element); + } + + @Override + public void init(Element element) { + fade.init(element); + slideY.init(element); + } +} diff --git a/src/main/java/org/saturnclient/ui/anim/SlideX.java b/src/main/java/org/saturnclient/ui/anim/SlideX.java new file mode 100644 index 0000000..82792ba --- /dev/null +++ b/src/main/java/org/saturnclient/ui/anim/SlideX.java @@ -0,0 +1,39 @@ +package org.saturnclient.ui.anim; + +import org.saturnclient.config.AnimationConfig; +import org.saturnclient.ui.Element; + +public class SlideX extends Animation { + int offset = 20; + double startX; + + public SlideX(AnimationConfig config) { + super(config); + } + + public SlideX(AnimationConfig config, int offset) { + super(config); + this.offset = offset; + } + + public SlideX(int duration) { + super(duration); + } + + public SlideX(int duration, int offset) { + super(duration); + this.offset = offset; + } + + @Override + public void init(Element element) { + startX = element.x - offset; + element.x = (int) startX; + } + + @Override + public void tick(double progress, Element element) { + double currentX = startX + offset * progress; + element.x = (int) currentX; + } +} diff --git a/src/main/java/org/saturnclient/ui/anim/SlideY.java b/src/main/java/org/saturnclient/ui/anim/SlideY.java new file mode 100644 index 0000000..0c0aa8a --- /dev/null +++ b/src/main/java/org/saturnclient/ui/anim/SlideY.java @@ -0,0 +1,39 @@ +package org.saturnclient.ui.anim; + +import org.saturnclient.config.AnimationConfig; +import org.saturnclient.ui.Element; + +public class SlideY extends Animation { + int offset = 20; + double startY; + + public SlideY(AnimationConfig config) { + super(config); + } + + public SlideY(AnimationConfig config, int offset) { + super(config); + this.offset = offset; + } + + public SlideY(int duration) { + super(duration); + } + + public SlideY(int duration, int offset) { + super(duration); + this.offset = offset; + } + + @Override + public void init(Element element) { + startY = element.y - offset; + element.y = (int) startY; + } + + @Override + public void tick(double progress, Element element) { + double currentY = startY + offset * progress; + element.y = (int) currentY; + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/AnimationStagger.java b/src/main/java/org/saturnclient/ui/elements/AnimationStagger.java new file mode 100644 index 0000000..3226e0f --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/AnimationStagger.java @@ -0,0 +1,66 @@ +package org.saturnclient.ui.elements; + +import java.util.ArrayList; +import java.util.List; + +import org.saturnclient.config.AnimationConfig; +import org.saturnclient.config.Config; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.ElementRenderer; +import org.saturnclient.ui.RenderScope; + +public class AnimationStagger extends Element { + protected List children = new ArrayList<>(); + public int delay; + + public AnimationStagger(int delay) { + this.delay = delay; + } + + public AnimationStagger(AnimationConfig config) { + this.delay = config.stagger.value; + } + + public void draw(Element element) { + if (!Config.stagger.value) { + element.animation = null; + } + + if (element.animation != null) { + element.animation.delay = delay * children.size(); + } + + ElementRenderer.INSTANCE.draw(children, element); + + this.height = Math.max(this.height, element.y + element.height); + this.width = Math.max(this.width, element.x + element.width); + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + ElementRenderer.INSTANCE.render(children, ctx.elapsed, renderScope, + ctx.mouseX, + ctx.mouseY); + } + + @Override + public void mouseClicked(double mouseX, double mouseY, int button) { + ElementRenderer.INSTANCE.mouseClicked(children, mouseX, mouseY, button); + } + + @Override + public void keyPressed(int keyCode, int scanCode, int modifiers) { + ElementRenderer.INSTANCE.keyPressed(children, keyCode, scanCode, modifiers); + } + + @Override + public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + ElementRenderer.INSTANCE.mouseDragged(children, mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public void mouseReleased(double mouseX, double mouseY, int button) { + ElementRenderer.INSTANCE.mouseReleased(children, mouseX, mouseY, button); + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/Button.java b/src/main/java/org/saturnclient/ui/elements/Button.java new file mode 100644 index 0000000..3c0d5ed --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/Button.java @@ -0,0 +1,35 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.config.Theme; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.resources.Fonts; + +public class Button extends Element { + private String text; + private Runnable onClick; + + public Button(String text, Runnable onClick) { + this.text = text; + this.onClick = onClick; + this.dimensions(Fonts.getWidth(text, Theme.FONT.value) + 50, 38); + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, + ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value); + + renderScope.drawText(text, Fonts.centerX(width, text, Theme.FONT.value), Fonts.centerY(height), + Theme.FONT.value, + ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value); + } + + @Override + public void click(int mouseX, int mouseY) { + if (onClick != null) { + onClick.run(); + } + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/ClickableTexture.java b/src/main/java/org/saturnclient/ui/elements/ClickableTexture.java new file mode 100644 index 0000000..1347b6d --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/ClickableTexture.java @@ -0,0 +1,30 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; +import org.saturnclient.config.Theme; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; + +public class ClickableTexture extends Element { + SaturnIdentifier sprite; + private Runnable onClick; + + public ClickableTexture(SaturnIdentifier sprite, Runnable onClick) { + this.sprite = sprite; + this.onClick = onClick; + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height, + ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value); + } + + @Override + public void click(int mouseX, int mouseY) { + if (onClick != null) { + onClick.run(); + } + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/ImageTexture.java b/src/main/java/org/saturnclient/ui/elements/ImageTexture.java new file mode 100644 index 0000000..442e723 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/ImageTexture.java @@ -0,0 +1,32 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.common.minecraft.IMinecraftClient; +import org.saturnclient.common.minecraft.MinecraftProvider; +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; + +public class ImageTexture extends Element { + SaturnIdentifier sprite; + + public ImageTexture(SaturnIdentifier sprite) { + this.sprite = sprite; + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + if (sprite.toString().endsWith(".svg")) { + IMinecraftClient client = MinecraftProvider.PROVIDER.getClient(); + + // Get the actual window pixel dimensions for the image + int renderWidth = (int) (width * client.getWindow().getFramebufferWidth() / client.getWindow().getWidth()); + int renderHeight = (int) (height * client.getWindow().getFramebufferHeight() + / client.getWindow().getHeight()); + + sprite = SvgTexture.getSvg(client, sprite, renderWidth * 2, renderHeight * 2); + } + + renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height); + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/Notification.java b/src/main/java/org/saturnclient/ui/elements/Notification.java new file mode 100644 index 0000000..110ff3c --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/Notification.java @@ -0,0 +1,58 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.config.Theme; +import org.saturnclient.config.manager.Property; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.anim.SlideX; +import org.saturnclient.ui.resources.Fonts; +import org.saturnclient.ui.resources.Textures; + +public class Notification extends Element { + private static Property error = Property.color(0xFFbf212f); + private static Property info = Property.color(0xFF264b96); + private static Property success = Property.color(0xFF27b376); + + public static enum NotificationKind { + Error, + Info, + Success, + } + + NotificationKind kind; + String text; + String title; + + public Notification(int width, int height, NotificationKind kind, String title, String text) { + this.kind = kind; + this.text = text; + this.title = title; + this.width = (int) Math.max(Fonts.getWidth(text, 1) * 0.6f, Fonts.getWidth(title, 1)) + 20; + this.height = 50; + this.x = width - this.width - 15; + this.y = height - this.height - 15; + this.animation = new SlideX(700, -width); + this.duration = 3000; + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawRoundedRectangle(0, 0, width, height, 10, Theme.PRIMARY.value); + switch (kind) { + case Error: + renderScope.drawTexture(Textures.CIRCLE_X, 9, 9, 0, 0, 15, 15, error.value); + break; + + case Info: + renderScope.drawTexture(Textures.INFO, 9, 9, 0, 0, 15, 15, info.value); + break; + + case Success: + renderScope.drawTexture(Textures.CHECK, 9, 9, 0, 0, 15, 15, success.value); + break; + } + renderScope.drawText(0.7f, title, 32, 10, 1, -1); + renderScope.drawText(0.5f, text, 32, 28, 1, -1); + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/Scroll.java b/src/main/java/org/saturnclient/ui/elements/Scroll.java new file mode 100644 index 0000000..434ea39 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/Scroll.java @@ -0,0 +1,130 @@ +package org.saturnclient.ui.elements; + +import java.util.ArrayList; +import java.util.List; + +import org.saturnclient.config.Theme; +import org.saturnclient.config.manager.Property; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; + +public class Scroll extends Element { + private static Property scrollBarRadius = Property.integer(10); + private static Property scrollBarWidth = Property.integer(5); + private static Property scrollBarPadding = Property.integer(5); + + int padding = 0; + + protected List children = new ArrayList<>(); + int scroll = 0; + int maxScroll = 0; + + public Scroll(int padding) { + this.padding = padding; + } + + public void draw(Element element) { + ElementRenderer.draw(children, element); + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + calculateMaxScroll(); + + renderScope.drawRoundedRectangle(0, 0, width, height, + Theme.BG_RADIUS.value, Theme.BACKGROUND.value); + + renderScope.enableScissor(padding, padding, width - padding, height - padding); + renderScope.matrices.push(); + renderScope.matrices.translate(padding, -scroll + padding, 0); + + ElementRenderer.render(children, ctx.elapsed, renderScope, + ctx.mouseX - padding, + ctx.mouseY - padding + scroll); + + renderScope.matrices.pop(); + renderScope.disableScissor(); + + if (maxScroll > 0) { + renderScope.drawRoundedRectangle( + width - scrollBarWidth.value - scrollBarPadding.value, + calculateScrollBarY(), + scrollBarWidth.value, + calculateScrollBarHeight(), + scrollBarRadius.value, + Theme.SCROLL.value); + } + } + + @Override + public void scroll(int mouseX, int mouseY, double horizontalAmount, double verticalAmount) { + scroll -= verticalAmount; + if (scroll < 0) { + scroll = 0; + } else if (scroll > maxScroll) { + scroll = maxScroll; + } + } + + @Override + public Element dimensions(int width, int height) { + super.dimensions(width, height); + + maxScroll = 0; + for (Element element : children) { + maxScroll = Math.max(maxScroll, (element.y + element.height + padding) - (height - padding * 2)); + } + + return this; + } + + int calculateScrollBarHeight() { + if (maxScroll <= 0) + return height - (scrollBarPadding.value * 2); + return Math.max(20, (height * height) / (height + maxScroll + 10)) - (scrollBarPadding.value * 2); + } + + int calculateScrollBarY() { + int scrollBarHeight = calculateScrollBarHeight(); + if (maxScroll <= 0) + return scrollBarPadding.value; + int y = (scroll * (height - scrollBarHeight - scrollBarPadding.value * 2)) / maxScroll + scrollBarPadding.value; + return Math.min(y, height - scrollBarHeight - scrollBarPadding.value); + } + + private void calculateMaxScroll() { + maxScroll = 0; + + for (Element element : children) { + int bottom = element.y + element.height; + maxScroll = Math.max(maxScroll, bottom - height + padding); + } + + if (maxScroll > 0) { + maxScroll = maxScroll + 10; + } else { + maxScroll = 0; + } + } + + @Override + public void keyPressed(int keyCode, int scanCode, int modifiers) { + ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers); + } + + @Override + public void mouseClicked(double mouseX, double mouseY, int button) { + ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, button); + } + + @Override + public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + ElementRenderer.mouseDragged(children, mouseX - padding, mouseY - padding + scroll, button, deltaX, deltaY); + } + + @Override + public void mouseReleased(double mouseX, double mouseY, int button) { + ElementRenderer.mouseReleased(children, mouseX - padding, mouseY - padding + scroll, button); + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/TabMenu.java b/src/main/java/org/saturnclient/ui/elements/TabMenu.java new file mode 100644 index 0000000..b87040a --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/TabMenu.java @@ -0,0 +1,63 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; +import org.saturnclient.config.Theme; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.Utils; + +public class TabMenu extends Element { + public static class TabMenuComponent { + Runnable onClick; + SaturnIdentifier sprite; + + public TabMenuComponent(SaturnIdentifier sprite, Runnable onClick) { + this.onClick = onClick; + this.sprite = sprite; + } + } + + TabMenuComponent[] components; + public int selectedScreen = 0; + + public TabMenu(int active, TabMenuComponent... components) { + this.components = components; + width = 25 * components.length + 6; + height = 30; + this.selectedScreen = active; + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Theme.PRIMARY.value); + + int col = 0; + for (TabMenuComponent component : components) { + int x = 25 * col + 5; + + boolean hovering = ctx.isHovering(x, 5, 20, 20) || selectedScreen == col; + + if (hovering) { + renderScope.drawRoundedRectangle(x, 5, 20, 20, Theme.WIDGET_RADIUS.value, Theme.ACCENT.value); + } + + renderScope.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10, + hovering ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value); + + col++; + } + } + + @Override + public void click(int mouseX, int mouseY) { + int col = 0; + for (TabMenuComponent component : components) { + int x = 25 * col + 5; + if (Utils.isHovering(mouseX - x, mouseY - 5, 20, 20, 1.0f)) { + component.onClick.run(); + } + col++; + } + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/Text.java b/src/main/java/org/saturnclient/ui/elements/Text.java new file mode 100644 index 0000000..7de1737 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/Text.java @@ -0,0 +1,22 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.config.Theme; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.resources.Fonts; + +public class Text extends Element { + private String text; + + public Text(String text) { + this.text = text; + width = Fonts.getWidth(text, Theme.FONT.value); + height = Fonts.getHeight(); + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawText(this.text, 0, 0, Theme.FONT.value, Theme.FOREGROUND.value); + } +} diff --git a/src/main/java/org/saturnclient/ui/elements/TextureButton.java b/src/main/java/org/saturnclient/ui/elements/TextureButton.java new file mode 100644 index 0000000..51ee39f --- /dev/null +++ b/src/main/java/org/saturnclient/ui/elements/TextureButton.java @@ -0,0 +1,37 @@ +package org.saturnclient.ui.elements; + +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; +import org.saturnclient.config.Theme; +import org.saturnclient.ui.Element; +import org.saturnclient.ui.ElementContext; +import org.saturnclient.ui.RenderScope; + +public class TextureButton extends Element { + SaturnIdentifier sprite; + private Runnable onClick; + static int padding = 14; + + public TextureButton(SaturnIdentifier sprite, Runnable onClick) { + this.sprite = sprite; + this.onClick = onClick; + } + + @Override + public void render(RenderScope renderScope, ElementContext ctx) { + renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, + ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value); + + int texWidth = width - padding; + int texHeight = height - padding; + + renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight, + ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value); + } + + @Override + public void click(int mouseX, int mouseY) { + if (onClick != null) { + onClick.run(); + } + } +} diff --git a/src/main/java/org/saturnclient/ui/resources/Fonts.java b/src/main/java/org/saturnclient/ui/resources/Fonts.java new file mode 100644 index 0000000..3e935b6 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/resources/Fonts.java @@ -0,0 +1,45 @@ +package org.saturnclient.ui.resources; + +import org.saturnclient.common.minecraft.MinecraftProvider; +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; + +public class Fonts { + public static final SaturnIdentifier INTER = SaturnIdentifier.of("saturnclient", "inter"); + public static final SaturnIdentifier INTER_BOLD = SaturnIdentifier.of("saturnclient", "inter_bold"); + public static final SaturnIdentifier DEFAULT = SaturnIdentifier.ofVanilla("default"); + + public static SaturnIdentifier getFont(int font) { + switch (font) { + case 0: + return DEFAULT; + case 1: + return INTER; + default: + return INTER_BOLD; + } + } + + public static int getWidth(String text, int font) { + int w = 0; + for (String line : text.split("\n")) { + w = Math.max(w, MinecraftProvider.PROVIDER.getWidth(line, font)); + } + if (font == 0) { + return w * 2; + } else { + return w; + } + } + + public static int getHeight() { + return 18; + } + + public static int centerX(int w, String text, int font) { + return (w - Fonts.getWidth(text, font)) / 2; + } + + public static int centerY(int h) { + return (h - 18) / 2; + } +} diff --git a/src/main/java/org/saturnclient/ui/resources/SvgTexture.java b/src/main/java/org/saturnclient/ui/resources/SvgTexture.java new file mode 100644 index 0000000..c4f4ea6 --- /dev/null +++ b/src/main/java/org/saturnclient/ui/resources/SvgTexture.java @@ -0,0 +1,74 @@ +package org.saturnclient.ui.resources; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import com.kitfox.svg.SVGDiagram; +import com.kitfox.svg.SVGUniverse; + +import org.saturnclient.common.minecraft.IMinecraftClient; +import org.saturnclient.common.minecraft.MinecraftProvider; +import org.saturnclient.common.minecraft.bindings.SaturnIdentifier; + +public class SvgTexture { + + private static List svgCache = new ArrayList<>(); + + private static BufferedImage renderSvg(InputStream svgStream, int width, int height) throws Exception { + // Load SVG + SVGUniverse universe = new SVGUniverse(); + java.net.URI uri = universe.loadSVG(svgStream, "tempSvg"); + SVGDiagram diagram = universe.getDiagram(uri); + + // Get intrinsic size of SVG + float svgWidth = (float) diagram.getWidth(); + float svgHeight = (float) diagram.getHeight(); + + // Compute scale factors + float scaleX = width / svgWidth; + float scaleY = height / svgHeight; + + // Create output image + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + + // Enable anti-aliasing for smooth scaling + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + + // Apply scaling transform + g.scale(scaleX, scaleY); + + // Render SVG + diagram.render(g); + + g.dispose(); + return image; + } + + public static SaturnIdentifier getSvg(IMinecraftClient client, SaturnIdentifier svgImage, int width, int height) { + SaturnIdentifier id = SaturnIdentifier + .of(svgImage.toString().replaceAll("\\.svg$", (width + "_" + height).toString() + ".png")); + + if (svgCache.contains(id)) { + return id; + } + + try (InputStream svgStream = client.getResource(svgImage)) { + + BufferedImage image = renderSvg(svgStream, width, height); + + MinecraftProvider.PROVIDER.registerBufferedImageTexture(id, image); + + return id; + + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file