Skip to content

Commit

Permalink
Add more accurate culling for single quad particles (#885)
Browse files Browse the repository at this point in the history
  • Loading branch information
embeddedt authored May 9, 2024
1 parent 27c5d7a commit 238a273
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 17 deletions.
8 changes: 4 additions & 4 deletions patches/net/minecraft/client/particle/Particle.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
}

+ /**
+ * Forge added method that controls if a particle should be culled to it's bounding box.
+ * Default behaviour is culling enabled
+ * Returns the bounding box that should be used for particle culling. {@link AABB#INFINITE} can be
+ * returned for particles that should not be culled.
+ */
+ public boolean shouldCull() {
+ return true;
+ public AABB getRenderBoundingBox(float partialTicks) {
+ return getBoundingBox().inflate(1.0);
+ }
+
+ public Vec3 getPos() {
Expand Down
14 changes: 12 additions & 2 deletions patches/net/minecraft/client/particle/ParticleEngine.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
particlerendertype.begin(bufferbuilder, this.textureManager);

for (Particle particle : iterable) {
+ if (frustum != null && particle.shouldCull() && !frustum.isVisible(particle.getBoundingBox().inflate(1.0))) continue;
+ if (frustum != null && !frustum.isVisible(particle.getRenderBoundingBox(p_107341_))) continue;
try {
particle.render(bufferbuilder, p_107340_, p_107341_);
} catch (Throwable throwable) {
Expand All @@ -102,7 +102,7 @@
);
}
}
@@ -556,12 +_,18 @@
@@ -556,12 +_,28 @@
d0 = (double)i + aabb.maxX + 0.1F;
}

Expand All @@ -115,6 +115,16 @@
return String.valueOf(this.particles.values().stream().mapToInt(Collection::size).sum());
+ }
+
+ public void iterateParticles(java.util.function.Consumer<Particle> consumer) {
+ for (ParticleRenderType particlerendertype : this.particles.keySet()) {
+ if (particlerendertype == ParticleRenderType.NO_RENDER) continue;
+ Iterable<Particle> iterable = this.particles.get(particlerendertype);
+ if (iterable != null) {
+ iterable.forEach(consumer);
+ }
+ }
+ }
+
+ public void addBlockHitEffects(BlockPos pos, net.minecraft.world.phys.BlockHitResult target) {
+ BlockState state = level.getBlockState(pos);
+ if (!net.neoforged.neoforge.client.extensions.common.IClientBlockExtensions.of(state).addHitEffects(state, level, target, this))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--- a/net/minecraft/client/particle/SingleQuadParticle.java
+++ b/net/minecraft/client/particle/SingleQuadParticle.java
@@ -81,6 +_,12 @@
.endVertex();
}

