package com.vitco.export.generic;
import com.vitco.core.data.container.Voxel;
import com.vitco.low.triangulate.Grid2TriGreedyOptimal;
import com.vitco.low.triangulate.Grid2TriMono;
import com.vitco.low.triangulate.Grid2TriNaiveGreedy;
import com.vitco.low.triangulate.Grid2TriPolyFast;
import com.vitco.low.triangulate.util.Grid2PolyHelper;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Helps with managing the data structure, so we can easily extract the information we
* need to generate the export file.
*
* Todo: Rewrite exporter to always use textures and to use fewer triangles (than for each voxel side two)
*/
public class ExportWorld {
// list of all voxels
private final ArrayList<VoxelSide> voxels = new ArrayList<VoxelSide>();
// voxel index to determine which sides are visible
private final HashMap<String, VoxelSide> index = new HashMap<String, VoxelSide>();
// helper class that represents a voxel
private final class VoxelSide {
private final int[] pos;
private final boolean[] visSides = new boolean[]{true, true, true, true, true, true};
private void hideSide(int side) {
visSides[side] = false;
}
// check if side is visible
public final boolean sideVisible(int side) {
return visSides[side];
}
// get position for axis
public final int posForAxis(int axis) {
return pos[axis];
}
// constructor
public VoxelSide(Voxel voxel) {
this.pos = voxel.getPosAsInt();
// update all the sides
for (int i = 0; i < 6; i++) {
int add = i%2 == 0 ? 1 : -1;
VoxelSide found = index.get(
(i/2 == 0 ? voxel.x + add : voxel.x) + "_" +
(i/2 == 1 ? voxel.y + add : voxel.y) + "_" +
(i/2 == 2 ? voxel.z + add : voxel.z)
);
if (found != null) {
hideSide(i);
found.hideSide(i%2 == 0 ? i + 1 : i - 1);
}
}
index.put(voxel.getPosAsString(), this);
}
}
// constructor
public ExportWorld(Voxel[] input) {
for (Voxel voxel : input) {
voxels.add(new VoxelSide(voxel));
}
}
// to reference the different algorithms
public static final int ALGORITHM_POLY2TRI = 0;
public static final int ALGORITHM_GREEDY = 1;
public static final int ALGORITHM_MONO = 2;
public static final int ALGORITHM_MONO_SAVE = 3;
public static final int ALGORITHM_GREEDY_OPTIMAL = 4;
// build the sides and returns the total and
// the (minimal possible) reduced number of triangles
public int[] analyzeTriCount(int algorithmid) {
int triCount = 0;
int triCountRaw = 0;
long time = 0;
// for all sides
for (int i = 0; i < 6; i++) {
// holds the sides per "slice"
HashMap<Integer, ArrayList<Point>> polygons = new HashMap<Integer, ArrayList<Point>>();
// min,max for other axix
Integer min2 = null;
Integer min3 = null;
Integer max2 = null;
Integer max3 = null;
// for all voxels
for (VoxelSide voxel : voxels) {
if (voxel.sideVisible(i)) {
triCountRaw += 2;
int depthAxis = i/2;
ArrayList<Point> pixels = polygons.get(voxel.posForAxis(depthAxis));
if (pixels == null) {
pixels = new ArrayList<Point>();
polygons.put(voxel.posForAxis(depthAxis), pixels);
}
// add the other two axis as point
Point pixel = new Point(voxel.posForAxis((depthAxis + 1)% 3), voxel.posForAxis((depthAxis + 2)% 3));
min2 = Math.min(min2 == null ? pixel.x : min2, pixel.x);
min3 = Math.min(min3 == null ? pixel.y : min3, pixel.y);
max2 = Math.max(max2 == null ? pixel.x : max2, pixel.x);
max3 = Math.max(max3 == null ? pixel.y : max3, pixel.y);
pixels.add(pixel);
}
}
if (min2 != null) {
// analyze the polygons
for (Map.Entry<Integer, ArrayList<Point>> entry : polygons.entrySet()) {
int w = max2 - min2 + 1;
int h = max3 - min3 + 1;
switch (algorithmid) {
case 0: // poly2tri (will produce no t-junction problems)
boolean[][] dataPoly2Tri = new boolean[w][h];
for (Point point : entry.getValue()) {
dataPoly2Tri[point.x - min2][point.y - min3] = true;
}
time -= System.currentTimeMillis();
triCount += Grid2TriPolyFast.triangulate(Grid2PolyHelper.convert(dataPoly2Tri)).size();
time += System.currentTimeMillis();
break;
case 1: // greedy (will produce many t-junction problems)
boolean[][] dataGreedy = new boolean[w][h];
for (Point point : entry.getValue()) {
dataGreedy[point.x - min2][point.y - min3] = true;
}
time -= System.currentTimeMillis();
triCount += Grid2TriNaiveGreedy.triangulate(dataGreedy).size();
time += System.currentTimeMillis();
break;
case 2: // mono (will produce some t-junction problems)
boolean[][] dataMono = new boolean[w][h];
for (Point point : entry.getValue()) {
dataMono[point.x - min2][point.y - min3] = true;
}
time -= System.currentTimeMillis();
triCount += Grid2TriMono.triangulate(dataMono, false).size();
time += System.currentTimeMillis();
break;
case 3: // altered mono (will only produce t-junction problems in 3D)
boolean[][] dataMonoSave = new boolean[w][h];
for (Point point : entry.getValue()) {
dataMonoSave[point.x - min2][point.y - min3] = true;
}
time -= System.currentTimeMillis();
triCount += Grid2TriMono.triangulate(dataMonoSave, true).size();
time += System.currentTimeMillis();
break;
default: // optimal greedy (will produce many t-junction problems)
boolean[][] dataGreedyOpt = new boolean[w][h];
for (Point point : entry.getValue()) {
dataGreedyOpt[point.x - min2][point.y - min3] = true;
}
time -= System.currentTimeMillis();
triCount += Grid2TriGreedyOptimal.triangulate(dataGreedyOpt).size();
time += System.currentTimeMillis();
break;
}
}
}
}
return new int[] {triCount, triCountRaw, (int) (time)};
}
}