package hunternif.mc.atlas.client;
import hunternif.mc.atlas.client.SubTile.Part;
import hunternif.mc.atlas.client.SubTile.Shape;
import hunternif.mc.atlas.core.ITileStorage;
import hunternif.mc.atlas.core.Tile;
import hunternif.mc.atlas.util.Rect;
import java.util.Iterator;
/**
* Iterates through a tile storage for the purpose of rendering their textures.
* Returned is an array of 4 {@link SubTile}s which constitute a whole
* {@link Tile}.
* The SubTile objects are generated on the fly and not retained in memory.
* May return null!
* @author Hunternif
*/
public class TileRenderIterator implements Iterator<SubTileQuartet> {
private final ITileStorage tiles;
/** How many chunks a tile spans. Used for viewing the map at a scale below
* the threshold at which the tile texture is of minimum size and no longer
* scales down. Can't be less than 1. */
private int step = 1;
public void setStep(int step) {
if (step >= 1) {
this.step = step;
}
}
/** The scope of iteration. */
private final Rect scope = new Rect();
public void setScope(int minX, int minY, int maxX, int maxY) {
scope.set(minX, minY, maxX, maxY);
chunkX = minX;
chunkY = minY;
}
public void setScope(Rect scope){
this.scope.set(scope);
chunkX = scope.minX;
chunkY = scope.minY;
}
/**
* The group of adjacent tiles used for traversing the storage.
* <pre>
* a b
* c d e f
* g h i j
* k l
* </pre>
* 'i' is at (x, y).
* The returned array of subtiles represents the corner 'd-e-h-i'
*/
private Tile a, b, c, d, e, f, g, h, i, j, k, l;
/** Shortcuts for the quartet. */
private final SubTile _d = new SubTile(Part.BOTTOM_RIGHT),
_e = new SubTile(Part.BOTTOM_LEFT),
_h = new SubTile(Part.TOP_RIGHT),
_i = new SubTile(Part.TOP_LEFT);
private final SubTileQuartet quartet = new SubTileQuartet(_d, _e, _h, _i);
/** Current index into the tile storage, which presumably has every tile spanning exactly 1 chunk. */
private int chunkX, chunkY;
/** Current index into the grid of subtiles, starting at (-1, -1). */
private int subtileX = -1, subtileY = -1;
public TileRenderIterator(ITileStorage tiles) {
this.tiles = tiles;
setScope(tiles.getScope());
}
@Override
public boolean hasNext() {
return chunkX >= scope.minX && chunkX <= scope.maxX + 1 &&
chunkY >= scope.minY && chunkY <= scope.maxY + 1;
}
@Override
public SubTileQuartet next() {
a = b;
b = tiles.getTile(chunkX, chunkY - step * 2);
c = d;
d = e;
e = f;
f = tiles.getTile(chunkX + step, chunkY - step);
g = h;
h = i;
i = j;
j = tiles.getTile(chunkX + step, chunkY);
k = l;
l = tiles.getTile(chunkX, chunkY + step);
quartet.setCoords(subtileX, subtileY);
_d.tile = d;
_e.tile = e;
_h.tile = h;
_i.tile = i;
// At first assume all convex:
for (SubTile subtile : quartet) {
subtile.shape = Shape.CONVEX;
}
// Connect horizontally:
if (shouldStitchToHorizontally(d, e)) {
stitchHorizontally(_d);
}
if (shouldStitchToHorizontally(e, d)) {
stitchHorizontally(_e);
}
if (shouldStitchToHorizontally(h, i)) {
stitchHorizontally(_h);
}
if (shouldStitchToHorizontally(i, h)) {
stitchHorizontally(_i);
}
// Connect vertically:
if (shouldStitchToVertically(d, h)) {
stitchVertically(_d);
if (_d.shape == Shape.CONCAVE && shouldStitchTo(d, i)) {
_d.shape = Shape.FULL;
}
}
if (shouldStitchToVertically(h, d)) {
stitchVertically(_h);
if (_h.shape == Shape.CONCAVE && shouldStitchTo(h, e)) {
_h.shape = Shape.FULL;
}
}
if (shouldStitchToVertically(e, i)) {
stitchVertically(_e);
if (_e.shape == Shape.CONCAVE && shouldStitchTo(e, h)) {
_e.shape = Shape.FULL;
}
}
if (shouldStitchToVertically(i, e)) {
stitchVertically(_i);
if (_i.shape == Shape.CONCAVE && shouldStitchTo(i, d)) {
_i.shape = Shape.FULL;
}
}
// For any convex subtile check for single-object:
if (_d.shape == Shape.CONVEX && !shouldStitchToVertically(d, a) && !shouldStitchToHorizontally(d, c)) {
_d.shape = Shape.SINGLE_OBJECT;
}
if (_e.shape == Shape.CONVEX && !shouldStitchToVertically(e, b) && !shouldStitchToHorizontally(e, f)) {
_e.shape = Shape.SINGLE_OBJECT;
}
if (_h.shape == Shape.CONVEX && !shouldStitchToHorizontally(h, g) && !shouldStitchToVertically(h, k)) {
_h.shape = Shape.SINGLE_OBJECT;
}
if (_i.shape == Shape.CONVEX && !shouldStitchToHorizontally(i, j) && !shouldStitchToVertically(i, l)) {
_i.shape = Shape.SINGLE_OBJECT;
}
chunkX += step;
subtileX += 2;
if (chunkX > scope.maxX + 1) {
chunkX = scope.minX;
subtileX = -1;
chunkY += step;
subtileY += 2;
a = null;
b = null;
c = null;
d = null;
e = null;
f = tiles.getTile(chunkX, chunkY - step);
g = null;
h = null;
i = null;
j = tiles.getTile(chunkX, chunkY);
k = null;
l = null;
}
return quartet;
}
/** Whether the first tile should be stitched to the 2nd (in any direction)
* (but the opposite is not always true!) */
private static boolean shouldStitchTo(Tile tile, Tile to) {
if (tile == null) return false;
TextureSet set = BiomeTextureMap.instance().getTextureSet(tile);
TextureSet toSet = BiomeTextureMap.instance().getTextureSet(to);
return set != null && set.shouldStitchTo(toSet);
}
/** Whether the first tile should be stitched to the 2nd along the X axis
* (but the opposite is not always true!) */
private static boolean shouldStitchToHorizontally(Tile tile, Tile to) {
if (tile == null) return false;
TextureSet set = BiomeTextureMap.instance().getTextureSet(tile);
TextureSet toSet = BiomeTextureMap.instance().getTextureSet(to);
return set != null && set.shouldStitchToHorizontally(toSet);
}
/** Whether the first tile should be stitched to the 2nd along the Z axis
* (but the opposite is not always true!) */
private static boolean shouldStitchToVertically(Tile tile, Tile to) {
if (tile == null) return false;
TextureSet set = BiomeTextureMap.instance().getTextureSet(tile);
TextureSet toSet = BiomeTextureMap.instance().getTextureSet(to);
return set != null && set.shouldStitchToVertically(toSet);
}
/** Change the shape of the subtile in order to stitch it vertically
* to another subtile. It doesn't matter if it's top or bottom. */
private static void stitchVertically(SubTile subtile) {
if (subtile.shape == Shape.HORIZONTAL) subtile.shape = Shape.CONCAVE;
if (subtile.shape == Shape.CONVEX) subtile.shape = Shape.VERTICAL;
}
/** Change the shape of the subtile in order to stitch it horizontally
* to another subtile. It doesn't matter if it's left or right. */
private static void stitchHorizontally(SubTile subtile) {
if (subtile.shape == Shape.VERTICAL) subtile.shape = Shape.CONCAVE;
if (subtile.shape == Shape.CONVEX) subtile.shape = Shape.HORIZONTAL;
}
@Override
public void remove() {
throw new UnsupportedOperationException("cannot remove subtiles from tile storage");
}
}