Java tutorial
/* * Copyright 2015 Lukas Tenbrink * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ivorius.ivtoolkit.rendering.grid; import com.google.common.base.Function; import gnu.trove.TIntCollection; import gnu.trove.list.array.TIntArrayList; import ivorius.ivtoolkit.blocks.BlockCoord; import net.minecraftforge.common.util.ForgeDirection; import org.apache.commons.lang3.tuple.Pair; import org.lwjgl.BufferUtils; import java.nio.FloatBuffer; import java.util.*; import static net.minecraftforge.common.util.ForgeDirection.*; /** * Created by lukas on 20.03.15. */ public class GridQuadCache<T> implements Iterable<GridQuadCache.CachedQuadLevel<T>> { protected final List<CachedQuadLevel<T>> cachedQuadLevels = new ArrayList<>(); protected float[] size; public static int[] getCacheAxes(ForgeDirection direction, int... axes) { switch (direction) { case DOWN: case UP: return new int[] { axes[1], axes[0], axes[2] }; case WEST: case EAST: return new int[] { axes[0], axes[2], axes[1] }; case NORTH: case SOUTH: return new int[] { axes[2], axes[1], axes[0] }; } throw new IllegalArgumentException(); } public static int[] getNormalAxes(ForgeDirection direction, int... axes) { return getCacheAxes(direction, axes); } public static float[] getCacheAxes(ForgeDirection direction, float... axes) { switch (direction) { case DOWN: case UP: return new float[] { axes[1], axes[0], axes[2] }; case WEST: case EAST: return new float[] { axes[0], axes[2], axes[1] }; case NORTH: case SOUTH: return new float[] { axes[2], axes[1], axes[0] }; } throw new IllegalArgumentException(); } public static float[] getNormalAxes(ForgeDirection direction, float... axes) { return getCacheAxes(direction, axes); } public static <T> GridQuadCache<T> createQuadCache(int[] size, float[] scale, Function<Pair<BlockCoord, ForgeDirection>, T> mapper) { return createQuadCacheGreedy(size, scale, mapper); } protected static <T> GridQuadCache<T> createQuadCacheGreedy(int[] size, float[] scale, Function<Pair<BlockCoord, ForgeDirection>, T> mapper) { Map<QuadContext<T>, CoordGrid> partialCache = new HashMap<>(); for (int x = 0; x < size[0]; x++) for (int y = 0; y < size[1]; y++) for (int z = 0; z < size[2]; z++) { BlockCoord coord = new BlockCoord(x, y, z); addToCache(partialCache, mapper, UP, coord); addToCache(partialCache, mapper, DOWN, coord); addToCache(partialCache, mapper, NORTH, coord); addToCache(partialCache, mapper, EAST, coord); addToCache(partialCache, mapper, SOUTH, coord); addToCache(partialCache, mapper, WEST, coord); } Set<Map.Entry<QuadContext<T>, CoordGrid>> quads = partialCache.entrySet(); GridQuadCache<T> cache = new GridQuadCache<>(); cache.size = new float[3]; for (int i = 0; i < 3; i++) cache.size[i] = size[i] * scale[i]; for (Map.Entry<QuadContext<T>, CoordGrid> entry : quads) { QuadContext<T> context = entry.getKey(); int[] sAxes = getCacheAxes(context.direction, size); float[] scAxes = getCacheAxes(context.direction, scale); QuadCollection mesh = entry.getValue().computeMesh(0, 0, sAxes[1], sAxes[2]); FloatBuffer cachedQuadCoords = BufferUtils.createFloatBuffer(mesh.quadCount() * 4); float pxAxis = scAxes[1]; float pzAxis = scAxes[2]; for (int i = 0; i < mesh.quadCount(); i++) { cachedQuadCoords.put(mesh.x1(i) * pxAxis).put(mesh.y1(i) * pzAxis).put((mesh.x2(i) + 1) * pxAxis) .put((mesh.y2(i) + 1) * pzAxis); } cachedQuadCoords.position(0); float zLevel; zLevel = (context.direction.offsetX + context.direction.offsetY + context.direction.offsetZ > 0 ? context.layer + 1 : context.layer) * scAxes[0]; cache.cachedQuadLevels .add(new CachedQuadLevel<>(zLevel, context.direction, context.t, cachedQuadCoords)); } return cache; } protected static <T> void addToCache(Map<QuadContext<T>, CoordGrid> cache, Function<Pair<BlockCoord, ForgeDirection>, T> mapper, ForgeDirection direction, BlockCoord coord) { T t = mapper.apply(Pair.of(coord, direction)); if (t != null) { int[] sAxes = getCacheAxes(direction, coord.x, coord.y, coord.z); addToCache(cache, new QuadContext<>(sAxes[0], direction, t), sAxes[1], sAxes[2]); } } protected static <T> void addToCache(Map<QuadContext<T>, CoordGrid> cache, QuadContext<T> context, int x, int y) { CoordGrid quad = cache.get(context); if (quad == null) cache.put(context, quad = new CoordGrid()); quad.addCoord(x, y); } public float[] getSize() { return size.clone(); } @Override public Iterator<CachedQuadLevel<T>> iterator() { return cachedQuadLevels.iterator(); } public static class QuadContext<T> { public final int layer; public final ForgeDirection direction; public final T t; public QuadContext(int layer, ForgeDirection direction, T t) { this.layer = layer; this.direction = direction; this.t = t; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; QuadContext that = (QuadContext) o; if (layer != that.layer) return false; if (direction != that.direction) return false; if (!t.equals(that.t)) return false; return true; } @Override public int hashCode() { int result = layer; result = 31 * result + direction.hashCode(); result = 31 * result + t.hashCode(); return result; } } public static class CoordGrid extends TIntArrayList { public CoordGrid() { } public CoordGrid(int capacity) { super(capacity); } public CoordGrid(int capacity, int no_entry_value) { super(capacity, no_entry_value); } public CoordGrid(TIntCollection collection) { super(collection); } public CoordGrid(int[] values) { super(values); } public CoordGrid(int[] values, int no_entry_value, boolean wrap) { super(values, no_entry_value, wrap); } private static boolean isFree(boolean[][] mask, int lX, int hX, int y) { for (int tX = lX; tX <= hX; tX++) if (!mask[tX][y]) return false; return true; } public void addCoord(int x, int y) { add(x); add(y); } public int coordCount() { return size() / 2; } public int x(int index) { return get(index * 2); } public int y(int index) { return get(index * 2 + 1); } public QuadCollection computeMesh(int minX, int minY, int maxX, int maxY) { boolean[][] mask = new boolean[maxX - minX][maxY - minY]; QuadCollection collection = new QuadCollection(); for (int c = 0; c < coordCount(); c++) mask[x(c)][y(c)] = true; for (int x = minX; x < maxX; x++) for (int y = minY; y < maxY; y++) { if (mask[x][y]) { // Expand X int lX = x, hX = x, lY = y, hY = y; while (lX > minX && mask[lX - 1][y]) lX--; while (hX < maxX - 1 && mask[hX + 1][y]) hX++; // Expand Y while (lY > minY && isFree(mask, lX, hX, lY - 1)) lY--; while (hY < maxY - 1 && isFree(mask, lX, hX, hY + 1)) hY++; // Fill mask for (int tX = lX; tX <= hX; tX++) for (int tY = lY; tY <= hY; tY++) mask[tX][tY] = false; collection.addQuad(lX, lY, hX, hY); } } return collection; } } public static class QuadCollection extends TIntArrayList { public QuadCollection() { } public QuadCollection(int capacity) { super(capacity); } public QuadCollection(int capacity, int no_entry_value) { super(capacity, no_entry_value); } public QuadCollection(TIntCollection collection) { super(collection); } public QuadCollection(int[] values) { super(values); } public QuadCollection(int[] values, int no_entry_value, boolean wrap) { super(values, no_entry_value, wrap); } public void addQuad(int x1, int y1, int x2, int y2) { add(x1); add(y1); add(x2); add(y2); } public int x1(int index) { return get(index * 4); } public int x2(int index) { return get(index * 4 + 2); } public int y1(int index) { return get(index * 4 + 1); } public int y2(int index) { return get(index * 4 + 3); } public int quadCount() { return size() / 4; } } public static class CachedQuadLevel<T> { public final float zLevel; public final ForgeDirection direction; public final T t; public final FloatBuffer quads; public CachedQuadLevel(float zLevel, ForgeDirection direction, T t, FloatBuffer quads) { this.zLevel = zLevel; this.direction = direction; this.t = t; this.quads = quads; } } }