diff --git a/src/main/java/gregtech/client/event/ClientEventHandler.java b/src/main/java/gregtech/client/event/ClientEventHandler.java index c83851d8e34..f4ac14d8bfd 100644 --- a/src/main/java/gregtech/client/event/ClientEventHandler.java +++ b/src/main/java/gregtech/client/event/ClientEventHandler.java @@ -11,6 +11,7 @@ import gregtech.client.renderer.handler.BlockPosHighlightRenderer; import gregtech.client.renderer.handler.MultiblockPreviewRenderer; import gregtech.client.renderer.handler.TerminalARRenderer; +import gregtech.client.utils.BloomEffectUtil; import gregtech.client.utils.DepthTextureUtil; import gregtech.client.utils.TooltipHelper; import gregtech.common.ConfigHolder; @@ -24,6 +25,7 @@ import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.*; +import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.client.event.ConfigChangedEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.EventPriority; @@ -157,4 +159,9 @@ private static void renderHUDMetaItem(@NotNull ItemStack stack) { } } } + + @SubscribeEvent + public static void onWorldUnload(WorldEvent.Unload event) { + BloomEffectUtil.invalidateWorldTickets(event.getWorld()); + } } diff --git a/src/main/java/gregtech/client/particle/GTOverheatParticle.java b/src/main/java/gregtech/client/particle/GTOverheatParticle.java index d017af7b3a0..8736b628798 100644 --- a/src/main/java/gregtech/client/particle/GTOverheatParticle.java +++ b/src/main/java/gregtech/client/particle/GTOverheatParticle.java @@ -251,7 +251,15 @@ public void renderBloomEffect(@NotNull BufferBuilder buffer, @NotNull EffectRend @Override public boolean shouldRenderBloomEffect(@NotNull EffectRenderContext context) { - return !this.insulated; + if (this.insulated) return false; + for (Cuboid6 cuboid : pipeBoxes) { + if (!context.camera().isBoxInFrustum( + cuboid.min.x + posX, cuboid.min.y + posY, cuboid.min.z + posZ, + cuboid.max.x + posX, cuboid.max.y + posY, cuboid.max.z + posZ)) { + return false; + } + } + return true; } private static final IRenderSetup SETUP = new IRenderSetup() { diff --git a/src/main/java/gregtech/client/utils/BloomEffectUtil.java b/src/main/java/gregtech/client/utils/BloomEffectUtil.java index 2dcc7d0be14..c4a4e0548ee 100644 --- a/src/main/java/gregtech/client/utils/BloomEffectUtil.java +++ b/src/main/java/gregtech/client/utils/BloomEffectUtil.java @@ -20,6 +20,7 @@ import net.minecraft.entity.Entity; import net.minecraft.launchwrapper.Launch; import net.minecraft.util.BlockRenderLayer; +import net.minecraft.world.World; import net.minecraftforge.common.util.EnumHelper; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -38,8 +39,10 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; @SideOnly(Side.CLIENT) public class BloomEffectUtil { @@ -47,6 +50,8 @@ public class BloomEffectUtil { private static final Map> BLOOM_RENDERS = new Object2ObjectOpenHashMap<>(); private static final List SCHEDULED_BLOOM_RENDERS = new ArrayList<>(); + private static final ReentrantLock BLOOM_RENDER_LOCK = new ReentrantLock(); + /** * @deprecated use {@link #getBloomLayer()} */ @@ -147,12 +152,12 @@ public static Framebuffer getBloomFBO() { /** *

* Register a custom bloom render callback for subsequent world render. The render call persists until the - * {@code metaTileEntity} is invalidated, or the ticket is manually freed by calling - * {@link BloomRenderTicket#invalidate()}. + * {@code metaTileEntity} is invalidated, or the world associated with {@code metaTileEntity} or the ticket is + * manually freed by calling {@link BloomRenderTicket#invalidate()}. *

*

- * This method does not register bloom render ticket when Optifine is present, and {@code null} will be returned - * instead of a ticket instance. + * This method does not register bloom render ticket when Optifine is present, and an invalid ticket will be + * returned instead. *

* * @param setup Render setup, if exists @@ -162,13 +167,28 @@ public static Framebuffer getBloomFBO() { * @return Ticket for the registered bloom render callback * @throws NullPointerException if {@code bloomType == null || render == null || metaTileEntity == null} */ - @Nullable + @NotNull public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup, @NotNull BloomType bloomType, @NotNull IBloomEffect render, @NotNull MetaTileEntity metaTileEntity) { Objects.requireNonNull(metaTileEntity, "metaTileEntity == null"); - return registerBloomRender(setup, bloomType, render, t -> metaTileEntity.isValid()); + return registerBloomRender(setup, bloomType, + new IBloomEffect() { + + @Override + public void renderBloomEffect(@NotNull BufferBuilder buffer, @NotNull EffectRenderContext context) { + render.renderBloomEffect(buffer, context); + } + + @Override + public boolean shouldRenderBloomEffect(@NotNull EffectRenderContext context) { + return metaTileEntity.getWorld() == context.renderViewEntity().world && + render.shouldRenderBloomEffect(context); + } + }, + t -> metaTileEntity.isValid(), + metaTileEntity::getWorld); } /** @@ -178,8 +198,8 @@ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup * {@link BloomRenderTicket#invalidate()}. *

*

- * This method does not register bloom render ticket when Optifine is present, and {@code null} will be returned - * instead of a ticket instance. + * This method does not register bloom render ticket when Optifine is present, and an invalid ticket will be + * returned instead. *

* * @param setup Render setup, if exists @@ -189,7 +209,7 @@ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup * @return Ticket for the registered bloom render callback * @throws NullPointerException if {@code bloomType == null || render == null || metaTileEntity == null} */ - @Nullable + @NotNull public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup, @NotNull BloomType bloomType, @NotNull IBloomEffect render, @@ -204,8 +224,8 @@ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup * manually freed by calling {@link BloomRenderTicket#invalidate()}, or invalidated by validity checker. *

*

- * This method does not register bloom render ticket when Optifine is present, and {@code null} will be returned - * instead of a ticket instance. + * This method does not register bloom render ticket when Optifine is present, and an invalid ticket will be + * returned instead. *

* * @param setup Render setup, if exists @@ -215,18 +235,85 @@ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup * Checked on both pre/post render each frame. * @return Ticket for the registered bloom render callback * @throws NullPointerException if {@code bloomType == null || render == null} + * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, MetaTileEntity) + * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, GTParticle) + * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, Predicate, Supplier) */ - @Nullable + @NotNull public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup, @NotNull BloomType bloomType, @NotNull IBloomEffect render, @Nullable Predicate validityChecker) { - if (Mods.Optifine.isModLoaded()) return null; - BloomRenderTicket ticket = new BloomRenderTicket(setup, bloomType, render, validityChecker); - SCHEDULED_BLOOM_RENDERS.add(ticket); + return registerBloomRender(setup, bloomType, render, validityChecker, null); + } + + /** + *

+ * Register a custom bloom render callback for subsequent world render. The render call persists until it is + * manually freed by calling {@link BloomRenderTicket#invalidate()}, or invalidated by validity checker. + *

+ *

+ * This method does not register bloom render ticket when Optifine is present, and an invalid ticket will be + * returned instead. + *

+ * + * @param setup Render setup, if exists + * @param bloomType Type of the bloom + * @param render Rendering callback + * @param validityChecker Optional validity checker; returning {@code false} causes the ticket to be invalidated. + * Checked on both pre/post render each frame. + * @param worldContext Optional world bound to the ticket. If the world returned is not null, the bloom ticket + * will be automatically invalidated on world unload. If world context returns {@code null}, + * it will not be affected by aforementioned automatic invalidation. + * @return Ticket for the registered bloom render callback + * @throws NullPointerException if {@code bloomType == null || render == null} + * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, MetaTileEntity) + * @see #registerBloomRender(IRenderSetup, BloomType, IBloomEffect, GTParticle) + */ + @NotNull + public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup, + @NotNull BloomType bloomType, + @NotNull IBloomEffect render, + @Nullable Predicate validityChecker, + @Nullable Supplier worldContext) { + if (Mods.Optifine.isModLoaded()) return BloomRenderTicket.INVALID; + BloomRenderTicket ticket = new BloomRenderTicket(setup, bloomType, render, validityChecker, worldContext); + BLOOM_RENDER_LOCK.lock(); + try { + SCHEDULED_BLOOM_RENDERS.add(ticket); + } finally { + BLOOM_RENDER_LOCK.unlock(); + } return ticket; } + /** + * Invalidate tickets associated with given world. + * + * @param world World + */ + public static void invalidateWorldTickets(@NotNull World world) { + Objects.requireNonNull(world, "world == null"); + BLOOM_RENDER_LOCK.lock(); + try { + for (BloomRenderTicket ticket : SCHEDULED_BLOOM_RENDERS) { + if (ticket.isValid() && ticket.worldContext != null && ticket.worldContext.get() == world) { + ticket.invalidate(); + } + } + + for (Map.Entry> e : BLOOM_RENDERS.entrySet()) { + for (BloomRenderTicket ticket : e.getValue()) { + if (ticket.isValid() && ticket.worldContext != null && ticket.worldContext.get() == world) { + ticket.invalidate(); + } + } + } + } finally { + BLOOM_RENDER_LOCK.unlock(); + } + } + /** * @deprecated use ticket-based bloom render hooks */ @@ -281,14 +368,26 @@ public static int renderBloomBlockLayer(RenderGlobal renderGlobal, BlockRenderLayer blockRenderLayer, // 70% sure it's translucent uh yeah double partialTicks, int pass, - Entity entity) { - Minecraft mc = Minecraft.getMinecraft(); - mc.profiler.endStartSection("BTLayer"); + @NotNull Entity entity) { + Minecraft.getMinecraft().profiler.endStartSection("BTLayer"); if (Mods.Optifine.isModLoaded()) { return renderGlobal.renderBlockLayer(blockRenderLayer, partialTicks, pass, entity); } + BLOOM_RENDER_LOCK.lock(); + try { + return renderBloomInternal(renderGlobal, blockRenderLayer, partialTicks, pass, entity); + } finally { + BLOOM_RENDER_LOCK.unlock(); + } + } + + private static int renderBloomInternal(RenderGlobal renderGlobal, + BlockRenderLayer blockRenderLayer, + double partialTicks, + int pass, + @NotNull Entity entity) { preDraw(); EffectRenderContext context = EffectRenderContext.getInstance().update(entity, (float) partialTicks); @@ -309,7 +408,7 @@ public static int renderBloomBlockLayer(RenderGlobal renderGlobal, return renderGlobal.renderBlockLayer(blockRenderLayer, partialTicks, pass, entity); } - Framebuffer fbo = mc.getFramebuffer(); + Framebuffer fbo = Minecraft.getMinecraft().getFramebuffer(); if (bloomFBO == null || bloomFBO.framebufferWidth != fbo.framebufferWidth || @@ -360,12 +459,12 @@ public static int renderBloomBlockLayer(RenderGlobal renderGlobal, // reset transparent layer render state and render OpenGlHelper.glBindFramebuffer(OpenGlHelper.GL_FRAMEBUFFER, fbo.framebufferObject); GlStateManager.enableBlend(); - mc.getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); GlStateManager.shadeModel(GL11.GL_SMOOTH); int result = renderGlobal.renderBlockLayer(blockRenderLayer, partialTicks, pass, entity); - mc.profiler.endStartSection("bloom"); + Minecraft.getMinecraft().profiler.endStartSection("bloom"); // blend bloom + transparent fbo.bindFramebufferTexture(); @@ -492,29 +591,44 @@ private record BloomRenderKey(@Nullable IRenderSetup renderSetup, @NotNull Bloom public static final class BloomRenderTicket { + public static final BloomRenderTicket INVALID = new BloomRenderTicket(); + @Nullable private final IRenderSetup renderSetup; private final BloomType bloomType; private final IBloomEffect render; @Nullable private final Predicate validityChecker; + @Nullable + private final Supplier worldContext; private boolean invalidated; + BloomRenderTicket() { + this(null, BloomType.DISABLED, (b, c) -> {}, null, null); + this.invalidated = true; + } + BloomRenderTicket(@Nullable IRenderSetup renderSetup, @NotNull BloomType bloomType, - @NotNull IBloomEffect render, @Nullable Predicate validityChecker) { + @NotNull IBloomEffect render, @Nullable Predicate validityChecker, + @Nullable Supplier worldContext) { this.renderSetup = renderSetup; this.bloomType = Objects.requireNonNull(bloomType, "bloomType == null"); this.render = Objects.requireNonNull(render, "render == null"); this.validityChecker = validityChecker; + this.worldContext = worldContext; } @Nullable + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "2.9") public IRenderSetup getRenderSetup() { return this.renderSetup; } @NotNull + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "2.9") public BloomType getBloomType() { return this.bloomType; } diff --git a/src/main/java/gregtech/client/utils/EffectRenderContext.java b/src/main/java/gregtech/client/utils/EffectRenderContext.java index e5b63b69feb..bee87a7a85a 100644 --- a/src/main/java/gregtech/client/utils/EffectRenderContext.java +++ b/src/main/java/gregtech/client/utils/EffectRenderContext.java @@ -1,6 +1,8 @@ package gregtech.client.utils; import net.minecraft.client.renderer.ActiveRenderInfo; +import net.minecraft.client.renderer.culling.ClippingHelperImpl; +import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.entity.Entity; import net.minecraft.util.math.Vec3d; @@ -20,6 +22,9 @@ public static EffectRenderContext getInstance() { return instance; } + private final ClippingHelperImpl clippingHelper = new ClippingHelperImpl(); + private final Frustum camera = new Frustum(this.clippingHelper); + @Nullable private Entity renderViewEntity; private float partialTicks; @@ -53,6 +58,9 @@ public EffectRenderContext update(@NotNull Entity renderViewEntity, float partia this.rotationXY = ActiveRenderInfo.getRotationXY(); this.rotationXZ = ActiveRenderInfo.getRotationXZ(); + this.clippingHelper.init(); + this.camera.setPosition(this.cameraX, this.cameraY, this.cameraZ); + return this; } @@ -134,4 +142,12 @@ public float rotationXY() { public float rotationXZ() { return rotationXZ; } + + /** + * @return camera + */ + @NotNull + public Frustum camera() { + return camera; + } } diff --git a/src/main/java/gregtech/client/utils/IBloomEffect.java b/src/main/java/gregtech/client/utils/IBloomEffect.java index 2d3052ce09d..0e345794f3d 100644 --- a/src/main/java/gregtech/client/utils/IBloomEffect.java +++ b/src/main/java/gregtech/client/utils/IBloomEffect.java @@ -9,8 +9,11 @@ import org.jetbrains.annotations.NotNull; +import java.util.function.Predicate; + /** - * Render callback interface for {@link BloomEffectUtil#registerBloomRender(IRenderSetup, BloomType, IBloomEffect)}. + * Render callback interface for + * {@link BloomEffectUtil#registerBloomRender(IRenderSetup, BloomType, IBloomEffect, Predicate)}. */ @FunctionalInterface public interface IBloomEffect { @@ -26,8 +29,8 @@ public interface IBloomEffect { /** * @param context render context - * @return if this effect should be rendered; returning {@code false} skips {@link #renderBloomEffect(BufferBuilder, - * EffectRenderContext)} call. + * @return if this effect should be rendered; returning {@code false} skips + * {@link #renderBloomEffect(BufferBuilder, EffectRenderContext)} call. */ @SideOnly(Side.CLIENT) default boolean shouldRenderBloomEffect(@NotNull EffectRenderContext context) { diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java index 35c06785bab..6b735dcc96c 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java @@ -691,18 +691,20 @@ public void renderBloomEffect(@NotNull BufferBuilder buffer, @NotNull EffectRend EnumFacing.Axis axis = RelativeDirection.UP.getRelativeFacing(getFrontFacing(), getUpwardsFacing(), isFlipped()) .getAxis(); + buffer.begin(GL11.GL_QUAD_STRIP, DefaultVertexFormats.POSITION_COLOR); RenderBufferHelper.renderRing(buffer, getPos().getX() - context.cameraX() + relativeBack.getXOffset() * 7 + 0.5, getPos().getY() - context.cameraY() + relativeBack.getYOffset() * 7 + 0.5, getPos().getZ() - context.cameraZ() + relativeBack.getZOffset() * 7 + 0.5, 6, 0.2, 10, 20, r, g, b, a, axis); + Tessellator.getInstance().draw(); } @Override @SideOnly(Side.CLIENT) public boolean shouldRenderBloomEffect(@NotNull EffectRenderContext context) { - return this.hasFusionRingColor(); + return this.hasFusionRingColor() && context.camera().isBoundingBoxInFrustum(getRenderBoundingBox()); } @Override @@ -753,14 +755,10 @@ public void preDraw(@NotNull BufferBuilder buffer) { GlStateManager.color(1, 1, 1, 1); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240.0F, 240.0F); GlStateManager.disableTexture2D(); - - buffer.begin(GL11.GL_QUAD_STRIP, DefaultVertexFormats.POSITION_COLOR); } @Override public void postDraw(@NotNull BufferBuilder buffer) { - Tessellator.getInstance().draw(); - GlStateManager.enableTexture2D(); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, lastBrightnessX, lastBrightnessY); }