Noob needs help for the oldest problem

Started by SuperTasche, March 05, 2017, 15:49:58

Previous topic - Next topic

SuperTasche

Hello.

I am a noob to LWJGL and OpenGL and failing to draw my first triangle.
Like many before me I am following a tutorial writing my own 3D Game Engine (https://www.youtube.com/playlist?list=PLEETnX-uPtBXP_B2yupUKlflXBznWIlL5).
Since it is already very old I have been trying to rewrite some parts since I am using LWJGL 3.
To cap it all I am developing this on a Windows machine as well as Mac OS X.
I arrived at the point where I want to render a triangle in my window. In several tutorials it is mentioned that drawing a triangle will only work on Mac OS X using shaders and VAOs. I set everything up so far but I wasn't able to run it on Windows yet, just on my Mac.
I am quite desperate now since I feel I have searched the whole internet for my problem and tried different approaches rewriting my code, without success. I have little hope that someone wants to look through my code. I am not sure if I made a mistake setting up my window and OpenGL, maybe there is a mistake. Also, when I try to glValidateProgram() I get an error.
Feel free to ask more questions, since I am not sure how I can better explain my problem. Maybe the code speaks for itself:

Main Class:
package com.base.engine;

// add -XstartOnFirstThread to VM Options in Run/Edit Config...
public class MainComponent
{

    private static int WIDTH = 800;
    private static int HEIGHT = 600;
    private static final String TITLE = "3D Engine";
    public static final double FRAME_CAP = 5000.0; // How many updates per second

    private boolean isRunning; // Is engine running
    private Game game;

    public MainComponent(Window window)
    {
        System.out.println(RenderUtil.getOpenGLVersion());
        RenderUtil.initGraphics(window);
        isRunning = false;
        game = new Game();
    }


    public void start(Window window)
    {
        if(isRunning)
            return;
        run(window);
    }

    public void stop()
    {
        if(!isRunning)
            return;

        isRunning = false;
    }


    private void run(Window window)
    {
        int frames = 0; // Increases for every render
        long frameCounter = 0;
        final double frameTime = 1.0/FRAME_CAP; // Amount of time one frame takes
        long lastTime = Time.getTime(); // Start time of previous frame rendering
        double unprocessedTime = 0; // Keep track of how much frames need to be updated

        isRunning = true;

        while(isRunning)
        {
            boolean render = false; // Indicate if frame needs to be rendered
            long startTime = Time.getTime(); // Start time of current frame rendering
            long passedTime = startTime - lastTime; // Time it took to render last frame

            lastTime = startTime; // Buffer start time of current frame rendering
            unprocessedTime += passedTime/(double)Time.SECOND;
            frameCounter += passedTime;

            // Render while frame still needs to be updated
            while(unprocessedTime > frameTime)
            {
                render = true;
                unprocessedTime -= frameTime; // Update unprocessed time

                // Stop engine if window close is requested
                if(window.closeRequested())
                    stop();

                Time.setDelta(frameTime); // Update delta

                // Update the game
                game.input();
                Input.update(window.getWindow()); // Update the input for every frame
                game.update();

                // Print amount of frames passed per second
                if(frameCounter >= Time.SECOND)
                {
                    System.out.println(frames);
                    frames = 0;
                    frameCounter = 0;
                }
            }

            // Render if frame needs to be updated
            if(render)
            {
                render(window);
                frames++;
            }
            else
            {
                // Sleep 1ms if there is no frame needed to be rendered
                try
                {
                    Thread.sleep(1);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }

        cleanUp(window); // Clean up after run
    }

    private void render(Window window)
    {
        RenderUtil.clearScreen();
        game.render();
        window.update();
        window.render();
    }

    private void cleanUp(Window window)
    {
        window.destroy();
    }

    public static void main(String[] args)
    {
        Window window = new Window(WIDTH, HEIGHT);
        window.createWindow(TITLE);

        MainComponent engine = new MainComponent(window);
        engine.start(window);
    }

}


Window Class:
package com.base.engine;

import static org.lwjgl.glfw.GLFW.*;            // Allows window creation
import static org.lwjgl.opengl.GL11.*;          // Access to GL defines
import static org.lwjgl.system.MemoryUtil.*;    // Access to NULL
import org.lwjgl.glfw.GLFWVidMode;              // Primary monitor dependencies
import org.lwjgl.glfw.GLFWWindowSizeCallback;


public class Window {
    private long window;
    private int width, height;
    private boolean fullscreen;
    /*private boolean hasResized;
    private GLFWWindowSizeCallback windowSizeCallback;

    private Input input;*/

    public Window(int width, int height) {
        setSize(width, height);
        setFullscreen(false);
    }

    public void createWindow(String title) {
        // Throw error if glfw init fails
        if (!glfwInit()) {
            System.err.println("GLFW initialization failed!");
        }

        // Define OpenGL Core Profile
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
        // Request an OpenGL debug context
        glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
        // Allow window to be resizeable
        glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

        // Create window
        window = glfwCreateWindow(width, height, title, NULL, NULL);

        // Check if window creation was successful
        if (window == NULL) {
            System.err.println("Could not create window.");
        }

        // Create vidmode object for primary monitor detection
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Set initial position of window
        glfwSetWindowPos(window, 100, 100);
        // Set context of GLFW
        glfwMakeContextCurrent(window);
        // Show window
        glfwShowWindow(window);
    }


    public void update() {
        glfwPollEvents();
    }

    public void render() {
        glfwSwapBuffers(window);
    }

    public void destroy()
    {
        glfwDestroyWindow(window);
    }

    public boolean closeRequested()
    {
        return glfwWindowShouldClose(window);
    }

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

    public void setFullscreen(boolean fullscreen)
    {
        this.fullscreen = fullscreen;
    }

    public long getWindow()
    {
        return window;
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }
}


Game Class:
package com.base.engine;


public class Game
{
    private MeshLoader loader;
    private Mesh mesh;
    private Shader shader;

    public Game()
    {
        Vertex[] data = new Vertex[] { new Vertex(new Vector3f(-0.5f, 0.5f, 0)),
                new Vertex(new Vector3f(-0.5f, -0.5f, 0)),
                new Vertex(new Vector3f(0.5f, -0.5f, 0)) };

        loader = new MeshLoader();
        mesh = loader.LoadToVAO(data);
        shader = new Shader();

        //mesh.addVertices(data);

        shader.addVertexShader(ResourceLoader.loadShader("basicVertex.vsh"));
        shader.addFragmentShader(ResourceLoader.loadShader("basicFragment.fsh"));
        shader.compileShader();
        shader.validateShader();
    }

    public void input()
    {

    }

    public void update()
    {

    }

    public void render()
    {
        shader.bind();
        mesh.draw();
    }
}


Render Util Class:
package com.base.engine;

import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL30.*;


public class RenderUtil
{
    public static void clearScreen()
    {
        GL.createCapabilities(); // Get current context

        //TODO: Stencil Buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    }

    public static void initGraphics(Window window)
    {
        GL.createCapabilities(); // Get current context

        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);

        glFrontFace(GL_CW); // Every face drawn in clockwise order is frontfaced
        glCullFace(GL_BACK); // Cull backfaces
        glEnable(GL_CULL_FACE); // Enable culling
        glEnable(GL_DEPTH_TEST); // Do depth comparision and update depth buffer

        //TODO: Depth Clamp

        glEnable(GL_FRAMEBUFFER_SRGB); // Assume input colors (being written) are in linear colorspace

        // Map the internal OpenGL coordinate system to the entire screen
        glViewport(0, 0, window.getWidth(), window.getHeight());
    }

    public static String getOpenGLVersion()
    {
        GL.createCapabilities(); // Get current context

        return glGetString(GL_VERSION);
    }
}


Mesh Class:
package com.base.engine;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;


public class Mesh
{
    private int vao; // Vertex Array Object
    // private int vbo; // Vertex Buffer Object: handle (pointer) that holds vertex data like (position, normals, etc.)
    private int size; // Size of mesh: number of vertices * vertex size

    public Mesh(int vao, int size)
    {
        //vao = glGenVertexArrays();
        //vbo = glGenBuffers(); //  Generates buffer object names
        //size = 0;
        this.vao = vao;
        this.size = size;
    }

    public void draw()
    {
        glBindVertexArray(vao);
        glEnableVertexAttribArray(0);
        glDrawArrays(GL_TRIANGLES, 0, size);
        glDisableVertexAttribArray(0);
        glBindVertexArray(0);
    }
}


Mesh Loader Class:
package com.base.engine;

import java.util.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;


public class MeshLoader
{
    private List<Integer> vaos = new ArrayList<Integer>();
    private List<Integer> vbos = new ArrayList<Integer>();

    public Mesh LoadToVAO(Vertex[] vertices)
    {
        int size = vertices.length * Vertex.SIZE;
        int vao = createVAO();
        storeDataInAttribList(0, vertices);
        unbindVAO();
        return new Mesh(vao, size);
    }

    public void cleanUp()
    {
        for(int vao: vaos)
        {
            glDeleteVertexArrays(vao);

        }
        for(int vbo: vbos)
        {
            glDeleteBuffers(vbo);
        }
    }

    private int createVAO()
    {
        int vao = glGenVertexArrays();
        vaos.add(vao);
        glBindVertexArray(vao);
        return vao;
    }

    private void storeDataInAttribList(int attribNum, Vertex[] vertices)
    {
        int vbo = glGenBuffers();
        vbos.add(vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, Util.createFlippedBuffer(vertices), GL_STATIC_DRAW);
        glVertexAttribPointer(attribNum, 3, GL_FLOAT, false, Vertex.SIZE * 4, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    private void unbindVAO()
    {
        glBindVertexArray(0);
    }
}


Shader Class:
package com.base.engine;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL32.*;


public class Shader
{
    private int program;

    public Shader() {
        program = glCreateProgram();

        if (program == 0)
        {
            System.err.println("Shader creation failed: Could not find valid memory location in constructor.");
            System.exit(1);

        }
    }

    public void bind()
    {
        glUseProgram(program);
    }

    public void addVertexShader(String text)
    {
        addProgram(text, GL_VERTEX_SHADER);
    }

    public void addFragmentShader(String text)
    {
        addProgram(text, GL_FRAGMENT_SHADER);
    }

    public void addGeometryShader(String text)
    {
        addProgram(text, GL_GEOMETRY_SHADER);
    }

    public void compileShader()
    {
        glLinkProgram(program);

        if(glGetProgrami(program, GL_LINK_STATUS) == 0)
        {
            System.err.println(glGetShaderInfoLog(program, 1024));
            System.exit(1);
        }

        //ERROR???
/*        glValidateProgram(program);

        if(glGetProgrami(program, GL_VALIDATE_STATUS) == 0)
        {
            System.err.println(glGetShaderInfoLog(program, 1024));
            System.exit(1);
        }*/
    }

    public void validateShader()
    {
        glValidateProgram(program);

        if(glGetProgrami(program, GL_VALIDATE_STATUS) == GL_FALSE)
        {
            System.out.println("Shader validation failed.");
            System.err.println(glGetShaderInfoLog(program, 1024));
            System.exit(1);
        }
    }

    private void addProgram(String text, int type)
    {
        int shader = glCreateShader(type);

        if(shader == 0)
        {
            System.err.println("Shader creation failed: Could not find valid memory location when adding shader.");
            System.exit(1);
        }

        glShaderSource(shader, text);
        glCompileShader(shader);

        if(glGetShaderi(shader, GL_COMPILE_STATUS) == 0)
        {
            System.err.println(glGetShaderInfoLog(shader, 1024));
            System.exit(1);
        }

        glAttachShader(program, shader);
    }
}


Resource Loader Class:
package com.base.engine;

import java.io.BufferedReader;
import java.io.FileReader;


public class ResourceLoader
{
    /*
     *  Load specified shader program
     */
    public static String loadShader(String filename)
    {
        StringBuilder shaderSource = new StringBuilder(); // Responsible for loading text of shader program
        BufferedReader shaderReader = null;

        try
        {
            shaderReader = new BufferedReader(new FileReader("./res/shaders/" + filename));
            String line;
            // Load text to StringBuilder while BufferedReader still reads text from source
            while((line = shaderReader.readLine()) != null)
            {
                shaderSource.append(line).append("\n");
            }

            shaderReader.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            System.exit(1);
        }

        return shaderSource.toString();
    }
}