Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fusion bloom fix & other bloom effect API modifications #2470

Merged
merged 6 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/main/java/gregtech/client/event/ClientEventHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -157,4 +159,9 @@ private static void renderHUDMetaItem(@NotNull ItemStack stack) {
}
}
}

@SubscribeEvent
public static void onWorldUnload(WorldEvent.Unload event) {
BloomEffectUtil.invalidateWorldTickets(event.getWorld());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
158 changes: 136 additions & 22 deletions src/main/java/gregtech/client/utils/BloomEffectUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,15 +39,19 @@
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 {

private static final Map<BloomRenderKey, List<BloomRenderTicket>> BLOOM_RENDERS = new Object2ObjectOpenHashMap<>();
private static final List<BloomRenderTicket> SCHEDULED_BLOOM_RENDERS = new ArrayList<>();

private static final ReentrantLock BLOOM_RENDER_LOCK = new ReentrantLock();

/**
* @deprecated use {@link #getBloomLayer()}
*/
Expand Down Expand Up @@ -147,12 +152,12 @@ public static Framebuffer getBloomFBO() {
/**
* <p>
* 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()}.
* </p>
* <p>
* 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.
* </p>
*
* @param setup Render setup, if exists
Expand All @@ -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);
}

/**
Expand All @@ -178,8 +198,8 @@ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup
* {@link BloomRenderTicket#invalidate()}.
* </p>
* <p>
* 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.
* </p>
*
* @param setup Render setup, if exists
Expand All @@ -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,
Expand All @@ -204,8 +224,8 @@ public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup
* manually freed by calling {@link BloomRenderTicket#invalidate()}, or invalidated by validity checker.
* </p>
* <p>
* 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.
* </p>
*
* @param setup Render setup, if exists
Expand All @@ -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<BloomRenderTicket> 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);
}

/**
* <p>
* 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.
* </p>
* <p>
* This method does not register bloom render ticket when Optifine is present, and an invalid ticket will be
* returned instead.
* </p>
*
* @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<BloomRenderTicket> validityChecker,
@Nullable Supplier<World> 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<BloomRenderKey, List<BloomRenderTicket>> 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
*/
Expand Down Expand Up @@ -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);
Expand All @@ -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 ||
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<BloomRenderTicket> validityChecker;
@Nullable
private final Supplier<World> 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<BloomRenderTicket> validityChecker) {
@NotNull IBloomEffect render, @Nullable Predicate<BloomRenderTicket> validityChecker,
@Nullable Supplier<World> 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;
}
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/gregtech/client/utils/EffectRenderContext.java
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -134,4 +142,12 @@ public float rotationXY() {
public float rotationXZ() {
return rotationXZ;
}

/**
* @return camera
*/
@NotNull
public Frustum camera() {
return camera;
}
}
Loading
Loading