illarion.graphics.lwjgl.TextureAtlasLWJGL.java Source code

Java tutorial

Introduction

Here is the source code for illarion.graphics.lwjgl.TextureAtlasLWJGL.java

Source

/*
 * This file is part of the Illarion Graphics Engine.
 *
 * Copyright  2011 - Illarion e.V.
 *
 * The Illarion Graphics Engine is free software: you can redistribute i 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.
 * 
 * The Illarion Graphics Engine 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
 * the Illarion Graphics Interface. If not, see <http://www.gnu.org/licenses/>.
 */
package illarion.graphics.lwjgl;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.Util;

import illarion.graphics.Graphics;
import illarion.graphics.generic.AbstractTextureAtlas;

/**
 * TextureAtlas implementation for usage with LWJGL.
 * 
 * @author Martin Karing
 * @version 2.00
 * @since 2.00
 */
public final class TextureAtlasLWJGL extends AbstractTextureAtlas {
    /**
     * The buffer that is used to get the generated texture IDs.
     */
    private static final IntBuffer texIDBuffer;

    static {
        texIDBuffer = BufferUtils.createIntBuffer(4);
    }

    /**
     * The internal format of this texture atlas.
     */
    private int internalFormat = 0;

    /**
     * The format of the original source data of this texture.
     */
    private int sourceFormat = 0;

    /**
     * Constructor to create a empty texture Atlas.
     */
    public TextureAtlasLWJGL() {
        super();
    }

    /**
     * Get a new texture ID from the OpenGL system. This also registers the
     * texture in the openGL environment, so it can be used right away.
     * 
     * @return the generated openGL texture ID
     */
    private static int getNewTextureID() {
        texIDBuffer.rewind();
        GL11.glGenTextures(texIDBuffer);
        return texIDBuffer.get(0);
    }

