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);
}