me.sunchiro.game.engine.gl.Graphic.java Source code

Java tutorial

Introduction

Here is the source code for me.sunchiro.game.engine.gl.Graphic.java

Source

/*
 * (C) Copyright 2015 Teerapath Sujitto
    
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
    
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
    
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
    
 */
package me.sunchiro.game.engine.gl;

import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;

import me.sunchiro.game.engine.gl.object.*;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.*;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.LinkedList;

public class Graphic {
    private GLFWErrorCallback errorCallback;
    private GLFWWindowSizeCallback windowSizeCalback;
    private boolean FBOEnabled;
    private boolean invalidated = false;
    private int width;
    private int height;
    private long window;
    public Shader shader;
    private TextureManager texManager;
    public Camera cam;
    private Matrix4f mvpMat;
    private FloatBuffer matBuff = null;
    private LinkedList<Drawable> objects = new LinkedList<Drawable>();
    private LinkedList<Drawable> orthoObjects = new LinkedList<Drawable>();
    public static Graphic instance;
    public Vector3f look = new Vector3f(0, 0, 0);
    public int vertexCount = 0;
    private int indicesCount = 0;
    public int tid;
    public int tid_charmap;
    public Vector3f bgColor = new Vector3f(0.22f, 0.23f, 0.25f);
    ByteBuffer vertByteBuff;
    int vaoId = 0;
    int vboId = 0;
    int vboiId = 0;
    public float allScale = 1;

    public Graphic() {
        shader = new Shader();
        texManager = new TextureManager();
        cam = new Camera();
    }

    public void invalidate() {
        invalidated = true;
    }

