diff --git a/src/main/java/org/dynmap/hdmap/HDBlockModels.java b/src/main/java/org/dynmap/hdmap/HDBlockModels.java new file mode 100644 index 00000000..f9e17bbc --- /dev/null +++ b/src/main/java/org/dynmap/hdmap/HDBlockModels.java @@ -0,0 +1,333 @@ +package org.dynmap.hdmap; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.bukkit.Material; + +/** + * Custom block models - used for non-cube blocks to represent the physical volume associated with the block + * Used by perspectives to determine if rays have intersected a block that doesn't occupy its whole block + */ +public class HDBlockModels { + private int blockid; + private int databits; + private long blockflags[]; + private int nativeres; + private HashMap scaledblocks; + + private static HashMap models_by_id_data = new HashMap(); + + public static class HDScaledBlockModels { + private short[][][] modelvectors; + + public final short[] getScaledModel(int blocktype, int blockdata) { + if((blocktype > modelvectors.length) || (modelvectors[blocktype] == null) || + (modelvectors[blocktype][blockdata] == null)) { + return null; + } + return modelvectors[blocktype][blockdata]; + } + } + + private static HashMap scaled_models_by_scale = new HashMap(); + + /* Block models */ + private static HDBlockModels WOOD_STAIR_UP_NORTH = new HDBlockModels(Material.WOOD_STAIRS, 1<<1, 2, new long[] { + 0x03, 0x03, + 0x01, 0x01 }); + private static HDBlockModels WOOD_STAIR_UP_SOUTH = new HDBlockModels(Material.WOOD_STAIRS, 1<<0, 2, new long[] { + 0x03, 0x03, + 0x02, 0x02 }); + private static HDBlockModels WOOD_STAIR_UP_WEST = new HDBlockModels(Material.WOOD_STAIRS, 1<<2, 2, new long[] { + 0x03, 0x03, + 0x00, 0x03 }); + private static HDBlockModels WOOD_STAIR_UP_EAST = new HDBlockModels(Material.WOOD_STAIRS, 1<<3, 2, new long[] { + 0x03, 0x03, + 0x03, 0x00 }); + private static HDBlockModels COBBLE_STAIR_UP_NORTH = new HDBlockModels(Material.COBBLESTONE_STAIRS, 1<<1, WOOD_STAIR_UP_NORTH); + private static HDBlockModels COBBLE_STAIR_UP_SOUTH = new HDBlockModels(Material.COBBLESTONE_STAIRS, 1<<0, WOOD_STAIR_UP_SOUTH); + private static HDBlockModels COBBLE_STAIR_UP_WEST = new HDBlockModels(Material.COBBLESTONE_STAIRS, 1<<2, WOOD_STAIR_UP_WEST); + private static HDBlockModels COBBLE_STAIR_UP_EAST = new HDBlockModels(Material.COBBLESTONE_STAIRS, 1<<3, WOOD_STAIR_UP_EAST); + private static HDBlockModels STEP = new HDBlockModels(Material.STEP, 0x0F, 2, new long[] { + 0x03, + 0x03 }); + private static HDBlockModels SNOW = new HDBlockModels(Material.SNOW, 0x0F, 4, new long[] { + 0x0F, 0x0F, 0x0F, 0x0F }); + private static HDBlockModels TORCH_UP = new HDBlockModels(Material.TORCH, (1<<5) | (1 << 0), 4, new long[] { + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00 }); + private static HDBlockModels TORCH_SOUTH = new HDBlockModels(Material.TORCH, (1<<1), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels TORCH_NORTH = new HDBlockModels(Material.TORCH, (1<<2), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels TORCH_WEST = new HDBlockModels(Material.TORCH, (1<<3), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels TORCH_EAST = new HDBlockModels(Material.TORCH, (1<<4), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels REDSTONETORCHON_UP = new HDBlockModels(Material.REDSTONE_TORCH_ON, (1<<5) | (1 << 0), TORCH_UP); + private static HDBlockModels REDSTONETORCHOFF_UP = new HDBlockModels(Material.REDSTONE_TORCH_OFF, (1<<5) | (1 << 0), TORCH_UP); + private static HDBlockModels REDSTONETORCHON_NORTH = new HDBlockModels(Material.REDSTONE_TORCH_ON, (1<<2), TORCH_NORTH); + private static HDBlockModels REDSTONETORCHOFF_NORTH = new HDBlockModels(Material.REDSTONE_TORCH_OFF, (1<<2), TORCH_NORTH); + private static HDBlockModels REDSTONETORCHON_SOUTH = new HDBlockModels(Material.REDSTONE_TORCH_ON, (1<<1), TORCH_SOUTH); + private static HDBlockModels REDSTONETORCHOFF_SOUTH = new HDBlockModels(Material.REDSTONE_TORCH_OFF, (1<<1), TORCH_SOUTH); + private static HDBlockModels REDSTONETORCHON_EAST = new HDBlockModels(Material.REDSTONE_TORCH_ON, (1<<4), TORCH_EAST); + private static HDBlockModels REDSTONETORCHOFF_EAST = new HDBlockModels(Material.REDSTONE_TORCH_OFF, (1<<4), TORCH_EAST); + private static HDBlockModels REDSTONETORCHON_WEST = new HDBlockModels(Material.REDSTONE_TORCH_ON, (1<<3), TORCH_WEST); + private static HDBlockModels REDSTONETORCHOFF_WEST = new HDBlockModels(Material.REDSTONE_TORCH_OFF, (1<<3), TORCH_WEST); + private static HDBlockModels FENCE = new HDBlockModels(Material.FENCE, 0xFFFF, 4, new long[] { + 0x00, 0x06, 0x06, 0x00, + 0x00, 0x06, 0x06, 0x00, + 0x00, 0x06, 0x06, 0x00, + 0x00, 0x06, 0x06, 0x00 }); + private static HDBlockModels TRAPDOOR = new HDBlockModels(Material.TRAP_DOOR, 0xFFFF, 4, new long[] { + 0x0F, 0x0F, 0x0F, 0x0F }); + private static HDBlockModels WOODPRESSPLATE = new HDBlockModels(Material.WOOD_PLATE, 0xFFFF, 4, new long[] { + 0x0F, 0x0F, 0x0F, 0x0F }); + private static HDBlockModels STONEPRESSPLATE = new HDBlockModels(Material.STONE_PLATE, 0xFFFF, 4, new long[] { + 0x0F, 0x0F, 0x0F, 0x0F }); + private static HDBlockModels WALLSIGN_NORTH = new HDBlockModels(Material.WALL_SIGN, (1<<4), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels WALLSIGN_SOUTH = new HDBlockModels(Material.WALL_SIGN, (1<<5), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels WALLSIGN_EAST = new HDBlockModels(Material.WALL_SIGN, (1<<2), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, + 0x00, 0x00, 0x00, 0x0F, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels WALLSIGN_WEST = new HDBlockModels(Material.WALL_SIGN, (1<<3), 4, new long[] { + 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels REDSTONEWIRE = new HDBlockModels(Material.REDSTONE_WIRE, 0xFFFF, 8, new long[] { + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18 }); + private static HDBlockModels SIGNPOST_EASTWEST = new HDBlockModels(Material.SIGN_POST, 0xC7C7, 8, new long[] { + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); + private static HDBlockModels SIGHPOST_NORTHSOUTH = new HDBlockModels(Material.SIGN_POST, 0x3838, 8, new long[] { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); + + /** + * Block definition - copy from other + */ + public HDBlockModels(Material blocktype, int databits, HDBlockModels m) { + this.blockid = blocktype.getId(); + this.databits = databits; + this.nativeres = m.nativeres; + this.blockflags = m.blockflags; + for(int i = 0; i < 16; i++) { + if((databits & (1<(); } + short[] map = scaledblocks.get(Integer.valueOf(res)); + if(map == null) { + map = new short[res*res*res]; + if(res == nativeres) { + for(int i = 0; i < blockflags.length; i++) { + for(int j = 0; j < nativeres; j++) { + if((blockflags[i] & (1 << j)) != 0) + map[res*i+j] = 255; + } + } + } + /* If scaling from smaller sub-blocks to larger, each subblock contributes to 1-2 blocks + * on each axis: need to calculate crossovers for each, and iterate through smaller + * blocks to accumulate contributions + */ + else if(res > nativeres) { + int weights[] = new int[res]; + int offsets[] = new int[res]; + /* LCM of resolutions is used as length of line (res * nativeres) + * Each native block is (res) long, each scaled block is (nativeres) long + * Each scaled block overlaps 1 or 2 native blocks: starting with native block 'offsets[]' with + * 'weights[]' of its (res) width in the first, and the rest in the second + */ + for(int v = 0, idx = 0; v < res*nativeres; v += nativeres, idx++) { + offsets[idx] = (v/res); /* Get index of the first native block we draw from */ + if((v+nativeres-1)/res == offsets[idx]) { /* If scaled block ends in same native block */ + weights[idx] = nativeres; + } + else { /* Else, see how much is in first one */ + weights[idx] = (offsets[idx] + res) - v; + } + } + /* Now, use weights and indices to fill in scaled map */ + for(int y = 0, off = 0; y < res; y++) { + int ind_y = offsets[y]; + int wgt_y = weights[y]; + for(int z = 0; z < res; z++) { + int ind_z = offsets[z]; + int wgt_z = weights[z]; + for(int x = 0; x < res; x++, off++) { + int ind_x = offsets[x]; + int wgt_x = weights[x]; + int raw_w = 0; + for(int xx = 0; xx < 2; xx++) { + int wx = (xx==0)?wgt_x:(nativeres-wgt_x); + if(wx == 0) continue; + for(int yy = 0; yy < 2; yy++) { + int wy = (yy==0)?wgt_y:(nativeres-wgt_y); + if(wy == 0) continue; + for(int zz = 0; zz < 2; zz++) { + int wz = (zz==0)?wgt_z:(nativeres-wgt_z); + if(wz == 0) continue; + if(isSubblockSet(ind_x+xx, ind_y+yy, ind_z+zz)) { + raw_w += wx*wy*wz; + } + } + } + } + map[off] = (short)((255*raw_w) / (nativeres*nativeres*nativeres)); + } + } + } + } + else { /* nativeres > res */ + int weights[] = new int[nativeres]; + int offsets[] = new int[nativeres]; + /* LCM of resolutions is used as length of line (res * nativeres) + * Each native block is (res) long, each scaled block is (nativeres) long + * Each native block overlaps 1 or 2 scaled blocks: starting with scaled block 'offsets[]' with + * 'weights[]' of its (res) width in the first, and the rest in the second + */ + for(int v = 0, idx = 0; v < res*nativeres; v += res, idx++) { + offsets[idx] = (v/nativeres); /* Get index of the first scaled block we draw to */ + if((v+res-1)/nativeres == offsets[idx]) { /* If native block ends in same scaled block */ + weights[idx] = res; + } + else { /* Else, see how much is in first one */ + weights[idx] = (offsets[idx] + nativeres) - v; + } + } + /* Now, use weights and indices to fill in scaled map */ + for(int y = 0; y < nativeres; y++) { + int ind_y = offsets[y]; + int wgt_y = weights[y]; + for(int z = 0; z < nativeres; z++) { + int ind_z = offsets[z]; + int wgt_z = weights[z]; + for(int x = 0; x < nativeres; x++) { + if(isSubblockSet(x, y, z)) { + int ind_x = offsets[x]; + int wgt_x = weights[x]; + for(int xx = 0; xx < 2; xx++) { + int wx = (xx==0)?wgt_x:(res-wgt_x); + if(wx == 0) continue; + for(int yy = 0; yy < 2; yy++) { + int wy = (yy==0)?wgt_y:(res-wgt_y); + if(wy == 0) continue; + for(int zz = 0; zz < 2; zz++) { + int wz = (zz==0)?wgt_z:(res-wgt_z); + if(wz == 0) continue; + map[(ind_y+yy)*res*res + (ind_z+zz)*res + (ind_x+xx)] += + wx*wy*wz; + } + } + } + } + } + } + } + for(int i = 0; i < map.length; i++) { + map[i] = (short)(255*map[i]/(nativeres*nativeres*nativeres)); + } + } + scaledblocks.put(Integer.valueOf(res), map); + } + return map; + } + + /** + * Get scaled set of models for all modelled blocks + * @param scale + * @return + */ + public static HDScaledBlockModels getModelsForScale(int scale) { + HDScaledBlockModels model = scaled_models_by_scale.get(Integer.valueOf(scale)); + if(model == null) { + model = new HDScaledBlockModels(); + short[][][] blockmodels = new short[256][][]; + for(HDBlockModels m : models_by_id_data.values()) { + short[][] row = blockmodels[m.blockid]; + if(row == null) { + row = new short[16][]; + blockmodels[m.blockid] = row; + } + short[] smod = null; + for(int i = 0; i < 16; i++) { + if((m.databits & (1 << i)) != 0) { + if(smod == null) smod = m.getScaledMap(scale); + row[i] = smod; + } + } + } + model.modelvectors = blockmodels; + scaled_models_by_scale.put(scale, model); + } + return model; + } +} diff --git a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index 7ac227ca..5b6a5320 100644 --- a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -49,6 +49,10 @@ public class IsoHDPerspective implements HDPerspective { private Matrix3D world_to_map; private Matrix3D map_to_world; + /* Scaled models for non-cube blocks */ + private HDBlockModels.HDScaledBlockModels scalemodels; + private int modscale; + /* dimensions of a map tile */ public static final int tileWidth = 128; public static final int tileHeight = 128; @@ -74,6 +78,14 @@ public class IsoHDPerspective implements HDPerspective { Vector3D top, bottom; int px, py; BlockStep laststep = BlockStep.Y_MINUS; + /* Raytrace state variables */ + double dx, dy, dz; + int x, y, z; + double dt_dx, dt_dy, dt_dz, t; + int n; + int x_inc, y_inc, z_inc; + double t_next_y, t_next_x, t_next_z; + boolean nonairhit; /** * Get sky light level - only available if shader requested it */ @@ -115,6 +127,258 @@ public class IsoHDPerspective implements HDPerspective { */ public final int getPixelY() { return py; } + /** + * Initialize raytrace state variables + */ + private void raytrace_init() { + /* Compute total delta on each axis */ + dx = Math.abs(bottom.x - top.x); + dy = Math.abs(bottom.y - top.y); + dz = Math.abs(bottom.z - top.z); + /* Initial block coord */ + x = (int) (Math.floor(top.x)); + y = (int) (Math.floor(top.y)); + z = (int) (Math.floor(top.z)); + /* Compute parametric step (dt) per step on each axis */ + dt_dx = 1.0 / dx; + dt_dy = 1.0 / dy; + dt_dz = 1.0 / dz; + /* Initialize parametric value to 0 (and we're stepping towards 1) */ + t = 0; + /* Compute number of steps and increments for each */ + n = 1; + + /* If perpendicular to X axis */ + if (dx == 0) { + x_inc = 0; + t_next_x = Double.MAX_VALUE; + } + /* If bottom is right of top */ + else if (bottom.x > top.x) { + x_inc = 1; + n += (int) (Math.floor(bottom.x)) - x; + t_next_x = (Math.floor(top.x) + 1 - top.x) * dt_dx; + } + /* Top is right of bottom */ + else { + x_inc = -1; + n += x - (int) (Math.floor(bottom.x)); + t_next_x = (top.x - Math.floor(top.x)) * dt_dx; + } + /* If perpendicular to Y axis */ + if (dy == 0) { + y_inc = 0; + t_next_y = Double.MAX_VALUE; + } + /* If bottom is above top */ + else if (bottom.y > top.y) { + y_inc = 1; + n += (int) (Math.floor(bottom.y)) - y; + t_next_y = (Math.floor(top.y) + 1 - top.y) * dt_dy; + } + /* If top is above bottom */ + else { + y_inc = -1; + n += y - (int) (Math.floor(bottom.y)); + t_next_y = (top.y - Math.floor(top.y)) * dt_dy; + } + /* If perpendicular to Z axis */ + if (dz == 0) { + z_inc = 0; + t_next_z = Double.MAX_VALUE; + } + /* If bottom right of top */ + else if (bottom.z > top.z) { + z_inc = 1; + n += (int) (Math.floor(bottom.z)) - z; + t_next_z = (Math.floor(top.z) + 1 - top.z) * dt_dz; + } + /* If bottom left of top */ + else { + z_inc = -1; + n += z - (int) (Math.floor(bottom.z)); + t_next_z = (top.z - Math.floor(top.z)) * dt_dz; + } + /* Walk through scene */ + laststep = BlockStep.Y_MINUS; /* Last step is down into map */ + skylightlevel = 15; + emittedlightlevel = 0; + nonairhit = false; + } + /** + * Process visit of ray to block + */ + private boolean visit_block(MapIterator mapiter, HDShaderState[] shaderstate, boolean[] shaderdone) { + blocktypeid = mapiter.getBlockTypeID(); + if(nonairhit || (blocktypeid != 0)) { + blockdata = mapiter.getBlockData(); + boolean missed = false; + /* Look up to see if block is modelled */ + short[] model = scalemodels.getScaledModel(blocktypeid, blockdata); + if(model != null) { + missed = raytraceSubblock(model); + } + if(!missed) { + boolean done = true; + for(int i = 0; i < shaderstate.length; i++) { + if(!shaderdone[i]) + shaderdone[i] = shaderstate[i].processBlock(this); + done = done && shaderdone[i]; + } + /* If all are done, we're out */ + if(done) + return true; + nonairhit = true; + } + } + if(need_skylightlevel) + skylightlevel = mapiter.getBlockSkyLight(); + if(need_emittedlightlevel) + emittedlightlevel = mapiter.getBlockEmittedLight(); + return false; + } + /** + * Trace ray, based on "Voxel Tranversal along a 3D line" + */ + private void raytrace(MapChunkCache cache, MapIterator mapiter, HDShaderState[] shaderstate, boolean[] shaderdone) { + /* Initialize raytrace state variables */ + raytrace_init(); + + mapiter.initialize(x, y, z); + + boolean nonairhit = false; + for (; n > 0; --n) { + /* Visit block */ + if(visit_block(mapiter, shaderstate, shaderdone)) { + return; + } + /* If X step is next best */ + if((t_next_x <= t_next_y) && (t_next_x <= t_next_z)) { + x += x_inc; + t = t_next_x; + t_next_x += dt_dx; + if(x_inc > 0) { + laststep = BlockStep.X_PLUS; + mapiter.incrementX(); + } + else { + laststep = BlockStep.X_MINUS; + mapiter.decrementX(); + } + } + /* If Y step is next best */ + else if((t_next_y <= t_next_x) && (t_next_y <= t_next_z)) { + y += y_inc; + t = t_next_y; + t_next_y += dt_dy; + if(y_inc > 0) { + laststep = BlockStep.Y_PLUS; + mapiter.incrementY(); + if(mapiter.getY() > 127) + return; + } + else { + laststep = BlockStep.Y_MINUS; + mapiter.decrementY(); + if(mapiter.getY() < 0) + return; + } + } + /* Else, Z step is next best */ + else { + z += z_inc; + t = t_next_z; + t_next_z += dt_dz; + if(z_inc > 0) { + laststep = BlockStep.Z_PLUS; + mapiter.incrementZ(); + } + else { + laststep = BlockStep.Z_MINUS; + mapiter.decrementZ(); + } + } + } + } + + private boolean raytraceSubblock(short[] model) { + int mx = 0, my = 0, mz = 0; + double xx, yy, zz; + double mt = t + 0.00000001; + xx = top.x + mt *(bottom.x - top.x); + yy = top.y + mt *(bottom.y - top.y); + zz = top.z + mt *(bottom.z - top.z); + mx = (int)((xx - Math.floor(xx)) * modscale); + my = (int)((yy - Math.floor(yy)) * modscale); + mz = (int)((zz - Math.floor(zz)) * modscale); + double mdt_dx = dt_dx / modscale; + double mdt_dy = dt_dy / modscale; + double mdt_dz = dt_dz / modscale; + double togo; + double mt_next_x = t_next_x, mt_next_y = t_next_y, mt_next_z = t_next_z; + if(mt_next_x != Double.MAX_VALUE) { + togo = ((t_next_x - t) / mdt_dx); + mt_next_x = mt + (togo - Math.floor(togo)) * mdt_dx; + } + if(mt_next_y != Double.MAX_VALUE) { + togo = ((t_next_y - t) / mdt_dy); + mt_next_y = mt + (togo - Math.floor(togo)) * mdt_dy; + } + if(mt_next_z != Double.MAX_VALUE) { + togo = ((t_next_z - t) / mdt_dz); + mt_next_z = mt + (togo - Math.floor(togo)) * mdt_dz; + } + double mtend = Math.min(t_next_x, Math.min(t_next_y, t_next_z)); + while(mt < mtend) { + if(model[modscale*modscale*my + modscale*mz + mx] > 0) { + return false; + } + /* If X step is next best */ + if((mt_next_x <= mt_next_y) && (mt_next_x <= mt_next_z)) { + mx += x_inc; + mt = mt_next_x; + mt_next_x += mdt_dx; + if(x_inc > 0) { + laststep = BlockStep.X_PLUS; + } + else { + laststep = BlockStep.X_MINUS; + if(mx < 0) + mx += modscale; + } + } + /* If Y step is next best */ + else if((mt_next_y <= mt_next_x) && (mt_next_y <= mt_next_z)) { + my += y_inc; + mt = mt_next_y; + mt_next_y += mdt_dy; + if(y_inc > 0) { + laststep = BlockStep.Y_PLUS; + } + else { + laststep = BlockStep.Y_MINUS; + if(my < 0) + my += modscale; + } + } + /* Else, Z step is next best */ + else { + mz += z_inc; + mt = mt_next_z; + mt_next_z += mdt_dz; + if(z_inc > 0) { + laststep = BlockStep.Z_PLUS; + } + else { + laststep = BlockStep.Z_MINUS; + if(mz < 0) + mz += modscale; + } + } + } + return true; + } + } public IsoHDPerspective(ConfigurationNode configuration) { @@ -130,7 +394,6 @@ public class IsoHDPerspective implements HDPerspective { scale = configuration.getDouble("scale", MIN_SCALE); if(scale < MIN_SCALE) scale = MIN_SCALE; if(scale > MAX_SCALE) scale = MAX_SCALE; - Log.info("azimuth=" + azimuth + ", inclination=" + inclination + ", scale=" + scale); /* Generate transform matrix for world-to-tile coordinate mapping */ /* First, need to fix basic coordinate mismatches before rotation - we want zero azimuth to have north to top @@ -155,6 +418,9 @@ public class IsoHDPerspective implements HDPerspective { Matrix3D coordswap = new Matrix3D(0.0, -1.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0); transform.multiply(coordswap); map_to_world = transform; + /* Scaled models for non-cube blocks */ + modscale = (int)Math.ceil(scale); + scalemodels = HDBlockModels.getModelsForScale(modscale);; } @Override @@ -397,7 +663,7 @@ public class IsoHDPerspective implements HDPerspective { for(int i = 0; i < numshaders; i++) { shaderstate[i].reset(ps); } - raytrace(cache, mapiter, ps, shaderstate, shaderdone); + ps.raytrace(cache, mapiter, shaderstate, shaderdone); for(int i = 0; i < numshaders; i++) { if(shaderdone[i] == false) { shaderstate[i].rayFinished(ps); @@ -492,157 +758,6 @@ public class IsoHDPerspective implements HDPerspective { } return renderone; } - - /** - * Trace ray, based on "Voxel Tranversal along a 3D line" - */ - private void raytrace(MapChunkCache cache, MapIterator mapiter, OurPerspectiveState ps, - HDShaderState[] shaderstate, boolean[] shaderdone) { - Vector3D top = ps.top; - Vector3D bottom = ps.bottom; - /* Compute total delta on each axis */ - double dx = Math.abs(bottom.x - top.x); - double dy = Math.abs(bottom.y - top.y); - double dz = Math.abs(bottom.z - top.z); - /* Initial block coord */ - int x = (int) (Math.floor(top.x)); - int y = (int) (Math.floor(top.y)); - int z = (int) (Math.floor(top.z)); - /* Compute parametric step (dt) per step on each axis */ - double dt_dx = 1.0 / dx; - double dt_dy = 1.0 / dy; - double dt_dz = 1.0 / dz; - /* Initialize parametric value to 0 (and we're stepping towards 1) */ - double t = 0; - /* Compute number of steps and increments for each */ - int n = 1; - int x_inc, y_inc, z_inc; - - double t_next_y, t_next_x, t_next_z; - /* If perpendicular to X axis */ - if (dx == 0) { - x_inc = 0; - t_next_x = Double.MAX_VALUE; - } - /* If bottom is right of top */ - else if (bottom.x > top.x) { - x_inc = 1; - n += (int) (Math.floor(bottom.x)) - x; - t_next_x = (Math.floor(top.x) + 1 - top.x) * dt_dx; - } - /* Top is right of bottom */ - else { - x_inc = -1; - n += x - (int) (Math.floor(bottom.x)); - t_next_x = (top.x - Math.floor(top.x)) * dt_dx; - } - /* If perpendicular to Y axis */ - if (dy == 0) { - y_inc = 0; - t_next_y = Double.MAX_VALUE; - } - /* If bottom is above top */ - else if (bottom.y > top.y) { - y_inc = 1; - n += (int) (Math.floor(bottom.y)) - y; - t_next_y = (Math.floor(top.y) + 1 - top.y) * dt_dy; - } - /* If top is above bottom */ - else { - y_inc = -1; - n += y - (int) (Math.floor(bottom.y)); - t_next_y = (top.y - Math.floor(top.y)) * dt_dy; - } - /* If perpendicular to Z axis */ - if (dz == 0) { - z_inc = 0; - t_next_z = Double.MAX_VALUE; - } - /* If bottom right of top */ - else if (bottom.z > top.z) { - z_inc = 1; - n += (int) (Math.floor(bottom.z)) - z; - t_next_z = (Math.floor(top.z) + 1 - top.z) * dt_dz; - } - /* If bottom left of top */ - else { - z_inc = -1; - n += z - (int) (Math.floor(bottom.z)); - t_next_z = (top.z - Math.floor(top.z)) * dt_dz; - } - /* Walk through scene */ - ps.laststep = BlockStep.Y_MINUS; /* Last step is down into map */ - mapiter.initialize(x, y, z); - ps.skylightlevel = 15; - ps.emittedlightlevel = 0; - boolean nonairhit = false; - for (; n > 0; --n) { - ps.blocktypeid = mapiter.getBlockTypeID(); - if(nonairhit || (ps.blocktypeid != 0)) { - ps.blockdata = mapiter.getBlockData(); - boolean done = true; - for(int i = 0; i < shaderstate.length; i++) { - if(!shaderdone[i]) - shaderdone[i] = shaderstate[i].processBlock(ps); - done = done && shaderdone[i]; - } - /* If all are done, we're out */ - if(done) - return; - nonairhit = true; - } - if(need_skylightlevel) - ps.skylightlevel = mapiter.getBlockSkyLight(); - if(need_emittedlightlevel) - ps.emittedlightlevel = mapiter.getBlockEmittedLight(); - /* If X step is next best */ - if((t_next_x <= t_next_y) && (t_next_x <= t_next_z)) { - x += x_inc; - t = t_next_x; - t_next_x += dt_dx; - if(x_inc > 0) { - ps.laststep = BlockStep.X_PLUS; - mapiter.incrementX(); - } - else { - ps.laststep = BlockStep.X_MINUS; - mapiter.decrementX(); - } - } - /* If Y step is next best */ - else if((t_next_y <= t_next_x) && (t_next_y <= t_next_z)) { - y += y_inc; - t = t_next_y; - t_next_y += dt_dy; - if(y_inc > 0) { - ps.laststep = BlockStep.Y_PLUS; - mapiter.incrementY(); - if(mapiter.getY() > 127) - return; - } - else { - ps.laststep = BlockStep.Y_MINUS; - mapiter.decrementY(); - if(mapiter.getY() < 0) - return; - } - } - /* Else, Z step is next best */ - else { - z += z_inc; - t = t_next_z; - t_next_z += dt_dz; - if(z_inc > 0) { - ps.laststep = BlockStep.Z_PLUS; - mapiter.incrementZ(); - } - else { - ps.laststep = BlockStep.Z_MINUS; - mapiter.decrementZ(); - } - } - } - } @Override public boolean isBiomeDataNeeded() {