package com.vitco.export.generic.container;
import com.vitco.util.graphic.G2DUtil;
import com.vitco.util.misc.IntegerTools;
import gnu.trove.map.hash.TIntObjectHashMap;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
public class TriTextureTest {
@Test
public void compressionTest() throws Exception {
// load image that we want to compress
BufferedImage input = ImageIO.read(new File("C:\\Users\\flux\\Dropbox\\java\\VoxelShop\\Test Files\\Texture Compression\\sample25.png"));
// create hashmap with pixels
TIntObjectHashMap<int[]> pixels = new TIntObjectHashMap<int[]>();
for (int x = 0, width = input.getWidth(); x < width; x++) {
for (int y = 0, height = input.getHeight(); y < height; y++) {
int rgb = input.getRGB(x,y);
// check that this is not a fully transparent pixel
if (((rgb >> 24) & 0xff) != 0) {
pixels.put(IntegerTools.makeInt(x, y), new int[]{x, y, rgb});
}
}
}
// enter uv points
double[][] uvPoints = new double[][] {
new double[] {1.0, 0.0} ,
new double[] {0.0, 1.0},
new double[] {0.8666666666666667, 0.0}
};
// compress input
int[] newSize = TriTexture.compress(input.getWidth(), input.getHeight(), pixels, uvPoints);
// alternative don't compress
//int[] newSize = new int[] {input.getWidth(),input.getHeight()};
System.out.println(input.getWidth() + " " + input.getHeight());
System.out.println(newSize[0] + " " + newSize[1]);
// -- create image
int imgSize = 20; // grid cells
int zoom = 50;
BufferedImage img = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_ARGB);
// print found voxels
for (int[] p : pixels.valueCollection()) {
if (p[0] > -1 && p[1] > -1 && p[0] < img.getWidth() && p[1] < img.getHeight()) {
img.setRGB(p[0], p[1], p[2]);
}
}
// enlarge so we can draw the triangle line
BufferedImage img2 = new BufferedImage(imgSize * zoom, imgSize * zoom, BufferedImage.TYPE_INT_ARGB);
img2.getGraphics().drawImage(img.getScaledInstance(imgSize * zoom, imgSize * zoom, 0), 0, 0, null);
Graphics2D g2 = (Graphics2D) img2.getGraphics();
// draw outlines of the voxels
g2.setColor(Color.WHITE);
for (int[] p : pixels.valueCollection()) {
g2.drawRect( (p[0])*zoom, (p[1])*zoom, zoom, zoom);
}
// draw the triangle
g2.setColor(Color.RED);
g2.drawLine(
(int) ((uvPoints[0][0] * newSize[0]) * zoom), (int) ((uvPoints[0][1] * newSize[1]) * zoom),
(int) ((uvPoints[1][0] * newSize[0]) * zoom), (int) ((uvPoints[1][1] * newSize[1]) * zoom)
);
g2.drawLine(
(int) ((uvPoints[2][0] * newSize[0]) * zoom), (int) ((uvPoints[2][1] * newSize[1]) * zoom),
(int) ((uvPoints[1][0] * newSize[0]) * zoom), (int) ((uvPoints[1][1] * newSize[1]) * zoom)
);
g2.drawLine(
(int) ((uvPoints[0][0] * newSize[0]) * zoom), (int) ((uvPoints[0][1] * newSize[1]) * zoom),
(int) ((uvPoints[2][0] * newSize[0]) * zoom), (int) ((uvPoints[2][1] * newSize[1]) * zoom)
);
// write the file
try {
ImageIO.write(img2, "png", new File("C:\\Users\\flux\\Dropbox\\java\\VoxelShop\\Test Files\\Texture Compression\\sample25_compressed.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void compressionTestBatch() throws Exception {
for (int j = 0; j < 10000000; j++) {
Random rand = new Random(j);
// print current id
if (j%10000 == 0) {
System.out.println(":: " + j);
}
// System.out.println(":: " + j);
// -- generate random triangle
// size of image
int[] size = new int[] {rand.nextInt(25)+1, rand.nextInt(25)+1};
double[][] uvPoints;
// triangle points
do {
uvPoints = new double[][]{
new double[]{rand.nextBoolean() ? rand.nextInt(100)/100d : rand.nextInt(2), rand.nextBoolean() ? rand.nextInt(100)/100d : rand.nextInt(2)},
new double[]{rand.nextBoolean() ? rand.nextInt(100)/100d : rand.nextInt(2), rand.nextBoolean() ? rand.nextInt(100)/100d : rand.nextInt(2)},
new double[]{rand.nextBoolean() ? rand.nextInt(100)/100d : rand.nextInt(2), rand.nextBoolean() ? rand.nextInt(100)/100d : rand.nextInt(2)}
};
} while (
// points not different
(uvPoints[0][0] == uvPoints[1][0] && uvPoints[0][1] == uvPoints[1][1]) ||
(uvPoints[0][0] == uvPoints[2][0] && uvPoints[0][1] == uvPoints[2][1]) ||
(uvPoints[2][0] == uvPoints[1][0] && uvPoints[2][1] == uvPoints[1][1]) ||
// points on one line
(uvPoints[0][0] == uvPoints[1][0] && uvPoints[2][0] == uvPoints[1][0]) ||
(uvPoints[0][1] == uvPoints[1][1] && uvPoints[2][1] == uvPoints[1][1])
);
// prevent rounding problems
uvPoints[0][0] = (double)Math.round(uvPoints[0][0] * 1000000000) / 1000000000;
uvPoints[0][1] = (double)Math.round(uvPoints[0][1] * 1000000000) / 1000000000;
uvPoints[1][0] = (double)Math.round(uvPoints[1][0] * 1000000000) / 1000000000;
uvPoints[1][1] = (double)Math.round(uvPoints[1][1] * 1000000000) / 1000000000;
uvPoints[2][0] = (double)Math.round(uvPoints[2][0] * 1000000000) / 1000000000;
uvPoints[2][1] = (double)Math.round(uvPoints[2][1] * 1000000000) / 1000000000;
// ---
// print generated points
// System.out.println(uvPoints[0][0] + " " + uvPoints[0][1] + " " + uvPoints[1][0] + " " + uvPoints[1][1] + " " + uvPoints[2][0] + " " + uvPoints[2][1]);
// System.out.println(uvPoints[0][0] * size[0] + " " + uvPoints[0][1] * size[1] + " " +
// uvPoints[1][0] * size[0] + " " + uvPoints[1][1] * size[1] + " " + uvPoints[2][0] * size[0] + " " + uvPoints[2][1] * size[1]);
// calculate required points
int[][] points = G2DUtil.getTriangleGridIntersection(
uvPoints[0][0] * size[0], uvPoints[0][1] * size[1],
uvPoints[1][0] * size[0], uvPoints[1][1] * size[1],
uvPoints[2][0] * size[0], uvPoints[2][1] * size[1]
);
// generate required points (two colors)
TIntObjectHashMap<int[]> pixels = new TIntObjectHashMap<int[]>();
for (int[] point : points) {
pixels.put(IntegerTools.makeInt(point[0], point[1]), new int[] {point[0], point[1],
rand.nextBoolean() ? Color.RED.getRGB() : Color.BLUE.getRGB()
});
}
// -- compress
TIntObjectHashMap<int[]> pixelsCompressed = new TIntObjectHashMap<int[]>();
pixelsCompressed.putAll(pixels);
double[][] uvPointsCompressed = new double[][] {
new double[] {uvPoints[0][0], uvPoints[0][1]},
new double[] {uvPoints[1][0], uvPoints[1][1]},
new double[] {uvPoints[2][0], uvPoints[2][1]}
};
int[] sizeCompressed = TriTexture.compress(size[0], size[1], pixelsCompressed, uvPointsCompressed);
// -- validate
// print size
// System.out.println(" === " + size[0] + " " + size[1] + " vs " + sizeCompressed[0] + " " + sizeCompressed[1]);
// check that size is correct
assert sizeCompressed[0] <= size[0];
assert sizeCompressed[1] <= size[1];
assert sizeCompressed[0] > 0;
assert sizeCompressed[1] > 0;
// validate pixel color for a number of random points
for (int i = 0; i < 1000; i++) {
// gives a uniform distribution over the triangle, see here:
// http://stackoverflow.com/questions/4778147/sample-random-point-in-triangle
double r1, r2;
do {
// we do not care about triangle corner points
// (since we're interpolating later anyway!)
// Note: these points might cause trouble since they might be rounded to the next integer value
// and that pixel might outside the triangle area
r1 = rand.nextDouble();
r2 = rand.nextDouble();
} while (r1 == 1 || r2 == 1 || r1 == 0 || r2 == 0);
double v1 = ((1 - Math.sqrt(r1)) * uvPoints[0][0] +
(Math.sqrt(r1) * (1 - r2)) * uvPoints[1][0] + (Math.sqrt(r1) * r2) * uvPoints[2][0]) * size[0];
double v2 = ((1 - Math.sqrt(r1)) * uvPoints[0][1] +
(Math.sqrt(r1) * (1 - r2)) * uvPoints[1][1] + (Math.sqrt(r1) * r2) * uvPoints[2][1]) * size[1];
int x = (int)Math.floor(v1);
int y = (int)Math.floor(v2);
int xCompressed = (int)Math.floor(((1 - Math.sqrt(r1)) * uvPointsCompressed[0][0] +
(Math.sqrt(r1) * (1 - r2)) * uvPointsCompressed[1][0] + (Math.sqrt(r1) * r2) * uvPointsCompressed[2][0]) * sizeCompressed[0]);
int yCompressed = (int)Math.floor(((1 - Math.sqrt(r1)) * uvPointsCompressed[0][1] +
(Math.sqrt(r1) * (1 - r2)) * uvPointsCompressed[1][1] + (Math.sqrt(r1) * r2) * uvPointsCompressed[2][1]) * sizeCompressed[1]);
x = Math.max(0, Math.min(size[0] - 1, x));
y = Math.max(0, Math.min(size[1] - 1, y));
xCompressed = Math.max(0, Math.min(sizeCompressed[0] - 1, xCompressed));
yCompressed = Math.max(0, Math.min(sizeCompressed[1] - 1, yCompressed));
// print points that are checked
// System.out.println(x + " " + y + " vs " + xCompressed + " " + yCompressed);
// check that the color values match
assert pixels.get(IntegerTools.makeInt(x, y))[2] == pixelsCompressed.get(IntegerTools.makeInt(xCompressed, yCompressed))[2];
}
}
}
}