From 89b864a1426ac90f1bccfb75b824864bb3652a3b Mon Sep 17 00:00:00 2001 From: piexel Date: Mon, 19 Oct 2026 18:12:46 +0200 Subject: [PATCH] Show both halves of two block tall plants in decorator previews A decorator places a two-part plant like tall grass as a bottom half with its top half above it, but the preview only drew the bottom half. It now squeezes both halves into the one block, and the inspector says so. Model used: Claude Opus 5.5 --- .../iris/core/gui/BiomePalettePlacement.java | 9 ++++ .../volmit/iris/core/gui/BlockTextures.java | 44 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/core/gui/BiomePalettePlacement.java b/core/src/main/java/com/volmit/iris/core/gui/BiomePalettePlacement.java index e6f932e8a..8110c8b51 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/BiomePalettePlacement.java +++ b/core/src/main/java/com/volmit/iris/core/gui/BiomePalettePlacement.java @@ -30,6 +30,7 @@ import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.noise.CNG; +import org.bukkit.block.data.Bisected; import java.util.ArrayList; import java.util.List; @@ -293,6 +294,9 @@ abstract class BiomePalettePlacement { if (sharesSpotsWithOtherDecorators) { notes.add("Where other decorators of this biome could place too, the world picks one of them at random."); } + if (placesTwoBlockTallPlants()) { + notes.add("Two block tall plants show both halves squeezed into one block."); + } if (decorator.isStacking()) { notes.add("It stacks " + decorator.getStackMin() + "–" + decorator.getStackMax() + " blocks high; the preview shows the bottom block."); @@ -303,6 +307,11 @@ abstract class BiomePalettePlacement { return notes.isEmpty() ? null : String.join(" ", notes); } + // The generator places any two-part block as a bottom half with its top half above it. + private boolean placesTwoBlockTallPlants() { + return !decorator.isStacking() && blocks().stream().anyMatch(block -> block.getBlockData(data) instanceof Bisected); + } + private static String partNote(IrisDecorationPart part) { return switch (part) { case NONE -> null; diff --git a/core/src/main/java/com/volmit/iris/core/gui/BlockTextures.java b/core/src/main/java/com/volmit/iris/core/gui/BlockTextures.java index b020c699c..79dfdd80c 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/BlockTextures.java +++ b/core/src/main/java/com/volmit/iris/core/gui/BlockTextures.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.gui; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import org.bukkit.block.data.Bisected; import org.bukkit.block.data.BlockData; import java.awt.Graphics2D; @@ -85,9 +86,24 @@ final class BlockTextures { /** * Like {@link #topTexture}, but plants keep their sprite, so single decorations on the ground stay recognisable. + * Two block tall plants show both halves squeezed into one block. */ Optional iconTexture(BlockData block) { - return resolve(block).map(ResolvedTexture::texture); + if (!(block instanceof Bisected)) { + return resolve(block).map(ResolvedTexture::texture); + } + Optional bottom = resolve(withHalf(block, Bisected.Half.BOTTOM)); + Optional top = resolve(withHalf(block, Bisected.Half.TOP)); + if (bottom.isEmpty() || top.isEmpty() || !bottom.get().isSideOnSprite() || !top.get().isSideOnSprite()) { + return resolve(block).map(ResolvedTexture::texture); + } + return Optional.of(BlockTexture.stacked(top.get().texture(), bottom.get().texture())); + } + + private static BlockData withHalf(BlockData block, Bisected.Half half) { + BlockData copy = block.clone(); + ((Bisected) copy).setHalf(half); + return copy; } // Blockstate files match on every property, so defaults the pack left out have to be spelled out too. @@ -415,6 +431,32 @@ final class BlockTextures { return new BlockTexture(size, pixels, opaque, average, biomeTint); } + /** + * The top texture above the bottom one, each at half height, or just the bottom when their sizes differ. + */ + static BlockTexture stacked(BlockTexture top, BlockTexture bottom) { + if (top.size != bottom.size) { + return bottom; + } + int size = bottom.size; + int[] pixels = new int[size * size]; + boolean[] opaque = new boolean[size * size]; + for (int z = 0; z < size; z++) { + BlockTexture half = z < size / 2 ? top : bottom; + int sourceRow = (z * 2) % size; + System.arraycopy(half.pixels, sourceRow * size, pixels, z * size, size); + System.arraycopy(half.opaque, sourceRow * size, opaque, z * size, size); + } + return new BlockTexture(size, pixels, opaque, averageOf(top.averageColor, bottom.averageColor), bottom.biomeTint); + } + + private static int averageOf(int first, int second) { + int red = (((first >>> 16) & 0xFF) + ((second >>> 16) & 0xFF)) / 2; + int green = (((first >>> 8) & 0xFF) + ((second >>> 8) & 0xFF)) / 2; + int blue = ((first & 0xFF) + (second & 0xFF)) / 2; + return red << 16 | green << 8 | blue; + } + BlockTexture flattened() { int[] flat = new int[pixels.length]; Arrays.fill(flat, averageColor);