    /**
     * Activate the texture and prepare it for usage by OpenGL.
     * 
     * @param resizeable true in case the texture shall be loaded with advanced
     *            rescaling methods, that are more calculation intensive but
     *            look better then the normal ones
     * @param allowCompression true if the texture is compressed at default
     *            settings, false if not. Best disallow compression for static
     *            images such as tiles, since the effects of the compression
     *            will be quite visible there
     */
    @Override
    @SuppressWarnings("nls")
    public void activateTexture(final boolean resizeable, final boolean allowCompression) {

        if (!hasTextureData()) {
            throw new IllegalStateException("No texturedata loaded");
        }

        final int quality = Graphics.getInstance().getQuality();

        if (getTextureID() != 0) {
            removeTexture();
        }

        // generate new texture ID
        final int texID = getNewTextureID();
        setTextureID(texID);

        // bind texture ID
        DriverSettingsLWJGL.getInstance().enableTexture(texID);

        // prepare texture data
        if (resizeable) { // Textures will be resized -> smoothing would be good
            if (quality <= Graphics.QUALITY_LOW) {
                GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
                GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
            } else if ((quality <= Graphics.QUALITY_NORMAL) || isNoMipMaps()) {
                GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
                GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
            } else {
                if (GLContext.getCapabilities().OpenGL14) {
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL14.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
                } else {
                    setNoMipMaps(true);
                }
                if (!isNoMipMaps()) {
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
                            GL11.GL_LINEAR_MIPMAP_LINEAR);
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
                } else {
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
                }
            }
        } else {
            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        }
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);

        if (GLContext.getCapabilities().OpenGL13) {
            GL11.glHint(GL13.GL_TEXTURE_COMPRESSION_HINT, GL11.GL_NICEST);
        }

        // setup texture compression
        final boolean activateCompression = GLContext.getCapabilities().OpenGL13
                && ((allowCompression && (quality < Graphics.QUALITY_MAX)) || (quality <= Graphics.QUALITY_LOW));
        if (isTextureRGBA()) {
            internalFormat = GL11.GL_RGBA;
            sourceFormat = GL11.GL_RGBA;
            if (activateCompression) {
                internalFormat = GL13.GL_COMPRESSED_RGBA;
            }
        } else if (isTextureRGB()) {
            internalFormat = GL11.GL_RGB;
            sourceFormat = GL11.GL_RGB;
            if (activateCompression) {
                internalFormat = GL13.GL_COMPRESSED_RGB;
            }
        } else if (isTextureGrey()) {
            internalFormat = GL11.GL_LUMINANCE;
            sourceFormat = GL11.GL_LUMINANCE;
            if (activateCompression) {
                internalFormat = GL13.GL_COMPRESSED_LUMINANCE;
            }
        } else if (isTextureGreyAlpha()) {
            internalFormat = GL11.GL_LUMINANCE_ALPHA;
            sourceFormat = GL11.GL_LUMINANCE_ALPHA;
            if (activateCompression) {
                internalFormat = GL13.GL_COMPRESSED_LUMINANCE_ALPHA;
            }
        }

        final ByteBuffer texData = getTextureData();

        final int texWidth = getTextureWidth();
        final int texHeight = getTextureHeight();

        // produce a texture from the byte buffer
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, texWidth, texHeight, 0, sourceFormat,
                GL11.GL_UNSIGNED_BYTE, texData);

        if (quality < Graphics.QUALITY_MAX) {
            texIDBuffer.rewind();
            GL11.glGetTexLevelParameter(GL11.GL_TEXTURE_2D, 0, GL13.GL_TEXTURE_COMPRESSED, texIDBuffer);

            texData.rewind();
            if (texIDBuffer.get(0) == GL11.GL_FALSE) {
                int newInternalFormat = internalFormat;
                if (internalFormat == GL13.GL_COMPRESSED_LUMINANCE_ALPHA) {
                    newInternalFormat = GL13.GL_COMPRESSED_RGBA;
                } else if (internalFormat == GL13.GL_COMPRESSED_LUMINANCE) {
                    newInternalFormat = GL13.GL_COMPRESSED_RGB;
                }
                final int orgSize = texData.remaining();
                if (newInternalFormat != internalFormat) {
                    GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, newInternalFormat, texWidth, texHeight, 0,
                            sourceFormat, GL11.GL_UNSIGNED_BYTE, texData);
                    GL11.glGetTexLevelParameter(GL11.GL_TEXTURE_2D, 0, GL13.GL_TEXTURE_COMPRESSED_IMAGE_SIZE,
                            texIDBuffer);
                    final int newSize = texIDBuffer.get(0);
                    if (newSize > orgSize) {
                        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, texWidth, texHeight, 0,
                                sourceFormat, GL11.GL_UNSIGNED_BYTE, texData);
                    }
                }
            }
        }

        texIDBuffer.rewind();
        GL11.glGetTexLevelParameter(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT, texIDBuffer);
        internalFormat = texIDBuffer.get();

        discardImageData();
    }

    /**
     * Add a image definition to the storage that marks the locations of the
     * image on the texture.
     * 
     * @param fileName the name of the image that works as the reference to the
     *            image file
     * @param x the x coordinate of the location of the image on the texture map
     * @param y the y coordinate of the location of the image on the texture map
     * @param w the width of the image
     * @param h the height of the image
     */
    @Override
    public void addImage(final String fileName, final int x, final int y, final int w, final int h) {

        final TextureLWJGL tex = new TextureLWJGL(w, h, getTextureWidth(), getTextureHeight(), x, y);
        tex.setParent(this);

        addTextureToBuffer(fileName, tex);
    }

    /**
     * Generate a identifier for this texture as human readable version.
     * 
     * @return human readable identifier for this texture
     */
    @SuppressWarnings("nls")
    @Override
    public String toString() {
        return "Texture: " + getFileName();
    }

    /**
     * Change a area of the texture.
     * 
     * @param x the x coordinate of the origin of the area that is changed
     * @param y the y coordinate of the origin of the area that is changed
     * @param w the width of the area that is changed
     * @param h the height of the area that is changed
     * @param image the image that is drawn in the area
     */
    @Override
    public void updateTextureArea(final int x, final int y, final int w, final int h, final BufferedImage image) {

        final byte[] imageByteData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();

        final ByteBuffer imageBuffer = getByteBuffer(imageByteData.length);
        imageBuffer.clear();
        imageBuffer.put(imageByteData, 0, imageByteData.length);
        imageBuffer.flip();

        updateTextureArea(x, y, w, h, imageBuffer);
    }

    /**
     * Change a area of the texture.
     * 
     * @param x the x coordinate of the origin of the area that is changed
     * @param y the y coordinate of the origin of the area that is changed
     * @param w the width of the area that is changed
     * @param h the height of the area that is changed
     * @param imageData the image data that shall be updated
     */
    @Override
    public void updateTextureArea(final int x, final int y, final int w, final int h, final ByteBuffer imageData) {
        DriverSettingsLWJGL.getInstance().enableTexture(getTextureID());
        GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, x, y, w, h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageData);
        Util.checkGLError();
    }

    /**
     * Remove the texture from the video ram of the graphic card.
     */
    @Override
    protected void removeFromVRam() {
        final int texID = getTextureID();
        if (texID != 0) {
            texIDBuffer.rewind();
            texIDBuffer.put(texID);

            GL11.glDeleteTextures(texIDBuffer);
            setTextureID(0);
        }
    }
}