diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java index d72e77b1..71460778 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java @@ -468,20 +468,18 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { int widthIndex = img.getWidth() - 1; int heightIndex = img.getHeight() - 1; int maxIndex = biomes.length - 1; + + int[] buffer = new int[2]; for (int y = 0; y < img.getHeight(); y++) { boolean yBiome = y > 0 && y < heightIndex; - for (int x = 0; x < img.getWidth(); x++) { + for (int x = 0; x < img.getWidth(); x++, index++) { int color = img.getRGB(x, y); - BaseBlock block = textureUtil.getNearestBlock(color); - TextureUtil.BiomeColor biome = textureUtil.getNearestBiome(color); - int blockColor = textureUtil.getColor(block); - biomes[index] = (byte) biome.id; - if (textureUtil.colorDistance(biome.grass, color) - biomePriority > textureUtil.colorDistance(blockColor, color)) { - char combined = (char) block.getCombined(); + if (textureUtil.getIsBlockCloserThanBiome(buffer, color, biomePriority)) { + char combined = (char) buffer[0]; main[index] = combined; floor[index] = combined; } - index++; + biomes[index] = (byte) buffer[1]; } } } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java index 658a754e..9f752d1e 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/CopyPastaBrush.java @@ -118,4 +118,4 @@ public class CopyPastaBrush implements Brush, ResettableTool { editSession.flushQueue(); } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java index b05effd9..f80211f2 100644 --- a/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java @@ -49,6 +49,16 @@ public class DelegateTextureUtil extends TextureUtil { return parent.getColor(block); } + @Override + public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, int biomePriority) { + return parent.getIsBlockCloserThanBiome(blockAndBiomeIdOutput, color, biomePriority); + } + + @Override + public int getBiomeMix(int[] biomeIdsOutput, int color) { + return parent.getBiomeMix(biomeIdsOutput, color); + } + @Override public BiomeColor getBiome(int biome) { return parent.getBiome(biome); diff --git a/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java index c3ea0b73..bada9a5c 100644 --- a/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java @@ -16,8 +16,10 @@ public class RandomTextureUtil extends CachedTextureUtil { this.grassColor = parent.getColor(FaweCache.getBlock(BlockID.GRASS, 0)); } + private int index; + private int[] biomeMixBuffer = new int[3]; private Int2ObjectOpenHashMap offsets = new Int2ObjectOpenHashMap<>(); - private Int2ObjectOpenHashMap biomeOffsets = new Int2ObjectOpenHashMap<>(); + private Int2ObjectOpenHashMap biomeMixes = new Int2ObjectOpenHashMap<>(); protected int addRandomColor(int c1, int c2) { int red1 = (c1 >> 16) & 0xFF; @@ -38,26 +40,45 @@ public class RandomTextureUtil extends CachedTextureUtil { } else { return PseudoRandom.random.nextInt(i); } -// return i; } + @Override + public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, int biomePriority) { + BaseBlock block = getNearestBlock(color); + int[] mix = biomeMixes.getOrDefault(color, null); + if (mix == null) { + int average = getBiomeMix(biomeMixBuffer, color); + mix = new int[4]; + System.arraycopy(biomeMixBuffer, 0, mix, 0, 3); + mix[3] = average; + biomeMixes.put(color, mix); + } + if (++index > 2) index = 0; + int biomeId = mix[index]; + int biomeAvColor = mix[3]; + int blockColor = getColor(block); + blockAndBiomeIdOutput[0] = block.getCombined(); + blockAndBiomeIdOutput[1] = biomeId; + if (colorDistance(biomeAvColor, color) - biomePriority > colorDistance(blockColor, color)) { + return true; + } + return false; + } + + @Override public BiomeColor getNearestBiome(int color) { - int offsetColor = biomeOffsets.getOrDefault(color, 0); - if (offsetColor != 0) { - offsetColor = addRandomColor(color, offsetColor); - } else { - offsetColor = color; + int[] mix = biomeMixes.getOrDefault(color, null); + if (mix == null) { + int average = getBiomeMix(biomeMixBuffer, color); + mix = new int[4]; + System.arraycopy(biomeMixBuffer, 0, mix, 0, 3); + mix[3] = average; + biomeMixes.put(color, mix); } - BiomeColor res = super.getNearestBiome(offsetColor); - int newColor = res.grass; - { - byte dr = (byte) (((color >> 16) & 0xFF) - ((newColor >> 16) & 0xFF)); - byte dg = (byte) (((color >> 8) & 0xFF) - ((newColor >> 8) & 0xFF)); - byte db = (byte) (((color >> 0) & 0xFF) - ((newColor >> 0) & 0xFF)); - biomeOffsets.put(color, (Integer) ((dr << 16) + (dg << 8) + (db << 0))); - } - return res; + if (++index > 2) index = 0; + int biomeId = mix[index]; + return getBiome(biomeId); } @Override diff --git a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java index a78c0e4c..150ce1d1 100644 --- a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java @@ -11,6 +11,8 @@ import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.world.registry.BundledBlockData; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArraySet; +import it.unimi.dsi.fastutil.longs.LongArrayList; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; @@ -50,9 +52,13 @@ public class TextureUtil { protected long[] distances; protected int[] validColors; protected char[] validBlockIds; + protected int[] validLayerColors; protected char[][] validLayerBlocks; + protected int[] validMixBiomeColors; + protected long[] validMixBiomeIds; + /** * https://github.com/erich666/Mineways/blob/master/Win/biomes.cpp @@ -420,6 +426,43 @@ public class TextureUtil { return biomes[biome]; } + public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, int biomePriority) { + BaseBlock block = getNearestBlock(color); + TextureUtil.BiomeColor biome = getNearestBiome(color); + int blockColor = getColor(block); + blockAndBiomeIdOutput[0] = block.getCombined(); + blockAndBiomeIdOutput[1] = biome.id; + if (colorDistance(biome.grass, color) - biomePriority > colorDistance(blockColor, color)) { + return true; + } + return false; + } + + public int getBiomeMix(int[] biomeIdsOutput, int color) { + long closest = Long.MAX_VALUE; + int closestAverage = Integer.MAX_VALUE; + long min = Long.MAX_VALUE; + int red1 = (color >> 16) & 0xFF; + int green1 = (color >> 8) & 0xFF; + int blue1 = (color >> 0) & 0xFF; + int alpha = (color >> 24) & 0xFF; + for (int i = 0; i < validMixBiomeColors.length; i++) { + int other = validMixBiomeColors[i]; + if (((other >> 24) & 0xFF) == alpha) { + long distance = colorDistance(red1, green1, blue1, other); + if (distance < min) { + min = distance; + closest = validMixBiomeIds[i]; + closestAverage = other; + } + } + } + biomeIdsOutput[0] = (int) ((closest >> 0) & 0xFF); + biomeIdsOutput[1] = (int) ((closest >> 8) & 0xFF); + biomeIdsOutput[2] = (int) ((closest >> 16) & 0xFF); + return closestAverage; + } + public BiomeColor getNearestBiome(int color) { int grass = blockColors[2 << 4]; if (grass == 0) { @@ -633,12 +676,47 @@ public class TextureUtil { List valid = new ArrayList<>(); for (int i = 0; i < biomes.length; i++) { BiomeColor biome = biomes[i]; - biome.grass = multiplyColor(biome.grass, grass); +// biome.grass = multiplyColor(biome.grass, grass); if (biome.grass != 0 && !biome.name.equalsIgnoreCase("Unknown Biome")) { valid.add(biome); } } this.validBiomes = valid.toArray(new BiomeColor[valid.size()]); + + { + ArrayList uniqueColors = new ArrayList<>(); + Set uniqueBiomesColors = new IntArraySet(); + for (BiomeColor color : validBiomes) { + if (uniqueBiomesColors.add(color.grass)) { + uniqueColors.add(color); + } + } + int count = 0; + int count2 = 0; + uniqueBiomesColors.clear(); + + LongArrayList layerIds = new LongArrayList(); + LongArrayList layerColors = new LongArrayList(); + for (int i = 0; i < uniqueColors.size(); i++) { + for (int j = i; j < uniqueColors.size(); j++) { + for (int k = j; k < uniqueColors.size(); k++) { + BiomeColor c1 = uniqueColors.get(i); + BiomeColor c2 = uniqueColors.get(j); + BiomeColor c3 = uniqueColors.get(k); + int average = averageColor(c1.grass, c2.grass, c3.grass); + if (uniqueBiomesColors.add(average)) { + count++; + layerColors.add((long) average); + layerIds.add((long) ((c1.id) + (c2.id << 8) + (c3.id << 16))); + } + } + } + } + + validMixBiomeColors = new int[layerColors.size()]; + for (int i = 0; i < layerColors.size(); i++) validMixBiomeColors[i] = (int) layerColors.getLong(i); + validMixBiomeIds = layerIds.toLongArray(); + } } } @@ -703,6 +781,25 @@ public class TextureUtil { return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0); } + public int averageColor(int... colors) { + int alpha = 0; + int red = 0; + int green = 0; + int blue = 0; + for (int c : colors) { + alpha += (c >> 24) & 0xFF; + red += (c >> 16) & 0xFF; + green += (c >> 8) & 0xFF; + blue += (c >> 0) & 0xFF; + } + int num = colors.length; + alpha /= num; + red /= num; + green /= num; + blue /= num; + return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0); + } + /** * Assumes the top layer is a transparent color and the bottom is opaque */