From b63b1450938d79e4208bb9e6910bbb8edb5ff597 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Tue, 16 Dec 2025 13:37:29 +0100 Subject: [PATCH 01/15] start to 1.21.11 migration --- build.gradle | 2 +- gradle.properties | 9 +++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/java/com/goby56/wakes/WakesClient.java | 8 ++++---- .../com/goby56/wakes/config/gui/ColorPicker.java | 10 +++++----- .../goby56/wakes/config/gui/ColorPickerScreen.java | 6 +++--- .../com/goby56/wakes/config/gui/SliderHandle.java | 8 ++++---- .../goby56/wakes/config/gui/TexturedButton.java | 14 +++++++------- .../com/goby56/wakes/debug/WakeDebugRenderer.java | 4 ++-- .../com/goby56/wakes/debug/WakesDebugInfo.java | 4 ++-- .../wakes/mixin/LightmapTextureManagerMixin.java | 6 +++--- .../com/goby56/wakes/particle/ModParticles.java | 6 +++--- .../wakes/particle/custom/SplashPlaneParticle.java | 4 ++-- .../goby56/wakes/render/SplashPlaneRenderer.java | 4 ++-- .../java/com/goby56/wakes/render/WakeRenderer.java | 8 ++++---- .../java/com/goby56/wakes/render/WakeTexture.java | 5 +++-- .../com/goby56/wakes/simulation/WakeHandler.java | 6 +++--- .../java/com/goby56/wakes/simulation/WakeNode.java | 2 +- .../java/com/goby56/wakes/utils/WakesUtils.java | 2 +- src/main/resources/wakes.accesswidener | 13 +++++++------ 20 files changed, 63 insertions(+), 60 deletions(-) diff --git a/build.gradle b/build.gradle index 00bb9ac..babd4b9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.13-SNAPSHOT' + id 'fabric-loom' version "${loom_version}" id 'maven-publish' id 'me.fallenbreath.yamlang' version '1.4.1' } diff --git a/gradle.properties b/gradle.properties index 2b88620..78184b6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,14 +4,15 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.10 -loader_version=0.17.3 +minecraft_version=1.21.11 +loader_version=0.18.2 +loom_version=1.14-SNAPSHOT # Fabric API -fabric_version=0.138.3+1.21.10 +fabric_version=0.139.5+1.21.11 # Mod Properties -mod_version=0.4.4+1.21.10 +mod_version=0.4.4+1.21.11 maven_group=com.goby56.wakes archives_base_name=wakes diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3c44eb1..20413ca 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/com/goby56/wakes/WakesClient.java b/src/main/java/com/goby56/wakes/WakesClient.java index 297eff1..1f2652a 100644 --- a/src/main/java/com/goby56/wakes/WakesClient.java +++ b/src/main/java/com/goby56/wakes/WakesClient.java @@ -23,7 +23,7 @@ import net.irisshaders.iris.api.v0.IrisApi; import net.minecraft.client.gui.components.debug.DebugScreenEntries; import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,8 +35,8 @@ public class WakesClient implements ClientModInitializer { public static boolean areShadersEnabled = false; public static final RenderPipeline GUI_HSV_PIPELINE = RenderPipelines.register( RenderPipeline.builder(RenderPipelines.GUI_TEXTURED_SNIPPET) - .withLocation(ResourceLocation.fromNamespaceAndPath("wakes", "pipeline/gui_hsv")) - .withFragmentShader(ResourceLocation.fromNamespaceAndPath("wakes", "gui_hsv")) + .withLocation(Identifier.fromNamespaceAndPath("wakes", "pipeline/gui_hsv")) + .withFragmentShader(Identifier.fromNamespaceAndPath("wakes", "gui_hsv")) .build() ); public static final RenderPipeline SPLASH_PLANE_PIPELINE = RenderPipelines.register( @@ -71,7 +71,7 @@ public void onInitializeClient() { SplashPlaneRenderer.initSplashPlane(); DebugScreenEntries.register( - ResourceLocation.fromNamespaceAndPath("wakes", "debug_entry"), + Identifier.fromNamespaceAndPath("wakes", "debug_entry"), new WakesDebugInfo()); } diff --git a/src/main/java/com/goby56/wakes/config/gui/ColorPicker.java b/src/main/java/com/goby56/wakes/config/gui/ColorPicker.java index 5c7bd89..ec878c2 100644 --- a/src/main/java/com/goby56/wakes/config/gui/ColorPicker.java +++ b/src/main/java/com/goby56/wakes/config/gui/ColorPicker.java @@ -14,7 +14,7 @@ import net.minecraft.client.gui.components.AbstractSliderButton; import net.minecraft.client.gui.components.EditBox; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import org.joml.Matrix3x2f; import org.joml.Vector2f; @@ -26,9 +26,9 @@ import java.util.regex.Pattern; public class ColorPicker extends AbstractWidget { - private static final ResourceLocation FRAME_TEXTURE = ResourceLocation.withDefaultNamespace("textures/gui/sprites/widget/slot_frame.png"); - private static final ResourceLocation PICKER_BG_TEXTURE = ResourceLocation.fromNamespaceAndPath(WakesClient.MOD_ID, "textures/picker_background.png"); - private static final ResourceLocation PICKER_KNOB_TEXTURE = ResourceLocation.fromNamespaceAndPath(WakesClient.MOD_ID, "textures/picker_knob.png"); + private static final Identifier FRAME_TEXTURE = Identifier.withDefaultNamespace("textures/gui/sprites/widget/slot_frame.png"); + private static final Identifier PICKER_BG_TEXTURE = Identifier.fromNamespaceAndPath(WakesClient.MOD_ID, "textures/picker_background.png"); + private static final Identifier PICKER_KNOB_TEXTURE = Identifier.fromNamespaceAndPath(WakesClient.MOD_ID, "textures/picker_knob.png"); private static final int pickerKnobDim = 7; private final Map widgets = new HashMap<>(); @@ -355,7 +355,7 @@ public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delt } context.blitSprite(RenderPipelines.GUI_TEXTURED, this.getHandleSprite(), this.getX() + (int)(this.value * (double)(this.width - 8)), this.getY(), 8, this.getHeight()); int i = this.active ? 0xFFFFFF : 0xA0A0A0; - this.renderScrollingString(context, Minecraft.getInstance().font, 2, i | Mth.ceil(this.alpha * 255.0f) << 24); + this.renderScrollingStringOverContents(context.textRenderer(), this.message, i | Mth.ceil(this.alpha * 255.0f) << 24); } @Override diff --git a/src/main/java/com/goby56/wakes/config/gui/ColorPickerScreen.java b/src/main/java/com/goby56/wakes/config/gui/ColorPickerScreen.java index 48d3acd..2eaf957 100644 --- a/src/main/java/com/goby56/wakes/config/gui/ColorPickerScreen.java +++ b/src/main/java/com/goby56/wakes/config/gui/ColorPickerScreen.java @@ -12,7 +12,7 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; public class ColorPickerScreen extends Screen { private final Screen parent; @@ -20,8 +20,8 @@ public class ColorPickerScreen extends Screen { private ColorIntervalSlider colorIntervalSlider; private WakeAffectingSlider wakeOpacitySlider; private WakeAffectingSlider blendStrengthSlider; - private static final ResourceLocation INFO_ICON_TEXTURE = ResourceLocation.fromNamespaceAndPath("minecraft", "textures/gui/sprites/icon/info.png"); - private static final ResourceLocation RESET_ICON_TEXTURE = ResourceLocation.fromNamespaceAndPath(WakesClient.MOD_ID, "textures/reset_icon.png"); + private static final Identifier INFO_ICON_TEXTURE = Identifier.fromNamespaceAndPath("minecraft", "textures/gui/sprites/icon/info.png"); + private static final Identifier RESET_ICON_TEXTURE = Identifier.fromNamespaceAndPath(WakesClient.MOD_ID, "textures/reset_icon.png"); public ColorPickerScreen(Screen parent) { super(Component.nullToEmpty("Configure wake colors")); this.parent = parent; diff --git a/src/main/java/com/goby56/wakes/config/gui/SliderHandle.java b/src/main/java/com/goby56/wakes/config/gui/SliderHandle.java index c6dc1b1..a8bb6b1 100644 --- a/src/main/java/com/goby56/wakes/config/gui/SliderHandle.java +++ b/src/main/java/com/goby56/wakes/config/gui/SliderHandle.java @@ -1,14 +1,14 @@ package com.goby56.wakes.config.gui; import com.goby56.wakes.render.WakeColor; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import org.jetbrains.annotations.NotNull; public class SliderHandle implements Comparable { - private static final ResourceLocation HANDLE_TEXTURE = ResourceLocation.withDefaultNamespace("widget/slider_handle"); - private static final ResourceLocation HANDLE_HIGHLIGHTED_TEXTURE = ResourceLocation.withDefaultNamespace("widget/slider_handle_highlighted"); + private static final Identifier HANDLE_TEXTURE = Identifier.withDefaultNamespace("widget/slider_handle"); + private static final Identifier HANDLE_HIGHLIGHTED_TEXTURE = Identifier.withDefaultNamespace("widget/slider_handle_highlighted"); protected WakeColor color; protected float value; @@ -31,7 +31,7 @@ public boolean inProximity(float value, int sliderWidth, int handleWidth) { return (Math.abs(value - this.value) < (float) handleWidth / (2 * sliderWidth)); } - public ResourceLocation getHandleTexture(boolean isHovered) { + public Identifier getHandleTexture(boolean isHovered) { if (focused || isHovered) { return HANDLE_HIGHLIGHTED_TEXTURE; } diff --git a/src/main/java/com/goby56/wakes/config/gui/TexturedButton.java b/src/main/java/com/goby56/wakes/config/gui/TexturedButton.java index b1c19ad..34bb908 100644 --- a/src/main/java/com/goby56/wakes/config/gui/TexturedButton.java +++ b/src/main/java/com/goby56/wakes/config/gui/TexturedButton.java @@ -4,13 +4,13 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; public class TexturedButton extends Button { - private final ResourceLocation texture; + private final Identifier texture; private final int textureWidth; private final int textureHeight; - protected TexturedButton(int width, int height, OnPress onPress, ResourceLocation texture, int texWidth, int texHeight) { + protected TexturedButton(int width, int height, OnPress onPress, Identifier texture, int texWidth, int texHeight) { super(0, 0, width, height, Component.empty(), onPress, DEFAULT_NARRATION); this.texture = texture; this.textureWidth = texWidth; @@ -22,20 +22,20 @@ public static com.goby56.wakes.config.gui.TexturedButton.Builder builder(Button. } @Override - protected void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delta) { - super.renderWidget(context, mouseX, mouseY, delta); + protected void renderContents(GuiGraphics context, int mouseX, int mouseY, float delta) { int tw = this.textureWidth; int th = this.textureHeight; int x = this.getX() + this.getWidth() / 2 - this.textureWidth / 2; int y = this.getY() + this.getHeight() / 2 - this.textureHeight / 2; context.blit(RenderPipelines.GUI_TEXTURED, this.texture, x, y, 0, 0, tw, th, tw, th); + } public static class Builder { private final Button.OnPress onPress; private int width = 30; private int height = 30; - private ResourceLocation texture; + private Identifier texture; private int textureWidth = 20; private int textureHeight = 20; @@ -49,7 +49,7 @@ public com.goby56.wakes.config.gui.TexturedButton.Builder dimension(int width, i return this; } - public com.goby56.wakes.config.gui.TexturedButton.Builder texture(ResourceLocation texture, int width, int height) { + public com.goby56.wakes.config.gui.TexturedButton.Builder texture(Identifier texture, int width, int height) { this.texture = texture; this.textureWidth = width; this.textureHeight = height; diff --git a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java index 269c2ba..3f54c31 100644 --- a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java +++ b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java @@ -24,7 +24,7 @@ public void beforeDebugRender(WorldRenderContext context) { Camera camera = context.gameRenderer().getMainCamera(); for (var node : wakeHandler.getVisible(WakeNode.class)) { DebugRenderer.renderFilledBox(context.matrices(), context.consumers(), - node.toBox().move(camera.getPosition().reverse()), + node.toBox().move(camera.position().reverse()), 1, 0, 1, 0.5f); } for (var brick : wakeHandler.getVisible(Brick.class)) { @@ -32,7 +32,7 @@ public void beforeDebugRender(WorldRenderContext context) { AABB box = new AABB(pos.x, pos.y - (1 - WakeNode.WATER_OFFSET), pos.z, pos.x + brick.dim, pos.y, pos.z + brick.dim); var col = Color.getHSBColor(new Random(pos.hashCode()).nextFloat(), 1f, 1f).getRGBColorComponents(null); DebugRenderer.renderFilledBox(context.matrices(), context.consumers(), - box.move(camera.getPosition().reverse()), + box.move(camera.position().reverse()), col[0], col[1], col[2], 0.5f); } } diff --git a/src/main/java/com/goby56/wakes/debug/WakesDebugInfo.java b/src/main/java/com/goby56/wakes/debug/WakesDebugInfo.java index c64d706..db35636 100644 --- a/src/main/java/com/goby56/wakes/debug/WakesDebugInfo.java +++ b/src/main/java/com/goby56/wakes/debug/WakesDebugInfo.java @@ -3,7 +3,7 @@ import com.goby56.wakes.config.WakesConfig; import net.minecraft.client.gui.components.debug.DebugScreenDisplayer; import net.minecraft.client.gui.components.debug.DebugScreenEntry; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.LevelChunk; import org.jetbrains.annotations.Nullable; @@ -31,7 +31,7 @@ public void display(DebugScreenDisplayer debugScreenDisplayer, @Nullable Level l if (WakesConfig.disableMod) { debugScreenDisplayer.addLine("[Wakes] Mod disabled!"); } else { - debugScreenDisplayer.addToGroup(ResourceLocation.fromNamespaceAndPath("wakes", "debug_category"), + debugScreenDisplayer.addToGroup(Identifier.fromNamespaceAndPath("wakes", "debug_category"), List.of( String.format("[Wakes] Rendering %d quads for %d wake nodes", WakesDebugInfo.quadsRendered, WakesDebugInfo.nodeCount), String.format("[Wakes] Node logic: %.2fms/t", 10e-6 * WakesDebugInfo.nodeLogicTime), diff --git a/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java b/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java index cc46325..566bde0 100644 --- a/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java +++ b/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java @@ -34,9 +34,9 @@ public abstract class LightmapTextureManagerMixin implements LightmapAccess { @Inject(method = "updateLightTexture", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoder;createRenderPass(Ljava/util/function/Supplier;Lcom/mojang/blaze3d/textures/GpuTextureView;Ljava/util/OptionalInt;)Lcom/mojang/blaze3d/systems/RenderPass;")) private void wakes$onUpdate(float tickProgress, CallbackInfo ci, @Local ClientLevel world, @Local(ordinal = 1) Vector3f skyColor) { - float f = world.getSkyDarken(1.0F); + float f = world.getSkyDarken(); float g; - if (world.getSkyFlashTime() > 0) { + if (world.skyFlashTime > 0) { g = 1.0F; } else { g = f * 0.95F + 0.05F; @@ -61,7 +61,7 @@ public abstract class LightmapTextureManagerMixin implements LightmapAccess { info = new LightmapInfo(world.dimensionType().ambientLight(), g, this.blockLightRedFlicker + 1.5f, - world.effects().constantAmbientLight(), l, j, this.renderer.getDarkenWorldAmount(tickProgress), Math.max(0.0F, o - i), skyColor, currentTick++); + world.light()AmbientLight(), l, j, this.renderer.getDarkenWorldAmount(tickProgress), Math.max(0.0F, o - i), skyColor, currentTick++); } @Override diff --git a/src/main/java/com/goby56/wakes/particle/ModParticles.java b/src/main/java/com/goby56/wakes/particle/ModParticles.java index 301d709..ff0a30b 100644 --- a/src/main/java/com/goby56/wakes/particle/ModParticles.java +++ b/src/main/java/com/goby56/wakes/particle/ModParticles.java @@ -6,17 +6,17 @@ import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; public class ModParticles { public static WithOwnerParticleType SPLASH_PLANE; public static WithOwnerParticleType SPLASH_CLOUD; public static void registerParticles() { - SPLASH_PLANE = Registry.register(BuiltInRegistries.PARTICLE_TYPE, ResourceLocation.fromNamespaceAndPath(WakesClient.MOD_ID, "splash_plane"), new WithOwnerParticleType(true)); + SPLASH_PLANE = Registry.register(BuiltInRegistries.PARTICLE_TYPE, Identifier.fromNamespaceAndPath(WakesClient.MOD_ID, "splash_plane"), new WithOwnerParticleType(true)); ParticleFactoryRegistry.getInstance().register(SPLASH_PLANE, SplashPlaneParticle.Factory::new); - SPLASH_CLOUD = Registry.register(BuiltInRegistries.PARTICLE_TYPE, ResourceLocation.fromNamespaceAndPath(WakesClient.MOD_ID, "splash_cloud"), new WithOwnerParticleType(true)); + SPLASH_CLOUD = Registry.register(BuiltInRegistries.PARTICLE_TYPE, Identifier.fromNamespaceAndPath(WakesClient.MOD_ID, "splash_cloud"), new WithOwnerParticleType(true)); ParticleFactoryRegistry.getInstance().register(SPLASH_CLOUD, SplashCloudParticle.Factory::new); } } diff --git a/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java b/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java index f3ae7ed..0fac8d4 100644 --- a/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java +++ b/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java @@ -17,8 +17,8 @@ import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.vehicle.AbstractBoat; import net.minecraft.core.particles.SimpleParticleType; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil; @@ -146,7 +146,7 @@ public void updateYaw(float tickDelta) { } public void translateMatrix(Camera camera, PoseStack matrices) { - Vec3 cameraPos = camera.getPosition(); + Vec3 cameraPos = camera.position(); float tickDelta = camera.getPartialTickTime(); float x = (float) (Mth.lerp(tickDelta, this.xo, this.x) - cameraPos.x()); float y = (float) (Mth.lerp(tickDelta, this.yo, this.y) - cameraPos.y()); diff --git a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java index facdd86..2adebb3 100644 --- a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java +++ b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java @@ -114,8 +114,8 @@ private static void renderSurface(Matrix4f matrix) { GpuBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.TRIANGLES).getBuffer(built.drawState().indexCount()); try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Splash Plane", Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { pass.setPipeline(WakesClient.SPLASH_PLANE_PIPELINE); - pass.bindSampler("Sampler0", RenderSystem.getShaderTexture(0)); - pass.bindSampler("Sampler2", RenderSystem.getShaderTexture(2)); + // pass.bindSampler("Sampler0", RenderSystem.getShaderTexture(0)); + // pass.bindSampler("Sampler2", RenderSystem.getShaderTexture(2)); RenderSystem.bindDefaultUniforms(pass); pass.setVertexBuffer(0, buffer); pass.setIndexBuffer(indices, RenderSystem.getSequentialBuffer(VertexFormat.Mode.TRIANGLES).type()); diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index 2bcd3ba..23f5c4d 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -40,14 +40,14 @@ public void endMain(WorldRenderContext context) { return; } - context.gameRenderer().lightTexture().turnOnLightLayer(); + // context.gameRenderer().lightTexture().turnOnLightLayer(); if (wakeTextures == null) initTextures(); WakeHandler wakeHandler = WakeHandler.getInstance().orElse(null); if (wakeHandler == null || WakeHandler.resolutionResetScheduled) return; ArrayList bricks = wakeHandler.getVisible(Brick.class); - Vec3 cameraPos = context.gameRenderer().getMainCamera().getPosition(); + Vec3 cameraPos = context.gameRenderer().getMainCamera().position(); PoseStack matrices = context.matrices(); matrices.pushPose(); matrices.translate(cameraPos.reverse()); @@ -100,8 +100,8 @@ private void render(Matrix4f matrix, Brick brick, WakeTexture texture) { GpuBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).getBuffer(built.drawState().indexCount()); try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Wake", Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { pass.setPipeline(RenderPipelines.TRANSLUCENT_MOVING_BLOCK); - pass.bindSampler("Sampler0", RenderSystem.getShaderTexture(0)); - pass.bindSampler("Sampler2", RenderSystem.getShaderTexture(2)); + // pass.bindSampler("Sampler0", RenderSystem.getShaderTexture(0)); + // pass.bindSampler("Sampler2", RenderSystem.getShaderTexture(2)); RenderSystem.bindDefaultUniforms(pass); pass.setVertexBuffer(0, buffer); diff --git a/src/main/java/com/goby56/wakes/render/WakeTexture.java b/src/main/java/com/goby56/wakes/render/WakeTexture.java index 6a40426..39d2b35 100644 --- a/src/main/java/com/goby56/wakes/render/WakeTexture.java +++ b/src/main/java/com/goby56/wakes/render/WakeTexture.java @@ -25,7 +25,7 @@ public WakeTexture(int res, boolean useBricks) { this.texture = RenderSystem.getDevice().createTexture(() -> "Wake Texture", GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING, TextureFormat.RGBA8, resolutionScaling * res, resolutionScaling * res, 1, 1); - texture.setTextureFilter(FilterMode.NEAREST, false); + // texture.setTextureFilter(FilterMode.NEAREST, false); this.textureView = RenderSystem.getDevice().createTextureView(texture); } @@ -43,7 +43,8 @@ public void loadTexture(long imgPtr, int glFormat) { //RenderSystem.setShader(ShaderProgramKeys.POSITION_TEX_COLOR); //RenderSystem.enableDepthTest(); // Is it THIS simple? https://github.com/Goby56/wakes/issues/46 //RenderSystem.disableCull(); - RenderSystem.setShaderTexture(0, textureView); + RenderSystem.outputColorTextureOverride = textureView; + // RenderSystem.setShaderTexture(0, textureView); } public GpuTexture getTexture() { diff --git a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java index c0a2d8d..0911b57 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java @@ -15,11 +15,11 @@ public class WakeHandler { private static WakeHandler INSTANCE; public Level world; - private QuadTree[] trees; - private QueueSet[] toBeInserted; + private final QuadTree[] trees; + private final QueueSet[] toBeInserted; private final int minY; private final int maxY; - private ArrayList splashPlanes; + private final ArrayList splashPlanes; public static Resolution resolution = WakesConfig.wakeResolution; diff --git a/src/main/java/com/goby56/wakes/simulation/WakeNode.java b/src/main/java/com/goby56/wakes/simulation/WakeNode.java index 0cb5d0b..8fb0f84 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeNode.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeNode.java @@ -3,7 +3,7 @@ import com.goby56.wakes.config.WakesConfig; import com.goby56.wakes.utils.WakesUtils; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.vehicle.AbstractBoat; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/goby56/wakes/utils/WakesUtils.java b/src/main/java/com/goby56/wakes/utils/WakesUtils.java index d975d3b..2f7dca8 100644 --- a/src/main/java/com/goby56/wakes/utils/WakesUtils.java +++ b/src/main/java/com/goby56/wakes/utils/WakesUtils.java @@ -18,7 +18,7 @@ import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.vehicle.AbstractBoat; +import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.level.material.FluidState; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Component; diff --git a/src/main/resources/wakes.accesswidener b/src/main/resources/wakes.accesswidener index aed4318..a3ec847 100644 --- a/src/main/resources/wakes.accesswidener +++ b/src/main/resources/wakes.accesswidener @@ -1,13 +1,14 @@ accessWidener v1 named -accessible field net/minecraft/world/entity/vehicle/AbstractBoat paddlePositions [F - -accessible field net/minecraft/world/entity/vehicle/AbstractBoat PADDLE_SPEED F +accessible field net/minecraft/world/entity/vehicle/boat/AbstractBoat PADDLE_SPEED F +accessible field net/minecraft/world/entity/vehicle/boat/AbstractBoat paddlePositions [F accessible field net/minecraft/client/model/geom/ModelPart$Cube polygons [Lnet/minecraft/client/model/geom/ModelPart$Polygon; - -accessible method net/minecraft/client/gui/components/AbstractSliderButton getSprite ()Lnet/minecraft/resources/ResourceLocation; -accessible method net/minecraft/client/gui/components/AbstractSliderButton getHandleSprite ()Lnet/minecraft/resources/ResourceLocation; +accessible method net/minecraft/client/gui/components/AbstractSliderButton getSprite ()Lnet/minecraft/resources/Identifier; +accessible method net/minecraft/client/gui/components/AbstractSliderButton getHandleSprite ()Lnet/minecraft/resources/Identifier; accessible method net/minecraft/client/gui/components/AbstractWidget onDrag (Lnet/minecraft/client/input/MouseButtonEvent;DD)V extendable method net/minecraft/client/gui/components/EditBox onValueChange (Ljava/lang/String;)V + +accessible field net/minecraft/client/multiplayer/ClientLevel skyFlashTime I + From 184ac3663ffe6c4ebd99d459ee42bf4677c20aa2 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Tue, 16 Dec 2025 14:17:54 +0100 Subject: [PATCH 02/15] why so large changes every time? --- gradle.properties | 6 ++--- .../java/com/goby56/wakes/WakesClient.java | 1 - .../goby56/wakes/debug/WakeDebugRenderer.java | 22 ++++++++----------- .../goby56/wakes/event/WakeWorldTicker.java | 2 ++ .../mixin/LightmapTextureManagerMixin.java | 5 +++-- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/gradle.properties b/gradle.properties index 78184b6..83bb576 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,6 @@ maven_group=com.goby56.wakes archives_base_name=wakes # Dependencies -midnightlib_version=1.8.3+1.21.9-fabric -iris_version=1.9.6+1.21.10-fabric -modmenu_version=16.0.0-rc.1 +midnightlib_version=1.9.2+1.21.11-fabric +iris_version=1.10.2+1.21.11-fabric +modmenu_version=17.0.0-alpha.1 diff --git a/src/main/java/com/goby56/wakes/WakesClient.java b/src/main/java/com/goby56/wakes/WakesClient.java index 1f2652a..97a88f0 100644 --- a/src/main/java/com/goby56/wakes/WakesClient.java +++ b/src/main/java/com/goby56/wakes/WakesClient.java @@ -67,7 +67,6 @@ public void onInitializeClient() { // Rendering events WorldRenderEvents.END_MAIN.register(new WakeRenderer()); WorldRenderEvents.END_MAIN.register(new SplashPlaneRenderer()); - WorldRenderEvents.BEFORE_DEBUG_RENDER.register(new WakeDebugRenderer()); SplashPlaneRenderer.initSplashPlane(); DebugScreenEntries.register( diff --git a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java index 3f54c31..bd181b9 100644 --- a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java +++ b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java @@ -1,6 +1,7 @@ package com.goby56.wakes.debug; import com.goby56.wakes.config.WakesConfig; +import com.goby56.wakes.render.WakeColor; import com.goby56.wakes.simulation.Brick; import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.simulation.WakeNode; @@ -8,34 +9,29 @@ import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; import net.minecraft.client.Camera; import net.minecraft.client.renderer.debug.DebugRenderer; +import net.minecraft.gizmos.GizmoStyle; +import net.minecraft.gizmos.Gizmos; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.Random; -public class WakeDebugRenderer implements WorldRenderEvents.DebugRender { - - @Override - public void beforeDebugRender(WorldRenderContext context) { +public class WakeDebugRenderer { + public static void addDebugGizmos() { WakeHandler wakeHandler = WakeHandler.getInstance().orElse(null); if (wakeHandler == null) return; + int color = new WakeColor(255, 0, 255, 128).argb; if (WakesConfig.drawDebugBoxes) { - Camera camera = context.gameRenderer().getMainCamera(); for (var node : wakeHandler.getVisible(WakeNode.class)) { - DebugRenderer.renderFilledBox(context.matrices(), context.consumers(), - node.toBox().move(camera.position().reverse()), - 1, 0, 1, 0.5f); + Gizmos.cuboid(node.toBox(), GizmoStyle.fill(color)); } for (var brick : wakeHandler.getVisible(Brick.class)) { Vec3 pos = brick.pos; AABB box = new AABB(pos.x, pos.y - (1 - WakeNode.WATER_OFFSET), pos.z, pos.x + brick.dim, pos.y, pos.z + brick.dim); - var col = Color.getHSBColor(new Random(pos.hashCode()).nextFloat(), 1f, 1f).getRGBColorComponents(null); - DebugRenderer.renderFilledBox(context.matrices(), context.consumers(), - box.move(camera.position().reverse()), - col[0], col[1], col[2], 0.5f); + var col = Color.getHSBColor(new Random(pos.hashCode()).nextFloat(), 1f, 1f).getRGB(); + Gizmos.cuboid(box, GizmoStyle.fill(col)); } } - } } diff --git a/src/main/java/com/goby56/wakes/event/WakeWorldTicker.java b/src/main/java/com/goby56/wakes/event/WakeWorldTicker.java index f9c4d73..e02e54f 100644 --- a/src/main/java/com/goby56/wakes/event/WakeWorldTicker.java +++ b/src/main/java/com/goby56/wakes/event/WakeWorldTicker.java @@ -1,6 +1,7 @@ package com.goby56.wakes.event; import com.goby56.wakes.WakesClient; +import com.goby56.wakes.debug.WakeDebugRenderer; import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.debug.WakesDebugInfo; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; @@ -16,6 +17,7 @@ public void onEndTick(ClientLevel world) { WakesClient.areShadersEnabled = WakesClient.areShadersEnabled(); WakesDebugInfo.reset(); WakeHandler.getInstance().ifPresent(WakeHandler::tick); + WakeDebugRenderer.addDebugGizmos(); } @Override diff --git a/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java b/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java index 566bde0..e24c7a3 100644 --- a/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java +++ b/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java @@ -33,7 +33,8 @@ public abstract class LightmapTextureManagerMixin implements LightmapAccess { private LightmapInfo info; @Inject(method = "updateLightTexture", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoder;createRenderPass(Ljava/util/function/Supplier;Lcom/mojang/blaze3d/textures/GpuTextureView;Ljava/util/OptionalInt;)Lcom/mojang/blaze3d/systems/RenderPass;")) - private void wakes$onUpdate(float tickProgress, CallbackInfo ci, @Local ClientLevel world, @Local(ordinal = 1) Vector3f skyColor) { + private void wakes$onUpdate(float tickProgress, CallbackInfo ci, @Local Vector3f skyColor) { + ClientLevel world = this.minecraft.level; float f = world.getSkyDarken(); float g; if (world.skyFlashTime > 0) { @@ -61,7 +62,7 @@ public abstract class LightmapTextureManagerMixin implements LightmapAccess { info = new LightmapInfo(world.dimensionType().ambientLight(), g, this.blockLightRedFlicker + 1.5f, - world.light()AmbientLight(), l, j, this.renderer.getDarkenWorldAmount(tickProgress), Math.max(0.0F, o - i), skyColor, currentTick++); + false, l, j, this.renderer.getDarkenWorldAmount(tickProgress), Math.max(0.0F, o - i), skyColor, currentTick++); } @Override From 7b0e03a10630f579b7b6f3eb949c943b5b0d3491 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sun, 25 Jan 2026 13:31:32 +0100 Subject: [PATCH 03/15] wakes visible but with incorrect lighting --- .../com/goby56/wakes/render/WakeRenderer.java | 25 ++++++++++++++----- .../assets/wakes/shaders/gui_hsv.fsh | 1 - 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index 23f5c4d..aae64de 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -7,9 +7,12 @@ import com.goby56.wakes.simulation.WakeNode; import com.goby56.wakes.debug.WakesDebugInfo; import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.GpuSampler; import com.mojang.blaze3d.vertex.*; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; @@ -19,6 +22,7 @@ import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Vector3f; +import org.joml.Vector4f; import java.util.*; @@ -46,19 +50,26 @@ public void endMain(WorldRenderContext context) { WakeHandler wakeHandler = WakeHandler.getInstance().orElse(null); if (wakeHandler == null || WakeHandler.resolutionResetScheduled) return; ArrayList bricks = wakeHandler.getVisible(Brick.class); - - Vec3 cameraPos = context.gameRenderer().getMainCamera().position(); + Vec3 cameraPos = context.worldState().cameraRenderState.pos; PoseStack matrices = context.matrices(); matrices.pushPose(); matrices.translate(cameraPos.reverse()); Matrix4f matrix = matrices.last().pose(); + // Matrix4f matrix = new Matrix4f(); + + GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( + RenderSystem.getModelViewMatrix(), + new Vector4f(1,1,1,1), + new Vector3f(), + new Matrix4f() + ); Resolution resolution = WakeHandler.resolution; int n = 0; long tRendering = System.nanoTime(); for (var brick : bricks) { - render(matrix, brick, wakeTextures.get(resolution)); + render(matrix, brick, wakeTextures.get(resolution), dynamicUniforms); n++; } WakesDebugInfo.renderingTime.add(System.nanoTime() - tRendering); @@ -67,7 +78,7 @@ public void endMain(WorldRenderContext context) { matrices.popPose(); } - private void render(Matrix4f matrix, Brick brick, WakeTexture texture) { + private void render(Matrix4f matrix, Brick brick, WakeTexture texture, GpuBufferSlice dynamicUniforms) { if (!brick.hasPopulatedPixels) return; texture.loadTexture(brick.imgPtr, GlConst.GL_RGBA); @@ -98,11 +109,13 @@ private void render(Matrix4f matrix, Brick brick, WakeTexture texture) { GpuBuffer buffer = DefaultVertexFormat.BLOCK.uploadImmediateVertexBuffer(built.vertexBuffer()); GpuBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).getBuffer(built.drawState().indexCount()); + GpuSampler sampler = RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST); try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Wake", Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { pass.setPipeline(RenderPipelines.TRANSLUCENT_MOVING_BLOCK); - // pass.bindSampler("Sampler0", RenderSystem.getShaderTexture(0)); - // pass.bindSampler("Sampler2", RenderSystem.getShaderTexture(2)); + pass.bindTexture("Sampler0", texture.getTextureView(), sampler); + pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); RenderSystem.bindDefaultUniforms(pass); + pass.setUniform("DynamicTransforms", dynamicUniforms); pass.setVertexBuffer(0, buffer); pass.setIndexBuffer(indices, RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).type()); diff --git a/src/main/resources/assets/wakes/shaders/gui_hsv.fsh b/src/main/resources/assets/wakes/shaders/gui_hsv.fsh index 751f551..3fe9af0 100644 --- a/src/main/resources/assets/wakes/shaders/gui_hsv.fsh +++ b/src/main/resources/assets/wakes/shaders/gui_hsv.fsh @@ -7,7 +7,6 @@ layout(std140) uniform DynamicTransforms { vec4 ColorModulator; vec3 ModelOffset; mat4 TextureMat; - float LineWidth; }; uniform sampler2D Sampler0; From c615091689c2d283f4d2ecd6573d6d171d03ba4f Mon Sep 17 00:00:00 2001 From: Goby56 Date: Mon, 26 Jan 2026 20:40:37 +0100 Subject: [PATCH 04/15] prepared draw (with issues) only one brick texture is being rendered/ all textures in one place (all wakes appear at the same place) --- .../com/goby56/wakes/render/WakeRenderer.java | 113 +++++++++++------- .../com/goby56/wakes/simulation/Brick.java | 7 ++ 2 files changed, 74 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index aae64de..4cedd9e 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -14,8 +14,10 @@ import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuSampler; import com.mojang.blaze3d.vertex.*; +import net.fabricmc.fabric.api.client.rendering.v1.world.WorldExtractionContext; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; +import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderPipelines; @@ -27,63 +29,98 @@ import java.util.*; public class WakeRenderer implements WorldRenderEvents.EndMain { - public static Map wakeTextures = null; - private void initTextures() { - wakeTextures = Map.of( - Resolution.EIGHT, new WakeTexture(Resolution.EIGHT.res, true), - Resolution.SIXTEEN, new WakeTexture(Resolution.SIXTEEN.res, true), - Resolution.THIRTYTWO, new WakeTexture(Resolution.THIRTYTWO.res, true) - ); + private record PreparedDraw(GpuBuffer vbo, GpuBuffer ibo, int indexCount, VertexFormat.IndexType indexType, WakeTexture texture) { + } @Override public void endMain(WorldRenderContext context) { + // ===== Prepare ===== if (WakesConfig.disableMod) { WakesDebugInfo.quadsRendered = 0; return; } - // context.gameRenderer().lightTexture().turnOnLightLayer(); - if (wakeTextures == null) initTextures(); - WakeHandler wakeHandler = WakeHandler.getInstance().orElse(null); if (wakeHandler == null || WakeHandler.resolutionResetScheduled) return; + ArrayList bricks = wakeHandler.getVisible(Brick.class); + Vec3 cameraPos = context.worldState().cameraRenderState.pos; - PoseStack matrices = context.matrices(); - matrices.pushPose(); - matrices.translate(cameraPos.reverse()); + // PoseStack matrices = context.matrices(); + // matrices.pushPose(); + // matrices.translate(cameraPos.reverse()); + // Matrix4f matrix = matrices.last().pose(); + + VertexFormat vertexFormat = RenderPipelines.TRANSLUCENT_MOVING_BLOCK.getVertexFormat(); + + ArrayList preparedDraws = new ArrayList<>(); + for (Brick brick : bricks) { + if (!brick.hasPopulatedPixels) continue; + + MeshData mesh = createBrickMesh(cameraPos.toVector3f(), brick); + GpuBuffer buffer = vertexFormat.uploadImmediateVertexBuffer(mesh.vertexBuffer()); + GpuBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).getBuffer(mesh.drawState().indexCount()); + //GpuBuffer indices = vertexFormat.uploadImmediateIndexBuffer(mesh.indexBuffer()); - Matrix4f matrix = matrices.last().pose(); - // Matrix4f matrix = new Matrix4f(); + int indexCount = mesh.drawState().indexCount(); + VertexFormat.IndexType indexType = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).type(); + //VertexFormat.IndexType indexType = mesh.drawState().indexType(); + + brick.wakeTexture.loadTexture(brick.imgPtr, GlConst.GL_RGBA); + + preparedDraws.add(new PreparedDraw(buffer, indices, indexCount, indexType, brick.wakeTexture)); + + mesh.close(); + } + //matrices.popPose(); + + // ===== Draw ===== GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( RenderSystem.getModelViewMatrix(), new Vector4f(1,1,1,1), new Vector3f(), new Matrix4f() ); + GpuSampler sampler = RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST); - Resolution resolution = WakeHandler.resolution; - int n = 0; - long tRendering = System.nanoTime(); - for (var brick : bricks) { - render(matrix, brick, wakeTextures.get(resolution), dynamicUniforms); - n++; - } - WakesDebugInfo.renderingTime.add(System.nanoTime() - tRendering); - WakesDebugInfo.quadsRendered = n; + try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( + () -> "Wake", Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), + OptionalInt.empty(), + Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), + OptionalDouble.empty())) + { + pass.setPipeline(RenderPipelines.TRANSLUCENT_MOVING_BLOCK); + pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); + RenderSystem.bindDefaultUniforms(pass); + pass.setUniform("DynamicTransforms", dynamicUniforms); - matrices.popPose(); + int n = 0; + long tRendering = System.nanoTime(); + for (PreparedDraw draw : preparedDraws) { + pass.bindTexture("Sampler0", draw.texture.getTextureView(), sampler); + + pass.setVertexBuffer(0, draw.vbo); + pass.setIndexBuffer(draw.ibo, draw.indexType); + pass.drawIndexed(0, 0, draw.indexCount, 1); + + n++; + } + WakesDebugInfo.renderingTime.add(System.nanoTime() - tRendering); + WakesDebugInfo.quadsRendered = n; + } } - private void render(Matrix4f matrix, Brick brick, WakeTexture texture, GpuBufferSlice dynamicUniforms) { - if (!brick.hasPopulatedPixels) return; - texture.loadTexture(brick.imgPtr, GlConst.GL_RGBA); + private MeshData createBrickMesh(Vector3f cameraPos, Brick brick) { + Vector3f pos = new Vector3f( + (float) (brick.pos.x - cameraPos.x), + (float) (brick.pos.y - cameraPos.y + WakeNode.WATER_OFFSET), + (float) (brick.pos.z - cameraPos.z)); + Matrix4f matrix = new Matrix4f(); BufferBuilder bb = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - Vector3f pos = brick.pos.toVector3f().add(0, WakeNode.WATER_OFFSET, 0); bb.addVertex(matrix, pos.x, pos.y, pos.z) .setUv(0, 0) .setColor(1f, 1f, 1f, 1f) @@ -105,22 +142,6 @@ private void render(Matrix4f matrix, Brick brick, WakeTexture texture, GpuBuffer .setLight(LightTexture.FULL_BRIGHT) .setNormal(0f, 1f, 0f); - MeshData built = bb.buildOrThrow(); - - GpuBuffer buffer = DefaultVertexFormat.BLOCK.uploadImmediateVertexBuffer(built.vertexBuffer()); - GpuBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).getBuffer(built.drawState().indexCount()); - GpuSampler sampler = RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST); - try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Wake", Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { - pass.setPipeline(RenderPipelines.TRANSLUCENT_MOVING_BLOCK); - pass.bindTexture("Sampler0", texture.getTextureView(), sampler); - pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); - RenderSystem.bindDefaultUniforms(pass); - pass.setUniform("DynamicTransforms", dynamicUniforms); - - pass.setVertexBuffer(0, buffer); - pass.setIndexBuffer(indices, RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).type()); - pass.drawIndexed(0, 0, built.drawState().indexCount(), 1); - } - built.close(); + return bb.buildOrThrow(); } } diff --git a/src/main/java/com/goby56/wakes/simulation/Brick.java b/src/main/java/com/goby56/wakes/simulation/Brick.java index 1344670..8b26f00 100644 --- a/src/main/java/com/goby56/wakes/simulation/Brick.java +++ b/src/main/java/com/goby56/wakes/simulation/Brick.java @@ -1,8 +1,10 @@ package com.goby56.wakes.simulation; import com.goby56.wakes.config.WakesConfig; +import com.goby56.wakes.config.enums.Resolution; import com.goby56.wakes.debug.WakesDebugInfo; import com.goby56.wakes.render.FrustumManager; +import com.goby56.wakes.render.WakeTexture; import com.goby56.wakes.utils.WakesUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BiomeColors; @@ -14,6 +16,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Stream; @@ -35,6 +38,8 @@ public class Brick { public int texRes; public boolean hasPopulatedPixels = false; + public WakeTexture wakeTexture; + public Brick(int x, float y, int z, int width) { this.dim = width; this.capacity = dim * dim; @@ -45,6 +50,7 @@ public Brick(int x, float y, int z, int width) { } public void initTexture(int res) { + this.wakeTexture = new WakeTexture(res, true); long size = 4L * dim * dim * res * res; if (imgPtr == -1) { this.imgPtr = MemoryUtil.nmemAlloc(size); @@ -53,6 +59,7 @@ public void initTexture(int res) { } this.texRes = res; this.hasPopulatedPixels = false; + } public void deallocTexture() { From f1c69eace3a956952886cdd0bd7554929a15ebf7 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Wed, 28 Jan 2026 08:06:15 +0100 Subject: [PATCH 05/15] Implemented code from rendering guide on fabric docs https://docs.fabricmc.net/develop/rendering/world Only lightmap textures show at the moment, need to implement texture atlas --- .../java/com/goby56/wakes/WakesClient.java | 5 +- .../wakes/mixin/WakeRendererCleanup.java | 17 ++ .../com/goby56/wakes/render/WakeRenderer.java | 225 +++++++++++------- .../com/goby56/wakes/simulation/Brick.java | 2 - src/main/resources/wakes.mixins.json | 7 +- 5 files changed, 166 insertions(+), 90 deletions(-) create mode 100644 src/main/java/com/goby56/wakes/mixin/WakeRendererCleanup.java diff --git a/src/main/java/com/goby56/wakes/WakesClient.java b/src/main/java/com/goby56/wakes/WakesClient.java index 97a88f0..e75efba 100644 --- a/src/main/java/com/goby56/wakes/WakesClient.java +++ b/src/main/java/com/goby56/wakes/WakesClient.java @@ -49,6 +49,8 @@ public class WakesClient implements ClientModInitializer { .withBlend(BlendFunction.TRANSLUCENT) .build()); + public static WakeRenderer wakeRenderer; + @Override public void onInitializeClient() { FabricLoader.getInstance().getModContainer(MOD_ID).ifPresent(container -> METADATA = container.getMetadata()); @@ -65,7 +67,8 @@ public void onInitializeClient() { ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register(new WakeWorldTicker()); // Rendering events - WorldRenderEvents.END_MAIN.register(new WakeRenderer()); + wakeRenderer = new WakeRenderer(); + WorldRenderEvents.BEFORE_TRANSLUCENT.register(wakeRenderer); WorldRenderEvents.END_MAIN.register(new SplashPlaneRenderer()); SplashPlaneRenderer.initSplashPlane(); diff --git a/src/main/java/com/goby56/wakes/mixin/WakeRendererCleanup.java b/src/main/java/com/goby56/wakes/mixin/WakeRendererCleanup.java new file mode 100644 index 0000000..a6f88f7 --- /dev/null +++ b/src/main/java/com/goby56/wakes/mixin/WakeRendererCleanup.java @@ -0,0 +1,17 @@ +package com.goby56.wakes.mixin; + +import com.goby56.wakes.WakesClient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.renderer.GameRenderer; + +@Mixin(GameRenderer.class) +public class WakeRendererCleanup { + @Inject(method = "close", at = @At("RETURN")) + private void onGameRendererClose(CallbackInfo ci) { + WakesClient.wakeRenderer.close(); + } +} diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index 4cedd9e..302cf55 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -1,42 +1,50 @@ package com.goby56.wakes.render; +import com.goby56.wakes.WakesClient; import com.goby56.wakes.config.WakesConfig; -import com.goby56.wakes.config.enums.Resolution; import com.goby56.wakes.simulation.Brick; import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.simulation.WakeNode; import com.goby56.wakes.debug.WakesDebugInfo; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.opengl.GlConst; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuSampler; import com.mojang.blaze3d.vertex.*; -import net.fabricmc.fabric.api.client.rendering.v1.world.WorldExtractionContext; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; -import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MappableRingBuffer; import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.rendertype.RenderType; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; +import org.joml.Matrix4fc; import org.joml.Vector3f; import org.joml.Vector4f; +import org.lwjgl.system.MemoryUtil; import java.util.*; -public class WakeRenderer implements WorldRenderEvents.EndMain { +public class WakeRenderer implements WorldRenderEvents.BeforeTranslucent { - private record PreparedDraw(GpuBuffer vbo, GpuBuffer ibo, int indexCount, VertexFormat.IndexType indexType, WakeTexture texture) { + private static final ByteBufferBuilder allocator = new ByteBufferBuilder(RenderType.BIG_BUFFER_SIZE); + private BufferBuilder buffer; - } + private static final Vector4f COLOR_MODULATOR = new Vector4f(1f, 1f, 1f, 1f); + private static final Vector3f MODEL_OFFSET = new Vector3f(); + private static final Matrix4f TEXTURE_MATRIX = new Matrix4f(); + private MappableRingBuffer vertexBuffer; + + private final RenderPipeline PIPELINE = RenderPipelines.TRANSLUCENT_MOVING_BLOCK; @Override - public void endMain(WorldRenderContext context) { - // ===== Prepare ===== + public void beforeTranslucent(WorldRenderContext context) { if (WakesConfig.disableMod) { WakesDebugInfo.quadsRendered = 0; return; @@ -47,101 +55,150 @@ public void endMain(WorldRenderContext context) { ArrayList bricks = wakeHandler.getVisible(Brick.class); - Vec3 cameraPos = context.worldState().cameraRenderState.pos; - // PoseStack matrices = context.matrices(); - // matrices.pushPose(); - // matrices.translate(cameraPos.reverse()); - // Matrix4f matrix = matrices.last().pose(); + if (bricks.isEmpty()) return; + + if (buffer == null) { + buffer = new BufferBuilder(allocator, PIPELINE.getVertexFormatMode(), PIPELINE.getVertexFormat()); + } + + PoseStack matrices = context.matrices(); + Vec3 camera = context.worldState().cameraRenderState.pos; + + matrices.pushPose(); + matrices.translate(-camera.x, -camera.y, -camera.z); + + addVertices(matrices.last().pose(), buffer, bricks); + + matrices.popPose(); - VertexFormat vertexFormat = RenderPipelines.TRANSLUCENT_MOVING_BLOCK.getVertexFormat(); + MeshData builtBuffer = buffer.buildOrThrow(); + MeshData.DrawState drawParameters = builtBuffer.drawState(); + VertexFormat format = drawParameters.format(); - ArrayList preparedDraws = new ArrayList<>(); + GpuBuffer vertices = uploadMesh(drawParameters, format, builtBuffer); + + draw(builtBuffer, drawParameters, vertices, format); + + // Rotate the vertex buffer so we are less likely to use buffers that the GPU is using + vertexBuffer.rotate(); + buffer = null; + } + + private void addVertices(Matrix4fc matrix, BufferBuilder bb, List bricks) { for (Brick brick : bricks) { if (!brick.hasPopulatedPixels) continue; - MeshData mesh = createBrickMesh(cameraPos.toVector3f(), brick); - GpuBuffer buffer = vertexFormat.uploadImmediateVertexBuffer(mesh.vertexBuffer()); - GpuBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).getBuffer(mesh.drawState().indexCount()); - //GpuBuffer indices = vertexFormat.uploadImmediateIndexBuffer(mesh.indexBuffer()); + float x0 = (float) brick.pos.x; + float y = (float) (brick.pos.y + WakeNode.WATER_OFFSET); + float z0 = (float) brick.pos.z; + + float x1 = x0 + brick.dim; + float z1 = z0 + brick.dim; + + bb.addVertex(matrix, x0, y, z0) + .setUv(0, 0) + .setColor(1f, 1f, 1f, 1f) + .setLight(LightTexture.FULL_BRIGHT) + .setNormal(0f, 1f, 0f); + bb.addVertex(matrix, x0, y, z1) + .setUv(0, 1) + .setColor(1f, 1f, 1f, 1f) + .setLight(LightTexture.FULL_BRIGHT) + .setNormal(0f, 1f, 0f); + bb.addVertex(matrix, x1, y, z1) + .setUv(1, 1) + .setColor(1f, 1f, 1f, 1f) + .setLight(LightTexture.FULL_BRIGHT) + .setNormal(0f, 1f, 0f); + bb.addVertex(matrix, x1, y, z0) + .setUv(1, 0) + .setColor(1f, 1f, 1f, 1f) + .setLight(LightTexture.FULL_BRIGHT) + .setNormal(0f, 1f, 0f); + } + } + + private GpuBuffer uploadMesh(MeshData.DrawState drawParameters, VertexFormat format, MeshData builtBuffer) { + // Calculate the size needed for the vertex buffer + int vertexBufferSize = drawParameters.vertexCount() * format.getVertexSize(); - int indexCount = mesh.drawState().indexCount(); + // Initialize or resize the vertex buffer as needed + if (vertexBuffer == null || vertexBuffer.size() < vertexBufferSize) { + if (vertexBuffer != null) { + vertexBuffer.close(); + } + + vertexBuffer = new MappableRingBuffer(() -> WakesClient.MOD_ID + " wake ring buffer", GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_MAP_WRITE, vertexBufferSize); + } - VertexFormat.IndexType indexType = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS).type(); - //VertexFormat.IndexType indexType = mesh.drawState().indexType(); + // Copy vertex data into the vertex buffer + CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder(); - brick.wakeTexture.loadTexture(brick.imgPtr, GlConst.GL_RGBA); + try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(vertexBuffer.currentBuffer().slice(0, builtBuffer.vertexBuffer().remaining()), false, true)) { + MemoryUtil.memCopy(builtBuffer.vertexBuffer(), mappedView.data()); + } - preparedDraws.add(new PreparedDraw(buffer, indices, indexCount, indexType, brick.wakeTexture)); + return vertexBuffer.currentBuffer(); + } - mesh.close(); + private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format) { + GpuBuffer indices; + VertexFormat.IndexType indexType; + + if (PIPELINE.getVertexFormatMode() == VertexFormat.Mode.QUADS) { + // Sort the quads if there is translucency + builtBuffer.sortQuads(allocator, RenderSystem.getProjectionType().vertexSorting()); + // Upload the index buffer + indices = PIPELINE.getVertexFormat().uploadImmediateIndexBuffer(builtBuffer.indexBuffer()); + indexType = builtBuffer.drawState().indexType(); + } else { + // Use the general shape index buffer for non-quad draw modes + RenderSystem.AutoStorageIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(PIPELINE.getVertexFormatMode()); + indices = shapeIndexBuffer.getBuffer(drawParameters.indexCount()); + indexType = shapeIndexBuffer.type(); } - //matrices.popPose(); - - // ===== Draw ===== - GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( - RenderSystem.getModelViewMatrix(), - new Vector4f(1,1,1,1), - new Vector3f(), - new Matrix4f() - ); + + // Actually execute the draw + GpuBufferSlice dynamicTransforms = RenderSystem.getDynamicUniforms() + .writeTransform(RenderSystem.getModelViewMatrix(), COLOR_MODULATOR, MODEL_OFFSET, TEXTURE_MATRIX); GpuSampler sampler = RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST); + Minecraft client = Minecraft.getInstance(); + try (RenderPass pass = RenderSystem.getDevice() + .createCommandEncoder() + .createRenderPass( + () -> WakesClient.MOD_ID + " wake render pipeline rendering", + client.getMainRenderTarget().getColorTextureView(), + OptionalInt.empty(), + client.getMainRenderTarget().getDepthTextureView(), + OptionalDouble.empty())) { + long tRendering = System.nanoTime(); - try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( - () -> "Wake", Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), - OptionalInt.empty(), - Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), - OptionalDouble.empty())) - { - pass.setPipeline(RenderPipelines.TRANSLUCENT_MOVING_BLOCK); - pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); + pass.setPipeline(PIPELINE); RenderSystem.bindDefaultUniforms(pass); - pass.setUniform("DynamicTransforms", dynamicUniforms); + pass.setUniform("DynamicTransforms", dynamicTransforms); - int n = 0; - long tRendering = System.nanoTime(); - for (PreparedDraw draw : preparedDraws) { - pass.bindTexture("Sampler0", draw.texture.getTextureView(), sampler); + pass.bindTexture("Sampler0", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); + pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); - pass.setVertexBuffer(0, draw.vbo); - pass.setIndexBuffer(draw.ibo, draw.indexType); - pass.drawIndexed(0, 0, draw.indexCount, 1); + pass.setVertexBuffer(0, vertices); + pass.setIndexBuffer(indices, indexType); + + // The base vertex is the starting index when we copied the data into the vertex buffer divided by vertex size + //noinspection ConstantValue + pass.drawIndexed(0 / format.getVertexSize(), 0, drawParameters.indexCount(), 1); - n++; - } WakesDebugInfo.renderingTime.add(System.nanoTime() - tRendering); - WakesDebugInfo.quadsRendered = n; } + + builtBuffer.close(); } - private MeshData createBrickMesh(Vector3f cameraPos, Brick brick) { - Vector3f pos = new Vector3f( - (float) (brick.pos.x - cameraPos.x), - (float) (brick.pos.y - cameraPos.y + WakeNode.WATER_OFFSET), - (float) (brick.pos.z - cameraPos.z)); - Matrix4f matrix = new Matrix4f(); - - BufferBuilder bb = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - bb.addVertex(matrix, pos.x, pos.y, pos.z) - .setUv(0, 0) - .setColor(1f, 1f, 1f, 1f) - .setLight(LightTexture.FULL_BRIGHT) - .setNormal(0f, 1f, 0f); - bb.addVertex(matrix, pos.x, pos.y, pos.z + brick.dim) - .setUv(0, 1) - .setColor(1f, 1f, 1f, 1f) - .setLight(LightTexture.FULL_BRIGHT) - .setNormal(0f, 1f, 0f); - bb.addVertex(matrix, pos.x + brick.dim, pos.y, pos.z + brick.dim) - .setUv(1, 1) - .setColor(1f, 1f, 1f, 1f) - .setLight(LightTexture.FULL_BRIGHT) - .setNormal(0f, 1f, 0f); - bb.addVertex(matrix, pos.x + brick.dim, pos.y, pos.z) - .setUv(1, 0) - .setColor(1f, 1f, 1f, 1f) - .setLight(LightTexture.FULL_BRIGHT) - .setNormal(0f, 1f, 0f); - - return bb.buildOrThrow(); + public void close() { + allocator.close(); + + if (vertexBuffer != null) { + vertexBuffer.close(); + vertexBuffer = null; + } } } diff --git a/src/main/java/com/goby56/wakes/simulation/Brick.java b/src/main/java/com/goby56/wakes/simulation/Brick.java index 8b26f00..c74d738 100644 --- a/src/main/java/com/goby56/wakes/simulation/Brick.java +++ b/src/main/java/com/goby56/wakes/simulation/Brick.java @@ -1,7 +1,6 @@ package com.goby56.wakes.simulation; import com.goby56.wakes.config.WakesConfig; -import com.goby56.wakes.config.enums.Resolution; import com.goby56.wakes.debug.WakesDebugInfo; import com.goby56.wakes.render.FrustumManager; import com.goby56.wakes.render.WakeTexture; @@ -16,7 +15,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.stream.Stream; diff --git a/src/main/resources/wakes.mixins.json b/src/main/resources/wakes.mixins.json index 6f9242a..1b480b0 100644 --- a/src/main/resources/wakes.mixins.json +++ b/src/main/resources/wakes.mixins.json @@ -3,10 +3,11 @@ "package": "com.goby56.wakes.mixin", "compatibilityLevel": "JAVA_17", "client": [ - "TameableTeleportMixin", - "WakeSpawnerMixin", "LightmapTextureManagerMixin", - "LilyPadFallMixin" + "LilyPadFallMixin", + "TameableTeleportMixin", + "WakeRendererCleanup", + "WakeSpawnerMixin" ], "injectors": { "defaultRequire": 1 From f946870fa754ba8852a321dbd010553abbd33fb4 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Wed, 28 Jan 2026 11:04:35 +0100 Subject: [PATCH 06/15] texture atlases + wake chunks instead of quad tree wake nodes seem to spread alongchunk edges indefinitely and textures are not rendered --- .../goby56/wakes/debug/WakeDebugRenderer.java | 14 +- .../wakes/render/SplashPlaneRenderer.java | 11 +- .../wakes/render/SplashPlaneTexture.java | 38 ++++ .../com/goby56/wakes/render/WakeRenderer.java | 57 ++++-- .../com/goby56/wakes/render/WakeTexture.java | 57 ------ .../goby56/wakes/render/WakeTextureAtlas.java | 70 +++++++ .../com/goby56/wakes/simulation/QuadTree.java | 178 ------------------ .../simulation/{Brick.java => WakeChunk.java} | 95 +++++----- .../goby56/wakes/simulation/WakeChunkPos.java | 7 + .../goby56/wakes/simulation/WakeHandler.java | 122 ++++++------ 10 files changed, 266 insertions(+), 383 deletions(-) create mode 100644 src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java delete mode 100644 src/main/java/com/goby56/wakes/render/WakeTexture.java create mode 100644 src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java delete mode 100644 src/main/java/com/goby56/wakes/simulation/QuadTree.java rename src/main/java/com/goby56/wakes/simulation/{Brick.java => WakeChunk.java} (67%) create mode 100644 src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java diff --git a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java index bd181b9..93e577d 100644 --- a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java +++ b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java @@ -2,13 +2,9 @@ import com.goby56.wakes.config.WakesConfig; import com.goby56.wakes.render.WakeColor; -import com.goby56.wakes.simulation.Brick; +import com.goby56.wakes.simulation.WakeChunk; import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.simulation.WakeNode; -import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; -import net.minecraft.client.Camera; -import net.minecraft.client.renderer.debug.DebugRenderer; import net.minecraft.gizmos.GizmoStyle; import net.minecraft.gizmos.Gizmos; import net.minecraft.world.phys.AABB; @@ -23,12 +19,12 @@ public static void addDebugGizmos() { if (wakeHandler == null) return; int color = new WakeColor(255, 0, 255, 128).argb; if (WakesConfig.drawDebugBoxes) { - for (var node : wakeHandler.getVisible(WakeNode.class)) { + for (var node : wakeHandler.getVisibleNodes()) { Gizmos.cuboid(node.toBox(), GizmoStyle.fill(color)); } - for (var brick : wakeHandler.getVisible(Brick.class)) { - Vec3 pos = brick.pos; - AABB box = new AABB(pos.x, pos.y - (1 - WakeNode.WATER_OFFSET), pos.z, pos.x + brick.dim, pos.y, pos.z + brick.dim); + for (var chunk : wakeHandler.getVisibleChunks()) { + Vec3 pos = chunk.pos; + AABB box = new AABB(pos.x, pos.y - (1 - WakeNode.WATER_OFFSET), pos.z, pos.x + WakeChunk.WIDTH, pos.y, pos.z + WakeChunk.WIDTH); var col = Color.getHSBColor(new Random(pos.hashCode()).nextFloat(), 1f, 1f).getRGB(); Gizmos.cuboid(box, GizmoStyle.fill(col)); } diff --git a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java index 2adebb3..554b6a7 100644 --- a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java +++ b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java @@ -29,19 +29,18 @@ import java.util.*; public class SplashPlaneRenderer implements WorldRenderEvents.EndMain { - private static ArrayList points; private static List triangles; private static ArrayList vertices; private static ArrayList normals; - public static Map wakeTextures = null; + public static Map wakeTextures = null; private static void initTextures() { wakeTextures = Map.of( - Resolution.EIGHT, new WakeTexture(Resolution.EIGHT.res, false), - Resolution.SIXTEEN, new WakeTexture(Resolution.SIXTEEN.res, false), - Resolution.THIRTYTWO, new WakeTexture(Resolution.THIRTYTWO.res, false) + Resolution.EIGHT, new SplashPlaneTexture(Resolution.EIGHT.res), + Resolution.SIXTEEN, new SplashPlaneTexture(Resolution.SIXTEEN.res), + Resolution.THIRTYTWO, new SplashPlaneTexture(Resolution.THIRTYTWO.res) ); } @@ -53,7 +52,7 @@ public void endMain(WorldRenderContext context) { return; } WakeHandler wakeHandler = WakeHandler.getInstance().get(); - for (SplashPlaneParticle particle : wakeHandler.getVisible(SplashPlaneParticle.class)) { + for (SplashPlaneParticle particle : wakeHandler.getVisibleSplashPlanes()) { SplashPlaneRenderer.render(particle.owner, particle, context, context.matrices()); } } diff --git a/src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java b/src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java new file mode 100644 index 0000000..13e39cf --- /dev/null +++ b/src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java @@ -0,0 +1,38 @@ +package com.goby56.wakes.render; + +import com.goby56.wakes.WakesClient; +import com.mojang.blaze3d.opengl.GlConst; +import com.mojang.blaze3d.opengl.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.GpuTexture; +import com.mojang.blaze3d.textures.GpuTextureView; +import com.mojang.blaze3d.textures.TextureFormat; +import com.mojang.blaze3d.opengl.GlTexture; + +public class SplashPlaneTexture { + public GpuTexture texture; + private final GpuTextureView textureView; + public final int resolution; + + public SplashPlaneTexture(int resolution) { + this.resolution = resolution; + + this.texture = RenderSystem.getDevice().createTexture(() -> WakesClient.MOD_ID + " splash plane texture", + GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING | GpuTexture.USAGE_RENDER_ATTACHMENT, + TextureFormat.RGBA8, resolution, resolution, 1, 1); + // texture.setTextureFilter(FilterMode.NEAREST, false); + this.textureView = RenderSystem.getDevice().createTextureView(texture); + } + + public void loadTexture(long imgPtr, int glFormat) { + GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); + + GlStateManager._bindTexture(((GlTexture) texture).glId()); + GlStateManager._texSubImage2D(GlConst.GL_TEXTURE_2D, 0,0,0,resolution, resolution, glFormat, GlConst.GL_UNSIGNED_BYTE, imgPtr); + + RenderSystem.outputColorTextureOverride = textureView; + } +} diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index 302cf55..86788ae 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -2,12 +2,14 @@ import com.goby56.wakes.WakesClient; import com.goby56.wakes.config.WakesConfig; -import com.goby56.wakes.simulation.Brick; +import com.goby56.wakes.config.enums.Resolution; +import com.goby56.wakes.simulation.WakeChunk; import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.simulation.WakeNode; import com.goby56.wakes.debug.WakesDebugInfo; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.RenderPass; @@ -18,6 +20,7 @@ import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; import net.minecraft.client.Minecraft; +import net.minecraft.client.model.geom.builders.UVPair; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MappableRingBuffer; import net.minecraft.client.renderer.RenderPipelines; @@ -43,6 +46,16 @@ public class WakeRenderer implements WorldRenderEvents.BeforeTranslucent { private final RenderPipeline PIPELINE = RenderPipelines.TRANSLUCENT_MOVING_BLOCK; + private Map textureAtlases = null; + + private void initTextures() { + textureAtlases = Map.of( + Resolution.EIGHT, new WakeTextureAtlas(Resolution.EIGHT.res), + Resolution.SIXTEEN, new WakeTextureAtlas(Resolution.SIXTEEN.res), + Resolution.THIRTYTWO, new WakeTextureAtlas(Resolution.THIRTYTWO.res) + ); + } + @Override public void beforeTranslucent(WorldRenderContext context) { if (WakesConfig.disableMod) { @@ -50,12 +63,14 @@ public void beforeTranslucent(WorldRenderContext context) { return; } + if (textureAtlases == null) initTextures(); + WakeHandler wakeHandler = WakeHandler.getInstance().orElse(null); if (wakeHandler == null || WakeHandler.resolutionResetScheduled) return; - ArrayList bricks = wakeHandler.getVisible(Brick.class); + List wakeChunks = wakeHandler.getVisibleChunks(); - if (bricks.isEmpty()) return; + if (wakeChunks.isEmpty()) return; if (buffer == null) { buffer = new BufferBuilder(allocator, PIPELINE.getVertexFormatMode(), PIPELINE.getVertexFormat()); @@ -67,7 +82,8 @@ public void beforeTranslucent(WorldRenderContext context) { matrices.pushPose(); matrices.translate(-camera.x, -camera.y, -camera.z); - addVertices(matrices.last().pose(), buffer, bricks); + WakeTextureAtlas atlas = textureAtlases.get(WakeHandler.resolution); + addVertices(matrices.last().pose(), buffer, wakeChunks, atlas); matrices.popPose(); @@ -77,41 +93,44 @@ public void beforeTranslucent(WorldRenderContext context) { GpuBuffer vertices = uploadMesh(drawParameters, format, builtBuffer); - draw(builtBuffer, drawParameters, vertices, format); + draw(builtBuffer, drawParameters, vertices, format, atlas); // Rotate the vertex buffer so we are less likely to use buffers that the GPU is using vertexBuffer.rotate(); buffer = null; } - private void addVertices(Matrix4fc matrix, BufferBuilder bb, List bricks) { - for (Brick brick : bricks) { - if (!brick.hasPopulatedPixels) continue; + private void addVertices(Matrix4fc matrix, BufferBuilder bb, List chunks, WakeTextureAtlas textureAtlas) { + for (WakeChunk wakeChunk : chunks) { + //if (!wakeChunk.hasPopulatedPixels) continue; + + UVPair uv = textureAtlas.loadTexture(wakeChunk.imgPtr, GlConst.GL_RGBA); + float uvOffset = textureAtlas.chunkResolution / (float) textureAtlas.resolution; - float x0 = (float) brick.pos.x; - float y = (float) (brick.pos.y + WakeNode.WATER_OFFSET); - float z0 = (float) brick.pos.z; + float x0 = (float) wakeChunk.pos.x; + float y = (float) (wakeChunk.pos.y + WakeNode.WATER_OFFSET); + float z0 = (float) wakeChunk.pos.z; - float x1 = x0 + brick.dim; - float z1 = z0 + brick.dim; + float x1 = x0 + WakeChunk.WIDTH; + float z1 = z0 + WakeChunk.WIDTH; bb.addVertex(matrix, x0, y, z0) - .setUv(0, 0) + .setUv(uv.u(), uv.v()) .setColor(1f, 1f, 1f, 1f) .setLight(LightTexture.FULL_BRIGHT) .setNormal(0f, 1f, 0f); bb.addVertex(matrix, x0, y, z1) - .setUv(0, 1) + .setUv(uv.u(), uv.v() + uvOffset) .setColor(1f, 1f, 1f, 1f) .setLight(LightTexture.FULL_BRIGHT) .setNormal(0f, 1f, 0f); bb.addVertex(matrix, x1, y, z1) - .setUv(1, 1) + .setUv(uv.u() + uvOffset, uv.v() + uvOffset) .setColor(1f, 1f, 1f, 1f) .setLight(LightTexture.FULL_BRIGHT) .setNormal(0f, 1f, 0f); bb.addVertex(matrix, x1, y, z0) - .setUv(1, 0) + .setUv(uv.u() + uvOffset, uv.v()) .setColor(1f, 1f, 1f, 1f) .setLight(LightTexture.FULL_BRIGHT) .setNormal(0f, 1f, 0f); @@ -141,7 +160,7 @@ private GpuBuffer uploadMesh(MeshData.DrawState drawParameters, VertexFormat for return vertexBuffer.currentBuffer(); } - private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format) { + private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format, WakeTextureAtlas texture) { GpuBuffer indices; VertexFormat.IndexType indexType; @@ -177,7 +196,7 @@ private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBu RenderSystem.bindDefaultUniforms(pass); pass.setUniform("DynamicTransforms", dynamicTransforms); - pass.bindTexture("Sampler0", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); + pass.bindTexture("Sampler0", texture.getTextureView(), sampler); pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); pass.setVertexBuffer(0, vertices); diff --git a/src/main/java/com/goby56/wakes/render/WakeTexture.java b/src/main/java/com/goby56/wakes/render/WakeTexture.java deleted file mode 100644 index 39d2b35..0000000 --- a/src/main/java/com/goby56/wakes/render/WakeTexture.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.goby56.wakes.render; - -import com.goby56.wakes.simulation.QuadTree; -import com.goby56.wakes.simulation.WakeHandler; -import com.mojang.blaze3d.opengl.GlConst; -import com.mojang.blaze3d.opengl.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.FilterMode; -import com.mojang.blaze3d.textures.GpuTexture; -import com.mojang.blaze3d.textures.GpuTextureView; -import com.mojang.blaze3d.textures.TextureFormat; -import com.mojang.blaze3d.opengl.GlTexture; - -public class WakeTexture { - private final GpuTextureView textureView; - public int res; - public GpuTexture texture; - public final boolean isUsingBricks; - private final int resolutionScaling; - - public WakeTexture(int res, boolean useBricks) { - this.res = res; - this.isUsingBricks = useBricks; - this.resolutionScaling = useBricks ? QuadTree.BRICK_WIDTH : 1; - - this.texture = RenderSystem.getDevice().createTexture(() -> "Wake Texture", GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING, - TextureFormat.RGBA8, resolutionScaling * res, resolutionScaling * res, 1, 1); - // texture.setTextureFilter(FilterMode.NEAREST, false); - this.textureView = RenderSystem.getDevice().createTextureView(texture); - } - - public void loadTexture(long imgPtr, int glFormat) { - GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); - - int dim = resolutionScaling * WakeHandler.resolution.res; - GlStateManager._bindTexture(((GlTexture) texture).glId()); - GlStateManager._texSubImage2D(GlConst.GL_TEXTURE_2D, 0,0,0,dim, dim, glFormat, GlConst.GL_UNSIGNED_BYTE, imgPtr); - - //RenderSystem.setShaderTexture(0, texture); - //RenderSystem.setShader(ShaderProgramKeys.POSITION_TEX_COLOR); - //RenderSystem.enableDepthTest(); // Is it THIS simple? https://github.com/Goby56/wakes/issues/46 - //RenderSystem.disableCull(); - RenderSystem.outputColorTextureOverride = textureView; - // RenderSystem.setShaderTexture(0, textureView); - } - - public GpuTexture getTexture() { - return texture; - } - - public GpuTextureView getTextureView() { - return textureView; - } -} diff --git a/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java new file mode 100644 index 0000000..fceab48 --- /dev/null +++ b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java @@ -0,0 +1,70 @@ +package com.goby56.wakes.render; + +import com.goby56.wakes.WakesClient; +import com.goby56.wakes.simulation.WakeChunk; +import com.mojang.blaze3d.opengl.GlConst; +import com.mojang.blaze3d.opengl.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.GpuTexture; +import com.mojang.blaze3d.textures.GpuTextureView; +import com.mojang.blaze3d.textures.TextureFormat; +import com.mojang.blaze3d.opengl.GlTexture; +import net.minecraft.client.model.geom.builders.UVPair; + +public class WakeTextureAtlas { + public static final int CHUNKS_PER_ROW = 16; + + public final int chunkResolution; + public final int resolution; + + public GpuTexture texture; + private final GpuTextureView textureView; + + private int chunkTexturesStored = 0; + private static final int CAPACITY = CHUNKS_PER_ROW * CHUNKS_PER_ROW - 1; + + public WakeTextureAtlas(int wakeNodeRes) { + chunkResolution = wakeNodeRes * WakeChunk.WIDTH; + resolution = chunkResolution * CHUNKS_PER_ROW; + + texture = RenderSystem.getDevice().createTexture(() -> WakesClient.MOD_ID + " wake texture atlas", + GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING | GpuTexture.USAGE_RENDER_ATTACHMENT, + TextureFormat.RGBA8, resolution, resolution, 1, 1); + // texture.setTextureFilter(FilterMode.NEAREST, false); + + textureView = RenderSystem.getDevice().createTextureView(texture); + } + + public UVPair loadTexture(long imgPtr, int glFormat) { + if (chunkTexturesStored == CAPACITY) { + float undefinedTextureCoord = 1f - 1 / (float) CHUNKS_PER_ROW; + return new UVPair(undefinedTextureCoord, undefinedTextureCoord); + } + + GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); + + int r = chunkTexturesStored / CHUNKS_PER_ROW; + int c = chunkTexturesStored % CHUNKS_PER_ROW; + int xOffset = c * chunkResolution; + int yOffset = r * chunkResolution; + + GlStateManager._bindTexture(((GlTexture) texture).glId()); + GlStateManager._texSubImage2D(GlConst.GL_TEXTURE_2D, 0, xOffset, yOffset, chunkResolution, chunkResolution, glFormat, GlConst.GL_UNSIGNED_BYTE, imgPtr); + + RenderSystem.outputColorTextureOverride = textureView; + chunkTexturesStored++; + + return new UVPair(c / (float) CHUNKS_PER_ROW, r / (float) CHUNKS_PER_ROW); + } + + public GpuTextureView getTextureView() { + return textureView; + } + + public void markDirty() { + this.chunkTexturesStored = 0; + } +} diff --git a/src/main/java/com/goby56/wakes/simulation/QuadTree.java b/src/main/java/com/goby56/wakes/simulation/QuadTree.java deleted file mode 100644 index e882093..0000000 --- a/src/main/java/com/goby56/wakes/simulation/QuadTree.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.goby56.wakes.simulation; - -import com.goby56.wakes.render.FrustumManager; -import net.minecraft.world.phys.AABB; - -import java.util.*; - -public class QuadTree { - public static final int BRICK_WIDTH = 4; - private static final int MAX_DEPTH = (int) (26 - Math.log(BRICK_WIDTH) / Math.log(2)); - private static final int ROOT_X = (int) - Math.pow(2, 25); - private static final int ROOT_Z = (int) - Math.pow(2, 25); - private static final int ROOT_WIDTH = (int) Math.pow(2, 26); - - private final QuadTree ROOT; - private List children; - - private final DecentralizedBounds bounds; - private final int depth; - private Brick brick; - public final float yLevel; - - public QuadTree(float y) { - this(ROOT_X, y, ROOT_Z, ROOT_WIDTH, 0, null); - } - - private QuadTree(int x, float y, int z, int width, int depth, QuadTree root) { - this.bounds = new DecentralizedBounds(x, y, z, width); - this.depth = depth; - this.ROOT = root == null ? this : root; - this.yLevel = y; - } - - private boolean hasLeaf() { - return depth == MAX_DEPTH && brick != null; - } - - private void initLeaf() { - if (depth >= MAX_DEPTH) { - this.brick = new Brick(bounds.x, this.yLevel, bounds.z, bounds.width); - this.ROOT.updateAdjacency(this); - } - } - - protected void updateAdjacency(QuadTree leaf) { - if (this == leaf) return; - if (!this.bounds.neighbors(leaf.bounds) && !this.bounds.intersects(leaf.bounds)) { - return; - } - if (brick != null) { - brick.updateAdjacency(leaf.brick); - return; - } - if (children != null) { - for (var tree : children) { - tree.updateAdjacency(leaf); - } - } - } - - public boolean tick(WakeHandler wakeHandler) { - if (hasLeaf()) { - return brick.tick(wakeHandler); - } - if (children == null) return false; - int aliveChildren = 0; - for (var tree : children) { - if (tree.tick(wakeHandler)) aliveChildren++; - } - if (aliveChildren == 0) this.prune(); - return aliveChildren > 0; - } - - public boolean insert(WakeNode node) { - if (!this.bounds.contains(node.x, node.z)) { - return false; - } - - if (depth == MAX_DEPTH) { - if (brick == null) { - initLeaf(); - } - brick.insert(node); - return true; - } - - if (children == null) this.subdivide(); - for (var tree : children) { - if (tree.insert(node)) return true; - } - return false; - } - - public void recolorWakes() { - if (hasLeaf()) { - brick.populatePixels(); - } - if (children == null) return; - for (var tree : children) { - tree.recolorWakes(); - } - } - - public void query(ArrayList output, Class type) { - if (!FrustumManager.isVisible(this.bounds.toBox((int) yLevel))) { - return; - } - if (hasLeaf() && brick.occupied > 0) { - if (type.equals(Brick.class)) { - output.add(type.cast(brick)); - } - if (type.equals(WakeNode.class)) { - ArrayList nodes = new ArrayList<>(); - brick.query(nodes); - for (var node : nodes) { - output.add(type.cast(node)); - } - } - return; - } - if (children == null) return; - for (var tree : children) { - tree.query(output, type); - } - } - - private void subdivide() { - if (depth == MAX_DEPTH) return; - int x = this.bounds.x; - int z = this.bounds.z; - int w = this.bounds.width >> 1; - children = new ArrayList<>(); - children.add(0, new QuadTree(x, yLevel, z, w, depth + 1, this.ROOT)); // NW - children.add(1, new QuadTree(x + w, yLevel, z, w, depth + 1, this.ROOT)); // NE - children.add(2, new QuadTree(x, yLevel, z + w, w, depth + 1, this.ROOT)); // SW - children.add(3, new QuadTree(x + w, yLevel, z + w, w, depth + 1, this.ROOT)); // SE - } - - public void prune() { - if (children != null) { - for (var tree : children) { - tree.prune(); - if (tree.hasLeaf()) tree.brick.deallocTexture(); - } - children.set(0, null); - children.set(1, null); - children.set(2, null); - children.set(3, null); - } - children = null; - } - - public record DecentralizedBounds(int x, float y, int z, int width) { - public boolean contains(int x, int z) { - return this.x <= x && x < this.x + this.width && - this.z <= z && z < this.z + this.width; - } - - public boolean intersects(DecentralizedBounds other) { - return !(this.x > other.x + other.width || - this.x + this.width < other.x || - this.z > other.z + other.width || - this.z + this.width < other.z); - } - - public boolean neighbors(DecentralizedBounds other) { - return !(this.x == other.x + other.width || - this.x + this.width == other.x || - this.z == other.z + other.width || - this.z + this.width == other.z); - } - - public AABB toBox(int y) { - return new AABB(this.x, y - 0.5, this.z, - this.x + this.width, y + 0.5, this.z + this.width); - } - } -} diff --git a/src/main/java/com/goby56/wakes/simulation/Brick.java b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java similarity index 67% rename from src/main/java/com/goby56/wakes/simulation/Brick.java rename to src/main/java/com/goby56/wakes/simulation/WakeChunk.java index c74d738..645c93b 100644 --- a/src/main/java/com/goby56/wakes/simulation/Brick.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java @@ -3,14 +3,13 @@ import com.goby56.wakes.config.WakesConfig; import com.goby56.wakes.debug.WakesDebugInfo; import com.goby56.wakes.render.FrustumManager; -import com.goby56.wakes.render.WakeTexture; import com.goby56.wakes.utils.WakesUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BiomeColors; import net.minecraft.client.renderer.LightTexture; +import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import net.minecraft.world.level.Level; import org.lwjgl.system.MemoryUtil; import java.util.ArrayList; @@ -18,38 +17,39 @@ import java.util.Objects; import java.util.stream.Stream; -public class Brick { +public class WakeChunk { + public static final int WIDTH = 16; private final WakeNode[][] nodes; public final int capacity; - public final int dim; public int occupied = 0; public final Vec3 pos; + public final WakeChunkPos chunkPos; + public final AABB boundingBox; - public Brick NORTH; - public Brick EAST; - public Brick SOUTH; - public Brick WEST; + public WakeChunk NORTH; + public WakeChunk EAST; + public WakeChunk SOUTH; + public WakeChunk WEST; public long imgPtr = -1; public int texRes; public boolean hasPopulatedPixels = false; - public WakeTexture wakeTexture; - public Brick(int x, float y, int z, int width) { - this.dim = width; - this.capacity = dim * dim; - this.nodes = new WakeNode[dim][dim]; - this.pos = new Vec3(x, y, z); + public WakeChunk(WakeChunkPos chunkPos) { + this.capacity = WIDTH * WIDTH; + this.nodes = new WakeNode[WIDTH][WIDTH]; + this.chunkPos = chunkPos; + this.pos = new Vec3(chunkPos.cx() * WIDTH, chunkPos.y(), chunkPos.cz() * WIDTH); + this.boundingBox = new AABB(pos.x, pos.y, pos.z, pos.x + WIDTH, pos.y + 1, pos.z + WIDTH); initTexture(WakeHandler.resolution.res); } public void initTexture(int res) { - this.wakeTexture = new WakeTexture(res, true); - long size = 4L * dim * dim * res * res; + long size = 4L * WIDTH * WIDTH * res * res; if (imgPtr == -1) { this.imgPtr = MemoryUtil.nmemAlloc(size); } else { @@ -57,7 +57,6 @@ public void initTexture(int res) { } this.texRes = res; this.hasPopulatedPixels = false; - } public void deallocTexture() { @@ -66,8 +65,8 @@ public void deallocTexture() { public boolean tick(WakeHandler wakeHandler) { long tNode = System.nanoTime(); - for (int z = 0; z < dim; z++) { - for (int x = 0; x < dim; x++) { + for (int z = 0; z < WIDTH; z++) { + for (int x = 0; x < WIDTH; x++) { if (this.get(x, z) == null) continue; if (!this.get(x, z).tick(wakeHandler)) { @@ -84,8 +83,8 @@ public boolean tick(WakeHandler wakeHandler) { } public void query(ArrayList output) { - for (int z = 0; z < dim; z++) { - for (int x = 0; x < dim; x++) { + for (int z = 0; z < WIDTH; z++) { + for (int x = 0; x < WIDTH; x++) { var node = this.get(x, z); if (node == null) continue; AABB b = node.toBox(); @@ -95,27 +94,27 @@ public void query(ArrayList output) { } public WakeNode get(int x, int z) { - if (x >= 0 && x < dim) { + if (x >= 0 && x < WIDTH) { if (z < 0 && NORTH != null) { - return NORTH.nodes[Math.floorMod(z, dim)][x]; - } else if (z >= dim && SOUTH != null) { - return SOUTH.nodes[Math.floorMod(z, dim)][x]; - } else if (z >= 0 && z < dim){ + return NORTH.nodes[Math.floorMod(z, WIDTH)][x]; + } else if (z >= WIDTH && SOUTH != null) { + return SOUTH.nodes[Math.floorMod(z, WIDTH)][x]; + } else if (z >= 0 && z < WIDTH){ return nodes[z][x]; } } - if (z >= 0 && z < dim) { + if (z >= 0 && z < WIDTH) { if (x < 0 && WEST != null) { - return WEST.nodes[z][Math.floorMod(x, dim)]; - } else if (x >= dim && EAST != null) { - return EAST.nodes[z][Math.floorMod(x, dim)]; + return WEST.nodes[z][Math.floorMod(x, WIDTH)]; + } else if (x >= WIDTH && EAST != null) { + return EAST.nodes[z][Math.floorMod(x, WIDTH)]; } } return null; } public void insert(WakeNode node) { - int x = Math.floorMod(node.x, dim), z = Math.floorMod(node.z, dim); + int x = Math.floorMod(node.x, WIDTH), z = Math.floorMod(node.z, WIDTH); if (nodes[z][x] != null) { nodes[z][x].revive(node); return; @@ -148,32 +147,32 @@ private List getAdjacentNodes(int x, int z) { this.get(x - 1, z)).filter(Objects::nonNull).toList(); } - public void updateAdjacency(Brick brick) { - if (brick.pos.x == this.pos.x && brick.pos.z == this.pos.z - dim) { - this.NORTH = brick; - brick.SOUTH = this; + public void updateAdjacency(WakeChunk newChunk) { + if (newChunk.pos.x == this.pos.x && newChunk.pos.z == this.pos.z - WIDTH) { + this.NORTH = newChunk; + newChunk.SOUTH = this; return; } - if (brick.pos.x == this.pos.x + dim && brick.pos.z == this.pos.z) { - this.EAST = brick; - brick.WEST = this; + if (newChunk.pos.x == this.pos.x + WIDTH && newChunk.pos.z == this.pos.z) { + this.EAST = newChunk; + newChunk.WEST = this; return; } - if (brick.pos.x == this.pos.x && brick.pos.z == this.pos.z + dim) { - this.SOUTH = brick; - brick.NORTH = this; + if (newChunk.pos.x == this.pos.x && newChunk.pos.z == this.pos.z + WIDTH) { + this.SOUTH = newChunk; + newChunk.NORTH = this; return; } - if (brick.pos.x == this.pos.x - dim && brick.pos.z == this.pos.z) { - this.WEST = brick; - brick.EAST = this; + if (newChunk.pos.x == this.pos.x - WIDTH && newChunk.pos.z == this.pos.z) { + this.WEST = newChunk; + newChunk.EAST = this; } } public void populatePixels() { Level world = Minecraft.getInstance().level; - for (int z = 0; z < dim; z++) { - for (int x = 0; x < dim; x++) { + for (int z = 0; z < WIDTH; z++) { + for (int x = 0; x < WIDTH; x++) { WakeNode node = this.get(x, z); int lightCol = LightTexture.FULL_BRIGHT; int fluidColor = 0; @@ -186,7 +185,7 @@ public void populatePixels() { } // TODO MASS SET PIXELS TO NO COLOR IF NODE DOESNT EXIST (NEED TO REORDER PIXELS STORED?) - long nodeOffset = texRes * 4L * (((long) z * dim * texRes) + (long) x); + long nodeOffset = texRes * 4L * (((long) z * WIDTH * texRes) + (long) x); for (int r = 0; r < texRes; r++) { for (int c = 0; c < texRes; c++) { int color = 0; @@ -194,7 +193,7 @@ public void populatePixels() { // TODO USE SHADERS TO COLOR THE WAKES? color = node.simulationNode.getPixelColor(c, r, fluidColor, lightCol, opacity); } - long pixelOffset = 4L * (((long) r * dim * texRes) + c); + long pixelOffset = 4L * (((long) r * WIDTH * texRes) + c); MemoryUtil.memPutInt(imgPtr + nodeOffset + pixelOffset, color); } } diff --git a/src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java b/src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java new file mode 100644 index 0000000..078f357 --- /dev/null +++ b/src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java @@ -0,0 +1,7 @@ +package com.goby56.wakes.simulation; + +public record WakeChunkPos(int cx, int y, int cz) { + public static WakeChunkPos fromWakeNode(WakeNode wakeNode) { + return new WakeChunkPos(Math.floorDiv(wakeNode.x, WakeChunk.WIDTH), wakeNode.y, Math.floorDiv(wakeNode.z, WakeChunk.WIDTH)); + } +} diff --git a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java index 0911b57..4a5a5e0 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java @@ -7,18 +7,14 @@ import net.minecraft.client.Minecraft; import net.minecraft.world.level.Level; -import java.util.ArrayList; -import java.util.Optional; -import java.util.Queue; +import java.util.*; public class WakeHandler { private static WakeHandler INSTANCE; public Level world; - private final QuadTree[] trees; - private final QueueSet[] toBeInserted; - private final int minY; - private final int maxY; + private final HashMap wakeChunks = new HashMap<>(); + private final QueueSet toBeInserted; private final ArrayList splashPlanes; public static Resolution resolution = WakesConfig.wakeResolution; @@ -27,14 +23,7 @@ public class WakeHandler { private WakeHandler(Level world) { this.world = world; - this.minY = world.getMinY(); - this.maxY = world.getMaxY(); - int worldHeight = this.maxY - this.minY; - this.trees = new QuadTree[worldHeight]; - this.toBeInserted = new QueueSet[worldHeight]; - for (int i = 0; i < worldHeight; i++) { - toBeInserted[i] = new QueueSet<>(); - } + this.toBeInserted = new QueueSet<>(); this.splashPlanes = new ArrayList<>(); } @@ -53,6 +42,7 @@ public static void init(Level world) { } public static void kill() { + getInstance().ifPresent(wakeHandler -> wakeHandler.wakeChunks.clear()); INSTANCE = null; } @@ -60,21 +50,32 @@ public void tick() { if (WakesConfig.wakeResolution.res != WakeHandler.resolution.res) { scheduleResolutionChange(WakesConfig.wakeResolution); } - for (int i = 0; i < this.maxY - this.minY; i++) { - Queue pendingNodes = this.toBeInserted[i]; - if (resolutionResetScheduled) { - if (pendingNodes != null) - pendingNodes.clear(); - continue; + if (resolutionResetScheduled) { + toBeInserted.clear(); + } + + ArrayList toBeRemovedChunks = new ArrayList<>(); + for (WakeChunk chunk : wakeChunks.values()) { + boolean wakesPresent = chunk.tick(this); + if (!wakesPresent) { + toBeRemovedChunks.add(chunk.chunkPos); } - QuadTree tree = this.trees[i]; - if (tree != null) { - tree.tick(this); - while (pendingNodes.peek() != null) { - tree.insert(pendingNodes.poll()); - } + } + for (WakeChunkPos pos : toBeRemovedChunks) { + wakeChunks.remove(pos); + } + + while (toBeInserted.peek() != null) { + WakeNode node = toBeInserted.poll(); + WakeChunkPos pos = WakeChunkPos.fromWakeNode(node); + WakeChunk chunk = wakeChunks.get(pos); + if (chunk == null) { + chunk = new WakeChunk(pos); + wakeChunks.put(pos, new WakeChunk(pos)); } + chunk.insert(node); } + for (int i = this.splashPlanes.size() - 1; i >= 0; i--) { if (!this.splashPlanes.get(i).isAlive()) { this.splashPlanes.remove(i); @@ -86,11 +87,8 @@ public void tick() { } public void recolorWakes() { - for (int i = 0; i < this.maxY - this.minY; i++) { - QuadTree tree = this.trees[i]; - if (tree != null) { - tree.recolorWakes(); - } + for (WakeChunk chunk : wakeChunks.values()) { + chunk.populatePixels(); } for (var splashPlane : this.splashPlanes) { if (splashPlane != null) { @@ -104,44 +102,41 @@ public void registerSplashPlane(SplashPlaneParticle splashPlane) { } public void insert(WakeNode node) { - if (resolutionResetScheduled) - return; - int i = this.getArrayIndex(node.y); - if (i < 0) - return; - - if (this.trees[i] == null) { - this.trees[i] = new QuadTree(node.y); - } + if (resolutionResetScheduled) return; if (node.validPos(world)) { - this.toBeInserted[i].add(node); + this.toBeInserted.add(node); } } - public ArrayList getVisible(Class type) { - ArrayList visibleObjects = new ArrayList<>(); - if (type.equals(SplashPlaneParticle.class)) { - for (SplashPlaneParticle particle : splashPlanes) { - if (FrustumManager.isVisible(particle.getBoundingBox())) { - visibleObjects.add(type.cast(particle)); - } + public List getVisibleNodes() { + ArrayList nodes = new ArrayList<>(); + for (WakeChunk chunk : wakeChunks.values()) { + if (FrustumManager.isVisible(chunk.boundingBox)) { + chunk.query(nodes); } - } else { - for (int i = 0; i < this.maxY - this.minY; i++) { - if (this.trees[i] != null) { - this.trees[i].query(visibleObjects, type); - } + } + return nodes; + } + + public List getVisibleChunks() { + ArrayList chunks = new ArrayList<>(); + for (WakeChunk chunk : wakeChunks.values()) { + if (FrustumManager.isVisible(chunk.boundingBox)) { + chunks.add(chunk); } } - return visibleObjects; + return chunks; } - private int getArrayIndex(int y) { - if (y < this.minY || y >= this.maxY) { - return -1; + public List getVisibleSplashPlanes() { + ArrayList splashPlanes = new ArrayList<>(); + for (SplashPlaneParticle particle : this.splashPlanes) { + if (FrustumManager.isVisible(particle.getBoundingBox())) { + splashPlanes.add(particle); + } } - return y - this.minY; + return splashPlanes; } public static void scheduleResolutionChange(Resolution newRes) { @@ -155,12 +150,7 @@ private void changeResolution() { } private void reset() { - for (int i = 0; i < this.maxY - this.minY; i++) { - QuadTree tree = this.trees[i]; - if (tree != null) { - tree.prune(); - } - toBeInserted[i].clear(); - } + wakeChunks.clear(); + toBeInserted.clear(); } } From c00f747326515c2086eb3811ada452bac5cd346e Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sat, 31 Jan 2026 10:14:09 +0100 Subject: [PATCH 07/15] fixed chunks spawning indefinitely still some issues with wakes persistent in corner of chunks even though no entity is near --- .../goby56/wakes/simulation/WakeChunk.java | 68 ++++++++----------- .../goby56/wakes/simulation/WakeChunkPos.java | 16 +++++ .../goby56/wakes/simulation/WakeHandler.java | 10 ++- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java index 645c93b..23d3746 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java @@ -12,12 +12,12 @@ import net.minecraft.world.phys.Vec3; import org.lwjgl.system.MemoryUtil; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Stream; public class WakeChunk { + private final WakeHandler wakeHandler; + public static final int WIDTH = 16; private final WakeNode[][] nodes; public final int capacity; @@ -28,22 +28,21 @@ public class WakeChunk { public final WakeChunkPos chunkPos; public final AABB boundingBox; - public WakeChunk NORTH; - public WakeChunk EAST; - public WakeChunk SOUTH; - public WakeChunk WEST; + public final Map neighbors; public long imgPtr = -1; public int texRes; public boolean hasPopulatedPixels = false; - public WakeChunk(WakeChunkPos chunkPos) { + public WakeChunk(WakeChunkPos chunkPos, WakeHandler wakeHandler) { this.capacity = WIDTH * WIDTH; this.nodes = new WakeNode[WIDTH][WIDTH]; this.chunkPos = chunkPos; this.pos = new Vec3(chunkPos.cx() * WIDTH, chunkPos.y(), chunkPos.cz() * WIDTH); this.boundingBox = new AABB(pos.x, pos.y, pos.z, pos.x + WIDTH, pos.y + 1, pos.z + WIDTH); + this.neighbors = new HashMap<>(); + this.wakeHandler = wakeHandler; initTexture(WakeHandler.resolution.res); } @@ -63,7 +62,7 @@ public void deallocTexture() { MemoryUtil.nmemFree(imgPtr); } - public boolean tick(WakeHandler wakeHandler) { + public boolean tick() { long tNode = System.nanoTime(); for (int z = 0; z < WIDTH; z++) { for (int x = 0; x < WIDTH; x++) { @@ -95,24 +94,35 @@ public void query(ArrayList output) { public WakeNode get(int x, int z) { if (x >= 0 && x < WIDTH) { - if (z < 0 && NORTH != null) { - return NORTH.nodes[Math.floorMod(z, WIDTH)][x]; - } else if (z >= WIDTH && SOUTH != null) { - return SOUTH.nodes[Math.floorMod(z, WIDTH)][x]; - } else if (z >= 0 && z < WIDTH){ + if (z < 0) { + return getNeighbor(WakeChunkPos.Direction.NORTH).map( + wakeChunk -> wakeChunk.nodes[Math.floorMod(z, WIDTH)][x]).orElse(null); + } else if (z >= WIDTH) { + return getNeighbor(WakeChunkPos.Direction.SOUTH).map( + wakeChunk -> wakeChunk.nodes[Math.floorMod(z, WIDTH)][x]).orElse(null); + } else { return nodes[z][x]; } } if (z >= 0 && z < WIDTH) { - if (x < 0 && WEST != null) { - return WEST.nodes[z][Math.floorMod(x, WIDTH)]; - } else if (x >= WIDTH && EAST != null) { - return EAST.nodes[z][Math.floorMod(x, WIDTH)]; + if (x < 0) { + return getNeighbor(WakeChunkPos.Direction.WEST).map( + wakeChunk -> wakeChunk.nodes[z][Math.floorMod(x, WIDTH)]).orElse(null); + } else { + return getNeighbor(WakeChunkPos.Direction.EAST).map( + wakeChunk -> wakeChunk.nodes[z][Math.floorMod(x, WIDTH)]).orElse(null); } } return null; } + private Optional getNeighbor(WakeChunkPos.Direction direction) { + if (neighbors.get(direction) == null) { + neighbors.put(direction, wakeHandler.getChunk(chunkPos.offset(direction))); + } + return Optional.ofNullable(neighbors.get(direction)); + } + public void insert(WakeNode node) { int x = Math.floorMod(node.x, WIDTH), z = Math.floorMod(node.z, WIDTH); if (nodes[z][x] != null) { @@ -147,28 +157,6 @@ private List getAdjacentNodes(int x, int z) { this.get(x - 1, z)).filter(Objects::nonNull).toList(); } - public void updateAdjacency(WakeChunk newChunk) { - if (newChunk.pos.x == this.pos.x && newChunk.pos.z == this.pos.z - WIDTH) { - this.NORTH = newChunk; - newChunk.SOUTH = this; - return; - } - if (newChunk.pos.x == this.pos.x + WIDTH && newChunk.pos.z == this.pos.z) { - this.EAST = newChunk; - newChunk.WEST = this; - return; - } - if (newChunk.pos.x == this.pos.x && newChunk.pos.z == this.pos.z + WIDTH) { - this.SOUTH = newChunk; - newChunk.NORTH = this; - return; - } - if (newChunk.pos.x == this.pos.x - WIDTH && newChunk.pos.z == this.pos.z) { - this.WEST = newChunk; - newChunk.EAST = this; - } - } - public void populatePixels() { Level world = Minecraft.getInstance().level; for (int z = 0; z < WIDTH; z++) { diff --git a/src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java b/src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java index 078f357..5e09dfb 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeChunkPos.java @@ -4,4 +4,20 @@ public record WakeChunkPos(int cx, int y, int cz) { public static WakeChunkPos fromWakeNode(WakeNode wakeNode) { return new WakeChunkPos(Math.floorDiv(wakeNode.x, WakeChunk.WIDTH), wakeNode.y, Math.floorDiv(wakeNode.z, WakeChunk.WIDTH)); } + + public enum Direction { + NORTH, + SOUTH, + EAST, + WEST + } + + public WakeChunkPos offset(Direction direction) { + return switch (direction) { + case NORTH -> new WakeChunkPos(cx, y, cz - 1); + case SOUTH -> new WakeChunkPos(cx, y, cz + 1); + case EAST -> new WakeChunkPos(cx + 1, y, cz); + case WEST -> new WakeChunkPos(cx - 1, y, cz); + }; + } } diff --git a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java index 4a5a5e0..5b3b2d6 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java @@ -56,7 +56,7 @@ public void tick() { ArrayList toBeRemovedChunks = new ArrayList<>(); for (WakeChunk chunk : wakeChunks.values()) { - boolean wakesPresent = chunk.tick(this); + boolean wakesPresent = chunk.tick(); if (!wakesPresent) { toBeRemovedChunks.add(chunk.chunkPos); } @@ -70,8 +70,8 @@ public void tick() { WakeChunkPos pos = WakeChunkPos.fromWakeNode(node); WakeChunk chunk = wakeChunks.get(pos); if (chunk == null) { - chunk = new WakeChunk(pos); - wakeChunks.put(pos, new WakeChunk(pos)); + chunk = new WakeChunk(pos, this); + wakeChunks.put(pos, chunk); } chunk.insert(node); } @@ -86,6 +86,10 @@ public void tick() { } } + public WakeChunk getChunk(WakeChunkPos pos) { + return wakeChunks.get(pos); + } + public void recolorWakes() { for (WakeChunk chunk : wakeChunks.values()) { chunk.populatePixels(); From 42e775694f0d6b29c51709a06ab6a7b51d9e92d8 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sun, 1 Feb 2026 16:31:23 +0100 Subject: [PATCH 08/15] renders brown wakes --- .../java/com/goby56/wakes/WakesClient.java | 4 +- .../goby56/wakes/debug/WakeDebugRenderer.java | 8 ++ .../wakes/render/BetterDynamicTexture.java | 40 +++++++ .../com/goby56/wakes/render/WakeRenderer.java | 39 +++---- .../goby56/wakes/render/WakeTextureAtlas.java | 100 +++++++++++------- .../goby56/wakes/simulation/WakeChunk.java | 60 +++++------ .../goby56/wakes/simulation/WakeHandler.java | 43 ++++---- 7 files changed, 174 insertions(+), 120 deletions(-) create mode 100644 src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java diff --git a/src/main/java/com/goby56/wakes/WakesClient.java b/src/main/java/com/goby56/wakes/WakesClient.java index e75efba..2c7e051 100644 --- a/src/main/java/com/goby56/wakes/WakesClient.java +++ b/src/main/java/com/goby56/wakes/WakesClient.java @@ -16,6 +16,7 @@ import eu.midnightdust.lib.config.MidnightConfig; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents; import net.fabricmc.loader.api.FabricLoader; @@ -68,8 +69,9 @@ public void onInitializeClient() { // Rendering events wakeRenderer = new WakeRenderer(); - WorldRenderEvents.BEFORE_TRANSLUCENT.register(wakeRenderer); + WorldRenderEvents.END_MAIN.register(wakeRenderer); WorldRenderEvents.END_MAIN.register(new SplashPlaneRenderer()); + // HudElementRegistry.addLast(Identifier.fromNamespaceAndPath(MOD_ID, "wakes_texture_atlas"), WakeDebugRenderer.wakeAtlasHudLayer()); SplashPlaneRenderer.initSplashPlane(); DebugScreenEntries.register( diff --git a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java index 93e577d..fa0ea3e 100644 --- a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java +++ b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java @@ -5,6 +5,7 @@ import com.goby56.wakes.simulation.WakeChunk; import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.simulation.WakeNode; +import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElement; import net.minecraft.gizmos.GizmoStyle; import net.minecraft.gizmos.Gizmos; import net.minecraft.world.phys.AABB; @@ -18,6 +19,7 @@ public static void addDebugGizmos() { WakeHandler wakeHandler = WakeHandler.getInstance().orElse(null); if (wakeHandler == null) return; int color = new WakeColor(255, 0, 255, 128).argb; + if (WakesConfig.drawDebugBoxes) { for (var node : wakeHandler.getVisibleNodes()) { Gizmos.cuboid(node.toBox(), GizmoStyle.fill(color)); @@ -30,4 +32,10 @@ public static void addDebugGizmos() { } } } + + public static HudElement wakeAtlasHudLayer() { + return (guiGraphics, deltaTracker) -> { + + }; + } } diff --git a/src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java b/src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java new file mode 100644 index 0000000..80dfe33 --- /dev/null +++ b/src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java @@ -0,0 +1,40 @@ +package com.goby56.wakes.render; + +import com.goby56.wakes.WakesClient; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.TextureFormat; +import net.minecraft.client.renderer.texture.AbstractTexture; + +import java.util.function.Supplier; + +public class BetterDynamicTexture extends AbstractTexture { + private NativeImage pixels; + + public BetterDynamicTexture(Supplier supplier, NativeImage nativeImage) { + this.pixels = nativeImage; + this.createTexture(supplier); + } + + private void createTexture(Supplier supplier) { + GpuDevice gpuDevice = RenderSystem.getDevice(); + this.texture = gpuDevice.createTexture(supplier, 5, TextureFormat.RGBA8, this.pixels.getWidth(), this.pixels.getHeight(), 1, 1); + this.sampler = RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST); + this.textureView = gpuDevice.createTextureView(this.texture); + } + + public void upload() { + if (this.texture != null) { + RenderSystem.getDevice().createCommandEncoder().writeToTexture(this.texture, this.pixels); + } else { + WakesClient.LOGGER.warn("Trying to upload disposed texture {}", this.getTexture().getLabel()); + } + } + + public void close() { + this.pixels.close(); + super.close(); + } +} diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index 86788ae..40e1a85 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -16,6 +16,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuSampler; +import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.blaze3d.vertex.*; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; @@ -25,6 +26,7 @@ import net.minecraft.client.renderer.MappableRingBuffer; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.texture.DynamicTexture; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -34,7 +36,7 @@ import java.util.*; -public class WakeRenderer implements WorldRenderEvents.BeforeTranslucent { +public class WakeRenderer implements WorldRenderEvents.EndMain { private static final ByteBufferBuilder allocator = new ByteBufferBuilder(RenderType.BIG_BUFFER_SIZE); private BufferBuilder buffer; @@ -46,27 +48,15 @@ public class WakeRenderer implements WorldRenderEvents.BeforeTranslucent { private final RenderPipeline PIPELINE = RenderPipelines.TRANSLUCENT_MOVING_BLOCK; - private Map textureAtlases = null; - - private void initTextures() { - textureAtlases = Map.of( - Resolution.EIGHT, new WakeTextureAtlas(Resolution.EIGHT.res), - Resolution.SIXTEEN, new WakeTextureAtlas(Resolution.SIXTEEN.res), - Resolution.THIRTYTWO, new WakeTextureAtlas(Resolution.THIRTYTWO.res) - ); - } - @Override - public void beforeTranslucent(WorldRenderContext context) { + public void endMain(WorldRenderContext context) { if (WakesConfig.disableMod) { WakesDebugInfo.quadsRendered = 0; return; } - if (textureAtlases == null) initTextures(); - WakeHandler wakeHandler = WakeHandler.getInstance().orElse(null); - if (wakeHandler == null || WakeHandler.resolutionResetScheduled) return; + if (wakeHandler == null) return; List wakeChunks = wakeHandler.getVisibleChunks(); @@ -82,8 +72,7 @@ public void beforeTranslucent(WorldRenderContext context) { matrices.pushPose(); matrices.translate(-camera.x, -camera.y, -camera.z); - WakeTextureAtlas atlas = textureAtlases.get(WakeHandler.resolution); - addVertices(matrices.last().pose(), buffer, wakeChunks, atlas); + addVertices(matrices.last().pose(), buffer, wakeChunks); matrices.popPose(); @@ -93,19 +82,19 @@ public void beforeTranslucent(WorldRenderContext context) { GpuBuffer vertices = uploadMesh(drawParameters, format, builtBuffer); - draw(builtBuffer, drawParameters, vertices, format, atlas); + BetterDynamicTexture texture = wakeHandler.getActiveTextureAtlas().dynamicTexture; + texture.upload(); + draw(builtBuffer, drawParameters, vertices, format, texture.getTextureView()); // Rotate the vertex buffer so we are less likely to use buffers that the GPU is using vertexBuffer.rotate(); buffer = null; } - private void addVertices(Matrix4fc matrix, BufferBuilder bb, List chunks, WakeTextureAtlas textureAtlas) { + private void addVertices(Matrix4fc matrix, BufferBuilder bb, List chunks) { for (WakeChunk wakeChunk : chunks) { - //if (!wakeChunk.hasPopulatedPixels) continue; - - UVPair uv = textureAtlas.loadTexture(wakeChunk.imgPtr, GlConst.GL_RGBA); - float uvOffset = textureAtlas.chunkResolution / (float) textureAtlas.resolution; + UVPair uv = wakeChunk.drawContext.uv; + float uvOffset = wakeChunk.drawContext.uvOffset; float x0 = (float) wakeChunk.pos.x; float y = (float) (wakeChunk.pos.y + WakeNode.WATER_OFFSET); @@ -160,7 +149,7 @@ private GpuBuffer uploadMesh(MeshData.DrawState drawParameters, VertexFormat for return vertexBuffer.currentBuffer(); } - private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format, WakeTextureAtlas texture) { + private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format, GpuTextureView texture) { GpuBuffer indices; VertexFormat.IndexType indexType; @@ -196,7 +185,7 @@ private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBu RenderSystem.bindDefaultUniforms(pass); pass.setUniform("DynamicTransforms", dynamicTransforms); - pass.bindTexture("Sampler0", texture.getTextureView(), sampler); + pass.bindTexture("Sampler0", texture, sampler); pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); pass.setVertexBuffer(0, vertices); diff --git a/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java index fceab48..b0ae4a0 100644 --- a/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java +++ b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java @@ -4,67 +4,89 @@ import com.goby56.wakes.simulation.WakeChunk; import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlStateManager; +import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.GpuTexture; -import com.mojang.blaze3d.textures.GpuTextureView; -import com.mojang.blaze3d.textures.TextureFormat; import com.mojang.blaze3d.opengl.GlTexture; import net.minecraft.client.model.geom.builders.UVPair; +import net.minecraft.client.renderer.texture.DynamicTexture; + +import java.util.function.Supplier; public class WakeTextureAtlas { public static final int CHUNKS_PER_ROW = 16; + public final int nodeResolution; public final int chunkResolution; public final int resolution; - public GpuTexture texture; - private final GpuTextureView textureView; + protected final NativeImage nativeImage; + public final BetterDynamicTexture dynamicTexture; + + private final boolean[] occupiedSubTextures = new boolean[CAPACITY]; - private int chunkTexturesStored = 0; - private static final int CAPACITY = CHUNKS_PER_ROW * CHUNKS_PER_ROW - 1; + private static final int CAPACITY = CHUNKS_PER_ROW * CHUNKS_PER_ROW - 1; // Include error texture public WakeTextureAtlas(int wakeNodeRes) { + nodeResolution = wakeNodeRes; chunkResolution = wakeNodeRes * WakeChunk.WIDTH; resolution = chunkResolution * CHUNKS_PER_ROW; - texture = RenderSystem.getDevice().createTexture(() -> WakesClient.MOD_ID + " wake texture atlas", - GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING | GpuTexture.USAGE_RENDER_ATTACHMENT, - TextureFormat.RGBA8, resolution, resolution, 1, 1); - // texture.setTextureFilter(FilterMode.NEAREST, false); - - textureView = RenderSystem.getDevice().createTextureView(texture); + nativeImage = new NativeImage(resolution, resolution, false); + Supplier name = () -> String.format("%s %dx%x texture atlas (%d%d wakes)", + WakesClient.MOD_ID, resolution, resolution, nodeResolution, nodeResolution); + dynamicTexture = new BetterDynamicTexture(name, nativeImage); } - public UVPair loadTexture(long imgPtr, int glFormat) { - if (chunkTexturesStored == CAPACITY) { - float undefinedTextureCoord = 1f - 1 / (float) CHUNKS_PER_ROW; - return new UVPair(undefinedTextureCoord, undefinedTextureCoord); + public DrawContext claimSubTexture() { + for (int i = 0; i < CAPACITY; i++) { + if (!occupiedSubTextures[i]) { + occupiedSubTextures[i] = true; + return new DrawContext(i, this); + } } - - GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); - - int r = chunkTexturesStored / CHUNKS_PER_ROW; - int c = chunkTexturesStored % CHUNKS_PER_ROW; - int xOffset = c * chunkResolution; - int yOffset = r * chunkResolution; - - GlStateManager._bindTexture(((GlTexture) texture).glId()); - GlStateManager._texSubImage2D(GlConst.GL_TEXTURE_2D, 0, xOffset, yOffset, chunkResolution, chunkResolution, glFormat, GlConst.GL_UNSIGNED_BYTE, imgPtr); - - RenderSystem.outputColorTextureOverride = textureView; - chunkTexturesStored++; - - return new UVPair(c / (float) CHUNKS_PER_ROW, r / (float) CHUNKS_PER_ROW); + return null; } - public GpuTextureView getTextureView() { - return textureView; + protected void vacateSubTexture(int subTextureIndex) { + occupiedSubTextures[subTextureIndex] = false; } - public void markDirty() { - this.chunkTexturesStored = 0; + public static class DrawContext { + private boolean active; + private final int subTextureIndex; + private final WakeTextureAtlas atlas; + + public final UVPair uv; + public final float uvOffset; + public final int nodeResolution; + + public DrawContext(int subTextureIndex, WakeTextureAtlas atlas) { + this.subTextureIndex = subTextureIndex; + this.active = true; + this.atlas = atlas; + + int r = subTextureIndex / CHUNKS_PER_ROW; + int c = subTextureIndex % CHUNKS_PER_ROW; + this.uv = new UVPair(c / (float) CHUNKS_PER_ROW, r / (float) CHUNKS_PER_ROW); + this.uvOffset = atlas.chunkResolution / (float) atlas.resolution; + this.nodeResolution = atlas.nodeResolution; + } + + public void invalidate() { + this.atlas.vacateSubTexture(subTextureIndex); + this.active = false; + } + + public void draw(int x, int y, int color) { + if (!active) { + return; + //throw new IllegalAccessException("Wake texture atlas draw context has been invalidated and cannot be drawn to"); + } + int r = subTextureIndex / CHUNKS_PER_ROW; + int c = subTextureIndex % CHUNKS_PER_ROW; + int globX = x + c * CHUNKS_PER_ROW; + int globY = y + r * CHUNKS_PER_ROW; + this.atlas.nativeImage.setPixel(globX, globY, color); + } } } diff --git a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java index 23d3746..69e4cc7 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java @@ -3,6 +3,7 @@ import com.goby56.wakes.config.WakesConfig; import com.goby56.wakes.debug.WakesDebugInfo; import com.goby56.wakes.render.FrustumManager; +import com.goby56.wakes.render.WakeTextureAtlas; import com.goby56.wakes.utils.WakesUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BiomeColors; @@ -10,7 +11,6 @@ import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import org.lwjgl.system.MemoryUtil; import java.util.*; import java.util.stream.Stream; @@ -30,10 +30,7 @@ public class WakeChunk { public final Map neighbors; - public long imgPtr = -1; - public int texRes; - public boolean hasPopulatedPixels = false; - + public WakeTextureAtlas.DrawContext drawContext; public WakeChunk(WakeChunkPos chunkPos, WakeHandler wakeHandler) { this.capacity = WIDTH * WIDTH; @@ -44,22 +41,7 @@ public WakeChunk(WakeChunkPos chunkPos, WakeHandler wakeHandler) { this.neighbors = new HashMap<>(); this.wakeHandler = wakeHandler; - initTexture(WakeHandler.resolution.res); - } - - public void initTexture(int res) { - long size = 4L * WIDTH * WIDTH * res * res; - if (imgPtr == -1) { - this.imgPtr = MemoryUtil.nmemAlloc(size); - } else { - this.imgPtr = MemoryUtil.nmemRealloc(imgPtr, size); - } - this.texRes = res; - this.hasPopulatedPixels = false; - } - - public void deallocTexture() { - MemoryUtil.nmemFree(imgPtr); + this.drawContext = wakeHandler.getActiveTextureAtlas().claimSubTexture(); } public boolean tick() { @@ -75,7 +57,7 @@ public boolean tick() { } WakesDebugInfo.nodeLogicTime += (System.nanoTime() - tNode); long tTexturing = System.nanoTime(); - populatePixels(); + drawWakes(); WakesDebugInfo.texturingTime += (System.nanoTime() - tTexturing); WakesDebugInfo.nodeCount += occupied; return occupied != 0; @@ -149,6 +131,16 @@ public void clear(int x, int z) { this.set(x, z, null); } + public void destroy() { + for (int z = 0; z < WIDTH; z++) { + for (int x = 0; x < WIDTH; x++) { + nodes[z][x] = null; + } + } + occupied = 0; + drawContext.invalidate(); + } + private List getAdjacentNodes(int x, int z) { return Stream.of( this.get(x, z + 1), @@ -157,11 +149,12 @@ private List getAdjacentNodes(int x, int z) { this.get(x - 1, z)).filter(Objects::nonNull).toList(); } - public void populatePixels() { + public void drawWakes() { Level world = Minecraft.getInstance().level; - for (int z = 0; z < WIDTH; z++) { - for (int x = 0; x < WIDTH; x++) { - WakeNode node = this.get(x, z); + int nodeRes = drawContext.nodeResolution; + for (int nodeZ = 0; nodeZ < WIDTH; nodeZ++) { + for (int nodeX = 0; nodeX < WIDTH; nodeX++) { + WakeNode node = this.get(nodeX, nodeZ); int lightCol = LightTexture.FULL_BRIGHT; int fluidColor = 0; float opacity = 0; @@ -172,21 +165,18 @@ public void populatePixels() { opacity = (float) ((-Math.pow(node.t, 2) + 1) * WakesConfig.wakeOpacity); } - // TODO MASS SET PIXELS TO NO COLOR IF NODE DOESNT EXIST (NEED TO REORDER PIXELS STORED?) - long nodeOffset = texRes * 4L * (((long) z * WIDTH * texRes) + (long) x); - for (int r = 0; r < texRes; r++) { - for (int c = 0; c < texRes; c++) { + int xOffset = nodeX * nodeRes; + int yOffset = nodeZ * nodeRes; + for (int x = 0; x < nodeRes; x++) { + for (int y = 0; y < nodeRes; y++) { int color = 0; if (node != null) { - // TODO USE SHADERS TO COLOR THE WAKES? - color = node.simulationNode.getPixelColor(c, r, fluidColor, lightCol, opacity); + color = node.simulationNode.getPixelColor(x, y, fluidColor, lightCol, opacity); } - long pixelOffset = 4L * (((long) r * WIDTH * texRes) + c); - MemoryUtil.memPutInt(imgPtr + nodeOffset + pixelOffset, color); + drawContext.draw(x + xOffset, y + yOffset, color); } } } } - hasPopulatedPixels = true; } } diff --git a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java index 5b3b2d6..77dc497 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java @@ -4,6 +4,7 @@ import com.goby56.wakes.config.enums.Resolution; import com.goby56.wakes.particle.custom.SplashPlaneParticle; import com.goby56.wakes.render.FrustumManager; +import com.goby56.wakes.render.WakeTextureAtlas; import net.minecraft.client.Minecraft; import net.minecraft.world.level.Level; @@ -18,8 +19,7 @@ public class WakeHandler { private final ArrayList splashPlanes; public static Resolution resolution = WakesConfig.wakeResolution; - - public static boolean resolutionResetScheduled = false; + private Map textureAtlases; private WakeHandler(Level world) { this.world = world; @@ -48,16 +48,19 @@ public static void kill() { public void tick() { if (WakesConfig.wakeResolution.res != WakeHandler.resolution.res) { - scheduleResolutionChange(WakesConfig.wakeResolution); - } - if (resolutionResetScheduled) { - toBeInserted.clear(); + WakeHandler.resolution = WakesConfig.wakeResolution; + reset(); + } else { + wakeLogic(); } + } + private void wakeLogic() { ArrayList toBeRemovedChunks = new ArrayList<>(); for (WakeChunk chunk : wakeChunks.values()) { boolean wakesPresent = chunk.tick(); if (!wakesPresent) { + chunk.destroy(); toBeRemovedChunks.add(chunk.chunkPos); } } @@ -81,9 +84,7 @@ public void tick() { this.splashPlanes.remove(i); } } - if (resolutionResetScheduled) { - this.changeResolution(); - } + } public WakeChunk getChunk(WakeChunkPos pos) { @@ -92,7 +93,7 @@ public WakeChunk getChunk(WakeChunkPos pos) { public void recolorWakes() { for (WakeChunk chunk : wakeChunks.values()) { - chunk.populatePixels(); + chunk.drawWakes(); } for (var splashPlane : this.splashPlanes) { if (splashPlane != null) { @@ -106,8 +107,6 @@ public void registerSplashPlane(SplashPlaneParticle splashPlane) { } public void insert(WakeNode node) { - if (resolutionResetScheduled) return; - if (node.validPos(world)) { this.toBeInserted.add(node); } @@ -143,17 +142,21 @@ public List getVisibleSplashPlanes() { return splashPlanes; } - public static void scheduleResolutionChange(Resolution newRes) { - resolutionResetScheduled = true; - } - - private void changeResolution() { - this.reset(); - WakeHandler.resolution = WakesConfig.wakeResolution; - resolutionResetScheduled = false; + public WakeTextureAtlas getActiveTextureAtlas() { + if (textureAtlases == null) { + this.textureAtlases = Map.of( + Resolution.EIGHT, new WakeTextureAtlas(Resolution.EIGHT.res), + Resolution.SIXTEEN, new WakeTextureAtlas(Resolution.SIXTEEN.res), + Resolution.THIRTYTWO, new WakeTextureAtlas(Resolution.THIRTYTWO.res) + ); + } + return textureAtlases.get(resolution); } private void reset() { + for (WakeChunk chunk : wakeChunks.values()) { + chunk.destroy(); + } wakeChunks.clear(); toBeInserted.clear(); } From 9694b112c004798134e36b9719f6891c20fed1e1 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sun, 1 Feb 2026 18:11:05 +0100 Subject: [PATCH 09/15] fixed nodes clustering at edges of wake chunks --- .../java/com/goby56/wakes/WakesClient.java | 1 - .../goby56/wakes/debug/WakeDebugRenderer.java | 6 --- .../wakes/render/BetterDynamicTexture.java | 5 ++- .../com/goby56/wakes/render/WakeRenderer.java | 13 +++--- .../goby56/wakes/render/WakeTextureAtlas.java | 5 ++- .../goby56/wakes/simulation/WakeChunk.java | 7 +++- .../com/goby56/wakes/simulation/WakeNode.java | 40 +++++++++---------- 7 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/goby56/wakes/WakesClient.java b/src/main/java/com/goby56/wakes/WakesClient.java index 2c7e051..5695f52 100644 --- a/src/main/java/com/goby56/wakes/WakesClient.java +++ b/src/main/java/com/goby56/wakes/WakesClient.java @@ -71,7 +71,6 @@ public void onInitializeClient() { wakeRenderer = new WakeRenderer(); WorldRenderEvents.END_MAIN.register(wakeRenderer); WorldRenderEvents.END_MAIN.register(new SplashPlaneRenderer()); - // HudElementRegistry.addLast(Identifier.fromNamespaceAndPath(MOD_ID, "wakes_texture_atlas"), WakeDebugRenderer.wakeAtlasHudLayer()); SplashPlaneRenderer.initSplashPlane(); DebugScreenEntries.register( diff --git a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java index fa0ea3e..b010859 100644 --- a/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java +++ b/src/main/java/com/goby56/wakes/debug/WakeDebugRenderer.java @@ -32,10 +32,4 @@ public static void addDebugGizmos() { } } } - - public static HudElement wakeAtlasHudLayer() { - return (guiGraphics, deltaTracker) -> { - - }; - } } diff --git a/src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java b/src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java index 80dfe33..f3d0f1c 100644 --- a/src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java +++ b/src/main/java/com/goby56/wakes/render/BetterDynamicTexture.java @@ -12,6 +12,7 @@ public class BetterDynamicTexture extends AbstractTexture { private NativeImage pixels; + public boolean dirty = true; public BetterDynamicTexture(Supplier supplier, NativeImage nativeImage) { this.pixels = nativeImage; @@ -25,9 +26,11 @@ private void createTexture(Supplier supplier) { this.textureView = gpuDevice.createTextureView(this.texture); } - public void upload() { + public void uploadIfDirty() { + if (!dirty) return; if (this.texture != null) { RenderSystem.getDevice().createCommandEncoder().writeToTexture(this.texture, this.pixels); + dirty = false; } else { WakesClient.LOGGER.warn("Trying to upload disposed texture {}", this.getTexture().getLabel()); } diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index 40e1a85..2fc823c 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -2,14 +2,12 @@ import com.goby56.wakes.WakesClient; import com.goby56.wakes.config.WakesConfig; -import com.goby56.wakes.config.enums.Resolution; import com.goby56.wakes.simulation.WakeChunk; import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.simulation.WakeNode; import com.goby56.wakes.debug.WakesDebugInfo; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.RenderPass; @@ -26,7 +24,6 @@ import net.minecraft.client.renderer.MappableRingBuffer; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.client.renderer.texture.DynamicTexture; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -62,10 +59,13 @@ public void endMain(WorldRenderContext context) { if (wakeChunks.isEmpty()) return; + long tRendering = System.nanoTime(); + if (buffer == null) { buffer = new BufferBuilder(allocator, PIPELINE.getVertexFormatMode(), PIPELINE.getVertexFormat()); } + PoseStack matrices = context.matrices(); Vec3 camera = context.worldState().cameraRenderState.pos; @@ -83,12 +83,15 @@ public void endMain(WorldRenderContext context) { GpuBuffer vertices = uploadMesh(drawParameters, format, builtBuffer); BetterDynamicTexture texture = wakeHandler.getActiveTextureAtlas().dynamicTexture; - texture.upload(); + texture.uploadIfDirty(); draw(builtBuffer, drawParameters, vertices, format, texture.getTextureView()); // Rotate the vertex buffer so we are less likely to use buffers that the GPU is using vertexBuffer.rotate(); buffer = null; + + WakesDebugInfo.renderingTime.add(System.nanoTime() - tRendering); + WakesDebugInfo.quadsRendered = wakeChunks.size(); } private void addVertices(Matrix4fc matrix, BufferBuilder bb, List chunks) { @@ -179,7 +182,6 @@ private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBu OptionalInt.empty(), client.getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { - long tRendering = System.nanoTime(); pass.setPipeline(PIPELINE); RenderSystem.bindDefaultUniforms(pass); @@ -195,7 +197,6 @@ private void draw(MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBu //noinspection ConstantValue pass.drawIndexed(0 / format.getVertexSize(), 0, drawParameters.indexCount(), 1); - WakesDebugInfo.renderingTime.add(System.nanoTime() - tRendering); } builtBuffer.close(); diff --git a/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java index b0ae4a0..7dbf0b3 100644 --- a/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java +++ b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java @@ -84,9 +84,10 @@ public void draw(int x, int y, int color) { } int r = subTextureIndex / CHUNKS_PER_ROW; int c = subTextureIndex % CHUNKS_PER_ROW; - int globX = x + c * CHUNKS_PER_ROW; - int globY = y + r * CHUNKS_PER_ROW; + int globX = x + c * atlas.chunkResolution; + int globY = y + r * atlas.chunkResolution; this.atlas.nativeImage.setPixel(globX, globY, color); + this.atlas.dynamicTexture.dirty = true; } } } diff --git a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java index 69e4cc7..65a82c6 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java @@ -18,11 +18,12 @@ public class WakeChunk { private final WakeHandler wakeHandler; - public static final int WIDTH = 16; + public static final int WIDTH = 8; private final WakeNode[][] nodes; public final int capacity; public int occupied = 0; + private boolean destroyed = false; public final Vec3 pos; public final WakeChunkPos chunkPos; @@ -99,7 +100,8 @@ public WakeNode get(int x, int z) { } private Optional getNeighbor(WakeChunkPos.Direction direction) { - if (neighbors.get(direction) == null) { + WakeChunk chunk = neighbors.get(direction); + if (chunk == null || chunk.destroyed) { neighbors.put(direction, wakeHandler.getChunk(chunkPos.offset(direction))); } return Optional.ofNullable(neighbors.get(direction)); @@ -138,6 +140,7 @@ public void destroy() { } } occupied = 0; + destroyed = true; drawContext.invalidate(); } diff --git a/src/main/java/com/goby56/wakes/simulation/WakeNode.java b/src/main/java/com/goby56/wakes/simulation/WakeNode.java index 8fb0f84..dd3ed58 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeNode.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeNode.java @@ -187,9 +187,9 @@ public String toString() { public static class Factory { public static Set splashNodes(Entity entity, int y) { int res = WakeHandler.resolution.res; - int w = (int) (0.8 * entity.getBbWidth() * res / 2); - int x = (int) (entity.getX() * res); - int z = (int) (entity.getZ() * res); + int w = (int) Math.floor(0.8 * entity.getBbWidth() * res / 2); + int x = (int) Math.floor(entity.getX() * res); + int z = (int) Math.floor(entity.getZ() * res); ArrayList pixelsAffected = new ArrayList<>(); for (int i = -w; i < w; i++) { @@ -225,10 +225,10 @@ public static Set rowingNodes(AbstractBoat boat, int y) { public static Set nodeTrail(double fromX, double fromZ, double toX, double toZ, int y, float waveStrength, double velocity) { int res = WakeHandler.resolution.res; - int x1 = (int) (fromX * res); - int z1 = (int) (fromZ * res); - int x2 = (int) (toX * res); - int z2 = (int) (toZ * res); + int x1 = (int) Math.floor(fromX * res); + int z1 = (int) Math.floor(fromZ * res); + int x2 = (int) Math.floor(toX * res); + int z2 = (int) Math.floor(toZ * res); ArrayList pixelsAffected = new ArrayList<>(); WakesUtils.bresenhamLine(x1, z1, x2, z2, pixelsAffected); @@ -237,10 +237,10 @@ public static Set nodeTrail(double fromX, double fromZ, double toX, do public static Set thickNodeTrail(double fromX, double fromZ, double toX, double toZ, int y, float waveStrength, double velocity, float width) { int res = WakeHandler.resolution.res; - int x1 = (int) (fromX * res); - int z1 = (int) (fromZ * res); - int x2 = (int) (toX * res); - int z2 = (int) (toZ * res); + int x1 = (int) Math.floor(fromX * res); + int z1 = (int) Math.floor(fromZ * res); + int x2 = (int) Math.floor(toX * res); + int z2 = (int) Math.floor(toZ * res); int w = (int) (0.8 * width * res / 2); // TODO MAKE MORE EFFICIENT THICK LINE DRAWER @@ -249,7 +249,7 @@ public static Set thickNodeTrail(double fromX, double fromZ, double to float nz = (x2 - x1) / len; ArrayList pixelsAffected = new ArrayList<>(); for (int i = -w; i < w; i++) { - WakesUtils.bresenhamLine((int) (x1 + nx * i), (int) (z1 + nz * i), (int) (x2 + nx * i), (int) (z2 + nz * i), pixelsAffected); + WakesUtils.bresenhamLine((int) Math.floor(x1 + nx * i), (int) Math.floor(z1 + nz * i), (int) Math.floor(x2 + nx * i), (int) Math.floor(z2 + nz * i), pixelsAffected); } return pixelsToNodes(pixelsAffected, y, waveStrength, velocity); } @@ -259,12 +259,12 @@ public static Set nodeLine(double x, int y, double z, float waveStreng Vec3 dir = velocity.normalize(); double nx = -dir.z; double nz = dir.x; - int w = (int) (0.8 * width * res / 2); + int w = (int) Math.floor(0.8 * width * res / 2); - int x1 = (int) (x * res - nx * w); - int z1 = (int) (z * res - nz * w); - int x2 = (int) (x * res + nx * w); - int z2 = (int) (z * res + nz * w); + int x1 = (int) Math.floor(x * res - nx * w); + int z1 = (int) Math.floor(z * res - nz * w); + int x2 = (int) Math.floor(x * res + nx * w); + int z2 = (int) Math.floor(z * res + nz * w); ArrayList pixelsAffected = new ArrayList<>(); WakesUtils.bresenhamLine(x1, z1, x2, z2, pixelsAffected); @@ -273,13 +273,13 @@ public static Set nodeLine(double x, int y, double z, float waveStreng private static Set pixelsToNodes(ArrayList pixelsAffected, int y, float waveStrength, double velocity) { int res = WakeHandler.resolution.res; - int power = (int) (Math.log(res) / Math.log(2)); + int power = WakeHandler.resolution.power; HashMap> pixelsInNodes = new HashMap<>(); for (Long pixel : pixelsAffected) { int[] pos = WakesUtils.longAsPos(pixel); long k = WakesUtils.posAsLong(pos[0] >> power, pos[1] >> power); - pos[0] %= res; - pos[1] %= res; + pos[0] = Math.floorMod(pos[0], res); + pos[1] = Math.floorMod(pos[1], res); long v = WakesUtils.posAsLong(pos[0], pos[1]); if (pixelsInNodes.containsKey(k)) { pixelsInNodes.get(k).add(v); From eba1f31ff7ff47cef9f6db056ef30f1b6eac3cef Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sun, 1 Feb 2026 19:26:56 +0100 Subject: [PATCH 10/15] wake color fixed but splash plane now reversed color --- src/main/java/com/goby56/wakes/render/WakeColor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/goby56/wakes/render/WakeColor.java b/src/main/java/com/goby56/wakes/render/WakeColor.java index 7da9719..92d5e40 100644 --- a/src/main/java/com/goby56/wakes/render/WakeColor.java +++ b/src/main/java/com/goby56/wakes/render/WakeColor.java @@ -64,7 +64,7 @@ public static int sampleColor(float waveEqAvg, int fluidCol, int lightColor, flo } } WakeColor color = WakesConfig.getWakeColor(returnIndex); - return color.blend(tint, lightColor, opacity).abgr; + return color.blend(tint, lightColor, opacity).argb; } public WakeColor modifyOpacity(float opacityMultiplier) { From 5b4aaa3e17157db62d441523f533c088d421c20b Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sun, 22 Feb 2026 15:27:02 +0100 Subject: [PATCH 11/15] use one texture atlas and subspace into it --- .../goby56/wakes/config/enums/Resolution.java | 4 ++ .../com/goby56/wakes/render/WakeRenderer.java | 6 +- .../goby56/wakes/render/WakeTextureAtlas.java | 56 ++++++++++++------- .../goby56/wakes/simulation/WakeChunk.java | 4 +- .../goby56/wakes/simulation/WakeHandler.java | 16 +++--- 5 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/goby56/wakes/config/enums/Resolution.java b/src/main/java/com/goby56/wakes/config/enums/Resolution.java index 0066aba..8bf0324 100644 --- a/src/main/java/com/goby56/wakes/config/enums/Resolution.java +++ b/src/main/java/com/goby56/wakes/config/enums/Resolution.java @@ -15,6 +15,10 @@ public enum Resolution { this.power = Mth.log2(res); } + public static Resolution getHighest() { + return THIRTYTWO; + } + @Override public String toString() { return String.valueOf(this.res); diff --git a/src/main/java/com/goby56/wakes/render/WakeRenderer.java b/src/main/java/com/goby56/wakes/render/WakeRenderer.java index 2fc823c..d592bb2 100644 --- a/src/main/java/com/goby56/wakes/render/WakeRenderer.java +++ b/src/main/java/com/goby56/wakes/render/WakeRenderer.java @@ -82,7 +82,7 @@ public void endMain(WorldRenderContext context) { GpuBuffer vertices = uploadMesh(drawParameters, format, builtBuffer); - BetterDynamicTexture texture = wakeHandler.getActiveTextureAtlas().dynamicTexture; + BetterDynamicTexture texture = wakeHandler.getTextureAtlas().dynamicTexture; texture.uploadIfDirty(); draw(builtBuffer, drawParameters, vertices, format, texture.getTextureView()); @@ -96,8 +96,8 @@ public void endMain(WorldRenderContext context) { private void addVertices(Matrix4fc matrix, BufferBuilder bb, List chunks) { for (WakeChunk wakeChunk : chunks) { - UVPair uv = wakeChunk.drawContext.uv; - float uvOffset = wakeChunk.drawContext.uvOffset; + UVPair uv = wakeChunk.drawContext.getUV(); + float uvOffset = wakeChunk.drawContext.getUVOffset(); float x0 = (float) wakeChunk.pos.x; float y = (float) (wakeChunk.pos.y + WakeNode.WATER_OFFSET); diff --git a/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java index 7dbf0b3..46b7892 100644 --- a/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java +++ b/src/main/java/com/goby56/wakes/render/WakeTextureAtlas.java @@ -1,7 +1,9 @@ package com.goby56.wakes.render; import com.goby56.wakes.WakesClient; +import com.goby56.wakes.config.enums.Resolution; import com.goby56.wakes.simulation.WakeChunk; +import com.goby56.wakes.simulation.WakeHandler; import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.platform.NativeImage; @@ -10,15 +12,19 @@ import net.minecraft.client.model.geom.builders.UVPair; import net.minecraft.client.renderer.texture.DynamicTexture; +import java.util.Arrays; import java.util.function.Supplier; public class WakeTextureAtlas { public static final int CHUNKS_PER_ROW = 16; - public final int nodeResolution; - public final int chunkResolution; public final int resolution; + public int nodeResolution; + public int chunkResolution; + public int effectiveResolution; + public float chunkUVOffset; + protected final NativeImage nativeImage; public final BetterDynamicTexture dynamicTexture; @@ -26,17 +32,24 @@ public class WakeTextureAtlas { private static final int CAPACITY = CHUNKS_PER_ROW * CHUNKS_PER_ROW - 1; // Include error texture - public WakeTextureAtlas(int wakeNodeRes) { - nodeResolution = wakeNodeRes; - chunkResolution = wakeNodeRes * WakeChunk.WIDTH; - resolution = chunkResolution * CHUNKS_PER_ROW; + public WakeTextureAtlas() { + resolution = Resolution.getHighest().res * WakeChunk.WIDTH * CHUNKS_PER_ROW; nativeImage = new NativeImage(resolution, resolution, false); - Supplier name = () -> String.format("%s %dx%x texture atlas (%d%d wakes)", - WakesClient.MOD_ID, resolution, resolution, nodeResolution, nodeResolution); + Supplier name = () -> String.format("%s %dx%x texture atlas", + WakesClient.MOD_ID, resolution, resolution); dynamicTexture = new BetterDynamicTexture(name, nativeImage); } + public void setResolution(int wakeNodeRes) { + nodeResolution = wakeNodeRes; + chunkResolution = wakeNodeRes * WakeChunk.WIDTH; + effectiveResolution = chunkResolution * CHUNKS_PER_ROW; + chunkUVOffset = chunkResolution / (float) resolution; + + Arrays.fill(occupiedSubTextures, false); + } + public DrawContext claimSubTexture() { for (int i = 0; i < CAPACITY; i++) { if (!occupiedSubTextures[i]) { @@ -56,20 +69,16 @@ public static class DrawContext { private final int subTextureIndex; private final WakeTextureAtlas atlas; - public final UVPair uv; - public final float uvOffset; - public final int nodeResolution; + public final int row; + public final int column; public DrawContext(int subTextureIndex, WakeTextureAtlas atlas) { this.subTextureIndex = subTextureIndex; this.active = true; this.atlas = atlas; - int r = subTextureIndex / CHUNKS_PER_ROW; - int c = subTextureIndex % CHUNKS_PER_ROW; - this.uv = new UVPair(c / (float) CHUNKS_PER_ROW, r / (float) CHUNKS_PER_ROW); - this.uvOffset = atlas.chunkResolution / (float) atlas.resolution; - this.nodeResolution = atlas.nodeResolution; + this.row = subTextureIndex / CHUNKS_PER_ROW; + this.column = subTextureIndex % CHUNKS_PER_ROW; } public void invalidate() { @@ -77,15 +86,22 @@ public void invalidate() { this.active = false; } + public float getUVOffset() { + return atlas.chunkUVOffset; + } + + public UVPair getUV() { + float uvOffset = atlas.chunkUVOffset; + return new UVPair(column * uvOffset, row * uvOffset); + } + public void draw(int x, int y, int color) { if (!active) { return; //throw new IllegalAccessException("Wake texture atlas draw context has been invalidated and cannot be drawn to"); } - int r = subTextureIndex / CHUNKS_PER_ROW; - int c = subTextureIndex % CHUNKS_PER_ROW; - int globX = x + c * atlas.chunkResolution; - int globY = y + r * atlas.chunkResolution; + int globX = x + column * atlas.chunkResolution; + int globY = y + row * atlas.chunkResolution; this.atlas.nativeImage.setPixel(globX, globY, color); this.atlas.dynamicTexture.dirty = true; } diff --git a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java index 65a82c6..63d67bf 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeChunk.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeChunk.java @@ -42,7 +42,7 @@ public WakeChunk(WakeChunkPos chunkPos, WakeHandler wakeHandler) { this.neighbors = new HashMap<>(); this.wakeHandler = wakeHandler; - this.drawContext = wakeHandler.getActiveTextureAtlas().claimSubTexture(); + this.drawContext = wakeHandler.getTextureAtlas().claimSubTexture(); } public boolean tick() { @@ -154,7 +154,7 @@ private List getAdjacentNodes(int x, int z) { public void drawWakes() { Level world = Minecraft.getInstance().level; - int nodeRes = drawContext.nodeResolution; + int nodeRes = WakeHandler.resolution.res; for (int nodeZ = 0; nodeZ < WIDTH; nodeZ++) { for (int nodeX = 0; nodeX < WIDTH; nodeX++) { WakeNode node = this.get(nodeX, nodeZ); diff --git a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java index 77dc497..094c37a 100644 --- a/src/main/java/com/goby56/wakes/simulation/WakeHandler.java +++ b/src/main/java/com/goby56/wakes/simulation/WakeHandler.java @@ -19,7 +19,7 @@ public class WakeHandler { private final ArrayList splashPlanes; public static Resolution resolution = WakesConfig.wakeResolution; - private Map textureAtlases; + private WakeTextureAtlas textureAtlas; private WakeHandler(Level world) { this.world = world; @@ -49,6 +49,7 @@ public static void kill() { public void tick() { if (WakesConfig.wakeResolution.res != WakeHandler.resolution.res) { WakeHandler.resolution = WakesConfig.wakeResolution; + textureAtlas.setResolution(resolution.res); reset(); } else { wakeLogic(); @@ -142,15 +143,12 @@ public List getVisibleSplashPlanes() { return splashPlanes; } - public WakeTextureAtlas getActiveTextureAtlas() { - if (textureAtlases == null) { - this.textureAtlases = Map.of( - Resolution.EIGHT, new WakeTextureAtlas(Resolution.EIGHT.res), - Resolution.SIXTEEN, new WakeTextureAtlas(Resolution.SIXTEEN.res), - Resolution.THIRTYTWO, new WakeTextureAtlas(Resolution.THIRTYTWO.res) - ); + public WakeTextureAtlas getTextureAtlas() { + if (textureAtlas == null) { + textureAtlas = new WakeTextureAtlas(); + textureAtlas.setResolution(resolution.res); } - return textureAtlases.get(resolution); + return textureAtlas; } private void reset() { From 8f4e9dd9768b345477f689a5aabb1ea8b69ea7fd Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sun, 22 Feb 2026 16:59:28 +0100 Subject: [PATCH 12/15] correct lighting on wakes --- .../mixin/LightmapTextureManagerMixin.java | 77 ++++++++----- .../com/goby56/wakes/render/LightmapInfo.java | 15 ++- .../goby56/wakes/render/LightmapWrapper.java | 103 +++++++++--------- .../com/goby56/wakes/render/WakeColor.java | 11 +- .../wakes/simulation/SimulationNode.java | 4 +- 5 files changed, 125 insertions(+), 85 deletions(-) diff --git a/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java b/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java index e24c7a3..1b16126 100644 --- a/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java +++ b/src/main/java/com/goby56/wakes/mixin/LightmapTextureManagerMixin.java @@ -2,13 +2,16 @@ import com.goby56.wakes.duck.LightmapAccess; import com.goby56.wakes.render.LightmapInfo; -import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.EndFlashState; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.world.entity.LivingEntity; +import net.minecraft.util.ARGB; +import net.minecraft.world.attribute.EnvironmentAttributeProbe; +import net.minecraft.world.attribute.EnvironmentAttributes; import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.LivingEntity; import org.joml.Vector3f; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -22,47 +25,71 @@ public abstract class LightmapTextureManagerMixin implements LightmapAccess { @Shadow private float blockLightRedFlicker; @Shadow @Final private Minecraft minecraft; + @Shadow @Final private GameRenderer renderer; + @Shadow protected abstract float calculateDarknessScale(LivingEntity entity, float factor, float tickProgress); @Unique private int currentTick; - @Shadow protected abstract float calculateDarknessScale(LivingEntity entity, float factor, float tickProgress); - - @Shadow @Final private GameRenderer renderer; @Unique private LightmapInfo info; @Inject(method = "updateLightTexture", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoder;createRenderPass(Ljava/util/function/Supplier;Lcom/mojang/blaze3d/textures/GpuTextureView;Ljava/util/OptionalInt;)Lcom/mojang/blaze3d/systems/RenderPass;")) - private void wakes$onUpdate(float tickProgress, CallbackInfo ci, @Local Vector3f skyColor) { + private void wakes$onUpdate(float tickProgress, CallbackInfo ci) { ClientLevel world = this.minecraft.level; - float f = world.getSkyDarken(); - float g; - if (world.skyFlashTime > 0) { - g = 1.0F; - } else { - g = f * 0.95F + 0.05F; + if (world == null || this.minecraft.player == null) { + return; } - float k = this.minecraft.player.getWaterVision(); + EnvironmentAttributeProbe probe = this.minecraft.gameRenderer.getMainCamera().attributeProbe(); + int skyLightColorRgb = ((Integer) probe.getValue(EnvironmentAttributes.SKY_LIGHT_COLOR, tickProgress)).intValue(); + float skyFactor = ((Float) probe.getValue(EnvironmentAttributes.SKY_LIGHT_FACTOR, tickProgress)).floatValue(); - float l; - if (this.minecraft.player.hasEffect(MobEffects.NIGHT_VISION)) { - l = GameRenderer.getNightVisionScale(this.minecraft.player, tickProgress); - } else if (k > 0.0F && this.minecraft.player.hasEffect(MobEffects.CONDUIT_POWER)) { - l = k; + EndFlashState endFlashState = world.endFlashState(); + Vector3f ambientColor; + if (endFlashState != null) { + ambientColor = new Vector3f(0.99f, 1.12f, 1.0f); + if (!this.minecraft.options.hideLightningFlash().get()) { + float intensity = endFlashState.getIntensity(tickProgress); + if (this.minecraft.gui.getBossOverlay().shouldCreateWorldFog()) { + skyFactor += intensity / 3.0f; + } else { + skyFactor += intensity; + } + } } else { - l = 0.0F; + ambientColor = new Vector3f(1.0f, 1.0f, 1.0f); } - float h = this.minecraft.options.darknessEffectScale().get().floatValue(); - float i = this.minecraft.player.getEffectBlendFactor(MobEffects.DARKNESS, tickProgress) * h; - float j = this.calculateDarknessScale(this.minecraft.player, i, tickProgress) * h; + float darknessScaleSetting = this.minecraft.options.darknessEffectScale().get().floatValue(); + float darknessBlend = this.minecraft.player.getEffectBlendFactor(MobEffects.DARKNESS, tickProgress) * darknessScaleSetting; + float darknessScale = this.calculateDarknessScale(this.minecraft.player, darknessBlend, tickProgress) * darknessScaleSetting; - float o = this.minecraft.options.gamma().get().floatValue(); + float waterVision = this.minecraft.player.getWaterVision(); + float nightVisionFactor; + if (this.minecraft.player.hasEffect(MobEffects.NIGHT_VISION)) { + nightVisionFactor = GameRenderer.getNightVisionScale(this.minecraft.player, tickProgress); + } else if (waterVision > 0.0f && this.minecraft.player.hasEffect(MobEffects.CONDUIT_POWER)) { + nightVisionFactor = waterVision; + } else { + nightVisionFactor = 0.0f; + } + float blockFactor = this.blockLightRedFlicker + 1.5f; + float brightnessFactor = Math.max(0.0f, this.minecraft.options.gamma().get().floatValue() - darknessBlend); - info = new LightmapInfo(world.dimensionType().ambientLight(), g, this.blockLightRedFlicker + 1.5f, - false, l, j, this.renderer.getDarkenWorldAmount(tickProgress), Math.max(0.0F, o - i), skyColor, currentTick++); + this.info = new LightmapInfo( + world.dimensionType().ambientLight(), + skyFactor, + blockFactor, + nightVisionFactor, + darknessScale, + this.renderer.getDarkenWorldAmount(tickProgress), + brightnessFactor, + ARGB.vector3fFromRGB24(skyLightColorRgb), + ambientColor, + currentTick++ + ); } @Override diff --git a/src/main/java/com/goby56/wakes/render/LightmapInfo.java b/src/main/java/com/goby56/wakes/render/LightmapInfo.java index 414383b..debf35f 100644 --- a/src/main/java/com/goby56/wakes/render/LightmapInfo.java +++ b/src/main/java/com/goby56/wakes/render/LightmapInfo.java @@ -2,7 +2,16 @@ import org.joml.Vector3f; -public record LightmapInfo(float AmbientLightFactor, float SkyFactor, float BlockFactor, boolean UseBrightLightmap, float NightVisionFactor, - float DarknessScale, float DarkenWorldFactor, float BrightnessFactor, Vector3f skyLightColor, - int currentTick) { +public record LightmapInfo( + float ambientLightFactor, + float skyFactor, + float blockFactor, + float nightVisionFactor, + float darknessScale, + float darkenWorldFactor, + float brightnessFactor, + Vector3f skyLightColor, + Vector3f ambientColor, + int currentTick +) { } diff --git a/src/main/java/com/goby56/wakes/render/LightmapWrapper.java b/src/main/java/com/goby56/wakes/render/LightmapWrapper.java index a606013..7096279 100644 --- a/src/main/java/com/goby56/wakes/render/LightmapWrapper.java +++ b/src/main/java/com/goby56/wakes/render/LightmapWrapper.java @@ -8,86 +8,89 @@ import org.joml.Vector3f; public class LightmapWrapper { - private static int[][] color = new int[16][16]; - private static int[][] tick = new int[16][16]; + private static final int[][] color = new int[16][16]; + private static final int[][] tick = new int[16][16]; private static float mix(float x, float y, float a) { return x * (1.0f - a) + y * a; } - private static float get_brightness(float level, float AmbientLightFactor) { - float curved_level = level / (4.0f - 3.0f * level); - return mix(curved_level, 1.0f, AmbientLightFactor); + private static float getBrightness(float level) { + return level / (4.0f - 3.0f * level); } - public static Vector3f notGamma(Vector3f v) { - float nx0 = 1.0f - v.x; - float nx1 = 1.0f - v.y; - float nx2 = 1.0f - v.z; - return new Vector3f( - 1.0f - nx0 * nx0 * nx0 * nx0, - 1.0f - nx1 * nx1 * nx1 * nx1, - 1.0f - nx2 * nx2 * nx2 * nx2); + // Mirrors assets/minecraft/shaders/core/lightmap.fsh + private static Vector3f notGamma(Vector3f color) { + float maxComponent = Math.max(color.x, Math.max(color.y, color.z)); + if (maxComponent <= 0.0f) { + return new Vector3f(0.0f, 0.0f, 0.0f); + } + + float maxInverted = 1.0f - maxComponent; + float maxScaled = 1.0f - maxInverted * maxInverted * maxInverted * maxInverted; + float scale = maxScaled / maxComponent; + return new Vector3f(color).mul(scale); } private static Vector3f mix3(Vector3f color, Vector3f c2, float v) { - return new Vector3f(mix(color.x, c2.x, v), + return new Vector3f( + mix(color.x, c2.x, v), mix(color.y, c2.y, v), - mix(color.z, c2.z, v)); + mix(color.z, c2.z, v) + ); + } + + private static Vector3f clamp3(Vector3f color, float min, float max) { + return color.set( + Mth.clamp(color.x, min, max), + Mth.clamp(color.y, min, max), + Mth.clamp(color.z, min, max) + ); } private static int calculatePixel(LightmapInfo info, int block, int sky) { - float block_brightness = get_brightness(block / 15.0f, info.AmbientLightFactor()) * info.BlockFactor(); - float sky_brightness = get_brightness(sky / 15.0f, info.AmbientLightFactor()) * info.SkyFactor(); + float blockBrightness = getBrightness(block / 15.0f) * info.blockFactor(); + float skyBrightness = getBrightness(sky / 15.0f) * info.skyFactor(); // cubic nonsense, dips to yellowish in the middle, white when fully saturated - Vector3f color = new Vector3f( - block_brightness, - block_brightness * ((block_brightness * 0.6f + 0.4f) * 0.6f + 0.4f), - block_brightness * (block_brightness * block_brightness * 0.6f + 0.4f)); - - if (info.UseBrightLightmap()) { - color = mix3(color, new Vector3f(0.99f, 1.12f, 1.0f), 0.25f); - color = clamp3(color, 0.0, 1.0); - } else { - color.add(info.skyLightColor().mul(sky_brightness, new Vector3f())); - color = mix3(color, new Vector3f(0.75f), 0.04f); - - Vector3f darkened_color = color.mul(new Vector3f(0.7f, 0.6f, 0.6f), new Vector3f()); - color = mix3(color, darkened_color, info.DarkenWorldFactor()); + Vector3f c = new Vector3f( + blockBrightness, + blockBrightness * ((blockBrightness * 0.6f + 0.4f) * 0.6f + 0.4f), + blockBrightness * (blockBrightness * blockBrightness * 0.6f + 0.4f) + ); + + c = mix3(c, info.ambientColor(), info.ambientLightFactor()); + c.add(new Vector3f(info.skyLightColor()).mul(skyBrightness)); + c = mix3(c, new Vector3f(0.75f), 0.04f); + + if (info.ambientLightFactor() == 0.0f) { + Vector3f darkened = new Vector3f(c).mul(0.7f, 0.6f, 0.6f); + c = mix3(c, darkened, info.darkenWorldFactor()); } - if (info.NightVisionFactor() > 0.0) { - // scale up uniformly until 1.0 is hit by one of the colors - float max_component = Math.max(color.x, Math.max(color.y, color.z)); - if (max_component < 1.0) { - Vector3f bright_color = color.div(max_component, new Vector3f()); - color = mix3(color, bright_color, info.NightVisionFactor()); + if (info.nightVisionFactor() > 0.0f) { + float maxComponent = Math.max(c.x, Math.max(c.y, c.z)); + if (maxComponent > 0.0f && maxComponent < 1.0f) { + Vector3f bright = new Vector3f(c).div(maxComponent); + c = mix3(c, bright, info.nightVisionFactor()); } } - if (!info.UseBrightLightmap()) { - color = clamp3(color.sub(new Vector3f(info.DarknessScale()), new Vector3f()), 0.0, 1.0); + if (info.ambientLightFactor() == 0.0f) { + c.sub(info.darknessScale(), info.darknessScale(), info.darknessScale()); } - Vector3f notGamma = notGamma(color); - color = mix3(color, notGamma, info.BrightnessFactor()); - color = mix3(color, new Vector3f(0.75f), 0.04f); - color = clamp3(color, 0.0, 1.0); - - return ARGB.colorFromFloat(1.0f, color.x, color.y, color.z); - } + c = clamp3(c, 0.0f, 1.0f); + c = mix3(c, notGamma(c), info.brightnessFactor()); + c = mix3(c, new Vector3f(0.75f), 0.04f); - private static Vector3f clamp3(Vector3f color, double v, double v1) { - return color.set(Mth.clamp(color.x, v, v1), Mth.clamp(color.y, v, v1), - Mth.clamp(color.z, v, v1)); + return ARGB.colorFromFloat(1.0f, c.x, c.y, c.z); } public static int readPixel(int block, int sky) { LightmapInfo info = ((LightmapAccess) Minecraft.getInstance().gameRenderer.lightTexture()) .wakes$getLightmapInfo(); - // Added null check to avoid NullPointerException if info is null if (info == null) { return color[block][sky]; } diff --git a/src/main/java/com/goby56/wakes/render/WakeColor.java b/src/main/java/com/goby56/wakes/render/WakeColor.java index 92d5e40..a53d3db 100644 --- a/src/main/java/com/goby56/wakes/render/WakeColor.java +++ b/src/main/java/com/goby56/wakes/render/WakeColor.java @@ -52,7 +52,7 @@ private static double invertedLogisticCurve(float x) { return WakesClient.areShadersEnabled ? k * (4 * Math.pow(x - 0.5f, 3) + 0.5f) : x; } - public static int sampleColor(float waveEqAvg, int fluidCol, int lightColor, float opacity) { + public static WakeColor sampleColor(float waveEqAvg, int fluidCol, int lightColor, float opacity) { WakeColor tint = new WakeColor(fluidCol); double clampedRange = 1 / (1 + Math.exp(-0.1 * waveEqAvg)); var ranges = WakesConfig.wakeColorIntervals; @@ -64,7 +64,7 @@ public static int sampleColor(float waveEqAvg, int fluidCol, int lightColor, flo } } WakeColor color = WakesConfig.getWakeColor(returnIndex); - return color.blend(tint, lightColor, opacity).argb; + return color.blend(tint, lightColor, opacity); } public WakeColor modifyOpacity(float opacityMultiplier) { @@ -79,9 +79,10 @@ public WakeColor blend(WakeColor tint, int lightColor, float opacity) { int g = (int) ((this.g) * (srcA) + (tint.g) * (1 - srcA)); int b = (int) ((this.b) * (srcA) + (tint.b) * (1 - srcA)); - r = (int) ((r * invertedLogisticCurve((lightColor >> 16 & 0xFF) / 255f))); - g = (int) ((g * invertedLogisticCurve((lightColor >> 8 & 0xFF) / 255f))); - b = (int) ((b * invertedLogisticCurve((lightColor & 0xFF) / 255f))); + // Lighting is baked directly into wake/splash pixels. + r = (int) (r * invertedLogisticCurve((lightColor >> 16 & 0xFF) / 255f)); + g = (int) (g * invertedLogisticCurve((lightColor >> 8 & 0xFF) / 255f)); + b = (int) (b * invertedLogisticCurve((lightColor & 0xFF) / 255f)); return new WakeColor(r, g, b, (int) (this.a * opacity)); } diff --git a/src/main/java/com/goby56/wakes/simulation/SimulationNode.java b/src/main/java/com/goby56/wakes/simulation/SimulationNode.java index ef9c439..d39890a 100644 --- a/src/main/java/com/goby56/wakes/simulation/SimulationNode.java +++ b/src/main/java/com/goby56/wakes/simulation/SimulationNode.java @@ -32,9 +32,9 @@ public int getPixelColor(int x, int z, int fluidCol, int lightCol, float opacity float waveEqAvg = (this.u[0][z + 1][x + 1] + this.u[1][z + 1][x + 1] + this.u[2][z + 1][x + 1]) / 3; if (WakesConfig.debugColors) { int clampedRange = (int) (255 * (2 / (1 + Math.exp(-0.1 * waveEqAvg)) - 1)); - return new WakeColor(Math.max(-clampedRange, 0), Math.max(clampedRange, 0), 0, 255).abgr; + return new WakeColor(Math.max(-clampedRange, 0), Math.max(clampedRange, 0), 0, 255).argb; } - return WakeColor.sampleColor(waveEqAvg, fluidCol, lightCol, opacity); + return WakeColor.sampleColor(waveEqAvg, fluidCol, lightCol, opacity).argb; } public abstract void tick(@Nullable Float velocity, @Nullable SimulationNode NORTH, @Nullable SimulationNode SOUTH, @Nullable SimulationNode EAST, @Nullable SimulationNode WEST); From 80cae958ba2f793ff5f10b2b81a73659417557cb Mon Sep 17 00:00:00 2001 From: Goby56 Date: Sun, 22 Feb 2026 19:53:13 +0100 Subject: [PATCH 13/15] moved from opengl for splash planes --- .../particle/custom/SplashPlaneParticle.java | 35 ++++++------ .../wakes/render/SplashPlaneRenderer.java | 54 +++++++++++-------- .../wakes/render/SplashPlaneTexture.java | 26 ++++----- 3 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java b/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java index 0fac8d4..5bcd2ac 100644 --- a/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java +++ b/src/main/java/com/goby56/wakes/particle/custom/SplashPlaneParticle.java @@ -9,19 +9,22 @@ import com.goby56.wakes.utils.WakesUtils; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.client.renderer.BiomeColors; -import net.minecraft.client.particle.*; import net.minecraft.client.Camera; -import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.particle.SpriteSet; +import net.minecraft.client.renderer.BiomeColors; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.core.particles.SimpleParticleType; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; -import net.minecraft.core.particles.SimpleParticleType; import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; import java.util.Random; @@ -34,7 +37,7 @@ public class SplashPlaneParticle extends Particle { private final SimulationNode simulationNode = new SimulationNode.SplashPlaneSimulation(); - public long imgPtr = -1; + public NativeImage image; public int texRes; public boolean hasPopulatedPixels = false; @@ -78,7 +81,6 @@ public void tick() { } private void aliveTick(ProducesWake wakeProducer) { - // Vec3d vel = wakeProducer.wakes$getNumericalVelocity(); // UNCOMMENT IF WEIRD SPLASH BEHAVIOR Vec3 vel = this.owner.getDeltaMovement(); if (this.owner instanceof AbstractBoat) { this.yaw = -this.owner.getYRot(); @@ -104,30 +106,31 @@ private void aliveTick(ProducesWake wakeProducer) { } public void initTexture(int res) { - long size = 4L * res * res; - if (imgPtr == -1) { - imgPtr = MemoryUtil.nmemAlloc(size); - } else { - imgPtr = MemoryUtil.nmemRealloc(imgPtr, size); + if (this.image != null) { + this.image.close(); } + this.image = new NativeImage(res, res, false); this.texRes = res; this.hasPopulatedPixels = false; } public void deallocTexture() { - MemoryUtil.nmemFree(imgPtr); + if (this.image != null) { + this.image.close(); + this.image = null; + } } public void populatePixels() { int fluidColor = BiomeColors.getAverageWaterColor(level, this.owner.blockPosition()); int lightCol = WakesUtils.getLightColor(level, this.owner.blockPosition()); - float opacity = WakesConfig.wakeOpacity * 0.9f; int res = WakeHandler.resolution.res; + float opacity = WakesConfig.wakeOpacity * 0.9f; for (int r = 0; r < res; r++) { for (int c = 0; c < res; c++) { - long pixelOffset = 4L * (((long) r * res) + c); - MemoryUtil.memPutInt(imgPtr + pixelOffset, simulationNode.getPixelColor(c, r, fluidColor, lightCol, opacity)); + int color = simulationNode.getPixelColor(c, r, fluidColor, lightCol, opacity); + this.image.setPixel(c, r, color); } } this.hasPopulatedPixels = true; diff --git a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java index 554b6a7..d04bc2d 100644 --- a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java +++ b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java @@ -8,9 +8,10 @@ import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.utils.WakesUtils; import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.GpuSampler; import com.mojang.blaze3d.vertex.*; import io.github.jdiemke.triangulation.DelaunayTriangulator; import io.github.jdiemke.triangulation.NotEnoughPointsException; @@ -26,7 +27,11 @@ import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.OptionalDouble; +import java.util.OptionalInt; public class SplashPlaneRenderer implements WorldRenderEvents.EndMain { private static ArrayList points; @@ -79,18 +84,15 @@ public static void render(T entity, SplashPlaneParticle splas matrices.scale(scalar, scalar, scalar); Matrix4f matrix = matrices.last().pose(); - wakeTextures.get(WakeHandler.resolution).loadTexture(splashPlane.imgPtr, GlConst.GL_RGBA); - renderSurface(matrix); + SplashPlaneTexture texture = wakeTextures.get(WakeHandler.resolution); + texture.loadTexture(splashPlane.image); + renderSurface(matrix, texture); matrices.popPose(); } - private static void renderSurface(Matrix4f matrix) { + private static void renderSurface(Matrix4f matrix, SplashPlaneTexture splashTexture) { BufferBuilder bb = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.BLOCK); - // TODO IMPROVE ANIMATION (WATER TRAVELS IN AN OUTWARDS DIRECTION) - // AND ADD A BOUNCY FEEL TO IT (BOBBING UP AND DOWN) WAIT IT IS JUST THE BOAT THAT IS DOING THAT - // MAYBE ADD TO BLAZINGLY FAST BOATS? - // https://streamable.com/tz0gp for (int s = -1; s < 2; s++) { if (s == 0) continue; for (int i = 0; i < vertices.size(); i++) { @@ -108,16 +110,23 @@ private static void renderSurface(Matrix4f matrix) { } MeshData built = bb.buildOrThrow(); - - GpuBuffer buffer = DefaultVertexFormat.BLOCK.uploadImmediateVertexBuffer(built.vertexBuffer()); - GpuBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.TRIANGLES).getBuffer(built.drawState().indexCount()); - try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(() -> "Splash Plane", Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { + GpuBuffer vertexBuffer = DefaultVertexFormat.BLOCK.uploadImmediateVertexBuffer(built.vertexBuffer()); + RenderSystem.AutoStorageIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.TRIANGLES); + GpuBuffer indexBuffer = shapeIndexBuffer.getBuffer(built.drawState().indexCount()); + + GpuSampler sampler = RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST); + try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( + () -> "Splash Plane", + Minecraft.getInstance().getMainRenderTarget().getColorTextureView(), + OptionalInt.empty(), + Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), + OptionalDouble.empty())) { pass.setPipeline(WakesClient.SPLASH_PLANE_PIPELINE); - // pass.bindSampler("Sampler0", RenderSystem.getShaderTexture(0)); - // pass.bindSampler("Sampler2", RenderSystem.getShaderTexture(2)); RenderSystem.bindDefaultUniforms(pass); - pass.setVertexBuffer(0, buffer); - pass.setIndexBuffer(indices, RenderSystem.getSequentialBuffer(VertexFormat.Mode.TRIANGLES).type()); + pass.bindTexture("Sampler0", splashTexture.getTextureView(), sampler); + pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); + pass.setVertexBuffer(0, vertexBuffer); + pass.setIndexBuffer(indexBuffer, shapeIndexBuffer.type()); pass.drawIndexed(0, 0, built.drawState().indexCount(), 1); } built.close(); @@ -132,7 +141,7 @@ private static double lowerBound(double x) { } private static double height(double x, double y) { - return 4 * (x * (SQRT_8 - x) -y - x * x) / SQRT_8; + return 4 * (x * (SQRT_8 - x) - y - x * x) / SQRT_8; } private static Vec3 normal(double x, double y) { @@ -148,9 +157,9 @@ private static void distributePoints() { for (float i = 0; i < res; i++) { double x = i / (res - 1); double h = upperBound(x) - lowerBound(x); - int n_points = (int) Math.max(1, Math.floor(h * res)); - for (float j = 0; j < n_points + 1; j++) { - float y = (float) ((j / n_points) * h + lowerBound(x)); + int nPoints = (int) Math.max(1, Math.floor(h * res)); + for (float j = 0; j < nPoints + 1; j++) { + float y = (float) ((j / nPoints) * h + lowerBound(x)); points.add(new Vector2D(x, y)); } } @@ -168,7 +177,8 @@ private static void generateMesh() { } for (Triangle2D tri : triangles) { for (Vector2D vec : new Vector2D[] {tri.a, tri.b, tri.c}) { - double x = vec.x, y = vec.y; + double x = vec.x; + double y = vec.y; vertices.add(new Vec3(x, y, height(x, y))); normals.add(normal(x, y)); } diff --git a/src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java b/src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java index 13e39cf..1211f85 100644 --- a/src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java +++ b/src/main/java/com/goby56/wakes/render/SplashPlaneTexture.java @@ -1,16 +1,14 @@ package com.goby56.wakes.render; import com.goby56.wakes.WakesClient; -import com.mojang.blaze3d.opengl.GlConst; -import com.mojang.blaze3d.opengl.GlStateManager; +import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.GpuTexture; import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.blaze3d.textures.TextureFormat; -import com.mojang.blaze3d.opengl.GlTexture; public class SplashPlaneTexture { - public GpuTexture texture; + private final GpuTexture texture; private final GpuTextureView textureView; public final int resolution; @@ -18,21 +16,19 @@ public SplashPlaneTexture(int resolution) { this.resolution = resolution; this.texture = RenderSystem.getDevice().createTexture(() -> WakesClient.MOD_ID + " splash plane texture", - GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING | GpuTexture.USAGE_RENDER_ATTACHMENT, + GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING, TextureFormat.RGBA8, resolution, resolution, 1, 1); - // texture.setTextureFilter(FilterMode.NEAREST, false); this.textureView = RenderSystem.getDevice().createTextureView(texture); } - public void loadTexture(long imgPtr, int glFormat) { - GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); - GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); - - GlStateManager._bindTexture(((GlTexture) texture).glId()); - GlStateManager._texSubImage2D(GlConst.GL_TEXTURE_2D, 0,0,0,resolution, resolution, glFormat, GlConst.GL_UNSIGNED_BYTE, imgPtr); + public void loadTexture(NativeImage image) { + if (image == null) { + return; + } + RenderSystem.getDevice().createCommandEncoder().writeToTexture(this.texture, image); + } - RenderSystem.outputColorTextureOverride = textureView; + public GpuTextureView getTextureView() { + return this.textureView; } } From 2c0deb238c06f19663957f9e84fe40621c25fbce Mon Sep 17 00:00:00 2001 From: Goby56 Date: Mon, 23 Feb 2026 21:04:40 +0100 Subject: [PATCH 14/15] splash planes now render on shaders splash planes now emit degenerate quads instead of triangles, making them compatible with quad render pipelines. with shaders splash planes seem to render fine using TRANSLUCENT_MOVING_BLOCK pipeline but on vanilla it needs BEACON_BEAM_TRANSLUCENT to not clip particles and clouds behind the planes so now the pipelines are chosen depending on if shaders are on or not A more permanent solution could be writing a shader that works for both but then iris needs to support custom shaders Also need to fix wakes appearing on both sides (below water too) --- .../java/com/goby56/wakes/WakesClient.java | 15 +--- .../wakes/render/SplashPlaneRenderer.java | 69 ++++++++++++++----- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/goby56/wakes/WakesClient.java b/src/main/java/com/goby56/wakes/WakesClient.java index 5695f52..fef3e80 100644 --- a/src/main/java/com/goby56/wakes/WakesClient.java +++ b/src/main/java/com/goby56/wakes/WakesClient.java @@ -8,11 +8,7 @@ import com.goby56.wakes.particle.ModParticles; import com.goby56.wakes.render.SplashPlaneRenderer; import com.goby56.wakes.render.WakeRenderer; -import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.VertexFormat; import eu.midnightdust.lib.config.MidnightConfig; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; @@ -40,15 +36,6 @@ public class WakesClient implements ClientModInitializer { .withFragmentShader(Identifier.fromNamespaceAndPath("wakes", "gui_hsv")) .build() ); - public static final RenderPipeline SPLASH_PLANE_PIPELINE = RenderPipelines.register( - RenderPipeline.builder(RenderPipelines.BEACON_BEAM_SNIPPET) - .withLocation("pipeline/beacon_beam_translucent") - .withVertexFormat(DefaultVertexFormat.BLOCK, VertexFormat.Mode.TRIANGLES) - .withDepthWrite(false) - .withCull(false) - .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) - .withBlend(BlendFunction.TRANSLUCENT) - .build()); public static WakeRenderer wakeRenderer; @@ -84,4 +71,4 @@ public static boolean areShadersEnabled() { } return false; } -} \ No newline at end of file +} diff --git a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java index d04bc2d..b02f9c8 100644 --- a/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java +++ b/src/main/java/com/goby56/wakes/render/SplashPlaneRenderer.java @@ -8,6 +8,7 @@ import com.goby56.wakes.simulation.WakeHandler; import com.goby56.wakes.utils.WakesUtils; import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.FilterMode; @@ -22,6 +23,8 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.rendertype.RenderType; import net.minecraft.world.entity.Entity; import com.mojang.math.Axis; import net.minecraft.world.phys.Vec3; @@ -34,6 +37,8 @@ import java.util.OptionalInt; public class SplashPlaneRenderer implements WorldRenderEvents.EndMain { + private static final ByteBufferBuilder allocator = new ByteBufferBuilder(RenderType.BIG_BUFFER_SIZE); + private static ArrayList points; private static List triangles; private static ArrayList vertices; @@ -51,6 +56,14 @@ Resolution.THIRTYTWO, new SplashPlaneTexture(Resolution.THIRTYTWO.res) private static final double SQRT_8 = Math.sqrt(8); + private static RenderPipeline getPipeline() { + if (WakesClient.areShadersEnabled) { + return RenderPipelines.TRANSLUCENT_MOVING_BLOCK; + } else { + return RenderPipelines.BEACON_BEAM_TRANSLUCENT; + } + } + @Override public void endMain(WorldRenderContext context) { if (WakeHandler.getInstance().isEmpty()) { @@ -92,27 +105,28 @@ public static void render(T entity, SplashPlaneParticle splas } private static void renderSurface(Matrix4f matrix, SplashPlaneTexture splashTexture) { - BufferBuilder bb = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.BLOCK); + RenderPipeline pipeline = getPipeline(); + BufferBuilder bb = Tesselator.getInstance().begin(pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); for (int s = -1; s < 2; s++) { if (s == 0) continue; - for (int i = 0; i < vertices.size(); i++) { - Vec3 vertex = vertices.get(i); - Vec3 normal = normals.get(i); - bb.addVertex(matrix, - (float) (s * (vertex.x * WakesConfig.splashPlaneWidth + WakesConfig.splashPlaneGap)), - (float) (vertex.z * WakesConfig.splashPlaneHeight), - (float) (vertex.y * WakesConfig.splashPlaneDepth)) - .setUv((float) (vertex.x), (float) (vertex.y)) - .setLight(LightTexture.FULL_BRIGHT) - .setColor(1f, 1f, 1f, 1f) - .setNormal((float) normal.x, (float) normal.y, (float) normal.z); + for (int i = 0; i < vertices.size(); i += 3) { + Vec3 v0 = vertices.get(i); + Vec3 n0 = normals.get(i); + Vec3 v1 = vertices.get(i + 1); + Vec3 n1 = normals.get(i + 1); + Vec3 v2 = vertices.get(i + 2); + Vec3 n2 = normals.get(i + 2); + addDegenerateQuad(bb, matrix, s, v0, n0, v1, n1, v2, n2); + addDegenerateQuad(bb, matrix, s, v0, n0, v2, n2, v1, n1); } } MeshData built = bb.buildOrThrow(); - GpuBuffer vertexBuffer = DefaultVertexFormat.BLOCK.uploadImmediateVertexBuffer(built.vertexBuffer()); - RenderSystem.AutoStorageIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.TRIANGLES); - GpuBuffer indexBuffer = shapeIndexBuffer.getBuffer(built.drawState().indexCount()); + MeshData.DrawState drawState = built.drawState(); + VertexFormat format = drawState.format(); + GpuBuffer vertexBuffer = format.uploadImmediateVertexBuffer(built.vertexBuffer()); + built.sortQuads(allocator, RenderSystem.getProjectionType().vertexSorting()); + GpuBuffer indexBuffer = pipeline.getVertexFormat().uploadImmediateIndexBuffer(built.indexBuffer()); GpuSampler sampler = RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST); try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( @@ -121,17 +135,34 @@ private static void renderSurface(Matrix4f matrix, SplashPlaneTexture splashText OptionalInt.empty(), Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) { - pass.setPipeline(WakesClient.SPLASH_PLANE_PIPELINE); + pass.setPipeline(pipeline); RenderSystem.bindDefaultUniforms(pass); pass.bindTexture("Sampler0", splashTexture.getTextureView(), sampler); - pass.bindTexture("Sampler2", Minecraft.getInstance().gameRenderer.lightTexture().getTextureView(), sampler); pass.setVertexBuffer(0, vertexBuffer); - pass.setIndexBuffer(indexBuffer, shapeIndexBuffer.type()); - pass.drawIndexed(0, 0, built.drawState().indexCount(), 1); + pass.setIndexBuffer(indexBuffer, drawState.indexType()); + pass.drawIndexed(0 / format.getVertexSize(), 0, drawState.indexCount(), 1); } built.close(); } + private static void addVertex(BufferBuilder bb, Matrix4f matrix, int side, Vec3 vertex, Vec3 normal) { + bb.addVertex(matrix, + (float) (side * (vertex.x * WakesConfig.splashPlaneWidth + WakesConfig.splashPlaneGap)), + (float) (vertex.z * WakesConfig.splashPlaneHeight), + (float) (vertex.y * WakesConfig.splashPlaneDepth)) + .setUv((float) vertex.x, (float) vertex.y) + .setLight(LightTexture.FULL_BRIGHT) + .setColor(1f, 1f, 1f, 1f) + .setNormal((float) normal.x, (float) normal.y, (float) normal.z); + } + + private static void addDegenerateQuad(BufferBuilder bb, Matrix4f matrix, int side, Vec3 a, Vec3 an, Vec3 b, Vec3 bn, Vec3 c, Vec3 cn) { + addVertex(bb, matrix, side, a, an); + addVertex(bb, matrix, side, b, bn); + addVertex(bb, matrix, side, c, cn); + addVertex(bb, matrix, side, c, cn); + } + private static double upperBound(double x) { return - 2 * x * x + SQRT_8 * x; } From f930e14bead6e001dec781e4e306d60589197700 Mon Sep 17 00:00:00 2001 From: Goby56 Date: Tue, 24 Feb 2026 21:51:58 +0100 Subject: [PATCH 15/15] reasonably large change --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 83bb576..abf4337 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ loom_version=1.14-SNAPSHOT fabric_version=0.139.5+1.21.11 # Mod Properties -mod_version=0.4.4+1.21.11 +mod_version=0.5.0+1.21.11 maven_group=com.goby56.wakes archives_base_name=wakes