aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2025-07-29 21:49:33 +0200
committerLinnea Gräf <nea@nea.moe>2025-07-29 21:49:33 +0200
commita30e0810604ee9122758879205434563ccc94738 (patch)
treeccdde75394db0917231dd3fdbcf9763c1a913de6
parent80d97bb0de46b1b0cefd8a5bd1ba22105ee08a50 (diff)
downloadFirmament-a30e0810604ee9122758879205434563ccc94738.tar.gz
Firmament-a30e0810604ee9122758879205434563ccc94738.tar.bz2
Firmament-a30e0810604ee9122758879205434563ccc94738.zip
fix: special element renderers / circle renderers
-rw-r--r--src/main/java/moe/nea/firmament/mixins/render/renderer/MultipleSpecialGuiRenderStates.java68
-rw-r--r--src/main/kotlin/util/render/MultiSpecialGuiRenderState.kt48
-rw-r--r--src/main/kotlin/util/render/RenderCircleProgress.kt170
3 files changed, 234 insertions, 52 deletions
diff --git a/src/main/java/moe/nea/firmament/mixins/render/renderer/MultipleSpecialGuiRenderStates.java b/src/main/java/moe/nea/firmament/mixins/render/renderer/MultipleSpecialGuiRenderStates.java
new file mode 100644
index 0000000..cc79591
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/render/renderer/MultipleSpecialGuiRenderStates.java
@@ -0,0 +1,68 @@
+/*
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ * SPDX-FileCopyrightText: 2025 azureaaron via Skyblocker
+ */
+
+package moe.nea.firmament.mixins.render.renderer;
+
+import com.mojang.blaze3d.buffers.GpuBufferSlice;
+import moe.nea.firmament.util.render.MultiSpecialGuiRenderState;
+import moe.nea.firmament.util.render.MultiSpecialGuiRenderer;
+import net.minecraft.client.gui.render.GuiRenderer;
+import net.minecraft.client.gui.render.SpecialGuiElementRenderer;
+import net.minecraft.client.gui.render.state.GuiRenderState;
+import net.minecraft.client.gui.render.state.special.SpecialGuiElementRenderState;
+import net.minecraft.client.render.VertexConsumerProvider;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The structure of this class was roughly taken from SkyBlocker, retrieved 29.07.2025
+ */
+@Mixin(GuiRenderer.class)
+public class MultipleSpecialGuiRenderStates {
+ @Shadow
+ @Final
+ private VertexConsumerProvider.Immediate vertexConsumers;
+ @Shadow
+ @Final
+ GuiRenderState state;
+ @Unique
+ Map<MultiSpecialGuiRenderState, MultiSpecialGuiRenderer<?>> multiRenderers = new HashMap<>();
+
+ @Inject(method = "prepareSpecialElement", at = @At("HEAD"), cancellable = true)
+ private <T extends SpecialGuiElementRenderState> void onPrepareElement(T elementState, int windowScaleFactor, CallbackInfo ci) {
+ if (elementState instanceof MultiSpecialGuiRenderState multiState) {
+ @SuppressWarnings({"resource", "unchecked"})
+ var renderer = (SpecialGuiElementRenderer<T>) multiRenderers
+ .computeIfAbsent(multiState, elementState$ -> elementState$.createRenderer(this.vertexConsumers));
+ renderer.render(elementState, state, windowScaleFactor);
+ ci.cancel();
+ }
+ }
+
+ @Inject(method = "close", at = @At("TAIL"))
+ private void onClose(CallbackInfo ci) {
+ multiRenderers.values().forEach(SpecialGuiElementRenderer::close);
+ }
+
+ @Inject(method = "render(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/render/GuiRenderer;clearOversizedItems()V"))
+ private void onAfterRender(GpuBufferSlice fogBuffer, CallbackInfo ci) {
+ multiRenderers.values().removeIf(it -> {
+ if (it.consumeRender()) {
+ return false;
+ } else {
+ it.close();
+ return true;
+ }
+ });
+ }
+}
diff --git a/src/main/kotlin/util/render/MultiSpecialGuiRenderState.kt b/src/main/kotlin/util/render/MultiSpecialGuiRenderState.kt
new file mode 100644
index 0000000..ce56df3
--- /dev/null
+++ b/src/main/kotlin/util/render/MultiSpecialGuiRenderState.kt
@@ -0,0 +1,48 @@
+package moe.nea.firmament.util.render
+
+import org.joml.Matrix3x2f
+import net.minecraft.client.gui.ScreenRect
+import net.minecraft.client.gui.render.SpecialGuiElementRenderer
+import net.minecraft.client.gui.render.state.GuiRenderState
+import net.minecraft.client.gui.render.state.special.SpecialGuiElementRenderState
+import net.minecraft.client.render.VertexConsumerProvider
+
+abstract class MultiSpecialGuiRenderState : SpecialGuiElementRenderState {
+ // I wish i had manifolds @Self type here... Maybe i should switch to java after all :(
+ abstract fun createRenderer(vertexConsumers: VertexConsumerProvider.Immediate): MultiSpecialGuiRenderer<out MultiSpecialGuiRenderState>
+ abstract val x1: Int
+ abstract val x2: Int
+ abstract val y1: Int
+ abstract val y2: Int
+ abstract val scale: Float
+ abstract val bounds: ScreenRect?
+ abstract val scissorArea: ScreenRect?
+ override fun x1(): Int = x1
+
+ override fun x2(): Int = x2
+
+ override fun y1(): Int = y1
+
+ override fun y2(): Int = y2
+
+ override fun scale(): Float = scale
+
+ override fun scissorArea(): ScreenRect? = scissorArea
+
+ override fun bounds(): ScreenRect? = bounds
+
+}
+
+abstract class MultiSpecialGuiRenderer<T : MultiSpecialGuiRenderState>(
+ vertexConsumers: VertexConsumerProvider.Immediate
+) : SpecialGuiElementRenderer<T>(vertexConsumers) {
+ var wasUsedThisFrame = false
+ fun consumeRender(): Boolean {
+ return wasUsedThisFrame.also { wasUsedThisFrame = false }
+ }
+
+ override fun renderElement(element: T, state: GuiRenderState) {
+ wasUsedThisFrame = true
+ super.renderElement(element, state)
+ }
+}
diff --git a/src/main/kotlin/util/render/RenderCircleProgress.kt b/src/main/kotlin/util/render/RenderCircleProgress.kt
index efd99fe..bbc4ace 100644
--- a/src/main/kotlin/util/render/RenderCircleProgress.kt
+++ b/src/main/kotlin/util/render/RenderCircleProgress.kt
@@ -4,9 +4,12 @@ import com.mojang.blaze3d.vertex.VertexFormat
import org.joml.Matrix3x2f
import util.render.CustomRenderLayers
import net.minecraft.client.gui.DrawContext
+import net.minecraft.client.gui.ScreenRect
import net.minecraft.client.render.BufferBuilder
import net.minecraft.client.render.RenderLayer
+import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.util.BufferAllocator
+import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.Identifier
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.collections.nonNegligibleSubSectionsAlignedWith
@@ -15,6 +18,105 @@ import moe.nea.firmament.util.mc.CustomRenderPassHelper
object RenderCircleProgress {
+
+ data class State(
+ override val x1: Int,
+ override val x2: Int,
+ override val y1: Int,
+ override val y2: Int,
+ val layer: RenderLayer.MultiPhase,
+ val u1: Float,
+ val u2: Float,
+ val v1: Float,
+ val v2: Float,
+ val angleRadians: ClosedFloatingPointRange<Float>,
+ val color: Int,
+ val innerCutoutRadius: Float,
+ override val scale: Float,
+ override val bounds: ScreenRect?,
+ override val scissorArea: ScreenRect?,
+ ) : MultiSpecialGuiRenderState() {
+ override fun createRenderer(vertexConsumers: VertexConsumerProvider.Immediate): MultiSpecialGuiRenderer<out MultiSpecialGuiRenderState> {
+ return Renderer(vertexConsumers)
+ }
+ }
+
+ class Renderer(vertexConsumers: VertexConsumerProvider.Immediate) :
+ MultiSpecialGuiRenderer<State>(vertexConsumers) {
+ override fun render(
+ state: State,
+ matrices: MatrixStack
+ ) {
+ matrices.push()
+ matrices.translate(0F, -1F, 0F)
+ val sections = state.angleRadians.nonNegligibleSubSectionsAlignedWith((τ / 8f).toFloat())
+ .zipWithNext().toList()
+ val u1 = state.u1
+ val u2 = state.u2
+ val v1 = state.v1
+ val v2 = state.v2
+ val color = state.color
+ val matrix = matrices.peek().positionMatrix
+ BufferAllocator(state.layer.vertexFormat.vertexSize * sections.size * 3).use { allocator ->
+
+ val bufferBuilder = BufferBuilder(allocator, VertexFormat.DrawMode.TRIANGLES, state.layer.vertexFormat)
+
+ for ((sectionStart, sectionEnd) in sections) {
+ val firstPoint = Projections.Two.projectAngleOntoUnitBox(sectionStart.toDouble())
+ val secondPoint = Projections.Two.projectAngleOntoUnitBox(sectionEnd.toDouble())
+ fun ilerp(f: Float): Float =
+ ilerp(-1f, 1f, f)
+
+ bufferBuilder
+ .vertex(matrix, secondPoint.x, secondPoint.y, 0F)
+ .texture(lerp(u1, u2, ilerp(secondPoint.x)), lerp(v1, v2, ilerp(secondPoint.y)))
+ .color(color)
+
+ bufferBuilder
+ .vertex(matrix, firstPoint.x, firstPoint.y, 0F)
+ .texture(lerp(u1, u2, ilerp(firstPoint.x)), lerp(v1, v2, ilerp(firstPoint.y)))
+ .color(color)
+
+ bufferBuilder
+ .vertex(matrix, 0F, 0F, 0F)
+ .texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F)))
+ .color(color)
+
+ }
+
+ bufferBuilder.end().use { buffer ->
+ if (state.innerCutoutRadius <= 0) {
+ state.layer.draw(buffer)
+ return
+ }
+ CustomRenderPassHelper(
+ { "RenderCircleProgress" },
+ VertexFormat.DrawMode.TRIANGLES,
+ state.layer.vertexFormat,
+ MC.instance.framebuffer,
+ false,
+ ).use { renderPass ->
+ renderPass.uploadVertices(buffer)
+ renderPass.setPipeline(state.layer.pipeline)
+ renderPass.setUniform("InnerCutoutRadius", 4) {
+ it.putFloat(state.innerCutoutRadius)
+ }
+ renderPass.draw()
+ }
+ }
+ }
+ matrices.pop()
+ }
+
+ override fun getElementClass(): Class<State> {
+ return State::class.java
+ }
+
+ override fun getName(): String {
+ return "Firmament Circle"
+ }
+ }
+
fun renderCircularSlice(
drawContext: DrawContext,
layer: RenderLayer.MultiPhase,
@@ -25,58 +127,22 @@ object RenderCircleProgress {
angleRadians: ClosedFloatingPointRange<Float>,
color: Int = -1,
innerCutoutRadius: Float = 0F
- ) { // TODO: this is fixed by adding a special gui element renderer
- val sections = angleRadians.nonNegligibleSubSectionsAlignedWith((τ / 8f).toFloat())
- .zipWithNext().toList()
- BufferAllocator(layer.vertexFormat.vertexSize * sections.size * 3).use { allocator ->
-
- val bufferBuilder = BufferBuilder(allocator, VertexFormat.DrawMode.TRIANGLES, layer.vertexFormat)
- val matrix: Matrix3x2f = drawContext.matrices
-
- for ((sectionStart, sectionEnd) in sections) {
- val firstPoint = Projections.Two.projectAngleOntoUnitBox(sectionStart.toDouble())
- val secondPoint = Projections.Two.projectAngleOntoUnitBox(sectionEnd.toDouble())
- fun ilerp(f: Float): Float =
- ilerp(-1f, 1f, f)
-
- bufferBuilder
- .vertex(matrix, secondPoint.x, secondPoint.y, 0F)
- .texture(lerp(u1, u2, ilerp(secondPoint.x)), lerp(v1, v2, ilerp(secondPoint.y)))
- .color(color)
-
- bufferBuilder
- .vertex(matrix, firstPoint.x, firstPoint.y, 0F)
- .texture(lerp(u1, u2, ilerp(firstPoint.x)), lerp(v1, v2, ilerp(firstPoint.y)))
- .color(color)
-
- bufferBuilder
- .vertex(matrix, 0F, 0F, 0F)
- .texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F)))
- .color(color)
-
- }
-
- bufferBuilder.end().use { buffer ->
- if (innerCutoutRadius <= 0) {
- layer.draw(buffer)
- return
- }
- CustomRenderPassHelper(
- { "RenderCircleProgress" },
- VertexFormat.DrawMode.TRIANGLES,
- layer.vertexFormat,
- MC.instance.framebuffer,
- false,
- ).use { renderPass ->
- renderPass.uploadVertices(buffer)
- renderPass.setPipeline(layer.pipeline)
- renderPass.setUniform("InnerCutoutRadius", 4) {
- it.putFloat(innerCutoutRadius)
- }
- renderPass.draw()
- }
- }
- }
+ ) {
+ val screenRect = ScreenRect(-1, -1, 2, 2).transform(drawContext.matrices)
+ drawContext.state.addSpecialElement(
+ State(
+ screenRect.left, screenRect.right,
+ screenRect.top, screenRect.bottom,
+ layer,
+ u1, u2, v1, v2,
+ angleRadians,
+ color,
+ innerCutoutRadius,
+ screenRect.width / 2F,
+ screenRect,
+ null
+ )
+ )
}
fun renderCircle(