Need help with basic LWJGL 2.9.0 demo loading on CheerpJ

Hello, I am trying to get a very simple LWJGL 2.9.0 Demo working via CheerpJ, but am running into an error Error: window.lwjglCanvasElement is not set or is not a canvas. I've attached a picture of my project structure as well as the the error itself and some other relavent things. My desktop application is a NetBeans project using JDK 8. Here's the source code of the triangle that I found online:
package org.nemotech.opengl;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

public class Main {

public static void main(String[] args) {

try {
Display.setDisplayMode(new DisplayMode(800, 600));
Display.setTitle("Triangle Example");
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(1);
}

// Initialize OpenGL
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 800, 0, 600, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);

while (!Display.isCloseRequested()) {
// Clear the screen
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

// Draw a triangle
GL11.glBegin(GL11.GL_TRIANGLES);
GL11.glColor3f(1.0f, 0.0f, 0.0f); // Red
GL11.glVertex2f(400, 100);
GL11.glColor3f(0.0f, 1.0f, 0.0f); // Green
GL11.glVertex2f(100, 500);
GL11.glColor3f(0.0f, 0.0f, 1.0f); // Blue
GL11.glVertex2f(700, 500);
GL11.glEnd();

Display.update();
}

Display.destroy();
}

}
package org.nemotech.opengl;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

public class Main {

public static void main(String[] args) {

try {
Display.setDisplayMode(new DisplayMode(800, 600));
Display.setTitle("Triangle Example");
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(1);
}

// Initialize OpenGL
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 800, 0, 600, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);

while (!Display.isCloseRequested()) {
// Clear the screen
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

// Draw a triangle
GL11.glBegin(GL11.GL_TRIANGLES);
GL11.glColor3f(1.0f, 0.0f, 0.0f); // Red
GL11.glVertex2f(400, 100);
GL11.glColor3f(0.0f, 1.0f, 0.0f); // Green
GL11.glVertex2f(100, 500);
GL11.glColor3f(0.0f, 0.0f, 1.0f); // Blue
GL11.glVertex2f(700, 500);
GL11.glEnd();

Display.update();
}

Display.destroy();
}

}
No description
No description
Solution:
1. add this HTML to the start of the document body
<canvas id="lwjgl" width="800" height"600"></canvas>
<canvas id="lwjgl" width="800" height"600"></canvas>
2. to the script, add the following line before the cheerpjRunMain call:...
Jump to solution
57 Replies
sean
seanOP•7mo ago
Here are the VM options I am using (just links native LWJGL binaries):
-Djava.library.path="/Users/sean/Documents/lwjgl-2.9.0/native/macosx"
-Djava.library.path="/Users/sean/Documents/lwjgl-2.9.0/native/macosx"
Here is my index.html page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>LWJGL Test</title>
<script src="https://cjrtnc.leaningtech.com/3.0/cj3loader.js"></script>
</head>
<body style="background-color:#D3D3D3;">
<script>
(async function () {
await cheerpjInit({
version: 8,
javaProperties: ["java.library.path=/app/cheerpj-natives/natives", "org.lwjgl.util.NoChecks=true"],
});
cheerpjCreateDisplay(800, 600);
await cheerpjRunMain(
"org.nemotech.opengl.Main",
"/app/OpenGLTest.jar:/app/lwjgl-2.9.0.jar:/app/lwjgl_util-2.9.0.jar"
);
})();
</script>
<pre id="console"/>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>LWJGL Test</title>
<script src="https://cjrtnc.leaningtech.com/3.0/cj3loader.js"></script>
</head>
<body style="background-color:#D3D3D3;">
<script>
(async function () {
await cheerpjInit({
version: 8,
javaProperties: ["java.library.path=/app/cheerpj-natives/natives", "org.lwjgl.util.NoChecks=true"],
});
cheerpjCreateDisplay(800, 600);
await cheerpjRunMain(
"org.nemotech.opengl.Main",
"/app/OpenGLTest.jar:/app/lwjgl-2.9.0.jar:/app/lwjgl_util-2.9.0.jar"
);
})();
</script>
<pre id="console"/>
</body>
</html>
I am using
npx http-server -p 8080
npx http-server -p 8080
for the local HTTP server
sean
seanOP•7mo ago
No description
No description
apignotti
apignotti•7mo ago
You need to simply initialize the global variable lwjglCanvasElement to the HTML5 canvas object that you want to use as your rendering area Please keep in mind that the lwjgl implementation we provide is partial, consider using the browsercraft demo as a guide on how it should be used.
apignotti
apignotti•7mo ago
sean
seanOP•7mo ago
Thanks for the quick response. How should I do this? I know little to nothing regarding HTML/JS
apignotti
apignotti•7mo ago
There are a few steps, you need to create a <canvas> element, resolve it using getElementById, and assign it with the global variable in JavaScript window.lwjglCanvasElement = getElementById(...). I don't have the opportunity of giving you direct code right now, @Alex please provide some guidance when you can But these are all very basic HTML/JS operations, so it could be a good chance of picking up the basics. You'll find countless tutorials on these.
Alex
Alex•7mo ago
hello 👋 as Alessandro suggested, the LWJGL implementation expects you to tell it what canvas to use by setting window.lwjglCanvasElement to an instance of HTMLCanvasElement before you run cheerpjRunMain
Solution
Alex
Alex•7mo ago
1. add this HTML to the start of the document body
<canvas id="lwjgl" width="800" height"600"></canvas>
<canvas id="lwjgl" width="800" height"600"></canvas>
2. to the script, add the following line before the cheerpjRunMain call:
window.lwjglCanvasElement = document.getElementById("lwjgl");
window.lwjglCanvasElement = document.getElementById("lwjgl");
Alex
Alex•7mo ago
(one of the reasons this is required because any canvases managed by CheerpJ inside the display div use a 2D graphics context. LWJGL’s canvas needs to be 3D - and you can’t use multiple contexts on the same canvas element: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext) hopefully this helps - @ me if you run into any trouble :)
sean
seanOP•7mo ago
Hey there, thanks for the response. I've edited the page with those 2 steps, but I'm getting a different error now:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>LWJGL Test</title>
<script src="https://cjrtnc.leaningtech.com/3.0/cj3loader.js"></script>
</head>
<body style="background-color:#D3D3D3;">
<canvas id="lwjgl"></canvas>
<script>
(async function () {
await cheerpjInit({
version: 8,
javaProperties: ["java.library.path=/app/cheerpj-natives/natives", "org.lwjgl.util.NoChecks=true"],
});
cheerpjCreateDisplay(800, 600);
window.lwjglCanvasElement = document.getElementById("lwjgl");
await cheerpjRunMain(
"org.nemotech.opengl.Main",
"/app/OpenGLTest.jar:/app/lwjgl-2.9.0.jar:/app/lwjgl_util-2.9.0.jar"
);
})();
</script>
<pre id="console"/>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>LWJGL Test</title>
<script src="https://cjrtnc.leaningtech.com/3.0/cj3loader.js"></script>
</head>
<body style="background-color:#D3D3D3;">
<canvas id="lwjgl"></canvas>
<script>
(async function () {
await cheerpjInit({
version: 8,
javaProperties: ["java.library.path=/app/cheerpj-natives/natives", "org.lwjgl.util.NoChecks=true"],
});
cheerpjCreateDisplay(800, 600);
window.lwjglCanvasElement = document.getElementById("lwjgl");
await cheerpjRunMain(
"org.nemotech.opengl.Main",
"/app/OpenGLTest.jar:/app/lwjgl-2.9.0.jar:/app/lwjgl_util-2.9.0.jar"
);
})();
</script>
<pre id="console"/>
</body>
</html>
@Alex
No description
Alex
Alex•7mo ago
hm, so it looks like this isn’t going to be as easy as we thought right now, the LWJGL implementation in cheerpj-natives is built so it supports only the functions that Minecraft uses you could try to implement that function (Java_org_…_getRootWindow) inside lwjgl.js
sean
seanOP•7mo ago
Okay I gotcha, I assume just making it a stub function should suffice?
Alex
Alex•7mo ago
hopefully…. try returning null
sean
seanOP•7mo ago
Okay :) trying that
Alex
Alex•7mo ago
alternatively, since you have access to the Java source you could modify it in such a way that it doesn’t call that method at all (i.e. remove the call to Display.create) my guess would be that you probably don’t need any display things at all actually
sean
seanOP•7mo ago
True, I had assumed that was necessary for CheerpJ, but maybe all Display calls can be omitted
sean
seanOP•7mo ago
Was writing my message as you were writing this, yeah I'll try omitting them for now
Alex
Alex•7mo ago
ah, you're also going to need to give the canvas a width and a height:
<canvas id="lwjgl" width="800" height"600"></canvas>
<canvas id="lwjgl" width="800" height"600"></canvas>
sean
seanOP•7mo ago
This happens with all Display code commented out unfortunately, also it seems detached from the CheerpJ frame
No description
sean
seanOP•7mo ago
And adding a
function Java_org_lwjgl_opengl_LinuxDisplay_getRootWindow()
{
return null;
}
function Java_org_lwjgl_opengl_LinuxDisplay_getRootWindow()
{
return null;
}
stub to lwjgl.js yields the same previous error message about the LinuxDisplay (with the Display code)
Alex
Alex•7mo ago
hmm here's where this is thrown in LWJGL itself: https://github.com/search?q=org%3ALWJGL+%22No+OpenGL+context+found+in+the+current+thread%22&type=code
also it seems detached from the CheerpJ frame
this is expected and fine; just hide the CheerpJ display. here's browsercraft doing exactly that: https://github.com/leaningtech/browsercraft/blob/main/minecraft-web.js#L146-L149
sean
seanOP•7mo ago
Okay gotcha Hmmm
Alex
Alex•7mo ago
my thoughts exactly! 😛 give me a minute to look into this more
sean
seanOP•7mo ago
Okay take your time. I really appreciate the help
Alex
Alex•7mo ago
OK, it looks like I was wrong here, you do need something Display-y so... I've gone and added LWJGL support to JavaFiddle so we can better test this
Alex
Alex•7mo ago
LWJGL test - JavaFiddle
// Example using LWJGL 2.9.0 // Warning: experimental! import org.lwjgl.LWJGLException; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.GL11; public class Main { public static void main(String[] args) { try { Display.setDisplayMode(new DisplayMode(800, 600)); Dis...
Alex
Alex•7mo ago
it exhibits the same error you are seeing there must be some other way of initialising GL that Minecraft 1.2.5 uses, since this error doesn't happen in browsercraft
sean
seanOP•7mo ago
Very nice :)
Alex
Alex•7mo ago
I'm not familiar enough with LWJGL myself in order to feel confident advising on this. Maybe if we could see how Minecraft does it or alternatively try to implement that stub correctly
sean
seanOP•7mo ago
Yes I think it's definitely the way it initalizes it as well, as I tried using browsercraft source, but I changed downloading the client from Mojang to this JAR instead, which yields the same error
Alex
Alex•7mo ago
oh, really? hm
sean
seanOP•7mo ago
Yeah
Alex
Alex•7mo ago
that throws my theory out of the window (pun intended)
sean
seanOP•7mo ago
Is that build of minecraft opensource? Well, i suppose it wouldn't be but idk if it's obfuscated
Alex
Alex•7mo ago
java tends to be easy to reverse-engineer...
sean
seanOP•7mo ago
I mean I could try analyzing the source code of their client
Alex
Alex•7mo ago
it might not be helpful anyway, if this is the case I'll try to implement getRootWindow properly
sean
seanOP•7mo ago
Sounds good Yeah, I was just unsure if they used some sophisticated obfuscation tool such as Zelix Klassmaster, etc
Alex
Alex•7mo ago
I don't think they did, no @ayunami2000 knows a bit about reverse-engineering Minecraft, I think, if we want to go down that road
sean
seanOP•7mo ago
OK just looks like very minor obfuscation, such as variable and class renaming. Control flow seems untouched as there's definitely not string encryption either.
No description
sean
seanOP•7mo ago
Now to find the classes that communicate with LWJGL ... Would probably be easy to just use grep to find them, but that's if we really need to see what they did
Alex
Alex•7mo ago
update on this: i will need to revisit this another day if thats OK
sean
seanOP•7mo ago
Sure no problem 🙂
Hypex
Hypex•7mo ago
I wonder if a return 0 would work I'm pretty sure the LWJGL impl just returns 0 for the other get window functions
sean
seanOP•7mo ago
Unfortunately the same thing. Exception in thread "Thread-0" java.lang.UnsatisfiedLinkError: Java_org_lwjgl_opengl_LinuxDisplay_getRootWindow, which is the same as if I don't have the stub at all
ayunami2000
ayunami2000•7mo ago
Look up MCP For deobfuscating Minecraft
sean
seanOP•7mo ago
Might be overkill at the moment given that I just want to render 8 lines of code for a 2D triangle. I might go down that route for a larger project, once I can get this triangle working
downthecrop
downthecrop•7mo ago
I'd suggest an even simpler example like just changing the glClear color to red. There is no implementation for glVertex2f in the natives.
downthecrop
downthecrop•7mo ago
No description
downthecrop
downthecrop•7mo ago
you can stub out LinuxDisplay_getRootWindow and LinuxDisplay_nSetWindowIcon as suggested above.
Alex
Alex•7mo ago
nice! yeah, these just need stubs. happy to take a PR on cheerpj-natives
sean
seanOP•7mo ago
Okay cool, this seems to be working 😎 . Yeah looks like 2d drawing calls aren't implemented yet. I modified the code to draw a small red square (since it looks like quads are supported, but tris are not) with 3F calls instead, but keep running into GL_INVALID_OPERATION: Vertex buffer is not big enough for the draw call. I've tried making vertexBuf larger than 32, but no matter what size I set it to, it results in the same thing. I'll try tinkering around with it some more and see if I have any luck
public static void main(String[] args) {
try {
Display.setDisplayMode(new DisplayMode(1000, 500));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(1);
}

// Initialize OpenGL
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 1000, 0, 500, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);

// Set the clear color to black
GL11.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

while (!Display.isCloseRequested()) {
// Clear the screen
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glLoadIdentity();

// Draw a red square
GL11.glColor3f(1.0f, 0.0f, 0.0f); // Red
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex3f(100, 100, 0); // Top-left
GL11.glVertex3f(200, 100, 0); // Top-right
GL11.glVertex3f(200, 200, 0); // Bottom-right
GL11.glVertex3f(100, 200, 0); // Bottom-left
GL11.glEnd();

Display.update();
Display.sync(60);
}

Display.destroy();
}
public static void main(String[] args) {
try {
Display.setDisplayMode(new DisplayMode(1000, 500));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(1);
}

// Initialize OpenGL
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 1000, 0, 500, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);

// Set the clear color to black
GL11.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

while (!Display.isCloseRequested()) {
// Clear the screen
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glLoadIdentity();

// Draw a red square
GL11.glColor3f(1.0f, 0.0f, 0.0f); // Red
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex3f(100, 100, 0); // Top-left
GL11.glVertex3f(200, 100, 0); // Top-right
GL11.glVertex3f(200, 200, 0); // Bottom-right
GL11.glVertex3f(100, 200, 0); // Bottom-left
GL11.glEnd();

Display.update();
Display.sync(60);
}

Display.destroy();
}
What it looks like rendered on the desktop:
No description
Alex
Alex•7mo ago
I think that tri fan and strip should be supported, just not TRIANGLES (it hits a debugger) I had a play about with this this morning and it feels to me that it might be more sensible to look into supporting modern LWJGL3 and OpenGL ES (ie WebGL) instead of trying to hack together a compatibility layer for GL11->WebGL. (alternatively, we do that, but seriously - the current implementation is mostly hacked together just to support a specific version of Minecraft) depends on your usecase @sean. perhaps you could explain what you’re aiming to build in the end?
Hypex
Hypex•7mo ago
That sounds better, and Minecraft does support LWJGL 3 if you use a patched asset index API Probably not for 1.2.5 tho
sean
seanOP•7mo ago
My long term goal was to port a legacy LWJGL 3d landscape and model editor that I wrote a few years ago for a game I'm working on. I thought it would be really neat to have it browser based
Alex
Alex•7mo ago
that sounds cool; let's make a thread in #cheerpj about it not a problem, we only picked that version because it used GL1 iirc
Want results from more Discord servers?
Add your server