com.opengrave.og.engine.RenderView.java Source code

Java tutorial

Introduction

Here is the source code for com.opengrave.og.engine.RenderView.java

Source

/*
 * Copyright 2016 Nathan Howard
 * 
 * This file is part of OpenGrave
 * 
 * OpenGrave 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.
 * 
 * OpenGrave 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 OpenGrave. If not, see <http://www.gnu.org/licenses/>.
 */
package com.opengrave.og.engine;

import java.util.ArrayList;
import java.util.Collections;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;

import com.opengrave.og.MainThread;
import com.opengrave.og.Util;
import com.opengrave.og.input.MouseRenderableHoverEvent;
import com.opengrave.og.light.Shadow;
import com.opengrave.og.light.Shadow2D;
import com.opengrave.og.light.ShadowCube;
import com.opengrave.og.util.Matrix3f;
import com.opengrave.og.util.Matrix4f;
import com.opengrave.og.util.Vector3f;
import com.opengrave.og.util.Vector4f;

public class RenderView {

    private RootNode sceneNode;
    private Camera cam;

    private Matrix4f projectionMatrix, viewMatrix, shadowMatrix;
    public int totalx;
    public int totaly;
    public int width;
    public int height;
    private boolean shadows = false, hasshadows = false;
    Matrix4f ident = new Matrix4f();
    private Vector4f clearCol = new Vector4f(0f, 0f, 0f, 1f);
    private boolean clear = false;
    private ArrayList<ShadowCube> lightShadows = new ArrayList<ShadowCube>();

    public RenderView(RootNode node, Camera camera) {
        this.sceneNode = node;
        this.cam = camera;
    }

    public Node getSceneNode() {
        return sceneNode;
    }

    public void setSceneNode(RootNode sceneNode) {
        this.sceneNode = sceneNode;
    }

    public Camera getCam() {
        return cam;
    }

    public void setCam(FlyByCamera cam) {
        this.cam = cam;
    }

    public void clearAreaBeforeDraw(boolean b) {
        clear = b;
    }

    public void renderForPicking(int totalx, int totaly, int width, int height) {
        if (totalx < 0 || totaly < 0 || width < 1 || height < 1) {
            return;
        }
        this.totalx = totalx;
        this.totaly = totaly;
        this.width = width;
        this.height = height;
        if (sceneNode == null) {
            System.out.println("No scene node to render");
            return;
        }
        if (cam == null) {
            System.out.println("No camera to render from");
            return;
        }
        Util.checkErr();
        GL11.glViewport(totalx, totaly, width, height);
        Util.checkErr();
        prepare3DOpaque();
        Util.checkErr();
        sceneNode.renderForPicking(ident);
        Util.checkErr();
        revertSettings();
    }

    public void render(int totalx, int totaly, int width, int height) {
        this.totalx = totalx;
        this.totaly = totaly;
        this.width = width;
        this.height = height;
        if (sceneNode == null) {
            System.out.println("No scene node to render");
            return;
        }
        if (cam == null) {
            System.out.println("No camera to render from");
            return;
        }
        Shadow2D skyLight = sceneNode.getSkyLight();
        if (shadows && hasshadows) {
            // For each light source we map out a texture using a quick
            // render.
            prepare3DShadow();

            skyLight.getFramebuffer().bindDraw();
            // GL11.glEnable(GL32.GL_DEPTH_CLAMP); // TODO Simu;ate depth clamp in shadow shader when z < 0. This way we force distanced objects to still cast a shadow.
            sceneNode.renderShadows(ident, skyLight);
            skyLight.getFramebuffer().unbindDraw();
            int count = 0;
            ArrayList<PointLightNode> lightList = new ArrayList<PointLightNode>();
            sceneNode.getAllLights(lightList, ident, cam.getLocation().toVector4());
            Collections.sort(lightList, new PointLightSorter());
            for (PointLightNode light : lightList) {
                if (light.getColour().x > 0 || light.getColour().y > 0 || light.getColour().z > 0) {
                    // Render each face
                    if (lightShadows.size() == count) {
                        ShadowCube shadow = new ShadowCube(MainThread.config.getInteger("shadowSize", 1024));
                        lightShadows.add(shadow);
                    }
                    ShadowCube shadow = lightShadows.get(count);
                    shadow.setLight(light);
                    for (int i = 0; i < 6; i++) {
                        shadow.getFramebuffer().bindDraw(i);
                        Util.checkErr();
                        shadow.setFace(i);
                        Util.checkErr();
                        sceneNode.renderShadows(ident, shadow);
                        Util.checkErr();
                        shadow.getFramebuffer().unbindDraw();
                        Util.checkErr();
                    }

                    count++;
                }
                if (count == 16) {
                    break;
                }
            }
            GL11.glDisable(GL11.GL_POLYGON_OFFSET_FILL);

        }
        GL11.glViewport(totalx, totaly, width, height);
        GL11.glScissor(totalx, totaly, width, height);
        GL11.glEnable(GL11.GL_SCISSOR_TEST);
        GL11.glClearColor(clearCol.x, clearCol.y, clearCol.z, clearCol.w);
        GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT | (clear ? GL11.GL_COLOR_BUFFER_BIT : 0));
        Util.checkErr();

        prepare3DOpaque();
        sceneNode.render(ident);
        Util.checkErr();
        prepare3DTransparent();
        sceneNode.renderSemiTransparent(ident);
        MouseRenderableHoverEvent lF = MainThread.main.input.getLastHovered();
        if (lF != null) {
            if (lF.getRenderable() instanceof BaseObject) {
                /*
                 * GL11.glDepthFunc(GL11.GL_LESS);
                 * GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
                 * GL11.glPolygonMode(GL11.GL_BACK, GL11.GL_LINE);
                 * 
                 * GL11.glLineWidth(10f);
                 * GL11.glCullFace(GL11.GL_FRONT);
                 * GL11.glEnable(GL11.GL_CULL_FACE);
                 * GL11.glDisable(GL11.GL_BLEND);
                 * BaseObject bo = (BaseObject) lF.getRenderable();
                 * RenderStyle rs = bo.getRenderStyle();
                 * bo.setRenderStyle(RenderStyle.HALO);
                 * sceneNode.renderOne(ident, bo, ident);
                 * 
                 * GL11.glDisable(GL11.GL_BLEND);
                 * 
                 * bo.setRenderStyle(rs);
                 * GL11.glCullFace(GL11.GL_BACK);
                 * GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
                 * GL11.glPolygonMode(GL11.GL_BACK, GL11.GL_FILL);
                 * 
                 * GL11.glLineWidth(1f);
                 * sceneNode.renderOne(ident, bo, ident);
                 */

            }
        }

