From 06472babe2cf686ac9f2f0300b6ee2d87f3c4169 Mon Sep 17 00:00:00 2001 From: OreCruncher Date: Fri, 3 Jan 2025 09:35:48 -0800 Subject: [PATCH] Diagnostic support for particle render collections --- .../dsurround/effects/WaterRippleHandler.java | 13 +-- .../particles/ParticleRenderCollection.java | 88 +++++++++++++++++-- .../eventing/CollectDiagnosticsEvent.java | 1 + .../gui/overlay/DiagnosticsOverlay.java | 2 + .../dsurround/lib/gui/ColorPalette.java | 24 ++--- 5 files changed, 100 insertions(+), 28 deletions(-) diff --git a/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java b/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java index 9f9f6052..1c6b15ed 100644 --- a/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java +++ b/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java @@ -1,6 +1,5 @@ package org.orecruncher.dsurround.effects; -import dev.architectury.event.events.client.ClientLifecycleEvent; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.Particle; import net.minecraft.core.BlockPos; @@ -18,25 +17,19 @@ public class WaterRippleHandler { private static final Configuration.BlockEffects CONFIG = ContainerManager.resolve(Configuration.BlockEffects.class); private static final ITagLibrary TAG_LIBRARY = ContainerManager.resolve(ITagLibrary.class); - private static final ParticleRenderCollection.Helper rippleHelper = new ParticleRenderCollection.Helper<>(() -> CONFIG.waterRippleStyle.getTexture()); + private static final ParticleRenderCollection.Helper rippleHelper = + new ParticleRenderCollection.Helper<>("WaterRipples", () -> CONFIG.waterRippleStyle.getTexture()); // Fudge factor because the height algo is off. private static final double LIQUID_HEIGHT_ADJUST = (1D / 9D) + 0.1D; - static { - // Register the load to clear out particle collections from manager - ClientLifecycleEvent.CLIENT_LEVEL_LOAD.register(state -> { - rippleHelper.clear(); - }); - } - private static boolean doRipples() { return CONFIG.waterRippleStyle != WaterRippleStyle.NONE; } private static void addWaterRipple(ClientLevel world, double x, double y, double z) { var ripple = new WaterRippleParticle(CONFIG.waterRippleStyle, world, x, y, z); - rippleHelper.get().add(ripple); + rippleHelper.add(ripple); } public static void createRippleParticle(ClientLevel world, Particle particle, Vec3 position) { diff --git a/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java b/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java index f364701d..05dbfdc8 100644 --- a/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java +++ b/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java @@ -2,17 +2,25 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.VertexConsumer; +import dev.architectury.event.events.client.ClientLifecycleEvent; import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.TextureSheetParticle; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.orecruncher.dsurround.eventing.ClientEventHooks; +import org.orecruncher.dsurround.eventing.ClientState; +import org.orecruncher.dsurround.eventing.CollectDiagnosticsEvent; import org.orecruncher.dsurround.lib.GameUtils; import org.orecruncher.dsurround.lib.collections.ObjectArray; import java.lang.ref.WeakReference; +import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -23,11 +31,13 @@ */ public final class ParticleRenderCollection extends Particle { + private final Consumer setup; private final Supplier textureSupplier; private final ObjectArray particles; - private ParticleRenderCollection(ClientLevel clientLevel, Supplier textureSupplier) { + private ParticleRenderCollection(@NotNull ClientLevel clientLevel, @NotNull Supplier textureSupplier, @Nullable Consumer setup) { super(clientLevel, 0, 0, 0); + this.setup = Objects.requireNonNullElseGet(setup, () -> this::standardSetup); this.textureSupplier = textureSupplier; this.particles = new ObjectArray<>(128); this.tick(); @@ -36,6 +46,7 @@ private ParticleRenderCollection(ClientLevel clientLevel, Supplier p.render(vertexConsumer, camera, tickDelta)); + } + + private void standardSetup(@NotNull Camera camera) { RenderSystem.depthMask(true); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); - - this.particles.forEach(p -> p.render(vertexConsumer, camera, tickDelta)); } - public void add(TParticle particle) { + public void add(@NotNull TParticle particle) { // Can only accept custom style particles if (particle.getRenderType() != this.getRenderType()) throw new RuntimeException("Can only add render type %s particles to collection".formatted(this.getRenderType())); this.particles.add(particle); } + /** + * Helper that manages related particles in Minecraft's ParticleEngine. The helper will register with events, so + * instances of this class need to be maintained as singletons throughout the lifetime of the client. + */ public static final class Helper { + private final String name; + private final Consumer setup; private final Supplier textureSupplier; + private WeakReference> particle; + private String diagnostics; + + /** + * Initializes a helper instance used to manage the state of the main particle within the ParticleEngine. + * Particle rendering will use the default setup. + * + * @param name The name of the helper; used in diagnostics + * @param textureSupplier Provides the texture to bind when rendering + */ + public Helper(@NotNull String name, @NotNull Supplier textureSupplier) { + this(name, textureSupplier, null); + } - public Helper(Supplier textureSupplier) { + /** + * Initializes a helper instance used to manage the state of the main particle within the ParticleEngine. + * + * @param name The name of the helper; used in diagnostics + * @param textureSupplier Provides the texture to bind when rendering + * @param setup Provides for the configuration of the rendering system if the default is not enough + */ + public Helper(@NotNull String name, @NotNull Supplier textureSupplier, @Nullable Consumer setup) { + this.name = name; + this.setup = setup; this.textureSupplier = textureSupplier; + this.diagnostics = this.name; + + ClientLifecycleEvent.CLIENT_LEVEL_LOAD.register(state -> this.clear()); + ClientEventHooks.COLLECT_DIAGNOSTICS.register(this::collectDiagnostics); + ClientState.TICK_END.register(this::tick); } - public ParticleRenderCollection get() { + /** + * Adds a particle to the helper. + * + * @param particle The particle to add + */ + public void add(TParticle particle) { + this.get().add(particle); + } + + @NotNull + private ParticleRenderCollection get() { var pc = this.particle != null ? this.particle.get() : null; if (pc == null || !pc.isAlive()) { - pc = new ParticleRenderCollection<>(GameUtils.getWorld().orElseThrow(), this.textureSupplier); + pc = new ParticleRenderCollection<>(GameUtils.getWorld().orElseThrow(), this.textureSupplier, this.setup); this.particle = new WeakReference<>(pc); GameUtils.getParticleManager().add(pc); } return pc; } - public void clear() { + private void clear() { var pc = this.particle != null ? this.particle.get() : null; if (pc != null) { pc.remove(); this.particle = null; } } + + private void tick(@NotNull Minecraft client) { + var pc = this.particle != null ? this.particle.get() : null; + this.diagnostics = this.name + ": "; + if (pc == null) + this.diagnostics += "Not Set"; + else if (!pc.isAlive()) + this.diagnostics += "DEAD"; + else + this.diagnostics += pc.particles.size(); + } + + private void collectDiagnostics(@NotNull CollectDiagnosticsEvent event) { + event.add(CollectDiagnosticsEvent.Section.Particles, this.diagnostics); + } } } diff --git a/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java b/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java index 7bc816e0..4d55e0cd 100644 --- a/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java +++ b/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java @@ -12,6 +12,7 @@ public final class CollectDiagnosticsEvent { public enum Section { Header(false), Systems, + Particles, Timers(false), Environment(false), Emitters, diff --git a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java index b8c3c741..32d71fa6 100644 --- a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java +++ b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java @@ -37,6 +37,7 @@ public class DiagnosticsOverlay extends AbstractOverlay { static { COLOR_MAP.put(CollectDiagnosticsEvent.Section.Header, ColorPalette.PUMPKIN_ORANGE); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Systems, ColorPalette.GREEN); + COLOR_MAP.put(CollectDiagnosticsEvent.Section.Particles, ColorPalette.HOT_PINK); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Timers, ColorPalette.KEY_LIME); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Environment, ColorPalette.AQUAMARINE); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Emitters, ColorPalette.SEASHELL); @@ -51,6 +52,7 @@ public class DiagnosticsOverlay extends AbstractOverlay { LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Header); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Environment); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Systems); + LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Particles); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Emitters); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Sounds); diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java b/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java index 9856d0cb..f2298414 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java @@ -2,6 +2,7 @@ import net.minecraft.ChatFormatting; import net.minecraft.network.chat.TextColor; +import net.minecraft.util.FastColor; import net.minecraft.util.Mth; @SuppressWarnings("unused") @@ -74,20 +75,21 @@ public final class ColorPalette { public static final TextColor ANTIQUE_WHITE = of(250,235,215); public static final TextColor PEARLY_PURPLE = of(183,104,162); public static final TextColor FRESH_AIR = of(166,231,255); + public static final TextColor HOT_PINK = of("#ff69b4"); public static final TextColor LEMON = of(254, 251, 1); public static final TextColor ELECTRIC_GREEN = of(0, 237, 1); public static int getRed(int rgb) { - return (rgb >> 16) & 0xFF; + return FastColor.ARGB32.red(rgb); } public static int getGreen(int rgb) { - return (rgb >> 8) & 0xFF; + return FastColor.ARGB32.green(rgb); } public static int getBlue(int rgb) { - return rgb & 0xFF; + return FastColor.ARGB32.blue(rgb); } private static TextColor of(ChatFormatting formatColor) { @@ -98,14 +100,16 @@ private static TextColor of(String formatColor) { return TextColor.parseColor(formatColor).getOrThrow(); } + private static TextColor of(int rgb) { + return TextColor.fromRgb(rgb); + } + private static TextColor of(int red, int green, int blue) { - return TextColor.fromRgb(toRGB(red, green, blue)); + return of(toRGB(red, green, blue)); } static int toRGB(int red, int green, int blue) { - return ((red & 0xFF) << 16) | - ((green & 0xFF) << 8) | - ((blue & 0xFF)); + return FastColor.ARGB32.color(red, green, blue); } public static TextColor lerp(float scale, TextColor start, TextColor end) { @@ -116,9 +120,9 @@ public static TextColor lerp(float scale, TextColor start, TextColor end) { var endGreen = getGreen(end.getValue()); var endBlue = getBlue(end.getValue()); - var red = (int)Mth.lerp(scale, startRed, endRed); - var green = (int)Mth.lerp(scale, startGreen, endGreen); - var blue = (int)Mth.lerp(scale, startBlue, endBlue); + var red = Mth.lerpInt(scale, startRed, endRed); + var green = Mth.lerpInt(scale, startGreen, endGreen); + var blue = Mth.lerpInt(scale, startBlue, endBlue); return of(red, green, blue); }