Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public class LuminRenderPipelines {
.withCull(false)
.build();

public final static RenderPipeline ROUND_RECT_OUTLINE = RenderPipeline.builder(NO_BLEND_DEPTH_SNIPPET)
.withLocation(ResourceLocationUtils.getIdentifier("pipelines/round_rectangle_outline"))
.withVertexFormat(LuminVertexFormats.ROUND_RECT_OUTLINE, VertexFormat.Mode.QUADS)
.withVertexShader(ResourceLocationUtils.getIdentifier("round_rectangle_outline"))
.withFragmentShader(ResourceLocationUtils.getIdentifier("round_rectangle_outline"))
.withCull(false)
.build();

public final static RenderPipeline SHADOW = RenderPipeline.builder(NO_BLEND_DEPTH_SNIPPET)
.withLocation(ResourceLocationUtils.getIdentifier("pipelines/shadow"))
.withVertexFormat(LuminVertexFormats.ROUND_RECT, VertexFormat.Mode.QUADS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ public class LuminVertexFormats {

private static final int ROUND_INNER_RECT_ID = findNextId();
private static final int ROUND_RADIUS_ID = findNextId(ROUND_INNER_RECT_ID + 1);
private static final int ROUND_OUTLINE_WIDTH_ID = findNextId(ROUND_RADIUS_ID + 1);

public static final VertexFormatElement ROUND_INNER_RECT = VertexFormatElement.register(ROUND_INNER_RECT_ID, 2, VertexFormatElement.Type.FLOAT, false, 4);
public static final VertexFormatElement ROUND_RADIUS = VertexFormatElement.register(ROUND_RADIUS_ID, 4, VertexFormatElement.Type.FLOAT, false, 4);
public static final VertexFormatElement ROUND_OUTLINE_WIDTH = VertexFormatElement.register(ROUND_OUTLINE_WIDTH_ID, 1, VertexFormatElement.Type.FLOAT, false, 1);

public static final VertexFormat ROUND_RECT = VertexFormat.builder()
.add("Position", VertexFormatElement.POSITION)
Expand All @@ -18,6 +20,14 @@ public class LuminVertexFormats {
.add("Radius", ROUND_RADIUS)
.build();

public static final VertexFormat ROUND_RECT_OUTLINE = VertexFormat.builder()
.add("Position", VertexFormatElement.POSITION)
.add("Color", VertexFormatElement.COLOR)
.add("InnerRect", ROUND_INNER_RECT)
.add("Radius", ROUND_RADIUS)
.add("OutlineWidth", ROUND_OUTLINE_WIDTH)
.build();

public static final VertexFormat TEXTURE = VertexFormat.builder()
.add("Position", VertexFormatElement.POSITION)
.add("Color", VertexFormatElement.COLOR)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.github.epsilon.graphics.renderers;

import com.github.epsilon.graphics.LuminRenderPipelines;
import com.github.epsilon.graphics.LuminRenderSystem;
import com.github.epsilon.graphics.buffer.LuminRingBuffer;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.util.ARGB;
import org.lwjgl.system.MemoryUtil;

import java.awt.*;
import java.util.OptionalDouble;
import java.util.OptionalInt;

public class RoundRectOutlineRenderer implements IRenderer {

private static final long BUFFER_SIZE = 2 * 1024 * 1024;
private static final int STRIDE = 52;

private final LuminRingBuffer buffer = new LuminRingBuffer(BUFFER_SIZE, GpuBuffer.USAGE_VERTEX);

private boolean scissorEnabled = false;
private int scissorX, scissorY, scissorW, scissorH;
private long currentOffset = 0;
private int vertexCount = 0;

public void addOutline(float x, float y, float width, float height, float radius, float outlineWidth, Color color) {
addOutline(x, y, width, height, radius, radius, radius, radius, outlineWidth, color);
}

public void addOutline(float x, float y, float width, float height, float radiusTopLeft, float radiusTopRight, float radiusBottomRight, float radiusBottomLeft, float outlineWidth, Color color) {
addOutlineGradient(x, y, width, height, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft, outlineWidth, color, color, color, color
);
}

public void addVerticalGradient(float x, float y, float width, float height, float radius, float outlineWidth, Color top, Color bottom) {
addVerticalGradient(x, y, width, height, radius, radius, radius, radius, outlineWidth, top, bottom);
}

public void addVerticalGradient(float x, float y, float width, float height, float radiusTopLeft, float radiusTopRight, float radiusBottomRight, float radiusBottomLeft, float outlineWidth, Color top, Color bottom) {
addOutlineGradient(x, y, width, height, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft, outlineWidth, top, bottom, bottom, top);
}

public void addHorizontalGradient(float x, float y, float width, float height, float radius, float outlineWidth, Color left, Color right) {
addHorizontalGradient(x, y, width, height, radius, radius, radius, radius, outlineWidth, left, right);
}

public void addHorizontalGradient(float x, float y, float width, float height, float radiusTopLeft, float radiusTopRight, float radiusBottomRight, float radiusBottomLeft, float outlineWidth, Color left, Color right) {
addOutlineGradient(x, y, width, height, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft, outlineWidth, left, left, right, right);
}

/**
* 颜色顺序对应四个角顶点:左上、左下、右下、右上 (TL, BL, BR, TR)
*/
public void addOutlineGradient(float x, float y, float width, float height, float radiusTopLeft, float radiusTopRight, float radiusBottomRight, float radiusBottomLeft, float outlineWidth, Color colorTopLeft, Color colorBottomLeft, Color colorBottomRight, Color colorTopRight) {
if (outlineWidth <= 0.0f) return;

buffer.tryMap();

float halfOutline = outlineWidth * 0.5f;
float x2 = x + width;
float y2 = y + height;
float outerX1 = x - halfOutline;
float outerY1 = y - halfOutline;
float outerX2 = x2 + halfOutline;
float outerY2 = y2 + halfOutline;
int argbTopLeft = ARGB.toABGR(colorTopLeft.getRGB());
int argbBottomLeft = ARGB.toABGR(colorBottomLeft.getRGB());
int argbBottomRight = ARGB.toABGR(colorBottomRight.getRGB());
int argbTopRight = ARGB.toABGR(colorTopRight.getRGB());

addVertex(outerX1, outerY1, x, y, x2, y2, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft, outlineWidth, argbTopLeft);
addVertex(outerX1, outerY2, x, y, x2, y2, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft, outlineWidth, argbBottomLeft);
addVertex(outerX2, outerY2, x, y, x2, y2, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft, outlineWidth, argbBottomRight);
addVertex(outerX2, outerY1, x, y, x2, y2, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft, outlineWidth, argbTopRight);
}

private void addVertex(float vx, float vy, float rx1, float ry1, float rx2, float ry2, float r1, float r2, float r3, float r4, float outlineWidth, int color) {
long baseAddr = MemoryUtil.memAddress(buffer.getMappedBuffer());
long p = baseAddr + currentOffset;

MemoryUtil.memPutFloat(p, vx);
MemoryUtil.memPutFloat(p + 4, vy);
MemoryUtil.memPutFloat(p + 8, 0.0f);
MemoryUtil.memPutInt(p + 12, color);
MemoryUtil.memPutFloat(p + 16, rx1);
MemoryUtil.memPutFloat(p + 20, ry1);
MemoryUtil.memPutFloat(p + 24, rx2);
MemoryUtil.memPutFloat(p + 28, ry2);
MemoryUtil.memPutFloat(p + 32, r1);
MemoryUtil.memPutFloat(p + 36, r2);
MemoryUtil.memPutFloat(p + 40, r3);
MemoryUtil.memPutFloat(p + 44, r4);
MemoryUtil.memPutFloat(p + 48, outlineWidth);
currentOffset += STRIDE;
vertexCount++;
}

public void setScissor(int x, int y, int width, int height) {
scissorEnabled = true;
scissorX = x;
scissorY = y;
scissorW = width;
scissorH = height;
}

public void clearScissor() {
scissorEnabled = false;
}

@Override
public void draw() {
if (vertexCount == 0) return;
if (buffer.isMapped()) buffer.unmap();

LuminRenderSystem.QuadRenderingInfo info = LuminRenderSystem.prepareQuadRendering(vertexCount);
if (info == null || info.colorView() == null) return;

try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(
() -> "Round Rect Outline Draw", info.colorView(), OptionalInt.empty(),
info.depthView(), OptionalDouble.empty())
) {
pass.setPipeline(LuminRenderPipelines.ROUND_RECT_OUTLINE);
if (scissorEnabled) pass.enableScissor(scissorX, scissorY, scissorW, scissorH);
RenderSystem.bindDefaultUniforms(pass);
pass.setUniform("DynamicTransforms", info.dynamicUniforms());
pass.setVertexBuffer(0, buffer.getGpuBuffer());
pass.setIndexBuffer(info.ibo(), info.autoIndices().type());
pass.drawIndexed(0, 0, info.indexCount(), 1);
}
}

@Override
public void clear() {
if (vertexCount > 0) {
if (buffer.isMapped()) buffer.unmap();
buffer.rotate();
}
vertexCount = 0;
currentOffset = 0;
}

@Override
public void close() {
buffer.close();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public void queueRender(GuiGraphicsExtractor graphics, HudModule selectedModule,

settingList.layoutRows(settings, viewport, scroll, rowWidth, (_, row, rowBounds) -> {
float hover = rowBounds.contains(effectiveMouseX, effectiveMouseY) ? 1.0f : 0.0f;
row.render(graphics, contentBuffer.roundRectRenderer(), contentBuffer.rectRenderer(), contentBuffer.textRenderer(), rowBounds, hover, effectiveMouseX, effectiveMouseY, partialTick);
row.render(graphics, contentBuffer.roundRectRenderer(), contentBuffer.roundRectOutlineRenderer(), contentBuffer.rectRenderer(), contentBuffer.textRenderer(), rowBounds, hover, effectiveMouseX, effectiveMouseY, partialTick);
});

flushChrome();
Expand Down
26 changes: 25 additions & 1 deletion common/src/main/java/com/github/epsilon/gui/panel/MD3Theme.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,13 @@ public class MD3Theme {
public static final float CONTROL_HEIGHT = 18.0f;
public static final float CONTROL_RADIUS = 7.0f;
public static final float COMPACT_CHIP_HEIGHT = 16.0f;
public static final float SWITCH_WIDTH = 30.0f;
public static final float SWITCH_WIDTH = 26.0f;
public static final float SWITCH_HEIGHT = 16.0f;
public static final float SWITCH_HANDLE_SIZE_OFF = 8.0f;
public static final float SWITCH_HANDLE_SIZE_ON = 12.0f;
public static final float SWITCH_HANDLE_INSET_OFF = 4.0f;
public static final float SWITCH_HANDLE_INSET_ON = 2.0f;
public static final float SWITCH_STATE_LAYER_SIZE = 20.0f;

private MD3Theme() {
}
Expand Down Expand Up @@ -176,6 +181,25 @@ public static Color segmentedControlInactiveLabel() {
return isLightTheme() ? TEXT_SECONDARY : TEXT_MUTED;
}

public static Color switchTrack(float toggleProgress) {
return lerp(SURFACE_CONTAINER_HIGHEST, PRIMARY, toggleProgress);
}

public static Color switchKnob(float toggleProgress) {
return lerp(OUTLINE, ON_PRIMARY, toggleProgress);
}

public static Color switchTrackOutline(float toggleProgress, float hoverProgress) {
float inactive = 1.0f - Mth.clamp(toggleProgress, 0.0f, 1.0f);
float hoverMix = Mth.clamp(hoverProgress * 0.35f, 0.0f, 1.0f);
Color base = lerp(OUTLINE, TEXT_PRIMARY, hoverMix);
return withAlpha(base, (int) (inactive * (isLightTheme() ? 188 : 168)));
}

public static float switchTrackOutlineWidth(float toggleProgress) {
return 1.0f + (1.0f - Mth.clamp(toggleProgress, 0.0f, 1.0f)) * 0.1f;
}

private record ThemePalette(
Color shadow,
Color surface,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import com.github.epsilon.assets.holders.TranslateHolder;
import com.github.epsilon.graphics.LuminRenderSystem;
import com.github.epsilon.graphics.renderers.RectRenderer;
import com.github.epsilon.graphics.renderers.RoundRectRenderer;
import com.github.epsilon.graphics.renderers.ShadowRenderer;
import com.github.epsilon.graphics.renderers.TextRenderer;
import com.github.epsilon.graphics.renderers.*;
import com.github.epsilon.gui.panel.dsl.PanelRenderBatch;
import com.github.epsilon.gui.panel.input.PanelInputRouter;
import com.github.epsilon.gui.panel.panel.CategoryRailPanel;
Expand Down Expand Up @@ -39,8 +36,9 @@ public class PanelScreen extends Screen {
private final TextRenderer textRenderer = new TextRenderer();
private final RectRenderer rectRenderer = new RectRenderer();
private final RoundRectRenderer roundRectRenderer = new RoundRectRenderer();
private final RoundRectOutlineRenderer roundRectOutlineRenderer = new RoundRectOutlineRenderer();
private final ShadowRenderer shadowRenderer = new ShadowRenderer();
private final PanelRenderBatch renderBatch = new PanelRenderBatch(shadowRenderer, roundRectRenderer, rectRenderer, textRenderer);
private final PanelRenderBatch renderBatch = new PanelRenderBatch(shadowRenderer, roundRectRenderer, roundRectOutlineRenderer, rectRenderer, textRenderer);
private final PanelPopupHost popupHost = new PanelPopupHost();
private final PanelInputRouter inputRouter = new PanelInputRouter();
private final CategoryRailPanel categoryRailPanel = new CategoryRailPanel(state, rectRenderer, roundRectRenderer, textRenderer);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.epsilon.gui.panel.component;

import com.github.epsilon.graphics.renderers.RectRenderer;
import com.github.epsilon.graphics.renderers.RoundRectOutlineRenderer;
import com.github.epsilon.graphics.renderers.RoundRectRenderer;
import com.github.epsilon.graphics.renderers.TextRenderer;
import com.github.epsilon.graphics.text.ttf.TtfFontLoader;
Expand Down Expand Up @@ -59,17 +60,32 @@ public static PanelLayout.Rect compactFieldBounds(PanelLayout.Rect bounds, float
}

public static void drawSwitch(RoundRectRenderer roundRectRenderer, PanelLayout.Rect rect, float toggleProgress, float hoverProgress) {
Color track = MD3Theme.lerp(MD3Theme.SURFACE_CONTAINER_HIGHEST, MD3Theme.PRIMARY, toggleProgress);
Color knob = MD3Theme.lerp(MD3Theme.OUTLINE, MD3Theme.ON_PRIMARY, toggleProgress);

float knobSize = 8.0f + 3.0f * toggleProgress;
float knobTravel = rect.width() - 10.0f - knobSize;
float knobX = rect.x() + 5.0f + knobTravel * toggleProgress;
drawSwitch(roundRectRenderer, null, rect, toggleProgress, hoverProgress);
}

public static void drawSwitch(RoundRectRenderer roundRectRenderer, RoundRectOutlineRenderer roundRectOutlineRenderer, PanelLayout.Rect rect, float toggleProgress, float hoverProgress) {
Color track = MD3Theme.switchTrack(toggleProgress);
Color knob = MD3Theme.switchKnob(toggleProgress);
Color outline = MD3Theme.switchTrackOutline(toggleProgress, hoverProgress);
float clampedToggle = Math.clamp(toggleProgress, 0.0f, 1.0f);
float knobSize = MD3Theme.SWITCH_HANDLE_SIZE_OFF
+ (MD3Theme.SWITCH_HANDLE_SIZE_ON - MD3Theme.SWITCH_HANDLE_SIZE_OFF) * clampedToggle;
float knobStartX = rect.x() + MD3Theme.SWITCH_HANDLE_INSET_OFF;
float knobEndX = rect.right() - MD3Theme.SWITCH_HANDLE_INSET_ON - knobSize;
float knobX = knobStartX + (knobEndX - knobStartX) * clampedToggle;
float knobY = rect.centerY() - knobSize / 2.0f;

roundRectRenderer.addRoundRect(rect.x(), rect.y(), rect.width(), rect.height(), rect.height() / 2.0f, track);
if (outline.getAlpha() > 0 && roundRectOutlineRenderer != null) {
roundRectOutlineRenderer.addOutline(
rect.x(), rect.y(), rect.width(), rect.height(),
rect.height() / 2.0f,
MD3Theme.switchTrackOutlineWidth(toggleProgress),
outline
);
}
if (hoverProgress > 0.02f) {
float haloSize = 16.0f;
float haloSize = MD3Theme.SWITCH_STATE_LAYER_SIZE;
float haloX = knobX + knobSize / 2.0f - haloSize / 2.0f;
float haloY = rect.centerY() - haloSize / 2.0f;
roundRectRenderer.addRoundRect(haloX, haloY, haloSize, haloSize, haloSize / 2.0f,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.epsilon.gui.panel.component;

import com.github.epsilon.graphics.renderers.RectRenderer;
import com.github.epsilon.graphics.renderers.RoundRectOutlineRenderer;
import com.github.epsilon.graphics.renderers.RoundRectRenderer;
import com.github.epsilon.graphics.renderers.TextRenderer;
import com.github.epsilon.gui.panel.PanelLayout;
Expand Down Expand Up @@ -30,9 +31,9 @@ public float getHeight() {
return 28.0f;
}

public void render(GuiGraphicsExtractor GuiGraphicsExtractor, RoundRectRenderer roundRectRenderer, RectRenderer rectRenderer, TextRenderer textRenderer, PanelLayout.Rect bounds, float hoverProgress, int mouseX, int mouseY, float partialTick) {
public void render(GuiGraphicsExtractor GuiGraphicsExtractor, RoundRectRenderer roundRectRenderer, RoundRectOutlineRenderer roundRectOutlineRenderer, RectRenderer rectRenderer, TextRenderer textRenderer, PanelLayout.Rect bounds, float hoverProgress, int mouseX, int mouseY, float partialTick) {
PanelUiTree tree = PanelUiTree.build(scope -> buildUi(scope, GuiGraphicsExtractor, textRenderer, bounds, hoverProgress, mouseX, mouseY, partialTick));
PanelUiCompiler.render(tree, roundRectRenderer, rectRenderer, textRenderer);
PanelUiCompiler.render(tree, null, roundRectRenderer, roundRectOutlineRenderer, rectRenderer, textRenderer);
}

public void buildUi(PanelUiTree.Scope scope, GuiGraphicsExtractor guiGraphics, TextRenderer textRenderer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void buildUi(PanelUiTree.Scope scope, GuiGraphicsExtractor guiGraphics, T
float toggleProgress = scope.animate(toggleAnimation, setting.getValue());

scope.roundRect(bounds.x(), bounds.y(), bounds.width(), bounds.height(), MD3Theme.CARD_RADIUS, MD3Theme.rowSurface(animatedHover));
scope.text(setting.getDisplayName(), bounds.x() + MD3Theme.ROW_CONTENT_INSET, labelY, labelScale, MD3Theme.TEXT_PRIMARY);
scope.text(setting.getDisplayName(), PanelElements.rowLabelX(bounds), labelY, labelScale, MD3Theme.TEXT_PRIMARY);
scope.toggle(getSwitchBounds(bounds), toggleProgress, animatedHover);
}

Expand Down
Loading
Loading