diff --git a/build.gradle.kts b/build.gradle.kts index 110f2dc535..6d8af20f7b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -81,6 +81,7 @@ dependencies { // Compat fixes modCompileOnly(fabricApi.module("fabric-renderer-indigo", fapiVersion)) + modCompileOnly(fabricApi.module("fabric-rendering-fluids-v1", fapiVersion)) modCompileOnly(libs.sodium) { isTransitive = false } modCompileOnly(libs.lithium) { isTransitive = false } modCompileOnly(libs.iris) { isTransitive = false } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java index 6a1b773118..4661444b6d 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java @@ -249,17 +249,17 @@ private void keybindW(WTable table, KeybindSetting setting) { private void blockW(WTable table, BlockSetting setting) { WHorizontalList list = table.add(theme.horizontalList()).expandX().widget(); - WItem item = list.add(theme.item(setting.get().asItem().getDefaultStack())).widget(); + WBlock block = list.add(theme.block(setting.get().getDefaultState())).widget(); WButton select = list.add(theme.button("Select")).widget(); select.action = () -> { BlockSettingScreen screen = new BlockSettingScreen(theme, setting); - screen.onClosed(() -> item.set(setting.get().asItem().getDefaultStack())); + screen.onClosed(() -> block.setState(setting.get().getDefaultState())); mc.setScreen(screen); }; - reset(table, setting, () -> item.set(setting.get().asItem().getDefaultStack())); + reset(table, setting, () -> block.setState(setting.get().getDefaultState())); } private void blockPosW(WTable table, BlockPosSetting setting) { diff --git a/src/main/java/meteordevelopment/meteorclient/gui/GuiTheme.java b/src/main/java/meteordevelopment/meteorclient/gui/GuiTheme.java index 2b52425f84..4d1b48a291 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/GuiTheme.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/GuiTheme.java @@ -28,6 +28,7 @@ import meteordevelopment.meteorclient.utils.misc.Keybind; import meteordevelopment.meteorclient.utils.misc.Names; import meteordevelopment.meteorclient.utils.render.color.Color; +import net.minecraft.block.BlockState; import net.minecraft.client.gui.screen.Screen; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; @@ -176,6 +177,16 @@ public WItemWithLabel itemWithLabel(ItemStack stack) { return itemWithLabel(stack, Names.get(stack.getItem())); } + public WBlock block(BlockState state) { + return w(new WBlock(state)); + } + public WBlockWithLabel blockWithLabel(BlockState state, String name) { + return w(new WBlockWithLabel(state, name)); + } + public WBlockWithLabel blockWithLabel(BlockState state) { + return blockWithLabel(state, Names.get(state.getBlock())); + } + public WTexture texture(double width, double height, double rotation, Texture texture) { return w(new WTexture(width, height, rotation, texture)); } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/renderer/GuiRenderer.java b/src/main/java/meteordevelopment/meteorclient/gui/renderer/GuiRenderer.java index 639b221ae9..1738a27cfa 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/renderer/GuiRenderer.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/renderer/GuiRenderer.java @@ -25,6 +25,7 @@ import net.minecraft.util.math.MathHelper; import java.util.List; +import java.util.Optional; import static meteordevelopment.meteorclient.MeteorClient.mc; import static meteordevelopment.meteorclient.utils.Utils.getWindowHeight; @@ -173,6 +174,10 @@ public void scissorEnd() { scissorPool.free(scissor); } + public Optional getScissor() { + return scissorStack.isEmpty() ? Optional.empty() : Optional.of(scissorStack.top()); + } + public boolean renderTooltip(DrawContext drawContext, double mouseX, double mouseY, double delta) { tooltipAnimProgress += (tooltip != null ? 1 : -1) * delta * 14; tooltipAnimProgress = MathHelper.clamp(tooltipAnimProgress, 0, 1); diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java index edae8bff8f..2d38771c4c 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java @@ -41,7 +41,7 @@ protected boolean includeValue(Block value) { @Override protected WWidget getValueWidget(Block block) { - return theme.itemWithLabel(block.asItem().getDefaultStack(), Names.get(block)); + return theme.blockWithLabel(block.getDefaultState()); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java index d0309ce85b..17e7dddc63 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java @@ -13,7 +13,6 @@ import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.registry.Registries; -import net.minecraft.util.Identifier; import java.util.function.Predicate; @@ -24,10 +23,6 @@ public BlockListSettingScreen(GuiTheme theme, BlockListSetting setting) { @Override protected boolean includeValue(Block value) { - if (Registries.BLOCK.getId(value).getPath().endsWith("_wall_banner")) { - return false; - } - Predicate filter = ((BlockListSetting) setting).filter; if (filter == null) return value != Blocks.AIR; @@ -35,8 +30,8 @@ protected boolean includeValue(Block value) { } @Override - protected WWidget getValueWidget(Block value) { - return theme.itemWithLabel(value.asItem().getDefaultStack(), Names.get(value)); + protected WWidget getValueWidget(Block block) { + return theme.blockWithLabel(block.getDefaultState()); } @Override @@ -46,12 +41,4 @@ protected String[] getValueNames(Block value) { Registries.BLOCK.getId(value).toString() }; } - - @Override - protected Block getAdditionalValue(Block value) { - String path = Registries.BLOCK.getId(value).getPath(); - if (!path.endsWith("_banner")) return null; - - return Registries.BLOCK.get(Identifier.ofVanilla(path.substring(0, path.length() - 6) + "wall_banner")); - } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockSettingScreen.java index ae4b4faa5b..29d924f676 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockSettingScreen.java @@ -7,12 +7,11 @@ import meteordevelopment.meteorclient.gui.GuiTheme; import meteordevelopment.meteorclient.gui.WindowScreen; -import meteordevelopment.meteorclient.gui.widgets.WItemWithLabel; +import meteordevelopment.meteorclient.gui.widgets.WBlockWithLabel; import meteordevelopment.meteorclient.gui.widgets.containers.WTable; import meteordevelopment.meteorclient.gui.widgets.input.WTextBox; import meteordevelopment.meteorclient.gui.widgets.pressable.WButton; import meteordevelopment.meteorclient.settings.BlockSetting; -import meteordevelopment.meteorclient.utils.misc.Names; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.registry.Registries; @@ -53,7 +52,7 @@ private void initTable() { if (setting.filter != null && !setting.filter.test(block)) continue; if (skipValue(block)) continue; - WItemWithLabel item = theme.itemWithLabel(block.asItem().getDefaultStack(), Names.get(block)); + WBlockWithLabel item = theme.blockWithLabel(block.getDefaultState()); if (!filterText.isEmpty() && !Strings.CI.contains(item.getLabelText(), filterText)) continue; table.add(item); diff --git a/src/main/java/meteordevelopment/meteorclient/gui/widgets/WBlock.java b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WBlock.java new file mode 100644 index 0000000000..eaff3fe816 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WBlock.java @@ -0,0 +1,172 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.gui.widgets; + +import com.mojang.blaze3d.systems.ProjectionType; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.TextureFormat; +import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import meteordevelopment.meteorclient.gui.renderer.GuiRenderer; +import meteordevelopment.meteorclient.gui.renderer.Scissor; +import meteordevelopment.meteorclient.renderer.Texture; +import meteordevelopment.meteorclient.utils.render.SimpleBlockRenderer; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.ProjectionMatrix2; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.BufferAllocator; +import net.minecraft.client.util.Window; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; + +import java.util.Optional; + +public class WBlock extends WWidget { + private static final int TEXTURE_SIZE = 64; + + private static VertexConsumerProvider.Immediate IMMEDIATE; + private static Texture DEPTH; + private static ProjectionMatrix2 PROJECTION_TEXTURE; + private static ProjectionMatrix2 PROJECTION_SCREEN; + + private static final Reference2ObjectMap TEXTURES = new Reference2ObjectOpenHashMap<>(); + + protected BlockState state; + protected boolean initialized; + protected boolean chached; + + public WBlock(BlockState state) { + this.state = state; + } + + @Override + protected void onCalculateSize() { + double s = theme.scale(32); + + width = s; + height = s; + } + + @Override + protected void onRender(GuiRenderer renderer, double mouseX, double mouseY, double delta) { + if (state.isAir()) return; + + // Render as an item model + ItemStack stack = state.getBlock().asItem().getDefaultStack(); + + if (!stack.isEmpty()) { + renderer.post(() -> { + double s = theme.scale(2); + renderer.item(stack, (int) x, (int) y, (float) s, true); + }); + + return; + } + + // Render block + if (!initialized) { + chached = !SimpleBlockRenderer.hasAnimatedTextures(state); + } + + if (IMMEDIATE == null) { + IMMEDIATE = VertexConsumerProvider.immediate(new BufferAllocator(1536)); + DEPTH = new Texture(TEXTURE_SIZE, TEXTURE_SIZE, TextureFormat.DEPTH32, FilterMode.NEAREST, FilterMode.NEAREST); + PROJECTION_TEXTURE = new ProjectionMatrix2("Block widget texture projection", -100, 100, true); + PROJECTION_SCREEN = new ProjectionMatrix2("Block widget screen projection", -100, 100, false); + } + + if (chached) { + Texture texture = TEXTURES.computeIfAbsent(state, WBlock::renderToTexture); + renderer.texture(x, y, width, height, 0, texture); + } else { + Optional scissorOpt = renderer.getScissor(); + renderer.post(() -> renderDirectly(scissorOpt, state, (float) x, (float) y, (float) width, (float) height, (float) theme.scale(0.5d))); + } + } + + private static Texture renderToTexture(BlockState state) { + Texture color = new Texture(TEXTURE_SIZE, TEXTURE_SIZE, TextureFormat.RGBA8, FilterMode.NEAREST, FilterMode.NEAREST); + + var commands = RenderSystem.getDevice().createCommandEncoder(); + commands.clearDepthTexture(DEPTH.getGlTexture(), 1); + commands.clearColorTexture(color.getGlTexture(), 0); + + RenderSystem.outputColorTextureOverride = color.getGlTextureView(); + RenderSystem.outputDepthTextureOverride = DEPTH.getGlTextureView(); + RenderSystem.backupProjectionMatrix(); + RenderSystem.setProjectionMatrix(PROJECTION_TEXTURE.set(TEXTURE_SIZE, TEXTURE_SIZE), ProjectionType.PERSPECTIVE); + + var view = RenderSystem.getModelViewStack(); + view.pushMatrix().identity(); + view.scale(TEXTURE_SIZE); + + view.rotateXYZ(30 * (float) (Math.PI / 180.0), 45 * (float) (Math.PI / 180.0), 0); + view.scale(0.625f, 0.625f, -0.625f); + view.translate(0.55f, 0, -0.5f); + + SimpleBlockRenderer.renderFull(null, BlockPos.ORIGIN, state, null, new MatrixStack(), MinecraftClient.getInstance().getRenderTickCounter().getDynamicDeltaTicks(), IMMEDIATE); + IMMEDIATE.draw(); + + view.popMatrix(); + + RenderSystem.restoreProjectionMatrix(); + RenderSystem.outputDepthTextureOverride = null; + RenderSystem.outputColorTextureOverride = null; + + return color; + } + + private static void renderDirectly(Optional scissorOpt, BlockState state, float x, float y, float width, float height, float scale) { + Window window = MinecraftClient.getInstance().getWindow(); + + int framebufferHeight = window.getFramebufferHeight(); + float canonicalY = framebufferHeight - y - height; + + if (scissorOpt.isPresent()) { + Scissor scissor = scissorOpt.get(); + int canonicalScissorY = framebufferHeight - scissor.y - scissor.height; + + int x1 = Math.max((int) x, scissor.x); + int y1 = Math.max((int) canonicalY, canonicalScissorY); + int x2 = Math.min((int) (x + width), scissor.x + scissor.width); + int y2 = Math.min((int) (canonicalY + height), canonicalScissorY + scissor.height); + int w = x2 - x1; + int h = y2 - y1; + + RenderSystem.enableScissorForRenderTypeDraws(x1, y1, w, h); + } else { + RenderSystem.enableScissorForRenderTypeDraws((int) x, (int) (canonicalY), (int) width, (int) height); + } + + RenderSystem.backupProjectionMatrix(); + RenderSystem.setProjectionMatrix(PROJECTION_SCREEN.set(window.getFramebufferWidth(), window.getFramebufferHeight()), ProjectionType.PERSPECTIVE); + + var view = RenderSystem.getModelViewStack(); + view.pushMatrix().identity(); + view.translate(x, canonicalY, 0); + view.scale(TEXTURE_SIZE * scale); + + view.rotateXYZ(30 * (float) (Math.PI / 180.0), 45 * (float) (Math.PI / 180.0), 0); + view.scale(0.625f, 0.625f, -0.625f); + view.translate(0.55f, 0, -0.5f); + + SimpleBlockRenderer.renderFull(null, BlockPos.ORIGIN, state, null, new MatrixStack(), MinecraftClient.getInstance().getRenderTickCounter().getDynamicDeltaTicks(), IMMEDIATE); + IMMEDIATE.draw(); + + view.popMatrix(); + + RenderSystem.restoreProjectionMatrix(); + RenderSystem.disableScissorForRenderTypeDraws(); + } + + public void setState(BlockState state) { + this.state = state; + this.initialized = false; + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/gui/widgets/WBlockWithLabel.java b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WBlockWithLabel.java new file mode 100644 index 0000000000..ab302aae7a --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WBlockWithLabel.java @@ -0,0 +1,41 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.gui.widgets; + +import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList; +import meteordevelopment.meteorclient.utils.misc.Names; +import net.minecraft.block.BlockState; + +public class WBlockWithLabel extends WHorizontalList { + private BlockState state; + private String name; + + private WBlock block; + private WLabel label; + + public WBlockWithLabel(BlockState state, String name) { + this.state = state; + this.name = name; + } + + @Override + public void init() { + block = add(theme.block(state)).widget(); + label = add(theme.label(name)).widget(); + } + + public void set(BlockState state) { + this.state = state; + block.state = state; + + name = Names.get(state.getBlock()); + label.set(name); + } + + public String getLabelText() { + return label == null ? name : label.get(); + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/StorageESP.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/StorageESP.java index 5e5c46d886..ca2844754f 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/StorageESP.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/StorageESP.java @@ -33,6 +33,7 @@ import net.minecraft.block.ChestBlock; import net.minecraft.block.entity.*; import net.minecraft.block.enums.ChestType; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -341,7 +342,7 @@ private void renderBox(Render3DEvent event, BlockEntity blockEntity) { private void renderShader(Render3DEvent event, BlockEntity blockEntity) { vertexConsumerProvider.setColor(lineColor); - SimpleBlockRenderer.renderWithBlockEntity(blockEntity, event.tickDelta, vertexConsumerProvider); + SimpleBlockRenderer.renderFlat(mc.world, blockEntity.getPos(), blockEntity.getCachedState(), blockEntity, new MatrixStack(), event.tickDelta, vertexConsumerProvider); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/Names.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/Names.java index 0166dd0e6d..184932e2b2 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/Names.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/Names.java @@ -5,12 +5,14 @@ package meteordevelopment.meteorclient.utils.misc; +import com.google.common.collect.ImmutableMap; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.events.game.ResourcePacksReloadedEvent; import meteordevelopment.meteorclient.utils.PreInit; import meteordevelopment.orbit.EventHandler; import net.minecraft.block.Block; +import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.resource.language.I18n; @@ -48,6 +50,64 @@ public class Names { private static final Map, String> particleTypesNames = new Reference2ObjectOpenHashMap<>(64); private static final Map soundNames = new HashMap<>(64); + private static final Map BLOCK_NAME_OVERRIDES = ImmutableMap.builder() + .put(Blocks.WALL_TORCH, "Wall Torch") + .put(Blocks.REDSTONE_WALL_TORCH, "Redstone Wall Torch") + .put(Blocks.SOUL_WALL_TORCH, "Soul Wall Torch") + .put(Blocks.COPPER_WALL_TORCH, "Copper Wall Torch") + + .put(Blocks.SKELETON_WALL_SKULL, "Skeleton Wall Skull") + .put(Blocks.WITHER_SKELETON_WALL_SKULL, "Wither Skeleton Wall Skull") + .put(Blocks.ZOMBIE_WALL_HEAD, "Zombie Wall Head") + .put(Blocks.PLAYER_WALL_HEAD, "Player Wall Head") + .put(Blocks.DRAGON_WALL_HEAD, "Dragon Wall Head") + .put(Blocks.PIGLIN_WALL_HEAD, "Piglin Wall Head") + .put(Blocks.CREEPER_WALL_HEAD, "Creeper Wall Head") + + .put(Blocks.OAK_WALL_SIGN, "Oak Wall Sign") + .put(Blocks.BIRCH_WALL_SIGN, "Birch Wall Sign") + .put(Blocks.SPRUCE_WALL_SIGN, "Spruce Wall Sign") + .put(Blocks.ACACIA_WALL_SIGN, "Acacia Wall Sign") + .put(Blocks.CHERRY_WALL_SIGN, "Cherry Wall Sign") + .put(Blocks.JUNGLE_WALL_SIGN, "Jungle Wall Sign") + .put(Blocks.BAMBOO_WALL_SIGN, "Bamboo Wall Sign") + .put(Blocks.WARPED_WALL_SIGN, "Warped Wall Sign") + .put(Blocks.CRIMSON_WALL_SIGN, "Crimson Wall Sign") + .put(Blocks.DARK_OAK_WALL_SIGN, "Dark Oak Wall Sign") + .put(Blocks.PALE_OAK_WALL_SIGN, "Pale Oak Wall Sign") + .put(Blocks.MANGROVE_WALL_SIGN, "Mangrove Wall Sign") + + .put(Blocks.OAK_WALL_HANGING_SIGN, "Oak Wall Hanging Sign") + .put(Blocks.BIRCH_WALL_HANGING_SIGN, "Birch Wall Hanging Sign") + .put(Blocks.SPRUCE_WALL_HANGING_SIGN, "Spruce Wall Hanging Sign") + .put(Blocks.ACACIA_WALL_HANGING_SIGN, "Acacia Wall Hanging Sign") + .put(Blocks.CHERRY_WALL_HANGING_SIGN, "Cherry Wall Hanging Sign") + .put(Blocks.JUNGLE_WALL_HANGING_SIGN, "Jungle Wall Hanging Sign") + .put(Blocks.BAMBOO_WALL_HANGING_SIGN, "Bamboo Wall Hanging Sign") + .put(Blocks.WARPED_WALL_HANGING_SIGN, "Warped Wall Hanging Sign") + .put(Blocks.CRIMSON_WALL_HANGING_SIGN, "Crimson Wall Hanging Sign") + .put(Blocks.DARK_OAK_WALL_HANGING_SIGN, "Dark Oak Wall Hanging Sign") + .put(Blocks.PALE_OAK_WALL_HANGING_SIGN, "Pale Oak Wall Hanging Sign") + .put(Blocks.MANGROVE_WALL_HANGING_SIGN, "Mangrove Wall Hanging Sign") + + .put(Blocks.YELLOW_WALL_BANNER, "Yellow Wall Banner") + .put(Blocks.RED_WALL_BANNER, "Red Wall Banner") + .put(Blocks.LIME_WALL_BANNER, "Lime Wall Banner") + .put(Blocks.PINK_WALL_BANNER, "Pink Wall Banner") + .put(Blocks.GRAY_WALL_BANNER, "Gray Wall Banner") + .put(Blocks.CYAN_WALL_BANNER, "Cyan Wall Banner") + .put(Blocks.BLUE_WALL_BANNER, "Blue Wall Banner") + .put(Blocks.WHITE_WALL_BANNER, "White Wall Banner") + .put(Blocks.LIGHT_BLUE_WALL_BANNER, "Light Blue Wall Banner") + .put(Blocks.BROWN_WALL_BANNER, "Brown Wall Banner") + .put(Blocks.GREEN_WALL_BANNER, "Green Wall Banner") + .put(Blocks.BLACK_WALL_BANNER, "Black Wall Banner") + .put(Blocks.ORANGE_WALL_BANNER, "Orange Wall Banner") + .put(Blocks.PURPLE_WALL_BANNER, "Purple Wall Banner") + .put(Blocks.MAGENTA_WALL_BANNER, "Magenta Wall Banner") + .put(Blocks.LIGHT_GRAY_WALL_BANNER, "Light Gray Wall Banner") + .buildOrThrow(); + private Names() { } @@ -76,7 +136,13 @@ public static String get(Item item) { } public static String get(Block block) { - return blockNames.computeIfAbsent(block, block1 -> StringHelper.stripTextFormat(I18n.translate(block1.getTranslationKey()))); + return blockNames.computeIfAbsent(block, block1 -> { + if (BLOCK_NAME_OVERRIDES.containsKey(block) && mc.options.language.startsWith("en_")) { + return BLOCK_NAME_OVERRIDES.get(block); + } else { + return StringHelper.stripTextFormat(I18n.translate(block.getTranslationKey())); + } + }); } /** diff --git a/src/main/java/meteordevelopment/meteorclient/utils/render/IVertexConsumerProvider.java b/src/main/java/meteordevelopment/meteorclient/utils/render/IVertexConsumerProvider.java deleted file mode 100644 index 9c8d10f159..0000000000 --- a/src/main/java/meteordevelopment/meteorclient/utils/render/IVertexConsumerProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). - * Copyright (c) Meteor Development. - */ - -package meteordevelopment.meteorclient.utils.render; - -import net.minecraft.client.render.VertexConsumerProvider; - -public interface IVertexConsumerProvider extends VertexConsumerProvider { - void setOffset(int offsetX, int offsetY, int offsetZ); -} diff --git a/src/main/java/meteordevelopment/meteorclient/utils/render/MeshBuilderVertexConsumerProvider.java b/src/main/java/meteordevelopment/meteorclient/utils/render/MeshBuilderVertexConsumerProvider.java index 609a0d45b8..ff30c95a1b 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/render/MeshBuilderVertexConsumerProvider.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/render/MeshBuilderVertexConsumerProvider.java @@ -9,8 +9,9 @@ import meteordevelopment.meteorclient.utils.render.color.Color; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; -public class MeshBuilderVertexConsumerProvider implements IVertexConsumerProvider { +public class MeshBuilderVertexConsumerProvider implements VertexConsumerProvider { private final MeshBuilderVertexConsumer vertexConsumer; public MeshBuilderVertexConsumerProvider(MeshBuilder mesh) { @@ -26,16 +27,9 @@ public void setColor(Color color) { vertexConsumer.fixedColor(color.r, color.g, color.b, color.a); } - @Override - public void setOffset(int offsetX, int offsetY, int offsetZ) { - vertexConsumer.setOffset(offsetX, offsetY, offsetZ); - } - public static class MeshBuilderVertexConsumer implements VertexConsumer { private final MeshBuilder mesh; - private int offsetX, offsetY, offsetZ; - private final double[] xs = new double[4]; private final double[] ys = new double[4]; private final double[] zs = new double[4]; @@ -47,17 +41,11 @@ public MeshBuilderVertexConsumer(MeshBuilder mesh) { this.mesh = mesh; } - public void setOffset(int offsetX, int offsetY, int offsetZ) { - this.offsetX = offsetX; - this.offsetY = offsetY; - this.offsetZ = offsetZ; - } - @Override public VertexConsumer vertex(float x, float y, float z) { - xs[i] = (double) offsetX + x; - ys[i] = (double) offsetY + y; - zs[i] = (double) offsetZ + z; + xs[i] = x; + ys[i] = y; + zs[i] = z; if (++i >= 4) { mesh.ensureQuadCapacity(); diff --git a/src/main/java/meteordevelopment/meteorclient/utils/render/SimpleBlockRenderer.java b/src/main/java/meteordevelopment/meteorclient/utils/render/SimpleBlockRenderer.java index cf3f0486e3..a0d8136357 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/render/SimpleBlockRenderer.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/render/SimpleBlockRenderer.java @@ -5,12 +5,17 @@ package meteordevelopment.meteorclient.utils.render; +import meteordevelopment.meteorclient.MeteorClient; +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; +import net.minecraft.block.BlockWithEntity; +import net.minecraft.block.Blocks; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.client.render.RenderLayers; -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.color.world.BiomeColors; +import net.minecraft.client.render.*; import net.minecraft.client.render.block.entity.BlockEntityRenderer; import net.minecraft.client.render.block.entity.state.BlockEntityRenderState; import net.minecraft.client.render.command.OrderedRenderCommandQueueImpl; @@ -19,11 +24,21 @@ import net.minecraft.client.render.model.BlockModelPart; import net.minecraft.client.render.model.BlockStateModel; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.fluid.FluidState; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.LightType; +import net.minecraft.world.biome.ColorResolver; +import net.minecraft.world.biome.DryFoliageColors; +import net.minecraft.world.biome.FoliageColors; +import net.minecraft.world.biome.GrassColors; +import net.minecraft.world.chunk.light.LightingProvider; +import org.joml.Matrix4f; +import org.joml.Vector3f; import org.joml.Vector3fc; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -31,10 +46,11 @@ import static meteordevelopment.meteorclient.MeteorClient.mc; public abstract class SimpleBlockRenderer { - private static final MatrixStack MATRICES = new MatrixStack(); + private static final boolean FABRIC_FLUID_RENDERER = FabricLoader.getInstance().isModLoaded("fabric-rendering-fluids-v1"); private static final List PARTS = new ArrayList<>(); private static final Direction[] DIRECTIONS = Direction.values(); private static final Random RANDOM = Random.create(); + private static final Vector3f POS = new Vector3f(); private static final OrderedRenderCommandQueueImpl renderCommandQueue = new OrderedRenderCommandQueueImpl(); @@ -52,60 +68,268 @@ public abstract class SimpleBlockRenderer { private SimpleBlockRenderer() {} - public static void renderWithBlockEntity(BlockEntity blockEntity, float tickDelta, IVertexConsumerProvider vertexConsumerProvider) { - vertexConsumerProvider.setOffset(blockEntity.getPos().getX(), blockEntity.getPos().getY(), blockEntity.getPos().getZ()); - SimpleBlockRenderer.render(blockEntity.getPos(), blockEntity.getCachedState(), vertexConsumerProvider); + public static void renderFlat(@Nullable BlockRenderView renderView, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, MatrixStack matrices, float tickDelta, VertexConsumerProvider vertexConsumerProvider) { + matrices.push(); + matrices.translate(pos.getX(), pos.getY(), pos.getZ()); - BlockEntityRenderer renderer = mc.getBlockEntityRenderDispatcher().get(blockEntity); + if (renderView == null) { + renderView = new StaticBlockRenderView(pos, state); + } + + // Render block model + if (state.getRenderType() == BlockRenderType.MODEL) { + VertexConsumer consumer = vertexConsumerProvider.getBuffer(RenderLayers.solid()); + + BlockStateModel model = mc.getBlockRenderManager().getModel(state); + RANDOM.setSeed(state.getRenderingSeed(pos)); + model.addParts(RANDOM, PARTS); + + matrices.translate(state.getModelOffset(pos)); + Matrix4f matrix4f = matrices.peek().getPositionMatrix(); + + for (BlockModelPart part : PARTS) { + for (Direction direction : DIRECTIONS) { + List quads = part.getQuads(direction); + if (!quads.isEmpty()) renderQuads(quads, matrix4f, consumer); + } + + List quads = part.getQuads(null); + if (!quads.isEmpty()) renderQuads(quads, matrix4f, consumer); + } + + PARTS.clear(); + } + + // Render fluid + if (!state.getFluidState().isEmpty()) { + VertexConsumer consumer = vertexConsumerProvider.getBuffer(RenderLayers.solid()); + renderFluid(renderView, pos, state, consumer); + } + + // Render block entity + if (blockEntity != null || state.getBlock() instanceof BlockWithEntity) { + if (blockEntity == null && state.getBlock() instanceof BlockWithEntity blockWithEntity) { + blockEntity = blockWithEntity.createBlockEntity(pos, state); + } - if (renderer != null && blockEntity.hasWorld() && blockEntity.getType().supports(blockEntity.getCachedState())) { - SimpleBlockRenderer.provider = vertexConsumerProvider; + if (blockEntity != null) { + BlockEntityRenderer renderer = mc.getBlockEntityRenderDispatcher().get(blockEntity); + if (renderer != null && blockEntity.getType().supports(blockEntity.getCachedState())) { + try { + SimpleBlockRenderer.provider = vertexConsumerProvider; - BlockEntityRenderState state = renderer.createRenderState(); - renderer.updateRenderState(blockEntity, state, tickDelta, mc.gameRenderer.getCamera().getCameraPos(), null); - renderer.render(state, MATRICES, renderCommandQueue, mc.gameRenderer.getEntityRenderStates().cameraRenderState); + BlockEntityRenderState renderState = renderer.createRenderState(); + renderer.updateRenderState(blockEntity, renderState, tickDelta, mc.gameRenderer.getCamera().getCameraPos(), null); + renderer.render(renderState, matrices, renderCommandQueue, mc.gameRenderer.getEntityRenderStates().cameraRenderState); - renderDispatcher.render(); - renderCommandQueue.onNextFrame(); + renderDispatcher.render(); + renderCommandQueue.onNextFrame(); - SimpleBlockRenderer.provider = null; + SimpleBlockRenderer.provider = null; + } catch (Throwable t) { + MeteorClient.LOG.error("Oops! no render", t); + } + } + } } - vertexConsumerProvider.setOffset(0, 0, 0); + matrices.pop(); } - public static void render(BlockPos pos, BlockState state, VertexConsumerProvider consumerProvider) { - if (state.getRenderType() != BlockRenderType.MODEL) return; + public static void renderFull(@Nullable BlockRenderView renderView, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, MatrixStack matrices, float tickDelta, VertexConsumerProvider.Immediate vertexConsumerProvider) { + matrices.push(); + matrices.translate(pos.getX(), pos.getY(), pos.getZ()); - VertexConsumer consumer = consumerProvider.getBuffer(RenderLayers.solid()); + if (renderView == null) { + renderView = new StaticBlockRenderView(pos, state); + } - BlockStateModel model = mc.getBlockRenderManager().getModel(state); - model.addParts(RANDOM, PARTS); + // Render block model + if (state.getRenderType() == BlockRenderType.MODEL) { + VertexConsumer consumer = vertexConsumerProvider.getBuffer(RenderLayers.cutout()); + + BlockStateModel model = mc.getBlockRenderManager().getModel(state); + RANDOM.setSeed(42L); + model.addParts(RANDOM, PARTS); + + MinecraftClient.getInstance().getBlockRenderManager().getModelRenderer().render( + renderView, + PARTS, + state, + pos, + matrices, + consumer, + false, + OverlayTexture.DEFAULT_UV + ); + + PARTS.clear(); + } - Vec3d offset = state.getModelOffset(pos); - float offsetX = (float) offset.x; - float offsetY = (float) offset.y; - float offsetZ = (float) offset.z; + // Render fluid + if (!state.getFluidState().isEmpty()) { + VertexConsumer consumer = vertexConsumerProvider.getBuffer(RenderLayers.cutout()); + renderFluid(renderView, pos, state, consumer); + } - for (BlockModelPart part : PARTS) { - for (Direction direction : DIRECTIONS) { - List quads = part.getQuads(direction); - if (!quads.isEmpty()) renderQuads(quads, offsetX, offsetY, offsetZ, consumer); + // Render block entity + if (blockEntity != null || state.getBlock() instanceof BlockWithEntity) { + if (blockEntity == null && state.getBlock() instanceof BlockWithEntity blockWithEntity) { + blockEntity = blockWithEntity.createBlockEntity(pos, state); } - List quads = part.getQuads(null); - if (!quads.isEmpty()) renderQuads(quads, offsetX, offsetY, offsetZ, consumer); + if (blockEntity != null) { + BlockEntityRenderer renderer = mc.getBlockEntityRenderDispatcher().get(blockEntity); + if (renderer != null && blockEntity.getType().supports(blockEntity.getCachedState())) { + try (RenderDispatcher renderDispatcher = new RenderDispatcher( + renderCommandQueue, + mc.getBlockRenderManager(), + vertexConsumerProvider, + mc.getAtlasManager(), + NoopOutlineVertexConsumerProvider.INSTANCE, + NoopImmediateVertexConsumerProvider.INSTANCE, + mc.textRenderer + )) { + BlockEntityRenderState renderState = renderer.createRenderState(); + renderer.updateRenderState(blockEntity, renderState, tickDelta, mc.gameRenderer.getCamera().getCameraPos(), null); + renderer.render(renderState, matrices, renderCommandQueue, mc.gameRenderer.getEntityRenderStates().cameraRenderState); + + renderDispatcher.render(); + renderCommandQueue.onNextFrame(); + } catch (Throwable t) { + MeteorClient.LOG.error("Oops! no render", t); + } + } + } } - PARTS.clear(); + matrices.pop(); } - private static void renderQuads(List quads, float offsetX, float offsetY, float offsetZ, VertexConsumer consumer) { + private static void renderFluid(BlockRenderView renderView, BlockPos pos, BlockState state, VertexConsumer consumer) { + if (FABRIC_FLUID_RENDERER) { + FluidRenderHandlerRegistry.INSTANCE.get(state.getFluidState().getFluid()).renderFluid( + pos, + renderView, + consumer, + state, + state.getFluidState() + ); + } else { + MinecraftClient.getInstance().getBlockRenderManager().renderFluid( + pos, + renderView, + consumer, + state, + state.getFluidState() + ); + } + } + + private static void renderQuads(List quads, Matrix4f matrix4f, VertexConsumer consumer) { for (BakedQuad quad : quads) { - for (int j = 0; j < 4; j++) { - Vector3fc vec = quad.getPosition(j); - consumer.vertex(offsetX + vec.x(), offsetY + vec.y(), offsetZ + vec.z()); + for (int i = 0; i < 4; i++) { + Vector3fc pos = quad.getPosition(i); + POS.set(pos.x(), pos.y(), pos.z()).mulPosition(matrix4f); + + consumer.vertex(POS.x(), POS.y(), POS.z()); } } } + + public static boolean hasAnimatedTextures(BlockState state) { + if (!state.getFluidState().isEmpty()) { + return true; + } + + BlockStateModel model = mc.getBlockRenderManager().getModel(state); + RANDOM.setSeed(42L); + model.addParts(RANDOM, PARTS); + + try { + for (BlockModelPart part : PARTS) { + for (Direction direction : DIRECTIONS) { + for (BakedQuad quad : part.getQuads(direction)) { + if (quad.sprite().getContents().isAnimated()) { + return true; + } + } + } + + for (BakedQuad quad : part.getQuads(null)) { + if (quad.sprite().getContents().isAnimated()) { + return true; + } + } + } + + return false; + } finally { + PARTS.clear(); + } + } + + private record StaticBlockRenderView(BlockPos originPos, BlockState originState) implements BlockRenderView { + @Override + public float getBrightness(Direction direction, boolean shaded) { + if (!shaded) { + return 1f; + } else { + return switch (direction) { + case DOWN -> 0.5F; + case UP -> 1.0F; + case NORTH, SOUTH -> 0.8F; + case WEST, EAST -> 0.6F; + }; + } + } + + @Override + public LightingProvider getLightingProvider() { + return null; + } + + @Override + public int getColor(BlockPos pos, ColorResolver color) { + if (color == BiomeColors.GRASS_COLOR) return GrassColors.getColor(0.7f, 0.8f); + if (color == BiomeColors.FOLIAGE_COLOR) return FoliageColors.getColor(0.7f, 0.8f); + if (color == BiomeColors.DRY_FOLIAGE_COLOR) return DryFoliageColors.getColor(0.7f, 0.8f); + return 0x3f76e4; + } + + @Override + public int getLightLevel(LightType type, BlockPos pos) { + return 8; + } + + @Override + public int getBaseLightLevel(BlockPos pos, int ambientDarkness) { + return 8; + } + + @Override + public @Nullable BlockEntity getBlockEntity(BlockPos pos) { + return null; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return pos.equals(this.originPos()) ? this.originState() : Blocks.AIR.getDefaultState(); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return this.getBlockState(pos).getFluidState(); + } + + @Override + public int getHeight() { + return 1; + } + + @Override + public int getBottomY() { + return 0; + } + } }