org.lwjgl.demo.opengl.shader.NoVerticesBSplineDemo.java Source code

Java tutorial

Introduction

Here is the source code for org.lwjgl.demo.opengl.shader.NoVerticesBSplineDemo.java

Source

/*
 * Copyright LWJGL. All rights reserved.
 * License terms: http://lwjgl.org/license.php
 */
package org.lwjgl.demo.opengl.shader;

import static org.lwjgl.demo.opengl.util.DemoUtils.createShader;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL31.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWFramebufferSizeCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.opengl.GLUtil;
import org.lwjgl.system.Callback;

/**
 * Renders a cubic B-spline without using any vertex source but fully computing the vertex positions in the vertex shader.
 * <p>
 * This demo implements cubic B-spline evaluation in the vertex shader and stores the control points in a Uniform Buffer Object.
 * 
 * @author Kai Burjack
 */
public class NoVerticesBSplineDemo {

    long window;
    int width = 1024;
    int height = 768;

    int program;
    int transformUniform;
    int lodUniform;
    int numPointsUniform;

    GLCapabilities caps;
    GLFWErrorCallback errCallback;
    GLFWKeyCallback keyCallback;
    GLFWFramebufferSizeCallback fbCallback;
    Callback debugProc;

    FloatBuffer matrixBuffer = BufferUtils.createFloatBuffer(16);
    Matrix4f transform = new Matrix4f();
    int lod = 10;
    static final int numPoints = 50;

    void init() throws IOException {
        glfwSetErrorCallback(errCallback = new GLFWErrorCallback() {
            GLFWErrorCallback delegate = GLFWErrorCallback.createPrint(System.err);

            @Override
            public void invoke(int error, long description) {
                if (error == GLFW_VERSION_UNAVAILABLE)
                    System.err.println("This demo requires OpenGL 3.0 or higher.");
                delegate.invoke(error, description);
            }

            @Override
            public void free() {
                delegate.free();
            }
        });

        if (!glfwInit())
            throw new IllegalStateException("Unable to initialize GLFW");

        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

        window = glfwCreateWindow(width, height, "No vertices cubic B-splines shader demo", NULL, NULL);
        if (window == NULL) {
            throw new AssertionError("Failed to create the GLFW window");
        }

        glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() {
            @Override
            public void invoke(long window, int width, int height) {
                if (width > 0 && height > 0 && (NoVerticesBSplineDemo.this.width != width
                        || NoVerticesBSplineDemo.this.height != height)) {
                    NoVerticesBSplineDemo.this.width = width;
                    NoVerticesBSplineDemo.this.height = height;
                }
            }
        });

        System.out.println("Press 'arrow up' to increase the lod");
        System.out.println("Press 'arrow down' to decrease the lod");
        glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) {
                if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
                    glfwSetWindowShouldClose(window, true);
                } else if (key == GLFW_KEY_UP && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
                    lod++;
                    System.out.println("Increased LOD to " + lod);
                } else if (key == GLFW_KEY_DOWN && (action == GLFW_RELEASE || action == GLFW_REPEAT)) {
                    lod = Math.max(1, lod - 1);
                    System.out.println("Decreased LOD to " + lod);
                }
            }
        });

        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);
        glfwMakeContextCurrent(window);
        glfwSwapInterval(1);
        glfwShowWindow(window);
        caps = GL.createCapabilities();
        debugProc = GLUtil.setupDebugMessageCallback();

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

        // Create all needed GL resources
        createProgram();
        createUbo();
        // and set some GL state
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    void createProgram() throws IOException {
        int program = glCreateProgram();
        int vshader = createShader("org/lwjgl/demo/opengl/shader/noverticesbspline.vs", GL_VERTEX_SHADER);
        int fshader = createShader("org/lwjgl/demo/opengl/shader/noverticesbspline.fs", GL_FRAGMENT_SHADER);
        glAttachShader(program, vshader);
        glAttachShader(program, fshader);
        glLinkProgram(program);
        int linked = glGetProgrami(program, GL_LINK_STATUS);
        String programLog = glGetProgramInfoLog(program);
        if (programLog.trim().length() > 0) {
            System.err.println(programLog);
        }
        if (linked == 0) {
            throw new AssertionError("Could not link program");
        }
        this.program = program;
        glUseProgram(program);
        transformUniform = glGetUniformLocation(program, "transform");
        lodUniform = glGetUniformLocation(program, "lod");
        numPointsUniform = glGetUniformLocation(program, "numPoints");
        glUseProgram(0);
    }

    static void createUbo() {
        int ubo = glGenBuffers();
        glBindBuffer(GL_UNIFORM_BUFFER, ubo);
        int pointsPerCircle = 5;
        ByteBuffer bb = BufferUtils.createByteBuffer(numPoints * 4 * 4);
        FloatBuffer fb = bb.asFloatBuffer();
        for (int i = 0; i < numPoints; i++) {
            float scale = 1.0f - (float) i / numPoints;
            float t = (float) i / pointsPerCircle;
            float ang = 2.0f * (float) Math.PI * t;
            float x = (float) Math.cos(ang) * scale;
            float y = i / 10.0f;
            float z = (float) Math.sin(ang) * scale;
            fb.put(x).put(y).put(z).put(1.0f);
        }
        glBufferData(GL_UNIFORM_BUFFER, bb, GL_STATIC_DRAW);
        glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo);
    }

    float angle = 0.0f;
    long lastTime = System.nanoTime();

    void render() {
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(this.program);

        // Compute rotation angle
        long thisTime = System.nanoTime();
        float delta = (thisTime - lastTime) / 1E9f;
        angle += delta;
        lastTime = thisTime;

        // Build some transformation matrix
        transform.setPerspective((float) Math.toRadians(45.0f), (float) width / height, 0.1f, 100.0f)
                .lookAt(0, 0, 6, 0, 2, 0, 0, 1, 0).rotateY(angle * (float) Math.toRadians(180)); // 180 degrees per second
        // and upload it to the shader
        glUniformMatrix4fv(transformUniform, false, transform.get(matrixBuffer));
        glUniform1i(lodUniform, lod);
        glUniform1i(numPointsUniform, numPoints);

        glDrawArrays(GL_LINE_STRIP, 0, lod * (numPoints + 1) + 1);

        glUseProgram(0);
    }

    void loop() {
        while (!glfwWindowShouldClose(window)) {
            glfwPollEvents();
            glViewport(0, 0, width, height);
            render();
            glfwSwapBuffers(window);
        }
    }

    void run() {
        try {
            init();
            loop();
            if (debugProc != null) {
                debugProc.free();
            }
            errCallback.free();
            keyCallback.free();
            fbCallback.free();
            glfwDestroyWindow(window);
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            glfwTerminate();
        }
    }

    public static void main(String[] args) {
        new NoVerticesBSplineDemo().run();
    }

}