From 844c1ee7b8755e16d60e52946ab75f69c4fd70c1 Mon Sep 17 00:00:00 2001 From: reoseah Date: Sun, 5 Apr 2026 02:41:55 +0200 Subject: [PATCH] render text, not baked --- .../glowcase/client/GlowcaseClient.java | 2 +- .../block/entity/TextBlockEntityRenderer.java | 109 +++++++++++++++--- 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java index 2eeee6e1..2c00e6fe 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java +++ b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java @@ -37,7 +37,7 @@ public class GlowcaseClient implements ClientModInitializer { public void onInitializeClient() { Glowcase.proxy = new GlowcaseClientProxy(); - BlockEntityRenderers.register(Glowcase.TEXT_BLOCK_ENTITY.get(), (ctx) -> new TextBlockEntityRenderer()); + BlockEntityRenderers.register(Glowcase.TEXT_BLOCK_ENTITY.get(), (ctx) -> new TextBlockEntityRenderer(ctx)); BlockEntityRenderers.register(Glowcase.HYPERLINK_BLOCK_ENTITY.get(), HyperlinkBlockEntityRenderer::new); BlockEntityRenderers.register(Glowcase.CONFIG_LINK_BLOCK_ENTITY.get(), ConfigLinkBlockEntityRenderer::new); BlockEntityRenderers.register(Glowcase.ITEM_DISPLAY_BLOCK_ENTITY.get(), ItemDisplayBlockEntityRenderer::new); diff --git a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java index ee20dc0c..e3831200 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java +++ b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/TextBlockEntityRenderer.java @@ -7,28 +7,42 @@ import dev.hephaestus.glowcase.client.util.BlockEntityRenderUtil; import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.SubmitNodeCollector; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; import net.minecraft.client.renderer.feature.ModelFeatureRenderer; import net.minecraft.client.renderer.state.level.CameraRenderState; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; +import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.LightCoordsUtil; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.phys.Vec3; -import org.joml.Matrix4f; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import java.util.List; + @NullMarked public class TextBlockEntityRenderer implements BakedBlockEntityRenderer { public static Identifier ITEM_TEXTURE = Glowcase.id("textures/item/text_block.png"); private boolean wasOutOfRange = false; + private final Font font; + + public TextBlockEntityRenderer(BlockEntityRendererProvider.Context ctx) { + this.font = ctx.font(); + } + public static class TextRenderState extends BlockEntityRenderState { public boolean shouldRenderPlaceholder; - public TextBlockEntity.ZOffset zOffset; public int rotation16; + public List lines = List.of(); + public TextBlockEntity.TextAlignment textAlignment; + public TextBlockEntity.ZOffset zOffset; + public boolean shadow; + public float scale = 1; + public int color; + public int backgroundColor; } @Override @@ -36,7 +50,6 @@ public TextRenderState createRenderState() { return new TextRenderState(); } - @Override public TextRenderState createBakedRenderState() { return new TextRenderState(); @@ -46,8 +59,16 @@ public TextRenderState createBakedRenderState() { public void extractRenderState(TextBlockEntity blockEntity, TextRenderState state, float partialTicks, Vec3 cameraPosition, ModelFeatureRenderer.@Nullable CrumblingOverlay breakProgress) { BakedBlockEntityRenderer.super.extractRenderState(blockEntity, state, partialTicks, cameraPosition, breakProgress); state.shouldRenderPlaceholder = blockEntity.lines.stream().allMatch(t -> t.getString().isBlank()) || BlockEntityRenderUtil.shouldRenderPlaceholder(blockEntity.getBlockPos()); - state.zOffset = blockEntity.zOffset; + state.rotation16 = blockEntity.getBlockState().getValue(BlockStateProperties.ROTATION_16); + + state.lines = blockEntity.lines.stream().map(Component::getVisualOrderText).toList(); + state.textAlignment = blockEntity.textAlignment; + state.zOffset = blockEntity.zOffset; + state.shadow = blockEntity.shadow; + state.scale = blockEntity.scale; + state.color = blockEntity.color; + state.backgroundColor = blockEntity.backgroundColor; } @Override @@ -57,6 +78,15 @@ public void extractBakingRenderState(TextBlockEntity blockEntity, TextRenderStat state.shouldRenderPlaceholder = blockEntity.lines.stream().allMatch(t -> t.getString().isBlank()) || BlockEntityRenderUtil.shouldRenderPlaceholder(blockEntity.getBlockPos()); state.zOffset = blockEntity.zOffset; state.rotation16 = blockEntity.getBlockState().getValue(BlockStateProperties.ROTATION_16); + + state.lines = blockEntity.lines.stream().map(Component::getVisualOrderText).toList(); + state.scale = blockEntity.scale; + } + + @Override + public boolean shouldBake(TextBlockEntity entity) { +// return !entity.lines.isEmpty(); + return false; } @Override @@ -64,23 +94,70 @@ public void submitForRendering(TextRenderState state, PoseStack poseStack, Submi if (state.shouldRenderPlaceholder) { BlockEntityRenderUtil.renderPlaceholderWithBlockRotation(state, state.rotation16, ITEM_TEXTURE, 1.0F, poseStack, submitNodeCollector, state.zOffset == TextBlockEntity.ZOffset.CENTER ? 0.01F : state.zOffset == TextBlockEntity.ZOffset.FRONT ? 0.4F : -0.4F); } - } - @Override - public void submitForBaking(TextRenderState state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector) { - // TODO: Port to 26.1 + // TODO: move this to a baked rendering method + // currently it errors "Rendersystem called from wrong thread" on `this.font.width(text)` + int maxWidth = 0; + for (var line : state.lines) { + maxWidth = Math.max(maxWidth, this.font.width(line)); + } + poseStack.pushPose(); - poseStack.mulPose(Axis.YP.rotationDegrees(-(state.rotation16 * 360) / 16.0F)); - float a = 1 / 9f; - poseStack.translate(-.5, 1.5, -.5); - poseStack.scale(a, -a, a); - submitNodeCollector.submitText(poseStack, 0, 0, Component.literal("waff :3").getVisualOrderText(), true, Font.DisplayMode.NORMAL, LightCoordsUtil.FULL_BRIGHT, 0xFFFFFFFF, 0, 0); + poseStack.translate(0.5D, 0.5D, 0.5D); + // 2D rendering of the font has Y axis going down, not up + poseStack.scale(1, -1, 1); + + switch (state.zOffset) { + case FRONT -> poseStack.translate(0D, 0D, 0.4D); + case BACK -> poseStack.translate(0D, 0D, -0.4D); + } + + float rotation = -(state.rotation16 * 360) / 16.0F; + poseStack.mulPose(Axis.YP.rotationDegrees(rotation)); + + poseStack.scale(0.1F * state.scale, 0.1F * state.scale, 0.1F * state.scale); + poseStack.translate(0, -(state.lines.size() * this.font.lineHeight) / 2D, 0D); + + for (int i = 0; i < state.lines.size(); ++i) { + var line = state.lines.get(i); + + int width = this.font.width(line); + if (width == 0) { + continue; + } + + float x = switch (state.textAlignment) { + case LEFT -> -maxWidth / 2F; + case CENTER -> (maxWidth - width) / 2F - maxWidth / 2F; + case CENTER_LEFT -> -(50F / state.scale) - (width / 2F); + case CENTER_RIGHT -> (50F / state.scale) - (width / 2F); + case RIGHT -> maxWidth - width - maxWidth / 2F; + }; + + submitNodeCollector.submitText(poseStack, + x, + i * this.font.lineHeight, + line, state.shadow, + Font.DisplayMode.NORMAL, + LightCoordsUtil.FULL_BRIGHT, + state.color, + state.backgroundColor, + 0); + } + poseStack.popPose(); } @Override - public boolean shouldBake(TextBlockEntity entity) { - return !entity.lines.isEmpty(); + public void submitForBaking(TextRenderState state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector) { + // TODO: Port to 26.1 +// poseStack.pushPose(); +// poseStack.mulPose(Axis.YP.rotationDegrees(-(state.rotation16 * 360) / 16.0F)); +// float a = 1 / 9f; +// poseStack.translate(-.5, 1.5, -.5); +// poseStack.scale(a, -a, a); +// submitNodeCollector.submitText(poseStack, 0, 0, Component.literal("waff :3").getVisualOrderText(), true, Font.DisplayMode.NORMAL, LightCoordsUtil.FULL_BRIGHT, 0xFFFFFFFF, 0, 0); +// poseStack.popPose(); } // FIXME 26.1