From 6f0a2b0e5643c82fb14af731bd82fb2309b2df13 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 12 Nov 2024 13:09:52 -0800 Subject: [PATCH 1/3] Reticulated splines - Fix line models rendering garbage - Fix shulker boxes appear at the origin before snapping into place - Make the box and line models public --- .../dev/engine_room/flywheel/lib/model/LineModelBuilder.java | 3 ++- .../flywheel/lib/visual/component/HitboxComponent.java | 4 ++-- .../dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java b/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java index 5e93d92ee..0689e8097 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java @@ -80,6 +80,7 @@ public final class LineModelBuilder { public Model build() { vertexView.vertexCount(vertexCount); + vertexView.nativeMemoryOwner(data); var boundingSphere = ModelUtil.computeBoundingSphere(vertexView); boundingSphere.w += 0.1f; // make the bounding sphere a little bigger to account for line width @@ -137,7 +138,7 @@ public final class LineModelBuilder { @Override public void write(MutableVertexList vertexList) { - vertexList.writeAll(vertexList); + this.vertexList.writeAll(vertexList); } @Override diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/visual/component/HitboxComponent.java b/common/src/lib/java/dev/engine_room/flywheel/lib/visual/component/HitboxComponent.java index 834410ed5..8cebf06a3 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/visual/component/HitboxComponent.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/visual/component/HitboxComponent.java @@ -25,7 +25,7 @@ public final class HitboxComponent implements EntityComponent { // | / | / // |/ |/ // 001------101 - private static final Model BOX_MODEL = new LineModelBuilder(12) + public static final Model BOX_MODEL = new LineModelBuilder(12) // Starting from 0, 0, 0 .line(0, 0, 0, 0, 0, 1) .line(0, 0, 0, 0, 1, 0) @@ -44,7 +44,7 @@ public final class HitboxComponent implements EntityComponent { .line(1, 1, 0, 0, 1, 0) .build(); - private static final Model LINE_MODEL = new LineModelBuilder(1) + public static final Model LINE_MODEL = new LineModelBuilder(1) .line(0, 0, 0, 0, 2, 0) .build(); diff --git a/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java b/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java index 0c7abb853..8f8953b60 100644 --- a/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java +++ b/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java @@ -53,6 +53,7 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual Date: Tue, 12 Nov 2024 13:33:30 -0800 Subject: [PATCH 2/3] Effective debugging - Add light storage debug view courtesy of effect visuals - Yellow boxes are filled in sections - Red/green/blue bars represent the LUT - Cyan boxes are the "wasted space" in the LUT. Freely representable sections that are simply not filled in --- .../flywheel/backend/engine/LightLut.java | 67 +++++-- .../flywheel/backend/engine/LightStorage.java | 167 +++++++++++++++++- .../flywheel/impl/FlwCommands.java | 101 ++++++----- .../flywheel/impl/FlwCommands.java | 100 ++++++----- 4 files changed, 340 insertions(+), 95 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java index 885c616dd..ffe0b2b4d 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java @@ -11,7 +11,7 @@ import net.minecraft.core.SectionPos; // Massive kudos to RogueLogix for figuring out this LUT scheme. // First layer is Y, then X, then Z. public final class LightLut { - private final Layer> indices = new Layer<>(); + public final Layer> indices = new Layer<>(); public void add(long position, int index) { final var x = SectionPos.x(position); @@ -49,7 +49,7 @@ public final class LightLut { return out; } - private static final class Layer { + public static final class Layer { private boolean hasBase = false; private int base = 0; private Object[] nextLayer = new Object[0]; @@ -79,23 +79,34 @@ public final class LightLut { } } + public int base() { + return base; + } + + public int size() { + return nextLayer.length; + } + + @Nullable + public T getRaw(int i) { + if (i < 0) { + return null; + } + + if (i >= nextLayer.length) { + return null; + } + + return (T) nextLayer[i]; + } + @Nullable public T get(int i) { if (!hasBase) { return null; } - if (i < base) { - return null; - } - - final var offset = i - base; - - if (offset >= nextLayer.length) { - return null; - } - - return (T) nextLayer[offset]; + return getRaw(i - base); } public T computeIfAbsent(int i, Supplier ifAbsent) { @@ -142,7 +153,7 @@ public final class LightLut { } } - private static final class IntLayer { + public static final class IntLayer { private boolean hasBase = false; private int base = 0; private int[] indices = new int[0]; @@ -156,6 +167,34 @@ public final class LightLut { } } + public int base() { + return base; + } + + public int size() { + return indices.length; + } + + public int getRaw(int i) { + if (i < 0) { + return 0; + } + + if (i >= indices.length) { + return 0; + } + + return indices[i]; + } + + public int get(int i) { + if (!hasBase) { + return 0; + } + + return getRaw(i - base); + } + public void set(int i, int index) { if (!hasBase) { base = i; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java index f8b91be89..9b3f779f7 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java @@ -6,15 +6,25 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil; import dev.engine_room.flywheel.api.task.Plan; +import dev.engine_room.flywheel.api.visual.Effect; +import dev.engine_room.flywheel.api.visual.EffectVisual; +import dev.engine_room.flywheel.api.visualization.VisualizationContext; +import dev.engine_room.flywheel.api.visualization.VisualizationManager; import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer; import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer; +import dev.engine_room.flywheel.lib.instance.InstanceTypes; +import dev.engine_room.flywheel.lib.instance.TransformedInstance; import dev.engine_room.flywheel.lib.math.MoreMath; import dev.engine_room.flywheel.lib.task.SimplePlan; +import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual; +import dev.engine_room.flywheel.lib.visual.component.HitboxComponent; +import dev.engine_room.flywheel.lib.visual.util.InstanceRecycler; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.Long2IntMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.client.renderer.LightTexture; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.world.level.LevelAccessor; @@ -35,7 +45,9 @@ import net.minecraft.world.level.lighting.LayerLightEventListener; * *

Thus, each section occupies 5832 bytes. */ -public class LightStorage { +public class LightStorage implements Effect { + public static boolean DEBUG = false; + public static final int BLOCKS_PER_SECTION = 18 * 18 * 18; public static final int LIGHT_SIZE_BYTES = BLOCKS_PER_SECTION; public static final int SOLID_SIZE_BYTES = MoreMath.ceilingDiv(BLOCKS_PER_SECTION, Integer.SIZE) * Integer.BYTES; @@ -50,6 +62,7 @@ public class LightStorage { private final BitSet changed = new BitSet(); private boolean needsLutRebuild = false; + private boolean isDebugOn = false; private final LongSet updatedSections = new LongOpenHashSet(); @Nullable @@ -63,6 +76,16 @@ public class LightStorage { section2ArenaIndex.defaultReturnValue(INVALID_SECTION); } + @Override + public LevelAccessor level() { + return level; + } + + @Override + public EffectVisual visualize(VisualizationContext ctx, float partialTick) { + return new DebugVisual(ctx, partialTick); + } + /** * Set the set of requested sections. *

When set, this will be processed in the next frame plan. It may not be set every frame. @@ -79,6 +102,22 @@ public class LightStorage { public Plan createFramePlan() { return SimplePlan.of(() -> { + if (DEBUG != isDebugOn) { + var visualizationManager = VisualizationManager.get(level); + + // Really should be non-null, but just in case. + if (visualizationManager != null) { + if (DEBUG) { + visualizationManager.effects() + .queueAdd(this); + } else { + visualizationManager.effects() + .queueRemove(this); + } + } + isDebugOn = DEBUG; + } + if (updatedSections.isEmpty() && requestedSections == null) { return; } @@ -442,4 +481,130 @@ public class LightStorage { this.sectionOffset = sectionOffset; } } + + public class DebugVisual implements EffectVisual, SimpleDynamicVisual { + + private final InstanceRecycler boxes; + + public DebugVisual(VisualizationContext ctx, float partialTick) { + boxes = new InstanceRecycler<>(() -> ctx.instancerProvider() + .instancer(InstanceTypes.TRANSFORMED, HitboxComponent.BOX_MODEL) + .createInstance()); + } + + @Override + public void beginFrame(Context ctx) { + boxes.resetCount(); + + setupSectionBoxes(); + setupLutRangeBoxes(); + + boxes.discardExtra(); + } + + private void setupSectionBoxes() { + section2ArenaIndex.keySet() + .forEach(l -> { + var x = SectionPos.x(l); + var y = SectionPos.y(l); + var z = SectionPos.z(l); + + var instance = boxes.get(); + + instance.setIdentityTransform() + .scale(16) + .translate(x, y, z) + .color(255, 255, 0) + .light(LightTexture.FULL_BRIGHT) + .setChanged(); + }); + } + + private void setupLutRangeBoxes() { + var first = lut.indices; + + var base1 = first.base(); + var size1 = first.size(); + + for (int y = 0; y < size1; y++) { + var second = first.getRaw(y); + + if (second == null) { + continue; + } + + var base2 = second.base(); + var size2 = second.size(); + + for (int x = 0; x < size2; x++) { + var third = second.getRaw(x); + + if (third == null) { + continue; + } + + var base3 = third.base(); + var size3 = third.size(); + + for (int z = 0; z < size3; z++) { + float x1 = base2 * 16; + float y1 = base1 * 16; + float z1 = base3 * 16; + + float x2 = (base2 + x) * 16 + 7.5f; + float y2 = (base1 + y) * 16 + 7.5f; + float z2 = (base3 + z) * 16 + 7.5f; + boxes.get() + .setIdentityTransform() + .translate(x1, y2, z2) + .scale(size2 * 16, 1, 1) + .color(255, 0, 0) + .light(LightTexture.FULL_BRIGHT) + .setChanged(); + + boxes.get() + .setIdentityTransform() + .translate(x2, y1, z2) + .scale(1, size1 * 16, 1) + .color(0, 255, 0) + .light(LightTexture.FULL_BRIGHT) + .setChanged(); + + boxes.get() + .setIdentityTransform() + .translate(x2, y2, z1) + .scale(1, 1, size3 * 16) + .color(0, 0, 255) + .light(LightTexture.FULL_BRIGHT) + .setChanged(); + + if (third.getRaw(z) == 0) { + float x3 = (base2 + x) * 16 + 6f; + float y3 = (base1 + y) * 16 + 6f; + float z3 = (base3 + z) * 16 + 6f; + + // Freely representable section that is not filled. + boxes.get() + .setIdentityTransform() + .translate(x3, y3, z3) + .scale(4) + .color(0, 255, 255) + .light(LightTexture.FULL_BRIGHT) + .setChanged(); + } + } + } + } + } + + @Override + public void update(float partialTick) { + + } + + @Override + public void delete() { + boxes.delete(); + } + } } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 46817bcaf..45e0703d6 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -10,6 +10,7 @@ import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.backend.compile.LightSmoothness; import dev.engine_room.flywheel.backend.compile.PipelineCompiler; +import dev.engine_room.flywheel.backend.engine.LightStorage; import dev.engine_room.flywheel.backend.engine.uniform.DebugMode; import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; @@ -85,46 +86,6 @@ public final class FlwCommands { return Command.SINGLE_SUCCESS; }))); - command.then(ClientCommandManager.literal("crumbling") - .then(ClientCommandManager.argument("pos", BlockPosArgument.blockPos()) - .then(ClientCommandManager.argument("stage", IntegerArgumentType.integer(0, 9)) - .executes(context -> { - Entity executor = context.getSource() - .getEntity(); - - if (executor == null) { - return 0; - } - - BlockPos pos = getBlockPos(context, "pos"); - int value = IntegerArgumentType.getInteger(context, "stage"); - - executor.level() - .destroyBlockProgress(executor.getId(), pos, value); - - return Command.SINGLE_SUCCESS; - })))); - - command.then(ClientCommandManager.literal("debug") - .then(ClientCommandManager.argument("mode", DebugModeArgument.INSTANCE) - .executes(context -> { - DebugMode mode = context.getArgument("mode", DebugMode.class); - FrameUniforms.debugMode(mode); - return Command.SINGLE_SUCCESS; - }))); - - command.then(ClientCommandManager.literal("frustum") - .then(ClientCommandManager.literal("capture") - .executes(context -> { - FrameUniforms.captureFrustum(); - return Command.SINGLE_SUCCESS; - })) - .then(ClientCommandManager.literal("unpause") - .executes(context -> { - FrameUniforms.unpauseFrustum(); - return Command.SINGLE_SUCCESS; - }))); - command.then(ClientCommandManager.literal("lightSmoothness") .then(ClientCommandManager.argument("mode", LightSmoothnessArgument.INSTANCE) .executes(context -> { @@ -167,9 +128,69 @@ public final class FlwCommands { return Command.SINGLE_SUCCESS; }))); + command.then(createDebugCommand()); + dispatcher.register(command); } + private static LiteralArgumentBuilder createDebugCommand() { + var debug = ClientCommandManager.literal("debug"); + + debug.then(ClientCommandManager.literal("crumbling") + .then(ClientCommandManager.argument("pos", BlockPosArgument.blockPos()) + .then(ClientCommandManager.argument("stage", IntegerArgumentType.integer(0, 9)) + .executes(context -> { + Entity executor = context.getSource() + .getEntity(); + + if (executor == null) { + return 0; + } + + BlockPos pos = getBlockPos(context, "pos"); + int value = IntegerArgumentType.getInteger(context, "stage"); + + executor.level() + .destroyBlockProgress(executor.getId(), pos, value); + + return Command.SINGLE_SUCCESS; + })))); + + debug.then(ClientCommandManager.literal("shader") + .then(ClientCommandManager.argument("mode", DebugModeArgument.INSTANCE) + .executes(context -> { + DebugMode mode = context.getArgument("mode", DebugMode.class); + FrameUniforms.debugMode(mode); + return Command.SINGLE_SUCCESS; + }))); + + debug.then(ClientCommandManager.literal("frustum") + .then(ClientCommandManager.literal("capture") + .executes(context -> { + FrameUniforms.captureFrustum(); + return Command.SINGLE_SUCCESS; + })) + .then(ClientCommandManager.literal("unpause") + .executes(context -> { + FrameUniforms.unpauseFrustum(); + return Command.SINGLE_SUCCESS; + }))); + + debug.then(ClientCommandManager.literal("lightSections") + .then(ClientCommandManager.literal("on") + .executes(context -> { + LightStorage.DEBUG = true; + return Command.SINGLE_SUCCESS; + })) + .then(ClientCommandManager.literal("off") + .executes(context -> { + LightStorage.DEBUG = false; + return Command.SINGLE_SUCCESS; + }))); + + return debug; + } + // Client version of BlockPosArgument.getBlockPos private static BlockPos getBlockPos(CommandContext context, String name) { return context.getArgument(name, Coordinates.class).getBlockPos(context.getSource().getPlayer().createCommandSourceStack()); diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 87a80c76f..0fcb96d35 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -8,6 +8,7 @@ import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.backend.compile.LightSmoothness; import dev.engine_room.flywheel.backend.compile.PipelineCompiler; +import dev.engine_room.flywheel.backend.engine.LightStorage; import dev.engine_room.flywheel.backend.engine.uniform.DebugMode; import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms; import net.minecraft.client.Minecraft; @@ -83,46 +84,6 @@ public final class FlwCommands { return Command.SINGLE_SUCCESS; }))); - command.then(Commands.literal("crumbling") - .then(Commands.argument("pos", BlockPosArgument.blockPos()) - .then(Commands.argument("stage", IntegerArgumentType.integer(0, 9)) - .executes(context -> { - Entity executor = context.getSource() - .getEntity(); - - if (executor == null) { - return 0; - } - - BlockPos pos = BlockPosArgument.getBlockPos(context, "pos"); - int value = IntegerArgumentType.getInteger(context, "stage"); - - executor.level() - .destroyBlockProgress(executor.getId(), pos, value); - - return Command.SINGLE_SUCCESS; - })))); - - command.then(Commands.literal("debug") - .then(Commands.argument("mode", DebugModeArgument.INSTANCE) - .executes(context -> { - DebugMode mode = context.getArgument("mode", DebugMode.class); - FrameUniforms.debugMode(mode); - return Command.SINGLE_SUCCESS; - }))); - - command.then(Commands.literal("frustum") - .then(Commands.literal("capture") - .executes(context -> { - FrameUniforms.captureFrustum(); - return Command.SINGLE_SUCCESS; - })) - .then(Commands.literal("unpause") - .executes(context -> { - FrameUniforms.unpauseFrustum(); - return Command.SINGLE_SUCCESS; - }))); - var lightSmoothnessValue = ForgeFlwConfig.INSTANCE.client.backendConfig.lightSmoothness; command.then(Commands.literal("lightSmoothness") .then(Commands.argument("mode", LightSmoothnessArgument.INSTANCE) @@ -160,9 +121,68 @@ public final class FlwCommands { return Command.SINGLE_SUCCESS; }))); + command.then(createDebugCommand()); + event.getDispatcher().register(command); } + private static LiteralArgumentBuilder createDebugCommand() { + var debug = Commands.literal("debug"); + + debug.then(Commands.literal("crumbling") + .then(Commands.argument("pos", BlockPosArgument.blockPos()) + .then(Commands.argument("stage", IntegerArgumentType.integer(0, 9)) + .executes(context -> { + Entity executor = context.getSource() + .getEntity(); + + if (executor == null) { + return 0; + } + + BlockPos pos = BlockPosArgument.getBlockPos(context, "pos"); + int value = IntegerArgumentType.getInteger(context, "stage"); + + executor.level() + .destroyBlockProgress(executor.getId(), pos, value); + + return Command.SINGLE_SUCCESS; + })))); + + debug.then(Commands.literal("shader") + .then(Commands.argument("mode", DebugModeArgument.INSTANCE) + .executes(context -> { + DebugMode mode = context.getArgument("mode", DebugMode.class); + FrameUniforms.debugMode(mode); + return Command.SINGLE_SUCCESS; + }))); + + debug.then(Commands.literal("frustum") + .then(Commands.literal("capture") + .executes(context -> { + FrameUniforms.captureFrustum(); + return Command.SINGLE_SUCCESS; + })) + .then(Commands.literal("unpause") + .executes(context -> { + FrameUniforms.unpauseFrustum(); + return Command.SINGLE_SUCCESS; + }))); + + debug.then(Commands.literal("lightSections") + .then(Commands.literal("on") + .executes(context -> { + LightStorage.DEBUG = true; + return Command.SINGLE_SUCCESS; + })) + .then(Commands.literal("off") + .executes(context -> { + LightStorage.DEBUG = false; + return Command.SINGLE_SUCCESS; + }))); + return debug; + } + private static void sendMessage(CommandSourceStack source, Component message) { source.sendSuccess(() -> message, true); } From 46fb59289f9e3e7e88f864d98c62de997681c627 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 12 Nov 2024 16:55:20 -0800 Subject: [PATCH 3/3] Weeping shulkers - Properly fix shulker boxes appearing in the wrong location --- .../engine_room/flywheel/vanilla/ShulkerBoxVisual.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java b/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java index 8f8953b60..699d96c9e 100644 --- a/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java +++ b/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java @@ -53,7 +53,7 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual