So i started to learn lwjgl recently, and quickly realized that I need to improve in OpenGL to continue. I followed tutorial here and tried to implement same code using java and lwjgl (code in tutorial is in C++). I successfully managed to replicate OpenGL calls from tutorial using lwjgl api, but when I start my program i see only black screen and nothing else =( No triangle, nothing. No errors in console ether.
My Window.java class where i perform OpenGL rendering (Never mind Spring annotations, i'm just using IoC container to manage objects, method run() is called after Spring context creation):
import com.gitlab.cvazer.dnd.map.desktop.ashlesy.Orchestrator;
import com.gitlab.cvazer.dnd.map.desktop.render.Camera;
import com.gitlab.cvazer.dnd.map.desktop.util.Shaders;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.lwjgl.Version;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.opengl.GL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Objects;
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.system.MemoryUtil.NULL;
#Slf4j
#Component
public class Window {
private #Autowired Shaders shaders;
private #Getter long window;
//START HERE!
public void run() {
new Thread(() -> {
log.info("Hello LWJGL " + Version.getVersion() + "!");
init();
try {
loop();
} catch (IOException e) {
e.printStackTrace();
}
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
glfwTerminate();
Objects.requireNonNull(glfwSetErrorCallback(null)).free();
}).start();
}
private void init() {
GLFWErrorCallback.createPrint(System.err).set();
if ( !glfwInit() ) throw new IllegalStateException("Unable to initialize GLFW");
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_SAMPLES, 8);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(800, 600, "Hello World!", NULL, NULL);
if ( window == NULL ) throw new RuntimeException("Failed to create the GLFW window");
callbacks();
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
glfwShowWindow(window);
}
private void loop() throws IOException {
GL.createCapabilities();
int vertexArray = glGenVertexArrays();
glBindVertexArray(vertexArray);
int vertexBuffer = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
float[] data = {-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f};
glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW);
int program = shaders.loadShaders("vertex.glsl", "fragment.glsl");
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
long lastTime = System.currentTimeMillis();
while ( !glfwWindowShouldClose(window) ) {
long delta = System.currentTimeMillis() - lastTime;
lastTime = System.currentTimeMillis();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(program);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(0,3,GL_FLOAT, false, 0, vertexBuffer);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(0);
glfwSwapBuffers(window); // swap the color buffers
glfwPollEvents();
}
}
}
Shaders.java class:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.lwjgl.opengl.GL30.*;
#Slf4j
#Service
public class Shaders {
public int loadShaders(String vertexFilePath, String fragmentFilePath) throws IOException {
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
String vertexCode = Files.lines(Paths.get(vertexFilePath))
.collect(Collectors.joining("\n"));
String fragmentCode = Files.lines(Paths.get(fragmentFilePath))
.collect(Collectors.joining("\n"));
compileShader(vertexShader, vertexCode);
compileShader(fragmentShader, fragmentCode);
int program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glDetachShader(program, vertexShader);
glDetachShader(program, fragmentShader);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
private void compileShader(int shader, String code){
glShaderSource(shader, code);
glCompileShader(shader);
String slog = glGetShaderInfoLog(shader);
if (slog.contentEquals("")) return;
log.info(slog);
}
}
vertex.glsl file:
#version 330 core
layout(location = 0) in vec3 vertexPosition_modelspace;
void main(){
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
}
fragment.glsl file:
#version 330 core
out vec3 color;
void main(){
color = vec3(1,0,0);
}
I followed part 1 (coding OpenGL calls) and 2 (coding GLSL shaders, loading them) of this tutorial, but adding shaders didn't fixed my problem
I don't think that googling further can give me answers since almost all tutorials online on topic of lwjgl use OpenGL 1 for rendering.
How can i make this work?
The issue is the line
glVertexAttribPointer(0,3,GL_FLOAT, false, 0, vertexBuffer);
If a named buffer object is bound then the last parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store.
When you use glVertexAttribPointer then you don't have to specify the vertex buffer by a parameter. The function associates the vertex attribute to the buffer object, which is currently bound to the target GL_ARRAY_BUFFER.
It has to be:
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
See also Java Code Examples for org.lwjgl.opengl.GL20.glVertexAttribPointer()
Related
While trying to set up a minimal reproducable example using LWJGL because my original question was closed, I ran into a problem I didn't have originally (Originally it was just not displaying), which is that the JVM now crashes with an EXCEPTION_ACCESS_VIOLATION error during the glDrawArrays() call.
I don't know what could be causing this, but I've tried for example initializing the vertex attribute pointers each frame. There is also no debug information or errors logged, while I did set up an error callback and I think a debug message callback.
All of the code:
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLUtil;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class MinimalExample {
private static String readResource(String res) {
try {
InputStream is = MinimalExample.class.getResourceAsStream(res);
String s = new String(is.readAllBytes(), StandardCharsets.UTF_8);
is.close();
return s;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
// vertex data buffer
private static final ByteBuffer buf = ByteBuffer.allocateDirect(4096);
// shader program
static int program;
// render objects
static int vao;
static int vbo;
public static void main(String[] args) {
// set buffer limit
buf.limit(4096);
// init glfw and create window
GLFW.glfwInit();
long window = GLFW.glfwCreateWindow(500, 500, "Hello", 0, 0);
// create GL
GLFW.glfwMakeContextCurrent(window);
GL.createCapabilities();
GLUtil.setupDebugMessageCallback(System.out);
GLFW.glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.out));
// create vertex objects
vao = GL30.glGenVertexArrays();
vbo = GL30.glGenBuffers();
GL30.glBindVertexArray(vao);
GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 7 * 4, 0);
GL30.glVertexAttribPointer(1, 4, GL30.GL_FLOAT, false, 7 * 4, 0);
GL30.glEnableVertexAttribArray(0);
GL30.glEnableVertexAttribArray(1);
GL30.glBindVertexArray(0);
// compile and link shaders
int vertexShader = GL30.glCreateShader(GL30.GL_VERTEX_SHADER);
int fragmentShader = GL30.glCreateShader(GL30.GL_FRAGMENT_SHADER);
GL30.glShaderSource(vertexShader, readResource("/test.vsh"));
GL30.glShaderSource(fragmentShader, readResource("/test.fsh"));
GL30.glCompileShader(vertexShader);
GL30.glCompileShader(fragmentShader);
program = GL30.glCreateProgram();
GL30.glAttachShader(program, vertexShader);
GL30.glAttachShader(program, fragmentShader);
GL30.glLinkProgram(program);
// render loop
while (!GLFW.glfwWindowShouldClose(window)) {
// poll events
GLFW.glfwPollEvents();
// clear screen
GL30.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GL30.glClear(GL30.GL_COLOR_BUFFER_BIT);
// render
render();
// swap buffers
GLFW.glfwSwapBuffers(window);
}
}
static void render() {
// put vertex data
// manual to simulate graphics library
putVec3(0.25f, 0.25f, 1f); putVec4(1.0f, 0.0f, 0.0f, 1.0f);
putVec3(0.75f, 0.25f, 1f); putVec4(0.0f, 1.0f, 0.0f, 1.0f);
putVec3(0.50f, 0.75f, 1f); putVec4(0.0f, 0.0f, 1.0f, 1.0f);
// bind program
GL30.glUseProgram(program);
// bind vertex array
GL30.glBindVertexArray(vao);
GL30.glEnableVertexAttribArray(0);
GL30.glEnableVertexAttribArray(1);
// upload graphics data and draw
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo);
GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buf, GL30.GL_STATIC_DRAW);
GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, 3);
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
// reset vertex data buffer
buf.position(0);
}
//////////////////////////////////////////
static void putVec3(float x, float y, float z) {
buf.putFloat(x).putFloat(y).putFloat(z);
}
static void putVec4(float x, float y, float z, float w) {
buf.putFloat(x).putFloat(y).putFloat(z).putFloat(z);
}
}
The full JVM error log (hs_err_pidX.log): pastes.dev
For context, I'm running this with JDK 17 from IntelliJ directly. Here is the build.gradle file if you want to check the dependencies: build.gradle (GitHub)
I have managed to open a window and render a triangle on the screen in LWJGL. I have now created two external files for the vertex shader & fragment shader. However the triangle remains white.
This is the main file, where it calls the method to load the vertex shader and compile it, I have added a simple system.out line to see if this is being called, which it is.
import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;
public class Main {
String TITLE = "f";
// The window handle
private long window;
public void run() {
System.out.println("Hello LWJGL " + Version.getVersion() + "!");
init();
loop();
// Free the window callbacks and destroy the window
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
// Terminate GLFW and free the error callback
glfwTerminate();
glfwSetErrorCallback(null).free();
}
private void init() {
// Setup an error callback. The default implementation
// will print the error message in System.err.
GLFWErrorCallback.createPrint(System.err).set();
// Initialize GLFW. Most GLFW functions will not work before doing this.
if ( !glfwInit() )
throw new IllegalStateException("Unable to initialize GLFW");
// Configure GLFW
glfwDefaultWindowHints(); // optional, the current window hints are already the default
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
// Create the window
window = glfwCreateWindow(300, 300, TITLE, NULL, NULL);
if ( window == NULL )
throw new RuntimeException("Failed to create the GLFW window");
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
});
// Get the thread stack and push a new frame
try ( MemoryStack stack = stackPush() ) {
IntBuffer pWidth = stack.mallocInt(1); // int*
IntBuffer pHeight = stack.mallocInt(1); // int*
// Get the window size passed to glfwCreateWindow
glfwGetWindowSize(window, pWidth, pHeight);
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center the window
glfwSetWindowPos(
window,
(vidmode.width() - pWidth.get(0)) / 2,
(vidmode.height() - pHeight.get(0)) / 2
);
} // the stack frame is popped automatically
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
private void loop() {
// This line is critical for LWJGL's interoperation with GLFW's
// OpenGL context, or any context that is managed externally.
// LWJGL detects the context that is current in the current thread,
// creates the GLCapabilities instance and makes the OpenGL
// bindings available for use.
GL.createCapabilities();
drawTriangle();
loadShader("screenvertfilelocation", GL30.GL_VERTEX_SHADER);
loadShader("screenvertfilelocation", GL30.GL_FRAGMENT_SHADER);
// Set the clear color
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
// Run the rendering loop until the user has attempted to close
// the window or has pressed the ESCAPE key.
while ( !glfwWindowShouldClose(window) ) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
processTriangle();
glfwSwapBuffers(window); // swap the color buffers
// Poll for window events. The key callback above will only be
// invoked during this call.
glfwPollEvents();
}
}
public static void main(String[] args) {
new Main().run();
}
int vertexCount = 0;
int VAO = 0;
int VBO = 0;
public void drawTriangle() {
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
int VBO = GL30.glGenBuffers();
int VAO = GL30.glGenBuffers();
// Sending data to OpenGL requires the usage of (flipped) byte buffers
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
verticesBuffer.put(vertices);
verticesBuffer.flip();
vertexCount = 3;
GL30.glBindVertexArray(VAO);
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, VBO);
GL30.glBufferData(GL30.GL_ARRAY_BUFFER, verticesBuffer, GL30.GL_STATIC_DRAW);
GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0);
GL30.glEnableVertexAttribArray(0);
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
public void processTriangle() {
GL30.glBindVertexArray(VAO);
GL30.glDrawArrays(GL_TRIANGLES, 0, 3);
}
//Deals with the Shaders
private static int loadShader(String file, int type){
StringBuilder shaderSource = new StringBuilder();
try{
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while((line = reader.readLine())!=null){
shaderSource.append(line).append("//\n");
}
reader.close();
}catch(IOException e){
e.printStackTrace();
System.exit(-1);
}
int shaderID = GL20.glCreateShader(type);
GL20.glShaderSource(shaderID, shaderSource);
GL20.glCompileShader(shaderID);
int vertexShader;
vertexShader = GL30.glCreateShader(GL30.GL_VERTEX_SHADER);
int fragmentShader;
fragmentShader = GL30.glCreateShader(GL30.GL_FRAGMENT_SHADER);
int shaderProgram;
shaderProgram = GL30.glCreateProgram();
GL30.glAttachShader(shaderProgram, vertexShader);
GL30. glAttachShader(shaderProgram, fragmentShader);
GL30.glLinkProgram(shaderProgram);
GL30.glUseProgram(shaderProgram);
GL30.glDeleteShader(vertexShader);
GL30.glDeleteShader(fragmentShader);
System.out.println("Hello");
if(GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS )== GL11.GL_FALSE){
System.out.println(GL20.glGetShaderInfoLog(shaderID, 500));
System.err.println("Could not compile shader!");
System.exit(-1);
}
return shaderID;
}
}
Here is the vertex shader file:
#version 330 core
layout (location = 0) in vec3 aPos;
out vec4 vertexColor;
void main()
{
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(0.5, 4.0, 0.0, 1.0);
}
and here is the fragment shader file:
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
void main()
{
FragColor = vertexColor;
}
There are both improvements and corrections to be made here
1. Improvements
This is in your shader. After linking your shader you also need to verify it. Although your shader works fine without it for more complex programs it can reveal hidden error's
Here is the modified code[Just an suggestion take it if you wish]
private void loadProgram()
{
int vertex,fragment;
int programID=GL20.glCreateProgram(); //Step 1 create program and attach source code to it
GL20.glAttachShader(programID,vertex=loadShader("screenvertfilelocation",GL20.GL_VERTEX_SHADER));
GL20.glAttachShader(programID,fragment=loadShader("screenvertfilelocation",GL20.GL_FRAGMENT_SHADER));
GL20.glLinkProgram(programID); //Step 2 link the program
if(GL20.glGetProgrami(programID,GL20.GL_LINK_STATUS)==GL11.GL_FALSE)
{
int infoLogSize=GL20.glGetProgrami(programID,GL20.GL_INFO_LOG_LENGTH); //Get's exact error size rather than using 500
System.err.println(GL20.glGetProgramInfoLog(programID,infoLogSize));
System.err.println("COULD NOT LINK SHADER");
System.exit(-1);
}
GL20.glValidateProgram(programID); //Step 3 Validate shader
if(GL20.glGetProgrami(programID,GL20.GL_VALIDATE_STATUS)==GL11.GL_FALSE)
{
int infoLogSize=GL20.glGetProgrami(programID,GL20.GL_INFO_LOG_LENGTH); //Get exact error size
System.err.println(GL20.glGetProgramInfoLog(programID,infoLogSize));
System.err.println("COULD NOT VALIDATE SHADER");
System.exit(-1);
}
GL20.glDeleteShader(vertex); //Step 4 clean up
GL20.glDeleteShader(fragment);
GL20.glUseProgram(programID);
}
private int loadShader(String fileName,int shaderType)
{
try(BufferedReader reader = new BufferedReader(new FileReader(fileName)))
{
StringBuilder source=new StringBuilder();
String line;
while((line=reader.readLine())!=null)
{
source.append(line)
.append(System.lineSeparator());
}
int shaderID=GL20.glCreateShader(shaderType);
GL20.glShaderSource(shaderID,source);
GL20.glCompileShader(shaderID);
if(GL20.glGetShaderi(shaderID,GL20.GL_COMPILE_STATUS)==GL11.GL_FALSE)
{
int infoLogSize=GL20.glGetShaderi(shaderID,GL20.GL_INFO_LOG_LENGTH);
throw new IOException("COULD NOT COMPILE SHADER "+GL20.glGetShaderInfoLog(shaderID,infoLogSize));
}
return shaderID;
}
catch(IOException ex)
{
ex.printStackTrace(System.err);
System.exit(-1);
return -1;
}
}
Call loadProgram() after drawTriangle()
2. Your Solution
This is just a follow up from #Rabbid76 post
As soon as i copy pasted your code in my IDE i get the warning
local variable hides a field
In this code
int vertexCount = 0;
int VAO = 0;
int VBO = 0;
public void drawTriangle() {
float vertices[] =
{
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
//The warning here. You are redeclaring VBO & VAO
int VBO = GL15.glGenBuffers();
int VAO = GL30.glGenVertexArrays(); // #Rabbid76 original fix
Just remove the declarations
VBO = GL15.glGenBuffers();
VAO = GL30.glGenVertexArrays();
I am using LWJGL 3.1.3 and most of the gl functions and constants i use belong to an diffrent gl version for example there is no
GL30.GL_FLOAT
but
GL11.GL_FLOAT
Just to name a few
Output:
GLFW.glfwMakeContextCurrent(window);
main = GLContext.createFromCurrent();
other = GLContext.createFromCurrent();
This is what I have tried so far, and in the other thread, I call
GL.setCurrent(other);
But all my OGL calls have no effect (calling genVertexArrays() returns 0).
I can't find any examples online of how to even create two different contexts let alone make one current in another thread.
Took me over 12 hours today to figure it out, but hey, it works and it's awesome. Anyways, the one thing that I lacked was ACTUAL EXAMPLES. So, for all of you who are currently in the position I was in, here you go:
import java.nio.FloatBuffer;
import nick.hacksoi.Program;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.system.MemoryUtil;
public class MultiThreadTest
{
long mainWindow, otherWindow;
public void run()
{
if (GLFW.glfwInit() != GL11.GL_TRUE)
throw new IllegalStateException("Unable to initialize GLFW");
mainWindow = GLFW.glfwCreateWindow(1366, 768, "threading", MemoryUtil.NULL, MemoryUtil.NULL);
if (mainWindow == MemoryUtil.NULL)
throw new RuntimeException("Failed to create the GLFW window");
GLFW.glfwSetWindowPos(mainWindow, 1080 / 2 - 1366 / 4, 30);
GLFW.glfwShowWindow(mainWindow);
GLFW.glfwMakeContextCurrent(mainWindow);
GLContext.createFromCurrent();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GL11.GL_FALSE);
otherWindow = GLFW.glfwCreateWindow(1, 1, "", MemoryUtil.NULL, mainWindow);
if (otherWindow == MemoryUtil.NULL)
throw new RuntimeException("Failed to create the GLFW window");
Runner runner = new Runner();
Thread other = new Thread(runner);
other.start();
try
{
other.join();
} catch (InterruptedException e)
{
e.printStackTrace();
}
Program program = new Program("shaders/2d/simple.vs", "shaders/2d/simple.fs");
int vao = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vao);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, runner.vbo);
GL20.glEnableVertexAttribArray(0);
GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 0, 0);
GL30.glBindVertexArray(0);
GL20.glDisableVertexAttribArray(0);
GL11.glClearColor(0.5f, 0.5f, 1f, 1);
while (GLFW.glfwWindowShouldClose(mainWindow) != GL11.GL_TRUE)
{
GLFW.glfwPollEvents();
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
program.use();
{
GL30.glBindVertexArray(vao);
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 6);
GL30.glBindVertexArray(0);
}
program.unuse();
GLFW.glfwSwapBuffers(mainWindow);
}
}
private class Runner implements Runnable
{
public int vbo;
#Override
public void run()
{
GLFW.glfwMakeContextCurrent(otherWindow);
GLContext.createFromCurrent();
float[] vertices = new float[] { -1, -1, 0, 1, 1, -1 };
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(6);
for (float f : vertices)
vertexBuffer.put(f);
vertexBuffer.flip();
vbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertexBuffer, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
public static void main(String[] args)
{
new MultiThreadTest().run();
}
}
The main caveat that I didn't realize until playing around with this test code was that VertexArrayObjects are NOT shared between contexts; so, you generate them in your rendering thread.
Anyways, hope this helps someone. It would've saved me hours of pain and suffering.
You need to create another window with glfwCreateWindow (but it does not need to be visible). See the GLFW context guide.
I'm using LibGDX for the first time and I'm getting a NullPointerException when trying to implement an InputAdapter into the multiplexer. The message seems to be less than helpful but I'm fairly rusty with my Java. I have tried using a regular class that implements the InputProcessor interface and I've attempted to log any thing that would throw a Null but I haven't been able to locate the cause.
Here's my code
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
public class Game implements ApplicationListener {
public PerspectiveCamera cam;
public Model model;
public ModelInstance instance;
public ModelBatch modelBatch;
public Environment environment;
public InputMultiplexer input;
public static int gWidth = 800;
public static int gHeight = 600;
#Override
public void create() {
this.modelBatch = new ModelBatch();
this.environment = new Environment();
this.environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
this.environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
/**
* Add input adapters.
*/
this.cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
this.cam.position.set(10f, 20f, 0);
this.cam.lookAt(0, 0, 0);
this.cam.rotate(67f, 0f, 0f, 0f);
this.cam.near = 1f;
this.cam.far = 300f;
this.input = new InputMultiplexer(Gdx.input.getInputProcessor());
this.input.addProcessor(new MouseCamController());
Gdx.input.setInputProcessor(this.input);
this.cam.update();
ModelBuilder modelBuilder = new ModelBuilder();
model = modelBuilder.createBox(5f, 5f, 5f,
new Material(ColorAttribute.createDiffuse(Color.GREEN)),
Usage.Position | Usage.Normal);
instance = new ModelInstance(model);
}
#Override
public void resize(int width, int height) {
}
#Override
public void render() {
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// if (this.cam.fieldOfView < 120) {
// this.cam.fieldOfView += 10;
// }
this.cam.update();
this.modelBatch.begin(this.cam);
this.modelBatch.render(instance, environment);
this.modelBatch.end();
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
this.modelBatch.dispose();
this.model.dispose();
}
public class MouseCamController extends InputAdapter {
#Override
public boolean scrolled(int amount) {
// Gdx.app.log("INPUT", "A: " + amount);
return true;
}
}
}
And here are the exception details:
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.badlogic.gdx.InputMultiplexer.mouseMoved(InputMultiplexer.java:107)
at com.badlogic.gdx.backends.lwjgl.LwjglInput.processEvents(LwjglInput.java:303)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:200)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)
The issue was getting the current input processor. Since there was none it did actually hit a NULL when it started going through each adapter. Stupid problem, simple answer. Problem solved and available for posterity.
The problem arises when InputMultiplexer reaches a null InputProcessor in it's list. I've tried replicating it by adding the null processor after an InputProcessor which returns, and the error does not occur.
Simply remove any null InputProcessor from the InputMultiplexer and it should work fine. If you are unsure which one is null, try listing all the processors.
InputMultiplexer plexer;
// Add processors to plexer...
System.out.println(plexer.getProcessors().toString());
I'm having some problems displaying text on the screen, in the past I have just used sprite based text, however this time I want to use UnicodeFont. TrueTypeFonts draw perfectly however its deprecated.
When I try to draw the UnicodeFont it seems to be affected by the characters I use. for example If I draw the string "stackoverflow" the text and the box will draw, if I try "stackoverflowcom" the box will not draw.
A barebones version of my source code is below. On line ~74 I call uniFont.drawString(0, 0,"stackoverflow"); , if the com (or anything really) the box will not be drawn.
edit. > You can use the boolean tryUnicode to swap between true and unicode.
Ive tried sticking them in to seperate display lists but it made no difference.
Could anyone offer an insight in to why this is happening?
Thanks
import java.awt.Font;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.TrueTypeFont;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.effects.ColorEffect;
import static org.lwjgl.opengl.GL11.*;
public class Game {
private UnicodeFont uniFont;
private TrueTypeFont truFont;
public static void main(String[] argv) {
Game game = new Game();
game.start();
}
public void start()
{
initGL(600, 600);
initFonts();
while(!Display.isCloseRequested()) //display not closed
{
render();
Display.update();
Display.sync(60);
}
Display.destroy();
System.exit(0);
}
private void render()
{
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(-0.25f,0.7f,0);
glScalef(0.001f,-0.001f,0.001f);
glEnable(GL_BLEND);
boolean tryUnicode = false;
if(tryUnicode)
{
uniFont.drawString(0, 0,"stackoverflow");
//EDIT.. glDisable texture is required here.
}else
{
glScalef(1.1f,1.1f,1f);
truFont.drawString(0, 0, "stackoverflow truFont");
}
glDisable(GL_BLEND);
glPopMatrix();
glPushMatrix();
glTranslatef(-0.25f,0,0);
glColor3f(0.5f, 0f, 0f);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(0, 0,0.0f);
glVertex3f(0.5f, 0,0f);
glVertex3f(0f,0.5f,0f);
glVertex3f(0.5f, 0.5f,0f);
glEnd();
glPopMatrix();
}
private void initGL(int width, int height) {
try {
Display.setDisplayMode(new DisplayMode(width,height));
Display.create();
//Display.setVSyncEnabled(true);
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
glEnable(GL11.GL_TEXTURE_2D);
glShadeModel(GL11.GL_SMOOTH);
glEnable(GL11.GL_DEPTH_TEST);
glDisable(GL11.GL_LIGHTING);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1);
glEnable(GL_BLEND);
glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glMatrixMode(GL11.GL_PROJECTION);
glLoadIdentity();
glOrtho(-1, 1, -1, 1, -1, 1);
glMatrixMode(GL11.GL_MODELVIEW);
}
private void initFonts() {
Font awtFont = new Font("", Font.PLAIN,55);
truFont = new TrueTypeFont(awtFont, true);
uniFont = new UnicodeFont(awtFont, 128, false, false);
uniFont.addAsciiGlyphs();
uniFont.addGlyphs(400,600); // Setting the unicode Range
uniFont.getEffects().add(new ColorEffect(java.awt.Color.white));
try {
uniFont.loadGlyphs();
} catch (SlickException e) {};
}
}
It seems I have this error now, it will only draw the outlined rectangle before using the font. This seems to be a Slick error, and is a major problem so they should fix it before adding new features.
The only work-around is to render fonts last on your frame.
EDIT: Problem Solved!
add this line after you render your font:
GL11.glDisable(GL11.GL_TEXTURE_2D);
The Slick people should really add that line in as it causes so many bugs!
This is how I create fonts:
Font font = ...; //Your loaded font
float size = 20.0F;
UnicodeFont f = new UnicodeFont(font.deriveFont(0 /*normal*/, size));
f.addAsciiGlyphs();
ColorEffect e = new ColorEffect();
e.setColor(java.awt.Color.white);
f.getEffects().add(e);
try {
f.loadGlyphs();
} catch (SlickException e1) {
e1.printStackTrace();
}
return f;
Hope this helped!