se.llbit.chunky.map.BlockLayer.java Source code

Java tutorial

Introduction

Here is the source code for se.llbit.chunky.map.BlockLayer.java

Source

/* Copyright (c) 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.map;

import java.awt.Graphics;
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.chunky.world.Biomes;
import se.llbit.chunky.world.Block;
import se.llbit.chunky.world.Chunk;
import se.llbit.chunky.world.ChunkView;
import se.llbit.math.Color;

/**
 * A layer with block data.
 * @author Jesper qvist <jesper@llbit.se>
 */
public class BlockLayer extends AbstractLayer {
    private final byte[] blocks;
    private final byte[] biomes;
    private final int avgColor;

    /**
     * Load layer from block data
     * @param blockData
     * @param chunkBiomes
     * @param layer
     */
    public BlockLayer(byte[] blockData, byte[] chunkBiomes, int layer) {
        blocks = new byte[Chunk.X_MAX * Chunk.Z_MAX];
        biomes = new byte[Chunk.X_MAX * Chunk.Z_MAX];
        double[] sum = new double[3];
        double[] rgb = new double[3];
        for (int x = 0; x < Chunk.X_MAX; ++x) {
            for (int z = 0; z < Chunk.Z_MAX; ++z) {
                byte block = blockData[Chunk.chunkIndex(x, layer, z)];
                byte biome = chunkBiomes[Chunk.chunkXZIndex(x, z)];
                blocks[x * Chunk.Z_MAX + z] = block;
                biomes[x * Chunk.Z_MAX + z] = block;
                Color.getRGBComponents(avgBlockColor(block, biome), rgb);
                sum[0] += rgb[0];
                sum[1] += rgb[1];
                sum[2] += rgb[2];
            }
        }
        sum[0] /= Chunk.X_MAX * Chunk.Z_MAX;
        sum[1] /= Chunk.X_MAX * Chunk.Z_MAX;
        sum[2] /= Chunk.X_MAX * Chunk.Z_MAX;
        avgColor = Color.getRGB(sum);
    }

    /**
     * Render this layer
     * @param rbuff
     * @param cx
     * @param cz
     */
    @Override
    public synchronized void render(MapBuffer rbuff, int cx, int cz) {
        ChunkView view = rbuff.getView();
        int x0 = view.chunkScale * (cx - view.px0);
        int z0 = view.chunkScale * (cz - view.pz0);

        if (view.chunkScale == 1) {
            rbuff.setRGB(x0, z0, getAvgColor());
        } else if (view.chunkScale == 16) {

            for (int z = 0; z < Chunk.Z_MAX; ++z) {
                int yp = z0 + z;

                for (int x = 0; x < Chunk.X_MAX; ++x) {
                    int xp = x0 + x;

                    byte block = blocks[x * Chunk.Z_MAX + z];
                    byte biome = biomes[x * Chunk.Z_MAX + z];
                    rbuff.setRGB(xp, yp, avgBlockColor(block, biome));
                }
            }
        } else if (view.chunkScale == 16 * 16) {
            for (int z = 0; z < Chunk.Z_MAX; ++z) {
                int yp0 = z0 + z * 16;

                for (int x = 0; x < Chunk.X_MAX; ++x) {
                    int xp0 = x0 + x * 16;

                    byte block = blocks[x * Chunk.Z_MAX + z];
                    if (block == Block.AIR.id) {
                        rbuff.fillRect(xp0, yp0, 16, 16, 0xFFFFFFFF);
                        continue;
                    }

                    switch ((int) block) {
                    case Block.GRASS_ID:
                    case Block.TALLGRASS_ID:
                    case Block.LEAVES_ID:
                    case Block.LEAVES2_ID:
                    case Block.VINES_ID: {
                        Texture tex = Block.get(block).getIcon();
                        for (int i = 0; i < 16; ++i) {
                            for (int j = 0; j < 16; ++j) {
                                float[] rgb = tex.getColor(j, i);
                                if (rgb[3] != 0) {
                                    rbuff.setRGB(xp0 + j, yp0 + i,
                                            getBiomeColor(rgb, block, biomes[x * Chunk.Z_MAX + z]));
                                } else {
                                    rbuff.setRGB(xp0 + j, yp0 + i, 0xFFFFFFFF);
                                }
                            }
                        }
                        break;
                    }
                    default: {
                        int[] tex = ((DataBufferInt) Block.get(block).getIcon().getImage().getRaster()
                                .getDataBuffer()).getData();
                        for (int i = 0; i < 16; ++i) {
                            for (int j = 0; j < 16; ++j) {
                                int rgb = tex[i * 16 + j];
                                if ((rgb & 0xFF000000) != 0) {
                                    rbuff.setRGB(xp0 + j, yp0 + i, rgb);
                                } else {
                                    rbuff.setRGB(xp0 + j, yp0 + i, 0xFFFFFFFF);
                                }
                            }
                        }
                    }
                    }
                }
            }
        }
        // unsupported chunkScale if none of the above
    }

