org.spout.engine.SpoutRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.spout.engine.SpoutRenderer.java

Source

/*
 * This file is part of Spout.
 *
 * Copyright (c) 2011 Spout LLC <http://www.spout.org/>
 * Spout is licensed under the Spout License Version 1.
 *
 * Spout is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * In addition, 180 days after any changes are published, you can use the
 * software, incorporating those changes, under the terms of the MIT license,
 * as described in the Spout License Version 1.
 *
 * Spout 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 Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License,
 * the MIT license and the Spout License Version 1 along with this program.
 * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
 * License and see <http://spout.in/licensev1> for the full license, including
 * the MIT license.
 */
package org.spout.engine;

import java.awt.Canvas;
import java.util.HashMap;

import gnu.trove.map.hash.TIntObjectHashMap;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.OpenGLException;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.opengl.Util;

import org.spout.api.Spout;
import org.spout.api.component.world.SkydomeComponent;
import org.spout.api.geo.discrete.Point;
import org.spout.api.gui.FullScreen;
import org.spout.api.gui.Screen;
import org.spout.api.gui.Widget;
import org.spout.api.gui.render.RenderPart;
import org.spout.api.gui.render.RenderPartPack;
import org.spout.api.math.Rectangle;
import org.spout.api.render.Camera;
import org.spout.api.render.RenderMode;
import org.spout.api.render.shader.Shader;

import org.spout.engine.batcher.SpriteBatch;
import org.spout.engine.filesystem.resource.ClientRenderMaterial;
import org.spout.engine.filesystem.resource.ClientRenderTexture;
import org.spout.engine.gui.DebugScreen;
import org.spout.engine.gui.SpoutScreenStack;
import org.spout.engine.gui.SpoutWidget;
import org.spout.engine.mesh.BaseMesh;
import org.spout.engine.renderer.BatchVertexRenderer;
import org.spout.engine.renderer.EntityRenderer;
import org.spout.engine.renderer.WorldRenderer;
import org.spout.engine.util.MacOSXUtils;
import org.spout.math.matrix.Matrix4;
import org.spout.math.vector.Vector2;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glClear;

public class SpoutRenderer {
    private static final TIntObjectHashMap<String> GL_TYPE_NAMES = new TIntObjectHashMap<>();
    private final SpoutClient client;
    private DebugScreen debugScreen;
    private SpoutScreenStack screenStack;
    private boolean showDebugInfos = true;
    //private ArrayList<RenderMaterial> postProcessMaterials = new ArrayList<>();
    private boolean ccoverride = false;
    private Vector2 resolution;
    private float aspectRatio;
    private EntityRenderer entityRenderer;
    private WorldRenderer worldRenderer;
    private boolean wireframe = false;
    // Screen texture
    private SpriteBatch screenBatcher;
    private ClientRenderTexture t;
    private ClientRenderMaterial mat;
    // Reflected world FBO
    // This will need the stencil buffer
    private boolean useReflexion = false; // Set this to true to experiment
    private ClientRenderTexture reflected;
    private SpriteBatch reflectedDebugBatch; // Debug
    private ClientRenderMaterial reflectedDebugMat; //Debug

    public SpoutRenderer(SpoutClient client, Vector2 resolution, boolean ccoverride) {
        this.client = client;
        this.resolution = resolution;
        this.aspectRatio = resolution.getX() / resolution.getY();

        // Building the screenStack
        FullScreen mainScreen = new FullScreen();
        mainScreen.setTakesInput(false);
        this.screenStack = new SpoutScreenStack(mainScreen);
        this.debugScreen = (DebugScreen) screenStack.getDebugHud();

        this.entityRenderer = new EntityRenderer();

        this.ccoverride = ccoverride;

        worldRenderer = new WorldRenderer();
    }

