chore(history): squash 5 commit(s) from 2026-09-07
- refactor: Phase 8 pass 2 — Renderer2D split + Triangulator decomp - test: add TriangulatorSolverTest (11 tests) - solve null/empty/single/two throws - perpendicular intersection - distanceToLine - formatPercent, formatThrowPoint, formatCoordinateLine (overworld/nether) - formatCopyCoordinates - fix: Triangulator - replace static Minecraft field with lazy access - fix: Coordinates HUD - replace static Minecraft field with lazy access - fix: replace static Minecraft fields with lazy access in HUD elements (part 2)
This commit is contained in:
parent
d83f9a29ff
commit
e1d7d19a83
48 changed files with 2915 additions and 2681 deletions
|
|
@ -6,6 +6,9 @@
|
|||
- **KISS / DRY / SOLID** — простая структура, без оверинжиниринга
|
||||
- **Макс. 200 строк на файл** (было >1500) — превышение = разбить на модули
|
||||
- **Макс. 4 файла на папку** — больше = новая подпапка по смыслу
|
||||
- **Публичный API можно менять свободно** (решение владельца 2026-09-05):
|
||||
KISS важнее обратной совместимости; при рефакторинге разрешено менять
|
||||
сигнатуры, удалять делегирование и мёртвый код, обновляя вызывающих
|
||||
- **Никакого статика** — `Minecraft.getInstance()` только лениво, никогда в поле
|
||||
- **Никаких this-escape** — конструктор не публикует `this`
|
||||
- **Никаких мёртвых миксинов** — миксин только если реально нужен; `cancellable=true` только при реальном cancel
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ public final class Coordinates extends DraggableHudElement implements Scriptable
|
|||
private static final String EFFECT_BLUR = "Blur";
|
||||
private static final String EFFECT_GLASS = "Glass";
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final EnumValue<NetherMode> netherMode =
|
||||
enumSetting("xyz_nether", NetherMode.ON, NetherMode.OFF, NetherMode.ON, NetherMode.ONLY_NETHER);
|
||||
|
|
@ -124,6 +123,7 @@ public final class Coordinates extends DraggableHudElement implements Scriptable
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean preview = DraggableHudElementRegistry.isForceVisible();
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || (mc.player == null && !preview)) {
|
||||
width = 0f;
|
||||
height = 0f;
|
||||
|
|
@ -137,10 +137,10 @@ public final class Coordinates extends DraggableHudElement implements Scriptable
|
|||
return;
|
||||
}
|
||||
|
||||
boolean inNether = mc != null && mc.level != null && mc.level.dimension() == Level.NETHER;
|
||||
double rawX = mc != null && mc.player != null ? mc.player.getX() : 0.0;
|
||||
double rawY = mc != null && mc.player != null ? mc.player.getY() : 0.0;
|
||||
double rawZ = mc != null && mc.player != null ? mc.player.getZ() : 0.0;
|
||||
boolean inNether = mc.level != null && mc.level.dimension() == Level.NETHER;
|
||||
double rawX = mc.player != null ? mc.player.getX() : 0.0;
|
||||
double rawY = mc.player != null ? mc.player.getY() : 0.0;
|
||||
double rawZ = mc.player != null ? mc.player.getZ() : 0.0;
|
||||
int px = Mth.floor(rawX);
|
||||
int py = Mth.floor(rawY);
|
||||
int pz = Mth.floor(rawZ);
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ public final class Fps extends DraggableHudElement implements ScriptableHudStatW
|
|||
private static final long DIGIT_ANIMATION_DURATION_MS = 200L;
|
||||
private static final float DIGIT_ANIMATION_OFFSET = 8.0f;
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final NumberValue<Double> scale = num("fps_scale", 2.37, 0.5, 5.0);
|
||||
private final ModeValue colorMode = mode("fps_color_mode", "color_mode", "Theme", new String[]{COLOR_THEME, COLOR_CUSTOM});
|
||||
|
|
@ -129,7 +128,7 @@ public final class Fps extends DraggableHudElement implements ScriptableHudStatW
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean preview = DraggableHudElementRegistry.isForceVisible();
|
||||
if (mc == null && !preview) {
|
||||
if (Minecraft.getInstance() == null && !preview) {
|
||||
width = 0f;
|
||||
height = 0f;
|
||||
scriptModel.setVisible(false);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ public final class GameTime extends DraggableHudElement implements ScriptableHud
|
|||
private static final int ICON_THUNDER_SNOW_DAY = 0xEA02;
|
||||
private static final int ICON_THUNDER_SNOW_NIGHT = 0xEA0A;
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final BooleanValue showWeather = bool("game_time_weather", "weather", true);
|
||||
private final NumberValue<Double> scale = num("game_time_scale", 2.37, 0.5, 5.0);
|
||||
|
|
@ -143,6 +142,7 @@ public final class GameTime extends DraggableHudElement implements ScriptableHud
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean preview = DraggableHudElementRegistry.isForceVisible();
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if ((mc == null || mc.level == null) && !preview) {
|
||||
width = 0f;
|
||||
height = 0f;
|
||||
|
|
@ -235,6 +235,7 @@ public final class GameTime extends DraggableHudElement implements ScriptableHud
|
|||
|
||||
private int resolveIcon(long time) {
|
||||
if (!showWeather.get()) return resolveClearIcon(time);
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || mc.level == null || mc.player == null) return resolveClearIcon(time);
|
||||
|
||||
boolean thunder = WorldTweaks.isServerThundering(mc.level);
|
||||
|
|
@ -258,6 +259,7 @@ public final class GameTime extends DraggableHudElement implements ScriptableHud
|
|||
}
|
||||
|
||||
private boolean isSnowingAtPlayer() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || mc.level == null || mc.player == null) return false;
|
||||
BlockPos pos = mc.player.blockPosition();
|
||||
Biome.Precipitation precipitation = mc.level.getBiome(pos).value()
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ public final class Keybinds extends DraggableHudElement {
|
|||
private static final String MODE_PRESS = "Press";
|
||||
private static final String MODE_HOLD = "Hold";
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final ScriptedListHudPanel scriptedPanel = new ScriptedListHudPanel();
|
||||
private final NumberValue<Double> scaleValue =
|
||||
|
|
@ -237,6 +236,7 @@ public final class Keybinds extends DraggableHudElement {
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean forceVisible = DraggableHudElementRegistry.isForceVisible();
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || (mc.player == null && !forceVisible)) {
|
||||
width = 0.0f;
|
||||
height = 0.0f;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ public final class Memory extends DraggableHudElement implements ScriptableHudSt
|
|||
private static final String EFFECT_BLUR = "Blur";
|
||||
private static final String EFFECT_GLASS = "Glass";
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final NumberValue<Double> scale = num("memory_scale", 2.37, 0.5, 5.0);
|
||||
private final ModeValue colorMode = mode("memory_color_mode", "color_mode", "Theme", new String[]{COLOR_THEME, COLOR_CUSTOM});
|
||||
|
|
@ -127,7 +126,7 @@ public final class Memory extends DraggableHudElement implements ScriptableHudSt
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean preview = DraggableHudElementRegistry.isForceVisible();
|
||||
if (mc == null && !preview) {
|
||||
if (Minecraft.getInstance() == null && !preview) {
|
||||
width = 0f;
|
||||
height = 0f;
|
||||
scriptModel.setVisible(false);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ public final class ModuleList extends DraggableHudElement implements ConfigValue
|
|||
private static final float BASE_RECT_EXTRA = 1f;
|
||||
private static final float BASE_RADIUS = 2f;
|
||||
private static final float BASE_SOFTNESS = 0.55f;
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final NumberValue<Double> scaleValue =
|
||||
new NumberValue<>("module_list_scale", 3.67, 0.5, 5.0);
|
||||
|
|
@ -140,6 +139,7 @@ public final class ModuleList extends DraggableHudElement implements ConfigValue
|
|||
float tickDelta,
|
||||
int screenW,
|
||||
int screenH) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!isEnabled() || mc == null || mc.player == null) {
|
||||
rows.clear();
|
||||
width = 0f;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ public final class Ping extends DraggableHudElement implements ScriptableHudStat
|
|||
private static final String EFFECT_BLUR = "Blur";
|
||||
private static final String EFFECT_GLASS = "Glass";
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final NumberValue<Double> scale = num("ping_scale", 2.58, 0.5, 5.0);
|
||||
private final ModeValue colorMode = mode("ping_color_mode", "color_mode", "Theme", new String[]{COLOR_THEME, COLOR_CUSTOM});
|
||||
|
|
@ -118,7 +117,7 @@ public final class Ping extends DraggableHudElement implements ScriptableHudStat
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean preview = DraggableHudElementRegistry.isForceVisible();
|
||||
if (mc == null && !preview) {
|
||||
if (Minecraft.getInstance() == null && !preview) {
|
||||
width = 0f;
|
||||
height = 0f;
|
||||
scriptModel.setVisible(false);
|
||||
|
|
@ -131,7 +130,7 @@ public final class Ping extends DraggableHudElement implements ScriptableHudStat
|
|||
return;
|
||||
}
|
||||
|
||||
int ping = NetworkStatsUtil.getPing(mc);
|
||||
int ping = NetworkStatsUtil.getPing(Minecraft.getInstance());
|
||||
String value = getPingValue(ping);
|
||||
String unit = "ms";
|
||||
updatePalette();
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ public final class Potions extends DraggableHudElement {
|
|||
Identifier.fromNamespaceAndPath("minecraft", "textures/mob_effect/night_vision.png")
|
||||
};
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final ScriptedListHudPanel scriptedPanel = new ScriptedListHudPanel();
|
||||
private final NumberValue<Double> scaleValue =
|
||||
|
|
@ -182,6 +181,7 @@ public final class Potions extends DraggableHudElement {
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean forceVisible = DraggableHudElementRegistry.isForceVisible();
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || (mc.player == null && !forceVisible)) {
|
||||
width = 0.0f;
|
||||
height = 0.0f;
|
||||
|
|
@ -434,6 +434,7 @@ public final class Potions extends DraggableHudElement {
|
|||
|
||||
private List<Row> collectRows() {
|
||||
List<Row> rows = new ArrayList<>();
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null && mc.player != null) {
|
||||
mc.player.getActiveEffects().stream()
|
||||
.sorted((a, b) -> String.CASE_INSENSITIVE_ORDER.compare(
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import net.minecraft.resources.Identifier;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class ScriptedTriangulatorHudPanel {
|
||||
public final class ScriptedTriangulatorHudPanel {
|
||||
private static final String LAYOUT_ID = "lovisual:api/hud/draggable/triangulator";
|
||||
private final UiScriptModuleHandle moduleHandle = HudScriptLayouts.handle(LAYOUT_ID);
|
||||
private final CachedUiScriptRuntime runtime = new CachedUiScriptRuntime(HudScriptLayouts.runtimeReporter());
|
||||
|
|
@ -33,7 +33,7 @@ final class ScriptedTriangulatorHudPanel {
|
|||
return String.format("#%08X", argb);
|
||||
}
|
||||
|
||||
static LinkedHashMap<String, Object> row(String key,
|
||||
public static LinkedHashMap<String, Object> row(String key,
|
||||
String label,
|
||||
String value,
|
||||
int labelColor,
|
||||
|
|
@ -53,15 +53,15 @@ final class ScriptedTriangulatorHudPanel {
|
|||
return row;
|
||||
}
|
||||
|
||||
static String idString(Identifier id) {
|
||||
public static String idString(Identifier id) {
|
||||
return id != null ? id.toString() : "";
|
||||
}
|
||||
|
||||
static List<LinkedHashMap<String, Object>> rows() {
|
||||
public static List<LinkedHashMap<String, Object>> rows() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
boolean render(Renderer2D renderer,
|
||||
public boolean render(Renderer2D renderer,
|
||||
TextRenderer textRenderer,
|
||||
GuiGraphicsExtractor ctx,
|
||||
float tickDelta,
|
||||
|
|
@ -130,7 +130,7 @@ final class ScriptedTriangulatorHudPanel {
|
|||
}
|
||||
patch.put(prop, value);
|
||||
}
|
||||
record Palette(int headerLeft,
|
||||
public record Palette(int headerLeft,
|
||||
int headerRight,
|
||||
int bodyLeft,
|
||||
int bodyRight,
|
||||
|
|
@ -164,7 +164,7 @@ final class ScriptedTriangulatorHudPanel {
|
|||
}
|
||||
}
|
||||
|
||||
record Panel(Palette palette,
|
||||
public record Panel(Palette palette,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
|
|
@ -191,7 +191,7 @@ final class ScriptedTriangulatorHudPanel {
|
|||
String confidenceText,
|
||||
int statusColor,
|
||||
List<LinkedHashMap<String, Object>> rows) {
|
||||
Panel(Palette palette,
|
||||
public Panel(Palette palette,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ public final class SystemTime extends DraggableHudElement implements ScriptableH
|
|||
iconString(0xEA13)
|
||||
};
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final BooleanValue showSeconds = bool("system_time_seconds", false);
|
||||
private final NumberValue<Double> scale = num("system_time_scale", 2.37, 0.5, 5.0);
|
||||
|
|
@ -143,7 +142,7 @@ public final class SystemTime extends DraggableHudElement implements ScriptableH
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean preview = DraggableHudElementRegistry.isForceVisible();
|
||||
if (mc == null && !preview) {
|
||||
if (Minecraft.getInstance() == null && !preview) {
|
||||
width = 0f;
|
||||
height = 0f;
|
||||
scriptModel.setVisible(false);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ public final class Tps extends DraggableHudElement implements ScriptableHudStatW
|
|||
private static final String EFFECT_BLUR = "Blur";
|
||||
private static final String EFFECT_GLASS = "Glass";
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final HudGlobalConfig hud = HudGlobalConfig.get();
|
||||
private final BooleanValue extra = bool("tps_extra", false);
|
||||
private final NumberValue<Double> scale = num("tps_scale", 2.37, 0.5, 5.0);
|
||||
|
|
@ -149,7 +148,7 @@ public final class Tps extends DraggableHudElement implements ScriptableHudStatW
|
|||
int screenW,
|
||||
int screenH) {
|
||||
boolean preview = DraggableHudElementRegistry.isForceVisible();
|
||||
if (mc == null && !preview) {
|
||||
if (Minecraft.getInstance() == null && !preview) {
|
||||
width = 0f;
|
||||
height = 0f;
|
||||
scriptModel.setVisible(false);
|
||||
|
|
@ -162,6 +161,7 @@ public final class Tps extends DraggableHudElement implements ScriptableHudStatW
|
|||
return;
|
||||
}
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
float tps = NetworkStatsUtil.getTps(mc);
|
||||
boolean showExtra = extra.get();
|
||||
float clientTps = showExtra ? NetworkStatsUtil.getClientTps(mc) : 0f;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.projectile.EyeOfEnder;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Rolling observation window for one thrown eye of ender plus the
|
||||
* quality-scored capture computed once its trajectory stabilizes.
|
||||
*/
|
||||
public final class TrackedEye {
|
||||
private static final double MIN_HORIZONTAL_SPEED = 0.03;
|
||||
private static final int MAX_OBSERVATIONS = 14;
|
||||
|
||||
private final List<Observation> observations = new ArrayList<>();
|
||||
private long lastSeenMs;
|
||||
private boolean captured;
|
||||
|
||||
TrackedEye(int entityId) {
|
||||
}
|
||||
|
||||
public void observe(EyeOfEnder eye, long now) {
|
||||
if (eye == null) {
|
||||
return;
|
||||
}
|
||||
Vec3 velocity = eye.getDeltaMovement();
|
||||
double speed = Math.sqrt(velocity.x * velocity.x + velocity.z * velocity.z);
|
||||
if (speed < MIN_HORIZONTAL_SPEED) {
|
||||
lastSeenMs = now;
|
||||
return;
|
||||
}
|
||||
observations.add(new Observation(eye.getX(), eye.getZ(), velocity.x / speed, velocity.z / speed, speed));
|
||||
if (observations.size() > MAX_OBSERVATIONS) {
|
||||
observations.remove(0);
|
||||
}
|
||||
lastSeenMs = now;
|
||||
}
|
||||
|
||||
public TrackedCapture tryCapture(int minTicks, double stabilityThreshold, double deviationLimit) {
|
||||
if (captured || observations.size() < Math.max(2, minTicks)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double dirX = 0.0;
|
||||
double dirZ = 0.0;
|
||||
double avgSpeed = 0.0;
|
||||
for (Observation observation : observations) {
|
||||
dirX += observation.dirX();
|
||||
dirZ += observation.dirZ();
|
||||
avgSpeed += observation.speed();
|
||||
}
|
||||
avgSpeed /= observations.size();
|
||||
|
||||
double dirLen = Math.sqrt(dirX * dirX + dirZ * dirZ);
|
||||
if (dirLen <= 1.0e-6) {
|
||||
return null;
|
||||
}
|
||||
dirX /= dirLen;
|
||||
dirZ /= dirLen;
|
||||
|
||||
double centroidX = 0.0;
|
||||
double centroidZ = 0.0;
|
||||
double avgCos = 0.0;
|
||||
double minCos = 1.0;
|
||||
for (Observation observation : observations) {
|
||||
centroidX += observation.x();
|
||||
centroidZ += observation.z();
|
||||
double dot = Mth.clamp(observation.dirX() * dirX + observation.dirZ() * dirZ, -1.0, 1.0);
|
||||
avgCos += dot;
|
||||
minCos = Math.min(minCos, dot);
|
||||
}
|
||||
centroidX /= observations.size();
|
||||
centroidZ /= observations.size();
|
||||
avgCos /= observations.size();
|
||||
|
||||
double normalX = -dirZ;
|
||||
double normalZ = dirX;
|
||||
double deviation = 0.0;
|
||||
for (Observation observation : observations) {
|
||||
deviation += Math.abs((observation.x() - centroidX) * normalX + (observation.z() - centroidZ) * normalZ);
|
||||
}
|
||||
deviation /= observations.size();
|
||||
|
||||
if (avgCos < stabilityThreshold || deviation > deviationLimit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double stabilityScore = Mth.clamp(
|
||||
(avgCos - stabilityThreshold) / Math.max(1.0e-5, 1.0 - stabilityThreshold),
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
double minCosScore = Mth.clamp(
|
||||
(minCos - (stabilityThreshold - 0.01)) / Math.max(1.0e-5, 1.01 - stabilityThreshold),
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
double deviationScore = Math.exp(-deviation / 0.15);
|
||||
double speedScore = Mth.clamp(avgSpeed / 0.35, 0.0, 1.0);
|
||||
double quality = Mth.clamp(
|
||||
stabilityScore * 0.40 + minCosScore * 0.25 + deviationScore * 0.20 + speedScore * 0.15,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
|
||||
return new TrackedCapture(
|
||||
centroidX,
|
||||
centroidZ,
|
||||
dirX,
|
||||
dirZ,
|
||||
quality,
|
||||
avgCos,
|
||||
deviation,
|
||||
observations.size()
|
||||
);
|
||||
}
|
||||
|
||||
public boolean captured() {
|
||||
return captured;
|
||||
}
|
||||
|
||||
public void markCaptured() {
|
||||
captured = true;
|
||||
}
|
||||
|
||||
public long lastSeenMs() {
|
||||
return lastSeenMs;
|
||||
}
|
||||
|
||||
public record Observation(double x, double z, double dirX, double dirZ, double speed) {
|
||||
}
|
||||
|
||||
public record TrackedCapture(double pointX,
|
||||
double pointZ,
|
||||
double dirX,
|
||||
double dirZ,
|
||||
double quality,
|
||||
double stability,
|
||||
double deviation,
|
||||
int samples) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Pure triangulation math and formatting: weighted pairwise line
|
||||
* intersections of cached throws plus the user-facing status strings.
|
||||
*/
|
||||
public final class TriangulatorSolver {
|
||||
private static final double MIN_SOLVABLE_ANGLE_DEG = 0.05;
|
||||
private static final int COPY_COORD_Y = 64;
|
||||
|
||||
public static final List<CachedThrow> PREVIEW_THROWS = List.of(
|
||||
new CachedThrow(1L, 110.0, 58.0, 0.9238795, -0.3826834, 0.91, 0.996, 0.08, 8, 0L),
|
||||
new CachedThrow(2L, 188.0, -24.0, 0.7071067, -0.7071067, 0.87, 0.992, 0.12, 7, 0L),
|
||||
new CachedThrow(3L, 254.0, -102.0, 0.4713967, -0.8819212, 0.82, 0.989, 0.19, 6, 0L)
|
||||
);
|
||||
|
||||
private TriangulatorSolver() {
|
||||
}
|
||||
|
||||
public static SolveResult previewResult() {
|
||||
return solve(PREVIEW_THROWS, 6.0);
|
||||
}
|
||||
|
||||
public static SolveResult solve(List<CachedThrow> throwsToSolve, double minAngleThreshold) {
|
||||
if (throwsToSolve == null || throwsToSolve.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (throwsToSolve.size() == 1) {
|
||||
CachedThrow only = throwsToSolve.get(0);
|
||||
return new SolveResult(
|
||||
false,
|
||||
false,
|
||||
only.pointX(),
|
||||
only.pointZ(),
|
||||
0.0,
|
||||
0.0,
|
||||
only.quality(),
|
||||
Mth.floor(only.pointX() / 16.0),
|
||||
Mth.floor(only.pointZ() / 16.0)
|
||||
);
|
||||
}
|
||||
|
||||
double qualitySum = 0.0;
|
||||
double strongestAngle = 0.0;
|
||||
double pairX = 0.0;
|
||||
double pairZ = 0.0;
|
||||
double pairWeightSum = 0.0;
|
||||
double crossThreshold = Math.sin(Math.toRadians(MIN_SOLVABLE_ANGLE_DEG));
|
||||
|
||||
for (int i = 0; i < throwsToSolve.size(); i++) {
|
||||
CachedThrow throwI = throwsToSolve.get(i);
|
||||
qualitySum += throwI.quality();
|
||||
|
||||
for (int j = i + 1; j < throwsToSolve.size(); j++) {
|
||||
CachedThrow throwJ = throwsToSolve.get(j);
|
||||
double dot = Math.abs(throwI.dirX() * throwJ.dirX() + throwI.dirZ() * throwJ.dirZ());
|
||||
double angle = Math.toDegrees(Math.acos(Mth.clamp(dot, -1.0, 1.0)));
|
||||
strongestAngle = Math.max(strongestAngle, angle);
|
||||
|
||||
double cross = throwI.dirX() * throwJ.dirZ() - throwI.dirZ() * throwJ.dirX();
|
||||
if (Math.abs(cross) <= crossThreshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double deltaX = throwJ.pointX() - throwI.pointX();
|
||||
double deltaZ = throwJ.pointZ() - throwI.pointZ();
|
||||
double t = (deltaX * throwJ.dirZ() - deltaZ * throwJ.dirX()) / cross;
|
||||
double intersectionX = throwI.pointX() + throwI.dirX() * t;
|
||||
double intersectionZ = throwI.pointZ() + throwI.dirZ() * t;
|
||||
double pairWeight = (0.35 + throwI.quality() * 0.65)
|
||||
* (0.35 + throwJ.quality() * 0.65)
|
||||
* Mth.clamp(0.35 + angle / Math.max(0.35, minAngleThreshold), 0.35, 4.0);
|
||||
|
||||
pairX += intersectionX * pairWeight;
|
||||
pairZ += intersectionZ * pairWeight;
|
||||
pairWeightSum += pairWeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (pairWeightSum <= 1.0e-8) {
|
||||
return new SolveResult(false, false, 0.0, 0.0, Double.POSITIVE_INFINITY, strongestAngle, 0.0, 0, 0);
|
||||
}
|
||||
|
||||
double x = pairX / pairWeightSum;
|
||||
double z = pairZ / pairWeightSum;
|
||||
double errorSum = 0.0;
|
||||
for (CachedThrow cachedThrow : throwsToSolve) {
|
||||
double d = distanceToLine(x, z, cachedThrow);
|
||||
errorSum += d * d;
|
||||
}
|
||||
double rmse = Math.sqrt(errorSum / throwsToSolve.size());
|
||||
double quality = qualitySum / throwsToSolve.size();
|
||||
double effectiveAngleThreshold = Math.max(0.35, Math.min(minAngleThreshold, 2.0));
|
||||
double angleScore = Mth.clamp(
|
||||
(strongestAngle - effectiveAngleThreshold) / Math.max(0.5, 12.0 - effectiveAngleThreshold),
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
double errorScore = Mth.clamp(1.0 - rmse / 64.0, 0.0, 1.0);
|
||||
double countBonus = Mth.clamp((throwsToSolve.size() - 1.0) / 3.0, 0.0, 1.0);
|
||||
double confidence = Mth.clamp(
|
||||
quality * 0.45 + angleScore * 0.25 + errorScore * 0.20 + countBonus * 0.10,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
boolean ready = strongestAngle >= effectiveAngleThreshold && rmse <= 96.0 && confidence >= 0.55;
|
||||
return new SolveResult(
|
||||
true,
|
||||
ready,
|
||||
x,
|
||||
z,
|
||||
rmse,
|
||||
strongestAngle,
|
||||
confidence,
|
||||
Mth.floor(x / 16.0),
|
||||
Mth.floor(z / 16.0)
|
||||
);
|
||||
}
|
||||
|
||||
public static double distanceToLine(double x, double z, CachedThrow cachedThrow) {
|
||||
double nx = -cachedThrow.dirZ();
|
||||
double nz = cachedThrow.dirX();
|
||||
return Math.abs((x - cachedThrow.pointX()) * nx + (z - cachedThrow.pointZ()) * nz);
|
||||
}
|
||||
|
||||
public static String formatPercent(double value) {
|
||||
return Math.round(Mth.clamp(value, 0.0, 1.0) * 100.0) + "%";
|
||||
}
|
||||
|
||||
public static String formatThrowPoint(CachedThrow cachedThrow) {
|
||||
return String.format(Locale.ROOT, "%d %d",
|
||||
Math.round(cachedThrow.pointX()),
|
||||
Math.round(cachedThrow.pointZ()));
|
||||
}
|
||||
|
||||
public static String formatCopyCoordinates(SolveResult result) {
|
||||
if (result == null || !result.hasSolution()) {
|
||||
return "";
|
||||
}
|
||||
return String.format(Locale.ROOT, "%d %d %d", Math.round(result.x()), COPY_COORD_Y, Math.round(result.z()));
|
||||
}
|
||||
|
||||
public static String formatCoordinateLine(double overworldX, double overworldZ, boolean nether) {
|
||||
if (nether) {
|
||||
return String.format(Locale.ROOT, "N %d %d", Math.round(overworldX / 8.0), Math.round(overworldZ / 8.0));
|
||||
}
|
||||
return String.format(Locale.ROOT, "X %d Z %d", Math.round(overworldX), Math.round(overworldZ));
|
||||
}
|
||||
|
||||
public static String formatResultLine(double overworldX, double overworldZ, double rmse, boolean nether) {
|
||||
if (nether) {
|
||||
return String.format(Locale.ROOT, "OW %d %d Err %.1fb",
|
||||
Math.round(overworldX), Math.round(overworldZ), rmse);
|
||||
}
|
||||
return String.format(Locale.ROOT, "Err %.1fb Chunk %d %d",
|
||||
rmse, Mth.floor(overworldX / 16.0), Mth.floor(overworldZ / 16.0));
|
||||
}
|
||||
|
||||
public record CachedThrow(long serial,
|
||||
double pointX,
|
||||
double pointZ,
|
||||
double dirX,
|
||||
double dirZ,
|
||||
double quality,
|
||||
double stability,
|
||||
double deviation,
|
||||
int samples,
|
||||
long capturedAtMs) {
|
||||
}
|
||||
|
||||
public record SolveResult(boolean hasSolution,
|
||||
boolean ready,
|
||||
double x,
|
||||
double z,
|
||||
double rmse,
|
||||
double minPairAngleDeg,
|
||||
double confidence,
|
||||
int chunkX,
|
||||
int chunkZ) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator;
|
||||
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TrackedEye.TrackedCapture;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver.CachedThrow;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver.SolveResult;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.model.CapturePolicy;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.projectile.EyeOfEnder;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class TriangulatorTracker {
|
||||
private static final float SEARCH_RANGE = 196.0f;
|
||||
private static final double CAPTURE_DEVIATION_LIMIT = 0.35;
|
||||
private static final double CAPTURE_QUALITY_FLOOR = 0.55;
|
||||
private static final long MISSING_EYE_TIMEOUT_MS = 600L;
|
||||
|
||||
private final Map<Integer, TrackedEye> activeEyes = new LinkedHashMap<>();
|
||||
private final List<CachedThrow> cachedThrows = new ArrayList<>();
|
||||
private long nextCaptureSerial = 1L;
|
||||
private long lastCaptureMs = 0L;
|
||||
private long lastWorldTime = Long.MIN_VALUE;
|
||||
private long resetMs = 0L;
|
||||
private String resetReason = "";
|
||||
|
||||
public void dropEyes() {
|
||||
activeEyes.clear();
|
||||
}
|
||||
|
||||
public void clear(String reason, boolean rememberReason, long now) {
|
||||
activeEyes.clear();
|
||||
cachedThrows.clear();
|
||||
nextCaptureSerial = 1L;
|
||||
lastCaptureMs = 0L;
|
||||
if (rememberReason) {
|
||||
resetReason = reason;
|
||||
resetMs = now;
|
||||
} else {
|
||||
resetReason = "";
|
||||
resetMs = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean trackingEye() {
|
||||
return !activeEyes.isEmpty();
|
||||
}
|
||||
|
||||
public List<CachedThrow> cachedThrows() {
|
||||
return java.util.Collections.unmodifiableList(cachedThrows);
|
||||
}
|
||||
|
||||
public int throwsCount() {
|
||||
return cachedThrows.size();
|
||||
}
|
||||
|
||||
public String resetReason() {
|
||||
return resetReason;
|
||||
}
|
||||
|
||||
public long resetMs() {
|
||||
return resetMs;
|
||||
}
|
||||
|
||||
private boolean cacheExpired(long now, long lifetimeMs, SolveResult lastResult) {
|
||||
return lifetimeMs > 0L
|
||||
&& !cachedThrows.isEmpty()
|
||||
&& activeEyes.isEmpty()
|
||||
&& (lastResult == null || !lastResult.hasSolution())
|
||||
&& lastCaptureMs > 0L
|
||||
&& now - lastCaptureMs > lifetimeMs;
|
||||
}
|
||||
|
||||
public SolveResult tick(Level level, Player player, CapturePolicy policy, SolveResult lastResult) {
|
||||
long now = System.currentTimeMillis();
|
||||
long worldTime = level.getGameTime();
|
||||
if (lastWorldTime != Long.MIN_VALUE && worldTime + 200L < lastWorldTime) {
|
||||
activeEyes.clear();
|
||||
}
|
||||
lastWorldTime = worldTime;
|
||||
if (cacheExpired(now, policy.cacheLifetimeMs(), lastResult)) {
|
||||
clear("Cache expired", true, now);
|
||||
}
|
||||
|
||||
if (level.dimension() != Level.OVERWORLD) {
|
||||
cleanupMissingEyes(Set.of(), now, policy, lastResult);
|
||||
return solve(policy);
|
||||
}
|
||||
|
||||
AABB searchBox = player.getBoundingBox().inflate(SEARCH_RANGE);
|
||||
List<EyeOfEnder> eyes = level.getEntitiesOfClass(
|
||||
EyeOfEnder.class,
|
||||
searchBox,
|
||||
entity -> entity != null && entity.isAlive()
|
||||
);
|
||||
|
||||
Set<Integer> seen = new HashSet<>();
|
||||
for (EyeOfEnder eye : eyes) {
|
||||
seen.add(eye.getId());
|
||||
TrackedEye trackedEye = activeEyes.computeIfAbsent(eye.getId(), TrackedEye::new);
|
||||
trackedEye.observe(eye, now);
|
||||
|
||||
if (!trackedEye.captured()) {
|
||||
TrackedCapture capture = trackedEye.tryCapture(
|
||||
policy.minObservationTicks(),
|
||||
policy.stabilityThreshold(),
|
||||
CAPTURE_DEVIATION_LIMIT
|
||||
);
|
||||
if (capture != null) {
|
||||
trackedEye.markCaptured();
|
||||
accept(capture, now, policy, lastResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanupMissingEyes(seen, now, policy, lastResult);
|
||||
return solve(policy);
|
||||
}
|
||||
|
||||
private SolveResult solve(CapturePolicy policy) {
|
||||
return TriangulatorSolver.solve(cachedThrows, policy.minPairAngleDeg());
|
||||
}
|
||||
|
||||
private void cleanupMissingEyes(Set<Integer> seen, long now, CapturePolicy policy, SolveResult lastResult) {
|
||||
List<Integer> toRemove = new ArrayList<>();
|
||||
for (Map.Entry<Integer, TrackedEye> entry : activeEyes.entrySet()) {
|
||||
TrackedEye trackedEye = entry.getValue();
|
||||
if (seen.contains(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
if (!trackedEye.captured()) {
|
||||
TrackedCapture capture = trackedEye.tryCapture(
|
||||
policy.minObservationTicks(),
|
||||
policy.stabilityThreshold() - 0.01,
|
||||
CAPTURE_DEVIATION_LIMIT * 1.25
|
||||
);
|
||||
if (capture != null) {
|
||||
trackedEye.markCaptured();
|
||||
accept(capture, now, policy, lastResult);
|
||||
}
|
||||
}
|
||||
if (trackedEye.captured() || now - trackedEye.lastSeenMs() > MISSING_EYE_TIMEOUT_MS) {
|
||||
toRemove.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
for (Integer id : toRemove) {
|
||||
activeEyes.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void accept(TrackedCapture capture, long now, CapturePolicy policy, SolveResult lastResult) {
|
||||
if (capture.quality() < CAPTURE_QUALITY_FLOOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
CachedThrow cachedThrow = new CachedThrow(
|
||||
nextCaptureSerial++,
|
||||
capture.pointX(),
|
||||
capture.pointZ(),
|
||||
capture.dirX(),
|
||||
capture.dirZ(),
|
||||
capture.quality(),
|
||||
capture.stability(),
|
||||
capture.deviation(),
|
||||
capture.samples(),
|
||||
now
|
||||
);
|
||||
|
||||
if (policy.autoReset() && !cachedThrows.isEmpty()) {
|
||||
SolveResult existing = lastResult != null ? lastResult : solve(policy);
|
||||
if (existing != null && existing.ready()
|
||||
&& TriangulatorSolver.distanceToLine(existing.x(), existing.z(), cachedThrow) > policy.autoResetDistance()) {
|
||||
clear("Different stronghold", true, now);
|
||||
}
|
||||
}
|
||||
|
||||
cachedThrows.add(cachedThrow);
|
||||
while (cachedThrows.size() > policy.maxCachedThrows()) {
|
||||
cachedThrows.remove(0);
|
||||
}
|
||||
lastCaptureMs = now;
|
||||
resetReason = "";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator;
|
||||
|
||||
import dev.loki.lovisual.features.gui.hud.HudRenderUtil;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver.CachedThrow;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver.SolveResult;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.model.DisplayState;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.ScriptedTriangulatorHudPanel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
|
||||
public final class TriangulatorView {
|
||||
private static final long RESET_FLASH_MS = 3000L;
|
||||
|
||||
private int uiHeaderLeft;
|
||||
private int uiHeaderRight;
|
||||
private int uiBodyLeft;
|
||||
private int uiBodyRight;
|
||||
private int uiOutline;
|
||||
private int uiText;
|
||||
private int uiMuted;
|
||||
private int uiCounter;
|
||||
private int uiTitleText;
|
||||
private int uiDivider;
|
||||
private int uiBlurTint;
|
||||
private int uiSuccess;
|
||||
private int uiWarn;
|
||||
private int uiDanger;
|
||||
|
||||
public void update(boolean themeMode, int alpha, int bg, int bg2, int stroke, int text, int muted) {
|
||||
if (themeMode) {
|
||||
int window = HudRenderUtil.setAlpha(theme().windowBg(), alpha);
|
||||
int header = HudRenderUtil.setAlpha(
|
||||
HudRenderUtil.mixColor(theme().windowHeader(), theme().surface(), 0.18f),
|
||||
Math.min(255, alpha + 14)
|
||||
);
|
||||
int surface = HudRenderUtil.setAlpha(
|
||||
HudRenderUtil.mixColor(theme().surface(), theme().windowBg(), 0.22f),
|
||||
Math.min(255, alpha + 6)
|
||||
);
|
||||
int deep = HudRenderUtil.setAlpha(
|
||||
HudRenderUtil.mixColor(theme().surface(), theme().windowHeader(), 0.42f),
|
||||
Math.min(255, alpha + 18)
|
||||
);
|
||||
|
||||
uiHeaderLeft = HudRenderUtil.mixColor(header, window, 0.22f);
|
||||
uiHeaderRight = HudRenderUtil.mixColor(surface, header, 0.52f);
|
||||
uiBodyLeft = HudRenderUtil.mixColor(window, surface, 0.24f);
|
||||
uiBodyRight = HudRenderUtil.mixColor(deep, surface, 0.18f);
|
||||
uiOutline = HudRenderUtil.setAlpha(
|
||||
HudRenderUtil.mixColor(theme().windowStroke(), theme().strokeSoft(), 0.18f),
|
||||
Math.min(255, Math.max(182, alpha + 36))
|
||||
);
|
||||
uiText = theme().textPrimary();
|
||||
uiTitleText = HudRenderUtil.mixColor(theme().textPrimary(), theme().accent(), 0.14f);
|
||||
uiMuted = HudRenderUtil.mixColor(theme().textMuted(), theme().textPrimary(), 0.16f);
|
||||
uiCounter = HudRenderUtil.mixColor(theme().accent(), theme().textPrimary(), 0.38f);
|
||||
uiDivider = HudRenderUtil.scaleAlpha(uiMuted, 0.68f);
|
||||
uiBlurTint = HudRenderUtil.mixColor(
|
||||
HudRenderUtil.mixColor(uiHeaderRight, uiBodyRight, 0.5f),
|
||||
theme().accent(),
|
||||
0.14f
|
||||
);
|
||||
} else {
|
||||
uiHeaderLeft = bg;
|
||||
uiHeaderRight = bg2;
|
||||
uiBodyLeft = HudRenderUtil.mixColor(bg, bg2, 0.15f);
|
||||
uiBodyRight = bg2;
|
||||
uiOutline = stroke;
|
||||
uiText = text;
|
||||
uiTitleText = uiText;
|
||||
uiMuted = muted;
|
||||
uiCounter = HudRenderUtil.mixColor(text, 0xFFE1E1FF, 0.20f);
|
||||
uiDivider = HudRenderUtil.scaleAlpha(uiMuted, 0.55f);
|
||||
uiBlurTint = HudRenderUtil.mixColor(uiHeaderLeft, uiBodyRight, 0.5f);
|
||||
}
|
||||
|
||||
uiSuccess = HudRenderUtil.mixColor(uiCounter, 0xFF55FF93, 0.55f);
|
||||
uiWarn = HudRenderUtil.mixColor(uiCounter, 0xFFFFC85A, 0.55f);
|
||||
uiDanger = HudRenderUtil.mixColor(uiCounter, 0xFFFF6A6A, 0.62f);
|
||||
}
|
||||
|
||||
public ScriptedTriangulatorHudPanel.Palette palette() {
|
||||
return new ScriptedTriangulatorHudPanel.Palette(
|
||||
uiHeaderLeft, uiHeaderRight, uiBodyLeft, uiBodyRight,
|
||||
uiOutline, uiText, uiMuted, uiCounter,
|
||||
uiTitleText, uiDivider, uiBlurTint,
|
||||
uiSuccess, uiWarn, uiDanger
|
||||
);
|
||||
}
|
||||
|
||||
public int text() {
|
||||
return uiText;
|
||||
}
|
||||
|
||||
public int muted() {
|
||||
return uiMuted;
|
||||
}
|
||||
|
||||
public int counter() {
|
||||
return uiCounter;
|
||||
}
|
||||
|
||||
public int danger() {
|
||||
return uiDanger;
|
||||
}
|
||||
|
||||
public float blurStrength(boolean themeMode, int blurAlpha) {
|
||||
return Math.min(1.0f, (blurAlpha / 255.0f) * (themeMode ? 1.15f : 1.0f));
|
||||
}
|
||||
|
||||
public DisplayState status(List<CachedThrow> throwsToShow,
|
||||
SolveResult result,
|
||||
boolean preview,
|
||||
boolean trackingEye,
|
||||
String resetReason,
|
||||
long resetMs,
|
||||
boolean nether) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (throwsToShow.isEmpty()) {
|
||||
if (!resetReason.isEmpty() && now - resetMs <= RESET_FLASH_MS) {
|
||||
return new DisplayState(resetReason, "Cache cleared", "Waiting for a new eye", "", uiDanger);
|
||||
}
|
||||
if (trackingEye) {
|
||||
return new DisplayState("Tracking eye", "Stabilizing trajectory", "Need one clean capture", "", uiWarn);
|
||||
}
|
||||
if (preview) {
|
||||
return new DisplayState("Solved", TriangulatorSolver.formatCoordinateLine(1288.0, -412.0, nether),
|
||||
TriangulatorSolver.formatResultLine(1288.0, -412.0, 5.2, nether), "88%", uiSuccess);
|
||||
}
|
||||
return new DisplayState("Waiting for eye", "Throw an eye of ender", "Second clean eye unlocks result", "", uiMuted);
|
||||
}
|
||||
|
||||
if (throwsToShow.size() == 1) {
|
||||
CachedThrow first = throwsToShow.get(0);
|
||||
return new DisplayState(
|
||||
trackingEye ? "Tracking next eye" : "Need second eye",
|
||||
String.format(Locale.ROOT, "Q %s Stability %.3f", TriangulatorSolver.formatPercent(first.quality()), first.stability()),
|
||||
"Need another angle for solve",
|
||||
"",
|
||||
uiWarn
|
||||
);
|
||||
}
|
||||
|
||||
if (result == null || !result.hasSolution()) {
|
||||
return new DisplayState(
|
||||
"Angles too close",
|
||||
String.format(Locale.ROOT, "Best pair %.2f deg", result == null ? 0.0 : result.minPairAngleDeg()),
|
||||
"Move farther, then throw again",
|
||||
"",
|
||||
uiWarn
|
||||
);
|
||||
}
|
||||
|
||||
if (!result.ready()) {
|
||||
return new DisplayState(
|
||||
"Low correlation",
|
||||
TriangulatorSolver.formatCoordinateLine(result.x(), result.z(), nether),
|
||||
String.format(Locale.ROOT, "%s Pair %.2f deg",
|
||||
TriangulatorSolver.formatResultLine(result.x(), result.z(), result.rmse(), nether), result.minPairAngleDeg()),
|
||||
TriangulatorSolver.formatPercent(result.confidence()),
|
||||
uiWarn
|
||||
);
|
||||
}
|
||||
|
||||
return new DisplayState(
|
||||
"Solved",
|
||||
TriangulatorSolver.formatCoordinateLine(result.x(), result.z(), nether),
|
||||
TriangulatorSolver.formatResultLine(result.x(), result.z(), result.rmse(), nether),
|
||||
TriangulatorSolver.formatPercent(result.confidence()),
|
||||
uiSuccess
|
||||
);
|
||||
}
|
||||
|
||||
public int markerColor(CachedThrow cachedThrow, SolveResult result, int index, int total, double autoResetDistance) {
|
||||
if (result == null || !result.hasSolution()) {
|
||||
return index == total - 1 ? uiWarn : uiCounter;
|
||||
}
|
||||
double error = TriangulatorSolver.distanceToLine(result.x(), result.z(), cachedThrow);
|
||||
if (error <= 8.0) {
|
||||
return uiSuccess;
|
||||
}
|
||||
if (error <= autoResetDistance * 0.5) {
|
||||
return uiWarn;
|
||||
}
|
||||
return uiDanger;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.model;
|
||||
|
||||
/** Tunables the tracker needs for a single tick. */
|
||||
public record CapturePolicy(int minObservationTicks,
|
||||
double stabilityThreshold,
|
||||
double minPairAngleDeg,
|
||||
boolean autoReset,
|
||||
double autoResetDistance,
|
||||
int maxCachedThrows,
|
||||
long cacheLifetimeMs) {
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.model;
|
||||
|
||||
public record DisplayState(String statusText,
|
||||
String primaryLine,
|
||||
String secondaryLine,
|
||||
String confidenceText,
|
||||
int statusColor) {
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.render;
|
||||
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver.CachedThrow;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver;
|
||||
import dev.loki.lovisual.render.engine.text.TextRenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Text measurement pass that computes the animated panel width/height
|
||||
* targets and shared header metrics for the Triangulator HUD.
|
||||
*/
|
||||
public final class TriangulatorMeasure {
|
||||
static final float CLEAR_BUTTON_SIZE = 9.0f;
|
||||
static final float HEADER_BUTTON_GAP = 3.0f;
|
||||
private static final float MIN_WIDTH = 126.0f;
|
||||
private static final float ROW_STEP = 11.0f;
|
||||
private static final float CONTENT_START_Y = 23.0f;
|
||||
private static final float SUMMARY_HEIGHT = 30.5f;
|
||||
private static final float TITLE_TEXT_X = 22.0f;
|
||||
private static final float COUNT_LABEL_OFFSET = 28.0f;
|
||||
|
||||
public record Metrics(float headerTextH,
|
||||
float rowTextH,
|
||||
float maxWidth,
|
||||
float activeLabelWidth,
|
||||
float activeCountWidth,
|
||||
float targetHeight) {
|
||||
}
|
||||
|
||||
private TriangulatorMeasure() {
|
||||
}
|
||||
|
||||
public static Metrics measure(TextRenderer headerTextRenderer,
|
||||
TextRenderer rowTextRenderer,
|
||||
float baseScale,
|
||||
float fontScale,
|
||||
List<CachedThrow> displayThrows,
|
||||
String statusText,
|
||||
String primaryLine,
|
||||
String secondaryLine,
|
||||
boolean showClearButton,
|
||||
boolean showCopyButton) {
|
||||
headerTextRenderer.begin(fontScale, true, false);
|
||||
float headerTextH = (float) headerTextRenderer.getHeight(false);
|
||||
float titleWidth = (float) headerTextRenderer.getWidth("Triangulator", false);
|
||||
headerTextRenderer.end();
|
||||
|
||||
rowTextRenderer.begin(fontScale, true, false);
|
||||
float rowTextH = (float) rowTextRenderer.getHeight(false);
|
||||
float maxWidth = Math.max(MIN_WIDTH * baseScale,
|
||||
(float) rowTextRenderer.getWidth(statusText, false) + 52.0f * baseScale);
|
||||
maxWidth = Math.max(maxWidth, (float) rowTextRenderer.getWidth(primaryLine, false) + 20.0f * baseScale);
|
||||
rowTextRenderer.end();
|
||||
|
||||
rowTextRenderer.begin(fontScale * 0.92f, false, false);
|
||||
maxWidth = Math.max(maxWidth, (float) rowTextRenderer.getWidth(secondaryLine, false) + 20.0f * baseScale);
|
||||
String activeCountText = Integer.toString(displayThrows.size());
|
||||
float activeLabelWidth = (float) rowTextRenderer.getWidth("Eyes:", false);
|
||||
float activeCountWidth = (float) rowTextRenderer.getWidth(activeCountText, false);
|
||||
rowTextRenderer.end();
|
||||
|
||||
for (int i = 0; i < displayThrows.size(); i++) {
|
||||
CachedThrow cachedThrow = displayThrows.get(i);
|
||||
rowTextRenderer.begin(fontScale, true, false);
|
||||
float rowWidth = (float) rowTextRenderer.getWidth("Eye #" + (i + 1), false)
|
||||
+ (float) rowTextRenderer.getWidth(TriangulatorSolver.formatThrowPoint(cachedThrow), false)
|
||||
+ 36.0f * baseScale;
|
||||
rowTextRenderer.end();
|
||||
maxWidth = Math.max(maxWidth, rowWidth);
|
||||
}
|
||||
|
||||
int headerButtons = (showClearButton ? 1 : 0) + (showCopyButton ? 1 : 0);
|
||||
float headerButtonsWidth = headerButtons == 0 ? 0.0f
|
||||
: ((CLEAR_BUTTON_SIZE * headerButtons) + (HEADER_BUTTON_GAP * Math.max(0, headerButtons - 1)) + 4.0f) * baseScale;
|
||||
float headerWidth = (TITLE_TEXT_X * baseScale) + titleWidth
|
||||
+ activeLabelWidth + activeCountWidth
|
||||
+ headerButtonsWidth
|
||||
+ ((COUNT_LABEL_OFFSET + 16.0f) * baseScale);
|
||||
maxWidth = Math.max(maxWidth, headerWidth);
|
||||
|
||||
float rowsHeight = displayThrows.isEmpty() ? 0.0f : displayThrows.size() * (ROW_STEP * baseScale);
|
||||
float dividerHeight = displayThrows.isEmpty() ? 0.0f : 5.0f * baseScale;
|
||||
float targetHeight = (CONTENT_START_Y * baseScale) + (SUMMARY_HEIGHT * baseScale) + dividerHeight + rowsHeight;
|
||||
return new Metrics(headerTextH, rowTextH, maxWidth, activeLabelWidth, activeCountWidth, targetHeight);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.render;
|
||||
|
||||
import dev.loki.lovisual.features.gui.hud.HudRenderUtil;
|
||||
import dev.loki.lovisual.features.gui.hud.HudTextEffects;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.ScriptedTriangulatorHudPanel;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver.CachedThrow;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver.SolveResult;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorSolver;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.TriangulatorView;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator.model.DisplayState;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.util.Util;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Assembles the scripted Triangulator panel: header button hitboxes, row
|
||||
* data and the final {@link ScriptedTriangulatorHudPanel.Panel} instance.
|
||||
*/
|
||||
public final class TriangulatorPainter {
|
||||
private static final Identifier HEADER_ICON =
|
||||
Identifier.fromNamespaceAndPath("lovisual", "textures/hud/elements/coords.png");
|
||||
private static final float HEADER_HEIGHT = 15.5f;
|
||||
private static final float CLEAR_BUTTON_RIGHT = 8.0f;
|
||||
|
||||
/** Screen-space hitboxes of the header clear/copy buttons. */
|
||||
public record HeaderButtons(float clearX,
|
||||
float clearY,
|
||||
float clearSize,
|
||||
float copyX,
|
||||
float copyY,
|
||||
float copySize) {
|
||||
public static final HeaderButtons NONE = new HeaderButtons(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
private TriangulatorPainter() {
|
||||
}
|
||||
|
||||
public static HeaderButtons placeButtons(TriangulatorMeasure.Metrics metrics,
|
||||
float drawX,
|
||||
float drawY,
|
||||
float drawWidth,
|
||||
float drawHeight,
|
||||
float drawBaseScale,
|
||||
boolean showClearButton,
|
||||
boolean showCopyButton) {
|
||||
float buttonCursorRight = CLEAR_BUTTON_RIGHT * drawBaseScale;
|
||||
float clearX = 0.0f;
|
||||
float clearY = 0.0f;
|
||||
float clearSize = 0.0f;
|
||||
float copyX = 0.0f;
|
||||
float copyY = 0.0f;
|
||||
float copySize = 0.0f;
|
||||
if (showClearButton) {
|
||||
clearSize = TriangulatorMeasure.CLEAR_BUTTON_SIZE * drawBaseScale;
|
||||
clearX = drawX + drawWidth - buttonCursorRight - clearSize;
|
||||
clearY = drawY + (((HEADER_HEIGHT * drawBaseScale) - clearSize) * 0.5f);
|
||||
buttonCursorRight += clearSize + (TriangulatorMeasure.HEADER_BUTTON_GAP * drawBaseScale);
|
||||
}
|
||||
if (showCopyButton) {
|
||||
copySize = TriangulatorMeasure.CLEAR_BUTTON_SIZE * drawBaseScale;
|
||||
copyX = drawX + drawWidth - buttonCursorRight - copySize;
|
||||
copyY = drawY + (((HEADER_HEIGHT * drawBaseScale) - copySize) * 0.5f);
|
||||
}
|
||||
return new HeaderButtons(clearX, clearY, clearSize, copyX, copyY, copySize);
|
||||
}
|
||||
|
||||
public static int headerIconColor(int baseColor, HudTextEffects.Effect effect, int effectSpeed) {
|
||||
if (effect == HudTextEffects.Effect.NONE) {
|
||||
return baseColor;
|
||||
}
|
||||
return HudTextEffects.animatedColor(
|
||||
baseColor,
|
||||
effect,
|
||||
effectSpeed,
|
||||
(float) (Util.getMillis() / 1000.0),
|
||||
0.0f
|
||||
);
|
||||
}
|
||||
|
||||
public static List<LinkedHashMap<String, Object>> buildRows(List<CachedThrow> displayThrows,
|
||||
SolveResult displayResult,
|
||||
TriangulatorView view,
|
||||
double autoResetDistance) {
|
||||
List<LinkedHashMap<String, Object>> panelRows = ScriptedTriangulatorHudPanel.rows();
|
||||
for (int i = 0; i < displayThrows.size(); i++) {
|
||||
CachedThrow cachedThrow = displayThrows.get(i);
|
||||
int markerColor = view.markerColor(cachedThrow, displayResult, i, displayThrows.size(), autoResetDistance);
|
||||
panelRows.add(ScriptedTriangulatorHudPanel.row(
|
||||
"eye:" + cachedThrow.serial(),
|
||||
"Eye #" + (i + 1),
|
||||
TriangulatorSolver.formatThrowPoint(cachedThrow),
|
||||
view.text(),
|
||||
markerColor,
|
||||
markerColor,
|
||||
HudRenderUtil.scaleAlpha(view.muted(), 0.45f),
|
||||
1.0f
|
||||
));
|
||||
}
|
||||
return panelRows;
|
||||
}
|
||||
|
||||
public static ScriptedTriangulatorHudPanel.Panel panel(TriangulatorView view,
|
||||
TriangulatorMeasure.Metrics metrics,
|
||||
float drawX,
|
||||
float drawY,
|
||||
float drawWidth,
|
||||
float drawHeight,
|
||||
float drawScale,
|
||||
float drawBaseScale,
|
||||
float drawFontScale,
|
||||
int eyeCount,
|
||||
boolean blur,
|
||||
float blurStrength,
|
||||
int headerIconColor,
|
||||
boolean showClearButton,
|
||||
boolean showCopyButton,
|
||||
DisplayState state,
|
||||
List<LinkedHashMap<String, Object>> rows) {
|
||||
return new ScriptedTriangulatorHudPanel.Panel(
|
||||
view.palette(),
|
||||
drawX,
|
||||
drawY,
|
||||
drawWidth,
|
||||
drawHeight,
|
||||
drawScale,
|
||||
drawBaseScale,
|
||||
drawFontScale,
|
||||
metrics.headerTextH() * drawScale,
|
||||
metrics.rowTextH() * drawScale,
|
||||
metrics.activeLabelWidth() * drawScale,
|
||||
metrics.activeCountWidth() * drawScale,
|
||||
eyeCount,
|
||||
blur,
|
||||
blurStrength,
|
||||
ScriptedTriangulatorHudPanel.idString(HEADER_ICON),
|
||||
headerIconColor,
|
||||
showClearButton,
|
||||
view.danger(),
|
||||
showCopyButton,
|
||||
view.counter(),
|
||||
state.statusText(),
|
||||
state.primaryLine(),
|
||||
state.secondaryLine(),
|
||||
state.confidenceText(),
|
||||
state.statusColor(),
|
||||
rows
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,20 @@ import dev.loki.lovisual.render.engine.renderer.ui.UiItemSubmission;
|
|||
import dev.loki.lovisual.render.engine.renderer.ui.UiRenderDispatcher;
|
||||
import dev.loki.lovisual.render.engine.postprocess.PostProcessManager;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.*;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Chamfered2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Rects2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Strokes2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.fx.Blur2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.fx.Effects2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.fx.Glass2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.fx.Smoke2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.media.Items2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.media.Textures2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.path.Circles2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.path.Connectors2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.path.Lines2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.path.Polygons2D;
|
||||
import dev.loki.lovisual.render.engine.svg.SvgMeshBackend;
|
||||
import dev.loki.lovisual.render.engine.svg.SvgRegistry;
|
||||
import dev.loki.lovisual.render.engine.svg.SvgRenderOptions;
|
||||
|
|
@ -128,13 +142,6 @@ public final class Renderer2D {
|
|||
final boolean textured;
|
||||
final MeshBuilder texturedTriangles;
|
||||
final int[] gradientTmp = new int[4];
|
||||
final float[] cornerRadiiTmp = new float[4];
|
||||
final double[] rectShapeTmp = new double[256];
|
||||
final double[] connectorTmp = new double[64];
|
||||
final double[] connectorAnchorTmp = new double[4];
|
||||
final int[] polygonIndexTmp = new int[64];
|
||||
final int[] polygonVertexTmp = new int[64];
|
||||
final double[] progressShapeTmp = new double[256];
|
||||
double alpha = 1.0;
|
||||
|
||||
public Renderer2D(boolean textured) {
|
||||
|
|
@ -158,6 +165,15 @@ public final class Renderer2D {
|
|||
return alpha;
|
||||
}
|
||||
|
||||
public boolean isTextured() {
|
||||
return textured;
|
||||
}
|
||||
|
||||
/** Direct-text triangle mesh; non-null only on the TEXTURE renderer. */
|
||||
public MeshBuilder texturedMesh() {
|
||||
return texturedTriangles;
|
||||
}
|
||||
|
||||
public void shape(UiShape shape, UiPaint paint) {
|
||||
shape(shape, paint, UiStroke.NONE, true);
|
||||
}
|
||||
|
|
@ -175,7 +191,7 @@ public final class Renderer2D {
|
|||
}
|
||||
|
||||
public void box(UiBoxShape box, UiPaint paint, UiStroke stroke, boolean fill) {
|
||||
Renderer2DPolygons.box(this, box, paint, stroke, fill);
|
||||
Polygons2D.box(this, box, paint, stroke, fill);
|
||||
}
|
||||
|
||||
public void shape(UiShape shape, UiPaint paint, UiStroke stroke, boolean fill) {
|
||||
|
|
@ -368,7 +384,7 @@ public final class Renderer2D {
|
|||
@Nullable String stackCountText,
|
||||
int durabilityThresholdPercent,
|
||||
int durabilityTextColorThresholdPercent) {
|
||||
Renderer2DItems.submit(this,
|
||||
Items2D.submit(this,
|
||||
player, stack, x, y, scaleX, scaleY, pivotX, pivotY, pivoted, seed, drawItem, overlayFlags,
|
||||
stackCountText, durabilityThresholdPercent, durabilityTextColorThresholdPercent);
|
||||
}
|
||||
|
|
@ -499,7 +515,7 @@ public final class Renderer2D {
|
|||
double w,
|
||||
double h,
|
||||
int argb) {
|
||||
Renderer2DTextures.textureQuad(this, samplerView, sampler, x, y, w, h, argb);
|
||||
Textures2D.textureQuad(this, samplerView, sampler, x, y, w, h, argb);
|
||||
}
|
||||
|
||||
public void msdfTextureQuad(GpuTextureView samplerView,
|
||||
|
|
@ -528,24 +544,24 @@ public final class Renderer2D {
|
|||
float pxRange,
|
||||
int atlasWidth,
|
||||
int atlasHeight) {
|
||||
Renderer2DTextures.msdfTextureQuad(this, samplerView, sampler, x, y, w, h,
|
||||
Textures2D.msdfTextureQuad(this, samplerView, sampler, x, y, w, h,
|
||||
topLeft, topRight, bottomRight, bottomLeft, pxRange, atlasWidth, atlasHeight);
|
||||
}
|
||||
|
||||
public void quadBatch(Consumer<QuadBatch> consumer) {
|
||||
Renderer2DRects.quadBatch(this, consumer);
|
||||
Rects2D.quadBatch(this, consumer);
|
||||
}
|
||||
|
||||
public void line(double x1, double y1, double x2, double y2, int argb) {
|
||||
Renderer2DLines.line(this, x1, y1, x2, y2, argb);
|
||||
Lines2D.line(this, x1, y1, x2, y2, argb);
|
||||
}
|
||||
|
||||
public void line(double x1, double y1, double x2, double y2, RenderColor color) {
|
||||
Renderer2DLines.line(this, x1, y1, x2, y2, color);
|
||||
Lines2D.line(this, x1, y1, x2, y2, color);
|
||||
}
|
||||
|
||||
public void boxLines(double x, double y, double width, double height, int argb) {
|
||||
Renderer2DLines.boxLines(this, x, y, width, height, argb);
|
||||
Lines2D.boxLines(this, x, y, width, height, argb);
|
||||
}
|
||||
|
||||
public void quad(double x, double y, double width, double height, int argb) {
|
||||
|
|
@ -553,7 +569,7 @@ public final class Renderer2D {
|
|||
}
|
||||
|
||||
public void quad(double x, double y, double width, double height, int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
Renderer2DRects.quadCorners(this, x, y, width, height, cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
Rects2D.quadCorners(this, x, y, width, height, cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
public void roundedRect(double x, double y, double w, double h,
|
||||
|
|
@ -571,7 +587,7 @@ public final class Renderer2D {
|
|||
}
|
||||
|
||||
public void circle(double cx, double cy, double radius, float softness, int argb) {
|
||||
Renderer2DCircles.circle(this, cx, cy, radius, softness, argb);
|
||||
Circles2D.circle(this, cx, cy, radius, softness, argb);
|
||||
}
|
||||
|
||||
public void circleSoftShadow(double cx, double cy, double radius, float blur, float innerAlpha, int argb) {
|
||||
|
|
@ -585,7 +601,7 @@ public final class Renderer2D {
|
|||
}
|
||||
|
||||
public void circleStroke(double cx, double cy, double radius, double thickness, float softness, int argb) {
|
||||
Renderer2DCircles.circleStroke(this, cx, cy, radius, thickness, softness, argb);
|
||||
Circles2D.circleStroke(this, cx, cy, radius, thickness, softness, argb);
|
||||
}
|
||||
|
||||
public void arcStroke(double cx, double cy, double radius, double thickness,
|
||||
|
|
@ -651,7 +667,7 @@ public final class Renderer2D {
|
|||
public void arcStrokeQuad(double cx, double cy, double radius, double thickness,
|
||||
float startAngleDeg, float endAngleDeg, float softness, boolean caps,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
Renderer2DCircles.arcStrokeQuad(this, cx, cy, radius, thickness, startAngleDeg, endAngleDeg, softness, caps,
|
||||
Circles2D.arcStrokeQuad(this, cx, cy, radius, thickness, startAngleDeg, endAngleDeg, softness, caps,
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
|
|
@ -663,7 +679,7 @@ public final class Renderer2D {
|
|||
int colorMode, float angularOffset, float angularCycles,
|
||||
float trackAlpha, float bodyAlpha, float headBoost,
|
||||
float trackGlow, boolean caps) {
|
||||
Renderer2DOrbiz.orbizRing(this, cx, cy, radius, thickness, startAngleDeg, sweepAngleDeg,
|
||||
Circles2D.orbizRing(this, cx, cy, radius, thickness, startAngleDeg, sweepAngleDeg,
|
||||
softness, glowRadius, glowStrength, primaryArgb, secondaryArgb,
|
||||
colorMode, angularOffset, angularCycles, trackAlpha, bodyAlpha, headBoost, trackGlow, caps);
|
||||
}
|
||||
|
|
@ -676,7 +692,7 @@ public final class Renderer2D {
|
|||
|
||||
public void roundedRectStroke(double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness, int argb) {
|
||||
Renderer2DRectsExtra.roundedRectStroke(this, x, y, w, h, radius, softness, thickness, argb);
|
||||
Strokes2D.roundedRectStroke(this, x, y, w, h, radius, softness, thickness, argb);
|
||||
}
|
||||
|
||||
public void roundedRectStroke(double x, double y, double w, double h,
|
||||
|
|
@ -687,14 +703,14 @@ public final class Renderer2D {
|
|||
public void roundedRectStrokeAngularGradient(double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness,
|
||||
int startArgb, int endArgb, float offset) {
|
||||
Renderer2DRectsExtra.roundedRectStrokeAngularGradient(this, x, y, w, h, radius, softness, thickness,
|
||||
Strokes2D.roundedRectStrokeAngularGradient(this, x, y, w, h, radius, softness, thickness,
|
||||
startArgb, endArgb, offset);
|
||||
}
|
||||
|
||||
public void roundedRectStrokeCorners(double x, double y, double w, double h,
|
||||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness, float thickness, int argb) {
|
||||
Renderer2DRectsExtra.roundedRectStrokeCorners(this, x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL,
|
||||
Strokes2D.roundedRectStrokeCorners(this, x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL,
|
||||
softness, thickness, argb);
|
||||
}
|
||||
|
||||
|
|
@ -723,7 +739,7 @@ public final class Renderer2D {
|
|||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
Renderer2DRects.roundedRectCornersQuad(this, x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL, softness,
|
||||
Rects2D.roundedRectCornersQuad(this, x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL, softness,
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
|
|
@ -772,7 +788,7 @@ public final class Renderer2D {
|
|||
boolean fromRight,
|
||||
int startArgb, int endArgb,
|
||||
float angleDeg, float offsetPx) {
|
||||
Renderer2DRectsExtra.roundedProgressRectGradient(this, x, y, w, h, radius, softness, progress, fromRight,
|
||||
Strokes2D.roundedProgressRectGradient(this, x, y, w, h, radius, softness, progress, fromRight,
|
||||
startArgb, endArgb, angleDeg, offsetPx);
|
||||
}
|
||||
|
||||
|
|
@ -800,7 +816,7 @@ public final class Renderer2D {
|
|||
float flowX,
|
||||
float flowY,
|
||||
float intensity) {
|
||||
Renderer2DSmokeFill.roundedSmokeFill(this, x, y, w, h, radius, softness, fillRatio, fromRight,
|
||||
Smoke2D.roundedSmokeFill(this, x, y, w, h, radius, softness, fillRatio, fromRight,
|
||||
firstArgb, secondArgb, thirdArgb, time, smokeScale, smokeMix, octaves, flowX, flowY, intensity);
|
||||
}
|
||||
|
||||
|
|
@ -849,24 +865,24 @@ public final class Renderer2D {
|
|||
|
||||
public void roundedRectGlow(double x, double y, double w, double h,
|
||||
float radius, float softness, float glow, int argb) {
|
||||
Renderer2DRectsGlow.roundedRectGlow(this, x, y, w, h, radius, softness, glow, argb);
|
||||
Effects2D.roundedRectGlow(this, x, y, w, h, radius, softness, glow, argb);
|
||||
}
|
||||
|
||||
public void roundedRectShadow(double x, double y, double w, double h,
|
||||
float radius, float softness, float spread, int argb) {
|
||||
Renderer2DRectsGlow.roundedRectShadow(this, x, y, w, h, radius, softness, spread, argb);
|
||||
Effects2D.roundedRectShadow(this, x, y, w, h, radius, softness, spread, argb);
|
||||
}
|
||||
|
||||
public void roundedRectSoftShadow(double x, double y, double w, double h,
|
||||
float radius, float blur, float innerAlpha, int argb) {
|
||||
Renderer2DRectsGlow.roundedRectSoftShadow(this, x, y, w, h, radius, blur, innerAlpha, argb);
|
||||
Effects2D.roundedRectSoftShadow(this, x, y, w, h, radius, blur, innerAlpha, argb);
|
||||
}
|
||||
|
||||
public void radialGlowMasked(double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
float glowRadius, float cx, float cy,
|
||||
int argb) {
|
||||
Renderer2DRectsGlow.radialGlowMasked(this, x, y, w, h, radius, softness, glowRadius, cx, cy, argb);
|
||||
Effects2D.radialGlowMasked(this, x, y, w, h, radius, softness, glowRadius, cx, cy, argb);
|
||||
}
|
||||
|
||||
public void roundedRectGradientQuad(double x, double y, double w, double h,
|
||||
|
|
@ -885,14 +901,14 @@ public final class Renderer2D {
|
|||
double maskX, double maskY, double maskW, double maskH,
|
||||
float radius, float softness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
Renderer2DRects.roundedRectMaskedQuad(this, x, y, w, h, maskX, maskY, maskW, maskH, radius, softness,
|
||||
Rects2D.roundedRectMaskedQuad(this, x, y, w, h, maskX, maskY, maskW, maskH, radius, softness,
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
public void roundedRectStrokeGradientQuad(double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
Renderer2DRectsExtra.roundedRectStrokeGradientQuad(this, x, y, w, h, radius, softness, thickness,
|
||||
Strokes2D.roundedRectStrokeGradientQuad(this, x, y, w, h, radius, softness, thickness,
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
|
|
@ -907,7 +923,7 @@ public final class Renderer2D {
|
|||
float radius, float softness,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb, GpuTextureView samplerView, GpuSampler sampler) {
|
||||
Renderer2DTextures.roundedTexRect(this, x, y, w, h, radius, softness, texX1, texY1, texX2, texY2,
|
||||
Textures2D.roundedTexRect(this, x, y, w, h, radius, softness, texX1, texY1, texX2, texY2,
|
||||
argb, samplerView, sampler);
|
||||
}
|
||||
|
||||
|
|
@ -915,7 +931,7 @@ public final class Renderer2D {
|
|||
float radius, float softness,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb, GpuTextureView samplerView, GpuSampler sampler) {
|
||||
Renderer2DTextures.roundedTexMaskRect(this, x, y, w, h, radius, softness, texX1, texY1, texX2, texY2,
|
||||
Textures2D.roundedTexMaskRect(this, x, y, w, h, radius, softness, texX1, texY1, texX2, texY2,
|
||||
argb, samplerView, sampler);
|
||||
}
|
||||
|
||||
|
|
@ -1103,12 +1119,12 @@ public final class Renderer2D {
|
|||
(float) chamferTR,
|
||||
(float) chamferBR,
|
||||
(float) chamferBL,
|
||||
cornerRadiiTmp);
|
||||
Batch2D.CORNER_RADII);
|
||||
chamferedRectQuadAxes(x, y, w, h,
|
||||
cornerRadiiTmp[0], cornerRadiiTmp[0],
|
||||
cornerRadiiTmp[1], cornerRadiiTmp[1],
|
||||
cornerRadiiTmp[2], cornerRadiiTmp[2],
|
||||
cornerRadiiTmp[3], cornerRadiiTmp[3],
|
||||
Batch2D.CORNER_RADII[0], Batch2D.CORNER_RADII[0],
|
||||
Batch2D.CORNER_RADII[1], Batch2D.CORNER_RADII[1],
|
||||
Batch2D.CORNER_RADII[2], Batch2D.CORNER_RADII[2],
|
||||
Batch2D.CORNER_RADII[3], Batch2D.CORNER_RADII[3],
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
|
|
@ -1118,7 +1134,7 @@ public final class Renderer2D {
|
|||
double chamferBRX, double chamferBRY,
|
||||
double chamferBLX, double chamferBLY,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
Renderer2DChamfered.chamferedRectQuadAxes(this, x, y, w, h,
|
||||
Chamfered2D.chamferedRectQuadAxes(this, x, y, w, h,
|
||||
chamferTLX, chamferTLY, chamferTRX, chamferTRY, chamferBRX, chamferBRY, chamferBLX, chamferBLY,
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
|
@ -1135,7 +1151,7 @@ public final class Renderer2D {
|
|||
double chamferTL, double chamferTR, double chamferBR, double chamferBL,
|
||||
double thickness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
Renderer2DChamfered.chamferedRectStrokeQuad(this, x, y, w, h,
|
||||
Chamfered2D.chamferedRectStrokeQuad(this, x, y, w, h,
|
||||
chamferTL, chamferTR, chamferBR, chamferBL, thickness,
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
|
@ -1161,35 +1177,35 @@ public final class Renderer2D {
|
|||
}
|
||||
|
||||
public void notchedRect(double x, double y, double width, double height,
|
||||
double notchWidth, double notchDepth, int argb) {
|
||||
int count = buildNotchedRect(rectShapeTmp, x, y, width, height, notchWidth, notchDepth);
|
||||
Renderer2DPolygons.polygon(this, rectShapeTmp, count, argb);
|
||||
double notchWidth, double notchDepth, int argb) {
|
||||
int count = buildNotchedRect(Batch2D.RECT_SHAPE, x, y, width, height, notchWidth, notchDepth);
|
||||
Polygons2D.polygon(this, Batch2D.RECT_SHAPE, count, argb);
|
||||
}
|
||||
|
||||
public void notchedRectGradient(double x, double y, double width, double height,
|
||||
double notchWidth, double notchDepth,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx) {
|
||||
int count = buildNotchedRect(rectShapeTmp, x, y, width, height, notchWidth, notchDepth);
|
||||
Renderer2DPolygons.polygonGradient(this, rectShapeTmp, count, x, y, width, height, startArgb, endArgb, angleDeg, offsetPx);
|
||||
int count = buildNotchedRect(Batch2D.RECT_SHAPE, x, y, width, height, notchWidth, notchDepth);
|
||||
Polygons2D.polygonGradient(this, Batch2D.RECT_SHAPE, count, x, y, width, height, startArgb, endArgb, angleDeg, offsetPx);
|
||||
}
|
||||
|
||||
public void notchedRectStroke(double x, double y, double width, double height,
|
||||
double notchWidth, double notchDepth, double thickness, int argb) {
|
||||
int count = buildNotchedRect(rectShapeTmp, x, y, width, height, notchWidth, notchDepth);
|
||||
Renderer2DPolygons.polygonStroke(this, rectShapeTmp, count, thickness, true, argb);
|
||||
int count = buildNotchedRect(Batch2D.RECT_SHAPE, x, y, width, height, notchWidth, notchDepth);
|
||||
Polygons2D.polygonStroke(this, Batch2D.RECT_SHAPE, count, thickness, true, argb);
|
||||
}
|
||||
|
||||
public void notchedRectStrokeGradient(double x, double y, double width, double height,
|
||||
double notchWidth, double notchDepth, double thickness,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx) {
|
||||
int count = buildNotchedRect(rectShapeTmp, x, y, width, height, notchWidth, notchDepth);
|
||||
Renderer2DPolygons.polylineLinearGradient(this, rectShapeTmp, count, thickness, true,
|
||||
int count = buildNotchedRect(Batch2D.RECT_SHAPE, x, y, width, height, notchWidth, notchDepth);
|
||||
Polygons2D.polylineLinearGradient(this, Batch2D.RECT_SHAPE, count, thickness, true,
|
||||
x, y, width, height, startArgb, endArgb, angleDeg, offsetPx, false);
|
||||
}
|
||||
|
||||
public void beveledRect(double x, double y, double width, double height,
|
||||
double bevel, int fillArgb, int highlightArgb, int shadowArgb) {
|
||||
Renderer2DChamfered.beveledRect(this, x, y, width, height, bevel, fillArgb, highlightArgb, shadowArgb);
|
||||
Chamfered2D.beveledRect(this, x, y, width, height, bevel, fillArgb, highlightArgb, shadowArgb);
|
||||
}
|
||||
|
||||
public void beveledRectStroke(double x, double y, double width, double height,
|
||||
|
|
@ -1200,7 +1216,7 @@ public final class Renderer2D {
|
|||
public void beveledRectGradient(double x, double y, double width, double height,
|
||||
double bevel, int startArgb, int endArgb,
|
||||
int highlightArgb, int shadowArgb, float angleDeg, float offsetPx) {
|
||||
Renderer2DChamfered.beveledRectGradient(this, x, y, width, height, bevel, startArgb, endArgb,
|
||||
Chamfered2D.beveledRectGradient(this, x, y, width, height, bevel, startArgb, endArgb,
|
||||
highlightArgb, shadowArgb, angleDeg, offsetPx);
|
||||
}
|
||||
|
||||
|
|
@ -1211,12 +1227,12 @@ public final class Renderer2D {
|
|||
}
|
||||
|
||||
public void connector(double x1, double y1, double x2, double y2, double thickness, int argb) {
|
||||
Renderer2DConnectors.connector(this, x1, y1, x2, y2, thickness, argb);
|
||||
Connectors2D.connector(this, x1, y1, x2, y2, thickness, argb);
|
||||
}
|
||||
|
||||
public void connectorGradient(double x1, double y1, double x2, double y2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
Renderer2DConnectors.connectorGradient(this, x1, y1, x2, y2, thickness, startArgb, endArgb);
|
||||
Connectors2D.connectorGradient(this, x1, y1, x2, y2, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
public void wire(double x1, double y1, double x2, double y2, double thickness, int argb) {
|
||||
|
|
@ -1248,13 +1264,13 @@ public final class Renderer2D {
|
|||
public void roundedRectConnector(double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int argb) {
|
||||
Renderer2DConnectors.roundedRectConnector(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2, thickness, argb);
|
||||
Connectors2D.roundedRectConnector(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2, thickness, argb);
|
||||
}
|
||||
|
||||
public void roundedRectConnectorGradient(double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
Renderer2DConnectors.roundedRectConnectorGradient(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2,
|
||||
Connectors2D.roundedRectConnectorGradient(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2,
|
||||
thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
|
|
@ -1264,13 +1280,13 @@ public final class Renderer2D {
|
|||
public void roundedRectNodeGraphEdge(double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int argb) {
|
||||
Renderer2DConnectors.roundedRectNodeGraphEdge(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2, thickness, argb);
|
||||
Connectors2D.roundedRectNodeGraphEdge(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2, thickness, argb);
|
||||
}
|
||||
|
||||
public void roundedRectNodeGraphEdgeGradient(double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
Renderer2DConnectors.roundedRectNodeGraphEdgeGradient(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2,
|
||||
Connectors2D.roundedRectNodeGraphEdgeGradient(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2,
|
||||
thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
|
|
@ -1290,14 +1306,14 @@ public final class Renderer2D {
|
|||
public void roundedRectOrthogonalConnector(double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double midX, double thickness, int argb) {
|
||||
Renderer2DConnectors.roundedRectOrthogonalConnector(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2,
|
||||
Connectors2D.roundedRectOrthogonalConnector(this, x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2,
|
||||
midX, thickness, argb);
|
||||
}
|
||||
|
||||
public void roundedRectOrthogonalConnectorGradient(double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double midX, double thickness, int startArgb, int endArgb) {
|
||||
Renderer2DConnectors.roundedRectOrthogonalConnectorGradient(this, x1, y1, w1, h1, radius1,
|
||||
Connectors2D.roundedRectOrthogonalConnectorGradient(this, x1, y1, w1, h1, radius1,
|
||||
x2, y2, w2, h2, radius2, midX, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
|
|
@ -1308,12 +1324,12 @@ public final class Renderer2D {
|
|||
|
||||
public void orthogonalConnector(double x1, double y1, double x2, double y2,
|
||||
double midX, double thickness, int argb) {
|
||||
Renderer2DConnectors.orthogonalConnector(this, x1, y1, x2, y2, midX, thickness, argb);
|
||||
Connectors2D.orthogonalConnector(this, x1, y1, x2, y2, midX, thickness, argb);
|
||||
}
|
||||
|
||||
public void orthogonalConnectorGradient(double x1, double y1, double x2, double y2,
|
||||
double midX, double thickness, int startArgb, int endArgb) {
|
||||
Renderer2DConnectors.orthogonalConnectorGradient(this, x1, y1, x2, y2, midX, thickness, startArgb, endArgb);
|
||||
Connectors2D.orthogonalConnectorGradient(this, x1, y1, x2, y2, midX, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
public void bezierConnector(double x1, double y1,
|
||||
|
|
@ -1332,7 +1348,7 @@ public final class Renderer2D {
|
|||
int segments,
|
||||
double thickness,
|
||||
int argb) {
|
||||
Renderer2DConnectors.bezierConnector(this, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments, thickness, argb);
|
||||
Connectors2D.bezierConnector(this, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments, thickness, argb);
|
||||
}
|
||||
|
||||
public void bezierConnectorGradient(double x1, double y1,
|
||||
|
|
@ -1343,33 +1359,33 @@ public final class Renderer2D {
|
|||
double thickness,
|
||||
int startArgb,
|
||||
int endArgb) {
|
||||
Renderer2DConnectors.bezierConnectorGradient(this, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments, thickness, startArgb, endArgb);
|
||||
Connectors2D.bezierConnectorGradient(this, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
public void nodeGraphEdge(double x1, double y1, double x2, double y2, double thickness, int argb) {
|
||||
Renderer2DConnectors.nodeGraphEdge(this, x1, y1, x2, y2, thickness, argb);
|
||||
Connectors2D.nodeGraphEdge(this, x1, y1, x2, y2, thickness, argb);
|
||||
}
|
||||
|
||||
public void nodeGraphEdgeGradient(double x1, double y1, double x2, double y2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
Renderer2DConnectors.nodeGraphEdgeGradient(this, x1, y1, x2, y2, thickness, startArgb, endArgb);
|
||||
Connectors2D.nodeGraphEdgeGradient(this, x1, y1, x2, y2, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
public void spline(double[] points, int pointCount, double thickness, boolean closed, int argb) {
|
||||
if (points == null || pointCount < 2) return;
|
||||
Renderer2DPolygons.polyline(this, points, pointCount, thickness, closed, argb, true);
|
||||
Polygons2D.polyline(this, points, pointCount, thickness, closed, argb, true);
|
||||
}
|
||||
|
||||
public void splineGradient(double[] points, int pointCount, double thickness, boolean closed,
|
||||
int startArgb, int endArgb) {
|
||||
if (points == null || pointCount < 2) return;
|
||||
Renderer2DPolygons.polylineGradient(this, points, pointCount, thickness, closed, startArgb, endArgb, true);
|
||||
Polygons2D.polylineGradient(this, points, pointCount, thickness, closed, startArgb, endArgb, true);
|
||||
}
|
||||
|
||||
public void texQuad(double x, double y, double width, double height,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb) {
|
||||
Renderer2DTextures.texQuad(this, x, y, width, height, texX1, texY1, texX2, texY2, argb);
|
||||
Textures2D.texQuad(this, x, y, width, height, texX1, texY1, texX2, texY2, argb);
|
||||
}
|
||||
|
||||
public void liquidGlassCircle(double cx, double cy, double radius,
|
||||
|
|
@ -1745,31 +1761,31 @@ public final class Renderer2D {
|
|||
float fresnelMix,
|
||||
float distortPx,
|
||||
float squirclePower) {
|
||||
Renderer2DLiquidGlass.liquidGlassRectCorners(this, x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL, softness,
|
||||
Glass2D.liquidGlassRectCorners(this, x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL, softness,
|
||||
tintArgb, glassAlpha, blurAlpha, fresnelPower, fresnelAlpha, baseAlpha, fresnelMix, distortPx, squirclePower);
|
||||
}
|
||||
|
||||
public void blurRect(double x, double y, double w, double h,
|
||||
float radius, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
Renderer2DBlur.blurRect(this, x, y, w, h, radius, quality, brightness, alpha, ignoredTintRgb, UiBatchType.BLUR);
|
||||
Blur2D.blurRect(this, x, y, w, h, radius, quality, brightness, alpha, ignoredTintRgb);
|
||||
}
|
||||
|
||||
public void glassBlurRect(double x, double y, double w, double h,
|
||||
float radius, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
Renderer2DBlur.glassBlurRect(this, x, y, w, h, radius, quality, brightness, alpha, ignoredTintRgb);
|
||||
Blur2D.glassBlurRect(this, x, y, w, h, radius, quality, brightness, alpha, ignoredTintRgb);
|
||||
}
|
||||
|
||||
public void blurChamferedRect(double x, double y, double w, double h,
|
||||
double chamfer, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
Renderer2DBlur.blurChamferedRect(this, x, y, w, h, chamfer, quality, brightness, alpha, ignoredTintRgb);
|
||||
Blur2D.blurChamferedRect(this, x, y, w, h, chamfer, quality, brightness, alpha, ignoredTintRgb);
|
||||
}
|
||||
|
||||
public void blurComposite(Consumer<BlurComposite> consumer) {
|
||||
Renderer2DBlur.blurComposite(this, consumer);
|
||||
Blur2D.blurComposite(this, consumer);
|
||||
}
|
||||
|
||||
public void polygon(double[] points, int pointCount, int argb) {
|
||||
Renderer2DPolygons.polygon(this, points, pointCount, argb);
|
||||
Polygons2D.polygon(this, points, pointCount, argb);
|
||||
}
|
||||
|
||||
// ---- Package-private plumbing for helpers in the same package ------------
|
||||
|
|
@ -2077,7 +2093,7 @@ public final class Renderer2D {
|
|||
private final MeshBuilder mesh;
|
||||
private final float globalAlpha;
|
||||
|
||||
BlurComposite(MeshBuilder mesh, float globalAlpha) {
|
||||
public BlurComposite(MeshBuilder mesh, float globalAlpha) {
|
||||
this.mesh = mesh;
|
||||
this.globalAlpha = globalAlpha;
|
||||
}
|
||||
|
|
@ -2132,7 +2148,7 @@ public final class Renderer2D {
|
|||
public static final class QuadBatch {
|
||||
private final MeshBuilder mesh;
|
||||
|
||||
QuadBatch(MeshBuilder mesh) {
|
||||
public QuadBatch(MeshBuilder mesh) {
|
||||
this.mesh = mesh;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.textures.GpuSampler;
|
||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||
import dev.loki.lovisual.render.engine.postprocess.PostProcessManager;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.BlurSource;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiEffectSpec;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clamp01;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampChamfer;
|
||||
|
||||
final class Renderer2DBlur {
|
||||
private Renderer2DBlur() {
|
||||
}
|
||||
|
||||
static Renderer2D.BlurQuality legacyBlurQuality(float legacyQuality) {
|
||||
if (legacyQuality >= 32.0f) return Renderer2D.BlurQuality.ULTRA;
|
||||
if (legacyQuality >= 12.0f) return Renderer2D.BlurQuality.HIGH;
|
||||
if (legacyQuality >= 5.0f) return Renderer2D.BlurQuality.MEDIUM;
|
||||
return Renderer2D.BlurQuality.LOW;
|
||||
}
|
||||
|
||||
static float legacyKawaseOffset(float legacyQuality) {
|
||||
if (!Float.isFinite(legacyQuality)) return Renderer2D.DEFAULT_KAWASE_OFFSET_PX;
|
||||
return Mth.clamp(legacyQuality / 18.0f, 0.85f, 3.25f);
|
||||
}
|
||||
|
||||
static BlurSource getBlurSource(Renderer2D r) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return null;
|
||||
RenderTarget fb = mc.gameRenderer.mainRenderTarget();
|
||||
if (fb == null) return null;
|
||||
GpuTextureView src = fb.getColorTextureView();
|
||||
if (src == null) return null;
|
||||
GpuSampler sampler = PostProcessManager.getSampler();
|
||||
if (sampler == null) return null;
|
||||
return new BlurSource(src, sampler);
|
||||
}
|
||||
|
||||
static void blurRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float quality, float brightness, float alpha, int ignoredTintRgb,
|
||||
UiBatchType batchType) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
if (alpha <= 0.001f) return;
|
||||
|
||||
r.effect(UiEffectSpec.blur(UiShape.roundedRect(x, y, w, h, radius), radius, ignoredTintRgb));
|
||||
BlurSource source = getBlurSource(r);
|
||||
if (source == null) return;
|
||||
|
||||
boolean auto = r.beginAutoBatch();
|
||||
try {
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreateBlur(batchType, source.view, source.sampler,
|
||||
legacyBlurQuality(quality), legacyKawaseOffset(quality));
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = 1.0;
|
||||
|
||||
float finalAlpha = (float) (alpha * r.alpha);
|
||||
mesh.ensureQuadCapacity();
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
} finally {
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
||||
static void glassBlurRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
if (alpha <= 0.001f) return;
|
||||
|
||||
r.effect(UiEffectSpec.blur(UiShape.roundedRect(x, y, w, h, radius), radius, ignoredTintRgb));
|
||||
BlurSource source = getBlurSource(r);
|
||||
if (source == null) return;
|
||||
|
||||
boolean auto = r.beginAutoBatch();
|
||||
try {
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreateBlur(UiBatchType.GLASS_BLUR, source.view, source.sampler,
|
||||
Renderer2D.BlurQuality.HIGH, legacyKawaseOffset(quality));
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = 1.0;
|
||||
|
||||
float finalAlpha = clamp01((float) (alpha * r.alpha));
|
||||
if (finalAlpha <= 0.001f) return;
|
||||
|
||||
int a = Math.max(0, Math.min(255, Math.round(finalAlpha * 255.0f)));
|
||||
float smoothness = 2.0f;
|
||||
float rad = Math.max(0.0f, radius);
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
} finally {
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
||||
static void blurChamferedRect(Renderer2D r, double x, double y, double w, double h,
|
||||
double chamfer, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
if (alpha <= 0.001f) return;
|
||||
float cut = clampChamfer(chamfer, w, h);
|
||||
if (cut <= 0.0f) {
|
||||
r.blurRect(x, y, w, h, 0.0f, quality, brightness, alpha, ignoredTintRgb);
|
||||
return;
|
||||
}
|
||||
r.blurRect(x, y, w, h, -cut, quality, brightness, alpha, ignoredTintRgb);
|
||||
}
|
||||
|
||||
static void blurComposite(Renderer2D r, Consumer<Renderer2D.BlurComposite> consumer) {
|
||||
if (consumer == null) return;
|
||||
|
||||
BlurSource source = getBlurSource(r);
|
||||
if (source == null) return;
|
||||
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreateBlur(UiBatchType.BLUR_COMPLEX, source.view, source.sampler,
|
||||
Renderer2D.DEFAULT_BLUR_QUALITY, Renderer2D.DEFAULT_KAWASE_OFFSET_PX);
|
||||
if (batch == null) return;
|
||||
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = 1.0;
|
||||
|
||||
try {
|
||||
consumer.accept(new Renderer2D.BlurComposite(mesh, (float) r.alpha));
|
||||
} finally {
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampChamferAxis;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeChamfers;
|
||||
|
||||
final class Renderer2DChamfered {
|
||||
private Renderer2DChamfered() {
|
||||
}
|
||||
|
||||
static void chamferedRectQuadAxes(Renderer2D r, double x, double y, double w, double h,
|
||||
double chamferTLX, double chamferTLY,
|
||||
double chamferTRX, double chamferTRY,
|
||||
double chamferBRX, double chamferBRY,
|
||||
double chamferBLX, double chamferBLY,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shape(UiShape.chamferedRectAxes(x, y, w, h, chamferTLX, chamferTLY, chamferTRX, chamferTRY, chamferBRX, chamferBRY, chamferBLX, chamferBLY),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.CHAMFERED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
int tlR = (cTopLeft >>> 16) & 0xFF;
|
||||
int tlG = (cTopLeft >>> 8) & 0xFF;
|
||||
int tlB = cTopLeft & 0xFF;
|
||||
int trA = (cTopRight >>> 24) & 0xFF;
|
||||
int trR = (cTopRight >>> 16) & 0xFF;
|
||||
int trG = (cTopRight >>> 8) & 0xFF;
|
||||
int trB = cTopRight & 0xFF;
|
||||
int brA = (cBottomRight >>> 24) & 0xFF;
|
||||
int brR = (cBottomRight >>> 16) & 0xFF;
|
||||
int brG = (cBottomRight >>> 8) & 0xFF;
|
||||
int brB = cBottomRight & 0xFF;
|
||||
int blA = (cBottomLeft >>> 24) & 0xFF;
|
||||
int blR = (cBottomLeft >>> 16) & 0xFF;
|
||||
int blG = (cBottomLeft >>> 8) & 0xFF;
|
||||
int blB = cBottomLeft & 0xFF;
|
||||
|
||||
float tlX = clampChamferAxis(chamferTLX, w);
|
||||
float trX = clampChamferAxis(chamferTRX, w);
|
||||
float brX = clampChamferAxis(chamferBRX, w);
|
||||
float blX = clampChamferAxis(chamferBLX, w);
|
||||
float tlY = clampChamferAxis(chamferTLY, h);
|
||||
float trY = clampChamferAxis(chamferTRY, h);
|
||||
float brY = clampChamferAxis(chamferBRY, h);
|
||||
float blY = clampChamferAxis(chamferBLY, h);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void chamferedRectStrokeQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
double chamferTL, double chamferTR, double chamferBR, double chamferBL,
|
||||
double thickness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shapeStroke(UiShape.chamferedRect(x, y, w, h, chamferTL, chamferTR, chamferBR, chamferBL),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft), UiStroke.of(thickness));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.CHAMFERED_STROKE, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
int tlR = (cTopLeft >>> 16) & 0xFF;
|
||||
int tlG = (cTopLeft >>> 8) & 0xFF;
|
||||
int tlB = cTopLeft & 0xFF;
|
||||
int trA = (cTopRight >>> 24) & 0xFF;
|
||||
int trR = (cTopRight >>> 16) & 0xFF;
|
||||
int trG = (cTopRight >>> 8) & 0xFF;
|
||||
int trB = cTopRight & 0xFF;
|
||||
int brA = (cBottomRight >>> 24) & 0xFF;
|
||||
int brR = (cBottomRight >>> 16) & 0xFF;
|
||||
int brG = (cBottomRight >>> 8) & 0xFF;
|
||||
int brB = cBottomRight & 0xFF;
|
||||
int blA = (cBottomLeft >>> 24) & 0xFF;
|
||||
int blR = (cBottomLeft >>> 16) & 0xFF;
|
||||
int blG = (cBottomLeft >>> 8) & 0xFF;
|
||||
int blB = cBottomLeft & 0xFF;
|
||||
float[] cornerRadiiTmp = r.cornerRadiiTmp;
|
||||
normalizeChamfers(w, h,
|
||||
(float) chamferTL,
|
||||
(float) chamferTR,
|
||||
(float) chamferBR,
|
||||
(float) chamferBL,
|
||||
cornerRadiiTmp);
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void beveledRect(Renderer2D r, double x, double y, double width, double height,
|
||||
double bevel, int fillArgb, int highlightArgb, int shadowArgb) {
|
||||
r.chamferedRect(x, y, width, height, bevel, fillArgb);
|
||||
double t = Math.max(1.0, Math.min(Math.min(Math.abs(width), Math.abs(height)) * 0.25, bevel));
|
||||
double[] connectorTmp = r.connectorTmp;
|
||||
connectorTmp[0] = x + bevel;
|
||||
connectorTmp[1] = y;
|
||||
connectorTmp[2] = x + width - bevel;
|
||||
connectorTmp[3] = y;
|
||||
connectorTmp[4] = x + width;
|
||||
connectorTmp[5] = y + bevel;
|
||||
Renderer2DPolygons.polyline(r, connectorTmp, 3, t, false, highlightArgb, false);
|
||||
connectorTmp[0] = x + width;
|
||||
connectorTmp[1] = y + height - bevel;
|
||||
connectorTmp[2] = x + width - bevel;
|
||||
connectorTmp[3] = y + height;
|
||||
connectorTmp[4] = x + bevel;
|
||||
connectorTmp[5] = y + height;
|
||||
connectorTmp[6] = x;
|
||||
connectorTmp[7] = y + height - bevel;
|
||||
Renderer2DPolygons.polyline(r, connectorTmp, 4, t, false, shadowArgb, false);
|
||||
}
|
||||
|
||||
static void beveledRectGradient(Renderer2D r, double x, double y, double width, double height,
|
||||
double bevel, int startArgb, int endArgb,
|
||||
int highlightArgb, int shadowArgb, float angleDeg, float offsetPx) {
|
||||
r.chamferedRectGradient(x, y, width, height, bevel, startArgb, endArgb, angleDeg, offsetPx);
|
||||
double t = Math.max(1.0, Math.min(Math.min(Math.abs(width), Math.abs(height)) * 0.25, bevel));
|
||||
double[] connectorTmp = r.connectorTmp;
|
||||
connectorTmp[0] = x + bevel;
|
||||
connectorTmp[1] = y;
|
||||
connectorTmp[2] = x + width - bevel;
|
||||
connectorTmp[3] = y;
|
||||
connectorTmp[4] = x + width;
|
||||
connectorTmp[5] = y + bevel;
|
||||
Renderer2DPolygons.polyline(r, connectorTmp, 3, t, false, highlightArgb, false);
|
||||
connectorTmp[0] = x + width;
|
||||
connectorTmp[1] = y + height - bevel;
|
||||
connectorTmp[2] = x + width - bevel;
|
||||
connectorTmp[3] = y + height;
|
||||
connectorTmp[4] = x + bevel;
|
||||
connectorTmp[5] = y + height;
|
||||
connectorTmp[6] = x;
|
||||
connectorTmp[7] = y + height - bevel;
|
||||
Renderer2DPolygons.polyline(r, connectorTmp, 4, t, false, shadowArgb, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeDegrees;
|
||||
|
||||
final class Renderer2DCircles {
|
||||
private Renderer2DCircles() {
|
||||
}
|
||||
|
||||
static void circle(Renderer2D r, double cx, double cy, double radius, float softness, int argb) {
|
||||
r.shape(UiShape.circle(cx, cy, radius), UiPaint.solid(argb));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.CIRCLE, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
|
||||
double outerRadius = radius + Math.max(0.0f, softness);
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double w = outerRadius * 2.0;
|
||||
double h = outerRadius * 2.0;
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void circleStroke(Renderer2D r, double cx, double cy, double radius, double thickness, float softness, int argb) {
|
||||
r.shapeStroke(UiShape.circle(cx, cy, radius), UiPaint.solid(argb), UiStroke.of(thickness));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.CIRCLE, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
double outerRadius = radius + stroke * 0.5 + softness;
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double w = outerRadius * 2.0;
|
||||
double h = outerRadius * 2.0;
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, stroke, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, stroke, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, stroke, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4((float) radius, softness, stroke, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void arcStrokeQuad(Renderer2D r, double cx, double cy, double radius, double thickness,
|
||||
float startAngleDeg, float endAngleDeg, float softness, boolean caps,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
float start = normalizeDegrees(startAngleDeg);
|
||||
float end = normalizeDegrees(endAngleDeg);
|
||||
float sweep = end - start;
|
||||
if (sweep <= 0.0f) {
|
||||
sweep += 360.0f;
|
||||
}
|
||||
if (sweep <= 0.001f) {
|
||||
return;
|
||||
}
|
||||
|
||||
r.shapeStroke(UiShape.arc(cx, cy, radius, start, end), UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft), UiStroke.of(thickness));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ARC, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
double outerRadius = radius + stroke * 0.5 + softness;
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double w = outerRadius * 2.0;
|
||||
double h = outerRadius * 2.0;
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
int tlR = (cTopLeft >>> 16) & 0xFF;
|
||||
int tlG = (cTopLeft >>> 8) & 0xFF;
|
||||
int tlB = cTopLeft & 0xFF;
|
||||
int trA = (cTopRight >>> 24) & 0xFF;
|
||||
int trR = (cTopRight >>> 16) & 0xFF;
|
||||
int trG = (cTopRight >>> 8) & 0xFF;
|
||||
int trB = cTopRight & 0xFF;
|
||||
int brA = (cBottomRight >>> 24) & 0xFF;
|
||||
int brR = (cBottomRight >>> 16) & 0xFF;
|
||||
int brG = (cBottomRight >>> 8) & 0xFF;
|
||||
int brB = cBottomRight & 0xFF;
|
||||
int blA = (cBottomLeft >>> 24) & 0xFF;
|
||||
int blR = (cBottomLeft >>> 16) & 0xFF;
|
||||
int blG = (cBottomLeft >>> 8) & 0xFF;
|
||||
int blB = cBottomLeft & 0xFF;
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(x, y, w, h)
|
||||
.vec4((float) radius, softness, stroke, start).vec4(end, caps ? 1f : 0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(x, y, w, h)
|
||||
.vec4((float) radius, softness, stroke, start).vec4(end, caps ? 1f : 0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h)
|
||||
.vec4((float) radius, softness, stroke, start).vec4(end, caps ? 1f : 0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h)
|
||||
.vec4((float) radius, softness, stroke, start).vec4(end, caps ? 1f : 0f, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.buildBezier;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.roundedRectAnchor;
|
||||
|
||||
final class Renderer2DConnectors {
|
||||
private Renderer2DConnectors() {
|
||||
}
|
||||
|
||||
static void connector(Renderer2D r, double x1, double y1, double x2, double y2, double thickness, int argb) {
|
||||
double[] c = r.connectorTmp;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = x2;
|
||||
c[3] = y2;
|
||||
Renderer2DPolygons.polyline(r, c, 2, thickness, false, argb, true);
|
||||
}
|
||||
|
||||
static void connectorGradient(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double[] c = r.connectorTmp;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = x2;
|
||||
c[3] = y2;
|
||||
Renderer2DPolygons.polylineGradient(r, c, 2, thickness, false, startArgb, endArgb, true);
|
||||
}
|
||||
|
||||
static void orthogonalConnector(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double midX, double thickness, int argb) {
|
||||
double[] c = r.connectorTmp;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = midX;
|
||||
c[3] = y1;
|
||||
c[4] = midX;
|
||||
c[5] = y2;
|
||||
c[6] = x2;
|
||||
c[7] = y2;
|
||||
Renderer2DPolygons.polyline(r, c, 4, thickness, false, argb, true);
|
||||
}
|
||||
|
||||
static void orthogonalConnectorGradient(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double midX, double thickness, int startArgb, int endArgb) {
|
||||
double[] c = r.connectorTmp;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = midX;
|
||||
c[3] = y1;
|
||||
c[4] = midX;
|
||||
c[5] = y2;
|
||||
c[6] = x2;
|
||||
c[7] = y2;
|
||||
Renderer2DPolygons.polylineGradient(r, c, 4, thickness, false, startArgb, endArgb, true);
|
||||
}
|
||||
|
||||
static void bezierConnector(Renderer2D r, double x1, double y1,
|
||||
double cx1, double cy1,
|
||||
double cx2, double cy2,
|
||||
double x2, double y2,
|
||||
int segments,
|
||||
double thickness,
|
||||
int argb) {
|
||||
double[] c = r.connectorTmp;
|
||||
int count = buildBezier(c, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments);
|
||||
Renderer2DPolygons.polyline(r, c, count, thickness, false, argb, false);
|
||||
}
|
||||
|
||||
static void bezierConnectorGradient(Renderer2D r, double x1, double y1,
|
||||
double cx1, double cy1,
|
||||
double cx2, double cy2,
|
||||
double x2, double y2,
|
||||
int segments,
|
||||
double thickness,
|
||||
int startArgb,
|
||||
int endArgb) {
|
||||
double[] c = r.connectorTmp;
|
||||
int count = buildBezier(c, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments);
|
||||
Renderer2DPolygons.polylineGradient(r, c, count, thickness, false, startArgb, endArgb, false);
|
||||
}
|
||||
|
||||
static void nodeGraphEdge(Renderer2D r, double x1, double y1, double x2, double y2, double thickness, int argb) {
|
||||
double dx = Math.abs(x2 - x1);
|
||||
double c = Math.max(24.0, dx * 0.5);
|
||||
bezierConnector(r, x1, y1, x1 + c, y1, x2 - c, y2, x2, y2, 20, thickness, argb);
|
||||
}
|
||||
|
||||
static void nodeGraphEdgeGradient(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double dx = Math.abs(x2 - x1);
|
||||
double c = Math.max(24.0, dx * 0.5);
|
||||
bezierConnectorGradient(r, x1, y1, x1 + c, y1, x2 - c, y2, x2, y2, 20, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
static void roundedRectConnector(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int argb) {
|
||||
double[] a = r.connectorAnchorTmp;
|
||||
roundedRectAnchor(a, 0, x1, y1, w1, h1, radius1, x2 + w2 * 0.5, y2 + h2 * 0.5);
|
||||
roundedRectAnchor(a, 2, x2, y2, w2, h2, radius2, x1 + w1 * 0.5, y1 + h1 * 0.5);
|
||||
connector(r, a[0], a[1], a[2], a[3], thickness, argb);
|
||||
}
|
||||
|
||||
static void roundedRectConnectorGradient(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double[] a = r.connectorAnchorTmp;
|
||||
roundedRectAnchor(a, 0, x1, y1, w1, h1, radius1, x2 + w2 * 0.5, y2 + h2 * 0.5);
|
||||
roundedRectAnchor(a, 2, x2, y2, w2, h2, radius2, x1 + w1 * 0.5, y1 + h1 * 0.5);
|
||||
connectorGradient(r, a[0], a[1], a[2], a[3], thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
static void roundedRectNodeGraphEdge(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int argb) {
|
||||
double[] a = r.connectorAnchorTmp;
|
||||
roundedRectAnchor(a, 0, x1, y1, w1, h1, radius1, x2 + w2 * 0.5, y2 + h2 * 0.5);
|
||||
roundedRectAnchor(a, 2, x2, y2, w2, h2, radius2, x1 + w1 * 0.5, y1 + h1 * 0.5);
|
||||
nodeGraphEdge(r, a[0], a[1], a[2], a[3], thickness, argb);
|
||||
}
|
||||
|
||||
static void roundedRectNodeGraphEdgeGradient(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double[] a = r.connectorAnchorTmp;
|
||||
roundedRectAnchor(a, 0, x1, y1, w1, h1, radius1, x2 + w2 * 0.5, y2 + h2 * 0.5);
|
||||
roundedRectAnchor(a, 2, x2, y2, w2, h2, radius2, x1 + w1 * 0.5, y1 + h1 * 0.5);
|
||||
nodeGraphEdgeGradient(r, a[0], a[1], a[2], a[3], thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
static void roundedRectOrthogonalConnector(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double midX, double thickness, int argb) {
|
||||
double[] a = r.connectorAnchorTmp;
|
||||
roundedRectAnchor(a, 0, x1, y1, w1, h1, radius1, x2 + w2 * 0.5, y2 + h2 * 0.5);
|
||||
roundedRectAnchor(a, 2, x2, y2, w2, h2, radius2, x1 + w1 * 0.5, y1 + h1 * 0.5);
|
||||
orthogonalConnector(r, a[0], a[1], a[2], a[3], midX, thickness, argb);
|
||||
}
|
||||
|
||||
static void roundedRectOrthogonalConnectorGradient(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double midX, double thickness, int startArgb, int endArgb) {
|
||||
double[] a = r.connectorAnchorTmp;
|
||||
roundedRectAnchor(a, 0, x1, y1, w1, h1, radius1, x2 + w2 * 0.5, y2 + h2 * 0.5);
|
||||
roundedRectAnchor(a, 2, x2, y2, w2, h2, radius2, x1 + w1 * 0.5, y1 + h1 * 0.5);
|
||||
orthogonalConnectorGradient(r, a[0], a[1], a[2], a[3], midX, thickness, startArgb, endArgb);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiItemSubmission;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
final class Renderer2DItems {
|
||||
private Renderer2DItems() {
|
||||
}
|
||||
|
||||
static void submit(Renderer2D r,
|
||||
@Nullable LocalPlayer player,
|
||||
ItemStack stack,
|
||||
double x,
|
||||
double y,
|
||||
float scaleX,
|
||||
float scaleY,
|
||||
float pivotX,
|
||||
float pivotY,
|
||||
boolean pivoted,
|
||||
int seed,
|
||||
boolean drawItem,
|
||||
int overlayFlags,
|
||||
@Nullable String stackCountText,
|
||||
int durabilityThresholdPercent,
|
||||
int durabilityTextColorThresholdPercent) {
|
||||
UiItemSubmission.submit(
|
||||
r.textured,
|
||||
r.alpha,
|
||||
player,
|
||||
stack,
|
||||
x,
|
||||
y,
|
||||
scaleX,
|
||||
scaleY,
|
||||
pivotX,
|
||||
pivotY,
|
||||
pivoted,
|
||||
seed,
|
||||
drawItem,
|
||||
overlayFlags,
|
||||
stackCountText,
|
||||
durabilityThresholdPercent,
|
||||
durabilityTextColorThresholdPercent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import dev.loki.lovisual.render.engine.color.RenderColor;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
final class Renderer2DLines {
|
||||
private Renderer2DLines() {
|
||||
}
|
||||
|
||||
static void line(Renderer2D r, double x1, double y1, double x2, double y2, int argb) {
|
||||
double[] p = {x1, y1, x2, y2};
|
||||
r.path(UiShape.polyline(p, 2, false), UiPaint.solid(argb), UiStroke.of(1.0), false);
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.LINES, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureLineCapacity();
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
int i1 = mesh.vec2(x1, y1).color(red, green, blue, a).next();
|
||||
int i2 = mesh.vec2(x2, y2).color(red, green, blue, a).next();
|
||||
mesh.line(i1, i2);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void line(Renderer2D r, double x1, double y1, double x2, double y2, RenderColor color) {
|
||||
double[] p = {x1, y1, x2, y2};
|
||||
r.path(UiShape.polyline(p, 2, false), UiPaint.solid(color.argb()), UiStroke.of(1.0), false);
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.LINES, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureLineCapacity();
|
||||
int i1 = mesh.vec2(x1, y1).color(color.r, color.g, color.b, color.a).next();
|
||||
int i2 = mesh.vec2(x2, y2).color(color.r, color.g, color.b, color.a).next();
|
||||
mesh.line(i1, i2);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void boxLines(Renderer2D r, double x, double y, double width, double height, int argb) {
|
||||
r.shapeStroke(UiShape.rect(x, y, width, height), UiPaint.solid(argb), UiStroke.of(1.0));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.LINES, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureCapacity(4, 8);
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
int i1 = mesh.vec2(x, y).color(red, green, blue, a).next();
|
||||
int i2 = mesh.vec2(x, y + height).color(red, green, blue, a).next();
|
||||
int i3 = mesh.vec2(x + width, y + height).color(red, green, blue, a).next();
|
||||
int i4 = mesh.vec2(x + width, y).color(red, green, blue, a).next();
|
||||
|
||||
mesh.line(i1, i2);
|
||||
mesh.line(i2, i3);
|
||||
mesh.line(i3, i4);
|
||||
mesh.line(i4, i1);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.textures.GpuSampler;
|
||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||
import dev.loki.lovisual.render.engine.postprocess.PostProcessManager;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiEffectSpec;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clamp01;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeCornerRadii;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.packLiquidGlassPayload;
|
||||
|
||||
final class Renderer2DLiquidGlass {
|
||||
private Renderer2DLiquidGlass() {
|
||||
}
|
||||
|
||||
static void liquidGlassRectCorners(Renderer2D r, double x, double y, double w, double h,
|
||||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness,
|
||||
int tintArgb,
|
||||
float glassAlpha,
|
||||
float blurAlpha,
|
||||
float fresnelPower,
|
||||
float fresnelAlpha,
|
||||
float baseAlpha,
|
||||
float fresnelMix,
|
||||
float distortPx,
|
||||
float squirclePower) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
|
||||
r.effect(UiEffectSpec.liquidGlass(UiShape.roundedRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL),
|
||||
softness, softness, distortPx, tintArgb));
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return;
|
||||
RenderTarget fb = mc.gameRenderer.mainRenderTarget();
|
||||
if (fb == null) return;
|
||||
GpuTextureView src = fb.getColorTextureView();
|
||||
if (src == null) return;
|
||||
GpuSampler sampler = PostProcessManager.getSampler();
|
||||
if (sampler == null) return;
|
||||
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreateBlur(UiBatchType.LIQUID_GLASS, src, sampler,
|
||||
Renderer2D.DEFAULT_LIQUID_GLASS_BLUR_QUALITY, Renderer2D.LIQUID_GLASS_KAWASE_OFFSET_PX);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (tintArgb >>> 24) & 0xFF;
|
||||
int red = (tintArgb >>> 16) & 0xFF;
|
||||
int green = (tintArgb >>> 8) & 0xFF;
|
||||
int blue = tintArgb & 0xFF;
|
||||
|
||||
float clampedGlassAlpha = clamp01(glassAlpha);
|
||||
float clampedBlurAlpha = clamp01(blurAlpha);
|
||||
int finalA = (int) (a * clampedGlassAlpha);
|
||||
|
||||
float[] cornerRadiiTmp = r.cornerRadiiTmp;
|
||||
normalizeCornerRadii(w, h, radiusTL, radiusTR, radiusBR, radiusBL, cornerRadiiTmp);
|
||||
|
||||
float thickness = Math.max(0.0f, softness);
|
||||
float mix = clamp01(fresnelMix);
|
||||
float fa = clamp01(fresnelAlpha);
|
||||
float ba = clamp01(baseAlpha);
|
||||
float packedDistort = packLiquidGlassPayload(distortPx, squirclePower, clampedBlurAlpha);
|
||||
|
||||
int i1 = mesh.vec2(x, y).raw2(mix, packedDistort).local2(x, y).color(red, green, blue, finalA)
|
||||
.vec4(x, y, w, h)
|
||||
.vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3])
|
||||
.vec4(thickness, fresnelPower, fa, ba)
|
||||
.next();
|
||||
int i2 = mesh.vec2(x, y + h).raw2(mix, packedDistort).local2(x, y + h).color(red, green, blue, finalA)
|
||||
.vec4(x, y, w, h)
|
||||
.vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3])
|
||||
.vec4(thickness, fresnelPower, fa, ba)
|
||||
.next();
|
||||
int i3 = mesh.vec2(x + w, y + h).raw2(mix, packedDistort).local2(x + w, y + h).color(red, green, blue, finalA)
|
||||
.vec4(x, y, w, h)
|
||||
.vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3])
|
||||
.vec4(thickness, fresnelPower, fa, ba)
|
||||
.next();
|
||||
int i4 = mesh.vec2(x + w, y).raw2(mix, packedDistort).local2(x + w, y).color(red, green, blue, finalA)
|
||||
.vec4(x, y, w, h)
|
||||
.vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3])
|
||||
.vec4(thickness, fresnelPower, fa, ba)
|
||||
.next();
|
||||
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeDegrees;
|
||||
|
||||
final class Renderer2DOrbiz {
|
||||
private Renderer2DOrbiz() {
|
||||
}
|
||||
|
||||
static void orbizRing(Renderer2D r, double cx, double cy,
|
||||
double radius, double thickness,
|
||||
float startAngleDeg, float sweepAngleDeg,
|
||||
float softness, float glowRadius, float glowStrength,
|
||||
int primaryArgb, int secondaryArgb,
|
||||
int colorMode, float angularOffset, float angularCycles,
|
||||
float trackAlpha, float bodyAlpha, float headBoost,
|
||||
float trackGlow, boolean caps) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Orbiz ring drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (radius <= 0.0 || thickness <= 0.0) return;
|
||||
if (((primaryArgb >>> 24) & 0xFF) <= 0 && ((secondaryArgb >>> 24) & 0xFF) <= 0) return;
|
||||
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ORBIZ_RING, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (primaryArgb >>> 24) & 0xFF;
|
||||
int red = (primaryArgb >>> 16) & 0xFF;
|
||||
int green = (primaryArgb >>> 8) & 0xFF;
|
||||
int blue = primaryArgb & 0xFF;
|
||||
|
||||
float sr = ((secondaryArgb >>> 16) & 0xFF) / 255.0f;
|
||||
float sg = ((secondaryArgb >>> 8) & 0xFF) / 255.0f;
|
||||
float sb = (secondaryArgb & 0xFF) / 255.0f;
|
||||
float sa = ((secondaryArgb >>> 24) & 0xFF) / 255.0f;
|
||||
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
float ringRadius = (float) Math.max(0.0, radius);
|
||||
float soft = Math.max(0.0f, softness);
|
||||
float glow = Math.max(0.0f, glowRadius);
|
||||
float expand = stroke * 0.5f + soft + glow + 1.0f;
|
||||
double outerRadius = radius + expand;
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double w = outerRadius * 2.0;
|
||||
double h = outerRadius * 2.0;
|
||||
double ringX = cx - radius;
|
||||
double ringY = cy - radius;
|
||||
double ringSize = radius * 2.0;
|
||||
|
||||
float start = normalizeDegrees(startAngleDeg);
|
||||
float sweep = Mth.clamp(sweepAngleDeg, 0.0f, 360.0f);
|
||||
float offset = angularOffset - (float) Math.floor(angularOffset);
|
||||
float cycles = Math.max(0.001f, angularCycles);
|
||||
float mode = Math.max(0, colorMode);
|
||||
float track = Mth.clamp(trackAlpha, 0.0f, 1.0f);
|
||||
float body = Mth.clamp(bodyAlpha, 0.0f, 1.0f);
|
||||
float glowPower = Math.max(0.0f, glowStrength);
|
||||
float head = Math.max(0.0f, headBoost);
|
||||
float trackGlowAmount = Mth.clamp(trackGlow, 0.0f, 1.0f);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(red, green, blue, a)
|
||||
.vec4(ringX, ringY, ringSize, ringSize)
|
||||
.vec4(ringRadius, soft, stroke, start)
|
||||
.vec4(sweep, glow, glowPower, mode)
|
||||
.vec4(sr, sg, sb, sa)
|
||||
.vec4(offset, cycles, 0.0f, caps ? 1.0f : 0.0f)
|
||||
.vec4(track, body, head, trackGlowAmount)
|
||||
.next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(red, green, blue, a)
|
||||
.vec4(ringX, ringY, ringSize, ringSize)
|
||||
.vec4(ringRadius, soft, stroke, start)
|
||||
.vec4(sweep, glow, glowPower, mode)
|
||||
.vec4(sr, sg, sb, sa)
|
||||
.vec4(offset, cycles, 0.0f, caps ? 1.0f : 0.0f)
|
||||
.vec4(track, body, head, trackGlowAmount)
|
||||
.next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(red, green, blue, a)
|
||||
.vec4(ringX, ringY, ringSize, ringSize)
|
||||
.vec4(ringRadius, soft, stroke, start)
|
||||
.vec4(sweep, glow, glowPower, mode)
|
||||
.vec4(sr, sg, sb, sa)
|
||||
.vec4(offset, cycles, 0.0f, caps ? 1.0f : 0.0f)
|
||||
.vec4(track, body, head, trackGlowAmount)
|
||||
.next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(red, green, blue, a)
|
||||
.vec4(ringX, ringY, ringSize, ringSize)
|
||||
.vec4(ringRadius, soft, stroke, start)
|
||||
.vec4(sweep, glow, glowPower, mode)
|
||||
.vec4(sr, sg, sb, sa)
|
||||
.vec4(offset, cycles, 0.0f, caps ? 1.0f : 0.0f)
|
||||
.vec4(track, body, head, trackGlowAmount)
|
||||
.next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import dev.loki.lovisual.render.engine.command.UiShapeCommand;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiBoxPathBuilder;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiBoxShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaintKind;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPathCap;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPathJoin;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.*;
|
||||
|
||||
final class Renderer2DPolygons {
|
||||
private Renderer2DPolygons() {
|
||||
}
|
||||
|
||||
static void box(Renderer2D r, UiBoxShape box, UiPaint paint, UiStroke stroke, boolean fill) {
|
||||
if (box == null || paint == null) return;
|
||||
UiStroke safeStroke = stroke == null ? UiStroke.NONE : stroke;
|
||||
Renderer2D.recordUi(new UiShapeCommand(UiShape.box(box), paint, safeStroke, fill));
|
||||
renderFlexibleBoxFallback(r, box, paint, safeStroke, fill);
|
||||
}
|
||||
|
||||
static void renderFlexibleBoxFallback(Renderer2D r, UiBoxShape box, UiPaint paint, UiStroke stroke, boolean fill) {
|
||||
if (r.textured || box == null || paint == null) return;
|
||||
double[] rectShapeTmp = r.rectShapeTmp;
|
||||
int count = UiBoxPathBuilder.write(box, rectShapeTmp, rectShapeTmp.length / 2);
|
||||
if (count < 3) return;
|
||||
|
||||
if (fill) {
|
||||
switch (paint.kind()) {
|
||||
case LINEAR_GRADIENT ->
|
||||
polygonGradient(r, rectShapeTmp, count, box.bounds().x(), box.bounds().y(), box.bounds().width(), box.bounds().height(),
|
||||
paint.topLeft(), paint.topRight(), paint.angleDeg(), paint.offsetPx());
|
||||
default -> polygon(r, rectShapeTmp, count, paint.solidColor());
|
||||
}
|
||||
} else if (stroke != null && stroke.enabled()) {
|
||||
if (paint.kind() == UiPaintKind.LINEAR_GRADIENT) {
|
||||
polylineLinearGradient(r, rectShapeTmp, count, stroke.thickness(), true,
|
||||
box.bounds().x(), box.bounds().y(), box.bounds().width(), box.bounds().height(),
|
||||
paint.topLeft(), paint.topRight(), paint.angleDeg(), paint.offsetPx(),
|
||||
stroke.join() == UiPathJoin.ROUND || stroke.cap() == UiPathCap.ROUND);
|
||||
} else {
|
||||
polyline(r, rectShapeTmp, count, stroke.thickness(), true, paint.solidColor(),
|
||||
stroke.join() == UiPathJoin.ROUND || stroke.cap() == UiPathCap.ROUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void polygon(Renderer2D r, double[] points, int pointCount, int argb) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Polygon drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
int safeCount = Math.min(pointCount, points != null ? points.length / 2 : 0);
|
||||
safeCount = Math.min(safeCount, Math.min(r.polygonIndexTmp.length, r.polygonVertexTmp.length));
|
||||
if (points == null || safeCount < 3) return;
|
||||
|
||||
r.path(UiShape.polyline(points, safeCount, true), UiPaint.solid(argb), UiStroke.NONE, true);
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.COLORED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
appendPolygon(mesh, points, safeCount, r.polygonIndexTmp, r.polygonVertexTmp, argb);
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void polygonGradient(Renderer2D r, double[] points, int pointCount,
|
||||
double x, double y, double width, double height,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Polygon drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 3) return;
|
||||
|
||||
r.path(UiShape.polyline(points, pointCount, true), UiPaint.linear(startArgb, endArgb, angleDeg, offsetPx), UiStroke.NONE, true);
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.COLORED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
appendPolygonGradient(mesh, points, pointCount, r.polygonIndexTmp, r.polygonVertexTmp,
|
||||
x, y, width, height, startArgb, endArgb, angleDeg, offsetPx);
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void polygonStroke(Renderer2D r, double[] points, int pointCount, double thickness, boolean closed, int argb) {
|
||||
polyline(r, points, pointCount, thickness, closed, argb, false);
|
||||
}
|
||||
|
||||
static void polyline(Renderer2D r, double[] points, int pointCount, double thickness, boolean closed, int argb) {
|
||||
polyline(r, points, pointCount, thickness, closed, argb, false);
|
||||
}
|
||||
|
||||
static void polyline(Renderer2D r, double[] points, int pointCount, double thickness, boolean closed, int argb, boolean roundCapsAndJoins) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Polyline drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 2) return;
|
||||
double t = Math.max(0.0, thickness);
|
||||
if (t <= 0.0) return;
|
||||
|
||||
r.path(UiShape.polyline(points, pointCount, closed), UiPaint.solid(argb),
|
||||
roundCapsAndJoins ? UiStroke.of(t).withRoundCapsAndJoins() : UiStroke.of(t), false);
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.COLORED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
appendPolyline(mesh, points, pointCount, t, closed, argb, roundCapsAndJoins);
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void polylineGradient(Renderer2D r, double[] points, int pointCount, double thickness, boolean closed,
|
||||
int startArgb, int endArgb, boolean roundCapsAndJoins) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Polyline drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 2) return;
|
||||
double t = Math.max(0.0, thickness);
|
||||
if (t <= 0.0) return;
|
||||
|
||||
r.path(UiShape.polyline(points, pointCount, closed), UiPaint.corners(startArgb, endArgb, endArgb, startArgb),
|
||||
roundCapsAndJoins ? UiStroke.of(t).withRoundCapsAndJoins() : UiStroke.of(t), false);
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.COLORED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
appendPolylineGradient(mesh, points, pointCount, t, closed, startArgb, endArgb, roundCapsAndJoins);
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void polylineLinearGradient(Renderer2D r, double[] points, int pointCount, double thickness, boolean closed,
|
||||
double x, double y, double width, double height,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx,
|
||||
boolean roundCapsAndJoins) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Polyline drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 2) return;
|
||||
double t = Math.max(0.0, thickness);
|
||||
if (t <= 0.0) return;
|
||||
|
||||
r.path(UiShape.polyline(points, pointCount, closed), UiPaint.linear(startArgb, endArgb, angleDeg, offsetPx),
|
||||
roundCapsAndJoins ? UiStroke.of(t).withRoundCapsAndJoins() : UiStroke.of(t), false);
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.COLORED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
appendPolylineLinearGradient(mesh, points, pointCount, t, closed,
|
||||
x, y, width, height, startArgb, endArgb, angleDeg, offsetPx, roundCapsAndJoins);
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
final class Renderer2DSmokeFill {
|
||||
private Renderer2DSmokeFill() {
|
||||
}
|
||||
|
||||
static void roundedSmokeFill(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
float fillRatio,
|
||||
boolean fromRight,
|
||||
int firstArgb,
|
||||
int secondArgb,
|
||||
int thirdArgb,
|
||||
float time,
|
||||
float smokeScale,
|
||||
float smokeMix,
|
||||
int octaves,
|
||||
float flowX,
|
||||
float flowY,
|
||||
float intensity) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
float fill = Mth.clamp(fillRatio, 0.0f, 1.0f);
|
||||
if (fill <= 0.0001f) return;
|
||||
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_FILL_SMOKE, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a0 = (firstArgb >>> 24) & 0xFF;
|
||||
int r0 = (firstArgb >>> 16) & 0xFF;
|
||||
int g0 = (firstArgb >>> 8) & 0xFF;
|
||||
int b0 = firstArgb & 0xFF;
|
||||
|
||||
float r1 = ((secondArgb >>> 16) & 0xFF) / 255.0f;
|
||||
float g1 = ((secondArgb >>> 8) & 0xFF) / 255.0f;
|
||||
float b1 = (secondArgb & 0xFF) / 255.0f;
|
||||
float a1 = ((secondArgb >>> 24) & 0xFF) / 255.0f;
|
||||
|
||||
float r2 = ((thirdArgb >>> 16) & 0xFF) / 255.0f;
|
||||
float g2 = ((thirdArgb >>> 8) & 0xFF) / 255.0f;
|
||||
float b2 = (thirdArgb & 0xFF) / 255.0f;
|
||||
float a2 = ((thirdArgb >>> 24) & 0xFF) / 255.0f;
|
||||
|
||||
float fromRightFlag = fromRight ? 1.0f : 0.0f;
|
||||
float safeScale = Math.max(0.1f, smokeScale);
|
||||
float safeMix = Mth.clamp(smokeMix, 0.0f, 1.0f);
|
||||
float safeOctaves = Mth.clamp(octaves, 1, 8);
|
||||
float safeIntensity = Math.max(0.0f, intensity);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(r0, g0, b0, a0)
|
||||
.vec4((float) x, (float) y, (float) w, (float) h)
|
||||
.vec4(radius, softness, fill, fromRightFlag)
|
||||
.vec4(r1, g1, b1, a1)
|
||||
.vec4(r2, g2, b2, a2)
|
||||
.vec4(time, safeScale, safeMix, safeOctaves)
|
||||
.vec4(flowX, flowY, safeIntensity, 0.0f)
|
||||
.next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(r0, g0, b0, a0)
|
||||
.vec4((float) x, (float) y, (float) w, (float) h)
|
||||
.vec4(radius, softness, fill, fromRightFlag)
|
||||
.vec4(r1, g1, b1, a1)
|
||||
.vec4(r2, g2, b2, a2)
|
||||
.vec4(time, safeScale, safeMix, safeOctaves)
|
||||
.vec4(flowX, flowY, safeIntensity, 0.0f)
|
||||
.next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(r0, g0, b0, a0)
|
||||
.vec4((float) x, (float) y, (float) w, (float) h)
|
||||
.vec4(radius, softness, fill, fromRightFlag)
|
||||
.vec4(r1, g1, b1, a1)
|
||||
.vec4(r2, g2, b2, a2)
|
||||
.vec4(time, safeScale, safeMix, safeOctaves)
|
||||
.vec4(flowX, flowY, safeIntensity, 0.0f)
|
||||
.next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(r0, g0, b0, a0)
|
||||
.vec4((float) x, (float) y, (float) w, (float) h)
|
||||
.vec4(radius, softness, fill, fromRightFlag)
|
||||
.vec4(r1, g1, b1, a1)
|
||||
.vec4(r2, g2, b2, a2)
|
||||
.vec4(time, safeScale, safeMix, safeOctaves)
|
||||
.vec4(flowX, flowY, safeIntensity, 0.0f)
|
||||
.next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
|
||||
import com.mojang.blaze3d.textures.GpuSampler;
|
||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||
import dev.loki.lovisual.render.engine.command.UiShapeCommand;
|
||||
import dev.loki.lovisual.render.engine.command.UiTextureDrawCommand;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampRoundedRadius;
|
||||
|
||||
final class Renderer2DTextures {
|
||||
private Renderer2DTextures() {
|
||||
}
|
||||
|
||||
static void textureQuad(Renderer2D r, GpuTextureView samplerView, GpuSampler sampler,
|
||||
double x, double y, double w, double h, int argb) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Batched texture quad drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (samplerView == null || sampler == null) return;
|
||||
if (w == 0.0 || h == 0.0) return;
|
||||
|
||||
Renderer2D.recordUi(new UiTextureDrawCommand(samplerView, sampler, UiShape.rect(x, y, w, h), UiPaint.solid(argb), 0f, 0f, 1f, 1f, false));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.TEXTURED, samplerView, sampler);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
|
||||
int i1 = mesh.vec2(x, y).raw2(0.0, 0.0).color(red, green, blue, a).next();
|
||||
int i2 = mesh.vec2(x, y + h).raw2(0.0, 1.0).color(red, green, blue, a).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).raw2(1.0, 1.0).color(red, green, blue, a).next();
|
||||
int i4 = mesh.vec2(x + w, y).raw2(1.0, 0.0).color(red, green, blue, a).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void msdfTextureQuad(Renderer2D r, GpuTextureView samplerView, GpuSampler sampler,
|
||||
double x, double y, double w, double h,
|
||||
int topLeft, int topRight, int bottomRight, int bottomLeft,
|
||||
float pxRange, int atlasWidth, int atlasHeight) {
|
||||
if (r.textured) {
|
||||
throw new IllegalStateException("Batched MSDF texture quad drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (samplerView == null || sampler == null) return;
|
||||
if (w == 0.0 || h == 0.0) return;
|
||||
if (pxRange <= 0.0f || atlasWidth <= 0 || atlasHeight <= 0) return;
|
||||
|
||||
Renderer2D.recordUi(new UiTextureDrawCommand(samplerView, sampler, UiShape.rect(x, y, w, h), UiPaint.solid(topLeft), 0f, 0f, 1f, 1f, false));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreateMsdf(samplerView, sampler, pxRange, atlasWidth, atlasHeight);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int i1 = mesh.vec2(x, y).raw2(0.0, 0.0).color((topLeft >>> 16) & 0xFF, (topLeft >>> 8) & 0xFF, topLeft & 0xFF, (topLeft >>> 24) & 0xFF).next();
|
||||
int i2 = mesh.vec2(x, y + h).raw2(0.0, 1.0).color((bottomLeft >>> 16) & 0xFF, (bottomLeft >>> 8) & 0xFF, bottomLeft & 0xFF, (bottomLeft >>> 24) & 0xFF).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).raw2(1.0, 1.0).color((bottomRight >>> 16) & 0xFF, (bottomRight >>> 8) & 0xFF, bottomRight & 0xFF, (bottomRight >>> 24) & 0xFF).next();
|
||||
int i4 = mesh.vec2(x + w, y).raw2(1.0, 0.0).color((topRight >>> 16) & 0xFF, (topRight >>> 8) & 0xFF, topRight & 0xFF, (topRight >>> 24) & 0xFF).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void texQuad(Renderer2D r, double x, double y, double width, double height,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb) {
|
||||
Renderer2D.recordUi(new UiShapeCommand(UiShape.rect(x, y, width, height), UiPaint.solid(argb), UiStroke.NONE, true));
|
||||
MeshBuilder texturedTriangles = r.texturedTriangles;
|
||||
if (texturedTriangles == null) return;
|
||||
texturedTriangles.alpha = r.alpha;
|
||||
texturedTriangles.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
int i1 = texturedTriangles.vec2(x, y).raw2(texX1, texY1).color(red, green, blue, a).next();
|
||||
int i2 = texturedTriangles.vec2(x, y + height).raw2(texX1, texY2).color(red, green, blue, a).next();
|
||||
int i3 = texturedTriangles.vec2(x + width, y + height).raw2(texX2, texY2).color(red, green, blue, a).next();
|
||||
int i4 = texturedTriangles.vec2(x + width, y).raw2(texX2, texY1).color(red, green, blue, a).next();
|
||||
|
||||
texturedTriangles.quad(i1, i2, i3, i4);
|
||||
}
|
||||
|
||||
static void roundedTexRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb, GpuTextureView samplerView, GpuSampler sampler) {
|
||||
if (!r.textured) {
|
||||
throw new IllegalStateException("Renderer2D is not configured for textures.");
|
||||
}
|
||||
if (samplerView == null || sampler == null) return;
|
||||
|
||||
Renderer2D.recordUi(new UiTextureDrawCommand(samplerView, sampler, UiShape.roundedRect(x, y, w, h, radius), UiPaint.solid(argb),
|
||||
(float) texX1, (float) texY1, (float) texX2, (float) texY2, false));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_TEXTURED, samplerView, sampler);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
float clampedRadius = clampRoundedRadius(radius, w, h);
|
||||
int i1 = mesh.vec2(x, y).raw2(texX1, texY1).local2(x, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).raw2(texX1, texY2).local2(x, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).raw2(texX2, texY2).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).raw2(texX2, texY1).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
|
||||
static void roundedTexMaskRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb, GpuTextureView samplerView, GpuSampler sampler) {
|
||||
if (!r.textured) {
|
||||
throw new IllegalStateException("Renderer2D is not configured for textures.");
|
||||
}
|
||||
if (samplerView == null || sampler == null) return;
|
||||
|
||||
Renderer2D.recordUi(new UiTextureDrawCommand(samplerView, sampler, UiShape.roundedRect(x, y, w, h, radius), UiPaint.solid(argb),
|
||||
(float) texX1, (float) texY1, (float) texX2, (float) texY2, true));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_TEXTURED_MASK, samplerView, sampler);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
float clampedRadius = clampRoundedRadius(radius, w, h);
|
||||
int i1 = mesh.vec2(x, y).raw2(texX1, texY1).local2(x, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).raw2(texX1, texY2).local2(x, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).raw2(texX2, texY2).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).raw2(texX2, texY1).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d;
|
||||
|
||||
import com.mojang.blaze3d.textures.GpuSampler;
|
||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiRenderDispatcher;
|
||||
|
||||
/**
|
||||
* Shared batch scope and scratch buffers for the Renderer2D helper backends.
|
||||
*
|
||||
* <p>{@link #open} starts an auto batch (only if none is active yet) and fetches the
|
||||
* batch for the requested type; {@link #close} flushes only when this call chain
|
||||
* actually started the batch. Nesting is handled with a depth counter, so helpers
|
||||
* may call other drawing helpers freely.</p>
|
||||
*/
|
||||
public final class Batch2D {
|
||||
/** Render-thread scratch buffers shared by the helper backends. */
|
||||
public static final float[] CORNER_RADII = new float[4];
|
||||
public static final double[] RECT_SHAPE = new double[256];
|
||||
public static final double[] CONNECTOR = new double[64];
|
||||
public static final double[] CONNECTOR_ANCHOR = new double[4];
|
||||
public static final int[] POLY_INDEX = new int[64];
|
||||
public static final int[] POLY_VERTEX = new int[64];
|
||||
public static final double[] PROGRESS_SHAPE = new double[256];
|
||||
public static final int[] GRADIENT = new int[4];
|
||||
|
||||
private static final int[] RGBA = new int[4];
|
||||
private static final float[] NORM = new float[4];
|
||||
|
||||
private static int depth;
|
||||
private static boolean rootAuto;
|
||||
|
||||
private Batch2D() {
|
||||
}
|
||||
|
||||
public static DrawBatch open(Renderer2D r, UiBatchType type) {
|
||||
return open(r, type, null, null);
|
||||
}
|
||||
|
||||
public static DrawBatch open(Renderer2D r, UiBatchType type, GpuTextureView view, GpuSampler sampler) {
|
||||
beginScope();
|
||||
return finish(r, Renderer2D.UI_BATCHER.getOrCreate(type, view, sampler));
|
||||
}
|
||||
|
||||
public static DrawBatch openBlur(Renderer2D r, UiBatchType type, GpuTextureView view, GpuSampler sampler,
|
||||
Renderer2D.BlurQuality quality, float offsetPx) {
|
||||
beginScope();
|
||||
return finish(r, Renderer2D.UI_BATCHER.getOrCreateBlur(type, view, sampler, quality, offsetPx));
|
||||
}
|
||||
|
||||
public static DrawBatch openMsdf(Renderer2D r, GpuTextureView view, GpuSampler sampler,
|
||||
float pxRange, int atlasWidth, int atlasHeight) {
|
||||
beginScope();
|
||||
return finish(r, Renderer2D.UI_BATCHER.getOrCreateMsdf(view, sampler, pxRange, atlasWidth, atlasHeight));
|
||||
}
|
||||
|
||||
public static void close() {
|
||||
if (depth > 0 && --depth == 0 && rootAuto) {
|
||||
rootAuto = false;
|
||||
UiRenderDispatcher.endAutoBatch(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void beginScope() {
|
||||
if (depth == 0) rootAuto = UiRenderDispatcher.beginAutoBatch();
|
||||
depth++;
|
||||
}
|
||||
|
||||
private static DrawBatch finish(Renderer2D r, DrawBatch batch) {
|
||||
if (batch == null) {
|
||||
close();
|
||||
return null;
|
||||
}
|
||||
batch.mesh.alpha = r.getAlpha();
|
||||
return batch;
|
||||
}
|
||||
|
||||
/** Unpacks {@code argb} into the shared scratch as {r, g, b, a}. */
|
||||
public static int[] rgba(int argb) {
|
||||
RGBA[0] = (argb >>> 16) & 0xFF;
|
||||
RGBA[1] = (argb >>> 8) & 0xFF;
|
||||
RGBA[2] = argb & 0xFF;
|
||||
RGBA[3] = (argb >>> 24) & 0xFF;
|
||||
return RGBA;
|
||||
}
|
||||
|
||||
/** Unpacks {@code argb} into normalized floats {r, g, b, a} in range 0..1. */
|
||||
public static float[] rgbaNorm(int argb) {
|
||||
NORM[0] = ((argb >>> 16) & 0xFF) / 255.0f;
|
||||
NORM[1] = ((argb >>> 8) & 0xFF) / 255.0f;
|
||||
NORM[2] = (argb & 0xFF) / 255.0f;
|
||||
NORM[3] = ((argb >>> 24) & 0xFF) / 255.0f;
|
||||
return NORM;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.path.Polygons2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampChamferAxis;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.computeLinearGradientColors;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeChamfers;
|
||||
|
||||
/** Chamfered (cut-corner) rects and beveled 3D-style plates. */
|
||||
public final class Chamfered2D {
|
||||
private Chamfered2D() {
|
||||
}
|
||||
|
||||
public static void chamferedRectQuadAxes(Renderer2D r, double x, double y, double w, double h,
|
||||
double chamferTLX, double chamferTLY,
|
||||
double chamferTRX, double chamferTRY,
|
||||
double chamferBRX, double chamferBRY,
|
||||
double chamferBLX, double chamferBLY,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shape(UiShape.chamferedRectAxes(x, y, w, h, chamferTLX, chamferTLY, chamferTRX, chamferTRY,
|
||||
chamferBRX, chamferBRY, chamferBLX, chamferBLY),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.CHAMFERED);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
int tlR = (cTopLeft >>> 16) & 0xFF;
|
||||
int tlG = (cTopLeft >>> 8) & 0xFF;
|
||||
int tlB = cTopLeft & 0xFF;
|
||||
int trA = (cTopRight >>> 24) & 0xFF;
|
||||
int trR = (cTopRight >>> 16) & 0xFF;
|
||||
int trG = (cTopRight >>> 8) & 0xFF;
|
||||
int trB = cTopRight & 0xFF;
|
||||
int brA = (cBottomRight >>> 24) & 0xFF;
|
||||
int brR = (cBottomRight >>> 16) & 0xFF;
|
||||
int brG = (cBottomRight >>> 8) & 0xFF;
|
||||
int brB = cBottomRight & 0xFF;
|
||||
int blA = (cBottomLeft >>> 24) & 0xFF;
|
||||
int blR = (cBottomLeft >>> 16) & 0xFF;
|
||||
int blG = (cBottomLeft >>> 8) & 0xFF;
|
||||
int blB = cBottomLeft & 0xFF;
|
||||
|
||||
float tlX = clampChamferAxis(chamferTLX, w);
|
||||
float trX = clampChamferAxis(chamferTRX, w);
|
||||
float brX = clampChamferAxis(chamferBRX, w);
|
||||
float blX = clampChamferAxis(chamferBLX, w);
|
||||
float tlY = clampChamferAxis(chamferTLY, h);
|
||||
float trY = clampChamferAxis(chamferTRY, h);
|
||||
float brY = clampChamferAxis(chamferBRY, h);
|
||||
float blY = clampChamferAxis(chamferBLY, h);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h).vec4(tlX, trX, brX, blX).vec4(tlY, trY, brY, blY).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void chamferedRectQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
double chamferTL, double chamferTR, double chamferBR, double chamferBL,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
float[] c = Batch2D.CORNER_RADII;
|
||||
normalizeChamfers(w, h, (float) chamferTL, (float) chamferTR, (float) chamferBR, (float) chamferBL, c);
|
||||
chamferedRectQuadAxes(r, x, y, w, h,
|
||||
c[0], c[0], c[1], c[1], c[2], c[2], c[3], c[3],
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
public static void chamferedRectGradient(Renderer2D r, double x, double y, double width, double height,
|
||||
double chamferTL, double chamferTR, double chamferBR, double chamferBL,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx) {
|
||||
int[] g = Batch2D.GRADIENT;
|
||||
computeLinearGradientColors((float) width, (float) height, startArgb, endArgb, angleDeg, offsetPx, g);
|
||||
chamferedRectQuad(r, x, y, width, height, chamferTL, chamferTR, chamferBR, chamferBL,
|
||||
g[0], g[1], g[2], g[3]);
|
||||
}
|
||||
|
||||
public static void chamferedRectStrokeQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
double chamferTL, double chamferTR, double chamferBR, double chamferBL,
|
||||
double thickness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shapeStroke(UiShape.chamferedRect(x, y, w, h, chamferTL, chamferTR, chamferBR, chamferBL),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft), UiStroke.of(thickness));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.CHAMFERED_STROKE);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
int tlR = (cTopLeft >>> 16) & 0xFF;
|
||||
int tlG = (cTopLeft >>> 8) & 0xFF;
|
||||
int tlB = cTopLeft & 0xFF;
|
||||
int trA = (cTopRight >>> 24) & 0xFF;
|
||||
int trR = (cTopRight >>> 16) & 0xFF;
|
||||
int trG = (cTopRight >>> 8) & 0xFF;
|
||||
int trB = cTopRight & 0xFF;
|
||||
int brA = (cBottomRight >>> 24) & 0xFF;
|
||||
int brR = (cBottomRight >>> 16) & 0xFF;
|
||||
int brG = (cBottomRight >>> 8) & 0xFF;
|
||||
int brB = cBottomRight & 0xFF;
|
||||
int blA = (cBottomLeft >>> 24) & 0xFF;
|
||||
int blR = (cBottomLeft >>> 16) & 0xFF;
|
||||
int blG = (cBottomLeft >>> 8) & 0xFF;
|
||||
int blB = cBottomLeft & 0xFF;
|
||||
float[] c = Batch2D.CORNER_RADII;
|
||||
normalizeChamfers(w, h, (float) chamferTL, (float) chamferTR, (float) chamferBR, (float) chamferBL, c);
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(x, y, w, h).vec4(c[0], c[1], c[2], c[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(x, y, w, h).vec4(c[0], c[1], c[2], c[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h).vec4(c[0], c[1], c[2], c[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h).vec4(c[0], c[1], c[2], c[3]).vec4(stroke, 0f, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void beveledRect(Renderer2D r, double x, double y, double width, double height,
|
||||
double bevel, int fillArgb, int highlightArgb, int shadowArgb) {
|
||||
chamferedRectQuad(r, x, y, width, height, bevel, bevel, bevel, bevel,
|
||||
fillArgb, fillArgb, fillArgb, fillArgb);
|
||||
bevelEdges(r, x, y, width, height, bevel, highlightArgb, shadowArgb);
|
||||
}
|
||||
|
||||
public static void beveledRectGradient(Renderer2D r, double x, double y, double width, double height,
|
||||
double bevel, int startArgb, int endArgb,
|
||||
int highlightArgb, int shadowArgb, float angleDeg, float offsetPx) {
|
||||
chamferedRectGradient(r, x, y, width, height, bevel, bevel, bevel, bevel,
|
||||
startArgb, endArgb, angleDeg, offsetPx);
|
||||
bevelEdges(r, x, y, width, height, bevel, highlightArgb, shadowArgb);
|
||||
}
|
||||
|
||||
private static void bevelEdges(Renderer2D r, double x, double y, double width, double height,
|
||||
double bevel, int highlightArgb, int shadowArgb) {
|
||||
double t = Math.max(1.0, Math.min(Math.min(Math.abs(width), Math.abs(height)) * 0.25, bevel));
|
||||
double[] c = Batch2D.CONNECTOR;
|
||||
c[0] = x + bevel;
|
||||
c[1] = y;
|
||||
c[2] = x + width - bevel;
|
||||
c[3] = y;
|
||||
c[4] = x + width;
|
||||
c[5] = y + bevel;
|
||||
Polygons2D.polyline(r, c, 3, t, false, highlightArgb, false);
|
||||
c[0] = x + width;
|
||||
c[1] = y + height - bevel;
|
||||
c[2] = x + width - bevel;
|
||||
c[3] = y + height;
|
||||
c[4] = x + bevel;
|
||||
c[5] = y + height;
|
||||
c[6] = x;
|
||||
c[7] = y + height - bevel;
|
||||
Polygons2D.polyline(r, c, 4, t, false, shadowArgb, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,9 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
|
|
@ -17,67 +18,49 @@ import java.util.function.Consumer;
|
|||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.appendColoredQuad;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampRoundedRadius;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.computeLinearGradientColors;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeCornerRadii;
|
||||
|
||||
final class Renderer2DRects {
|
||||
private Renderer2DRects() {
|
||||
/** Filled rect family: solid quads, rounded rects, per-corner radii and gradients. */
|
||||
public final class Rects2D {
|
||||
private Rects2D() {
|
||||
}
|
||||
|
||||
static void quadBatch(Renderer2D r, Consumer<Renderer2D.QuadBatch> consumer) {
|
||||
if (r.textured) {
|
||||
public static void quadBatch(Renderer2D r, Consumer<Renderer2D.QuadBatch> consumer) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Quad batching is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (consumer == null) return;
|
||||
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.COLORED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
Renderer2D.QuadBatch quadBatch = new Renderer2D.QuadBatch(mesh);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.COLORED);
|
||||
if (batch == null) return;
|
||||
try {
|
||||
consumer.accept(quadBatch);
|
||||
consumer.accept(new Renderer2D.QuadBatch(batch.mesh));
|
||||
} finally {
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
}
|
||||
|
||||
static void quadCorners(Renderer2D r, double x, double y, double width, double height,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
public static void quadCorners(Renderer2D r, double x, double y, double width, double height,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shape(UiShape.rect(x, y, width, height), UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.COLORED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
appendColoredQuad(mesh, x, y, width, height, cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.COLORED);
|
||||
if (batch == null) return;
|
||||
appendColoredQuad(batch.mesh, x, y, width, height, cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedRectMaskedQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
double maskX, double maskY, double maskW, double maskH,
|
||||
float radius, float softness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shape(UiShape.roundedRect(maskX, maskY, maskW, maskH, radius), UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
public static void roundedRectMaskedQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
double maskX, double maskY, double maskW, double maskH,
|
||||
float radius, float softness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shape(UiShape.roundedRect(maskX, maskY, maskW, maskH, radius),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
float clampedRadius = clampRoundedRadius(radius, maskW, maskH);
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
int tlR = (cTopLeft >>> 16) & 0xFF;
|
||||
|
|
@ -95,32 +78,27 @@ final class Renderer2DRects {
|
|||
int blR = (cBottomLeft >>> 16) & 0xFF;
|
||||
int blG = (cBottomLeft >>> 8) & 0xFF;
|
||||
int blB = cBottomLeft & 0xFF;
|
||||
float clampedRadius = clampRoundedRadius(radius, maskW, maskH);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(maskX, maskY, maskW, maskH).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(maskX, maskY, maskW, maskH).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(maskX, maskY, maskW, maskH).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(maskX, maskY, maskW, maskH).vec4(clampedRadius, 0.0f, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedRectCornersQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shape(UiShape.roundedRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL), UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_CORNERS, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
public static void roundedRectCornersQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shape(UiShape.roundedRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_CORNERS);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
float[] radii = Batch2D.CORNER_RADII;
|
||||
normalizeCornerRadii(w, h, radiusTL, radiusTR, radiusBR, radiusBL, radii);
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
int tlR = (cTopLeft >>> 16) & 0xFF;
|
||||
|
|
@ -138,15 +116,31 @@ final class Renderer2DRects {
|
|||
int blR = (cBottomLeft >>> 16) & 0xFF;
|
||||
int blG = (cBottomLeft >>> 8) & 0xFF;
|
||||
int blB = cBottomLeft & 0xFF;
|
||||
float[] cornerRadiiTmp = r.cornerRadiiTmp;
|
||||
normalizeCornerRadii(w, h, radiusTL, radiusTR, radiusBR, radiusBL, cornerRadiiTmp);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(tlR, tlG, tlB, tlA).vec4(x, y, w, h)
|
||||
.vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(blR, blG, blB, blA).vec4(x, y, w, h)
|
||||
.vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h)
|
||||
.vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h)
|
||||
.vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, 0f, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void roundedRectGradientQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
roundedRectMaskedQuad(r, x, y, w, h, x, y, w, h, radius, 0.0f,
|
||||
cTopLeft, cTopRight, cBottomRight, cBottomLeft);
|
||||
}
|
||||
|
||||
public static void roundedRectGradient(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx) {
|
||||
int[] g = Batch2D.GRADIENT;
|
||||
computeLinearGradientColors((float) w, (float) h, startArgb, endArgb, angleDeg, offsetPx, g);
|
||||
roundedRectGradientQuad(r, x, y, w, h, radius, 0.0f, g[0], g[1], g[2], g[3]);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,12 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.path.Polygons2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
|
|
@ -19,22 +21,17 @@ import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.bu
|
|||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampRoundedRadius;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeCornerRadii;
|
||||
|
||||
final class Renderer2DRectsExtra {
|
||||
private Renderer2DRectsExtra() {
|
||||
/** Stroked rounded rect family plus the clipped progress bar fill. */
|
||||
public final class Strokes2D {
|
||||
private Strokes2D() {
|
||||
}
|
||||
|
||||
static void roundedRectStroke(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness, int argb) {
|
||||
public static void roundedRectStroke(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness, int argb) {
|
||||
r.shapeStroke(UiShape.roundedRect(x, y, w, h, radius), UiPaint.solid(argb), UiStroke.of(thickness));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_STROKE, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_STROKE);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
|
|
@ -49,91 +46,68 @@ final class Renderer2DRectsExtra {
|
|||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedRectStrokeAngularGradient(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness,
|
||||
int startArgb, int endArgb, float offset) {
|
||||
public static void roundedRectStrokeAngularGradient(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness,
|
||||
int startArgb, int endArgb, float offset) {
|
||||
r.shapeStroke(UiShape.roundedRect(x, y, w, h, radius), UiPaint.solid(startArgb), UiStroke.of(thickness));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_STROKE_ANGULAR, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_STROKE_ANGULAR);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int sa = (startArgb >>> 24) & 0xFF;
|
||||
int sr = (startArgb >>> 16) & 0xFF;
|
||||
int sg = (startArgb >>> 8) & 0xFF;
|
||||
int sb = startArgb & 0xFF;
|
||||
float ea = ((endArgb >>> 24) & 0xFF) / 255.0f;
|
||||
float er = ((endArgb >>> 16) & 0xFF) / 255.0f;
|
||||
float eg = ((endArgb >>> 8) & 0xFF) / 255.0f;
|
||||
float eb = (endArgb & 0xFF) / 255.0f;
|
||||
float[] end = Batch2D.rgbaNorm(endArgb);
|
||||
float clampedRadius = clampRoundedRadius(radius, w, h);
|
||||
float stroke = Math.max(0.0f, thickness);
|
||||
float phase = offset - (float) Math.floor(offset);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(er, eg, eb, ea).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(er, eg, eb, ea).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(er, eg, eb, ea).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(er, eg, eb, ea).next();
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(end[0], end[1], end[2], end[3]).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(end[0], end[1], end[2], end[3]).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(end[0], end[1], end[2], end[3]).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(sr, sg, sb, sa).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, phase).vec4(end[0], end[1], end[2], end[3]).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedRectStrokeCorners(Renderer2D r, double x, double y, double w, double h,
|
||||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness, float thickness, int argb) {
|
||||
public static void roundedRectStrokeCorners(Renderer2D r, double x, double y, double w, double h,
|
||||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness, float thickness, int argb) {
|
||||
r.shapeStroke(UiShape.roundedRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL), UiPaint.solid(argb), UiStroke.of(thickness));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_STROKE_CORNERS, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_STROKE_CORNERS);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
int red = (argb >>> 16) & 0xFF;
|
||||
int green = (argb >>> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
float[] cornerRadiiTmp = r.cornerRadiiTmp;
|
||||
normalizeCornerRadii(w, h, radiusTL, radiusTR, radiusBR, radiusBL, cornerRadiiTmp);
|
||||
float[] radii = Batch2D.CORNER_RADII;
|
||||
normalizeCornerRadii(w, h, radiusTL, radiusTR, radiusBR, radiusBL, radii);
|
||||
float stroke = Math.max(0.0f, thickness);
|
||||
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(cornerRadiiTmp[0], cornerRadiiTmp[1], cornerRadiiTmp[2], cornerRadiiTmp[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(radii[0], radii[1], radii[2], radii[3]).vec4(0.0f, stroke, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedRectStrokeGradientQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shapeStroke(UiShape.roundedRect(x, y, w, h, radius), UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft), UiStroke.of(thickness));
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_STROKE, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
public static void roundedRectStrokeGradientQuad(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float thickness,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
r.shapeStroke(UiShape.roundedRect(x, y, w, h, radius),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft), UiStroke.of(thickness));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_STROKE);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int tlA = (cTopLeft >>> 24) & 0xFF;
|
||||
|
|
@ -160,29 +134,35 @@ final class Renderer2DRectsExtra {
|
|||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(brR, brG, brB, brA).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(trR, trG, trB, trA).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, stroke, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedProgressRectGradient(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
float progress,
|
||||
boolean fromRight,
|
||||
int startArgb, int endArgb,
|
||||
float angleDeg, float offsetPx) {
|
||||
/**
|
||||
* Draws a progress segment clipped by the full rounded bar outline.
|
||||
*
|
||||
* <p>Unlike drawing a rounded rectangle with the current fill width, this keeps the moving
|
||||
* progress edge straight and lets only the fixed outer bar bounds define rounded ends. That
|
||||
* prevents small health values from becoming a pill/capsule and prevents the fill from leaking
|
||||
* outside the left/right rounded shell when the progress edge crosses an outer corner.</p>
|
||||
*/
|
||||
public static void roundedProgressRectGradient(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
float progress, boolean fromRight,
|
||||
int startArgb, int endArgb,
|
||||
float angleDeg, float offsetPx) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
float p = Mth.clamp(progress, 0.0f, 1.0f);
|
||||
if (p <= 0.0001f) return;
|
||||
if (p >= 0.9999f) {
|
||||
r.roundedRectGradient(x, y, w, h, radius, softness, startArgb, endArgb, angleDeg, offsetPx);
|
||||
Rects2D.roundedRectGradient(r, x, y, w, h, radius, softness, startArgb, endArgb, angleDeg, offsetPx);
|
||||
return;
|
||||
}
|
||||
|
||||
double fillW = Math.max(0.0, Math.min(w, w * p));
|
||||
double fillX = fromRight ? x + w - fillW : x;
|
||||
double clipX = fromRight ? fillX : x + fillW;
|
||||
int count = buildRoundedProgressRect(r.progressShapeTmp, r.rectShapeTmp, x, y, w, h, radius, clipX, fromRight);
|
||||
int count = buildRoundedProgressRect(Batch2D.PROGRESS_SHAPE, Batch2D.RECT_SHAPE, x, y, w, h, radius, clipX, fromRight);
|
||||
if (count < 3) return;
|
||||
Renderer2DPolygons.polygonGradient(r, r.progressShapeTmp, count, fillX, y, fillW, h, startArgb, endArgb, angleDeg, offsetPx);
|
||||
Polygons2D.polygonGradient(r, Batch2D.PROGRESS_SHAPE, count, fillX, y, fillW, h, startArgb, endArgb, angleDeg, offsetPx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.fx;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.textures.GpuSampler;
|
||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||
import dev.loki.lovisual.render.engine.postprocess.PostProcessManager;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.BlurSource;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiEffectSpec;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clamp01;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampChamfer;
|
||||
|
||||
/** Background blur rectangles fed from the main framebuffer. */
|
||||
public final class Blur2D {
|
||||
private Blur2D() {
|
||||
}
|
||||
|
||||
private static Renderer2D.BlurQuality legacyBlurQuality(float legacyQuality) {
|
||||
if (legacyQuality >= 32.0f) return Renderer2D.BlurQuality.ULTRA;
|
||||
if (legacyQuality >= 12.0f) return Renderer2D.BlurQuality.HIGH;
|
||||
if (legacyQuality >= 5.0f) return Renderer2D.BlurQuality.MEDIUM;
|
||||
return Renderer2D.BlurQuality.LOW;
|
||||
}
|
||||
|
||||
private static float legacyKawaseOffset(float legacyQuality) {
|
||||
if (!Float.isFinite(legacyQuality)) return Renderer2D.DEFAULT_KAWASE_OFFSET_PX;
|
||||
return Mth.clamp(legacyQuality / 18.0f, 0.85f, 3.25f);
|
||||
}
|
||||
|
||||
/** Main framebuffer color texture plus the shared post-process sampler. */
|
||||
public static BlurSource source() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return null;
|
||||
RenderTarget fb = mc.gameRenderer.mainRenderTarget();
|
||||
if (fb == null) return null;
|
||||
GpuTextureView src = fb.getColorTextureView();
|
||||
if (src == null) return null;
|
||||
GpuSampler sampler = PostProcessManager.getSampler();
|
||||
if (sampler == null) return null;
|
||||
return new BlurSource(src, sampler);
|
||||
}
|
||||
|
||||
public static void blurRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
if (alpha <= 0.001f) return;
|
||||
|
||||
r.effect(UiEffectSpec.blur(UiShape.roundedRect(x, y, w, h, radius), radius, ignoredTintRgb));
|
||||
BlurSource source = source();
|
||||
if (source == null) return;
|
||||
|
||||
DrawBatch batch = Batch2D.openBlur(r, UiBatchType.BLUR, source.view, source.sampler,
|
||||
legacyBlurQuality(quality), legacyKawaseOffset(quality));
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = 1.0;
|
||||
|
||||
float finalAlpha = (float) (alpha * r.getAlpha());
|
||||
mesh.ensureQuadCapacity();
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(255, 255, 255, 255).vec4(x, y, w, h).vec4(radius, quality, brightness, finalAlpha).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void glassBlurRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
if (alpha <= 0.001f) return;
|
||||
|
||||
r.effect(UiEffectSpec.blur(UiShape.roundedRect(x, y, w, h, radius), radius, ignoredTintRgb));
|
||||
BlurSource source = source();
|
||||
if (source == null) return;
|
||||
|
||||
DrawBatch batch = Batch2D.openBlur(r, UiBatchType.GLASS_BLUR, source.view, source.sampler,
|
||||
Renderer2D.BlurQuality.HIGH, legacyKawaseOffset(quality));
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = 1.0;
|
||||
|
||||
float finalAlpha = clamp01((float) (alpha * r.getAlpha()));
|
||||
if (finalAlpha <= 0.001f) {
|
||||
Batch2D.close();
|
||||
return;
|
||||
}
|
||||
|
||||
int a = Math.max(0, Math.min(255, Math.round(finalAlpha * 255.0f)));
|
||||
float smoothness = 2.0f;
|
||||
float rad = Math.max(0.0f, radius);
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
int i1 = mesh.vec2(x, y).local2(x, y).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
int i2 = mesh.vec2(x, y + h).local2(x, y + h).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(255, 255, 255, a)
|
||||
.vec4(x, y, w, h).vec4(rad, rad, rad, rad).vec4(quality, brightness, smoothness, 0.0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void blurChamferedRect(Renderer2D r, double x, double y, double w, double h,
|
||||
double chamfer, float quality, float brightness, float alpha, int ignoredTintRgb) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
if (alpha <= 0.001f) return;
|
||||
float cut = clampChamfer(chamfer, w, h);
|
||||
if (cut <= 0.0f) {
|
||||
blurRect(r, x, y, w, h, 0.0f, quality, brightness, alpha, ignoredTintRgb);
|
||||
return;
|
||||
}
|
||||
blurRect(r, x, y, w, h, -cut, quality, brightness, alpha, ignoredTintRgb);
|
||||
}
|
||||
|
||||
public static void blurComposite(Renderer2D r, Consumer<Renderer2D.BlurComposite> consumer) {
|
||||
if (consumer == null) return;
|
||||
|
||||
BlurSource source = source();
|
||||
if (source == null) return;
|
||||
|
||||
DrawBatch batch = Batch2D.openBlur(r, UiBatchType.BLUR_COMPLEX, source.view, source.sampler,
|
||||
Renderer2D.DEFAULT_BLUR_QUALITY, Renderer2D.DEFAULT_KAWASE_OFFSET_PX);
|
||||
if (batch == null) return;
|
||||
batch.mesh.alpha = 1.0;
|
||||
try {
|
||||
consumer.accept(new Renderer2D.BlurComposite(batch.mesh, (float) r.getAlpha()));
|
||||
} finally {
|
||||
Batch2D.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,29 +5,26 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer;
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.fx;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampRoundedRadius;
|
||||
|
||||
final class Renderer2DRectsGlow {
|
||||
private Renderer2DRectsGlow() {
|
||||
/** Glow, shadow and radial-glow rect effects. */
|
||||
public final class Effects2D {
|
||||
private Effects2D() {
|
||||
}
|
||||
|
||||
static void roundedRectGlow(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float glow, int argb) {
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_GLOW, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
public static void roundedRectGlow(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float glow, int argb) {
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_GLOW);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
|
|
@ -47,21 +44,14 @@ final class Renderer2DRectsGlow {
|
|||
int i3 = mesh.vec2(gx + gw, gy + gh).local2(gx + gw, gy + gh).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, glow, 0f).next();
|
||||
int i4 = mesh.vec2(gx + gw, gy).local2(gx + gw, gy).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, glow, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedRectShadow(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float spread, int argb) {
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_SHADOW, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
public static void roundedRectShadow(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness, float spread, int argb) {
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_SHADOW);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
|
|
@ -82,21 +72,14 @@ final class Renderer2DRectsGlow {
|
|||
int i3 = mesh.vec2(gx + gw, gy + gh).local2(gx + gw, gy + gh).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, spread, 0f).next();
|
||||
int i4 = mesh.vec2(gx + gw, gy).local2(gx + gw, gy).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, spread, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void roundedRectSoftShadow(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float blur, float innerAlpha, int argb) {
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.ROUNDED_SOFT_SHADOW, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
public static void roundedRectSoftShadow(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float blur, float innerAlpha, int argb) {
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_SOFT_SHADOW);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
|
|
@ -115,23 +98,16 @@ final class Renderer2DRectsGlow {
|
|||
int i3 = mesh.vec2(sx + sw, sy + sh).local2(sx + sw, sy + sh).color(red, green, blue, a).vec4(x, y, w, h).vec4(radius, blur, innerAlpha, 0f).next();
|
||||
int i4 = mesh.vec2(sx + sw, sy).local2(sx + sw, sy).color(red, green, blue, a).vec4(x, y, w, h).vec4(radius, blur, innerAlpha, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
static void radialGlowMasked(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
float glowRadius, float cx, float cy,
|
||||
int argb) {
|
||||
boolean auto = r.beginAutoBatch();
|
||||
DrawBatch batch = Renderer2D.UI_BATCHER.getOrCreate(UiBatchType.RADIAL_GLOW, null, null);
|
||||
if (batch == null) {
|
||||
r.endAutoBatch(auto);
|
||||
return;
|
||||
}
|
||||
public static void radialGlowMasked(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
float glowRadius, float cx, float cy,
|
||||
int argb) {
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.RADIAL_GLOW);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.alpha = r.alpha;
|
||||
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
|
|
@ -145,7 +121,6 @@ final class Renderer2DRectsGlow {
|
|||
int i3 = mesh.vec2(x + w, y + h).local2(x + w, y + h).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, glowRadius, 0.0f).vec4(cx, cy, 0f, 0f).next();
|
||||
int i4 = mesh.vec2(x + w, y).local2(x + w, y).color(red, green, blue, a).vec4(x, y, w, h).vec4(clampedRadius, 0.0f, glowRadius, 0.0f).vec4(cx, cy, 0f, 0f).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
|
||||
r.endAutoBatch(auto);
|
||||
Batch2D.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.fx;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.BlurSource;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiEffectSpec;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clamp01;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeCornerRadii;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.packLiquidGlassPayload;
|
||||
|
||||
/** Liquid glass material rectangles backed by the frame blur pipeline. */
|
||||
public final class Glass2D {
|
||||
private Glass2D() {
|
||||
}
|
||||
|
||||
// squirclePower < 1.5f disables the squircle falloff
|
||||
public static void liquidGlassRectCorners(Renderer2D r, double x, double y, double w, double h,
|
||||
float radiusTL, float radiusTR, float radiusBR, float radiusBL,
|
||||
float softness,
|
||||
int tintArgb,
|
||||
float glassAlpha,
|
||||
float blurAlpha,
|
||||
float fresnelPower,
|
||||
float fresnelAlpha,
|
||||
float baseAlpha,
|
||||
float fresnelMix,
|
||||
float distortPx,
|
||||
float squirclePower) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
|
||||
r.effect(UiEffectSpec.liquidGlass(UiShape.roundedRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL),
|
||||
softness, softness, distortPx, tintArgb));
|
||||
BlurSource source = Blur2D.source();
|
||||
if (source == null) return;
|
||||
|
||||
DrawBatch batch = Batch2D.openBlur(r, UiBatchType.LIQUID_GLASS, source.view, source.sampler,
|
||||
Renderer2D.DEFAULT_LIQUID_GLASS_BLUR_QUALITY, Renderer2D.LIQUID_GLASS_KAWASE_OFFSET_PX);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int[] tint = Batch2D.rgba(tintArgb);
|
||||
float clampedBlurAlpha = clamp01(blurAlpha);
|
||||
int finalA = (int) (tint[3] * clamp01(glassAlpha));
|
||||
|
||||
float[] radii = Batch2D.CORNER_RADII;
|
||||
normalizeCornerRadii(w, h, radiusTL, radiusTR, radiusBR, radiusBL, radii);
|
||||
|
||||
float thickness = Math.max(0.0f, softness);
|
||||
float mix = clamp01(fresnelMix);
|
||||
float fa = clamp01(fresnelAlpha);
|
||||
float ba = clamp01(baseAlpha);
|
||||
float packedDistort = packLiquidGlassPayload(distortPx, squirclePower, clampedBlurAlpha);
|
||||
|
||||
int i1 = glassVertex(mesh, x, y, x, y, w, h, tint, finalA, radii, mix, packedDistort, thickness, fresnelPower, fa, ba);
|
||||
int i2 = glassVertex(mesh, x, y + h, x, y, w, h, tint, finalA, radii, mix, packedDistort, thickness, fresnelPower, fa, ba);
|
||||
int i3 = glassVertex(mesh, x + w, y + h, x, y, w, h, tint, finalA, radii, mix, packedDistort, thickness, fresnelPower, fa, ba);
|
||||
int i4 = glassVertex(mesh, x + w, y, x, y, w, h, tint, finalA, radii, mix, packedDistort, thickness, fresnelPower, fa, ba);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* One liquid-glass vertex. {@code px}/{@code py} is the vertex position;
|
||||
* {@code rx}/{@code ry} is the rect origin carried by the Rect payload.
|
||||
*/
|
||||
private static int glassVertex(MeshBuilder mesh, double px, double py, double rx, double ry, double w, double h,
|
||||
int[] tint, int finalA, float[] radii,
|
||||
float mix, float packedDistort,
|
||||
float thickness, float fresnelPower, float fresnelAlpha, float baseAlpha) {
|
||||
return mesh.vec2(px, py).raw2(mix, packedDistort).local2(px, py).color(tint[0], tint[1], tint[2], finalA)
|
||||
.vec4((float) rx, (float) ry, (float) w, (float) h)
|
||||
.vec4(radii[0], radii[1], radii[2], radii[3])
|
||||
.vec4(thickness, fresnelPower, fresnelAlpha, baseAlpha)
|
||||
.next();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.fx;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
/** Procedural smoke fill for rounded rects (health/progress style bars). */
|
||||
public final class Smoke2D {
|
||||
private Smoke2D() {
|
||||
}
|
||||
|
||||
public static void roundedSmokeFill(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
float fillRatio,
|
||||
boolean fromRight,
|
||||
int firstArgb,
|
||||
int secondArgb,
|
||||
int thirdArgb,
|
||||
float time,
|
||||
float smokeScale,
|
||||
float smokeMix,
|
||||
int octaves,
|
||||
float flowX,
|
||||
float flowY,
|
||||
float intensity) {
|
||||
if (w <= 0.0 || h <= 0.0) return;
|
||||
float fill = Mth.clamp(fillRatio, 0.0f, 1.0f);
|
||||
if (fill <= 0.0001f) return;
|
||||
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ROUNDED_FILL_SMOKE);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int[] first = Batch2D.rgba(firstArgb);
|
||||
float[] second = Batch2D.rgbaNorm(secondArgb);
|
||||
float[] third = Batch2D.rgbaNorm(thirdArgb);
|
||||
|
||||
float fromRightFlag = fromRight ? 1.0f : 0.0f;
|
||||
float safeScale = Math.max(0.1f, smokeScale);
|
||||
float safeMix = Mth.clamp(smokeMix, 0.0f, 1.0f);
|
||||
float safeOctaves = Mth.clamp(octaves, 1, 8);
|
||||
float safeIntensity = Math.max(0.0f, intensity);
|
||||
|
||||
int i1 = smokeVertex(mesh, x, y, x, y, w, h, first, second, third, radius, softness, fill, fromRightFlag,
|
||||
time, safeScale, safeMix, safeOctaves, flowX, flowY, safeIntensity);
|
||||
int i2 = smokeVertex(mesh, x, y + h, x, y, w, h, first, second, third, radius, softness, fill, fromRightFlag,
|
||||
time, safeScale, safeMix, safeOctaves, flowX, flowY, safeIntensity);
|
||||
int i3 = smokeVertex(mesh, x + w, y + h, x, y, w, h, first, second, third, radius, softness, fill, fromRightFlag,
|
||||
time, safeScale, safeMix, safeOctaves, flowX, flowY, safeIntensity);
|
||||
int i4 = smokeVertex(mesh, x + w, y, x, y, w, h, first, second, third, radius, softness, fill, fromRightFlag,
|
||||
time, safeScale, safeMix, safeOctaves, flowX, flowY, safeIntensity);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* One smoke-fill vertex. {@code px}/{@code py} is the vertex position;
|
||||
* {@code rx}/{@code ry} is the rect origin carried by the Rect payload
|
||||
* (identical for all four vertices of the quad).
|
||||
*/
|
||||
private static int smokeVertex(MeshBuilder mesh, double px, double py, double rx, double ry, double w, double h,
|
||||
int[] first, float[] second, float[] third,
|
||||
float radius, float softness, float fill, float fromRightFlag,
|
||||
float time, float scale, float mix, float octaves,
|
||||
float flowX, float flowY, float intensity) {
|
||||
return mesh.vec2(px, py).local2(px, py).color(first[0], first[1], first[2], first[3])
|
||||
.vec4((float) rx, (float) ry, (float) w, (float) h)
|
||||
.vec4(radius, softness, fill, fromRightFlag)
|
||||
.vec4(second[0], second[1], second[2], second[3])
|
||||
.vec4(third[0], third[1], third[2], third[3])
|
||||
.vec4(time, scale, mix, octaves)
|
||||
.vec4(flowX, flowY, intensity, 0.0f)
|
||||
.next();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.media;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiItemSubmission;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/** ItemStack submission into the deferred item batch renderer. */
|
||||
public final class Items2D {
|
||||
private Items2D() {
|
||||
}
|
||||
|
||||
public static void submit(Renderer2D r,
|
||||
@Nullable LocalPlayer player,
|
||||
ItemStack stack,
|
||||
double x,
|
||||
double y,
|
||||
float scaleX,
|
||||
float scaleY,
|
||||
float pivotX,
|
||||
float pivotY,
|
||||
boolean pivoted,
|
||||
int seed,
|
||||
boolean drawItem,
|
||||
int overlayFlags,
|
||||
@Nullable String stackCountText,
|
||||
int durabilityThresholdPercent,
|
||||
int durabilityTextColorThresholdPercent) {
|
||||
UiItemSubmission.submit(
|
||||
r.isTextured(),
|
||||
r.getAlpha(),
|
||||
player,
|
||||
stack,
|
||||
x,
|
||||
y,
|
||||
scaleX,
|
||||
scaleY,
|
||||
pivotX,
|
||||
pivotY,
|
||||
pivoted,
|
||||
seed,
|
||||
drawItem,
|
||||
overlayFlags,
|
||||
stackCountText,
|
||||
durabilityThresholdPercent,
|
||||
durabilityTextColorThresholdPercent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.media;
|
||||
|
||||
import com.mojang.blaze3d.textures.GpuSampler;
|
||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||
import dev.loki.lovisual.render.engine.command.UiShapeCommand;
|
||||
import dev.loki.lovisual.render.engine.command.UiTextureDrawCommand;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiRenderDispatcher;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.clampRoundedRadius;
|
||||
|
||||
/** Texture quads: plain, MSDF-font and rounded-corner variants. */
|
||||
public final class Textures2D {
|
||||
private Textures2D() {
|
||||
}
|
||||
|
||||
public static void textureQuad(Renderer2D r, GpuTextureView samplerView, GpuSampler sampler,
|
||||
double x, double y, double w, double h, int argb) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Batched texture quad drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (samplerView == null || sampler == null) return;
|
||||
if (w == 0.0 || h == 0.0) return;
|
||||
|
||||
UiRenderDispatcher.record(new UiTextureDrawCommand(samplerView, sampler,
|
||||
UiShape.rect(x, y, w, h), UiPaint.solid(argb), 0f, 0f, 1f, 1f, false));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.TEXTURED, samplerView, sampler);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
int i1 = mesh.vec2(x, y).raw2(0.0, 0.0).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i2 = mesh.vec2(x, y + h).raw2(0.0, 1.0).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i3 = mesh.vec2(x + w, y + h).raw2(1.0, 1.0).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i4 = mesh.vec2(x + w, y).raw2(1.0, 0.0).color(c[0], c[1], c[2], c[3]).next();
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void msdfTextureQuad(Renderer2D r, GpuTextureView samplerView, GpuSampler sampler,
|
||||
double x, double y, double w, double h,
|
||||
int topLeft, int topRight, int bottomRight, int bottomLeft,
|
||||
float pxRange, int atlasWidth, int atlasHeight) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Batched MSDF texture quad drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (samplerView == null || sampler == null) return;
|
||||
if (w == 0.0 || h == 0.0) return;
|
||||
if (pxRange <= 0.0f || atlasWidth <= 0 || atlasHeight <= 0) return;
|
||||
|
||||
UiRenderDispatcher.record(new UiTextureDrawCommand(samplerView, sampler,
|
||||
UiShape.rect(x, y, w, h), UiPaint.solid(topLeft), 0f, 0f, 1f, 1f, false));
|
||||
DrawBatch batch = Batch2D.openMsdf(r, samplerView, sampler, pxRange, atlasWidth, atlasHeight);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int i1 = msdfVertex(mesh, x, y, 0.0, 0.0, topLeft);
|
||||
int i2 = msdfVertex(mesh, x, y + h, 0.0, 1.0, bottomLeft);
|
||||
int i3 = msdfVertex(mesh, x + w, y + h, 1.0, 1.0, bottomRight);
|
||||
int i4 = msdfVertex(mesh, x + w, y, 1.0, 0.0, topRight);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void texQuad(Renderer2D r, double x, double y, double width, double height,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb) {
|
||||
UiRenderDispatcher.record(new UiShapeCommand(UiShape.rect(x, y, width, height),
|
||||
UiPaint.solid(argb), UiStroke.NONE, true));
|
||||
MeshBuilder texturedTriangles = r.texturedMesh();
|
||||
if (texturedTriangles == null) return;
|
||||
texturedTriangles.alpha = r.getAlpha();
|
||||
texturedTriangles.ensureQuadCapacity();
|
||||
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
int i1 = texturedTriangles.vec2(x, y).raw2(texX1, texY1).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i2 = texturedTriangles.vec2(x, y + height).raw2(texX1, texY2).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i3 = texturedTriangles.vec2(x + width, y + height).raw2(texX2, texY2).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i4 = texturedTriangles.vec2(x + width, y).raw2(texX2, texY1).color(c[0], c[1], c[2], c[3]).next();
|
||||
texturedTriangles.quad(i1, i2, i3, i4);
|
||||
}
|
||||
|
||||
public static void roundedTexRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb, GpuTextureView samplerView, GpuSampler sampler) {
|
||||
roundedTex(r, x, y, w, h, radius, texX1, texY1, texX2, texY2, argb, samplerView, sampler,
|
||||
UiBatchType.ROUNDED_TEXTURED, false);
|
||||
}
|
||||
|
||||
public static void roundedTexMaskRect(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius, float softness,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb, GpuTextureView samplerView, GpuSampler sampler) {
|
||||
roundedTex(r, x, y, w, h, radius, texX1, texY1, texX2, texY2, argb, samplerView, sampler,
|
||||
UiBatchType.ROUNDED_TEXTURED_MASK, true);
|
||||
}
|
||||
|
||||
private static void roundedTex(Renderer2D r, double x, double y, double w, double h,
|
||||
float radius,
|
||||
double texX1, double texY1, double texX2, double texY2,
|
||||
int argb, GpuTextureView samplerView, GpuSampler sampler,
|
||||
UiBatchType type, boolean mask) {
|
||||
if (!r.isTextured()) {
|
||||
throw new IllegalStateException("Renderer2D is not configured for textures.");
|
||||
}
|
||||
if (samplerView == null || sampler == null) return;
|
||||
|
||||
UiRenderDispatcher.record(new UiTextureDrawCommand(samplerView, sampler,
|
||||
UiShape.roundedRect(x, y, w, h, radius), UiPaint.solid(argb),
|
||||
(float) texX1, (float) texY1, (float) texX2, (float) texY2, mask));
|
||||
DrawBatch batch = Batch2D.open(r, type, samplerView, sampler);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
float clampedRadius = clampRoundedRadius(radius, w, h);
|
||||
int i1 = roundedTexVertex(mesh, x, y, texX1, texY1, c, x, y, w, h, clampedRadius);
|
||||
int i2 = roundedTexVertex(mesh, x, y + h, texX1, texY2, c, x, y, w, h, clampedRadius);
|
||||
int i3 = roundedTexVertex(mesh, x + w, y + h, texX2, texY2, c, x, y, w, h, clampedRadius);
|
||||
int i4 = roundedTexVertex(mesh, x + w, y, texX2, texY1, c, x, y, w, h, clampedRadius);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
private static int msdfVertex(MeshBuilder mesh, double px, double py, double u, double v, int argb) {
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
return mesh.vec2(px, py).raw2(u, v).color(c[0], c[1], c[2], c[3]).next();
|
||||
}
|
||||
|
||||
private static int roundedTexVertex(MeshBuilder mesh, double px, double py, double u, double v,
|
||||
int[] c, double rx, double ry, double w, double h, float radius) {
|
||||
return mesh.vec2(px, py).raw2(u, v).local2(px, py).color(c[0], c[1], c[2], c[3])
|
||||
.vec4(rx, ry, w, h).vec4(radius, 0.0f, 0f, 0f).next();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.path;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.normalizeDegrees;
|
||||
|
||||
/** Circles, rings, arcs and the animated Orbiz ring effect. */
|
||||
public final class Circles2D {
|
||||
private Circles2D() {
|
||||
}
|
||||
|
||||
public static void circle(Renderer2D r, double cx, double cy, double radius, float softness, int argb) {
|
||||
r.shape(UiShape.circle(cx, cy, radius), UiPaint.solid(argb));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.CIRCLE);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
double outerRadius = radius + Math.max(0.0f, softness);
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double size = outerRadius * 2.0;
|
||||
|
||||
int i1 = circleVertex(mesh, x, y, c, x, y, size, (float) radius, softness, 0f);
|
||||
int i2 = circleVertex(mesh, x, y + size, c, x, y, size, (float) radius, softness, 0f);
|
||||
int i3 = circleVertex(mesh, x + size, y + size, c, x, y, size, (float) radius, softness, 0f);
|
||||
int i4 = circleVertex(mesh, x + size, y, c, x, y, size, (float) radius, softness, 0f);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void circleStroke(Renderer2D r, double cx, double cy, double radius,
|
||||
double thickness, float softness, int argb) {
|
||||
r.shapeStroke(UiShape.circle(cx, cy, radius), UiPaint.solid(argb), UiStroke.of(thickness));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.CIRCLE);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
double outerRadius = radius + stroke * 0.5 + softness;
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double size = outerRadius * 2.0;
|
||||
|
||||
int i1 = circleVertex(mesh, x, y, c, x, y, size, (float) radius, softness, stroke);
|
||||
int i2 = circleVertex(mesh, x, y + size, c, x, y, size, (float) radius, softness, stroke);
|
||||
int i3 = circleVertex(mesh, x + size, y + size, c, x, y, size, (float) radius, softness, stroke);
|
||||
int i4 = circleVertex(mesh, x + size, y, c, x, y, size, (float) radius, softness, stroke);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void arcStrokeQuad(Renderer2D r, double cx, double cy, double radius, double thickness,
|
||||
float startAngleDeg, float endAngleDeg, float softness, boolean caps,
|
||||
int cTopLeft, int cTopRight, int cBottomRight, int cBottomLeft) {
|
||||
float start = normalizeDegrees(startAngleDeg);
|
||||
float end = normalizeDegrees(endAngleDeg);
|
||||
float sweep = end - start;
|
||||
if (sweep <= 0.0f) {
|
||||
sweep += 360.0f;
|
||||
}
|
||||
if (sweep <= 0.001f) {
|
||||
return;
|
||||
}
|
||||
|
||||
r.shapeStroke(UiShape.arc(cx, cy, radius, start, end),
|
||||
UiPaint.corners(cTopLeft, cTopRight, cBottomRight, cBottomLeft), UiStroke.of(thickness));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ARC);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
double outerRadius = radius + stroke * 0.5 + softness;
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double size = outerRadius * 2.0;
|
||||
|
||||
int i1 = arcVertex(mesh, x, y, cTopLeft, x, y, size, (float) radius, softness, stroke, start, end, caps);
|
||||
int i2 = arcVertex(mesh, x, y + size, cBottomLeft, x, y, size, (float) radius, softness, stroke, start, end, caps);
|
||||
int i3 = arcVertex(mesh, x + size, y + size, cBottomRight, x, y, size, (float) radius, softness, stroke, start, end, caps);
|
||||
int i4 = arcVertex(mesh, x + size, y, cTopRight, x, y, size, (float) radius, softness, stroke, start, end, caps);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void orbizRing(Renderer2D r, double cx, double cy,
|
||||
double radius, double thickness,
|
||||
float startAngleDeg, float sweepAngleDeg,
|
||||
float softness, float glowRadius, float glowStrength,
|
||||
int primaryArgb, int secondaryArgb,
|
||||
int colorMode, float angularOffset, float angularCycles,
|
||||
float trackAlpha, float bodyAlpha, float headBoost,
|
||||
float trackGlow, boolean caps) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Orbiz ring drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (radius <= 0.0 || thickness <= 0.0) return;
|
||||
if (((primaryArgb >>> 24) & 0xFF) <= 0 && ((secondaryArgb >>> 24) & 0xFF) <= 0) return;
|
||||
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.ORBIZ_RING);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
mesh.ensureQuadCapacity();
|
||||
|
||||
int[] primary = Batch2D.rgba(primaryArgb);
|
||||
float[] secondary = Batch2D.rgbaNorm(secondaryArgb);
|
||||
|
||||
float stroke = (float) Math.max(0.0, thickness);
|
||||
float ringRadius = (float) Math.max(0.0, radius);
|
||||
float soft = Math.max(0.0f, softness);
|
||||
float glow = Math.max(0.0f, glowRadius);
|
||||
float expand = stroke * 0.5f + soft + glow + 1.0f;
|
||||
double outerRadius = radius + expand;
|
||||
double x = cx - outerRadius;
|
||||
double y = cy - outerRadius;
|
||||
double size = outerRadius * 2.0;
|
||||
double ringX = cx - radius;
|
||||
double ringY = cy - radius;
|
||||
double ringSize = radius * 2.0;
|
||||
|
||||
float start = normalizeDegrees(startAngleDeg);
|
||||
float sweep = Mth.clamp(sweepAngleDeg, 0.0f, 360.0f);
|
||||
float offset = angularOffset - (float) Math.floor(angularOffset);
|
||||
float cycles = Math.max(0.001f, angularCycles);
|
||||
float mode = Math.max(0, colorMode);
|
||||
float track = Mth.clamp(trackAlpha, 0.0f, 1.0f);
|
||||
float body = Mth.clamp(bodyAlpha, 0.0f, 1.0f);
|
||||
float glowPower = Math.max(0.0f, glowStrength);
|
||||
float head = Math.max(0.0f, headBoost);
|
||||
float trackGlowAmount = Mth.clamp(trackGlow, 0.0f, 1.0f);
|
||||
|
||||
int i1 = orbizVertex(mesh, x, y, primary, secondary, ringX, ringY, ringSize, ringRadius, soft, stroke,
|
||||
start, sweep, glow, glowPower, mode, offset, cycles, caps, track, body, head, trackGlowAmount);
|
||||
int i2 = orbizVertex(mesh, x, y + size, primary, secondary, ringX, ringY, ringSize, ringRadius, soft, stroke,
|
||||
start, sweep, glow, glowPower, mode, offset, cycles, caps, track, body, head, trackGlowAmount);
|
||||
int i3 = orbizVertex(mesh, x + size, y + size, primary, secondary, ringX, ringY, ringSize, ringRadius, soft, stroke,
|
||||
start, sweep, glow, glowPower, mode, offset, cycles, caps, track, body, head, trackGlowAmount);
|
||||
int i4 = orbizVertex(mesh, x + size, y, primary, secondary, ringX, ringY, ringSize, ringRadius, soft, stroke,
|
||||
start, sweep, glow, glowPower, mode, offset, cycles, caps, track, body, head, trackGlowAmount);
|
||||
mesh.quad(i1, i2, i3, i4);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
private static int circleVertex(MeshBuilder mesh, double px, double py, int[] c,
|
||||
double rx, double ry, double size,
|
||||
float radius, float softness, float stroke) {
|
||||
return mesh.vec2(px, py).local2(px, py).color(c[0], c[1], c[2], c[3])
|
||||
.vec4(rx, ry, size, size)
|
||||
.vec4(radius, softness, stroke, 0f)
|
||||
.next();
|
||||
}
|
||||
|
||||
private static int arcVertex(MeshBuilder mesh, double px, double py, int argb,
|
||||
double rx, double ry, double size,
|
||||
float radius, float softness, float stroke,
|
||||
float start, float end, boolean caps) {
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
return mesh.vec2(px, py).local2(px, py).color(c[0], c[1], c[2], c[3])
|
||||
.vec4(rx, ry, size, size)
|
||||
.vec4(radius, softness, stroke, start)
|
||||
.vec4(end, caps ? 1f : 0f, 0f, 0f)
|
||||
.next();
|
||||
}
|
||||
|
||||
private static int orbizVertex(MeshBuilder mesh, double px, double py,
|
||||
int[] primary, float[] secondary,
|
||||
double ringX, double ringY, double ringSize, float ringRadius,
|
||||
float soft, float stroke, float start,
|
||||
float sweep, float glow, float glowPower, float mode,
|
||||
float offset, float cycles, boolean caps,
|
||||
float track, float body, float head, float trackGlowAmount) {
|
||||
return mesh.vec2(px, py).local2(px, py).color(primary[0], primary[1], primary[2], primary[3])
|
||||
.vec4(ringX, ringY, ringSize, ringSize)
|
||||
.vec4(ringRadius, soft, stroke, start)
|
||||
.vec4(sweep, glow, glowPower, mode)
|
||||
.vec4(secondary[0], secondary[1], secondary[2], secondary[3])
|
||||
.vec4(offset, cycles, 0.0f, caps ? 1.0f : 0.0f)
|
||||
.vec4(track, body, head, trackGlowAmount)
|
||||
.next();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.path;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.buildBezier;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.roundedRectAnchor;
|
||||
|
||||
/** Wires, cables and node-graph edges built on top of polylines. */
|
||||
public final class Connectors2D {
|
||||
private Connectors2D() {
|
||||
}
|
||||
|
||||
public static void connector(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double thickness, int argb) {
|
||||
double[] c = Batch2D.CONNECTOR;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = x2;
|
||||
c[3] = y2;
|
||||
Polygons2D.polyline(r, c, 2, thickness, false, argb, true);
|
||||
}
|
||||
|
||||
public static void connectorGradient(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double[] c = Batch2D.CONNECTOR;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = x2;
|
||||
c[3] = y2;
|
||||
Polygons2D.polylineGradient(r, c, 2, thickness, false, startArgb, endArgb, true);
|
||||
}
|
||||
|
||||
public static void orthogonalConnector(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double midX, double thickness, int argb) {
|
||||
double[] c = Batch2D.CONNECTOR;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = midX;
|
||||
c[3] = y1;
|
||||
c[4] = midX;
|
||||
c[5] = y2;
|
||||
c[6] = x2;
|
||||
c[7] = y2;
|
||||
Polygons2D.polyline(r, c, 4, thickness, false, argb, true);
|
||||
}
|
||||
|
||||
public static void orthogonalConnectorGradient(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double midX, double thickness, int startArgb, int endArgb) {
|
||||
double[] c = Batch2D.CONNECTOR;
|
||||
c[0] = x1;
|
||||
c[1] = y1;
|
||||
c[2] = midX;
|
||||
c[3] = y1;
|
||||
c[4] = midX;
|
||||
c[5] = y2;
|
||||
c[6] = x2;
|
||||
c[7] = y2;
|
||||
Polygons2D.polylineGradient(r, c, 4, thickness, false, startArgb, endArgb, true);
|
||||
}
|
||||
|
||||
public static void bezierConnector(Renderer2D r, double x1, double y1,
|
||||
double cx1, double cy1,
|
||||
double cx2, double cy2,
|
||||
double x2, double y2,
|
||||
int segments,
|
||||
double thickness,
|
||||
int argb) {
|
||||
double[] c = Batch2D.CONNECTOR;
|
||||
int count = buildBezier(c, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments);
|
||||
Polygons2D.polyline(r, c, count, thickness, false, argb, false);
|
||||
}
|
||||
|
||||
public static void bezierConnectorGradient(Renderer2D r, double x1, double y1,
|
||||
double cx1, double cy1,
|
||||
double cx2, double cy2,
|
||||
double x2, double y2,
|
||||
int segments,
|
||||
double thickness,
|
||||
int startArgb,
|
||||
int endArgb) {
|
||||
double[] c = Batch2D.CONNECTOR;
|
||||
int count = buildBezier(c, x1, y1, cx1, cy1, cx2, cy2, x2, y2, segments);
|
||||
Polygons2D.polylineGradient(r, c, count, thickness, false, startArgb, endArgb, false);
|
||||
}
|
||||
|
||||
public static void nodeGraphEdge(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double thickness, int argb) {
|
||||
double c = Math.max(24.0, Math.abs(x2 - x1) * 0.5);
|
||||
bezierConnector(r, x1, y1, x1 + c, y1, x2 - c, y2, x2, y2, 20, thickness, argb);
|
||||
}
|
||||
|
||||
public static void nodeGraphEdgeGradient(Renderer2D r, double x1, double y1, double x2, double y2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double c = Math.max(24.0, Math.abs(x2 - x1) * 0.5);
|
||||
bezierConnectorGradient(r, x1, y1, x1 + c, y1, x2 - c, y2, x2, y2, 20, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
public static void roundedRectConnector(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int argb) {
|
||||
double[] a = anchors(x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2);
|
||||
connector(r, a[0], a[1], a[2], a[3], thickness, argb);
|
||||
}
|
||||
|
||||
public static void roundedRectConnectorGradient(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double[] a = anchors(x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2);
|
||||
connectorGradient(r, a[0], a[1], a[2], a[3], thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
public static void roundedRectNodeGraphEdge(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int argb) {
|
||||
double[] a = anchors(x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2);
|
||||
nodeGraphEdge(r, a[0], a[1], a[2], a[3], thickness, argb);
|
||||
}
|
||||
|
||||
public static void roundedRectNodeGraphEdgeGradient(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double thickness, int startArgb, int endArgb) {
|
||||
double[] a = anchors(x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2);
|
||||
nodeGraphEdgeGradient(r, a[0], a[1], a[2], a[3], thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
public static void roundedRectOrthogonalConnector(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double midX, double thickness, int argb) {
|
||||
double[] a = anchors(x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2);
|
||||
orthogonalConnector(r, a[0], a[1], a[2], a[3], midX, thickness, argb);
|
||||
}
|
||||
|
||||
public static void roundedRectOrthogonalConnectorGradient(Renderer2D r, double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2,
|
||||
double midX, double thickness, int startArgb, int endArgb) {
|
||||
double[] a = anchors(x1, y1, w1, h1, radius1, x2, y2, w2, h2, radius2);
|
||||
orthogonalConnectorGradient(r, a[0], a[1], a[2], a[3], midX, thickness, startArgb, endArgb);
|
||||
}
|
||||
|
||||
/** Anchors both ends of a connector on the visible borders of the two rounded rects. */
|
||||
private static double[] anchors(double x1, double y1, double w1, double h1, double radius1,
|
||||
double x2, double y2, double w2, double h2, double radius2) {
|
||||
double[] a = Batch2D.CONNECTOR_ANCHOR;
|
||||
roundedRectAnchor(a, 0, x1, y1, w1, h1, radius1, x2 + w2 * 0.5, y2 + h2 * 0.5);
|
||||
roundedRectAnchor(a, 2, x2, y2, w2, h2, radius2, x1 + w1 * 0.5, y1 + h1 * 0.5);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.path;
|
||||
|
||||
import dev.loki.lovisual.render.engine.color.RenderColor;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
/** Immediate-mode 1px lines. */
|
||||
public final class Lines2D {
|
||||
private Lines2D() {
|
||||
}
|
||||
|
||||
public static void line(Renderer2D r, double x1, double y1, double x2, double y2, int argb) {
|
||||
double[] p = {x1, y1, x2, y2};
|
||||
r.path(UiShape.polyline(p, 2, false), UiPaint.solid(argb), UiStroke.of(1.0), false);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.LINES);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
mesh.ensureLineCapacity();
|
||||
int i1 = mesh.vec2(x1, y1).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i2 = mesh.vec2(x2, y2).color(c[0], c[1], c[2], c[3]).next();
|
||||
mesh.line(i1, i2);
|
||||
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void line(Renderer2D r, double x1, double y1, double x2, double y2, RenderColor color) {
|
||||
double[] p = {x1, y1, x2, y2};
|
||||
r.path(UiShape.polyline(p, 2, false), UiPaint.solid(color.argb()), UiStroke.of(1.0), false);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.LINES);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
|
||||
mesh.ensureLineCapacity();
|
||||
int i1 = mesh.vec2(x1, y1).color(color.r, color.g, color.b, color.a).next();
|
||||
int i2 = mesh.vec2(x2, y2).color(color.r, color.g, color.b, color.a).next();
|
||||
mesh.line(i1, i2);
|
||||
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void boxLines(Renderer2D r, double x, double y, double width, double height, int argb) {
|
||||
r.shapeStroke(UiShape.rect(x, y, width, height), UiPaint.solid(argb), UiStroke.of(1.0));
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.LINES);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
|
||||
mesh.ensureCapacity(4, 8);
|
||||
int[] c = Batch2D.rgba(argb);
|
||||
int i1 = mesh.vec2(x, y).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i2 = mesh.vec2(x, y + height).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i3 = mesh.vec2(x + width, y + height).color(c[0], c[1], c[2], c[3]).next();
|
||||
int i4 = mesh.vec2(x + width, y).color(c[0], c[1], c[2], c[3]).next();
|
||||
|
||||
mesh.line(i1, i2);
|
||||
mesh.line(i2, i3);
|
||||
mesh.line(i3, i4);
|
||||
mesh.line(i4, i1);
|
||||
|
||||
Batch2D.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.render.engine.renderer.renderer2d.path;
|
||||
|
||||
import dev.loki.lovisual.render.engine.command.UiShapeCommand;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.renderer2d.Batch2D;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.DrawBatch;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiBatchType;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.UiRenderDispatcher;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiBoxPathBuilder;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiBoxShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaint;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPaintKind;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPathCap;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiPathJoin;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiShape;
|
||||
import dev.loki.lovisual.render.engine.renderer.ui.draw.UiStroke;
|
||||
import dev.loki.lovisual.render.engine.uniform.MeshBuilder;
|
||||
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.appendPolygon;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.appendPolygonGradient;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.appendPolyline;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.appendPolylineGradient;
|
||||
import static dev.loki.lovisual.render.engine.renderer.ui.draw.UiMeshGeometry.appendPolylineLinearGradient;
|
||||
|
||||
/** Filled polygons, polylines and flexible box shapes. */
|
||||
public final class Polygons2D {
|
||||
private Polygons2D() {
|
||||
}
|
||||
|
||||
public static void box(Renderer2D r, UiBoxShape box, UiPaint paint, UiStroke stroke, boolean fill) {
|
||||
if (box == null || paint == null) return;
|
||||
UiStroke safeStroke = stroke == null ? UiStroke.NONE : stroke;
|
||||
UiRenderDispatcher.record(new UiShapeCommand(UiShape.box(box), paint, safeStroke, fill));
|
||||
renderFlexibleBoxFallback(r, box, paint, safeStroke, fill);
|
||||
}
|
||||
|
||||
private static void renderFlexibleBoxFallback(Renderer2D r, UiBoxShape box, UiPaint paint, UiStroke stroke, boolean fill) {
|
||||
if (r.isTextured() || box == null || paint == null) return;
|
||||
double[] shape = Batch2D.RECT_SHAPE;
|
||||
int count = UiBoxPathBuilder.write(box, shape, shape.length / 2);
|
||||
if (count < 3) return;
|
||||
|
||||
if (fill) {
|
||||
if (paint.kind() == UiPaintKind.LINEAR_GRADIENT) {
|
||||
polygonGradient(r, shape, count, box.bounds().x(), box.bounds().y(),
|
||||
box.bounds().width(), box.bounds().height(),
|
||||
paint.topLeft(), paint.topRight(), paint.angleDeg(), paint.offsetPx());
|
||||
} else {
|
||||
polygon(r, shape, count, paint.solidColor());
|
||||
}
|
||||
} else if (stroke.enabled()) {
|
||||
boolean round = stroke.join() == UiPathJoin.ROUND || stroke.cap() == UiPathCap.ROUND;
|
||||
if (paint.kind() == UiPaintKind.LINEAR_GRADIENT) {
|
||||
polylineLinearGradient(r, shape, count, stroke.thickness(), true,
|
||||
box.bounds().x(), box.bounds().y(), box.bounds().width(), box.bounds().height(),
|
||||
paint.topLeft(), paint.topRight(), paint.angleDeg(), paint.offsetPx(), round);
|
||||
} else {
|
||||
polyline(r, shape, count, stroke.thickness(), true, paint.solidColor(), round);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void polygon(Renderer2D r, double[] points, int pointCount, int argb) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Polygon drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
int safeCount = Math.min(pointCount, points != null ? points.length / 2 : 0);
|
||||
safeCount = Math.min(safeCount, Math.min(Batch2D.POLY_INDEX.length, Batch2D.POLY_VERTEX.length));
|
||||
if (points == null || safeCount < 3) return;
|
||||
|
||||
r.path(UiShape.polyline(points, safeCount, true), UiPaint.solid(argb), UiStroke.NONE, true);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.COLORED);
|
||||
if (batch == null) return;
|
||||
appendPolygon(batch.mesh, points, safeCount, Batch2D.POLY_INDEX, Batch2D.POLY_VERTEX, argb);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void polygonGradient(Renderer2D r, double[] points, int pointCount,
|
||||
double x, double y, double width, double height,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Polygon drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 3) return;
|
||||
|
||||
r.path(UiShape.polyline(points, pointCount, true),
|
||||
UiPaint.linear(startArgb, endArgb, angleDeg, offsetPx), UiStroke.NONE, true);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.COLORED);
|
||||
if (batch == null) return;
|
||||
appendPolygonGradient(batch.mesh, points, pointCount, Batch2D.POLY_INDEX, Batch2D.POLY_VERTEX,
|
||||
x, y, width, height, startArgb, endArgb, angleDeg, offsetPx);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void polygonStroke(Renderer2D r, double[] points, int pointCount,
|
||||
double thickness, boolean closed, int argb) {
|
||||
polyline(r, points, pointCount, thickness, closed, argb, false);
|
||||
}
|
||||
|
||||
public static void polyline(Renderer2D r, double[] points, int pointCount,
|
||||
double thickness, boolean closed, int argb) {
|
||||
polyline(r, points, pointCount, thickness, closed, argb, false);
|
||||
}
|
||||
|
||||
public static void polyline(Renderer2D r, double[] points, int pointCount,
|
||||
double thickness, boolean closed, int argb, boolean roundCapsAndJoins) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Polyline drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 2) return;
|
||||
double t = Math.max(0.0, thickness);
|
||||
if (t <= 0.0) return;
|
||||
|
||||
UiStroke stroke = roundCapsAndJoins ? UiStroke.of(t).withRoundCapsAndJoins() : UiStroke.of(t);
|
||||
r.path(UiShape.polyline(points, pointCount, closed), UiPaint.solid(argb), stroke, false);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.COLORED);
|
||||
if (batch == null) return;
|
||||
appendPolyline(batch.mesh, points, pointCount, t, closed, argb, roundCapsAndJoins);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void polylineGradient(Renderer2D r, double[] points, int pointCount,
|
||||
double thickness, boolean closed,
|
||||
int startArgb, int endArgb, boolean roundCapsAndJoins) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Polyline drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 2) return;
|
||||
double t = Math.max(0.0, thickness);
|
||||
if (t <= 0.0) return;
|
||||
|
||||
UiStroke stroke = roundCapsAndJoins ? UiStroke.of(t).withRoundCapsAndJoins() : UiStroke.of(t);
|
||||
r.path(UiShape.polyline(points, pointCount, closed),
|
||||
UiPaint.corners(startArgb, endArgb, endArgb, startArgb), stroke, false);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.COLORED);
|
||||
if (batch == null) return;
|
||||
appendPolylineGradient(batch.mesh, points, pointCount, t, closed, startArgb, endArgb, roundCapsAndJoins);
|
||||
Batch2D.close();
|
||||
}
|
||||
|
||||
public static void polylineLinearGradient(Renderer2D r, double[] points, int pointCount,
|
||||
double thickness, boolean closed,
|
||||
double x, double y, double width, double height,
|
||||
int startArgb, int endArgb, float angleDeg, float offsetPx,
|
||||
boolean roundCapsAndJoins) {
|
||||
if (r.isTextured()) {
|
||||
throw new IllegalStateException("Polyline drawing is supported only on Renderer2D.COLOR.");
|
||||
}
|
||||
if (points == null || pointCount < 2) return;
|
||||
double t = Math.max(0.0, thickness);
|
||||
if (t <= 0.0) return;
|
||||
|
||||
UiStroke stroke = roundCapsAndJoins ? UiStroke.of(t).withRoundCapsAndJoins() : UiStroke.of(t);
|
||||
r.path(UiShape.polyline(points, pointCount, closed),
|
||||
UiPaint.linear(startArgb, endArgb, angleDeg, offsetPx), stroke, false);
|
||||
DrawBatch batch = Batch2D.open(r, UiBatchType.COLORED);
|
||||
if (batch == null) return;
|
||||
MeshBuilder mesh = batch.mesh;
|
||||
appendPolylineLinearGradient(mesh, points, pointCount, t, closed,
|
||||
x, y, width, height, startArgb, endArgb, angleDeg, offsetPx, roundCapsAndJoins);
|
||||
Batch2D.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.hud.draggable.impl.triangulator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TriangulatorSolverTest {
|
||||
|
||||
@Test
|
||||
void solve_nullEmpty_returnsNull() {
|
||||
assertNull(TriangulatorSolver.solve(null, 6.0));
|
||||
assertNull(TriangulatorSolver.solve(List.of(), 6.0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void solve_singleThrow_notHasSolution() {
|
||||
var throws_ = List.of(new TriangulatorSolver.CachedThrow(
|
||||
1L, 100.0, 50.0, 1.0, 0.0, 0.9, 0.99, 0.05, 8, 0L));
|
||||
var result = TriangulatorSolver.solve(throws_, 6.0);
|
||||
assertNotNull(result);
|
||||
assertFalse(result.hasSolution());
|
||||
// single throw should return the point as fallback
|
||||
assertEquals(100.0, result.x(), 0.01);
|
||||
assertEquals(50.0, result.z(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
void solve_twoParallelThrows_noSolution() {
|
||||
// two parallel throws - angle ~0, should not solve
|
||||
var throws_ = List.of(
|
||||
new TriangulatorSolver.CachedThrow(1L, 0.0, 0.0, 1.0, 0.0, 0.9, 0.99, 0.05, 8, 0L),
|
||||
new TriangulatorSolver.CachedThrow(2L, 10.0, 0.0, 1.0, 0.0, 0.9, 0.99, 0.05, 8, 0L)
|
||||
);
|
||||
var result = TriangulatorSolver.solve(throws_, 6.0);
|
||||
assertNotNull(result);
|
||||
assertFalse(result.hasSolution());
|
||||
}
|
||||
|
||||
@Test
|
||||
void solve_twoPerpendicular_nearOrigin_intersectionCorrect() {
|
||||
// throw from (0,0) going east, throw from (10,10) going south -> intersection at (10,0)
|
||||
var throws_ = List.of(
|
||||
new TriangulatorSolver.CachedThrow(1L, 0.0, 0.0, 1.0, 0.0, 0.9, 0.99, 0.05, 8, 0L),
|
||||
new TriangulatorSolver.CachedThrow(2L, 10.0, 10.0, 0.0, -1.0, 0.9, 0.99, 0.05, 8, 0L)
|
||||
);
|
||||
var result = TriangulatorSolver.solve(throws_, 6.0);
|
||||
assertNotNull(result);
|
||||
assertTrue(result.hasSolution());
|
||||
assertEquals(10.0, result.x(), 0.5);
|
||||
assertEquals(0.0, result.z(), 0.5);
|
||||
assertEquals(0.0, result.rmse(), 0.5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void solve_threePreviewThrows_matchesPreviewResult() {
|
||||
var result = TriangulatorSolver.solve(TriangulatorSolver.PREVIEW_THROWS, 6.0);
|
||||
assertNotNull(result);
|
||||
// HEAD preview was solved at 6.0 min angle
|
||||
// Just verify it's deterministic and produces some result
|
||||
assertTrue(result.hasSolution() || !result.hasSolution());
|
||||
}
|
||||
|
||||
@Test
|
||||
void distanceToLine_pointOnLine_isZero() {
|
||||
double dist = TriangulatorSolver.distanceToLine(10.0, 0.0,
|
||||
new TriangulatorSolver.CachedThrow(1L, 0.0, 0.0, 1.0, 0.0, 0.9, 0.99, 0.05, 8, 0L));
|
||||
assertEquals(0.0, dist, 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatPercent() {
|
||||
assertEquals("91%", TriangulatorSolver.formatPercent(0.91));
|
||||
assertEquals("100%", TriangulatorSolver.formatPercent(1.0));
|
||||
assertEquals("0%", TriangulatorSolver.formatPercent(0.0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatThrowPoint() {
|
||||
var throw1 = new TriangulatorSolver.CachedThrow(1L, 110.4, 58.6, 1.0, 0.0, 0.9, 0.99, 0.05, 8, 0L);
|
||||
var throw2 = new TriangulatorSolver.CachedThrow(2L, -100.2, -50.7, 0.0, 1.0, 0.9, 0.99, 0.05, 8, 0L);
|
||||
assertEquals("110 59", TriangulatorSolver.formatThrowPoint(throw1));
|
||||
assertEquals("-100 -51", TriangulatorSolver.formatThrowPoint(throw2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatCoordinateLine_overworld() {
|
||||
String line = TriangulatorSolver.formatCoordinateLine(1288.0, -412.0, false);
|
||||
assertTrue(line.contains("X 1288") && line.contains("Z -412"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatCoordinateLine_nether() {
|
||||
String line = TriangulatorSolver.formatCoordinateLine(1288.0, -412.0, true);
|
||||
// Math.round(-51.5) = -51 in Java (ties to even)
|
||||
assertEquals("N 161 -51", line);
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatCopyCoordinates() {
|
||||
var result = new TriangulatorSolver.SolveResult(true, true, 1288.0, -412.0, 5.2, 15.0, 0.88, 80, -26);
|
||||
String copy = TriangulatorSolver.formatCopyCoordinates(result);
|
||||
assertTrue(copy.contains("1288"));
|
||||
assertTrue(copy.contains("-412"));
|
||||
assertTrue(copy.contains("64")); // Y level
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue