/* Copyright (c) 2012-2014 Jesper Öqvist <jesper@llbit.se>
*
* This file is part of Chunky.
*
* Chunky is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chunky is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Chunky. If not, see <http://www.gnu.org/licenses/>.
*/
package se.llbit.chunky.world;
import org.apache.commons.math3.util.FastMath;
import se.llbit.chunky.renderer.scene.Scene;
import se.llbit.chunky.resources.BitmapImage;
import se.llbit.chunky.resources.Texture;
import se.llbit.log.Log;
import se.llbit.math.ColorUtil;
import se.llbit.math.QuickMath;
import se.llbit.math.Ray;
import se.llbit.math.Vector4;
import se.llbit.util.ImageTools;
/**
* Specialized skymap texture with multithreaded gamma correction
* preprocessing.
*
* @author Jesper Öqvist (jesper@llbit.se)
*/
public class SkymapTexture extends Texture {
class TexturePreprocessor extends Thread {
private final int x0;
private final int x1;
private final int y0;
private final int y1;
TexturePreprocessor(int x0, int x1, int y0, int y1) {
super("Texture Preprocessor");
this.x0 = x0;
this.x1 = x1;
this.y0 = y0;
this.y1 = y1;
}
@Override public void run() {
float[] c = new float[4];
for (int y = y0; y <= y1; ++y) {
for (int x = x0; x <= x1; ++x) {
int index = width * y + x;
ColorUtil.getRGBAComponents(image.data[index], c);
ColorUtil.getRGBAComponents(image.getPixel(x, y), c);
c[0] = (float) FastMath.pow(c[0], Scene.DEFAULT_GAMMA);
c[1] = (float) FastMath.pow(c[1], Scene.DEFAULT_GAMMA);
c[2] = (float) FastMath.pow(c[2], Scene.DEFAULT_GAMMA);
image.data[index] = ColorUtil.getRGB(c);
}
}
}
}
/**
* Create new skymap.
*/
public SkymapTexture(BitmapImage image) {
super(image);
}
@Override public void setTexture(BitmapImage newImage) {
image = newImage;
width = image.width;
height = image.height;
avgColor = ImageTools.calcAvgColor(image);
Log.info("Preprocessing skymap texture");
long start = System.currentTimeMillis();
// Gamma correct the texture.
int segX = 4;
int segY = 4;
if (width < segX) {
segX = 1;
}
if (height < segY) {
segY = 1;
}
TexturePreprocessor[][] preprocessor = new TexturePreprocessor[segX][segY];
int w = width / segX;
int h = height / segY;
for (int i = 0; i < segX; ++i) {
int x0 = w * i;
int x1 = x0 + w - 1;
if ((i + 1) == segX)
x1 = width - 1;
for (int j = 0; j < segY; ++j) {
int y0 = h * j;
int y1 = y0 + h - 1;
if ((j + 1) == segY)
y1 = height - 1;
preprocessor[i][j] = new TexturePreprocessor(x0, x1, y0, y1);
preprocessor[i][j].start();
}
}
for (int i = 0; i < segX; ++i) {
for (int j = 0; j < segY; ++j) {
try {
preprocessor[i][j].join();
} catch (InterruptedException e) {
// Interrupted.
}
}
}
long time = System.currentTimeMillis() - start;
Log.info("Skymap preprocessing took " + time + "ms");
}
@Override public void getColor(double u, double v, Vector4 c) {
ColorUtil.getRGBComponents(image.getPixel((int) (u * width - Ray.EPSILON),
(int) ((1 - v) * height - Ray.EPSILON)), c);
}
/**
* Get skymap color at (x, y).
*/
public void getColor(int x, int y, Vector4 c) {
ColorUtil.getRGBComponents(image.getPixel(x, y), c);
}
@Override public void getColor(Ray ray) {
throw new UnsupportedOperationException();
}
@Override public float[] getColor(double u, double v) {
throw new UnsupportedOperationException();
}
@Override public void getColorInterpolated(double u, double v, Vector4 c) {
double x = u * (width - 1);
double y = (1 - v) * (height - 1);
double weight;
int fx = (int) QuickMath.floor(x);
int cx = (int) QuickMath.ceil(x);
int fy = (int) QuickMath.floor(y);
int cy = (int) QuickMath.ceil(y);
double r, g, b;
getColor(fx, fy, c);
weight = (1 - (y - fy)) * (1 - (x - fx));
r = weight * c.x;
g = weight * c.y;
b = weight * c.z;
getColor(cx, fy, c);
weight = (1 - (y - fy)) * (1 - (cx - x));
r += weight * c.x;
g += weight * c.y;
b += weight * c.z;
getColor(fx, cy, c);
weight = (1 - (cy - y)) * (1 - (x - fx));
r += weight * c.x;
g += weight * c.y;
b += weight * c.z;
getColor(cx, cy, c);
weight = (1 - (cy - y)) * (1 - (cx - x));
r += weight * c.x;
g += weight * c.y;
b += weight * c.z;
c.set(r, g, b, 1);
}
@Override public int getColorWrapped(int u, int v) {
throw new UnsupportedOperationException();
}
@Override public int getAvgColor() {
throw new UnsupportedOperationException();
}
@Override public void getAvgColorLinear(Vector4 c) {
throw new UnsupportedOperationException();
}
@Override public int getWidth() {
return super.getWidth();
}
}