    public void initRenderer(Canvas parent) {
        createWindow(parent);

        if (Spout.debugMode()) {
            client.getLogger().info("SpoutClient Information");
            client.getLogger().info("Operating System: " + System.getProperty("os.name"));
            client.getLogger().info("Renderer Mode: " + client.getRenderMode().toString());
            client.getLogger().info("GL21: " + GLContext.getCapabilities().OpenGL21 + " GL32: "
                    + GLContext.getCapabilities().OpenGL32);
            client.getLogger().info("Resolution: " + Display.getWidth() + "x" + Display.getHeight());
            client.getLogger().info("OpenGL Information");
            client.getLogger().info("Vendor: " + GL11.glGetString(GL11.GL_VENDOR));
            client.getLogger().info("OpenGL Version: " + GL11.glGetString(GL11.GL_VERSION));
            client.getLogger().info("GLSL Version: " + GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION));
            client.getLogger().info("Max Textures: " + GL11.glGetInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS));
            String extensions = "Extensions Supported: ";
            if (client.getRenderMode() == RenderMode.GL30 || client.getRenderMode() == RenderMode.GL40) {
                for (int i = 0; i < GL11.glGetInteger(GL30.GL_NUM_EXTENSIONS); i++) {
                    extensions += GL30.glGetStringi(GL11.GL_EXTENSIONS, i) + " ";
                }
            } else {
                extensions += GL11.glGetString(GL11.GL_EXTENSIONS);
            }
            client.getLogger().info(extensions);
        }

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

        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glClearColor((135.f / 255.0f), 206.f / 255.f, 250.f / 255.f, 1);
        SpoutRenderer.checkGLError();

        //Init pool of BatchVertexRenderer
        BatchVertexRenderer.initPool(GL11.GL_TRIANGLES, 500);

        if (useReflexion) {
            reflected = new ClientRenderTexture(true, false, true);
            reflected.writeGPU();
            // Test
            reflectedDebugBatch = new SpriteBatch();
            Shader s1 = client.getFileSystem().getResource("shader://Spout/shaders/diffuse.ssf");
            HashMap<String, Object> map1 = new HashMap<>();
            map1.put("Diffuse", reflected);
            reflectedDebugMat = new ClientRenderMaterial(s1, map1);
            RenderPart screenPart1 = new RenderPart();
            screenPart1.setSprite(new Rectangle(-1, -1, 0.5f, 0.5f));
            screenPart1.setSource(new Rectangle(0, 1, 1, -1));
            RenderPartPack pack1 = new RenderPartPack(reflectedDebugMat);
            pack1.add(screenPart1);
            reflectedDebugBatch.flush(pack1);
            // Test end
        }

        screenBatcher = new SpriteBatch();
        t = new ClientRenderTexture(true, false, true);
        t.writeGPU();
        Shader s = client.getFileSystem().getResource("shader://Spout/shaders/diffuse.ssf");
        HashMap<String, Object> map = new HashMap<>();
        map.put("Diffuse", t);
        mat = new ClientRenderMaterial(s, map);
        RenderPart screenPart = new RenderPart();
        screenPart.setSprite(new Rectangle(-1, -1, 2, 2));
        screenPart.setSource(new Rectangle(0, 1, 1, -1));
        RenderPartPack pack = new RenderPartPack(mat);
        pack.add(screenPart);
        screenBatcher.flush(pack);
    }

    public void updateRender(long limit) {
        worldRenderer.update(limit);
    }

    long guiTime, worldTime, entityTime;

    public void render(float dt) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        final Camera camera = client.getPlayer().getType(Camera.class);
        final SkydomeComponent skydome = client.getWorld().get(SkydomeComponent.class);

        // Render reflected world
        if (useReflexion) {
            reflected.activate();
            GL11.glCullFace(GL11.GL_FRONT);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            if (camera != null) {
                camera.updateReflectedView();

                if (skydome != null && skydome.getModel() != null) {
                    skydome.getModel().getRenderMaterial().getShader().setUniform("View", camera.getRotation());
                    skydome.getModel().getRenderMaterial().getShader().setUniform("Projection",
                            camera.getProjection());
                    skydome.getModel().getRenderMaterial().getShader().setUniform("Model", Matrix4.IDENTITY);
                    BaseMesh reflectedSkydomeMesh = (BaseMesh) skydome.getModel().getMesh();
                    if (!reflectedSkydomeMesh.isBatched()) {
                        reflectedSkydomeMesh.batch();
                    }
                    reflectedSkydomeMesh.render(skydome.getModel().getRenderMaterial());
                }
            }

            worldRenderer.render();
            entityRenderer.render(dt);

            GL11.glCullFace(GL11.GL_BACK);
            reflected.release();
        }

        // Render normal world
        t.activate();
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        if (camera != null) {
            camera.updateView();

            if (skydome != null && skydome.getModel() != null) {
                skydome.getModel().getRenderMaterial().getShader().setUniform("View", camera.getRotation());
                skydome.getModel().getRenderMaterial().getShader().setUniform("Projection", camera.getProjection());
                skydome.getModel().getRenderMaterial().getShader().setUniform("Model", Matrix4.IDENTITY);
                BaseMesh skydomeMesh = (BaseMesh) skydome.getModel().getMesh();
                if (!skydomeMesh.isBatched()) {
                    skydomeMesh.batch();
                }
                skydomeMesh.render(skydome.getModel().getRenderMaterial());
            }
        }

        long start = System.nanoTime();
        worldRenderer.render();
        worldTime = System.nanoTime() - start;
        start = System.nanoTime();
        entityRenderer.render(dt);
        entityTime = System.nanoTime() - start;
        start = System.nanoTime();

        t.release();

        // Render gui
        if (wireframe) {
            GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
        }

        screenBatcher.render(Matrix4.IDENTITY);
        if (useReflexion) {
            reflectedDebugBatch.render(Matrix4.IDENTITY);
        }

        //GUI -> Render all widgets
        for (Screen screen : screenStack.getVisibleScreens()) {
            for (Widget widget : screen.getWidgets()) {
                ((SpoutWidget) widget).render();
            }
        }

        //GUI -> Give the main screen the mouse if no input screen is set
        final Screen input = screenStack.getInputScreen();
        if (input == null) {
            Mouse.setGrabbed(true);
        } else {
            Mouse.setGrabbed(input.grabsMouse());
        }

        //GUI -> Update debug info
        if (showDebugInfos) {
            int id = 0;
            Point position = client.getPlayer().getPhysics().getPosition();
            debugScreen.spoutUpdate(id++, "Spout client! Logged as " + client.getPlayer().getDisplayName()
                    + " in world: " + client.getWorld().getName());
            debugScreen.spoutUpdate(id++,
                    "x: " + position.getX() + "y: " + position.getY() + "z: " + position.getZ());
            debugScreen.spoutUpdate(id++, "FPS: " + client.getScheduler().getFps() + " ("
                    + (client.getScheduler().isRendererOverloaded() ? "Overloaded" : "Normal") + ")");
            debugScreen.spoutUpdate(id++, "Chunks Loaded: " + client.getWorld().getNumLoadedChunks());
            debugScreen.spoutUpdate(id++,
                    "Total ChunkMeshBatchAggregators in Renderer: " + worldRenderer.getTotalChunks() + "");
            debugScreen.spoutUpdate(id++,
                    "Chunks Drawn: "
                            + ((int) ((float) worldRenderer.getRenderedChunks()
                                    / (float) (worldRenderer.getTotalChunks()) * 100))
                            + "%" + " (" + worldRenderer.getRenderedChunks() + ")");
            debugScreen.spoutUpdate(id++, "Occluded Chunks: "
                    + (int) ((float) worldRenderer.getOccludedChunks() / worldRenderer.getTotalChunks() * 100)
                    + "% (" + worldRenderer.getOccludedChunks() + ")");
            debugScreen.spoutUpdate(id++,
                    "Cull Chunks: "
                            + (int) ((float) worldRenderer.getCulledChunks() / worldRenderer.getTotalChunks() * 100)
                            + "% (" + worldRenderer.getCulledChunks() + ")");
            debugScreen.spoutUpdate(id++, "Entities: " + entityRenderer.getRenderedEntities());
            debugScreen.spoutUpdate(id++,
                    "Buffer: " + worldRenderer.addedBatch + " / " + worldRenderer.updatedBatch);
            debugScreen.spoutUpdate(id++, "Mesh batch queue size: "
                    + ((SpoutClient) Spout.getEngine()).getRenderer().getWorldRenderer().getBatchWaiting());
        }

        for (Screen screen : screenStack.getVisibleScreens()) {
            for (Widget widget : screen.getWidgets()) {
                ((SpoutWidget) widget).render();
            }
        }

        guiTime = System.nanoTime() - start;

        if (wireframe) {
            GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
        }
    }

    public WorldRenderer getWorldRenderer() {
        return worldRenderer;
    }

    public EntityRenderer getEntityRenderer() {
        return entityRenderer;
    }

    public SpoutScreenStack getScreenStack() {
        return screenStack;
    }

    public Vector2 getResolution() {
        return resolution;
    }

    public float getAspectRatio() {
        return aspectRatio;
    }

    private void createWindow(Canvas parent) {
        try {
            Display.setDisplayMode(new DisplayMode((int) resolution.getX(), (int) resolution.getY()));
            Display.setParent(parent);
            //Override using ContextAttribs for some videocards that don't support ARB_CREATE_CONTEXT
            if (ccoverride) {
                Display.create(new PixelFormat(8, 24, 0));
                Display.setTitle("Spout Client");
                return;
            }

            if (MacOSXUtils.isMac()) {
                createMacWindow();
            } else {
                if (client.getRenderMode() == RenderMode.GL20) {
                    ContextAttribs ca = new ContextAttribs(2, 1);
                    Display.create(new PixelFormat(8, 24, 0), ca);
                } else if (client.getRenderMode() == RenderMode.GL30) {
                    ContextAttribs ca = new ContextAttribs(3, 2).withForwardCompatible(false);
                    Display.create(new PixelFormat(8, 24, 0), ca);
                } else if (client.getRenderMode() == RenderMode.GL40) {
                    ContextAttribs ca = new ContextAttribs(4, 0).withForwardCompatible(false);
                    Display.create(new PixelFormat(8, 24, 0), ca);
                }
            }

            Display.setTitle("Spout Client");
        } catch (LWJGLException e) {
            e.printStackTrace();
        }
    }

    private void createMacWindow() throws LWJGLException {
        if (client.getRenderMode() == RenderMode.GL30) {
            if (MacOSXUtils.getOSXVersion() >= 7) {
                ContextAttribs ca = new ContextAttribs(3, 2).withProfileCore(true);
                Display.create(new PixelFormat(8, 24, 0), ca);
            } else {
                throw new UnsupportedOperationException("Cannot create a 3.0 context without OSX 10.7_");
            }
        } else {
            Display.create();
        }
    }

    public void toggleWireframe() {
        if (wireframe) {
            GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
            wireframe = false;
        } else {
            GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
            wireframe = true;
        }
    }

    private static void setGLTypeName(int typeId, String typeName) {
        GL_TYPE_NAMES.put(typeId, typeName);
    }

    static {
        setGLTypeName(GL20.GL_BOOL, "GL_BOOL");
        setGLTypeName(GL20.GL_BOOL_VEC2, "GL_BOOL_VEC2");
        setGLTypeName(GL20.GL_BOOL_VEC3, "GL_BOOL_VEC3");
        setGLTypeName(GL20.GL_BOOL_VEC4, "GL_BOOL_VEC4");
        setGLTypeName(GL20.GL_FLOAT_VEC2, "GL_FLOAT_VEC2");
        setGLTypeName(GL20.GL_FLOAT_VEC3, "GL_FLOAT_VEC3");
        setGLTypeName(GL20.GL_FLOAT_VEC4, "GL_FLOAT_VEC4");
        setGLTypeName(GL20.GL_FLOAT_MAT2, "GL_FLOAT_MAT2");
        setGLTypeName(GL20.GL_FLOAT_MAT3, "GL_FLOAT_MAT3");
        setGLTypeName(GL20.GL_FLOAT_MAT4, "GL_FLOAT_MAT4");
        setGLTypeName(GL20.GL_INT_VEC2, "GL_INT_VEC2");
        setGLTypeName(GL20.GL_INT_VEC3, "GL_INT_VEC3");
        setGLTypeName(GL20.GL_INT_VEC4, "GL_INT_VEC4");
        setGLTypeName(GL20.GL_SAMPLER_1D, "GL_SAMPLER_1D");
        setGLTypeName(GL20.GL_SAMPLER_1D_SHADOW, "GL_SAMPLER_1D_SHADOW");
        setGLTypeName(GL20.GL_SAMPLER_2D, "GL_SAMPLER_2D");
        setGLTypeName(GL20.GL_SAMPLER_2D_SHADOW, "GL_SAMPLER_2D_SHADOW");
        setGLTypeName(GL20.GL_SAMPLER_3D, "GL_SAMPLER_3D");
        setGLTypeName(GL20.GL_SAMPLER_CUBE, "GL_SAMPLER_CUBE");
    }

    /**
     * Gets the GL type name from a GL type identifier
     *
     * @param glType to get the name of
     * @return The GL type name
     */
    public static String getGLTypeName(int glType) {
        String name = GL_TYPE_NAMES.get(glType);
        if (name == null) {
            return "UNKNOWN(" + glType + ")";
        } else {
            return name;
        }
    }

    public static void checkGLError() {
        try {
            Util.checkGLError();
        } catch (OpenGLException e) {
            e.printStackTrace();
        }
    }
}