    public void createWindow(int width, int height, String name) {
        this.width = width;
        this.height = height;
        glfwSetErrorCallback(errorCallback = Callbacks.errorCallbackPrint(System.err));
        if (glfwInit() != GL11.GL_TRUE)
            throw new IllegalStateException("Unable to initialize GLFW");
        glfwDefaultWindowHints(); // optional, the current window hints are
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
        glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); // the window will stay
        // hidden
        glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); // the window will be
        // resizable
        window = glfwCreateWindow(width, height, name, NULL, NULL);
        if (window == NULL)
            throw new RuntimeException("Failed to create the GLFW window");
        // glfwSetKeyCallback(window,this.keyCallback = keyCallback);
        glfwSetWindowSizeCallback(window, resizeCallback());
        ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        glfwSetWindowPos(window, (vidmode.asIntBuffer().get(0) - width) / 2,
                (vidmode.asIntBuffer().get(1) - height) / 2);
        glfwMakeContextCurrent(window);
        Graphic.instance = this;
        glfwSwapInterval(1);
        GL.createCapabilities();
        setCapabilities();

        glfwShowWindow(window);

        // Setup an XNA like background color
        GL11.glEnable(GL11.GL_DEPTH_TEST);
        GL11.glEnable(GL11.GL_ALPHA_TEST);
        GL11.glDepthFunc(GL11.GL_LESS);

        GL11.glClearColor(bgColor.x, bgColor.y, bgColor.z, 0);
        // Map the internal OpenGL coordinate system to the entire screen
        GL11.glViewport(0, 0, width, height);
        matBuff = BufferUtils.createFloatBuffer(16);
        vaoId = GL30.glGenVertexArrays();
        vboiId = GL15.glGenBuffers();
        vboId = GL15.glGenBuffers();
        tid_charmap = texManager.loadTexture("textures/charmap.png", GL13.GL_TEXTURE0);
        shader.setupShader();
        // testQuad();

    }

    public void loadTexture(String[] filename) {
        for (String file : filename) {
            texManager.loadTexture(file, GL13.GL_TEXTURE0);
        }
    }

    public synchronized void addObject(Drawable object) {
        objects.add(object);
        vertexCount += object.getVertex().length;
        indicesCount += object.getIndices().length;
        invalidate();
    }

    public synchronized void addOrthoObject(Drawable object) {
        orthoObjects.add(object);
        vertexCount += object.getVertex().length;
        indicesCount += object.getIndices().length;
        invalidate();
    }

    public synchronized void addObjects(Drawable[] objectArray) {
        for (Drawable object : objectArray) {
            vertexCount += object.getVertex().length;
            indicesCount += object.getIndices().length;
            objects.add(object);
        }
        invalidate();
    }

    public synchronized void addOrthoObjects(Drawable[] objectArray) {
        for (Drawable object : objectArray) {
            vertexCount += object.getVertex().length;
            indicesCount += object.getIndices().length;
            orthoObjects.add(object);
        }
        invalidate();
    }

    public synchronized void removeObject(Drawable object) {
        vertexCount -= object.getVertex().length;
        indicesCount -= object.getIndices().length;
        objects.remove(object);
        invalidate();
    }

    public synchronized void clearScene() {
        objects = new LinkedList<Drawable>();
        orthoObjects = new LinkedList<Drawable>();
        vertexCount = 0;
        indicesCount = 0;
    }

    public void removeOrthoObject(Drawable object) {
        vertexCount -= object.getVertex().length;
        indicesCount -= object.getIndices().length;
        orthoObjects.remove(object);
        invalidate();
    }

    public void resizeBuffer() {
        vertByteBuff = BufferUtils.createByteBuffer(vertexCount * Vertex.stride);
        ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(indicesCount);
        FloatBuffer vertFloatBuff = vertByteBuff.asFloatBuffer();
        for (Drawable object : objects) {
            object.store(vertFloatBuff);
            indicesBuffer.put(object.getIndices());
        }
        for (Drawable object : orthoObjects) {
            object.store(vertFloatBuff);
            indicesBuffer.put(object.getIndices());
        }
        indicesBuffer.flip();
        vertFloatBuff.flip();
        GL30.glBindVertexArray(vaoId);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertFloatBuff, GL15.GL_STREAM_DRAW);
        GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, Vertex.stride, 0);
        GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, Vertex.stride, 16);
        GL20.glVertexAttribPointer(2, 2, GL11.GL_FLOAT, false, Vertex.stride, 32);

        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        GL30.glBindVertexArray(0);

        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);

        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public Cube cube;

    public void testQuad() {
        String l = "YEAHHH SUCH STRING,MUCH AWESOME";
        CharQuad quads[] = Generator.StringQuadGenerator(l, 20, 10, 10, 0, true);
        addOrthoObjects(quads);
        Vertex v0 = new Vertex();
        Vertex v1 = new Vertex();
        Vertex v2 = new Vertex();
        Vertex v3 = new Vertex();
        Vertex v4 = new Vertex();
        Vertex v5 = new Vertex();
        Vertex v6 = new Vertex();
        Vertex v7 = new Vertex();

        v0.setXYZ(-1.5f, 1.5f, 0);
        v1.setXYZ(-1.5f, 0.5f, 0);
        v2.setXYZ(-0.5f, 0.5f, 0);
        v3.setXYZ(-0.5f, 1.5f, 0);
        v4.setXYZ(-1.5f, 1.5f, -1f);
        v5.setXYZ(-1.5f, 0.5f, -1f);
        v6.setXYZ(-0.5f, 0.5f, -1f);
        v7.setXYZ(-0.5f, 1.5f, -1f);
        cube = new Cube(new Vertex[] { v0, v1, v2, v3, v4, v5, v6, v7 });
        cube.mapTexture(new Vector2f(0, 0f), new Vector2f(1, 1 / 8f));
        addObject(cube);
    }

    public void setCapabilities() {
        FBOEnabled = GL.getCapabilities().GL_EXT_framebuffer_object;
    }

    public void destroy() {
        shader.destroy();
    }

    public void draw() {
        logic();
        render();
    }

    private synchronized void logic() {
        if (invalidated) {
            invalidated = false;
            resizeBuffer();
        }
        mvpMat = cam.getMVP();
        if (vertexCount == 0)
            return;
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
        int offset = 0;
        FloatBuffer vertFloatBuff = vertByteBuff.asFloatBuffer();
        GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, offset, vertByteBuff);
        vertFloatBuff.rewind();
        for (Drawable object : objects) {
            object.store(vertFloatBuff);
        }
        for (Drawable object : orthoObjects) {
            object.store(vertFloatBuff);
        }
        vertFloatBuff.flip();
        GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, offset, vertByteBuff);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        // GL20.glUseProgram(shader.getPID());
        // mvpMat.get(0, matBuff);
        // // matBuff.flip();
        // // matBuff.put(0,5.0f);
        // GL20.glUniformMatrix4fv(shader.getMVPLocation(), false, matBuff);
        // GL20.glUseProgram(0);
    }

    private synchronized void render() {
        GL11.glClearColor(bgColor.x, bgColor.y, bgColor.z, 0);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        GL20.glUseProgram(shader.getPID());
        //

        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL30.glBindVertexArray(vaoId);

        GL20.glEnableVertexAttribArray(0);
        GL20.glEnableVertexAttribArray(1);
        GL20.glEnableVertexAttribArray(2);

        GL11.glBindTexture(GL11.GL_TEXTURE_2D, texManager.getTexture(tid));
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
        long offset = 0;
        int shift = 0;
        GL11.glAlphaFunc(GL11.GL_GREATER, 0.4f);
        GL11.glDisable(GL11.GL_CULL_FACE);
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

        for (Drawable object : objects) {
            Matrix4f finalMVP = new Matrix4f(mvpMat);
            Matrix4f modelMat = new Matrix4f();
            Matrix4f scaler = new Matrix4f();
            scaler.scale(object.scale * allScale);
            modelMat.translate(object.translation);
            modelMat.rotateXYZ(object.rotation.x, object.rotation.y, object.rotation.z);
            finalMVP.mul(modelMat);
            finalMVP.mul(scaler);
            finalMVP.get(0, matBuff);
            if (object.isChar) {
                GL11.glBindTexture(GL11.GL_TEXTURE_2D, texManager.getTexture(tid_charmap));
            }
            GL20.glUniformMatrix4fv(shader.getMVPLocation(), false, matBuff);
            GL20.glUniform1i(shader.getInverseLocation(), object.inverseAlpha ? 1 : 0);
            if (object.isVisible())
                GL32.glDrawElementsBaseVertex(GL11.GL_TRIANGLES, object.getIndices().length, GL11.GL_UNSIGNED_BYTE,
                        offset, shift);

            offset += object.getIndices().length;
            shift += object.getVertex().length;
            if (object.isChar) {
                GL11.glBindTexture(GL11.GL_TEXTURE_2D, texManager.getTexture(tid));
            }
        }
        render2d(offset, shift);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        GL20.glDisableVertexAttribArray(0);
        GL20.glDisableVertexAttribArray(1);
        GL20.glDisableVertexAttribArray(2);
        GL30.glBindVertexArray(0);
        //
        GL20.glUseProgram(0);
        glfwSwapBuffers(window);
        glfwPollEvents();

    }

    private void render2d(long offset, int shift) {
        cam.getOrthoMVP().get(0, matBuff);
        GL20.glUniformMatrix4fv(shader.getMVPLocation(), false, matBuff);

        for (Drawable object : orthoObjects) {
            if (object.isChar) {
                GL11.glBindTexture(GL11.GL_TEXTURE_2D, texManager.getTexture(tid_charmap));
            }
            GL20.glUniform1i(shader.getInverseLocation(), object.inverseAlpha ? 1 : 0);
            if (object.isVisible())
                GL32.glDrawElementsBaseVertex(GL11.GL_TRIANGLES, object.getIndices().length, GL11.GL_UNSIGNED_BYTE,
                        offset, shift);
            offset += object.getIndices().length;
            shift += object.getVertex().length;
        }
        // static utility function
    }

    private GLFWWindowSizeCallback resizeCallback() {
        return windowSizeCalback = new GLFWWindowSizeCallback() {

            @Override
            public void invoke(long window, int newwidth, int newheight) {
                width = newwidth;
                height = newheight;
                cam.resize(newwidth, newheight);
                GL11.glViewport(0, 0, width, height);

            }
        };
    }

    public long getWindow() {
        return window;
    }

    public boolean windowShouldClose() {
        return glfwWindowShouldClose(window) == GL11.GL_TRUE;
    }

    public void resizeWindow(int width, int height) {
        glfwSetWindowSize(window, width, height);
    }

    public void setWindowTitle(byte[] titleName) {
        glfwSetWindowTitle(window, ByteBuffer.wrap(titleName));
    }

    public void setWindowTitle(String titleName) {
        try {
            setWindowTitle(titleName.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
        resizeWindow(width, height);
    }

    public int getHeight() {
        return height;
    }

    public float getAspectRatio() {
        return width / (float) height;
    }

    public void setHeight(int height) {
        this.height = height;
        resizeWindow(width, height);
    }
}