+ @Override
+ public net.minecraft.world.phys.AABB getRenderBoundingBox(float partialTicks) {
+ float size = getQuadSize(partialTicks);
+ return new net.minecraft.world.phys.AABB(this.x - size, this.y - size, this.z - size, this.x + size, this.y + size, this.z + size);
+ }
+
public float getQuadSize(float p_107681_) {
return this.quadSize;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
+
+ @Override
+ public net.minecraft.world.phys.AABB getRenderBoundingBox(PistonMovingBlockEntity blockEntity) {
+ return INFINITE_EXTENT_AABB;
+ return net.minecraft.world.phys.AABB.INFINITE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
+
+ @Override
+ public net.minecraft.world.phys.AABB getRenderBoundingBox(StructureBlockEntity blockEntity) {
+ return INFINITE_EXTENT_AABB;
+ return net.minecraft.world.phys.AABB.INFINITE;
+ }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
+
+ @Override
+ public net.minecraft.world.phys.AABB getRenderBoundingBox(TheEndGatewayBlockEntity blockEntity) {
+ return blockEntity.isSpawning() || blockEntity.isCoolingDown() ? INFINITE_EXTENT_AABB : super.getRenderBoundingBox(blockEntity);
+ return blockEntity.isSpawning() || blockEntity.isCoolingDown() ? net.minecraft.world.phys.AABB.INFINITE : super.getRenderBoundingBox(blockEntity);
+ }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

public boolean isVisible(AABB p_113030_) {
+ // FORGE: exit early for infinite bounds, these would otherwise fail in the intersection test at certain camera angles (GH-9321)
+ if (p_113030_.equals(net.neoforged.neoforge.client.extensions.IBlockEntityRendererExtension.INFINITE_EXTENT_AABB)) return true;
+ if (p_113030_.isInfinite()) return true;
return this.cubeInFrustum(p_113030_.minX, p_113030_.minY, p_113030_.minZ, p_113030_.maxX, p_113030_.maxY, p_113030_.maxZ);
}

24 changes: 24 additions & 0 deletions patches/net/minecraft/world/phys/AABB.java.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--- a/net/minecraft/world/phys/AABB.java
+++ b/net/minecraft/world/phys/AABB.java
@@ -9,6 +_,7 @@

public class AABB {
private static final double EPSILON = 1.0E-7;
+ public static final AABB INFINITE = new AABB(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
public final double minX;
public final double minY;
public final double minZ;
@@ -511,5 +_,13 @@
p_165883_.y + p_165885_ / 2.0,
p_165883_.z + p_165886_ / 2.0
);
+ }
+
+ /**
+ * {@return true if this AABB is infinite in all directions}
+ */
+ public boolean isInfinite() {
+ return this == INFINITE || (Double.isInfinite(this.minX) && Double.isInfinite(this.minY) && Double.isInfinite(this.minZ)
+ && Double.isInfinite(this.maxX) && Double.isInfinite(this.maxY) && Double.isInfinite(this.maxZ));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.BoolArgumentType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.commands.Commands;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.RegisterClientCommandsEvent;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion;

@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.GAME, modid = NeoForgeVersion.MOD_ID)
public final class ParticleBoundsDebugRenderer {
private static boolean enabled = false;

@SubscribeEvent
public static void onRenderLevelStage(RenderLevelStageEvent event) {
if (!enabled || event.getStage() != RenderLevelStageEvent.Stage.AFTER_PARTICLES) {
return;
}

var camPos = event.getCamera().getPosition();

PoseStack poseStack = event.getPoseStack();
poseStack.pushPose();
poseStack.translate(-camPos.x, -camPos.y, -camPos.z);

VertexConsumer consumer = Minecraft.getInstance().renderBuffers().bufferSource().getBuffer(RenderType.lines());

Minecraft.getInstance().particleEngine.iterateParticles(particle -> {
var bb = particle.getRenderBoundingBox(event.getPartialTick());
if (!bb.isInfinite() && event.getFrustum().isVisible(bb)) {
LevelRenderer.renderLineBox(poseStack, consumer, bb, 1F, 0F, 0F, 1F);
}
});

poseStack.popPose();
}

@SubscribeEvent
public static void onRegisterClientCommands(RegisterClientCommandsEvent event) {
event.getDispatcher().register(
Commands.literal("neoforge")
.then(Commands.literal("debug_particle_renderbounds")
.requires(src -> src.hasPermission(Commands.LEVEL_ADMINS))
.then(Commands.argument("enable", BoolArgumentType.bool())
.executes(ctx -> {
enabled = BoolArgumentType.getBool(ctx, "enable");
return Command.SINGLE_SUCCESS;
}))));
}

private ParticleBoundsDebugRenderer() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,10 @@
import net.minecraft.world.phys.AABB;

public interface IBlockEntityRendererExtension<T extends BlockEntity> {
/**
* Bounding box with infinite scope. Used as the render bounding box for blocks with dynamic render bounds which
* can't be trivially determined
*/
AABB INFINITE_EXTENT_AABB = new AABB(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);

/**
* Return an {@link AABB} that controls the visible scope of this {@link BlockEntityRenderer}.
* Defaults to the unit cube at the given position.
* Defaults to the unit cube at the given position. {@link AABB#INFINITE} can be used to declare the BER
* should be visible everywhere.
*
* @return an appropriately sized {@link AABB} for the {@link BlockEntityRenderer}
*/
Expand Down

0 comments on commit 238a273

Please sign in to comment.