From 44a7a59ff98f0bd987ec141aa1d7c5685e109a8d Mon Sep 17 00:00:00 2001 From: httpdigest Date: Sun, 14 Mar 2021 13:23:55 +0100 Subject: [PATCH] Migrate some JOML-CI/joml-lwjgl3-demos to LWJGL/lwjgl3-demos --- src/org/lwjgl/demo/opengl/PolygonDrawer.java | 317 ++++++++++++++++++ src/org/lwjgl/demo/opengl/PolygonDrawer2.java | 291 ++++++++++++++++ .../opengl/shader/SimpleQuadAndGridDemo.java | 247 ++++++++++++++ .../opengl/shadow/ProjectiveShadowDemo.java | 225 +++++++++++++ .../demo/opengl/transform/LwjglDemo.java | 176 ++++++++++ .../demo/opengl/transform/LwjglDemoLH.java | 175 ++++++++++ .../opengl/transform/ObliqueProjectDemo.java | 160 +++++++++ 7 files changed, 1591 insertions(+) create mode 100644 src/org/lwjgl/demo/opengl/PolygonDrawer.java create mode 100644 src/org/lwjgl/demo/opengl/PolygonDrawer2.java create mode 100644 src/org/lwjgl/demo/opengl/shader/SimpleQuadAndGridDemo.java create mode 100644 src/org/lwjgl/demo/opengl/shadow/ProjectiveShadowDemo.java create mode 100644 src/org/lwjgl/demo/opengl/transform/LwjglDemo.java create mode 100644 src/org/lwjgl/demo/opengl/transform/LwjglDemoLH.java create mode 100644 src/org/lwjgl/demo/opengl/transform/ObliqueProjectDemo.java diff --git a/src/org/lwjgl/demo/opengl/PolygonDrawer.java b/src/org/lwjgl/demo/opengl/PolygonDrawer.java new file mode 100644 index 00000000..f5fa2245 --- /dev/null +++ b/src/org/lwjgl/demo/opengl/PolygonDrawer.java @@ -0,0 +1,317 @@ +package org.lwjgl.demo.opengl; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.system.MemoryUtil.memAddress; + +import java.nio.IntBuffer; +import java.util.BitSet; + +import org.joml.PolygonsIntersection; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWCursorPosCallback; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWFramebufferSizeCallback; +import org.lwjgl.glfw.GLFWKeyCallback; +import org.lwjgl.glfw.GLFWMouseButtonCallback; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.glfw.GLFWWindowSizeCallback; +import org.lwjgl.opengl.GL; + +/** + * This demo showcases the {@link PolygonsIntersection} algorithm. The outlines of a polygon can be drawn with the mouse and an intersection test is + * performed on every mouse movement to color the polygon in red if the mouse cursor is inside; or black if not. + * + * @author Kai Burjack + */ +public class PolygonDrawer { + GLFWErrorCallback errorCallback; + GLFWKeyCallback keyCallback; + GLFWFramebufferSizeCallback fbCallback; + GLFWCursorPosCallback cpCallback; + GLFWMouseButtonCallback mbCallback; + GLFWWindowSizeCallback wsCallback; + + long window; + int width = 800; + int height = 600; + int fbWidth = 800; + int fbHeight = 600; + int x, y; + boolean down; + float[] verticesXY = new float[1024 * 1024]; + int[] polygons = new int[0]; + PolygonsIntersection pointIntersection; + BitSet hitPolygons = new BitSet(); + int first = 0; + int num = 0; + boolean inside; + int querymicroseconds = 0; + int hitPolygonIndex = -1; + + void run() { + try { + init(); + loop(); + + glfwDestroyWindow(window); + keyCallback.free(); + fbCallback.free(); + cpCallback.free(); + mbCallback.free(); + wsCallback.free(); + } finally { + glfwTerminate(); + errorCallback.free(); + } + } + + // void store(String file) { + // try { + // RandomAccessFile rFile = new RandomAccessFile(file, "rw"); + // rFile.setLength(0); + // FileChannel inChannel = rFile.getChannel(); + // ByteBuffer buf_in = ByteBuffer.allocateDirect(num * 2 * 4).order(ByteOrder.nativeOrder()); + // FloatBuffer fb = buf_in.asFloatBuffer(); + // fb.put(verticesXY, 0, num * 2); + // while (buf_in.hasRemaining()) { + // inChannel.write(buf_in); + // } + // inChannel.close(); + // rFile.close(); + // } catch (IOException e) { + // // just ignore everything :) + // } + // } + // + // void load(String file) { + // try { + // RandomAccessFile rFile = new RandomAccessFile(file, "r"); + // int size = (int) rFile.length(); + // FileChannel inChannel = rFile.getChannel(); + // ByteBuffer buf_in = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); + // while (buf_in.hasRemaining()) { + // inChannel.read(buf_in); + // } + // buf_in.flip(); + // FloatBuffer fb = buf_in.asFloatBuffer(); + // num = fb.remaining() / 2; + // fb.get(verticesXY, 0, num * 2); + // inChannel.close(); + // rFile.close(); + // pointIntersection = new PolygonPointIntersection(verticesXY, num); + // } catch (IOException e) { + // // just ignore everything :) + // } + // } + + void updateStats() { + glfwSetWindowTitle(window, "Polygon Demo (" + num + " vertices @ " + querymicroseconds + " µs.)"); + } + + void init() { + glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err)); + if (!glfwInit()) + throw new IllegalStateException("Unable to initialize GLFW"); + + // Configure our window + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_SAMPLES, 4); + + System.out.println("Draw polygons with holding the left mouse button down"); + System.out.println("Move the mouse cursor in and out of the polygons"); + System.out.println("Press 'C' to clear all polygons"); + // System.out.println("Press 'S' to load save the current polygon in file 'poly.gon'"); + // System.out.println("Press 'L' to load a previously saved polygon from file 'poly.gon'"); + + window = glfwCreateWindow(width, height, "Polygon Demo", NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + long cursor = glfwCreateStandardCursor(GLFW.GLFW_CROSSHAIR_CURSOR); + glfwSetCursor(window, cursor); + + 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); + if (key == GLFW_KEY_L && action == GLFW_RELEASE) { + // load("poly.gon"); + } else if (key == GLFW_KEY_S && action == GLFW_RELEASE) { + // store("poly.gon"); + } else if (key == GLFW_KEY_C && action == GLFW_RELEASE) { + num = 0; + polygons = new int[0]; + updateStats(); + } + } + }); + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + @Override + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + fbWidth = w; + fbHeight = h; + } + } + }); + glfwSetWindowSizeCallback(window, wsCallback = new GLFWWindowSizeCallback() { + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + width = w; + height = h; + } + } + }); + glfwSetCursorPosCallback(window, cpCallback = new GLFWCursorPosCallback() { + @Override + public void invoke(long window, double xpos, double ypos) { + x = (int) xpos; + y = (int) ypos; + if (down) { + verticesXY[2 * num + 0] = x; + verticesXY[2 * num + 1] = y; + num++; + updateStats(); + } else { + if (pointIntersection != null) { + long time1 = System.nanoTime(); + inside = pointIntersection.testPoint(x, y, hitPolygons); + if (inside) { + hitPolygonIndex = hitPolygons.nextSetBit(0); + } + long time2 = System.nanoTime(); + querymicroseconds = (int) ((time2 - time1) / 1E3); + updateStats(); + } + else + inside = false; + } + } + }); + glfwSetMouseButtonCallback(window, mbCallback = new GLFWMouseButtonCallback() { + @Override + public void invoke(long window, int button, int action, int mods) { + if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) { + down = true; + inside = false; + } else if (action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) { + down = false; + first = num; + int[] newPolygons = new int[polygons.length + 1]; + System.arraycopy(polygons, 0, newPolygons, 0, polygons.length); + newPolygons[polygons.length] = num; + polygons = newPolygons; + pointIntersection = new PolygonsIntersection(verticesXY, polygons, num); + } + } + }); + + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); + + IntBuffer framebufferSize = BufferUtils.createIntBuffer(2); + nglfwGetFramebufferSize(window, memAddress(framebufferSize), memAddress(framebufferSize) + 4); + width = framebufferSize.get(0); + height = framebufferSize.get(1); + + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + glfwShowWindow(window); + + warmup(); + updateStats(); + + // auto-restore last autosave + // load("autopoly.gon"); + } + + void warmup() { + glfwSetWindowTitle(window, "Warming up..."); + glfwPollEvents(); + // Warmup polygon + int warmupCount = 1024 * 64; + float[] warmupVertices = new float[warmupCount * 2]; + for (int i = 0; i < warmupCount; i++) { + warmupVertices[2*i+0] = ((float)Math.cos((float)i/warmupCount) - 0.5f) * 2.0f; + warmupVertices[2*i+1] = ((float)Math.cos((float)i/warmupCount) - 0.5f) * 2.0f; + } + pointIntersection = new PolygonsIntersection(warmupVertices, new int[0], warmupCount); + int warmupIterations = 1024 * 1024 * 8; + float[] warmupSamples = new float[256]; + for (int i = 0; i < warmupSamples.length; i++) { + warmupSamples[i] = ((float)Math.random() - 0.5f); + } + for (int i = 0; i < warmupIterations; i++) { + float x = warmupSamples[i%256]; + float y = warmupSamples[(i+1)%256]; + pointIntersection.testPoint(x, y); + } + pointIntersection = new PolygonsIntersection(verticesXY, new int[0], 0); + } + + void renderPolygon() { + glBegin(GL_LINE_STRIP); + if (num > 0) { + int curr = 0; + int first = 0; + for (int i = 0; i < num; i++) { + if (inside && curr == hitPolygonIndex) + glColor3f(1.0f, 0.3f, 0.3f); + else + glColor3f(0.01f, 0.01f, 0.01f); + if ((i == (num - 1)) && down) + glColor3f(0.8f, 0.8f, 0.8f); + if (polygons.length > curr && polygons[curr] == i) { + // close current polygon + glVertex2f(verticesXY[2 * first + 0], verticesXY[2 * first + 1]); + first = i; + curr++; + glEnd(); + glBegin(GL_LINE_STRIP); + if (inside && curr == hitPolygonIndex) + glColor3f(1.0f, 0.3f, 0.3f); + else + glColor3f(0.01f, 0.01f, 0.01f); + } + glVertex2f(verticesXY[2 * i + 0], verticesXY[2 * i + 1]); + } + glVertex2f(verticesXY[2 * first + 0], verticesXY[2 * first + 1]); + } + glEnd(); + } + + void loop() { + GL.createCapabilities(); + + glClearColor(0.99f, 0.99f, 0.99f, 1.0f); + glLineWidth(1.8f); + + while (!glfwWindowShouldClose(window)) { + glViewport(0, 0, fbWidth, fbHeight); + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, fbWidth, fbHeight, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + renderPolygon(); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // autosave current polygon + // store("autopoly.gon"); + } + + public static void main(String[] args) { + new PolygonDrawer().run(); + } +} \ No newline at end of file diff --git a/src/org/lwjgl/demo/opengl/PolygonDrawer2.java b/src/org/lwjgl/demo/opengl/PolygonDrawer2.java new file mode 100644 index 00000000..41fe4764 --- /dev/null +++ b/src/org/lwjgl/demo/opengl/PolygonDrawer2.java @@ -0,0 +1,291 @@ +package org.lwjgl.demo.opengl; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.system.MemoryUtil.memAddress; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.BitSet; + +import org.joml.Matrix4f; +import org.joml.PolygonsIntersection; +import org.joml.Vector3f; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWCursorPosCallback; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWFramebufferSizeCallback; +import org.lwjgl.glfw.GLFWKeyCallback; +import org.lwjgl.glfw.GLFWMouseButtonCallback; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.glfw.GLFWWindowSizeCallback; +import org.lwjgl.opengl.GL; + +/** + * Like the {@link PolygonDrawer} but it rotates everything around the viewport center. + *

+ * Intersection tests and drawing at the right position still work! :) + * + * @author Kai Burjack + */ +public class PolygonDrawer2 { + GLFWErrorCallback errorCallback; + GLFWKeyCallback keyCallback; + GLFWFramebufferSizeCallback fbCallback; + GLFWWindowSizeCallback wsCallback; + GLFWCursorPosCallback cpCallback; + GLFWMouseButtonCallback mbCallback; + + long window; + int width = 800; + int height = 600; + int fbWidth = 800; + int fbHeight = 600; + int x, y; + boolean down; + float[] verticesXY = new float[1024 * 1024]; + int[] polygons = new int[0]; + PolygonsIntersection pointIntersection; + BitSet hitPolygons = new BitSet(); + int num = 0; + boolean inside; + int querymicroseconds = 0; + int hitPolygonIndex = -1; + Matrix4f transformation = new Matrix4f(); + Matrix4f transformationInv = new Matrix4f(); + Vector3f p = new Vector3f(); + FloatBuffer matBuffer = BufferUtils.createFloatBuffer(16); + + void run() { + try { + init(); + loop(); + + glfwDestroyWindow(window); + keyCallback.free(); + fbCallback.free(); + wsCallback.free(); + cpCallback.free(); + mbCallback.free(); + } finally { + glfwTerminate(); + errorCallback.free(); + } + } + + void updateStats() { + glfwSetWindowTitle(window, "Polygon Demo (" + num + " vertices @ " + querymicroseconds + " µs.)"); + } + + void intersect() { + if (pointIntersection != null) { + long time1 = System.nanoTime(); + transformationInv.transformPosition(p.set(x, y, 0)); + inside = pointIntersection.testPoint(p.x, p.y, hitPolygons); + if (inside) { + hitPolygonIndex = hitPolygons.nextSetBit(0); + } + long time2 = System.nanoTime(); + querymicroseconds = (int) ((time2 - time1) / 1E3); + updateStats(); + } + else + inside = false; + } + + void init() { + glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err)); + if (!glfwInit()) + throw new IllegalStateException("Unable to initialize GLFW"); + + // Configure our window + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_SAMPLES, 4); + + System.out.println("Draw polygons with holding the left mouse button down"); + System.out.println("Move the mouse cursor in and out of the polygons"); + System.out.println("Press 'C' to clear all polygons"); + + window = glfwCreateWindow(width, height, "Polygon Demo", NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + long cursor = glfwCreateStandardCursor(GLFW.GLFW_CROSSHAIR_CURSOR); + glfwSetCursor(window, cursor); + + 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_C && action == GLFW_RELEASE) { + num = 0; + polygons = new int[0]; + updateStats(); + } + } + }); + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + @Override + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + fbWidth = w; + fbHeight = h; + } + } + }); + glfwSetWindowSizeCallback(window, wsCallback = new GLFWWindowSizeCallback() { + @Override + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + width = w; + height = h; + } + } + }); + glfwSetCursorPosCallback(window, cpCallback = new GLFWCursorPosCallback() { + @Override + public void invoke(long window, double xpos, double ypos) { + x = (int) xpos; + y = (int) ypos; + transformationInv.transformPosition(p.set(x, y, 0)); + if (down) { + verticesXY[2 * num + 0] = p.x; + verticesXY[2 * num + 1] = p.y; + num++; + updateStats(); + } + } + }); + glfwSetMouseButtonCallback(window, mbCallback = new GLFWMouseButtonCallback() { + @Override + public void invoke(long window, int button, int action, int mods) { + if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) { + down = true; + inside = false; + } else if (action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) { + down = false; + int[] newPolygons = new int[polygons.length + 1]; + System.arraycopy(polygons, 0, newPolygons, 0, polygons.length); + newPolygons[polygons.length] = num; + polygons = newPolygons; + pointIntersection = new PolygonsIntersection(verticesXY, polygons, num); + } + } + }); + + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); + + IntBuffer framebufferSize = BufferUtils.createIntBuffer(2); + nglfwGetFramebufferSize(window, memAddress(framebufferSize), memAddress(framebufferSize) + 4); + fbWidth = framebufferSize.get(0); + fbHeight = framebufferSize.get(1); + + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + glfwShowWindow(window); + + warmup(); + updateStats(); + } + + void warmup() { + glfwSetWindowTitle(window, "Warming up..."); + glfwPollEvents(); + // Warmup polygon + int warmupCount = 1024 * 64; + float[] warmupVertices = new float[warmupCount * 2]; + for (int i = 0; i < warmupCount; i++) { + warmupVertices[2*i+0] = ((float)Math.cos((float)i/warmupCount) - 0.5f) * 2.0f; + warmupVertices[2*i+1] = ((float)Math.cos((float)i/warmupCount) - 0.5f) * 2.0f; + } + pointIntersection = new PolygonsIntersection(warmupVertices, new int[0], warmupCount); + int warmupIterations = 1024 * 1024 * 8; + float[] warmupSamples = new float[256]; + for (int i = 0; i < warmupSamples.length; i++) { + warmupSamples[i] = ((float)Math.random() - 0.5f); + } + for (int i = 0; i < warmupIterations; i++) { + float x = warmupSamples[i%256]; + float y = warmupSamples[(i+1)%256]; + pointIntersection.testPoint(x, y); + } + pointIntersection = new PolygonsIntersection(verticesXY, new int[0], 0); + } + + void renderPolygon() { + glBegin(GL_LINE_STRIP); + if (num > 0) { + int curr = 0; + int first = 0; + for (int i = 0; i < num; i++) { + if (inside && curr == hitPolygonIndex) + glColor3f(1.0f, 0.3f, 0.3f); + else + glColor3f(0.01f, 0.01f, 0.01f); + if ((i == (num - 1)) && down) + glColor3f(0.8f, 0.8f, 0.8f); + if (polygons.length > curr && polygons[curr] == i) { + // close current polygon + glVertex2f(verticesXY[2 * first + 0], verticesXY[2 * first + 1]); + first = i; + curr++; + glEnd(); + glBegin(GL_LINE_STRIP); + if (inside && curr == hitPolygonIndex) + glColor3f(1.0f, 0.3f, 0.3f); + else + glColor3f(0.01f, 0.01f, 0.01f); + } + glVertex2f(verticesXY[2 * i + 0], verticesXY[2 * i + 1]); + } + glVertex2f(verticesXY[2 * first + 0], verticesXY[2 * first + 1]); + } + glEnd(); + } + + float angle = 0.0f; + void loop() { + GL.createCapabilities(); + + glClearColor(0.99f, 0.99f, 0.99f, 1.0f); + glLineWidth(1.8f); + + long lastTime = System.nanoTime(); + + while (!glfwWindowShouldClose(window)) { + long thisTime = System.nanoTime(); + float dt = (thisTime - lastTime) / 1E9f; + lastTime = thisTime; + + glViewport(0, 0, fbWidth, fbHeight); + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + transformation + .identity() + .translate(width/2, height/2, 0) + .rotateZ(angle += dt * 0.2f) + .translate(-width/2, -height/2, 0) + .invert(transformationInv); + glLoadMatrixf(transformation.get(matBuffer)); + + intersect(); + renderPolygon(); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + } + + public static void main(String[] args) { + new PolygonDrawer2().run(); + } +} \ No newline at end of file diff --git a/src/org/lwjgl/demo/opengl/shader/SimpleQuadAndGridDemo.java b/src/org/lwjgl/demo/opengl/shader/SimpleQuadAndGridDemo.java new file mode 100644 index 00000000..81f0b66e --- /dev/null +++ b/src/org/lwjgl/demo/opengl/shader/SimpleQuadAndGridDemo.java @@ -0,0 +1,247 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl.demo.opengl.shader; + +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.GL; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; +import static org.lwjgl.system.MemoryUtil.*; + +/** + * @author Kai Burjack + */ +public class SimpleQuadAndGridDemo { + GLFWErrorCallback errorCallback; + GLFWKeyCallback keyCallback; + GLFWFramebufferSizeCallback fbCallback; + + long window; + int width = 300; + int height = 300; + Object lock = new Object(); + boolean destroyed; + + Matrix4f viewProjMatrix = new Matrix4f(); + FloatBuffer fb = BufferUtils.createFloatBuffer(16); + + void run() { + try { + init(); + loop(); + + synchronized (lock) { + destroyed = true; + glfwDestroyWindow(window); + } + keyCallback.free(); + fbCallback.free(); + } finally { + glfwTerminate(); + errorCallback.free(); + } + } + + void init() { + glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err)); + if (!glfwInit()) + throw new IllegalStateException("Unable to initialize GLFW"); + + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_SAMPLES, 8); + + window = glfwCreateWindow(width, height, "Hello shaders!", NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + + 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); + } + }); + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + @Override + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + width = w; + height = h; + } + } + }); + + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); + glfwShowWindow(window); + + IntBuffer framebufferSize = BufferUtils.createIntBuffer(2); + nglfwGetFramebufferSize(window, memAddress(framebufferSize), memAddress(framebufferSize) + 4); + width = framebufferSize.get(0); + height = framebufferSize.get(1); + } + + void renderCube() { + glBegin(GL_QUADS); + glVertex3f( 0.5f, -0.5f, -0.5f ); + glVertex3f( -0.5f, -0.5f, -0.5f ); + glVertex3f( -0.5f, 0.5f, -0.5f ); + glVertex3f( 0.5f, 0.5f, -0.5f ); + + glVertex3f( 0.5f, -0.5f, 0.5f ); + glVertex3f( 0.5f, 0.5f, 0.5f ); + glVertex3f( -0.5f, 0.5f, 0.5f ); + glVertex3f( -0.5f, -0.5f, 0.5f ); + + glVertex3f( 0.5f, -0.5f, -0.5f ); + glVertex3f( 0.5f, 0.5f, -0.5f ); + glVertex3f( 0.5f, 0.5f, 0.5f ); + glVertex3f( 0.5f, -0.5f, 0.5f ); + + glVertex3f( -0.5f, -0.5f, 0.5f ); + glVertex3f( -0.5f, 0.5f, 0.5f ); + glVertex3f( -0.5f, 0.5f, -0.5f ); + glVertex3f( -0.5f, -0.5f, -0.5f ); + + glVertex3f( 0.5f, 0.5f, 0.5f ); + glVertex3f( 0.5f, 0.5f, -0.5f ); + glVertex3f( -0.5f, 0.5f, -0.5f ); + glVertex3f( -0.5f, 0.5f, 0.5f ); + + glVertex3f( 0.5f, -0.5f, -0.5f ); + glVertex3f( 0.5f, -0.5f, 0.5f ); + glVertex3f( -0.5f, -0.5f, 0.5f ); + glVertex3f( -0.5f, -0.5f, -0.5f ); + glEnd(); + } + + void renderGrid() { + glBegin(GL_LINES); + for (int i = -20; i <= 20; i++) { + glVertex3f(-20.0f, 0.0f, i); + glVertex3f( 20.0f, 0.0f, i); + glVertex3f(i, 0.0f, -20.0f); + glVertex3f(i, 0.0f, 20.0f); + } + glEnd(); + } + + void initOpenGLAndRenderInAnotherThread() { + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + GL.createCapabilities(); + + glClearColor(0.6f, 0.7f, 0.8f, 1.0f); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + // Create a simple shader program + int program = glCreateProgram(); + int vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, + "uniform mat4 viewProjMatrix;" + + "void main(void) {" + + " gl_Position = viewProjMatrix * gl_Vertex;" + + "}"); + glCompileShader(vs); + glAttachShader(program, vs); + int fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, + "uniform vec3 color;" + + "void main(void) {" + + " gl_FragColor = vec4(color, 1.0);" + + "}"); + glCompileShader(fs); + glAttachShader(program, fs); + glLinkProgram(program); + glUseProgram(program); + + // Obtain uniform location + int matLocation = glGetUniformLocation(program, "viewProjMatrix"); + int colorLocation = glGetUniformLocation(program, "color"); + long lastTime = System.nanoTime(); + + /* Quaternion to rotate the cube */ + Quaternionf q = new Quaternionf(); + + while (!destroyed) { + long thisTime = System.nanoTime(); + float dt = (thisTime - lastTime) / 1E9f; + lastTime = thisTime; + + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Create a view-projection matrix + viewProjMatrix.setPerspective((float) Math.toRadians(45.0f), + (float) width / height, 0.01f, 100.0f) + .lookAt(0.0f, 4.0f, 10.0f, + 0.0f, 0.5f, 0.0f, + 0.0f, 1.0f, 0.0f); + // Upload the matrix stored in the FloatBuffer to the + // shader uniform. + glUniformMatrix4fv(matLocation, false, viewProjMatrix.get(fb)); + // Render the grid without rotating + glUniform3f(colorLocation, 0.3f, 0.3f, 0.3f); + renderGrid(); + + // rotate the cube (45 degrees per second) + // and translate it by 0.5 in y + viewProjMatrix.translate(0.0f, 0.5f, 0.0f) + .rotate(q.rotateY((float) Math.toRadians(45) * dt).normalize()); + // Upload the matrix + glUniformMatrix4fv(matLocation, false, viewProjMatrix.get(fb)); + + // Render solid cube with outlines + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glUniform3f(colorLocation, 0.6f, 0.7f, 0.8f); + renderCube(); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(-1.f,-1.f); + glUniform3f(colorLocation, 0.0f, 0.0f, 0.0f); + renderCube(); + glDisable(GL_POLYGON_OFFSET_LINE); + + synchronized (lock) { + if (!destroyed) { + glfwSwapBuffers(window); + } + } + } + } + + void loop() { + /* + * Spawn a new thread which to make the OpenGL context current in and which does the + * rendering. + */ + new Thread(new Runnable() { + public void run() { + initOpenGLAndRenderInAnotherThread(); + } + }).start(); + + /* Process window messages in the main thread */ + while (!glfwWindowShouldClose(window)) { + glfwWaitEvents(); + } + } + + public static void main(String[] args) { + new SimpleQuadAndGridDemo().run(); + } +} \ No newline at end of file diff --git a/src/org/lwjgl/demo/opengl/shadow/ProjectiveShadowDemo.java b/src/org/lwjgl/demo/opengl/shadow/ProjectiveShadowDemo.java new file mode 100644 index 00000000..762ad004 --- /dev/null +++ b/src/org/lwjgl/demo/opengl/shadow/ProjectiveShadowDemo.java @@ -0,0 +1,225 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl.demo.opengl.shadow; + +import org.joml.Matrix4f; +import org.joml.Vector4f; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryUtil.*; + +/** + * @author Kai Burjack + */ +public class ProjectiveShadowDemo { + GLFWErrorCallback errorCallback; + GLFWKeyCallback keyCallback; + GLFWFramebufferSizeCallback fbCallback; + + long window; + int width = 300; + int height = 300; + + // FloatBuffer for transferring matrices to OpenGL + FloatBuffer fb = BufferUtils.createFloatBuffer(16); + + void run() { + try { + init(); + loop(); + + glfwDestroyWindow(window); + keyCallback.free(); + } finally { + glfwTerminate(); + errorCallback.free(); + } + } + + void init() { + glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err)); + if (!glfwInit()) + throw new IllegalStateException("Unable to initialize GLFW"); + + // Configure our window + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_SAMPLES, 4); + + window = glfwCreateWindow(width, height, "Hello projective shadows!", NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + + 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); + } + }); + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + @Override + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + width = w; + height = h; + } + } + }); + + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); + + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + glfwShowWindow(window); + + IntBuffer framebufferSize = BufferUtils.createIntBuffer(2); + nglfwGetFramebufferSize(window, memAddress(framebufferSize), memAddress(framebufferSize) + 4); + width = framebufferSize.get(0); + height = framebufferSize.get(1); + } + + void renderPlane() { + glBegin(GL_QUADS); + glColor3f(0.5f, 0.6f, 0.7f); + glVertex3f(-1.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 0.0f, -1.0f); + glVertex3f(-1.0f, 0.0f, -1.0f); + glEnd(); + } + + void renderLight() { + glPointSize(10.0f); + glBegin(GL_POINTS); + glColor3f(1.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glEnd(); + } + + void renderCube(boolean shadow) { + glBegin(GL_QUADS); + if (shadow) { + glColor3f(0.2f, 0.2f, 0.2f); + } else { + glColor3f(0.0f, 0.0f, 0.2f); + } + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + if (!shadow) { + glColor3f(0.0f, 0.0f, 1.0f); + } + glVertex3f(0.5f, -0.5f, 0.5f); + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, 0.5f); + if (!shadow) { + glColor3f(1.0f, 0.0f, 0.0f); + } + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(0.5f, -0.5f, 0.5f); + if (!shadow) { + glColor3f(0.2f, 0.0f, 0.0f); + } + glVertex3f(-0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + if (!shadow) { + glColor3f(0.0f, 1.0f, 0.0f); + } + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + if (!shadow) { + glColor3f(0.0f, 0.2f, 0.0f); + } + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glEnd(); + } + + void loop() { + GL.createCapabilities(); + + glClearColor(0.6f, 0.7f, 0.8f, 1.0f); + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + glEnable(GL_CULL_FACE); + + long firstTime = System.nanoTime(); + + Matrix4f m = new Matrix4f(); + Matrix4f m2 = new Matrix4f(); + Matrix4f planeTransform = new Matrix4f().translate(0.0f, -0.5f, 0.0f).scale(10.0f); + Vector4f lightPos = new Vector4f(); + + // when we write stencil, we always replace the current value + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + while (!glfwWindowShouldClose(window)) { + long thisTime = System.nanoTime(); + float diff = (thisTime - firstTime) / 1E9f; + float angle = diff; + + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(m.setPerspective((float) Math.toRadians(30.0f), (float) width / height, 0.01f, 100.0f).get(fb)); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(m.setLookAt(1.0f, 6.0f, 12.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f).rotateY(angle / 5.0f).get(fb)); + + // always write stencil = 1 + glStencilFunc(GL_ALWAYS, 1, 1); + + // render cube in the center + renderCube(false); + + // Render the plane on which to project the shadow + glLoadMatrixf(m.mulAffine(planeTransform, m2).get(fb)); + renderPlane(); + + // Render light bulb + m2.rotationY(angle).translate(0, 0.8f, 2).transform(lightPos.set(0, 0, 0, 1)); + glLoadMatrixf(m.mulAffine(m2, m2).get(fb)); + renderLight(); + + // Render projected shadow of the cube + glLoadMatrixf(m.shadow(lightPos, planeTransform).get(fb)); + // Draw only on the stenciled area + glStencilFunc(GL_EQUAL, 1, 1); + glEnable(GL_POLYGON_OFFSET_FILL); + // use polygon offset to combat z-fighting between plane and projected shadow + glPolygonOffset(-1.0f, -1.0f); + renderCube(true); + glDisable(GL_POLYGON_OFFSET_FILL); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + } + + public static void main(String[] args) { + new ProjectiveShadowDemo().run(); + } +} \ No newline at end of file diff --git a/src/org/lwjgl/demo/opengl/transform/LwjglDemo.java b/src/org/lwjgl/demo/opengl/transform/LwjglDemo.java new file mode 100644 index 00000000..392d0ef2 --- /dev/null +++ b/src/org/lwjgl/demo/opengl/transform/LwjglDemo.java @@ -0,0 +1,176 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl.demo.opengl.transform; + +import org.joml.*; +import org.joml.Math; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; + +import java.nio.FloatBuffer; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryUtil.*; + +/** + * @author Kai Burjack + */ +public class LwjglDemo { + GLFWErrorCallback errorCallback; + GLFWKeyCallback keyCallback; + GLFWFramebufferSizeCallback fbCallback; + + long window; + int width = 400; + int height = 300; + + // JOML matrices + Matrix4f projMatrix = new Matrix4f(); + Matrix4x3f viewMatrix = new Matrix4x3f(); + Matrix4x3f modelMatrix = new Matrix4x3f(); + Matrix4x3f modelViewMatrix = new Matrix4x3f(); + + // FloatBuffer for transferring matrices to OpenGL + FloatBuffer fb = BufferUtils.createFloatBuffer(16); + + void run() { + try { + init(); + loop(); + + glfwDestroyWindow(window); + keyCallback.free(); + } finally { + glfwTerminate(); + errorCallback.free(); + } + } + + void init() { + glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err)); + if (!glfwInit()) + throw new IllegalStateException("Unable to initialize GLFW"); + + // Configure our window + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + + window = glfwCreateWindow(width, height, "Hello World!", NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + + 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); + } + }); + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + @Override + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + width = w; + height = h; + } + } + }); + + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); + + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + glfwShowWindow(window); + } + + void renderCube() { + glBegin(GL_QUADS); + glColor3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + glColor3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.5f, -0.5f, 0.5f); + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, 0.5f); + glColor3f(1.0f, 0.0f, 0.0f); + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(0.5f, -0.5f, 0.5f); + glColor3f(0.2f, 0.0f, 0.0f); + glVertex3f(-0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glColor3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glColor3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glEnd(); + } + + void loop() { + GL.createCapabilities(); + + // Set the clear color + glClearColor(0.6f, 0.7f, 0.8f, 1.0f); + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + // Remember the current time. + long firstTime = System.nanoTime(); + + while (!glfwWindowShouldClose(window)) { + // Build time difference between this and first time. + long thisTime = System.nanoTime(); + float diff = (thisTime - firstTime) / 1E9f; + // Compute some rotation angle. + float angle = diff; + + // Make the viewport always fill the whole window. + glViewport(0, 0, width, height); + + // Build the projection matrix. Watch out here for integer division + // when computing the aspect ratio! + projMatrix.setPerspective((float) Math.toRadians(40), (float) width / height, 0.01f, 100.0f); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projMatrix.get(fb)); + + // Set lookat view matrix + viewMatrix.setLookAt(0.0f, 4.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); + glMatrixMode(GL_MODELVIEW); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Render some grid of cubes at different x and z positions + for (int x = -2; x <= 2; x++) { + for (int z = -2; z <= 2; z++) { + modelMatrix.translation(x * 2.0f, 0, z * 2.0f).rotateY(angle * (float) Math.toRadians(90)); + glLoadMatrixf(viewMatrix.mul(modelMatrix, modelViewMatrix).get4x4(fb)); + renderCube(); + } + } + glfwSwapBuffers(window); + glfwPollEvents(); + } + } + + public static void main(String[] args) { + new LwjglDemo().run(); + } +} \ No newline at end of file diff --git a/src/org/lwjgl/demo/opengl/transform/LwjglDemoLH.java b/src/org/lwjgl/demo/opengl/transform/LwjglDemoLH.java new file mode 100644 index 00000000..a987097d --- /dev/null +++ b/src/org/lwjgl/demo/opengl/transform/LwjglDemoLH.java @@ -0,0 +1,175 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl.demo.opengl.transform; + +import org.joml.*; +import org.joml.Math; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; + +import java.nio.FloatBuffer; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryUtil.*; + +/** + * @author Kai Burjack + */ +public class LwjglDemoLH { + GLFWErrorCallback errorCallback; + GLFWKeyCallback keyCallback; + GLFWFramebufferSizeCallback fbCallback; + + long window; + int width = 300; + int height = 300; + + // JOML matrices + Matrix4f projMatrix = new Matrix4f(); + Matrix4x3f viewMatrix = new Matrix4x3f(); + + // FloatBuffer for transferring matrices to OpenGL + FloatBuffer fb = BufferUtils.createFloatBuffer(16); + + void run() { + try { + init(); + loop(); + + glfwDestroyWindow(window); + keyCallback.free(); + } finally { + glfwTerminate(); + errorCallback.free(); + } + } + + void init() { + glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err)); + if (!glfwInit()) + throw new IllegalStateException("Unable to initialize GLFW"); + + // Configure our window + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + + window = glfwCreateWindow(width, height, "Hello World!", NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + + 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); + } + }); + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + @Override + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + width = w; + height = h; + } + } + }); + + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); + + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + glfwShowWindow(window); + } + + void renderCube() { + glBegin(GL_QUADS); + glColor3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glVertex3f(0.5f, 0.5f, 0.5f); + glColor3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glColor3f(1.0f, 0.0f, 0.0f); + glVertex3f(0.5f, -0.5f, 0.5f); + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(0.5f, 0.5f, -0.5f); + glVertex3f(0.5f, -0.5f, -0.5f); + glColor3f(0.2f, 0.0f, 0.0f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, -0.5f, 0.5f); + glColor3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.5f, 0.5f, -0.5f); + glVertex3f(0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, 0.5f); + glVertex3f(-0.5f, 0.5f, -0.5f); + glColor3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.5f, -0.5f, 0.5f); + glVertex3f(0.5f, -0.5f, -0.5f); + glVertex3f(-0.5f, -0.5f, -0.5f); + glVertex3f(-0.5f, -0.5f, 0.5f); + glEnd(); + } + + void loop() { + GL.createCapabilities(); + + // Set the clear color + glClearColor(0.6f, 0.7f, 0.8f, 1.0f); + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + // Remember the current time. + long firstTime = System.nanoTime(); + + while (!glfwWindowShouldClose(window)) { + // Build time difference between this and first time. + long thisTime = System.nanoTime(); + float diff = (thisTime - firstTime) / 1E9f; + // Compute some rotation angle. + float angle = diff; + + // Make the viewport always fill the whole window. + glViewport(0, 0, width, height); + + // Build the projection matrix. Watch out here for integer division + // when computing the aspect ratio! + projMatrix.setPerspectiveLH((float) Math.toRadians(40), (float) width / height, 0.01f, 100.0f); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projMatrix.get(fb)); + + // Build a model-view matrix which first rotates the cube + // about the Y-axis and then lets a "camera" look at that + // cube from a certain distance. + viewMatrix.setLookAtLH(0.0f, 2.0f, -5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f) + // rotate 90 degrees per second + .rotateY(angle * (float) Math.toRadians(90)); + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(viewMatrix.get4x4(fb)); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Render a simple cube + renderCube(); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + } + + public static void main(String[] args) { + new LwjglDemoLH().run(); + } +} \ No newline at end of file diff --git a/src/org/lwjgl/demo/opengl/transform/ObliqueProjectDemo.java b/src/org/lwjgl/demo/opengl/transform/ObliqueProjectDemo.java new file mode 100644 index 00000000..7ed47228 --- /dev/null +++ b/src/org/lwjgl/demo/opengl/transform/ObliqueProjectDemo.java @@ -0,0 +1,160 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl.demo.opengl.transform; + +import org.joml.*; +import org.joml.Math; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; + +import java.nio.FloatBuffer; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryUtil.*; + +/** + * @author Kai Burjack + */ +public class ObliqueProjectDemo { + private GLFWErrorCallback errorCallback; + private GLFWKeyCallback keyCallback; + private GLFWFramebufferSizeCallback fbCallback; + + private long window; + private int width = 200; + private int height = 200; + + private Matrix4f projMatrix = new Matrix4f(); + private Matrix4x3f modelMatrix = new Matrix4x3f(); + + private FloatBuffer fb = BufferUtils.createFloatBuffer(16); + + private void run() { + try { + init(); + loop(); + glfwDestroyWindow(window); + keyCallback.free(); + fbCallback.free(); + } finally { + glfwTerminate(); + errorCallback.free(); + } + } + + private void init() { + glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err)); + if (!glfwInit()) + throw new IllegalStateException("Unable to initialize GLFW"); + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + window = glfwCreateWindow(width, height, "Hello Cavalier Projection", NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() { + public void invoke(long window, int key, int scancode, int action, int mods) { + if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) + glfwSetWindowShouldClose(window, true); + } + }); + glfwSetFramebufferSizeCallback(window, fbCallback = new GLFWFramebufferSizeCallback() { + public void invoke(long window, int w, int h) { + if (w > 0 && h > 0) { + width = w; + height = h; + } + } + }); + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); + glfwMakeContextCurrent(window); + glfwSwapInterval(0); + glfwShowWindow(window); + } + + private void renderCube() { + glBegin(GL_QUADS); + glColor3f( 0.0f, 0.0f, 0.2f ); + glVertex3f( 0.5f, -0.5f, -0.5f ); + glVertex3f( -0.5f, -0.5f, -0.5f ); + glVertex3f( -0.5f, 0.5f, -0.5f ); + glVertex3f( 0.5f, 0.5f, -0.5f ); + glColor3f( 0.0f, 0.0f, 1.0f ); + glVertex3f( 0.5f, -0.5f, 0.5f ); + glVertex3f( 0.5f, 0.5f, 0.5f ); + glVertex3f( -0.5f, 0.5f, 0.5f ); + glVertex3f( -0.5f, -0.5f, 0.5f ); + glColor3f( 1.0f, 0.0f, 0.0f ); + glVertex3f( 0.5f, -0.5f, -0.5f ); + glVertex3f( 0.5f, 0.5f, -0.5f ); + glVertex3f( 0.5f, 0.5f, 0.5f ); + glVertex3f( 0.5f, -0.5f, 0.5f ); + glColor3f( 0.2f, 0.0f, 0.0f ); + glVertex3f( -0.5f, -0.5f, 0.5f ); + glVertex3f( -0.5f, 0.5f, 0.5f ); + glVertex3f( -0.5f, 0.5f, -0.5f ); + glVertex3f( -0.5f, -0.5f, -0.5f ); + glColor3f( 0.0f, 1.0f, 0.0f ); + glVertex3f( 0.5f, 0.5f, 0.5f ); + glVertex3f( 0.5f, 0.5f, -0.5f ); + glVertex3f( -0.5f, 0.5f, -0.5f ); + glVertex3f( -0.5f, 0.5f, 0.5f ); + glColor3f( 0.0f, 0.2f, 0.0f ); + glVertex3f( 0.5f, -0.5f, -0.5f ); + glVertex3f( 0.5f, -0.5f, 0.5f ); + glVertex3f( -0.5f, -0.5f, 0.5f ); + glVertex3f( -0.5f, -0.5f, -0.5f ); + glEnd(); + } + + private void loop() { + GL.createCapabilities(); + glClearColor(0.6f, 0.7f, 0.8f, 1.0f); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + /* Create a matrix for an oblique (cavalier) projection of 45° */ + float angle = 45.0f; + float a = (float)Math.cos(Math.toRadians(-angle)); + float b = (float)Math.sin(Math.toRadians(-angle)); + // x' = x + a*z + // y' = y + b*z + // z' = z + Matrix4f oblique = new Matrix4f( + 1, 0, a, 0, + 0, 1, b, 0, + 0, 0, 1, 0, + 0, 0, 0, 1).transpose(); + + while (!glfwWindowShouldClose(window)) { + glViewport(0, 0, width, height); + + float ar = (float) width / height; + float ex = 6.0f; // <- view extents + projMatrix.setOrtho2D(-ex * ar, +ex * ar, -ex, +ex) + .mul(oblique); // <- multiply oblique projection + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projMatrix.get(fb)); + + glMatrixMode(GL_MODELVIEW); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + for (int x = -2; x <= 2; x++) { + for (int y = -2; y <= 2; y++) { + glLoadMatrixf(modelMatrix.translation(x * 2.0f, y * 2.0f, 0).get4x4(fb)); + renderCube(); + } + } + glfwSwapBuffers(window); + glfwPollEvents(); + } + } + + public static void main(String[] args) { + new ObliqueProjectDemo().run(); + } +} \ No newline at end of file