mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-23 16:44:06 +00:00
Draw the biome's ground under decorator previews
Decorator previews used the dark "nothing here" colour around and between their sprites. They now show the biome's top surface layer there: through the see-through parts of each sprite, and dimmed where the decorator places nothing or places air. Hovering a bare spot says the decorator places nothing there. Model used: Claude Opus 5.5
This commit is contained in:
1 parent
84ffce263a
commit
3c773e3e57
6 files changed
+83
-10
No files matched your search
@@ -324,7 +324,8 @@ final class BlockTextures {
|
||||
}
|
||||
|
||||
// Biome tinted textures keep their grey pixels, because the biome colour changes from column to column.
|
||||
record BlockTexture(int size, int[] pixels, int averageColor, BiomeTint biomeTint) {
|
||||
// See-through pixels get a darker shade of the average colour, and the opaque mask remembers which they were.
|
||||
record BlockTexture(int size, int[] pixels, boolean[] opaque, int averageColor, BiomeTint biomeTint) {
|
||||
private static final int OPAQUE_ALPHA = 128;
|
||||
private static final double GAP_SHADE = 0.55D;
|
||||
|
||||
@@ -363,13 +364,15 @@ final class BlockTextures {
|
||||
pixels[index] = gapColor;
|
||||
}
|
||||
}
|
||||
return new BlockTexture(size, pixels, average, biomeTint);
|
||||
return new BlockTexture(size, pixels, opaque, average, biomeTint);
|
||||
}
|
||||
|
||||
BlockTexture flattened() {
|
||||
int[] flat = new int[pixels.length];
|
||||
Arrays.fill(flat, averageColor);
|
||||
return new BlockTexture(size, flat, averageColor, biomeTint);
|
||||
boolean[] allOpaque = new boolean[pixels.length];
|
||||
Arrays.fill(allOpaque, true);
|
||||
return new BlockTexture(size, flat, allOpaque, averageColor, biomeTint);
|
||||
}
|
||||
|
||||
BlockTexture tinted(int tint) {
|
||||
@@ -377,13 +380,21 @@ final class BlockTextures {
|
||||
for (int index = 0; index < pixels.length; index++) {
|
||||
tintedPixels[index] = multiply(pixels[index], tint);
|
||||
}
|
||||
return new BlockTexture(size, tintedPixels, multiply(averageColor, tint), null);
|
||||
return new BlockTexture(size, tintedPixels, opaque, multiply(averageColor, tint), null);
|
||||
}
|
||||
|
||||
int texelAt(double worldX, double worldZ) {
|
||||
return pixels[texelIndex(worldX, worldZ)];
|
||||
}
|
||||
|
||||
boolean isOpaqueAt(double worldX, double worldZ) {
|
||||
return opaque[texelIndex(worldX, worldZ)];
|
||||
}
|
||||
|
||||
private int texelIndex(double worldX, double worldZ) {
|
||||
int x = Math.min(size - 1, (int) ((worldX - Math.floor(worldX)) * size));
|
||||
int z = Math.min(size - 1, (int) ((worldZ - Math.floor(worldZ)) * size));
|
||||
return pixels[z * size + x];
|
||||
return z * size + x;
|
||||
}
|
||||
|
||||
BufferedImage toImage() {
|
||||
|
||||
@@ -29,6 +29,10 @@ interface NoiseColorScale {
|
||||
|
||||
int colorInvalid();
|
||||
|
||||
default int colorInvalidAt(double worldX, double worldZ) {
|
||||
return colorInvalid();
|
||||
}
|
||||
|
||||
Color displayColorNormalized(double normalized);
|
||||
|
||||
String label();
|
||||
|
||||
@@ -1305,7 +1305,9 @@ public final class NoiseExplorerGUI extends JPanel {
|
||||
private static String describePaletteAt(PaletteLayerPreview preview, double worldX, double worldZ) {
|
||||
double noise = preview.sample(worldX, worldZ);
|
||||
if (Double.isNaN(noise)) {
|
||||
return "The layer doesn't reach this depth here";
|
||||
return preview.palette().kind() == BiomePalette.Kind.DECORATOR
|
||||
? "The decorator places nothing here"
|
||||
: "The layer doesn't reach this depth here";
|
||||
}
|
||||
return preview.entryForNoise(noise).block() + " noise " + formatNumber(noise);
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ final class NoiseRenderCoordinator implements AutoCloseable {
|
||||
pixels[pixelIndex++] = colorScale.colorAt(value, worldX, worldZ);
|
||||
stats.acceptFinite(value, center, paletteMinimum, paletteMaximum);
|
||||
} else {
|
||||
pixels[pixelIndex++] = colorScale.colorInvalid();
|
||||
pixels[pixelIndex++] = colorScale.colorInvalidAt(worldX, worldZ);
|
||||
stats.acceptInvalid(value, center);
|
||||
}
|
||||
worldX += worldStep;
|
||||
|
||||
@@ -40,10 +40,13 @@ final class PaletteLayerPreview {
|
||||
private static final int MISSING_BLOCK_COLOR = 0x5A5F6B;
|
||||
private static final double FAR_FROM_WEIGHT_FACTOR = 2D;
|
||||
private static final int BLOCK_CACHE_SIZE = 1 << 16;
|
||||
private static final double BARE_GROUND_SHADE = 0.6D;
|
||||
|
||||
private final BiomePalette palette;
|
||||
private final KList<IrisBlockData> blocks;
|
||||
private final BiomePalettePlacement placement;
|
||||
private final PaletteLayerPreview ground;
|
||||
private final boolean[] showsGroundByEntry;
|
||||
private final int depth;
|
||||
private final IrisData data;
|
||||
private final int[] entryBySlot;
|
||||
@@ -57,11 +60,12 @@ final class PaletteLayerPreview {
|
||||
private final double absentShare;
|
||||
private final double outOfRangeShare;
|
||||
|
||||
private PaletteLayerPreview(BiomePalette palette, BiomePalettePlacement placement, int depth, IrisData data,
|
||||
BlockTextures textures, BiomeTintField biomeTints) {
|
||||
private PaletteLayerPreview(BiomePalette palette, BiomePalettePlacement placement, PaletteLayerPreview ground, int depth,
|
||||
IrisData data, BlockTextures textures, BiomeTintField biomeTints) {
|
||||
this.palette = palette;
|
||||
this.blocks = placement.blocks();
|
||||
this.placement = placement;
|
||||
this.ground = ground;
|
||||
this.depth = depth;
|
||||
this.data = data;
|
||||
this.entryBySlot = IrisBiomePaletteLayer.paletteEntryPerSlot(blocks, data).stream().mapToInt(Integer::intValue).toArray();
|
||||
@@ -69,6 +73,7 @@ final class PaletteLayerPreview {
|
||||
throw new IllegalStateException("No block of " + palette.biomeKey() + " " + palette.lowercaseLabel() + " could be found");
|
||||
}
|
||||
this.biomeTints = biomeTints;
|
||||
this.showsGroundByEntry = resolveGroundShowingEntries();
|
||||
this.textureByEntry = resolveEntryTextures(textures);
|
||||
this.legendTextureByEntry = tintForLegend(textureByEntry);
|
||||
this.legendColorByEntry = resolveLegendColors();
|
||||
@@ -93,7 +98,22 @@ final class PaletteLayerPreview {
|
||||
BiomeTintField biomeTints = textures == null
|
||||
? null
|
||||
: new BiomeTintField(biome, new RNG(engine.getSeedManager().getBiome()), textures.biomeColors());
|
||||
return new PaletteLayerPreview(palette, placement, depth, data, textures, biomeTints);
|
||||
PaletteLayerPreview ground = palette.kind() == BiomePalette.Kind.DECORATOR
|
||||
? groundUnder(engine, palette.biomeKey(), biome, data, textures, biomeTints)
|
||||
: null;
|
||||
return new PaletteLayerPreview(palette, placement, ground, depth, data, textures, biomeTints);
|
||||
}
|
||||
|
||||
// Decorations stand on the biome's top surface layer, so that is what shows around and between them.
|
||||
private static PaletteLayerPreview groundUnder(Engine engine, String biomeKey, IrisBiome biome, IrisData data,
|
||||
BlockTextures textures, BiomeTintField biomeTints) {
|
||||
BiomePalette topLayer = new BiomePalette(biomeKey, BiomePalette.Kind.LAYER, 0);
|
||||
KList<IrisBlockData> blocks = topLayer.blocksIn(biome);
|
||||
if (blocks == null || blocks.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new PaletteLayerPreview(topLayer, BiomePalettePlacement.of(engine, biome, topLayer), null, 0, data, textures,
|
||||
biomeTints);
|
||||
}
|
||||
|
||||
// The world picks one block per whole coordinate, and zoomed in, many pixels fall on the same block.
|
||||
@@ -128,10 +148,16 @@ final class PaletteLayerPreview {
|
||||
|
||||
int blockColorAt(double noise, double worldX, double worldZ, boolean showTexturePixels) {
|
||||
int entry = entryIndexForNoise(noise);
|
||||
if (showsGroundByEntry[entry]) {
|
||||
return bareColorAt(worldX, worldZ, showTexturePixels);
|
||||
}
|
||||
BlockTexture texture = textureByEntry[entry];
|
||||
if (texture == null) {
|
||||
return legendColorByEntry[entry];
|
||||
}
|
||||
if (ground != null && showTexturePixels && !texture.isOpaqueAt(worldX, worldZ)) {
|
||||
return groundColorAt(worldX, worldZ, true);
|
||||
}
|
||||
int untintedColor = showTexturePixels ? texture.texelAt(worldX, worldZ) : texture.averageColor();
|
||||
if (texture.biomeTint() == null) {
|
||||
return untintedColor;
|
||||
@@ -140,6 +166,31 @@ final class PaletteLayerPreview {
|
||||
return BlockTexture.multiply(untintedColor, tint);
|
||||
}
|
||||
|
||||
/**
|
||||
* The colour where the palette places nothing: the dimmed ground under a decorator, or plain dark otherwise.
|
||||
*/
|
||||
int bareColorAt(double worldX, double worldZ, boolean showTexturePixels) {
|
||||
if (ground == null) {
|
||||
return PaletteLayerScale.ABSENT_COLOR;
|
||||
}
|
||||
return BlockPreviewColors.shade(groundColorAt(worldX, worldZ, showTexturePixels), BARE_GROUND_SHADE);
|
||||
}
|
||||
|
||||
private int groundColorAt(double worldX, double worldZ, boolean showTexturePixels) {
|
||||
double noise = ground.sample(worldX, worldZ);
|
||||
return Double.isNaN(noise) ? PaletteLayerScale.ABSENT_COLOR : ground.blockColorAt(noise, worldX, worldZ, showTexturePixels);
|
||||
}
|
||||
|
||||
// A decorator places air to leave gaps, which looks the same as placing nothing.
|
||||
private boolean[] resolveGroundShowingEntries() {
|
||||
boolean[] showsGround = new boolean[blocks.size()];
|
||||
for (int entry = 0; entry < blocks.size(); entry++) {
|
||||
BlockData block = blocks.get(entry).getBlockData(data);
|
||||
showsGround[entry] = ground != null && block != null && block.getMaterial().isAir();
|
||||
}
|
||||
return showsGround;
|
||||
}
|
||||
|
||||
Entry entryForNoise(double noise) {
|
||||
return entries.get(entryIndexForNoise(noise));
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ final class PaletteLayerScale implements NoiseColorScale {
|
||||
return ABSENT_COLOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int colorInvalidAt(double worldX, double worldZ) {
|
||||
return view == View.NOISE ? ABSENT_COLOR : preview.bareColorAt(worldX, worldZ, showTexturePixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color displayColorNormalized(double normalized) {
|
||||
return new Color(colorFinite(minimum() + normalized * (maximum() - minimum())));
|
||||
|
||||
Reference in new issue
Block a user