Tinker with performance on some base data classes

This commit is contained in:
Mike Primm 2026-03-05 18:45:42 -05:00
parent 5a30ab0d3f
commit c6fb0be7e7
8 changed files with 172 additions and 119 deletions

View file

@ -38,12 +38,13 @@ public class Color {
val = TRANSPARENT; val = TRANSPARENT;
} }
public final void setGrayscale() { public final void setGrayscale() {
int alpha = (val >> 24) & 0xFF; int alpha = val & 0xFF000000;
int red = (val >> 16) & 0xFF; int num = (((val >> 16) & 0xFF) * 76)
int green = (val >> 8) & 0xFF; + (((val >> 8) & 0xFF) * 151)
int blue = val & 0xFF; + (( val & 0xFF) * 28);
int gray = ((red * 76) + (green * 151) + (blue * 28)) / 255; // weights sum to 255, so num [0, 65025]; fast /255 via shift
setRGBA(gray, gray, gray, alpha); int gray = (num + (num >> 8) + 1) >> 8;
val = alpha | (gray << 16) | (gray << 8) | gray;
} }
public final void setColor(Color c) { public final void setColor(Color c) {
val = c.val; val = c.val;
@ -85,11 +86,10 @@ public class Color {
* @param argb - ARGB to blend * @param argb - ARGB to blend
*/ */
public final void blendColor(int argb) { public final void blendColor(int argb) {
int nval = (((((val >> 24) & 0xFF) * ((argb >> 24) & 0xFF)) / 255) << 24); val = (mulDiv255(val >>> 24, argb >>> 24 ) << 24)
nval = nval | (((((val >> 16) & 0xFF) * ((argb >> 16) & 0xFF)) / 255) << 16); | (mulDiv255((val >> 16) & 0xFF, (argb >> 16) & 0xFF) << 16)
nval = nval | (((((val >> 8) & 0xFF) * ((argb >> 8) & 0xFF)) / 255) << 8); | (mulDiv255((val >> 8) & 0xFF, (argb >> 8) & 0xFF) << 8)
nval = nval | (((val & 0xFF) * (argb & 0xFF)) / 255); | mulDiv255( val & 0xFF, argb & 0xFF);
val = nval;
} }
/** /**
* Scale each color component, based on the corresponding component * Scale each color component, based on the corresponding component
@ -98,10 +98,30 @@ public class Color {
* @return blended color * @return blended color
*/ */
public static final int blendColor(int argb0, int argb1) { public static final int blendColor(int argb0, int argb1) {
int nval = (((((argb0 >> 24) & 0xFF) * ((argb1 >> 24) & 0xFF)) / 255) << 24); return (mulDiv255(argb0 >>> 24, argb1 >>> 24 ) << 24)
nval = nval | (((((argb0 >> 16) & 0xFF) * ((argb1 >> 16) & 0xFF)) / 255) << 16); | (mulDiv255((argb0 >> 16) & 0xFF, (argb1 >> 16) & 0xFF) << 16)
nval = nval | (((((argb0 >> 8) & 0xFF) * ((argb1 >> 8) & 0xFF)) / 255) << 8); | (mulDiv255((argb0 >> 8) & 0xFF, (argb1 >> 8) & 0xFF) << 8)
nval = nval | (((argb0 & 0xFF) * (argb1 & 0xFF)) / 255); | mulDiv255( argb0 & 0xFF, argb1 & 0xFF);
return nval; }
/**
* Scale the RGB channels by scale/256, leaving alpha unchanged.
* Equivalent to setRGBA(getRed()*scale>>8, getGreen()*scale>>8, getBlue()*scale>>8, getAlpha())
* but avoids redundant unpack/repack of the alpha channel.
* @param scale - scale factor 0..256 (256 = full brightness)
*/
public final void scaleRGB(int scale) {
val = (val & 0xFF000000)
| ((((val >> 16) & 0xFF) * scale >> 8) << 16)
| ((((val >> 8) & 0xFF) * scale >> 8) << 8)
| ((val & 0xFF) * scale >> 8);
}
/**
* Fast multiply-then-divide-by-255 for two values a, b each in [0, 255].
* Returns floor(a*b/255), equivalent to the standard integer division but
* computed with shifts only: (x + (x >> 8) + 1) >> 8 where x = a * b.
*/
private static int mulDiv255(int a, int b) {
int x = a * b;
return (x + (x >> 8) + 1) >> 8;
} }
} }

View file

@ -2,6 +2,7 @@ package org.dynmap.common;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -11,7 +12,9 @@ import org.dynmap.hdmap.HDBlockModels;
public class BiomeMap { public class BiomeMap {
public static final int NO_INDEX = -2; public static final int NO_INDEX = -2;
private static BiomeMap[] biome_by_index = new BiomeMap[256]; private static BiomeMap[] biome_by_index = new BiomeMap[256];
private static Map<String, BiomeMap> biome_by_rl = new HashMap<String, BiomeMap>(); private static Map<String, BiomeMap> biome_by_rl = new HashMap<String, BiomeMap>(256);
// Tracks registered IDs for O(1) uniqueness checks during initialization
private static final HashSet<String> biome_ids = new HashSet<String>(256);
public static final BiomeMap NULL = new BiomeMap(-1, "NULL", 0.5, 0.5, 0xFFFFFF, 0, 0, null); public static final BiomeMap NULL = new BiomeMap(-1, "NULL", 0.5, 0.5, 0xFFFFFF, 0, 0, null);
public static final BiomeMap OCEAN = new BiomeMap(0, "OCEAN", "minecraft:ocean"); public static final BiomeMap OCEAN = new BiomeMap(0, "OCEAN", "minecraft:ocean");
@ -145,12 +148,17 @@ public class BiomeMap {
} }
private static boolean isUniqueID(String id) { private static boolean isUniqueID(String id) {
for(int i = 0; i < biome_by_index.length; i++) { return !biome_ids.contains(id);
if(biome_by_index[i] == null) continue;
if(biome_by_index[i].id.equals(id))
return false;
} }
return true;
/**
* Encodes a grass/foliage color multiplier for efficient hot-path dispatch:
* == 0 0 (passthrough: return raw value unchanged)
* 0 < val 0xFFFFFF positive (blend: average with raw)
* val > 0xFFFFFF -(val & 0xFFFFFF) (negative sentinel: fixed override color)
*/
private static int encodeColorMult(int val) {
return (val > 0xFFFFFF) ? -(val & 0xFFFFFF) : val;
} }
private static void resizeIfNeeded(int idx) { private static void resizeIfNeeded(int idx) {
@ -171,8 +179,8 @@ public class BiomeMap {
setTemperature(tmp); setTemperature(tmp);
setRainfall(rain); setRainfall(rain);
this.watercolormult = waterColorMultiplier; this.watercolormult = waterColorMultiplier;
this.grassmult = grassmult; this.grassmult = encodeColorMult(grassmult);
this.foliagemult = foliagemult; this.foliagemult = encodeColorMult(foliagemult);
// Handle null biome // Handle null biome
if (id == null) { id = "biome_" + idx; } if (id == null) { id = "biome_" + idx; }
id = id.toUpperCase().replace(' ', '_'); id = id.toUpperCase().replace(' ', '_');
@ -180,6 +188,7 @@ public class BiomeMap {
id = id + "_" + idx; id = id + "_" + idx;
} }
this.id = id; this.id = id;
biome_ids.add(this.id);
// If index is NO_INDEX, find one after the well known ones // If index is NO_INDEX, find one after the well known ones
if (idx == NO_INDEX) { if (idx == NO_INDEX) {
idx = LAST_WELL_KNOWN; idx = LAST_WELL_KNOWN;
@ -235,21 +244,15 @@ public class BiomeMap {
} }
public final int getModifiedGrassMultiplier(int rawgrassmult) { public final int getModifiedGrassMultiplier(int rawgrassmult) {
if(grassmult == 0) if (grassmult == 0) return rawgrassmult; // common case: no override
return rawgrassmult; if (grassmult < 0) return -grassmult; // fixed color (pre-masked at set-time)
else if(grassmult > 0xFFFFFF) return ((rawgrassmult & 0xfefefe) + grassmult) >> 1; // blend
return grassmult & 0xFFFFFF;
else
return ((rawgrassmult & 0xfefefe) + grassmult) / 2;
} }
public final int getModifiedFoliageMultiplier(int rawfoliagemult) { public final int getModifiedFoliageMultiplier(int rawfoliagemult) {
if(foliagemult == 0) if (foliagemult == 0) return rawfoliagemult; // common case: no override
return rawfoliagemult; if (foliagemult < 0) return -foliagemult; // fixed color (pre-masked at set-time)
else if(foliagemult > 0xFFFFFF) return ((rawfoliagemult & 0xfefefe) + foliagemult) >> 1; // blend
return foliagemult & 0xFFFFFF;
else
return ((rawfoliagemult & 0xfefefe) + foliagemult) / 2;
} }
public final int getWaterColorMult() { public final int getWaterColorMult() {
return watercolormult; return watercolormult;
@ -278,10 +281,10 @@ public class BiomeMap {
this.watercolormult = watercolormult; this.watercolormult = watercolormult;
} }
public void setGrassColorMultiplier(int grassmult) { public void setGrassColorMultiplier(int grassmult) {
this.grassmult = grassmult; this.grassmult = encodeColorMult(grassmult);
} }
public void setFoliageColorMultiplier(int foliagemult) { public void setFoliageColorMultiplier(int foliagemult) {
this.foliagemult = foliagemult; this.foliagemult = encodeColorMult(foliagemult);
} }
public void setTemperature(double tmp) { public void setTemperature(double tmp) {
if(tmp < 0.0) tmp = 0.0; if(tmp < 0.0) tmp = 0.0;

View file

@ -38,7 +38,7 @@ public class GenericChunk {
// Get section for given block Y coord // Get section for given block Y coord
public final GenericChunkSection getSection(int y) { public final GenericChunkSection getSection(int y) {
int idx = (y >> 4) - this.cy_min; int idx = (y >> 4) - this.cy_min;
if ((idx < 0) || (idx >= sectionCnt)) { if ((idx < 0) || (idx >= sections.length)) {
return GenericChunkSection.EMPTY; return GenericChunkSection.EMPTY;
} }
return this.sections[idx]; return this.sections[idx];

View file

@ -4,6 +4,7 @@ import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -23,22 +24,26 @@ public class GenericChunkCache {
private long cache_attempts; private long cache_attempts;
private long cache_success; private long cache_success;
private boolean softref; private boolean softref;
// World name -> small integer ID, used to build long cache keys without String concatenation.
// Accessed only while holding snapcachelock.
private final HashMap<String, Integer> worldIds = new HashMap<String, Integer>();
private int nextWorldId = 0;
private static class CacheRec { private static class CacheRec {
Reference<ChunkCacheRec> ref; Reference<ChunkCacheRec> ref;
} }
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class CacheHashMap extends LinkedHashMap<String, CacheRec> { public class CacheHashMap extends LinkedHashMap<Long, CacheRec> {
private int limit; private int limit;
private IdentityHashMap<Reference<ChunkCacheRec>, String> reverselookup; private IdentityHashMap<Reference<ChunkCacheRec>, Long> reverselookup;
public CacheHashMap(int lim) { public CacheHashMap(int lim) {
super(16, (float)0.75, true); super(16, (float)0.75, true);
limit = lim; limit = lim;
reverselookup = new IdentityHashMap<Reference<ChunkCacheRec>, String>(); reverselookup = new IdentityHashMap<Reference<ChunkCacheRec>, Long>();
} }
protected boolean removeEldestEntry(Map.Entry<String, CacheRec> last) { protected boolean removeEldestEntry(Map.Entry<Long, CacheRec> last) {
boolean remove = (size() >= limit); boolean remove = (size() >= limit);
if(remove && (last != null) && (last.getValue() != null)) { if(remove && (last != null) && (last.getValue() != null)) {
reverselookup.remove(last.getValue().ref); reverselookup.remove(last.getValue().ref);
@ -56,15 +61,26 @@ public class GenericChunkCache {
refqueue = new ReferenceQueue<ChunkCacheRec>(); refqueue = new ReferenceQueue<ChunkCacheRec>();
this.softref = softref; this.softref = softref;
} }
private String getKey(String w, int cx, int cz) { /**
return w + ":" + cx + ":" + cz; * Encode world name + chunk coords as a single long key.
* Worlds are assigned small integer IDs (10 bits) on first use.
* cx and cz are each encoded in 27 bits (signed, supports ±8M chunks / ±128M blocks).
* Must be called while holding snapcachelock.
*/
private long getKey(String w, int cx, int cz) {
Integer wid = worldIds.get(w);
if (wid == null) {
wid = nextWorldId++;
worldIds.put(w, wid);
}
return ((long)(wid & 0x3FF) << 54) | ((long)(cx & 0x7FFFFFF) << 27) | (long)(cz & 0x7FFFFFF);
} }
/** /**
* Invalidate cached snapshot, if in cache * Invalidate cached snapshot, if in cache
*/ */
public void invalidateSnapshot(String w, int x, int y, int z) { public void invalidateSnapshot(String w, int x, int y, int z) {
String key = getKey(w, x>>4, z>>4);
synchronized(snapcachelock) { synchronized(snapcachelock) {
long key = getKey(w, x>>4, z>>4);
CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null; CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null;
if(rec != null) { if(rec != null) {
snapcache.reverselookup.remove(rec.ref); snapcache.reverselookup.remove(rec.ref);
@ -77,10 +93,10 @@ public class GenericChunkCache {
* Invalidate cached snapshot, if in cache * Invalidate cached snapshot, if in cache
*/ */
public void invalidateSnapshot(String w, int x0, int y0, int z0, int x1, int y1, int z1) { public void invalidateSnapshot(String w, int x0, int y0, int z0, int x1, int y1, int z1) {
synchronized(snapcachelock) {
for(int xx = (x0>>4); xx <= (x1>>4); xx++) { for(int xx = (x0>>4); xx <= (x1>>4); xx++) {
for(int zz = (z0>>4); zz <= (z1>>4); zz++) { for(int zz = (z0>>4); zz <= (z1>>4); zz++) {
String key = getKey(w, xx, zz); long key = getKey(w, xx, zz);
synchronized(snapcachelock) {
CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null; CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null;
if(rec != null) { if(rec != null) {
snapcache.reverselookup.remove(rec.ref); snapcache.reverselookup.remove(rec.ref);
@ -95,11 +111,11 @@ public class GenericChunkCache {
* Look for chunk snapshot in cache * Look for chunk snapshot in cache
*/ */
public ChunkCacheRec getSnapshot(String w, int chunkx, int chunkz) { public ChunkCacheRec getSnapshot(String w, int chunkx, int chunkz) {
String key = getKey(w, chunkx, chunkz);
processRefQueue(); processRefQueue();
ChunkCacheRec ss = null; ChunkCacheRec ss = null;
CacheRec rec; CacheRec rec;
synchronized(snapcachelock) { synchronized(snapcachelock) {
long key = getKey(w, chunkx, chunkz);
rec = (snapcache != null) ? snapcache.get(key) : null; rec = (snapcache != null) ? snapcache.get(key) : null;
if(rec != null) { if(rec != null) {
ss = rec.ref.get(); ss = rec.ref.get();
@ -118,7 +134,6 @@ public class GenericChunkCache {
* Add chunk snapshot to cache * Add chunk snapshot to cache
*/ */
public void putSnapshot(String w, int chunkx, int chunkz, ChunkCacheRec ss) { public void putSnapshot(String w, int chunkx, int chunkz, ChunkCacheRec ss) {
String key = getKey(w, chunkx, chunkz);
processRefQueue(); processRefQueue();
CacheRec rec = new CacheRec(); CacheRec rec = new CacheRec();
if (softref) if (softref)
@ -126,6 +141,7 @@ public class GenericChunkCache {
else else
rec.ref = new WeakReference<ChunkCacheRec>(ss, refqueue); rec.ref = new WeakReference<ChunkCacheRec>(ss, refqueue);
synchronized(snapcachelock) { synchronized(snapcachelock) {
long key = getKey(w, chunkx, chunkz);
CacheRec prevrec = (snapcache != null) ? snapcache.put(key, rec) : null; CacheRec prevrec = (snapcache != null) ? snapcache.put(key, rec) : null;
if(prevrec != null) { if(prevrec != null) {
snapcache.reverselookup.remove(prevrec.ref); snapcache.reverselookup.remove(prevrec.ref);
@ -140,7 +156,7 @@ public class GenericChunkCache {
Reference<? extends ChunkCacheRec> ref; Reference<? extends ChunkCacheRec> ref;
while((ref = refqueue.poll()) != null) { while((ref = refqueue.poll()) != null) {
synchronized(snapcachelock) { synchronized(snapcachelock) {
String k = (snapcache != null) ? snapcache.reverselookup.remove(ref) : null; Long k = (snapcache != null) ? snapcache.reverselookup.remove(ref) : null;
if(k != null) { if(k != null) {
snapcache.remove(k); snapcache.remove(k);
} }

View file

@ -25,7 +25,7 @@ public class GenericChunkSection {
blocks = bs; blocks = bs;
} }
public final DynmapBlockState getBlock(int x, int y, int z) { public final DynmapBlockState getBlock(int x, int y, int z) {
return blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]; return blocks[((y & 0xF) << 8) | ((z & 0xF) << 4) | (x & 0xF)];
} }
public final DynmapBlockState getBlock(GenericChunkPos pos) { public final DynmapBlockState getBlock(GenericChunkPos pos) {
return blocks[pos.soffset]; return blocks[pos.soffset];
@ -40,7 +40,7 @@ public class GenericChunkSection {
palette = pal; palette = pal;
} }
public final DynmapBlockState getBlock(int x, int y, int z) { public final DynmapBlockState getBlock(int x, int y, int z) {
return palette[blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]]; return palette[blocks[((y & 0xF) << 8) | ((z & 0xF) << 4) | (x & 0xF)]];
} }
public final DynmapBlockState getBlock(GenericChunkPos pos) { public final DynmapBlockState getBlock(GenericChunkPos pos) {
return palette[blocks[pos.soffset]]; return palette[blocks[pos.soffset]];
@ -126,12 +126,12 @@ public class GenericChunkSection {
light = new long[256]; light = new long[256];
if (lig != null) { if (lig != null) {
for (int off = 0; (off < lig.length) && (off < 2048); off++) { for (int off = 0; (off < lig.length) && (off < 2048); off++) {
light[off >> 3] |= (0xFFL & (long)lig[off]) << (8 * (off & 0x7)); light[off >> 3] |= (0xFFL & (long)lig[off]) << ((off & 0x7) << 3);
} }
} }
} }
public final int getLight(int x, int y, int z) { public final int getLight(int x, int y, int z) {
return 0xF & (int)(light[(16 * (y & 0xF)) + (z & 0xF)] >> (4 * (x & 0xF))); return 0xF & (int)(light[((y & 0xF) << 4) | (z & 0xF)] >> ((x & 0xF) << 2));
} }
public final int getLight(GenericChunkPos pos) { public final int getLight(GenericChunkPos pos) {
return 0xF & (int)(light[pos.soffset >> 4] >> (4 * pos.sx)); return 0xF & (int)(light[pos.soffset >> 4] >> (4 * pos.sx));

View file

@ -154,9 +154,7 @@ public class ShadowHDLighting extends DefaultHDLighting {
} }
} }
if(cscale < 256) { if(cscale < 256) {
Color c = outcolor[0]; outcolor[0].scaleRGB(cscale);
c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8,
(c.getBlue() * cscale) >> 8, c.getAlpha());
} }
if(outcolor.length > 1) { if(outcolor.length > 1) {
ll0 = getLightLevel(skyemit0, false); ll0 = getLightLevel(skyemit0, false);
@ -194,9 +192,7 @@ public class ShadowHDLighting extends DefaultHDLighting {
} }
} }
if(cscale < 256) { if(cscale < 256) {
Color c = outcolor[1]; outcolor[1].scaleRGB(cscale);
c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8,
(c.getBlue() * cscale) >> 8, c.getAlpha());
} }
} }
} }
@ -269,8 +265,7 @@ public class ShadowHDLighting extends DefaultHDLighting {
private final void shadowColor(Color c, int lightlevel, int[] shadowscale) { private final void shadowColor(Color c, int lightlevel, int[] shadowscale) {
int scale = shadowscale[lightlevel]; int scale = shadowscale[lightlevel];
if(scale < 256) if(scale < 256)
c.setRGBA((c.getRed() * scale) >> 8, (c.getGreen() * scale) >> 8, c.scaleRGB(scale);
(c.getBlue() * scale) >> 8, c.getAlpha());
} }

View file

@ -97,16 +97,21 @@ public class PatchDefinition implements RenderPatch {
*/ */
PatchDefinition(PatchDefinition orig, double rotatex, double rotatey, double rotatez, Vector3D rotorigin, int textureindex) { PatchDefinition(PatchDefinition orig, double rotatex, double rotatey, double rotatez, Vector3D rotorigin, int textureindex) {
if (rotorigin == null) rotorigin = offsetCenter; if (rotorigin == null) rotorigin = offsetCenter;
/* Precompute trig once - reused for all three vector rotations */
double sinX = 0, cosX = 1, sinY = 0, cosY = 1, sinZ = 0, cosZ = 1;
if (rotatex != 0) { double r = Math.toRadians(rotatex); sinX = Math.sin(r); cosX = Math.cos(r); }
if (rotatey != 0) { double r = Math.toRadians(rotatey); sinY = Math.sin(r); cosY = Math.cos(r); }
if (rotatez != 0) { double r = Math.toRadians(rotatez); sinZ = Math.sin(r); cosZ = Math.cos(r); }
Vector3D vec = new Vector3D(orig.x0, orig.y0, orig.z0); Vector3D vec = new Vector3D(orig.x0, orig.y0, orig.z0);
rotate(vec, rotatex, rotatey, rotatez, rotorigin); /* Rotate origin */ rotatePrecomputed(vec, sinX, cosX, sinY, cosY, sinZ, cosZ, rotorigin); /* Rotate origin */
x0 = vec.x; y0 = vec.y; z0 = vec.z; x0 = vec.x; y0 = vec.y; z0 = vec.z;
/* Rotate U */ /* Rotate U */
vec.x = orig.xu; vec.y = orig.yu; vec.z = orig.zu; vec.x = orig.xu; vec.y = orig.yu; vec.z = orig.zu;
rotate(vec, rotatex, rotatey, rotatez, rotorigin); /* Rotate origin */ rotatePrecomputed(vec, sinX, cosX, sinY, cosY, sinZ, cosZ, rotorigin);
xu = vec.x; yu = vec.y; zu = vec.z; xu = vec.x; yu = vec.y; zu = vec.z;
/* Rotate V */ /* Rotate V */
vec.x = orig.xv; vec.y = orig.yv; vec.z = orig.zv; vec.x = orig.xv; vec.y = orig.yv; vec.z = orig.zv;
rotate(vec, rotatex, rotatey, rotatez, rotorigin); /* Rotate origin */ rotatePrecomputed(vec, sinX, cosX, sinY, cosY, sinZ, cosZ, rotorigin);
xv = vec.x; yv = vec.y; zv = vec.z; xv = vec.x; yv = vec.y; zv = vec.z;
umin = orig.umin; vmin = orig.vmin; umin = orig.umin; vmin = orig.vmin;
umax = orig.umax; vmax = orig.vmax; umax = orig.umax; vmax = orig.vmax;
@ -120,26 +125,30 @@ public class PatchDefinition implements RenderPatch {
update(); update();
} }
private void rotate(Vector3D vec, double xcnt, double ycnt, double zcnt, Vector3D origin) { private static void rotatePrecomputed(Vector3D vec,
// If no rotation, skip double sinX, double cosX, double sinY, double cosY, double sinZ, double cosZ,
if ((xcnt == 0) && (ycnt == 0) && (zcnt == 0)) return; Vector3D origin) {
vec.subtract(origin); /* Shoft to center of block */ if (sinX == 0 && sinY == 0 && sinZ == 0) return;
vec.subtract(origin);
/* Do X rotation */ /* Do X rotation */
double rot = Math.toRadians(xcnt); if (sinX != 0) {
double nval = vec.z * Math.sin(rot) + vec.y * Math.cos(rot); double nval = vec.z * sinX + vec.y * cosX;
vec.z = vec.z * Math.cos(rot) - vec.y * Math.sin(rot); vec.z = vec.z * cosX - vec.y * sinX;
vec.y = nval; vec.y = nval;
}
/* Do Y rotation */ /* Do Y rotation */
rot = Math.toRadians(ycnt); if (sinY != 0) {
nval = vec.x * Math.cos(rot) - vec.z * Math.sin(rot); double nval = vec.x * cosY - vec.z * sinY;
vec.z = vec.x * Math.sin(rot) + vec.z * Math.cos(rot); vec.z = vec.x * sinY + vec.z * cosY;
vec.x = nval; vec.x = nval;
}
/* Do Z rotation */ /* Do Z rotation */
rot = Math.toRadians(zcnt); if (sinZ != 0) {
nval = vec.y * Math.sin(rot) + vec.x * Math.cos(rot); double nval = vec.y * sinZ + vec.x * cosZ;
vec.y = vec.y * Math.cos(rot) - vec.x * Math.sin(rot); vec.y = vec.y * cosZ - vec.x * sinZ;
vec.x = nval; vec.x = nval;
vec.add(origin); /* Shoft back to corner */ }
vec.add(origin);
} }
public void update(double x0, double y0, double z0, double xu, public void update(double x0, double y0, double z0, double xu,
double yu, double zu, double xv, double yv, double zv, double umin, double yu, double zu, double xv, double yv, double zv, double umin,
@ -171,7 +180,7 @@ public class PatchDefinition implements RenderPatch {
/* Compute hash code */ /* Compute hash code */
hc = (int)((Double.doubleToLongBits(x0 + xu + xv) >> 32) ^ hc = (int)((Double.doubleToLongBits(x0 + xu + xv) >> 32) ^
(Double.doubleToLongBits(y0 + yu + yv) >> 34) ^ (Double.doubleToLongBits(y0 + yu + yv) >> 34) ^
(Double.doubleToLongBits(z0 + yu + yv) >> 36) ^ (Double.doubleToLongBits(z0 + zu + zv) >> 36) ^
(Double.doubleToLongBits(umin + umax + vmin + vmax + vmaxatumax) >> 38)) ^ (Double.doubleToLongBits(umin + umax + vmin + vmax + vmaxatumax) >> 38)) ^
(sidevis.ordinal() << 8) ^ textureindex; (sidevis.ordinal() << 8) ^ textureindex;
/* Now compute normal of surface - U cross V */ /* Now compute normal of surface - U cross V */
@ -221,27 +230,27 @@ public class PatchDefinition implements RenderPatch {
} }
public boolean validate() { public boolean validate() {
boolean good = true; boolean good = true;
// Compute visible corners to see if we're inside cube // Compute visible corners to see if we're inside cube (u.x = xu-x0, v.x = xv-x0)
double xx0 = x0 + (xu - x0) * umin + (xv - x0) * vmin; double xx0 = x0 + u.x * umin + v.x * vmin;
double xx1 = x0 + (xu - x0) * vmin + (xv - x0) * vmax; double xx1 = x0 + u.x * vmin + v.x * vmax;
double xx2 = x0 + (xu - x0) * umax + (xv - x0) * vmin; double xx2 = x0 + u.x * umax + v.x * vmin;
double xx3 = x0 + (xu - x0) * vmax + (xv - x0) * vmax;; double xx3 = x0 + u.x * vmax + v.x * vmax;
if (outOfRange(xx0) || outOfRange(xx1) || outOfRange(xx2) || outOfRange(xx3)) { if (outOfRange(xx0) || outOfRange(xx1) || outOfRange(xx2) || outOfRange(xx3)) {
Log.verboseinfo(String.format("Invalid visible range xu=[%f:%f], xv=[%f:%f]", xx0, xx2, xx1, xx3)); Log.verboseinfo(String.format("Invalid visible range xu=[%f:%f], xv=[%f:%f]", xx0, xx2, xx1, xx3));
good = false; good = false;
} }
double yy0 = y0 + (yu - y0) * umin + (yv - y0) * vmin; double yy0 = y0 + u.y * umin + v.y * vmin;
double yy1 = y0 + (yu - y0) * vmin + (yv - y0) * vmax; double yy1 = y0 + u.y * vmin + v.y * vmax;
double yy2 = y0 + (yu - y0) * umax + (yv - y0) * vmin; double yy2 = y0 + u.y * umax + v.y * vmin;
double yy3 = y0 + (yu - y0) * vmax + (yv - y0) * vmax;; double yy3 = y0 + u.y * vmax + v.y * vmax;
if (outOfRange(yy0) || outOfRange(yy1) || outOfRange(yy2) || outOfRange(yy3)) { if (outOfRange(yy0) || outOfRange(yy1) || outOfRange(yy2) || outOfRange(yy3)) {
Log.verboseinfo(String.format("Invalid visible range yu=[%f:%f], yv=[%f:%f]", yy0, yy2, yy1, yy3)); Log.verboseinfo(String.format("Invalid visible range yu=[%f:%f], yv=[%f:%f]", yy0, yy2, yy1, yy3));
good = false; good = false;
} }
double zz0 = z0 + (zu - z0) * umin + (zv - z0) * vmin; double zz0 = z0 + u.z * umin + v.z * vmin;
double zz1 = z0 + (zu - z0) * vmin + (zv - z0) * vmax; double zz1 = z0 + u.z * vmin + v.z * vmax;
double zz2 = z0 + (zu - z0) * umax + (zv - z0) * vmin; double zz2 = z0 + u.z * umax + v.z * vmin;
double zz3 = z0 + (zu - z0) * vmax + (zv - z0) * vmax; double zz3 = z0 + u.z * vmax + v.z * vmax;
if (outOfRange(zz0) || outOfRange(zz1) || outOfRange(zz2) || outOfRange(zz3)) { if (outOfRange(zz0) || outOfRange(zz1) || outOfRange(zz2) || outOfRange(zz3)) {
Log.verboseinfo(String.format("Invalid visible range zu=[%f:%f], zv=[%f:%f]", zz0, zz2, zz1, zz3)); Log.verboseinfo(String.format("Invalid visible range zu=[%f:%f], zv=[%f:%f]", zz0, zz2, zz1, zz3));
good = false; good = false;

View file

@ -306,11 +306,10 @@ public class DynmapBlockState {
*/ */
public static final DynmapBlockState getStateByGlobalIndex(int gidx) { public static final DynmapBlockState getStateByGlobalIndex(int gidx) {
if (blockArrayByIndex != null) { if (blockArrayByIndex != null) {
try { if (gidx >= 0 && gidx < blockArrayByIndex.length) {
return blockArrayByIndex[gidx]; return blockArrayByIndex[gidx];
} catch (ArrayIndexOutOfBoundsException aioob) {
return AIR;
} }
return AIR;
} }
DynmapBlockState bs = blocksByIndex.get(gidx); DynmapBlockState bs = blocksByIndex.get(gidx);
return (bs != null) ? bs : AIR; return (bs != null) ? bs : AIR;
@ -322,11 +321,10 @@ public class DynmapBlockState {
*/ */
public static final DynmapBlockState getStateByLegacyBlockID(int legacyid) { public static final DynmapBlockState getStateByLegacyBlockID(int legacyid) {
if (blockArrayByLegacyID != null) { if (blockArrayByLegacyID != null) {
try { if (legacyid >= 0 && legacyid < blockArrayByLegacyID.length) {
return blockArrayByLegacyID[legacyid]; return blockArrayByLegacyID[legacyid];
} catch (ArrayIndexOutOfBoundsException aioob) {
return null;
} }
return null;
} }
return blocksByLegacyID.get(legacyid); return blocksByLegacyID.get(legacyid);
} }
@ -348,7 +346,9 @@ public class DynmapBlockState {
rslt = AIR; // Assume miss rslt = AIR; // Assume miss
String[] statelist = statename.toLowerCase().split(","); String[] statelist = statename.toLowerCase().split(",");
for (DynmapBlockState bb : blk.states) { for (int sidx = 0; sidx <= blk.stateLastIdx; sidx++) {
DynmapBlockState bb = blk.states[sidx];
if (bb == AIR) continue; // Skip AIR fill slots
boolean match = true; boolean match = true;
for (int i = 0; i < statelist.length; i++) { for (int i = 0; i < statelist.length; i++) {
boolean valmatch = false; boolean valmatch = false;
@ -546,10 +546,20 @@ public class DynmapBlockState {
* Test if matches attrib=value pair * Test if matches attrib=value pair
*/ */
public final boolean isStateMatch(String attrib, String value) { public final boolean isStateMatch(String attrib, String value) {
String v = attrib + "=" + value; int alen = attrib.length();
v = v.toLowerCase(); int vlen = value.length();
int total = alen + 1 + vlen;
outer:
for (String state : stateList) { for (String state : stateList) {
if (state.equals(v)) return true; if (state.length() != total) continue;
for (int i = 0; i < alen; i++) {
if (Character.toLowerCase(attrib.charAt(i)) != state.charAt(i)) continue outer;
}
if (state.charAt(alen) != '=') continue;
for (int i = 0; i < vlen; i++) {
if (Character.toLowerCase(value.charAt(i)) != state.charAt(alen + 1 + i)) continue outer;
}
return true;
} }
return false; return false;
} }