Add biome-based coloring option for surface map

This commit is contained in:
Mike Primm 2011-06-20 00:09:22 -05:00
parent 2bc9b410a6
commit edf7d4f5c8
16 changed files with 290 additions and 20 deletions

View file

@ -8,19 +8,22 @@ import java.util.HashMap;
import java.util.Scanner;
import org.dynmap.debug.Debug;
import org.bukkit.block.Biome;
public class ColorScheme {
private static final HashMap<String, ColorScheme> cache = new HashMap<String, ColorScheme>();
public String name;
/* Switch to arrays - faster than map */
public Color[][] colors; /* [blk-type][step] */
public Color[][][] datacolors; /* [bkt-type][blk-dat][step] */
public final Color[][] colors; /* [blk-type][step] */
public final Color[][][] datacolors; /* [bkt-type][blk-dat][step] */
public final Color[][] biomecolors; /* [Biome.ordinal][step] */
public ColorScheme(String name, Color[][] colors, Color[][][] datacolors) {
public ColorScheme(String name, Color[][] colors, Color[][][] datacolors, Color[][] biomecolors) {
this.name = name;
this.colors = colors;
this.datacolors = datacolors;
this.biomecolors = biomecolors;
}
private static File getColorSchemeDirectory() {
@ -42,6 +45,7 @@ public class ColorScheme {
File colorSchemeFile = new File(getColorSchemeDirectory(), name + ".txt");
Color[][] colors = new Color[256][];
Color[][][] datacolors = new Color[256][][];
Color[][] biomecolors = new Color[Biome.values().length][];
InputStream stream;
try {
Debug.debug("Loading colors from '" + colorSchemeFile + "'...");
@ -54,18 +58,38 @@ public class ColorScheme {
if (line.startsWith("#") || line.equals("")) {
continue;
}
String[] split = line.split("\t");
/* Make parser less pedantic - tabs or spaces should be fine */
String[] split = line.split("[\t ]");
int cnt = 0;
for(String s: split) { if(s.length() > 0) cnt++; }
String[] nsplit = new String[cnt];
cnt = 0;
for(String s: split) { if(s.length() > 0) { nsplit[cnt] = s; cnt++; } }
split = nsplit;
if (split.length < 17) {
continue;
}
Integer id;
Integer dat = null;
boolean isbiome = false;
int idx = split[0].indexOf(':');
if(idx > 0) { /* ID:data - data color */
id = new Integer(split[0].substring(0, idx));
dat = new Integer(split[0].substring(idx+1));
}
else if(split[0].charAt(0) == '[') { /* Biome color data */
String bio = split[0].substring(1);
idx = bio.indexOf(']');
if(idx >= 0) bio = bio.substring(0, idx);
isbiome = true;
id = -1;
for(Biome b : Biome.values()) {
if(b.toString().equalsIgnoreCase(bio)) {
id = b.ordinal();
break;
}
}
}
else {
id = new Integer(split[0]);
}
@ -78,8 +102,12 @@ public class ColorScheme {
c[2] = new Color(Integer.parseInt(split[13]), Integer.parseInt(split[14]), Integer.parseInt(split[15]), Integer.parseInt(split[16]));
/* Blended color - for 'smooth' option on flat map */
c[4] = new Color((c[0].getRed()+c[2].getRed())/2, (c[0].getGreen()+c[2].getGreen())/2, (c[0].getBlue()+c[2].getBlue())/2, (c[0].getAlpha()+c[2].getAlpha())/2);
if(dat != null) {
if(isbiome) {
if((id >= 0) && (id < biomecolors.length))
biomecolors[id] = c;
}
else if(dat != null) {
Color[][] dcolor = datacolors[id]; /* Existing list? */
if(dcolor == null) {
dcolor = new Color[16][]; /* Make 16 index long list */
@ -115,6 +143,6 @@ public class ColorScheme {
} catch (FileNotFoundException e) {
Log.severe("Could not load colors '" + name + "' ('" + colorSchemeFile + "'): File not found.", e);
}
return new ColorScheme(name, colors, datacolors);
return new ColorScheme(name, colors, datacolors, biomecolors);
}
}

View file

@ -180,8 +180,11 @@ public class MapManager {
}
World w = world.world;
/* Fetch chunk cache from server thread */
List<DynmapChunk> requiredChunks = tile.getMap().getRequiredChunks(tile);
MapChunkCache cache = createMapChunkCache(world, requiredChunks);
MapType mt = tile.getMap();
List<DynmapChunk> requiredChunks = mt.getRequiredChunks(tile);
MapChunkCache cache = createMapChunkCache(world, requiredChunks, mt.isBlockTypeDataNeeded(),
mt.isHightestBlockYDataNeeded(), mt.isBiomeDataNeeded(),
mt.isRawBiomeDataNeeded());
if(cache == null) {
cleanup();
return; /* Cancelled/aborted */
@ -476,7 +479,8 @@ public class MapManager {
/**
* Render processor helper - used by code running on render threads to request chunk snapshot cache from server/sync thread
*/
public MapChunkCache createMapChunkCache(final DynmapWorld w, final List<DynmapChunk> chunks) {
public MapChunkCache createMapChunkCache(DynmapWorld w, List<DynmapChunk> chunks,
boolean blockdata, boolean highesty, boolean biome, boolean rawbiome) {
MapChunkCache c = null;
try {
if(!use_legacy)
@ -493,6 +497,9 @@ public class MapManager {
c.setHiddenFillStyle(w.hiddenchunkstyle);
}
c.setChunks(w.world, chunks);
if(c.setChunkDataTypes(blockdata, biome, highesty, rawbiome) == false)
Log.severe("CraftBukkit build does not support biome APIs");
synchronized(c) {
chunkloads.add(c);
try {

View file

@ -22,4 +22,10 @@ public abstract class MapType {
}
public abstract String getName();
public boolean isBiomeDataNeeded() { return false; }
public boolean isHightestBlockYDataNeeded() { return false; }
public boolean isRawBiomeDataNeeded() { return false; }
public boolean isBlockTypeDataNeeded() { return true; }
}

View file

@ -117,6 +117,11 @@ public class FlatMap extends MapType {
return result;
}
@Override
public boolean isHightestBlockYDataNeeded() {
return true;
}
@Override
public boolean render(MapChunkCache cache, MapTile tile, File outputFile) {
FlatMapTile t = (FlatMapTile) tile;

View file

@ -11,6 +11,7 @@ import javax.imageio.ImageIO;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.dynmap.Client;
import org.dynmap.Color;
import org.dynmap.ColorScheme;
@ -38,6 +39,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
protected int lightscale[]; /* scale skylight level (light = lightscale[skylight] */
protected boolean night_and_day; /* If true, render both day (prefix+'-day') and night (prefix) tiles */
protected boolean transparency; /* Is transparency support active? */
protected boolean biomecolored; /* Use biome for coloring */
@Override
public String getName() {
return name;
@ -81,7 +83,9 @@ public class DefaultTileRenderer implements MapTileRenderer {
colorScheme = ColorScheme.getScheme((String)configuration.get("colorscheme"));
night_and_day = configuration.getBoolean("night-and-day", false);
transparency = configuration.getBoolean("transparency", true); /* Default on */
biomecolored = configuration.getBoolean("biomecolored", false);
}
public boolean isBiomeDataNeeded() { return biomecolored; }
public boolean render(MapChunkCache cache, KzedMapTile tile, File outputFile) {
World world = tile.getWorld();
@ -360,6 +364,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
MapIterator mapiter) {
int lightlevel = 15;
int lightlevel_day = 15;
Biome bio = null;
result.setTransparent();
if(result_day != null)
result_day.setTransparent();
@ -384,6 +389,8 @@ public class DefaultTileRenderer implements MapTileRenderer {
if(colorScheme.datacolors[id] != null) { /* If data colored */
data = mapiter.getBlockData();
}
if(biomecolored)
bio = mapiter.getBiome();
if((shadowscale != null) && (mapiter.getY() < 127)) {
/* Find light level of previous chunk */
switch(seq) {
@ -442,7 +449,13 @@ public class DefaultTileRenderer implements MapTileRenderer {
return;
}
Color[] colors;
if(data != 0)
if(biomecolored) {
if(bio != null)
colors = colorScheme.biomecolors[bio.ordinal()];
else
colors = null;
}
else if(data != 0)
colors = colorScheme.datacolors[id][data];
else
colors = colorScheme.colors[id];

View file

@ -310,6 +310,14 @@ public class KzedMap extends MapType {
}
}
public boolean isBiomeDataNeeded() {
for(MapTileRenderer r : renderers) {
if(r.isBiomeDataNeeded())
return true;
}
return false;
}
public String getName() {
return "KzedMap";
}

View file

@ -12,4 +12,6 @@ public interface MapTileRenderer {
boolean render(MapChunkCache cache, KzedMapTile tile, File outputFile);
void buildClientConfiguration(JSONObject worldObject);
boolean isBiomeDataNeeded();
}

View file

@ -4,8 +4,11 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.ListIterator;
import java.lang.reflect.Field;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
import org.bukkit.Chunk;
import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
import org.dynmap.DynmapChunk;
import org.dynmap.Log;
@ -68,6 +71,16 @@ public class LegacyMapChunkCache implements MapChunkCache {
public final int getBlockEmittedLight() {
return snap.getBlockEmittedLight(x & 0xF, y, z & 0xF);
}
public Biome getBiome() {
return null;
}
public double getRawBiomeTemperature() {
return 0.0;
}
public double getRawBiomeRainfall() {
return 0.0;
}
public final void incrementX() {
x++;
if((x & 0xF) == 0) { /* Next chunk? */
@ -377,6 +390,16 @@ public class LegacyMapChunkCache implements MapChunkCache {
LegacyChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
return ss.getBlockEmittedLight(x & 0xF, y, z & 0xF);
}
public Biome getBiome(int x, int z) {
return null;
}
public double getRawBiomeTemperature(int x, int z) {
return 0.0;
}
public double getRawBiomeRainfall(int x, int z) {
return 0.0;
}
/**
* Get cache iterator
*/
@ -412,4 +435,10 @@ public class LegacyMapChunkCache implements MapChunkCache {
visible_limits = new ArrayList<VisibilityLimit>();
visible_limits.add(limit);
}
@Override
public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) {
if(biome || rawbiome) /* Legacy doesn't support these */
return false;
return true;
}
}

View file

@ -1,5 +1,7 @@
package org.dynmap.utils;
import org.bukkit.World;
import org.bukkit.block.Biome;
import java.util.List;
import org.dynmap.DynmapChunk;
@ -16,6 +18,15 @@ public interface MapChunkCache {
* Set chunks to load, and world to load from
*/
void setChunks(World w, List<DynmapChunk> chunks);
/**
* Set chunk data type needed
* @param blockdata - need block type and data for chunk
* @param biome - need biome data
* @param highestblocky - need highest-block-y data
* @param rawbiome - need raw biome temp/rain data
* @return true if all data types can be retrieved, false if not
*/
boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome);
/**
* Load chunks into cache
* @param maxToLoad - maximum number to load at once
@ -50,6 +61,18 @@ public interface MapChunkCache {
* Get emitted light level
*/
int getBlockEmittedLight(int x, int y, int z);
/**
* Get biome at coordinates
*/
public Biome getBiome(int x, int z);
/**
* Get raw temperature data (0.0-1.0)
*/
public double getRawBiomeTemperature(int x, int z);
/**
* Get raw rainfall data (0.0-1.0)
*/
public double getRawBiomeRainfall(int x, int z);
/**
* Get cache iterator
*/

View file

@ -1,5 +1,7 @@
package org.dynmap.utils;
import org.bukkit.block.Biome;
/**
* Iterator for traversing map chunk cache (base is for non-snapshot)
*/
@ -38,6 +40,18 @@ public interface MapIterator {
* @return emitted light level
*/
int getBlockEmittedLight();
/**
* Get biome at coordinates
*/
public Biome getBiome();
/**
* Get raw temperature data (0.0-1.0)
*/
public double getRawBiomeTemperature();
/**
* Get raw rainfall data (0.0-1.0)
*/
public double getRawBiomeRainfall();
/**
* Increment X of current position
*/

View file

@ -1,5 +1,6 @@
package org.dynmap.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@ -17,13 +18,17 @@ import org.dynmap.Log;
* Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread
*/
public class NewMapChunkCache implements MapChunkCache {
private static boolean init = false;
private static Method poppreservedchunk = null;
private static Method getsnapshot2 = null;
private static Method getemptysnapshot = null;
private World w;
private List<DynmapChunk> chunks;
private ListIterator<DynmapChunk> iterator;
private int x_min, x_max, z_min, z_max;
private int x_dim;
private boolean biome, biomeraw, highesty, blockdata;
private HiddenChunkStyle hidestyle = HiddenChunkStyle.FILL_AIR;
private List<VisibilityLimit> visible_limits = null;
@ -64,6 +69,15 @@ public class NewMapChunkCache implements MapChunkCache {
public final int getBlockEmittedLight() {
return snap.getBlockEmittedLight(x & 0xF, y, z & 0xF);
}
public Biome getBiome() {
return snap.getBiome(x & 0xF, z & 0xF);
}
public double getRawBiomeTemperature() {
return snap.getRawBiomeTemperature(x & 0xf, z & 0xf);
}
public double getRawBiomeRainfall() {
return snap.getRawBiomeRainfall(x & 0xf, z & 0xf);
}
public final void incrementX() {
x++;
if((x & 0xF) == 0) { /* Next chunk? */
@ -126,9 +140,6 @@ public class NewMapChunkCache implements MapChunkCache {
public int getX() { return 0; }
public int getZ() { return 0; }
public String getWorldName() { return ""; }
public Biome getBiome(int x, int z) { return null; }
public double getRawBiomeTemperature(int x, int z) { return 0.0; }
public double getRawBiomeRainfall(int x, int z) { return 0.0; }
public long getCaptureFullTime() { return 0; }
public final int getBlockTypeId(int x, int y, int z) {
@ -146,6 +157,15 @@ public class NewMapChunkCache implements MapChunkCache {
public final int getHighestBlockYAt(int x, int z) {
return 0;
}
public Biome getBiome(int x, int z) {
return null;
}
public double getRawBiomeTemperature(int x, int z) {
return 0.0;
}
public double getRawBiomeRainfall(int x, int z) {
return 0.0;
}
}
/**
@ -186,12 +206,36 @@ public class NewMapChunkCache implements MapChunkCache {
private static final EmptyChunk EMPTY = new EmptyChunk();
private static final PlainChunk STONE = new PlainChunk(1);
private static final PlainChunk OCEAN = new PlainChunk(9);
/**
* Construct empty cache
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public NewMapChunkCache() {
if(!init) {
/* Get CraftWorld.popPreservedChunk(x,z) - reduces memory bloat from map traversals (optional) */
try {
Class c = Class.forName("org.bukkit.craftbukkit.CraftWorld");
poppreservedchunk = c.getDeclaredMethod("popPreservedChunk", new Class[] { int.class, int.class });
/* getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain) */
getemptysnapshot = c.getDeclaredMethod("getEmptyChunkSnapshot", new Class[] { int.class, int.class,
boolean.class, boolean.class });
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
}
/* Get CraftChunk.getChunkSnapshot(boolean,boolean,boolean) */
try {
Class c = Class.forName("org.bukkit.craftbukkit.CraftChunk");
getsnapshot2 = c.getDeclaredMethod("getChunkSnapshot", new Class[] { boolean.class, boolean.class, boolean.class });
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
}
if(getsnapshot2 != null)
Log.info("Biome data support is enabled");
else
Log.info("Biome data support is disabled");
init = true;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void setChunks(World w, List<DynmapChunk> chunks) {
@ -266,7 +310,19 @@ public class NewMapChunkCache implements MapChunkCache {
}
else {
Chunk c = w.getChunkAt(chunk.x, chunk.z);
ss = c.getChunkSnapshot();
if(getsnapshot2 != null) {
try {
if(blockdata || highesty)
ss = (ChunkSnapshot)getsnapshot2.invoke(c, highesty, biome, biomeraw);
else
ss = (ChunkSnapshot)getemptysnapshot.invoke(w, chunk.x, chunk.z, biome, biomeraw);
} catch (InvocationTargetException itx) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
else
ss = c.getChunkSnapshot();
}
snaparray[(chunk.x-x_min) + (chunk.z - z_min)*x_dim] = ss;
}
@ -355,6 +411,19 @@ public class NewMapChunkCache implements MapChunkCache {
ChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
return ss.getBlockEmittedLight(x & 0xF, y, z & 0xF);
}
public Biome getBiome(int x, int z) {
ChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
return ss.getBiome(x & 0xF, z & 0xF);
}
public double getRawBiomeTemperature(int x, int z) {
ChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
return ss.getRawBiomeTemperature(x & 0xF, z & 0xF);
}
public double getRawBiomeRainfall(int x, int z) {
ChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
return ss.getRawBiomeRainfall(x & 0xF, z & 0xF);
}
/**
* Get cache iterator
*/
@ -390,4 +459,14 @@ public class NewMapChunkCache implements MapChunkCache {
visible_limits = new ArrayList<VisibilityLimit>();
visible_limits.add(limit);
}
@Override
public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) {
if((getsnapshot2 == null) && (biome || rawbiome))
return false;
this.biome = biome;
this.biomeraw = rawbiome;
this.highesty = highestblocky;
this.blockdata = blockdata;
return true;
}
}