mirror of https://github.com/webbukkit/dynmap.git
368 lines
12 KiB
Java
368 lines
12 KiB
Java
package org.dynmap.common.chunk;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import org.dynmap.common.BiomeMap;
|
|
import org.dynmap.renderer.DynmapBlockState;
|
|
|
|
// Generic section: represents 16 x 16 x 16 grid of blocks
|
|
public class GenericChunkSection {
|
|
public final BiomeAccess biomes; // Access for biome data
|
|
public final BlockStateAccess blocks; // Access for block states
|
|
public final LightingAccess sky; // Access for sky light data
|
|
public final LightingAccess emitted; // Access for emitted light data
|
|
public final boolean isEmpty; // if true, section is all air with default sky and emitted light
|
|
|
|
// Block state access interface
|
|
public interface BlockStateAccess {
|
|
public DynmapBlockState getBlock(int x, int y, int z);
|
|
public DynmapBlockState getBlock(GenericChunkPos pos);
|
|
}
|
|
private static class BlockStateAccess3D implements BlockStateAccess {
|
|
private final DynmapBlockState blocks[]; // YZX order
|
|
// Array given to us by builder
|
|
BlockStateAccess3D(DynmapBlockState bs[]) {
|
|
blocks = bs;
|
|
}
|
|
public final DynmapBlockState getBlock(int x, int y, int z) {
|
|
return blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)];
|
|
}
|
|
public final DynmapBlockState getBlock(GenericChunkPos pos) {
|
|
return blocks[pos.soffset];
|
|
}
|
|
}
|
|
private static class BlockStateAccess3DPalette implements BlockStateAccess {
|
|
private final DynmapBlockState palette[];
|
|
private final short[] blocks; // YZX order
|
|
// Array given to us by builder
|
|
BlockStateAccess3DPalette(DynmapBlockState pal[], short[] blks) {
|
|
blocks = blks;
|
|
palette = pal;
|
|
}
|
|
public final DynmapBlockState getBlock(int x, int y, int z) {
|
|
return palette[blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]];
|
|
}
|
|
public final DynmapBlockState getBlock(GenericChunkPos pos) {
|
|
return palette[blocks[pos.soffset]];
|
|
}
|
|
}
|
|
private static class BlockStateAccessSingle implements BlockStateAccess {
|
|
private final DynmapBlockState block;
|
|
BlockStateAccessSingle(DynmapBlockState bs) {
|
|
block = bs;
|
|
}
|
|
public final DynmapBlockState getBlock(int x, int y, int z) {
|
|
return block;
|
|
}
|
|
public final DynmapBlockState getBlock(GenericChunkPos pos) {
|
|
return block;
|
|
}
|
|
}
|
|
// Biome access interface
|
|
public interface BiomeAccess {
|
|
public BiomeMap getBiome(int x, int y, int z);
|
|
public BiomeMap getBiome(GenericChunkPos pos);
|
|
}
|
|
// For classic 2D biome map
|
|
private static class BiomeAccess2D implements BiomeAccess {
|
|
private final BiomeMap biomes[]; // (16 * Z) + X
|
|
// Array given to us by builder in right format
|
|
BiomeAccess2D(BiomeMap b[]) {
|
|
biomes = b;
|
|
}
|
|
public final BiomeMap getBiome(int x, int y, int z) {
|
|
return biomes[((z & 0xF) << 4) + (x & 0xF)];
|
|
}
|
|
public final BiomeMap getBiome(GenericChunkPos pos) {
|
|
return biomes[pos.soffset & 0xFF]; // Just ZX portion
|
|
}
|
|
public String toString() {
|
|
return String.format("Biome2D(%s)", Arrays.deepToString(biomes));
|
|
}
|
|
}
|
|
// For 3D biome map
|
|
private static class BiomeAccess3D implements BiomeAccess {
|
|
private final BiomeMap biomes[]; // (16 * (Y >> 2)) + (4 * (Z >> 2)) + (X >> 2)
|
|
// Array given to us by builder in right format (64 - YZX divided by 4)
|
|
BiomeAccess3D(BiomeMap[] b) {
|
|
biomes = b;
|
|
}
|
|
public final BiomeMap getBiome(int x, int y, int z) {
|
|
return biomes[ ((y & 0xC) << 2) | (z & 0xC) | ((x & 0xC) >> 2) ];
|
|
}
|
|
public final BiomeMap getBiome(GenericChunkPos pos) {
|
|
return biomes[pos.sdiv4offset];
|
|
}
|
|
public String toString() {
|
|
return String.format("Biome3D(%s)", Arrays.deepToString(biomes));
|
|
}
|
|
}
|
|
// For single biome map
|
|
private static class BiomeAccessSingle implements BiomeAccess {
|
|
private final BiomeMap biome;
|
|
BiomeAccessSingle(BiomeMap b) {
|
|
biome = b;
|
|
}
|
|
public final BiomeMap getBiome(int x, int y, int z) {
|
|
return biome;
|
|
}
|
|
public final BiomeMap getBiome(GenericChunkPos pos) {
|
|
return biome;
|
|
}
|
|
public String toString() {
|
|
return String.format("Biome1(%s)", biome);
|
|
}
|
|
}
|
|
// Lighting access interface
|
|
public interface LightingAccess {
|
|
public int getLight(int x, int y, int z);
|
|
public int getLight(GenericChunkPos pos);
|
|
}
|
|
private static class LightingAccess3D implements LightingAccess {
|
|
private final long[] light; // Nibble array (16 * y) * z (nibble at << (4*x))
|
|
|
|
// Construct using nibble array (same as lighting format in NBT fields) (128*Y + 8*Z + X/2) (oddX high, evenX low)
|
|
LightingAccess3D(byte[] lig) {
|
|
light = new long[256];
|
|
if (lig != null) {
|
|
for (int off = 0; (off < lig.length) && (off < 2048); off++) {
|
|
light[off >> 3] |= (0xFFL & (long)lig[off]) << (8 * (off & 0x7));
|
|
}
|
|
}
|
|
}
|
|
public final int getLight(int x, int y, int z) {
|
|
return 0xF & (int)(light[(16 * (y & 0xF)) + (z & 0xF)] >> (4 * (x & 0xF)));
|
|
}
|
|
public final int getLight(GenericChunkPos pos) {
|
|
return 0xF & (int)(light[pos.soffset >> 4] >> (4 * pos.sx));
|
|
}
|
|
}
|
|
private static class LightingAccessSingle implements LightingAccess {
|
|
private final int light;
|
|
LightingAccessSingle(int lig) {
|
|
light = lig & 0xF;
|
|
}
|
|
public final int getLight(int x, int y, int z) {
|
|
return light;
|
|
}
|
|
public final int getLight(GenericChunkPos pos) {
|
|
return light;
|
|
}
|
|
}
|
|
private GenericChunkSection(BlockStateAccess blks, BiomeAccess bio, LightingAccess skyac, LightingAccess emitac, boolean empty) {
|
|
blocks = blks;
|
|
biomes = bio;
|
|
sky = skyac;
|
|
emitted = emitac;
|
|
isEmpty = empty;
|
|
}
|
|
public String toString() {
|
|
return String.format("sect(bip:%s)", biomes);
|
|
}
|
|
private static BiomeAccess defaultBiome = new BiomeAccessSingle(BiomeMap.NULL);
|
|
private static BlockStateAccess defaultBlockState = new BlockStateAccessSingle(DynmapBlockState.AIR);
|
|
private static LightingAccess defaultLight = new LightingAccessSingle(0);
|
|
|
|
// Shared default empty section
|
|
public static final GenericChunkSection EMPTY = new GenericChunkSection(defaultBlockState, defaultBiome, new LightingAccessSingle(15), defaultLight, true);
|
|
|
|
// Factory for building section
|
|
public static class Builder {
|
|
private LightingAccess sk;
|
|
private LightingAccess em;
|
|
private DynmapBlockState bsaccumsing; // Used for single
|
|
private DynmapBlockState bsaccum[]; // Use for incremental setting of 3D - YZX order
|
|
private short[] bsblks; // Use for incremental setting of 3D palette - XZY order
|
|
private DynmapBlockState[] bspal; // Palette for bsblks
|
|
private BiomeMap baaccumsingle; // Use for single
|
|
private BiomeMap baaccum[]; // Use for incremental setting of 3D biome - YZX order or 2D biome (ZX order) length used to control which
|
|
private boolean empty;
|
|
// Initialize builder with empty state
|
|
public Builder() {
|
|
reset();
|
|
}
|
|
// Reset builder to default state
|
|
public void reset() {
|
|
bsaccumsing = DynmapBlockState.AIR;
|
|
bsaccum = null;
|
|
baaccumsingle = BiomeMap.NULL;
|
|
baaccum = null;
|
|
bsblks = null;
|
|
bspal = null;
|
|
sk = defaultLight;
|
|
em = defaultLight;
|
|
empty = true;
|
|
}
|
|
// Set sky lighting to single value
|
|
public Builder singleSkyLight(int val) {
|
|
sk = new LightingAccessSingle(val);
|
|
return this;
|
|
}
|
|
// Set sky lighting to given nibble array (YZX order)
|
|
public Builder skyLight(byte[] data) {
|
|
sk = new LightingAccess3D(data);
|
|
return this;
|
|
}
|
|
// Set emitted lighting to single value
|
|
public Builder singleEmittedLight(int val) {
|
|
em = new LightingAccessSingle(val);
|
|
return this;
|
|
}
|
|
// Set emitted lighting to given nibble array (YZX order)
|
|
public Builder emittedLight(byte[] data) {
|
|
em = new LightingAccess3D(data);
|
|
return this;
|
|
}
|
|
// Set bipme to single value
|
|
public Builder singleBiome(BiomeMap bio) {
|
|
baaccumsingle = bio;
|
|
baaccum = null;
|
|
return this;
|
|
}
|
|
// Set bipme for 2D style
|
|
public Builder xzBiome(int x, int z, BiomeMap bio) {
|
|
if ((baaccum == null) || (baaccum.length != 256)) {
|
|
baaccum = new BiomeMap[256];
|
|
Arrays.fill(baaccum, BiomeMap.NULL);
|
|
baaccumsingle = BiomeMap.NULL;
|
|
}
|
|
baaccum[((z & 0xF) << 4) + (x & 0xF)] = bio;
|
|
return this;
|
|
}
|
|
// Set bipme to 3D style
|
|
public Builder xyzBiome(int xdiv4, int ydiv4, int zdiv4, BiomeMap bio) {
|
|
if ((baaccum == null) || (baaccum.length != 64)) {
|
|
baaccum = new BiomeMap[64];
|
|
Arrays.fill(baaccum, BiomeMap.NULL);
|
|
baaccumsingle = BiomeMap.NULL;
|
|
}
|
|
baaccum[((ydiv4 & 0x3) << 4) + ((zdiv4 & 0x3) << 2) + (xdiv4 & 0x3)] = bio;
|
|
return this;
|
|
}
|
|
// Set block state to single value
|
|
public Builder singleBlockState(DynmapBlockState block) {
|
|
bsaccumsing = block;
|
|
bsaccum = null;
|
|
bsblks = null;
|
|
bspal = null;
|
|
empty = block.isAir();
|
|
return this;
|
|
}
|
|
// Set block state
|
|
public Builder xyzBlockState(int x, int y, int z, DynmapBlockState block) {
|
|
if (bsaccum == null) {
|
|
bsaccum = new DynmapBlockState[4096];
|
|
Arrays.fill(bsaccum, DynmapBlockState.AIR);
|
|
bsaccumsing = DynmapBlockState.AIR;
|
|
}
|
|
bsaccum[((y & 0xF) << 8) + ((z & 0xF) << 4) + (x & 0xF)] = block;
|
|
empty = false;
|
|
return this;
|
|
}
|
|
// Set block state palette (states will be indexes vs this
|
|
public Builder xyzBlockStatePalette(DynmapBlockState[] bspalette) {
|
|
if (bsblks == null) {
|
|
bsblks = new short[4096];
|
|
}
|
|
bspal = Arrays.copyOf(bspalette, bspalette.length);
|
|
return this;
|
|
}
|
|
// Set block state using palette
|
|
public Builder xyzBlockStateInPalette(int x, int y, int z, short palidx) {
|
|
if (bsblks == null) {
|
|
bsblks = new short[4096];
|
|
}
|
|
bsblks[((y & 0xF) << 8) + ((z & 0xF) << 4) + (x & 0xF)] = palidx;
|
|
empty = false;
|
|
return this;
|
|
}
|
|
// Build copy from existing section with new skylight (YZX nibble array)
|
|
public GenericChunkSection buildFrom(GenericChunkSection s, byte[] sky) {
|
|
LightingAccess skyA = new LightingAccess3D(sky);
|
|
return new GenericChunkSection(s.blocks, s.biomes, skyA, s.emitted, s.isEmpty);
|
|
}
|
|
// Build copy from existing section with new single value skylight
|
|
public GenericChunkSection buildFrom(GenericChunkSection s, int singlesky) {
|
|
LightingAccess skyA = new LightingAccessSingle(singlesky);
|
|
return new GenericChunkSection(s.blocks, s.biomes, skyA, s.emitted, s.isEmpty);
|
|
}
|
|
|
|
// Build section based on current builder state
|
|
public GenericChunkSection build() {
|
|
// Process state access - see if we can reduce
|
|
if (bsaccum != null) {
|
|
DynmapBlockState v = bsaccum[0]; // Get first
|
|
boolean mismatch = false;
|
|
for (int i = 0; i < bsaccum.length; i++) {
|
|
if (bsaccum[i] != v) {
|
|
mismatch = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!mismatch) { // All the same?
|
|
bsaccumsing = v;
|
|
bsaccum = null;
|
|
}
|
|
}
|
|
BlockStateAccess bs;
|
|
if (bsaccum != null) {
|
|
bs = new BlockStateAccess3D(bsaccum);
|
|
bsaccum = null;
|
|
empty = false;
|
|
}
|
|
else if (bspal != null) { // 3D palette
|
|
// Only one state in palette?
|
|
if (bspal.length == 1) {
|
|
bs = new BlockStateAccessSingle(bspal[0]); // Just single
|
|
}
|
|
else {
|
|
bs = new BlockStateAccess3DPalette(bspal, bsblks);
|
|
}
|
|
bspal = null;
|
|
bsblks = null;
|
|
}
|
|
else if (bsaccumsing == DynmapBlockState.AIR) { // Just air?
|
|
bs = defaultBlockState;
|
|
empty = true;
|
|
}
|
|
else {
|
|
bs = new BlockStateAccessSingle(bsaccumsing);
|
|
bsaccumsing = DynmapBlockState.AIR;
|
|
}
|
|
// See if biome access can be reduced to single
|
|
if (baaccum != null) {
|
|
BiomeMap v = baaccum[0]; // Get first
|
|
boolean mismatch = false;
|
|
for (int i = 0; i < baaccum.length; i++) {
|
|
if (baaccum[i] != v) {
|
|
mismatch = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!mismatch) { // All the same?
|
|
baaccumsingle = v;
|
|
baaccum = null;
|
|
}
|
|
}
|
|
BiomeAccess ba;
|
|
if (baaccum != null) {
|
|
if (baaccum.length == 64) { // 3D?
|
|
ba = new BiomeAccess3D(baaccum);
|
|
}
|
|
else {
|
|
ba = new BiomeAccess2D(baaccum);
|
|
}
|
|
baaccum = null;
|
|
}
|
|
else if (baaccumsingle == BiomeMap.NULL) { // Just null?
|
|
ba = defaultBiome;
|
|
}
|
|
else {
|
|
ba = new BiomeAccessSingle(baaccumsingle);
|
|
baaccumsingle = BiomeMap.NULL;
|
|
}
|
|
return new GenericChunkSection(bs, ba, sk, em, empty);
|
|
}
|
|
}
|
|
}
|