Add cfi setcolor

This commit is contained in:
Jesse Boyd 2017-04-28 07:10:09 +10:00
parent 17fb559f10
commit 370ce99d93
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
5 changed files with 341 additions and 167 deletions

View File

@ -120,6 +120,7 @@ import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
import com.sk89q.worldedit.util.formatting.component.MessageBox; import com.sk89q.worldedit.util.formatting.component.MessageBox;
import com.sk89q.worldedit.world.registry.BundledBlockData; import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean; import java.lang.management.MemoryMXBean;
@ -135,6 +136,7 @@ import javax.management.InstanceAlreadyExistsException;
import javax.management.Notification; import javax.management.Notification;
import javax.management.NotificationEmitter; import javax.management.NotificationEmitter;
import javax.management.NotificationListener; import javax.management.NotificationListener;
import org.json.simple.parser.ParseException;
/**[ WorldEdit action] /**[ WorldEdit action]
* | * |
@ -326,7 +328,14 @@ public class Fawe {
synchronized (this) { synchronized (this) {
tmp = textures; tmp = textures;
if (tmp == null) { if (tmp == null) {
try {
textures = tmp = new TextureUtil(); textures = tmp = new TextureUtil();
tmp.loadModTextures();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
} }
} }
} }
@ -518,6 +527,7 @@ public class Fawe {
File extraBlocks = MainUtil.copyFile(jar, "extrablocks.json", null); File extraBlocks = MainUtil.copyFile(jar, "extrablocks.json", null);
if (extraBlocks != null && extraBlocks.exists()) { if (extraBlocks != null && extraBlocks.exists()) {
try { try {
BundledBlockData.getInstance().loadFromResource();
BundledBlockData.getInstance().add(extraBlocks.toURI().toURL(), true); BundledBlockData.getInstance().add(extraBlocks.toURI().toURL(), true);
} catch (Throwable ignore) { } catch (Throwable ignore) {
Fawe.debug("Invalid format: extrablocks.json"); Fawe.debug("Invalid format: extrablocks.json");

View File

@ -1,7 +1,9 @@
package com.boydti.fawe.jnbt.anvil; package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.util.CachedTextureUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MutableBlockVector; import com.sk89q.worldedit.MutableBlockVector;
@ -235,6 +237,24 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent {
} }
} }
public void setColor(BufferedImage img) {
CachedTextureUtil textureUtil = new CachedTextureUtil(Fawe.get().getTextureUtil());
if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
int index = 0;
for (int y = 0; y < img.getHeight(); y++) {
for (int x = 0; x < img.getWidth(); x++) {
int color = img.getRGB(x, y);
BaseBlock block = textureUtil.getNearestBlock(color);
if (block == null) {
continue;
}
char combined = (char) block.getCombined();
main[index] = combined;
floor[index++] = combined;
}
}
}
public void setBiome(Mask mask, byte biome) { public void setBiome(Mask mask, byte biome) {
int index = 0; int index = 0;
for (int z = 0; z < getLength(); z++) { for (int z = 0; z < getLength(); z++) {

View File

@ -138,6 +138,7 @@ public class CreateFromImage extends Command {
fp.sendMessage(BBC.getPrefix() + "/2 cfi addore[s]"); fp.sendMessage(BBC.getPrefix() + "/2 cfi addore[s]");
fp.sendMessage(BBC.getPrefix() + "/2 cfi addschems"); fp.sendMessage(BBC.getPrefix() + "/2 cfi addschems");
fp.sendMessage(BBC.getPrefix() + "/2 cfi setheight"); fp.sendMessage(BBC.getPrefix() + "/2 cfi setheight");
fp.sendMessage(BBC.getPrefix() + "/2 cfi setcolor");
fp.sendMessage(BBC.getPrefix() + "/2 cfi done"); fp.sendMessage(BBC.getPrefix() + "/2 cfi done");
fp.sendMessage(BBC.getPrefix() + "/2 cfi cancel"); fp.sendMessage(BBC.getPrefix() + "/2 cfi cancel");
File folder = new File(PS.imp().getWorldContainer(), plot.getWorldName() + File.separator + "region"); File folder = new File(PS.imp().getWorldContainer(), plot.getWorldName() + File.separator + "region");
@ -161,12 +162,12 @@ public class CreateFromImage extends Command {
return; return;
} }
if (argList.size() == 1) { if (argList.size() == 1) {
if (StringMan.isEqualIgnoreCaseToAny(argList.get(0), "setbiome", "setoverlay", "setmain", "setfloor", "setcolumn")) { if (StringMan.isEqualIgnoreCaseToAny(argList.get(0), "setbiome", "setoverlay", "setmain", "setfloor", "setcolumn", "setcolor")) {
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <image or mask> <value> [white-only]"); C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <image or mask> <value> [white-only]");
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <value>"); C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <value>");
return; return;
} else if (!StringMan.isEqualIgnoreCaseToAny(argList.get(0), "done", "cancel", "addcaves", "addore", "addores", "addschems", "setheight")) { } else if (!StringMan.isEqualIgnoreCaseToAny(argList.get(0), "done", "cancel", "addcaves", "addore", "addores", "addschems", "setheight")) {
C.COMMAND_SYNTAX.send(player, "/2 cfi <setbiome|setoverlay|setmain|setfloor|setcolumn|done|cancel|addcaves|addore[s]|addschems|setheight>"); C.COMMAND_SYNTAX.send(player, "/2 cfi <setbiome|setoverlay|setmain|setfloor|setcolumn|done|cancel|addcaves|addore[s]|addschems|setheight|setcolor>");
return; return;
} }
} }
@ -196,6 +197,16 @@ public class CreateFromImage extends Command {
player.sendMessage(BBC.getPrefix() + "Added schems, what's next?"); player.sendMessage(BBC.getPrefix() + "Added schems, what's next?");
return; return;
} }
case "setcolor": {
if (argList.size() != 2) {
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <url>");
return;
}
BufferedImage image = getImgurImage(argList.get(1), fp);
generator.setColor(image);
player.sendMessage("Set color, what's next?");
return;
}
case "setheight": { case "setheight": {
if (argList.size() != 2) { if (argList.size() != 2) {
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <height>"); C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <height>");

View File

@ -0,0 +1,79 @@
package com.boydti.fawe.util;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.blocks.BaseBlock;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import org.json.simple.parser.ParseException;
public class CachedTextureUtil extends TextureUtil {
private final TextureUtil parent;
private Int2ObjectOpenHashMap<Integer> colorBlockMap;
public CachedTextureUtil(TextureUtil parent) {
super(parent.getFolder());
this.parent = parent;
this.colorBlockMap = new Int2ObjectOpenHashMap<>();
}
@Override
public BaseBlock getNearestBlock(int color) {
Integer value = colorBlockMap.get(color);
if (value != null) {
return FaweCache.CACHE_BLOCK[value];
}
BaseBlock result = parent.getNearestBlock(color);
if (result != null) {
colorBlockMap.put((int) color, (Integer) result.getCombined());
}
return result;
}
@Override
public BaseBlock getDarkerBlock(BaseBlock block) {
return parent.getDarkerBlock(block);
}
@Override
public int getColor(BaseBlock block) {
return parent.getColor(block);
}
@Override
public File getFolder() {
return parent.getFolder();
}
@Override
public void loadModTextures() throws IOException, ParseException {
parent.loadModTextures();
}
@Override
public BaseBlock getNearestBlock(BaseBlock block, boolean darker) {
return parent.getNearestBlock(block, darker);
}
@Override
public BaseBlock getNearestBlock(int color, boolean darker) {
return parent.getNearestBlock(color, darker);
}
@Override
public long colorDistance(int c1, int c2) {
return parent.colorDistance(c1, c2);
}
@Override
public int getColor(BufferedImage image) {
return parent.getColor(image);
}
@Override
public BaseBlock getLighterBlock(BaseBlock block) {
return parent.getLighterBlock(block);
}
}

View File

@ -38,6 +38,224 @@ public class TextureUtil {
private int[] validColors; private int[] validColors;
private int[] validBlockIds; private int[] validBlockIds;
public TextureUtil() {
this(MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.TEXTURES));
}
public TextureUtil(File folder) {
this.folder = folder;
}
public BaseBlock getNearestBlock(int color) {
long min = Long.MAX_VALUE;
int closest = 0;
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 < validColors.length; i++) {
int other = validColors[i];
if (((other >> 24) & 0xFF) == alpha) {
long distance = colorDistance(red1, green1, blue1, other);
if (distance < min) {
min = distance;
closest = validBlockIds[i];
}
}
}
if (min == Long.MAX_VALUE) return null;
return FaweCache.CACHE_BLOCK[closest];
}
public BaseBlock getLighterBlock(BaseBlock block) {
return getNearestBlock(block, false);
}
public BaseBlock getDarkerBlock(BaseBlock block) {
return getNearestBlock(block, false);
}
public int getColor(BaseBlock block) {
return blockColors[block.getCombined()];
}
public File getFolder() {
return folder;
}
public void loadModTextures() throws IOException, ParseException {
Int2ObjectOpenHashMap<Integer> colorMap = new Int2ObjectOpenHashMap<>();
if (folder.exists()) {
// Get all the jar files
for (File file : folder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
})) {
ZipFile zipFile = new ZipFile(file);
BundledBlockData bundled = BundledBlockData.getInstance();
// Get all the groups in the current jar
// The vanilla textures are in `assets/minecraft`
// A jar may contain textures for multiple mods
Set<String> mods = new HashSet<String>();
{
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
Path path = Paths.get(name);
if (path.startsWith("assets" + File.separator)) {
String[] split = path.toString().split(Pattern.quote(File.separator));
if (split.length > 1) {
String modId = split[1];
mods.add(modId);
}
}
continue;
}
}
for (String modId : mods) {
String modelsDir = "assets" + "/" + modId + "/" + "models" + "/" + "block";
String texturesDir = "assets" + "/" + modId + "/" + "textures" + "/" + "blocks";
Map<String, String> texturesMap = new ConcurrentHashMap<>();
{ // Read models
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory()) {
continue;
}
String name = entry.getName();
if (!name.endsWith(".json")) {
continue;
}
Path path = Paths.get(name);
if (path.startsWith(modelsDir)) {
String[] split = path.toString().split("[/|\\\\|\\.]");
String blockName = getFileName(path.toString());
// Ignore special models
if (blockName.startsWith("#")) {
continue;
}
try (InputStream is = zipFile.getInputStream(entry)) { //Read from a file, or a HttpRequest, or whatever.
JSONParser parser = new JSONParser();
JSONObject root = (JSONObject) parser.parse(new InputStreamReader(is, "UTF-8"));
// Try to work out the texture names for this file
addTextureNames(blockName, root, texturesMap);
}
}
}
}
// Add some possible variations for the file names to try and match it to a block
// - As vanilla minecraft doesn't use consistent naming for the assets and block names
for (String key : new ArrayList<>(texturesMap.keySet())) {
String value = texturesMap.get(key);
texturesMap.put(alphabetize(key), value);
String[] split = key.split("_");
if (split.length > 1) {
key = StringMan.join(Arrays.copyOfRange(split, 0, split.length - 1), "_");
texturesMap.putIfAbsent(key, value);
}
}
// Try to match the textures to a block
Int2ObjectOpenHashMap<String> idMap = new Int2ObjectOpenHashMap<>();
for (String id : bundled.stateMap.keySet()) {
if (id.startsWith(modId)) {
BaseBlock block = bundled.findByState(id);
BundledBlockData.BlockEntry state = bundled.findById(block.getId());
// Ignore non blocks
if (!state.material.isRenderedAsNormalBlock()) {
continue;
}
if (state.material.getLightValue() != 0) {
continue;
}
id = id.substring(modId.length() + 1).replaceAll(":", "_");
String texture = texturesMap.remove(id);
if (texture == null) {
texture = texturesMap.remove(alphabetize(id));
}
if (texture != null) {
int combined = block.getCombined();
if (id.startsWith("log_") || id.startsWith("log2_")) {
combined += 12;
}
idMap.put(combined, texture);
}
}
}
{ // Calculate the colors for each block
for (Int2ObjectMap.Entry<String> entry : idMap.int2ObjectEntrySet()) {
int combined = entry.getIntKey();
String path = texturesDir + "/" + entry.getValue() + ".png";
ZipEntry textureEntry = zipFile.getEntry(path);
try (InputStream is = zipFile.getInputStream(textureEntry)) {
BufferedImage image = ImageIO.read(is);
int color = getColor(image);
colorMap.put((int) combined, (Integer) color);
}
}
}
}
// Close the file
zipFile.close();
}
}
// Convert the color map to a simple array
validBlockIds = new int[colorMap.size()];
validColors = new int[colorMap.size()];
int index = 0;
for (Int2ObjectMap.Entry<Integer> entry : colorMap.int2ObjectEntrySet()) {
int combinedId = entry.getIntKey();
int color = entry.getValue();
blockColors[combinedId] = color;
validBlockIds[index] = combinedId;
validColors[index] = color;
index++;
}
}
protected BaseBlock getNearestBlock(BaseBlock block, boolean darker) {
int color = getColor(block);
if (color == 0) {
return block;
}
BaseBlock darkerBlock = getNearestBlock(color, darker);
return darkerBlock != null ? darkerBlock : block;
}
protected BaseBlock getNearestBlock(int color, boolean darker) {
long min = Long.MAX_VALUE;
int closest = 0;
int red1 = (color >> 16) & 0xFF;
int green1 = (color >> 8) & 0xFF;
int blue1 = (color >> 0) & 0xFF;
int alpha = (color >> 24) & 0xFF;
int intensity1 = red1 + green1 + blue1;
for (int i = 0; i < validColors.length; i++) {
int other = validColors[i];
if (other != color && ((other >> 24) & 0xFF) == alpha) {
int red2 = (other >> 16) & 0xFF;
int green2 = (other >> 8) & 0xFF;
int blue2 = (other >> 0) & 0xFF;
int intensity2 = red2 + green2 + blue2;
if (darker ? intensity2 >= intensity1 : intensity1 >= intensity2) {
continue;
}
long distance = colorDistance(red1, green1, blue1, other);
if (distance < min) {
min = distance;
closest = validBlockIds[i];
}
}
}
if (min == Long.MAX_VALUE) return null;
return FaweCache.CACHE_BLOCK[closest];
}
private String getFileName(String path) { private String getFileName(String path) {
String[] split = path.toString().split("[/|\\\\]"); String[] split = path.toString().split("[/|\\\\]");
String name = split[split.length - 1]; String name = split[split.length - 1];
@ -108,162 +326,6 @@ public class TextureUtil {
} }
} }
public TextureUtil() throws IOException, ParseException {
this(MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.TEXTURES));
}
public TextureUtil(File folder) throws IOException, ParseException {
this.folder = folder;
loadModTextures();
}
public void loadModTextures() throws IOException, ParseException {
if (!folder.exists()) {
return;
}
for (File file : folder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
})) {
ZipFile zipFile = new ZipFile(file);
// get mods
BundledBlockData bundled = BundledBlockData.getInstance();
bundled.loadFromResource();
Set<String> mods = new HashSet<String>();
{
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
Path path = Paths.get(name);
if (path.startsWith("assets" + File.separator)) {
String[] split = path.toString().split(Pattern.quote(File.separator));
if (split.length > 1) {
String modId = split[1];
if (mods.add(modId)) {
}
}
}
continue;
}
}
Int2ObjectOpenHashMap<Integer> colorMap = new Int2ObjectOpenHashMap<>();
for (String modId : mods) {
String modelsDir = "assets" + "/" + modId + "/" + "models" + "/" + "block";
String texturesDir = "assets" + "/" + modId + "/" + "textures" + "/" + "blocks";
Map<String, String> texturesMap = new ConcurrentHashMap<>();
// Read models
{
// Read .json
// Find texture file
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory()) {
continue;
}
String name = entry.getName();
if (!name.endsWith(".json")) {
continue;
}
Path path = Paths.get(name);
if (path.startsWith(modelsDir)) {
String[] split = path.toString().split("[/|\\\\|\\.]");
String blockName = getFileName(path.toString());
// Ignore special models
if (blockName.startsWith("#")) {
continue;
}
try (InputStream is = zipFile.getInputStream(entry)) { //Read from a file, or a HttpRequest, or whatever.
JSONParser parser = new JSONParser();
JSONObject root = (JSONObject) parser.parse(new InputStreamReader(is, "UTF-8"));
addTextureNames(blockName, root, texturesMap);
}
}
}
}
for (String key : new ArrayList<>(texturesMap.keySet())) {
String value = texturesMap.get(key);
texturesMap.put(alphabetize(key), value);
String[] split = key.split("_");
if (split.length > 1) {
key = StringMan.join(Arrays.copyOfRange(split, 0, split.length - 1), "_");
texturesMap.putIfAbsent(key, value);
}
}
Int2ObjectOpenHashMap<String> idMap = new Int2ObjectOpenHashMap<>();
for (String id : bundled.stateMap.keySet()) {
if (id.startsWith(modId)) {
BaseBlock block = bundled.findByState(id);
id = id.substring(modId.length() + 1).replaceAll(":", "_");
String texture = texturesMap.remove(id);
if (texture == null) {
texture = texturesMap.remove(alphabetize(id));
}
if (texture != null) {
idMap.put(block.getCombined(), texture);
}
}
}
{
for (Int2ObjectMap.Entry<String> entry : idMap.int2ObjectEntrySet()) {
int combined = entry.getIntKey();
String path = texturesDir + "/" + entry.getValue() + ".png";
ZipEntry textureEntry = zipFile.getEntry(path);
try (InputStream is = zipFile.getInputStream(textureEntry)) {
BufferedImage image = ImageIO.read(is);
int color = getColor(image);
colorMap.put((int) combined, (Integer) color);
}
}
// Load and map the textures
//
}
}
validBlockIds = new int[colorMap.size()];
validColors = new int[colorMap.size()];
Arrays.fill(blockColors, 0);
int index = 0;
for (Int2ObjectMap.Entry<Integer> entry : colorMap.int2ObjectEntrySet()) {
int combinedId = entry.getIntKey();
int color = entry.getValue();
blockColors[combinedId] = color;
validBlockIds[index] = combinedId;
validColors[index] = color;
index++;
}
zipFile.close();
}
}
public BaseBlock getNearestBlock(int color) {
long min = Long.MAX_VALUE;
int closest = 0;
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 < validColors.length; i++) {
int other = validColors[i];
if (((other >> 24) & 0xFF) == alpha) {
long distance = colorDistance(red1, green1, blue1, other);
if (distance < min) {
min = distance;
closest = validBlockIds[i];
}
}
}
return FaweCache.CACHE_BLOCK[closest];
}
public int getColor(BaseBlock block) {
return blockColors[block.getCombined()];
}
private boolean hasAlpha(int color) { private boolean hasAlpha(int color) {
int alpha = (color >> 24) & 0xFF; int alpha = (color >> 24) & 0xFF;
return alpha != 255; return alpha != 255;
@ -307,12 +369,4 @@ public class TextureUtil {
Color color = new Color((int) (totalRed / a), (int) (totalGreen / a), (int) (totalBlue / a), (int) (totalAlpha / a)); Color color = new Color((int) (totalRed / a), (int) (totalGreen / a), (int) (totalBlue / a), (int) (totalAlpha / a));
return color.getRGB(); return color.getRGB();
} }
// public Color getColor(BaseBlock block) {
// long r;
// long b;
// long g;
// long a;
//
// }
} }