tectonicus.rasteriser.lwjgl.LwjglRasteriser.java Source code

Java tutorial

Introduction

Here is the source code for tectonicus.rasteriser.lwjgl.LwjglRasteriser.java

Source

/*
 * Source code from Tectonicus, http://code.google.com/p/tectonicus/
 *
 * Tectonicus is released under the BSD license (below).
 *
 *
 * Original code John Campbell / "Orangy Tang" / www.triangularpixels.com
 *
 * Copyright (c) 2012, John Campbell
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, this list
 *     of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright notice, this
 *     list of conditions and the following disclaimer in the documentation and/or
 *     other materials provided with the distribution.
 *   * Neither the name of 'Tecctonicus' nor the names of
 *     its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package tectonicus.rasteriser.lwjgl;

import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.Drawable;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

import tectonicus.configuration.ImageFormat;
import tectonicus.rasteriser.AlphaFunc;
import tectonicus.rasteriser.BlendFunc;
import tectonicus.rasteriser.Mesh;
import tectonicus.rasteriser.PrimativeType;
import tectonicus.rasteriser.Rasteriser;
import tectonicus.rasteriser.Texture;
import tectonicus.rasteriser.TextureFilter;
import tectonicus.rasteriser.RasteriserFactory.DisplayType;

public class LwjglRasteriser implements Rasteriser {
    private final DisplayType type;

    private Pbuffer pbuffer;

    private int width, height;

    private Map<Integer, Integer> keyCodeMap;

    private Map<Integer, Boolean> prevKeyStates;

    public LwjglRasteriser(DisplayType type, final int displayWidth, final int displayHeight, final int colourDepth,
            final int alphaBits, final int depthBits, final int numSamples) throws Exception {
        this.type = type;

        this.width = displayWidth;
        this.height = displayHeight;

        keyCodeMap = new HashMap<Integer, Integer>();

        keyCodeMap.put(KeyEvent.VK_0, Keyboard.KEY_0);
        keyCodeMap.put(KeyEvent.VK_1, Keyboard.KEY_1);
        keyCodeMap.put(KeyEvent.VK_2, Keyboard.KEY_2);
        keyCodeMap.put(KeyEvent.VK_3, Keyboard.KEY_3);
        keyCodeMap.put(KeyEvent.VK_4, Keyboard.KEY_4);
        keyCodeMap.put(KeyEvent.VK_5, Keyboard.KEY_5);
        keyCodeMap.put(KeyEvent.VK_6, Keyboard.KEY_6);
        keyCodeMap.put(KeyEvent.VK_7, Keyboard.KEY_7);
        keyCodeMap.put(KeyEvent.VK_8, Keyboard.KEY_8);
        keyCodeMap.put(KeyEvent.VK_9, Keyboard.KEY_9);

        keyCodeMap.put(KeyEvent.VK_UP, Keyboard.KEY_UP);
        keyCodeMap.put(KeyEvent.VK_DOWN, Keyboard.KEY_DOWN);
        keyCodeMap.put(KeyEvent.VK_LEFT, Keyboard.KEY_LEFT);
        keyCodeMap.put(KeyEvent.VK_RIGHT, Keyboard.KEY_RIGHT);

        keyCodeMap.put(KeyEvent.VK_SPACE, Keyboard.KEY_SPACE);
        keyCodeMap.put(KeyEvent.VK_MINUS, Keyboard.KEY_MINUS);
        keyCodeMap.put(KeyEvent.VK_EQUALS, Keyboard.KEY_EQUALS);
        keyCodeMap.put(KeyEvent.VK_BACK_SPACE, Keyboard.KEY_BACK);

        keyCodeMap.put(KeyEvent.VK_A, Keyboard.KEY_A);
        keyCodeMap.put(KeyEvent.VK_B, Keyboard.KEY_B);
        keyCodeMap.put(KeyEvent.VK_C, Keyboard.KEY_C);
        keyCodeMap.put(KeyEvent.VK_D, Keyboard.KEY_D);
        keyCodeMap.put(KeyEvent.VK_E, Keyboard.KEY_E);
        keyCodeMap.put(KeyEvent.VK_F, Keyboard.KEY_F);
        keyCodeMap.put(KeyEvent.VK_G, Keyboard.KEY_G);
        keyCodeMap.put(KeyEvent.VK_H, Keyboard.KEY_H);
        keyCodeMap.put(KeyEvent.VK_I, Keyboard.KEY_I);
        keyCodeMap.put(KeyEvent.VK_J, Keyboard.KEY_J);
        keyCodeMap.put(KeyEvent.VK_K, Keyboard.KEY_K);
        keyCodeMap.put(KeyEvent.VK_L, Keyboard.KEY_L);
        keyCodeMap.put(KeyEvent.VK_M, Keyboard.KEY_M);
        keyCodeMap.put(KeyEvent.VK_N, Keyboard.KEY_N);
        keyCodeMap.put(KeyEvent.VK_O, Keyboard.KEY_O);
        keyCodeMap.put(KeyEvent.VK_P, Keyboard.KEY_P);
        keyCodeMap.put(KeyEvent.VK_Q, Keyboard.KEY_Q);
        keyCodeMap.put(KeyEvent.VK_R, Keyboard.KEY_R);
        keyCodeMap.put(KeyEvent.VK_S, Keyboard.KEY_S);
        keyCodeMap.put(KeyEvent.VK_T, Keyboard.KEY_T);
        keyCodeMap.put(KeyEvent.VK_U, Keyboard.KEY_U);
        keyCodeMap.put(KeyEvent.VK_V, Keyboard.KEY_V);
        keyCodeMap.put(KeyEvent.VK_W, Keyboard.KEY_W);
        keyCodeMap.put(KeyEvent.VK_X, Keyboard.KEY_X);
        keyCodeMap.put(KeyEvent.VK_Y, Keyboard.KEY_Y);
        keyCodeMap.put(KeyEvent.VK_Z, Keyboard.KEY_Z);

        prevKeyStates = new HashMap<Integer, Boolean>();

        Drawable drawable = Display.getDrawable();
        System.out.println("\tDrawable: " + drawable);

        // Make a list of pixel formats to try (in preferance order)
        ArrayList<PixelFormat> pixelFormats = new ArrayList<PixelFormat>();

        // As requested
        pixelFormats.add(new PixelFormat(colourDepth, alphaBits, depthBits, 0, numSamples));

        // No anti-aliasing
        pixelFormats.add(new PixelFormat(colourDepth, alphaBits, depthBits, 0, 0));

        // No anti-aliasing or alpha buffer
        pixelFormats.add(new PixelFormat(colourDepth, 0, depthBits, 0, 0));

        // No anti-aliasing, no alpha buffer, 16bit colour
        pixelFormats.add(new PixelFormat(16, 0, depthBits, 0, 0));

        // No anti-aliasing, no alpha buffer, 16bit colour, 16bit depth
        pixelFormats.add(new PixelFormat(16, 0, 16, 0, 0));

        // Ugh. Anything with a depth buffer.
        pixelFormats.add(new PixelFormat(0, 0, 1, 0, 0));

        PixelFormat usedPixelFormat = null;
        LWJGLException pbufferException = null;

        if (type == DisplayType.Offscreen) {
            for (PixelFormat pf : pixelFormats) {
                try {
                    pbuffer = new Pbuffer(displayWidth, displayHeight, pf, drawable);
                    usedPixelFormat = pf;
                    break;
                } catch (LWJGLException e) {
                    pbufferException = e;
                }
            }

            if (pbuffer != null) {
                // Ok!
                System.out.println("\tcreated pbuffer: " + pbuffer);
                System.out.println("\tused pixel format:   colour:" + usedPixelFormat.getBitsPerPixel() + " depth:"
                        + usedPixelFormat.getDepthBits() + " alpha:" + usedPixelFormat.getAlphaBits() + " stencil:"
                        + usedPixelFormat.getStencilBits() + " samples:" + usedPixelFormat.getSamples());
            } else {
                System.err.println("Could not create pbuffer! (colour:" + colourDepth + ", alpha:" + alphaBits
                        + ", depth:" + depthBits + ", samples:" + numSamples + ")");
                throw pbufferException;
            }

            pbuffer.makeCurrent();

            // Issue a few gl commands so we fail early if the pbuffer is actually a bogus one
            resetState();

        } else if (type == DisplayType.Window) {
            Display.setDisplayMode(new DisplayMode(displayWidth, displayHeight));
            Display.setLocation((Display.getDisplayMode().getWidth() - displayWidth) / 2,
                    (Display.getDisplayMode().getHeight() - displayHeight) / 2);
            Display.setTitle("Tectonicus");

            // TODO: Use same pixel formats from above here
            Display.create(new PixelFormat(32, 0, 16, 0, 4));
        } else {
            throw new RuntimeException("Unknown display type: " + type);
        }

        System.out.println("\tdisplay created ok");
    }

    @Override
    public void destroy() {
        if (type == DisplayType.Window) {
            Display.destroy();
        } else if (type == DisplayType.Offscreen) {
            pbuffer.destroy();
        }
    }

    @Override
    public void printInfo() {
        System.out.println(" -- Lwjgl Rasteriser -- ");
        System.out.println("\tLWJGL version: " + Sys.getVersion());
        System.out.println("\ttype: " + type);
        System.out.println("\twidth: " + width);
        System.out.println("\theigth: " + height);
        System.out.println("\tpBuffer: " + pbuffer);

        System.out.println("\tOpenGL Vendor: " + GL11.glGetString(GL11.GL_VENDOR));
        System.out.println("\tOpenGL Renderer: " + GL11.glGetString(GL11.GL_RENDERER));
        System.out.println("\tOpenGL Version: " + GL11.glGetString(GL11.GL_VERSION));

        //   System.out.println();

        //   GL11.glGetString(GL11.GL_EXTENSIONS);
    }

    @Override
    public void sync() {
        prevKeyStates.clear();
        for (Integer i : keyCodeMap.values()) {
            prevKeyStates.put(i, Keyboard.isKeyDown(i));
        }

        Display.update();
        Display.sync(60);
    }

    @Override
    public boolean isCloseRequested() {
        return Display.isCloseRequested();
    }

    @Override
    public void setViewport(final int x, final int y, final int width, final int height) {
        GL11.glViewport(x, y, width, height);
    }

    @Override
    public boolean isKeyDown(final int vkKey) {
        if (!keyCodeMap.containsKey(vkKey))
            throw new RuntimeException("No mapping for :" + vkKey);

        Integer lwjglKey = keyCodeMap.get(vkKey);
        return Keyboard.isKeyDown(lwjglKey);
    }

    public boolean isKeyJustDown(final int vkKey) {
        if (!keyCodeMap.containsKey(vkKey))
            throw new RuntimeException("No mapping for :" + vkKey);

        Integer lwjglKey = keyCodeMap.get(vkKey);

        return Keyboard.isKeyDown(lwjglKey) && !prevKeyStates.get(lwjglKey);
    }

    public int getDisplayWidth() {
        return width;
    }

    public int getDisplayHeight() {
        return height;
    }

    public Texture createTexture(BufferedImage image, TextureFilter filter) {
        final int id = LwjglTextureUtils.createTexture(image, filter);
        Texture texture = new LwjglTexture(id, image.getWidth(), image.getHeight());
        return texture;
    }

    public Texture createTexture(BufferedImage[] mips, TextureFilter filter) {
        final int id = LwjglTextureUtils.createTexture(mips, filter);
        Texture texture = new LwjglTexture(id, mips[0].getWidth(), mips[0].getHeight());
        return texture;
    }

    public Mesh createMesh(Texture texture) {
        return new LwjglMesh((LwjglTexture) texture);
    }

    @Override
    public void beginFrame() {

    }

    @Override
    public void resetState() {
        GL11.glColorMask(true, true, true, true);

        GL11.glDepthFunc(GL11.GL_LEQUAL);

        GL11.glEnable(GL11.GL_DEPTH_TEST);
        GL11.glEnable(GL11.GL_CULL_FACE);
        GL11.glCullFace(GL11.GL_BACK);
        GL11.glFrontFace(GL11.GL_CW);

        GL11.glDisable(GL11.GL_BLEND);
        GL11.glDisable(GL11.GL_ALPHA_TEST);
    }

    @Override
    public void clear(Color clearColour) {
        GL11.glClearColor(clearColour.getRed() / 255.0f, clearColour.getGreen() / 255.0f,
                clearColour.getBlue() / 255.0f, 0);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
    }

    @Override
    public void clearDepthBuffer() {
        GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
    }

    // Slow, simple version. Pull framebuffer and convert to BufferedImage via setRGB()
    public BufferedImage takeScreenshot2(final int startX, final int startY, final int width, final int height,
            ImageFormat imageFormat) {
        ByteBuffer screenContentsBytes = ByteBuffer.allocateDirect(width * height * 4)
                .order(ByteOrder.LITTLE_ENDIAN);
        IntBuffer screenContents = screenContentsBytes.asIntBuffer();

        GL11.glReadPixels(startX, startY, width, height, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, screenContents);

        int[] pixels = new int[width * height];
        screenContents.get(pixels);

        final int pixelFormat = imageFormat.hasAlpha() ? BufferedImage.TYPE_4BYTE_ABGR
                : BufferedImage.TYPE_3BYTE_BGR;
        BufferedImage img = new BufferedImage(width, height, pixelFormat);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                final int rgba = pixels[x + y * width];

                img.setRGB(x, height - 1 - y, rgba);
            }
        }

        return img;
    }

    public BufferedImage takeScreenshot(final int startX, final int startY, final int width, final int height,
            ImageFormat imageFormat) {
        BufferedImage img = null;

        ByteBuffer screenContentsBytes = ByteBuffer.allocateDirect(width * height * 4)
                .order(ByteOrder.LITTLE_ENDIAN);
        IntBuffer screenContents = screenContentsBytes.asIntBuffer();

        if (imageFormat.hasAlpha()) {
            img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            int[] pixels = ((DataBufferInt) (img.getRaster().getDataBuffer())).getData();

            GL11.glReadPixels(startX, startY, width, height, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, screenContents);

            for (int y = startY; y < startY + height; y++) {
                screenContents.position(y * width);
                screenContents.get(pixels, (height - y - 1) * width, width);
            }
        } else {
            img = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
            byte[] pixels = ((DataBufferByte) (img.getRaster().getDataBuffer())).getData();

            GL11.glReadPixels(startX, startY, width, height, GL12.GL_BGR, GL11.GL_UNSIGNED_BYTE, screenContents);

            for (int y = startY; y < startY + height; y++) {
                screenContentsBytes.position(y * width * 3);
                screenContentsBytes.get(pixels, (height - y - 1) * width * 3, width * 3);
            }
        }

        return img;
    }

    public void bindTexture(Texture texture) {
        LwjglTexture tex = (LwjglTexture) texture;

        if (tex != null) {
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex.getId());
        } else {
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
        }
    }

    public void beginShape(PrimativeType type) {
        int glType = GL11.GL_TRIANGLES;
        switch (type) {
        case Points:
            glType = GL11.GL_POINTS;
            break;
        case Lines:
            glType = GL11.GL_LINES;
            break;
        case Triangles:
            glType = GL11.GL_TRIANGLES;
            break;
        case Quads:
            glType = GL11.GL_QUADS;
            break;
        default:
            assert false;
        }
        GL11.glBegin(glType);
    }

    public void colour(final float r, final float g, final float b, final float a) {
        GL11.glColor4f(r, g, b, a);
    }

    @Override
    public void texCoord(final float u, final float v) {
        GL11.glTexCoord2f(u, v);
    }

    public void vertex(final float x, final float y, final float z) {
        GL11.glVertex3f(x, y, z);
    }

    public void endShape() {
        GL11.glEnd();
    }

    public void setProjectionMatrix(Matrix4f matrix) {
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();

        FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
        matrix.store(buffer);
        buffer.flip();
        GL11.glLoadMatrix(buffer);
    }

    public void setCameraMatrix(Matrix4f matrix, Vector3f lookAt, Vector3f eye, Vector3f up) {
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();

        FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
        matrix.store(buffer);
        buffer.flip();
        GL11.glLoadMatrix(buffer);
    }

    @Override
    public void enableBlending(final boolean enable) {
        if (enable)
            GL11.glEnable(GL11.GL_BLEND);
        else
            GL11.glDisable(GL11.GL_BLEND);
    }

    @Override
    public void enableDepthTest(boolean enable) {
        if (enable)
            GL11.glEnable(GL11.GL_DEPTH_TEST);
        else
            GL11.glDisable(GL11.GL_DEPTH_TEST);
    }

    @Override
    public void enableAlphaTest(boolean enable) {
        if (enable)
            GL11.glEnable(GL11.GL_ALPHA_TEST);
        else
            GL11.glDisable(GL11.GL_ALPHA_TEST);
    }

    @Override
    public void enableColourWriting(final boolean colourMask, final boolean alphaMask) {
        GL11.glColorMask(colourMask, colourMask, colourMask, alphaMask);
    }

    @Override
    public void enableDepthWriting(final boolean enable) {
        GL11.glDepthMask(enable);
    }

    @Override
    public void setBlendFunc(BlendFunc func) {
        switch (func) {
        case Regular:
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
            break;
        case Additive:
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
            break;
        default:
            assert false;
        }
    }

    @Override
    public void setAlphaFunc(AlphaFunc func, final float refValue) {
        switch (func) {
        case Greater:
            GL11.glAlphaFunc(GL11.GL_GREATER, refValue);
            break;
        case GreaterOrEqual:
            GL11.glAlphaFunc(GL11.GL_GEQUAL, refValue);
            break;
        case Equal:
            GL11.glAlphaFunc(GL11.GL_EQUAL, refValue);
            break;
        case Less:
            GL11.glAlphaFunc(GL11.GL_LESS, refValue);
            break;
        case LessOrEqual:
            GL11.glAlphaFunc(GL11.GL_LEQUAL, refValue);
            break;
        default:
            assert false;
        }
    }
}