package org.sunflow.core; import java.io.IOException; import org.sunflow.image.Bitmap; import org.sunflow.image.Color; import org.sunflow.math.OrthoNormalBasis; import org.sunflow.math.Vector3; import org.sunflow.system.UI; import org.sunflow.system.UI.Module; /** * Represents a 2D texture, typically used by {@link Shader shaders}. */ public class Texture { private String filename; private boolean isLinear; private Bitmap bitmap; private int loaded; /** * Creates a new texture from the specfied file. * * @param filename image file to load * @param isLinear is the texture gamma corrected already? */ Texture(String filename, boolean isLinear) { this.filename = filename; this.isLinear = isLinear; loaded = 0; } private synchronized void load() { if (loaded != 0) return; try { UI.printInfo(Module.TEX, "Reading texture bitmap from: \"%s\" ...", filename); bitmap = new Bitmap(filename, isLinear); if (bitmap.getWidth() == 0 || bitmap.getHeight() == 0) bitmap = null; } catch (IOException e) { UI.printError(Module.TEX, "%s", e.getMessage()); } loaded = 1; } public Bitmap getBitmap() { if (loaded == 0) load(); return bitmap; } /** * Gets the color at location (x,y) in the texture. The lookup is performed * using the fractional component of the coordinates, treating the texture * as a unit square tiled in both directions. Bicubic filtering is performed * on the four nearest pixels to the lookup point. * * @param x x coordinate into the texture * @param y y coordinate into the texture * @return filtered color at location (x,y) */ public Color getPixel(float x, float y) { Bitmap bitmap = getBitmap(); if (bitmap == null) return Color.BLACK; x = x - (int) x; y = y - (int) y; if (x < 0) x++; if (y < 0) y++; float dx = (float) x * (bitmap.getWidth() - 1); float dy = (float) y * (bitmap.getHeight() - 1); int ix0 = (int) dx; int iy0 = (int) dy; int ix1 = (ix0 + 1) % bitmap.getWidth(); int iy1 = (iy0 + 1) % bitmap.getHeight(); float u = dx - ix0; float v = dy - iy0; u = u * u * (3.0f - (2.0f * u)); v = v * v * (3.0f - (2.0f * v)); float k00 = (1.0f - u) * (1.0f - v); Color c00 = bitmap.getPixel(ix0, iy0); float k01 = (1.0f - u) * v; Color c01 = bitmap.getPixel(ix0, iy1); float k10 = u * (1.0f - v); Color c10 = bitmap.getPixel(ix1, iy0); float k11 = u * v; Color c11 = bitmap.getPixel(ix1, iy1); Color c = Color.mul(k00, c00); c.madd(k01, c01); c.madd(k10, c10); c.madd(k11, c11); return c; } public Vector3 getNormal(float x, float y, OrthoNormalBasis basis) { float[] rgb = getPixel(x, y).getRGB(); return basis.transform(new Vector3(2 * rgb[0] - 1, 2 * rgb[1] - 1, 2 * rgb[2] - 1)).normalize(); } public Vector3 getBump(float x, float y, OrthoNormalBasis basis, float scale) { Bitmap bitmap = getBitmap(); if (bitmap == null) return basis.transform(new Vector3(0, 0, 1)); float dx = 1.0f / (bitmap.getWidth() - 1); float dy = 1.0f / (bitmap.getHeight() - 1); float b0 = getPixel(x, y).getLuminance(); float bx = getPixel(x + dx, y).getLuminance(); float by = getPixel(x, y + dy).getLuminance(); return basis.transform(new Vector3(scale * (bx - b0) / dx, scale * (by - b0) / dy, 1)).normalize(); } }