diff --git a/build.gradle b/build.gradle index 66b400a..78a4603 100644 --- a/build.gradle +++ b/build.gradle @@ -52,7 +52,7 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - modImplementation "io.wispforest:owo-lib:${project.owo_version}" + annotationProcessor modImplementation("io.wispforest:owo-lib:${project.owo_version}") include "io.wispforest:owo-sentinel:${project.owo_version}" // modLocalRuntime "me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}" diff --git a/src/main/java/io/wispforest/lavender/Lavender.java b/src/main/java/io/wispforest/lavender/Lavender.java index 894c06d..36a49ef 100644 --- a/src/main/java/io/wispforest/lavender/Lavender.java +++ b/src/main/java/io/wispforest/lavender/Lavender.java @@ -21,6 +21,7 @@ import net.minecraft.util.Identifier; import net.minecraft.world.PersistentState; import org.slf4j.Logger; +import io.wispforest.lavender.LavenderConfig; import java.util.UUID; @@ -32,6 +33,8 @@ public class Lavender implements ModInitializer { public static final Identifier WORLD_ID_CHANNEL = Lavender.id("world_id_channel"); + public static final LavenderConfig CONFIG = LavenderConfig.createAndLoad(); + @Override public void onInitialize() { Registry.register(Registries.ITEM, id("dynamic_book"), LavenderBookItem.DYNAMIC_BOOK); diff --git a/src/main/java/io/wispforest/lavender/LavenderConfigModel.java b/src/main/java/io/wispforest/lavender/LavenderConfigModel.java new file mode 100644 index 0000000..3f5e914 --- /dev/null +++ b/src/main/java/io/wispforest/lavender/LavenderConfigModel.java @@ -0,0 +1,12 @@ +package io.wispforest.lavender; + +import io.wispforest.owo.config.annotation.Config; +import io.wispforest.owo.config.annotation.Modmenu; +import io.wispforest.owo.config.annotation.RangeConstraint; + +@Modmenu(modId = Lavender.MOD_ID) +@Config(name = "lavender", wrapperName = "LavenderConfig") +public class LavenderConfigModel { + @RangeConstraint(min = 0.0, max = 1.0) + public float structurePreviewAlpha = 0.5f; +} diff --git a/src/main/java/io/wispforest/lavender/client/StructureOverlayRenderer.java b/src/main/java/io/wispforest/lavender/client/StructureOverlayRenderer.java index e9cf125..1fd87bd 100644 --- a/src/main/java/io/wispforest/lavender/client/StructureOverlayRenderer.java +++ b/src/main/java/io/wispforest/lavender/client/StructureOverlayRenderer.java @@ -11,22 +11,31 @@ import io.wispforest.owo.ui.component.Components; import io.wispforest.owo.ui.container.Containers; import io.wispforest.owo.ui.container.FlowLayout; -import io.wispforest.owo.ui.core.*; +import io.wispforest.owo.ui.core.Easing; +import io.wispforest.owo.ui.core.HorizontalAlignment; +import io.wispforest.owo.ui.core.Insets; +import io.wispforest.owo.ui.core.Positioning; +import io.wispforest.owo.ui.core.Sizing; import io.wispforest.owo.ui.event.WindowResizeCallback; import io.wispforest.owo.ui.hud.Hud; import io.wispforest.owo.ui.util.Delta; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gl.SimpleFramebuffer; import net.minecraft.client.render.LightmapTextureManager; import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.OverlayVertexConsumer; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.RenderLayers; +import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.model.ModelLoader; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.sound.SoundCategory; +import net.minecraft.fluid.FluidState; import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; import net.minecraft.util.ActionResult; @@ -36,6 +45,7 @@ import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3i; +import net.minecraft.world.BlockRenderView; import org.apache.commons.lang3.mutable.MutableBoolean; import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL30C; @@ -51,7 +61,7 @@ public class StructureOverlayRenderer { var framebuffer = new SimpleFramebuffer(window.getFramebufferWidth(), window.getFramebufferHeight(), true, MinecraftClient.IS_SYSTEM_MAC); ((LavenderFramebufferExtension)framebuffer).lavender$setBlitProgram(() -> { - LavenderClient.BLIT_ALPHA_PROGRAM.setAlpha(.5f); + LavenderClient.BLIT_ALPHA_PROGRAM.setAlpha(Lavender.CONFIG.structurePreviewAlpha()); return LavenderClient.BLIT_ALPHA_PROGRAM.program(); }); framebuffer.setClearColor(0f, 0f, 0f, 0f); @@ -157,6 +167,7 @@ public static void initialize() { var entry = ACTIVE_OVERLAYS.get(anchor); var structure = entry.fetchStructure(); if (structure == null) return true; + var renderView = structure.asBlockRenderView(); // --- overlay rendering --- @@ -172,24 +183,36 @@ public static void initialize() { matrices.translate(anchor.getX(), anchor.getY(), anchor.getZ()); structure.forEachPredicate((pos, predicate) -> { - var state = context.world().getBlockState(testPos.set(anchor).move(pos)).rotate(StructureTemplate.inverse(entry.rotation)); + var state = context.world() + .getBlockState(testPos.set(anchor).move(pos)) + .rotate(StructureTemplate.inverse(entry.rotation)); var result = predicate.test(state); - if (result == BlockStatePredicate.Result.STATE_MATCH) { + if (result == BlockStatePredicate.Result.STATE_MATCH) return; - } else if (!state.isAir() && result == BlockStatePredicate.Result.NO_MATCH) { - hasInvalidBlock.setTrue(); - - matrices.push(); - matrices.translate(pos.getX(), pos.getY(), pos.getZ()); - client.getBlockRenderManager().renderDamage(state, testPos, context.world(), matrices, overlayConsumer); - matrices.pop(); - } + else if (entry.visibleLayer != -1 && pos.getY() != entry.visibleLayer) + return; + else if (!state.isAir() && result == BlockStatePredicate.Result.NO_MATCH) + renderBlockBreaking(context, pos, state, hasInvalidBlock, matrices, client, testPos, overlayConsumer); - if (entry.visibleLayer != -1 && pos.getY() != entry.visibleLayer) return; renderOverlayBlock(matrices, CONSUMERS, pos, predicate, entry.rotation); + }, entry.rotation); + CONSUMERS.draw(); // render blocks before fluids + + GlStateManager._depthMask(false); // Disable depth mask for rendering fluids + var matrixStack = RenderSystem.getModelViewStack().pushMatrix(); + matrixStack.mul(matrices.peek().getPositionMatrix()); + RenderSystem.applyModelViewMatrix(); + structure.forEachPredicate((pos, predicate) -> { + if (entry.visibleLayer != -1 && pos.getY() != entry.visibleLayer) + return; + renderOverlayFluid(renderView, matrices, CONSUMERS, pos, predicate, entry.rotation); }, entry.rotation); + CONSUMERS.draw(); // render fluids + RenderSystem.getModelViewStack().popMatrix(); + RenderSystem.applyModelViewMatrix(); + GlStateManager._depthMask(true); matrices.pop(); } @@ -234,11 +257,23 @@ public static void initialize() { var structure = PENDING_OVERLAY.fetchStructure(); if (structure != null) { if (client.player.raycast(5, client.getRenderTickCounter().getTickDelta(false), false) instanceof BlockHitResult target) { + var renderView = structure.asBlockRenderView(); var targetPos = target.getBlockPos().add(getPendingOffset(structure)); if (!client.player.isSneaking()) targetPos = targetPos.offset(target.getSide()); matrices.translate(targetPos.getX(), targetPos.getY(), targetPos.getZ()); structure.forEachPredicate((pos, predicate) -> renderOverlayBlock(matrices, CONSUMERS, pos, predicate, PENDING_OVERLAY.rotation), PENDING_OVERLAY.rotation); + CONSUMERS.draw(); // render blocks before fluids + + GlStateManager._depthMask(false); // Disable depth mask for rendering fluids + var matrixStack = RenderSystem.getModelViewStack().pushMatrix(); + matrixStack.mul(matrices.peek().getPositionMatrix()); + RenderSystem.applyModelViewMatrix(); + structure.forEachPredicate((pos, predicate) -> renderOverlayFluid(renderView, matrices, CONSUMERS, pos, predicate, PENDING_OVERLAY.rotation), PENDING_OVERLAY.rotation); + CONSUMERS.draw(); // render fluids + RenderSystem.getModelViewStack().popMatrix(); + RenderSystem.applyModelViewMatrix(); + GlStateManager._depthMask(true); } } else { PENDING_OVERLAY = null; @@ -294,7 +329,34 @@ private static Vec3i getPendingOffset(StructureTemplate structure) { // @formatter:on } - private static void renderOverlayBlock(MatrixStack matrices, VertexConsumerProvider consumers, BlockPos offsetInStructure, BlockStatePredicate block, BlockRotation rotation) { + private static void renderBlockBreaking(WorldRenderContext context, BlockPos pos, BlockState state, MutableBoolean hasInvalidBlock, + MatrixStack matrices, MinecraftClient client, BlockPos.Mutable testPos, + OverlayVertexConsumer overlayConsumer) { + hasInvalidBlock.setTrue(); + + matrices.push(); + matrices.translate(pos.getX(), pos.getY(), pos.getZ()); + client.getBlockRenderManager().renderDamage(state, testPos, context.world(), matrices, overlayConsumer); + matrices.pop(); + } + + private static void renderOverlayFluid(BlockRenderView renderView, MatrixStack matrices, VertexConsumerProvider consumers, + BlockPos offsetInStructure, BlockStatePredicate block, BlockRotation rotation) { + BlockState state = block.preview().rotate(rotation); + FluidState fluidState = state.getFluidState(); + + if (!fluidState.isEmpty()) { + RenderLayer renderLayer = RenderLayers.getFluidLayer(fluidState); + VertexConsumer consumer = consumers.getBuffer(renderLayer); + MinecraftClient.getInstance() + .getBlockRenderManager() + .renderFluid(offsetInStructure, renderView, consumer, state, fluidState); + } + } + + private static void renderOverlayBlock(MatrixStack matrices, VertexConsumerProvider consumers, BlockPos offsetInStructure, + BlockStatePredicate block, BlockRotation rotation) { + matrices.push(); matrices.translate(offsetInStructure.getX(), offsetInStructure.getY(), offsetInStructure.getZ()); @@ -309,6 +371,7 @@ private static void renderOverlayBlock(MatrixStack matrices, VertexConsumerProvi LightmapTextureManager.MAX_BLOCK_LIGHT_COORDINATE, OverlayTexture.DEFAULT_UV ); + matrices.pop(); } diff --git a/src/main/resources/assets/lavender/lang/en_us.json b/src/main/resources/assets/lavender/lang/en_us.json index 904466a..080e6d8 100644 --- a/src/main/resources/assets/lavender/lang/en_us.json +++ b/src/main/resources/assets/lavender/lang/en_us.json @@ -68,5 +68,7 @@ ], "text.lavender.book.bookmark.add": "Add Bookmark", - "text.lavender.book.bookmark.remove_hint": {"text": "Shift-click to remove", "color": "dark_gray"} -} \ No newline at end of file + "text.lavender.book.bookmark.remove_hint": {"text": "Shift-click to remove", "color": "dark_gray"}, + + "text.config.lavender.option.structurePreviewAlpha": "Structure Preview Alpha" +} diff --git a/src/testmod/resources/assets/lavender-flower/lavender/structures/conduit.json b/src/testmod/resources/assets/lavender-flower/lavender/structures/conduit.json new file mode 100644 index 0000000..d0d8c9a --- /dev/null +++ b/src/testmod/resources/assets/lavender-flower/lavender/structures/conduit.json @@ -0,0 +1,44 @@ +{ + "keys": { + "p": "minecraft:prismarine", + ",": "minecraft:water", + "anchor": "minecraft:conduit" + }, + "layers": [ + [ + " p ", + " p ", + "ppppp", + " p ", + " p " + ], + [ + " p ", + " ,,, ", + "p,,,p", + " ,,, ", + " p " + ], + [ + "ppppp", + "p,,,p", + "p,#,p", + "p,,,p", + "ppppp" + ], + [ + " p ", + " ,,, ", + "p,,,p", + " ,,, ", + " p " + ], + [ + " p ", + " p ", + "ppppp", + " p ", + " p " + ] + ] +}