package com.vitco.export; import com.vitco.core.data.Data; import com.vitco.core.data.container.Voxel; import com.vitco.export.dataStatic.Kv6Static; import com.vitco.layout.content.console.ConsoleInterface; import com.vitco.low.CubeIndexer; import com.vitco.low.hull.HullManager; import com.vitco.util.components.progressbar.ProgressDialog; import com.vitco.util.misc.ByteHelper; import gnu.trove.iterator.TIntIterator; import gnu.trove.map.hash.TIntShortHashMap; import gnu.trove.map.hash.TShortIntHashMap; import gnu.trove.set.hash.TIntHashSet; import java.awt.*; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; /** * Exporter into *.kv6 (Ace of Spades Game) */ public class Kv6Exporter extends AbstractExporter { // constructor public Kv6Exporter(File exportTo, Data data, ProgressDialog dialog, ConsoleInterface console) throws IOException { super(exportTo, data, dialog, console); } // decide whether to use weighted center or origin (default) private boolean useWeightedCenter = false; public void setUseWeightedCenter(boolean flag) { useWeightedCenter = flag; } // helper to get direction (lighting bit) private static byte getDirectionByte(int voxPosId, HullManager<String> hullManager) { // the offsets (direction) int ox = 0, oy = 0, oz = 0; // loop over precomputed circle values for (int[] circeValue : Kv6Static.circleValues) { // check if voxel position is set if (hullManager.contains(voxPosId + circeValue[0])) { // add the offset (this voxel adds to the direction) ox += circeValue[1]; oy += circeValue[2]; oz += circeValue[3]; } } // If voxels aren't directional (thin), return the 0 vector (no direction) double f = ox * ox + oy * oy + oz * oz; if (f < 32*32) { return (byte) 255; } // loop over all directions and find the best (least best?) matching one double maxf = Double.MIN_VALUE; int j = 0; for (int i = 0; i < 255; i++) { double[] directionVector = Kv6Static.directions[i]; f = directionVector[0] * ox + directionVector[2] * oy - directionVector[1] * oz; if (f > maxf) { maxf = f; j = i; } } return (byte) j; } // write the file @Override protected boolean writeFile() throws IOException { // write magic number fileOut.writeIntRev(0x6c78764b); // write dimension information int[] size = getSize(); fileOut.writeIntRev(size[0]); fileOut.writeIntRev(size[2]); fileOut.writeIntRev(size[1]); // obtain min and max int[] min = getMin(); int[] max = getMax(); // write center if (useWeightedCenter) { float[] center = getCenter(); fileOut.writeFloatRev(center[0] - min[0]); fileOut.writeFloatRev(center[2] - min[2]); fileOut.writeFloatRev(center[1] - min[1]); } else { fileOut.writeFloatRev(- min[0]); fileOut.writeFloatRev(max[2]); fileOut.writeFloatRev(- min[1]); } // fetch all visible voxels HullManager<String> hullManager = new HullManager<String>(); for (Voxel voxel : data.getVisibleLayerVoxel()) { hullManager.update(voxel.posId, null); } TIntHashSet visibleVoxel = hullManager.getVisibleVoxelsIds(); int voxelCount = visibleVoxel.size(); short[][] voxels = new short[visibleVoxel.size()][]; TIntIterator iter = visibleVoxel.iterator(); int i = 0; while (iter.hasNext() && i < voxelCount) { voxels[i] = CubeIndexer.getPos(iter.next()); i++; } // sort voxels Arrays.sort(voxels, new Comparator<short[]>() { @Override public int compare(short[] o1, short[] o2) { int dist = o1[0] - o2[0]; if (dist != 0) { return dist; } dist = o2[2] - o1[2]; if (dist != 0) { return dist; } return o1[1] - o2[1]; } }); // write the amount of voxel that have a visible side fileOut.writeIntRev(voxelCount); setActivity("Exporting to file...", false); // write all the voxel data for (int i1 = 0; i1 < voxels.length; i1++) { short[] voxPos = voxels[i1]; setProgress((i1/(float)voxels.length)*100); Color col = data.searchVoxel( new int[]{voxPos[0], voxPos[1], voxPos[2]}, false ).getColor(); // write color fileOut.writeByte((byte) col.getBlue()); fileOut.writeByte((byte) col.getGreen()); fileOut.writeByte((byte) col.getRed()); // write "l" (fill byte) fileOut.writeByte((byte) 128); // write z pos fileOut.writeShortRev((short) (voxPos[1] - min[1])); // write visible faces int voxPosId = CubeIndexer.getId(voxPos); byte visibleFaces = 0; for (int j = 0; j < 6; j++) { if (hullManager.containsBorder(voxPosId, j)) { int mapTo; switch (j) { case 0: mapTo = 6; break; case 1: mapTo = 7; break; case 2: mapTo = 2; break; case 3: mapTo = 3; break; case 4: mapTo = 5; break; default: mapTo = 4; break; } visibleFaces = ByteHelper.setBit(visibleFaces, mapTo); } } fileOut.writeByte(visibleFaces); // write lighting byte fileOut.writeByte(getDirectionByte(voxPosId, hullManager)); } // collect the offsets TShortIntHashMap xMap = new TShortIntHashMap(); TIntShortHashMap xyMap = new TIntShortHashMap(); for (short[] voxel : voxels) { short shiftedVal = (short) (voxel[0] - min[0]); xMap.put(shiftedVal, xMap.get(shiftedVal)+1); int id = CubeIndexer.getId(voxel[0] - min[0], voxel[2] - min[2], 0); xyMap.put(id, (short) (xyMap.get(id)+1)); } // write the xoffsets for (short x = 0; x < size[0]; x++) { fileOut.writeIntRev(xMap.get(x)); } // write the zyoffset for (int x = 0; x < size[0]; x++) { for (int y = size[2] - 1; y >= 0; y--) { fileOut.writeShortRev(xyMap.get(CubeIndexer.getId(x, y, 0))); } } // success return true; } }