se.llbit.chunky.world.SkymapTexture.java Source code

Java tutorial

Introduction

Here is the source code for se.llbit.chunky.world.SkymapTexture.java

Source

/* 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 java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;

import org.apache.commons.math3.util.FastMath;

import se.llbit.chunky.renderer.scene.Scene;
import se.llbit.chunky.resources.Texture;
import se.llbit.log.Log;
import se.llbit.math.Color;
import se.llbit.math.QuickMath;
import se.llbit.math.Ray;
import se.llbit.math.Vector4d;
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;
                    Color.getRGBAComponents(data[index], c);
                    Color.getRGBAComponents(image.getRGB(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);
                    data[index] = Color.getRGB(c);
                }
            }
        }
    }

    private int[] data;

    /**
     * Create new skymap
     * @param image
     */
    public SkymapTexture(BufferedImage image) {
        super(image);
    }

    @Override
    public void setTexture(BufferedImage newImage) {

        if (newImage.getType() == BufferedImage.TYPE_INT_ARGB) {
            image = newImage;
        } else {
            // convert to ARGB
            image = new BufferedImage(newImage.getWidth(), newImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics g = image.createGraphics();
            g.drawImage(newImage, 0, 0, null);
            g.dispose();
        }

        DataBufferInt dataBuffer = (DataBufferInt) image.getRaster().getDataBuffer();
        data = dataBuffer.getData();

        width = image.getWidth();
        height = image.getHeight();
        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) {
                }
            }
        }

        long time = System.currentTimeMillis() - start;

        Log.info("Skymap preprocessing took " + time + "ms");

    }

    @Override
    public void getColor(double u, double v, Vector4d c) {
        Color.getRGBComponents(
                data[(int) (u * width - Ray.EPSILON) + width * (int) ((1 - v) * height - Ray.EPSILON)], c);
    }

    /**
     * Get skymap color at (x, y)
     * @param x
     * @param y
     * @param c
     */
    public void getColor(int x, int y, Vector4d c) {
        Color.getRGBComponents(data[x + width * 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, Vector4d 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(Vector4d c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public BufferedImage getImage() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getWidth() {
        return super.getWidth();
    }

}