    private int avgBlockColor(byte block, byte biome) {
        if (block == Block.AIR.id) {
            return 0xFFFFFFFF;
        } else {
            switch ((int) block) {
            case Block.GRASS_ID:
            case Block.TALLGRASS_ID:
            case Block.LEAVES_ID:
            case Block.LEAVES2_ID:
            case Block.VINES_ID: {
                float[] rgb = Block.get(block).getIcon().getAvgColorLinear();
                return getBiomeColor(rgb, block, biome);
            }
            default:
                return Block.get(block).getIcon().getAvgColor();
            }
        }
    }

    private int getBiomeColor(float[] rgb, byte block, byte biome) {
        float[] biomeColor;
        float alpha = rgb[3];
        switch ((int) block) {
        case Block.GRASS_ID:
        case Block.TALLGRASS_ID:
            biomeColor = Biomes.getGrassColorLinear(biome);
            return Color.getRGB((1 - alpha) + alpha * FastMath.pow(rgb[0] * biomeColor[0], Scene.DEFAULT_GAMMA_INV),
                    (1 - alpha) + alpha * FastMath.pow(rgb[1] * biomeColor[1], Scene.DEFAULT_GAMMA_INV),
                    (1 - alpha) + alpha * FastMath.pow(rgb[2] * biomeColor[2], Scene.DEFAULT_GAMMA_INV));
        case Block.LEAVES_ID:
        case Block.LEAVES2_ID:
        case Block.VINES_ID:
            biomeColor = Biomes.getFoliageColorLinear(biome);
            return Color.getRGB((1 - alpha) + alpha * FastMath.pow(rgb[0] * biomeColor[0], Scene.DEFAULT_GAMMA_INV),
                    (1 - alpha) + alpha * FastMath.pow(rgb[1] * biomeColor[1], Scene.DEFAULT_GAMMA_INV),
                    (1 - alpha) + alpha * FastMath.pow(rgb[2] * biomeColor[2], Scene.DEFAULT_GAMMA_INV));
        default:
            return Color.getRGB((1 - alpha) + alpha * FastMath.pow(rgb[0], Scene.DEFAULT_GAMMA_INV),
                    (1 - alpha) + alpha * FastMath.pow(rgb[1], Scene.DEFAULT_GAMMA_INV),
                    (1 - alpha) + alpha * FastMath.pow(rgb[2], Scene.DEFAULT_GAMMA_INV));
        }
    }

    /**
     * Render block highlight
     * @param rbuff
     * @param cx
     * @param cz
     * @param hlBlock
     * @param highlight
     */
    @Override
    public synchronized void renderHighlight(MapBuffer rbuff, int cx, int cz, Block hlBlock,
            java.awt.Color highlight) {

        ChunkView view = rbuff.getView();
        int x0 = view.chunkScale * (cx - view.px0);
        int z0 = view.chunkScale * (cz - view.pz0);

        if (blocks == null)
            return;

        Graphics g = rbuff.getGraphics();
        g.setColor(new java.awt.Color(1, 1, 1, 0.35f));
        g.fillRect(x0, z0, view.chunkScale, view.chunkScale);
        g.setColor(highlight);

        if (view.chunkScale == 16) {

            for (int x = 0; x < 16; ++x) {
                int xp = x0 + x;

                for (int z = 0; z < 16; ++z) {
                    int yp = z0 + z;

                    if (hlBlock.id == (0xFF & blocks[x * 16 + z])) {
                        rbuff.setRGB(xp, yp, highlight.getRGB());
                    }
                }
            }
        } else {
            int blockScale = view.chunkScale / 16;

            for (int x = 0; x < 16; ++x) {
                int xp0 = x0 + x * blockScale;
                int xp1 = xp0 + blockScale;

                for (int z = 0; z < 16; ++z) {
                    int yp0 = z0 + z * blockScale;
                    int yp1 = yp0 + blockScale;

                    if (hlBlock.id == (0xFF & blocks[x * 16 + z])) {
                        g.fillRect(xp0, yp0, xp1 - xp0, yp1 - yp0);
                    }
                }
            }
        }
    }

    @Override
    public int getAvgColor() {
        return avgColor;
    }
}