        revertSettings();
        GL11.glDisable(GL11.GL_SCISSOR_TEST);

    }

    private void prepare3DShadow() {
        GL11.glDisable(GL11.GL_BLEND);
        GL11.glDepthFunc(GL11.GL_LESS);
        // GL11.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
        // GL11.glPolygonOffset(1.1f, 10f);
        GL11.glCullFace(GL11.GL_FRONT);
        GL11.glEnable(GL11.GL_CULL_FACE);
    }

    private void revertSettings() {
        GL11.glViewport(0, 0, MainThread.lastW, MainThread.lastH);
        GL11.glDisable(GL11.GL_CULL_FACE);
        GL11.glEnable(GL11.GL_DEPTH_TEST);
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glDepthFunc(GL11.GL_LEQUAL);
        GL11.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
    }

    public void prepare3DTransparent() {
        Util.checkErr();
        GL11.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
        Util.checkErr();
        GL11.glEnable(GL11.GL_BLEND);
        Util.checkErr();
        GL11.glDepthFunc(GL11.GL_LEQUAL);
        Util.checkErr();
        GL11.glDisable(GL11.GL_CULL_FACE);
    }

    private void prepare3DOpaque() {
        GL11.glDisable(GL11.GL_POLYGON_OFFSET_FILL);

        GL11.glDisable(GL11.GL_BLEND);
        GL11.glDepthFunc(GL11.GL_LESS);
        GL11.glCullFace(GL11.GL_BACK);
        GL11.glEnable(GL11.GL_CULL_FACE);
    }

    public void update(float delta) {
        cam.update(delta);

        projectionMatrix = cam.getProjectionMatrix(width, height);
        viewMatrix = cam.getViewMatrix();

        Shadow2D skyLight = sceneNode.getSkyLight();
        hasshadows = skyLight != null;
        shadows = MainThread.config.getBoolean("shadows", true);
        if (hasshadows) {
            skyLight.update(delta);
        }
        sceneNode.update(this, delta);

    }

    public Matrix4f getProjectionMatrix() {
        return projectionMatrix;
    }

    public void setProjectionMatrix(Matrix4f projectionMatrix) {
        this.projectionMatrix = projectionMatrix;
    }

    public Matrix4f getViewMatrix() {
        return viewMatrix;
    }

    public void setViewMatrix(Matrix4f viewMatrix) {
        this.viewMatrix = viewMatrix;
    }

    public Matrix4f getShadowMatrix() {
        return shadowMatrix;
    }

    public void setShadowMatrix(Matrix4f shadowMatrix) {
        this.shadowMatrix = shadowMatrix;
    }

    public void setMatrices(int pID, Matrix4f modelView) {
        GL20.glUseProgram(pID);
        Util.setUniformMat44(pID, "M", modelView);
        Matrix4f MV = viewMatrix.mult(modelView, null);
        Util.setUniformMat44(pID, "V", viewMatrix);
        Util.setUniformMat44(pID, "MV", MV);
        Matrix4f MVP = projectionMatrix.mult(MV, null);
        Util.setUniformMat44(pID, "MVP", MVP);
        Matrix3f N = makeNormalMatrix(modelView);
        Util.setUniformMat33(pID, "N", N); // FIXME Was M 3x3, watch out for the bugs this'll cause

        if (shadows && hasshadows) {
            Matrix4f shadowBMVP = Util.shadowBiasMatrix.mult(sceneNode.getSkyLight().getMVP(modelView, this), null);
            Util.setUniformMat44(pID, "shadowBMVP", shadowBMVP);
        }
    }

    public Matrix3f makeNormalMatrix(Matrix4f in) {
        Matrix4f out = new Matrix4f(in);

        out.inverse(out);
        out.transpose(out);
        // out.m00 = in.m00;
        // out.m01 = in.m01;
        // out.m02 = in.m02;
        // out.m10 = in.m10;
        // out.m11 = in.m11;
        // out.m12 = in.m12;
        // out.m20 = in.m20;
        // out.m21 = in.m21;
        // out.m22 = in.m22;
        // out = (Matrix3f) out.transpose();
        // out = (Matrix3f) out.invert();

        return new Matrix3f(out);
    }

    public void setShadowMatrices(int pID, Matrix4f modelView, Shadow shadow) {
        GL20.glUseProgram(pID);

        Util.setUniformMat44(pID, "M", modelView);
        Matrix4f MVP = shadow.getMVP(modelView, this);
        Util.setUniformMat44(pID, "MVP", MVP);
    }

    public Matrix4f createMatrixFor(Location location, Matrix4f parent, Matrix4f inside) {
        Vector3f loc = location.toVector3();
        if (cam != null) {
            loc = location.minus(cam.getLocation());
        }
        Matrix4f modelView = new Matrix4f();
        // Translate to world co-ords first
        modelView.translate(loc, modelView);

        // Then scale/rotate
        modelView.scale(location.getScale(), modelView);
        modelView.rotate(Util.degreesToRadians(location.getRotate().z), new Vector3f(0, 0, 1), modelView);
        modelView.rotate(Util.degreesToRadians(location.getRotate().y), new Vector3f(0, 1, 0), modelView);
        modelView.rotate(Util.degreesToRadians(location.getRotate().x), new Vector3f(1, 0, 0), modelView);
        if (parent != null) {
            // Matrix4f.mul(modelView, parent, modelView);
            parent.mult(modelView, modelView);
        }
        if (inside != null) {
            inside.mult(inside, modelView);
        }
        return modelView;
    }

    public void bindLights(int pID, int texture) {
        GL20.glUseProgram(pID);
        int lights = MainThread.config.getInteger("lightCount", 16);
        int showShadows = GL20.glGetUniformLocation(pID, "showShadows");
        GL20.glUniform1i(showShadows, hasshadows ? 1 : 0);
        Util.checkErr();
        if (cam instanceof FlyByCamera) {
            int eyepos = GL20.glGetUniformLocation(pID, "eyepos");
            Util.checkErr();
            Location camL = ((FlyByCamera) cam).getCameraLocation();
            Util.checkErr();
            GL20.glUniform3f(eyepos, camL.getFullXAsFloat(), camL.getFullYAsFloat(), camL.getZ());
            Util.checkErr();
        }
        if (hasshadows) {
            int lightDir = GL20.glGetUniformLocation(pID, "sunDir");
            Vector3f dir = sceneNode.getSkyLight().getDirection().normalise(null);
            GL20.glUniform3f(lightDir, dir.x, dir.y, dir.z);
            int sunStr = GL20.glGetUniformLocation(pID, "sunStr");
            GL20.glUniform1f(sunStr, sceneNode.getSkyLight().getIntensity());
        }
        if (!shadows) {
            return;
        }

        int lightCount = GL20.glGetUniformLocation(pID, "lightCount");
        GL20.glUniform1i(lightCount, lights);

        if (lights < 4) {
            lights = 4;
        }
        for (int i = 0; i < lights; i++) {
            ShadowCube ls;
            if (i >= lightShadows.size()) {
                ls = new ShadowCube(MainThread.config.getInteger("shadowSize", 1024));
                lightShadows.add(ls);
            }
            ls = lightShadows.get(i);
            if (ls == null) {
                continue;
            }
            PointLightNode light = ls.getLight();
            if (light == null) {
                continue;
            }
            int cube = GL20.glGetUniformLocation(pID, "cube" + i);
            int lightcolour = GL20.glGetUniformLocation(pID, "lights[" + i + "].colour");
            int lightposition = GL20.glGetUniformLocation(pID, "lights[" + i + "].position");
            int lightpower = GL20.glGetUniformLocation(pID, "lights[" + i + "].power");
            Vector3f loc = light.getPosition().toVector3();
            if (cam != null) {
                // loc = light.getPosition().minus(cam.getLocation());
                // loc = cam.minus(light.getPosition());
            }
            Util.checkErr();
            // Only upload a shadow texture location if we're having shadows for
            // the light. Basic has lights but no shadows
            if (i < showShadows) {
                GL20.glUniform1i(cube, texture + i - GL13.GL_TEXTURE0);
                ls.getFramebuffer().bindDepthTexture(texture + i);
            }
            Util.checkErr();
            GL20.glUniform4f(lightcolour, light.getColour().x, light.getColour().y, light.getColour().z,
                    light.getColour().w);
            Util.checkErr();
            GL20.glUniform4f(lightposition, loc.x, loc.y, loc.z, 1f);
            Util.checkErr();
            GL20.glUniform1f(lightpower, light.getPower());
            Util.checkErr();
            Util.checkErr();
        }
    }

    public void unbindLights() {
        int lights = MainThread.config.getInteger("lightCount", 16);
        if (!shadows) {
            return;
        }
        int lightShadowCount = lights;
        if (lights < 4) {
            lights = 4;
        }
        for (int i = 0; i < lightShadowCount; i++) {
            ShadowCube ls = lightShadows.get(i);
            if (ls == null) {
                continue;
            }
            Util.checkErr();
            ls.getFramebuffer().unbindDepthTexture();
            Util.checkErr();
        }
    }

    public void bindShadowData(int glTexture) {
        if (!hasshadows || !shadows) {
            return;
        }
        Util.checkErr();
        sceneNode.getSkyLight().getFramebuffer().bindDepthTexture(glTexture);
        Util.checkErr();
    }

    public void unbindShadowData() {
        if (!shadows || !hasshadows) {
            return;
        }
        Util.checkErr();
        sceneNode.getSkyLight().getFramebuffer().unbindDepthTexture();
        Util.checkErr();
    }

}